🎨 docs: new webtui/ β€” from-scratch terminal docs site (WebTUI, 8-bit palette, dual sidebar, nerd icons, 5 themes)

swadhinbiswas committed 2 months ago
74e99fc
1 parent ac54424
commit 74e99fc
32 files changed
+1035 -0
β–Όwebtui/README.md
+45-0
1
+# JustAPI Docs β€” WebTUI Terminal Site
2
+
3
+A from-scratch documentation site built on **WebTUI** (https://webtui.ironclad.sh)
4
+β€” the pure-CSS library that brings Terminal UIs to the browser.
5
+
6
+## Structure
7
+
8
+```
9
+webtui/
10
+β”œβ”€β”€ build.py # static site generator (markdown β†’ HTML)
11
+β”œβ”€β”€ css/ # vendored WebTUI (base, components, nf, themes)
12
+β”œβ”€β”€ src/
13
+β”‚ β”œβ”€β”€ main.css # terminal design system (navbar, dual sidebar, landing)
14
+β”‚ └── main.js # theme switcher, tabs, bars, sidebar active states
15
+β”œβ”€β”€ templates/
16
+β”‚ β”œβ”€β”€ base.html # page template (navbar + left nav + content + right TOC)
17
+β”‚ └── landing.html # home page (hero, stats, terminal, bars, cards)
18
+β”œβ”€β”€ content/ # (empty β€” source markdown lives in ../docs_site/src/content/docs)
19
+└── dist/ # generated static site (deploy this)
20
+```
21
+
22
+## Build
23
+
24
+```bash
25
+python3 build.py # reads ../docs_site/src/content/docs β†’ dist/
26
+```
27
+
28
+Output: static HTML with zero JS framework β€” WebTUI is pure CSS + a small
29
+vanilla JS file for theme switching and the terminal interactions.
30
+
31
+## Design
32
+
33
+- **8-bit terminal palette** β€” classic ANSI colors (`--bit0`..`--bit15`)
34
+- **Themes**: dark Β· light Β· catppuccin (default) Β· everforest Β· nord
35
+ (floating switcher, bottom-right)
36
+- **Nerd Font icons** β€” Symbols Nerd Font via CDN + vendored `nf.css`
37
+- **Font**: JetBrains Mono + Symbols Nerd Font fallback
38
+- **Layout**: terminal title-bar navbar, left docs sidebar, content, right
39
+ per-page TOC β€” the classic dual-sidebar docs layout, terminal-styled
40
+
41
+## Deploy
42
+
43
+```bash
44
+npx wrangler pages deploy dist --project-name=justapi
45
+```
0
46
β–Όwebtui/build.py
+198-0
1
+#!/usr/bin/env python3
2
+"""JustAPI docs build β€” WebTUI terminal static site generator.
3
+
4
+Reads markdown from ../docs_site/src/content/docs, renders each page with
5
+the WebTUI terminal template (navbar + dual sidebars + content), and writes
6
+static HTML to dist/.
7
+"""
8
+import re
9
+import shutil
10
+import html as html_mod
11
+from pathlib import Path
12
+
13
+import markdown
14
+
15
+ROOT = Path(__file__).parent
16
+CONTENT = Path(__file__).parent.parent / "docs_site/src/content/docs"
17
+DIST = ROOT / "dist"
18
+
19
+# ─── Sidebar structure: (group, [(label, path), ...]) ───
20
+SIDEBAR = [
21
+ ("Getting Started", [
22
+ ("Overview", "/getting-started/overview/"),
23
+ ("Installation", "/getting-started/installation/"),
24
+ ("First Steps", "/getting-started/first-steps/"),
25
+ ("CLI Scaffolder", "/getting-started/cli-scaffolder/"),
26
+ ("Migrate from FastAPI", "/getting-started/migrating-from-fastapi/"),
27
+ ]),
28
+ ("Tutorials", [
29
+ ("Hello World", "/tutorials/hello-world/"),
30
+ ("Path Parameters", "/tutorials/path-params/"),
31
+ ("Query Parameters", "/tutorials/query-params/"),
32
+ ("Request Body", "/tutorials/request-body/"),
33
+ ("Error Handling", "/tutorials/error-handling/"),
34
+ ("Dependencies", "/tutorials/dependency-injection/"),
35
+ ("Middleware", "/tutorials/middleware/"),
36
+ ("CORS", "/tutorials/cors/"),
37
+ ("Databases", "/tutorials/database-integration/"),
38
+ ("Background Tasks", "/tutorials/background-tasks/"),
39
+ ("Testing", "/tutorials/testing/"),
40
+ ("Static Files", "/tutorials/static-files/"),
41
+ ]),
42
+ ("Advanced", [
43
+ ("Zero-GIL", "/advanced/zero-gil-architecture/"),
44
+ ("Rust Core", "/advanced/rust-core-deep-dive/"),
45
+ ("Native Fast Path", "/advanced/native-fast-path/"),
46
+ ("Streaming Output", "/advanced/streaming-output/"),
47
+ ("WebSockets", "/advanced/websockets-advanced/"),
48
+ ("Templates", "/advanced/templates/"),
49
+ ("Performance Tuning", "/advanced/performance-tuning/"),
50
+ ("Resilience", "/advanced/resilience-patterns/"),
51
+ ]),
52
+ ("API Reference", [
53
+ ("JustAPIApp", "/api-reference/justapiapp/"),
54
+ ("Routing", "/api-reference/routing/"),
55
+ ("Request", "/api-reference/request/"),
56
+ ("Responses", "/api-reference/responses/"),
57
+ ("Dependency Injection", "/api-reference/dependency-injection/"),
58
+ ("Exceptions", "/api-reference/exceptions/"),
59
+ ("Schema Validation", "/api-reference/schema-validation/"),
60
+ ("WebSockets", "/api-reference/websockets/"),
61
+ ("Background Tasks", "/api-reference/background-tasks/"),
62
+ ("Scheduler", "/api-reference/scheduler/"),
63
+ ("Database", "/api-reference/database/"),
64
+ ("Testing Client", "/api-reference/testing-client/"),
65
+ ("UploadFile", "/api-reference/uploadfile/"),
66
+ ]),
67
+ ("Deployment", [
68
+ ("Docker", "/deployment/docker/"),
69
+ ("Kubernetes", "/deployment/kubernetes-helm/"),
70
+ ("Cloudflare Pages", "/deployment/cloudflare-pages/"),
71
+ ("Production Checklist", "/deployment/production-checklist/"),
72
+ ]),
73
+ ("Security", [
74
+ ("Security Policy", "/security/policy/"),
75
+ ("OWASP", "/security/owasp-compliance/"),
76
+ ("Secure Config", "/security/secure-configuration/"),
77
+ ]),
78
+ ("Observability", [
79
+ ("Metrics", "/observability/metrics-monitoring/"),
80
+ ("OpenTelemetry", "/observability/opentelemetry/"),
81
+ ("Structured Logging", "/observability/structured-logging/"),
82
+ ("Health Checks", "/observability/health-checks/"),
83
+ ]),
84
+ ("Reference", [
85
+ ("CLI", "/reference/cli/"),
86
+ ("Configuration", "/reference/configuration/"),
87
+ ("API Stability", "/reference/api-stability/"),
88
+ ("Release Notes", "/reference/release-notes/"),
89
+ ("Glossary", "/reference/glossary/"),
90
+ ]),
91
+]
92
+
93
+TEMPLATE = (ROOT / "templates/base.html").read_text()
94
+
95
+
96
+def parse_frontmatter(text: str) -> tuple[dict, str]:
97
+ """Extract YAML-ish frontmatter (title/description) and the body."""
98
+ meta: dict = {}
99
+ if text.startswith("---"):
100
+ end = text.find("---", 3)
101
+ if end != -1:
102
+ raw = text[3:end]
103
+ for line in raw.strip().splitlines():
104
+ if ":" in line:
105
+ k, v = line.split(":", 1)
106
+ v = v.strip().strip('"').strip("'")
107
+ if v.startswith("[") or v.startswith("{"):
108
+ continue # skip list/dict meta
109
+ meta[k.strip()] = v
110
+ text = text[end + 3 :]
111
+ return meta, text
112
+
113
+
114
+def build_sidebar(active_path: str) -> str:
115
+ parts = []
116
+ for group, items in SIDEBAR:
117
+ parts.append(f'<div class="ja-nav-group">{html_mod.escape(group)}</div>')
118
+ for label, path in items:
119
+ cls = ' class="active"' if path == active_path else ""
120
+ parts.append(f'<a href="{path}"{cls}>{html_mod.escape(label)}</a>')
121
+ return "\n".join(parts)
122
+
123
+
124
+def build_toc(html: str) -> str:
125
+ """Extract h2/h3 headings from rendered HTML for the right-side TOC."""
126
+ links = []
127
+ for m in re.finditer(r"<h([23])[^>]*>(.*?)</h\1>", html, re.S):
128
+ level, title = m.group(1), re.sub(r"<[^>]+>", "", m.group(2))
129
+ slug = re.sub(r"[^a-z0-9]+", "-", title.lower()).strip("-")
130
+ links.append(f'<a class="h{level}" href="#{slug}">{html_mod.escape(title)}</a>')
131
+ return "\n".join(links)
132
+
133
+
134
+def slugify_headers(html: str) -> str:
135
+ """Add id attributes to h2/h3 for TOC anchors."""
136
+ def repl(m):
137
+ level, attrs, title = m.group(1), m.group(2), m.group(3)
138
+ slug = re.sub(r"[^a-z0-9]+", "-", title.lower()).strip("-")
139
+ return f'<h{level} id="{slug}"{attrs}>{title}</h{level}>'
140
+ return re.sub(r"<h([23])([^>]*)>(.*?)</h\1>", repl, html, flags=re.S)
141
+
142
+
143
+def md_to_html(md_text: str) -> str:
144
+ return markdown.markdown(
145
+ md_text,
146
+ extensions=["fenced_code", "tables", "codehilite", "toc", "attr_list"],
147
+ )
148
+
149
+
150
+def render_page(meta: dict, body: str, path: str) -> str:
151
+ content_html = md_to_html(body)
152
+ content_html = slugify_headers(content_html)
153
+ title = meta.get("title", "JustAPI")
154
+ description = meta.get("description", "JustAPI β€” Python web framework with a Rust core.")
155
+ page = TEMPLATE
156
+ page = page.replace("{{ title }}", html_mod.escape(title))
157
+ page = page.replace("{{ description }}", html_mod.escape(description))
158
+ page = page.replace("{{ content }}", content_html)
159
+ page = page.replace("{{ sidebar }}", build_sidebar(path))
160
+ page = page.replace("{{ toc }}", build_toc(content_html))
161
+ return page
162
+
163
+
164
+def build() -> None:
165
+ if DIST.exists():
166
+ shutil.rmtree(DIST)
167
+ DIST.mkdir(parents=True)
168
+
169
+ # Static assets
170
+ (DIST / "css").mkdir(parents=True)
171
+ shutil.copy(ROOT / "src/main.css", DIST / "css/main.css")
172
+ shutil.copytree(ROOT / "css", DIST / "css/webtui")
173
+ (DIST / "js").mkdir(parents=True)
174
+ shutil.copy(ROOT / "src/main.js", DIST / "js/main.js")
175
+
176
+ count = 0
177
+ for md_file in sorted(CONTENT.rglob("*.md*")):
178
+ rel = md_file.relative_to(CONTENT)
179
+ if rel.name == "index.mdx":
180
+ continue # landing page handled separately
181
+ meta, body = parse_frontmatter(md_file.read_text())
182
+ url_path = "/" + str(rel.with_suffix("")).replace("\\", "/") + "/"
183
+ out = DIST / str(rel).replace(".mdx", "").replace(".md", "") / "index.html"
184
+ out.parent.mkdir(parents=True, exist_ok=True)
185
+ out.write_text(render_page(meta, body, url_path))
186
+ count += 1
187
+
188
+ # Landing page (hand-built, no frontmatter)
189
+ landing = (ROOT / "templates/landing.html").read_text()
190
+ landing = landing.replace("{{ sidebar }}", build_sidebar("/"))
191
+ landing = landing.replace("{{ toc }}", "")
192
+ (DIST / "index.html").write_text(landing)
193
+
194
+ print(f"built {count} docs pages + landing β†’ {DIST}")
195
+
196
+
197
+if __name__ == "__main__":
198
+ build()
0
199
β–Όwebtui/css/base.css
+1-0
1
+@layer base{:root{--background0: #fff;--background1: #ddd;--background2: #bbb;--background3: #999;--foreground0: #000;--foreground1: #444;--foreground2: #888;--font-size: 16px;--line-height: 1.3;--font-weight-bold: 700;--font-weight-normal: 400;--font-family: monospace;--box-border-color: var(--foreground0)}[data-webtui-theme=dark]{--background0: #000;--background1: #222;--background2: #444;--background3: #666;--foreground0: #fff;--foreground1: #ccc;--foreground2: #999}body,html{background-color:var(--background0);color:var(--foreground0);font-family:var(--font-family);font-size:var(--font-size);font-weight:var(--font-weight-normal);line-height:var(--line-height, 1.5);font-variant-ligatures:common-ligatures;word-break:break-all}*{box-sizing:border-box;margin:0;padding:0;outline:none}}
0
2
β–Όwebtui/css/box.css
+1-0
1
+@layer utils{@property --box-rounded-radius{syntax: "<length>"; inherits: false; initial-value: 4px;}@property --box-border-width{syntax: "<length>"; inherits: false; initial-value: 2px;}@property --box-double-border-width{syntax: "<length>"; inherits: false; initial-value: 1px;}[box-=square],[box-=round],[box-=double]{position:relative;isolation:isolate;padding:1lh 1ch}[box-=square]:before,[box-=round]:before,[box-=double]:before{content:"";position:absolute;top:50%;left:50%;translate:-50% -50%;width:calc(100% - 1ch - var(--box-border-width));height:calc(100% - 1lh - var(--box-border-width));border:solid var(--box-border-width) var(--box-border-color);z-index:-1}[box-=square][box-=round]:before,[box-=round][box-=round]:before,[box-=double][box-=round]:before{border-radius:var(--box-rounded-radius)}[box-=square][box-=round]:after,[box-=round][box-=round]:after,[box-=double][box-=round]:after{border-radius:calc(var(--box-rounded-radius) + 2px)}[box-=square][box-=double]:before,[box-=round][box-=double]:before,[box-=double][box-=double]:before{border-width:var(--box-double-border-width)}[box-=square][box-=double]:after,[box-=round][box-=double]:after,[box-=double][box-=double]:after{content:"";position:absolute;top:50%;left:50%;translate:-50% -50%;width:calc(100% - 1ch + var(--box-double-border-width) * 2);height:calc(100% - 1lh + var(--box-double-border-width) * 2);border:solid var(--box-double-border-width) var(--box-border-color);z-index:-1}[box-=square][shear-=both],[box-=round][shear-=both],[box-=double][shear-=both]{padding-top:0;padding-bottom:0}[box-=square][shear-=top],[box-=round][shear-=top],[box-=double][shear-=top]{padding-top:0}[box-=square][shear-=bottom],[box-=round][shear-=bottom],[box-=double][shear-=bottom]{padding-bottom:0}}
0
2
β–Όwebtui/css/catppuccin.css
+1-0
1
+@layer base{[data-webtui-theme=catppuccin],[data-webtui-theme=catppuccin-mocha]{--rosewater: #f5e0dc;--flamingo: #f2cdcd;--pink: #f5c2e7;--mauve: #cba6f7;--red: #f38ba8;--maroon: #eba0ac;--peach: #fab387;--yellow: #f9e2af;--green: #a6e3a1;--teal: #94e2d5;--sky: #89dceb;--sapphire: #74c7ec;--blue: #89b4fa;--lavender: #b4befe;--text: #cdd6f4;--subtext1: #bac2de;--subtext0: #a6adc8;--overlay2: #9399b2;--overlay1: #7f849c;--overlay0: #6c7086;--surface2: #585b70;--surface1: #45475a;--surface0: #313244;--base: #1e1e2e;--mantle: #181825;--crust: #11111b}[data-webtui-theme=catppuccin-macchiato]{--rosewater: #f4dbd6;--flamingo: #f0c6c6;--pink: #f5bde6;--mauve: #c6a0f6;--red: #ed8796;--maroon: #ee99a0;--peach: #f5a97f;--yellow: #eed49f;--green: #a6da95;--teal: #8bd5ca;--sky: #91d7e3;--sapphire: #7dc4e4;--blue: #8aadf4;--lavender: #b7bdf8;--text: #cad3f5;--subtext1: #b8c0e0;--subtext0: #a5adcb;--overlay2: #939ab7;--overlay1: #8087a2;--overlay0: #6e738d;--surface2: #5b6078;--surface1: #494d64;--surface0: #363a4f;--base: #24273a;--mantle: #1e2030;--crust: #181926}[data-webtui-theme=catppuccin-frappe]{--rosewater: #f2d5cf;--flamingo: #eebebe;--pink: #f4b8e4;--mauve: #ca9ee6;--red: #e78284;--maroon: #ea999c;--peach: #ef9f76;--yellow: #e5c890;--green: #a6d189;--teal: #81c8be;--sky: #99d1db;--sapphire: #85c1dc;--blue: #8caaee;--lavender: #babbf1;--text: #c6d0f5;--subtext1: #b5bfe2;--subtext0: #a5adce;--overlay2: #949cbb;--overlay1: #838ba7;--overlay0: #737994;--surface2: #626880;--surface1: #51576d;--surface0: #414559;--base: #303446;--mantle: #292c3c;--crust: #232634}[data-webtui-theme=catppuccin-latte]{--rosewater: #dc8a78;--flamingo: #dd7878;--pink: #ea76cb;--mauve: #8839ef;--red: #d20f39;--maroon: #e64553;--peach: #fe640b;--yellow: #df8e1d;--green: #40a02b;--teal: #179299;--sky: #04a5e5;--sapphire: #209fb5;--blue: #1e66f5;--lavender: #7287fd;--text: #4c4f69;--subtext1: #5c5f77;--subtext0: #6c6f85;--overlay2: #7c7f93;--overlay1: #8c8fa1;--overlay0: #9ca0b0;--surface2: #acb0be;--surface1: #bcc0cc;--surface0: #ccd0da;--base: #eff1f5;--mantle: #e6e9ef;--crust: #dce0e8}[data-webtui-theme|=catppuccin]{--background0: var(--base);--background1: var(--surface0);--background2: var(--surface1);--background3: var(--surface2);--foreground0: var(--text);--foreground1: var(--subtext1);--foreground2: var(--overlay2)}}@layer components{[data-webtui-theme|=catppuccin] button[variant-=rosewater]{--button-primary: var(--rosewater);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=flamingo]{--button-primary: var(--flamingo);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=pink]{--button-primary: var(--pink);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=mauve]{--button-primary: var(--mauve);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=red]{--button-primary: var(--red);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=maroon]{--button-primary: var(--maroon);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=peach]{--button-primary: var(--peach);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=yellow]{--button-primary: var(--yellow);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=green]{--button-primary: var(--green);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=teal]{--button-primary: var(--teal);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=sky]{--button-primary: var(--sky);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=sapphire]{--button-primary: var(--sapphire);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=blue]{--button-primary: var(--blue);--button-secondary: var(--background0)}[data-webtui-theme|=catppuccin] button[variant-=lavender]{--button-primary: var(--lavender);--button-secondary: var(--background0)}}@layer components{[data-webtui-theme|=catppuccin] h1{color:var(--red)}[data-webtui-theme|=catppuccin] h2{color:var(--peach)}[data-webtui-theme|=catppuccin] h3{color:var(--yellow)}[data-webtui-theme|=catppuccin] h4{color:var(--green)}[data-webtui-theme|=catppuccin] h5{color:var(--blue)}[data-webtui-theme|=catppuccin] h6{color:var(--lavender)}[data-webtui-theme|=catppuccin] p a,[data-webtui-theme|=catppuccin] blockquote a,[data-webtui-theme|=catppuccin] li a,[data-webtui-theme|=catppuccin] [is-~=typography-block] a{color:var(--sky);text-decoration:underline}[data-webtui-theme|=catppuccin] p a:hover,[data-webtui-theme|=catppuccin] blockquote a:hover,[data-webtui-theme|=catppuccin] li a:hover,[data-webtui-theme|=catppuccin] [is-~=typography-block] a:hover{color:var(--blue)}[data-webtui-theme|=catppuccin] p code,[data-webtui-theme|=catppuccin] blockquote code,[data-webtui-theme|=catppuccin] li code,[data-webtui-theme|=catppuccin] [is-~=typography-block] code{color:var(--red)}}@layer components{[data-webtui-theme|=catppuccin] [is-~=badge][variant-=rosewater]{--badge-color: var(--rosewater);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=flamingo]{--badge-color: var(--flamingo);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=pink]{--badge-color: var(--pink);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=mauve]{--badge-color: var(--mauve);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=red]{--badge-color: var(--red);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=maroon]{--badge-color: var(--maroon);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=peach]{--badge-color: var(--peach);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=yellow]{--badge-color: var(--yellow);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=green]{--badge-color: var(--green);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=teal]{--badge-color: var(--teal);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=sky]{--badge-color: var(--sky);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=sapphire]{--badge-color: var(--sapphire);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=blue]{--badge-color: var(--blue);--badge-text: var(--background0)}[data-webtui-theme|=catppuccin] [is-~=badge][variant-=lavender]{--badge-color: var(--lavender);--badge-text: var(--background0)}}@layer components{[data-webtui-theme|=catppuccin] mark[bg-=rosewater],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=rosewater]{background-color:var(--rosewater)}[data-webtui-theme|=catppuccin] mark[bg-=flamingo],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=flamingo]{background-color:var(--flamingo)}[data-webtui-theme|=catppuccin] mark[bg-=pink],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=pink]{background-color:var(--pink)}[data-webtui-theme|=catppuccin] mark[bg-=mauve],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=mauve]{background-color:var(--mauve)}[data-webtui-theme|=catppuccin] mark[bg-=red],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=red]{background-color:var(--red)}[data-webtui-theme|=catppuccin] mark[bg-=maroon],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=maroon]{background-color:var(--maroon)}[data-webtui-theme|=catppuccin] mark[bg-=peach],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=peach]{background-color:var(--peach)}[data-webtui-theme|=catppuccin] mark[bg-=yellow],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=yellow]{background-color:var(--yellow)}[data-webtui-theme|=catppuccin] mark[bg-=green],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=green]{background-color:var(--green)}[data-webtui-theme|=catppuccin] mark[bg-=teal],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=teal]{background-color:var(--teal)}[data-webtui-theme|=catppuccin] mark[bg-=sky],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=sky]{background-color:var(--sky)}[data-webtui-theme|=catppuccin] mark[bg-=sapphire],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=sapphire]{background-color:var(--sapphire)}[data-webtui-theme|=catppuccin] mark[bg-=blue],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=blue]{background-color:var(--blue)}[data-webtui-theme|=catppuccin] mark[bg-=lavender],[data-webtui-theme|=catppuccin] [is-~=mark][bg-=lavender]{background-color:var(--lavender)}[data-webtui-theme|=catppuccin] mark[fg-=rosewater],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=rosewater]{color:var(--rosewater)}[data-webtui-theme|=catppuccin] mark[fg-=flamingo],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=flamingo]{color:var(--flamingo)}[data-webtui-theme|=catppuccin] mark[fg-=pink],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=pink]{color:var(--pink)}[data-webtui-theme|=catppuccin] mark[fg-=mauve],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=mauve]{color:var(--mauve)}[data-webtui-theme|=catppuccin] mark[fg-=red],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=red]{color:var(--red)}[data-webtui-theme|=catppuccin] mark[fg-=maroon],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=maroon]{color:var(--maroon)}[data-webtui-theme|=catppuccin] mark[fg-=peach],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=peach]{color:var(--peach)}[data-webtui-theme|=catppuccin] mark[fg-=yellow],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=yellow]{color:var(--yellow)}[data-webtui-theme|=catppuccin] mark[fg-=green],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=green]{color:var(--green)}[data-webtui-theme|=catppuccin] mark[fg-=teal],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=teal]{color:var(--teal)}[data-webtui-theme|=catppuccin] mark[fg-=sky],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=sky]{color:var(--sky)}[data-webtui-theme|=catppuccin] mark[fg-=sapphire],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=sapphire]{color:var(--sapphire)}[data-webtui-theme|=catppuccin] mark[fg-=blue],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=blue]{color:var(--blue)}[data-webtui-theme|=catppuccin] mark[fg-=lavender],[data-webtui-theme|=catppuccin] [is-~=mark][fg-=lavender]{color:var(--lavender)}}
0
2
β–Όwebtui/css/components/accordion.css
+1-0
1
+@layer components{details[is-~=accordion][open]::details-content{padding-left:2ch}details[is-~=accordion]>summary:before{content:"Λƒ "}details[is-~=accordion]>summary::-webkit-details-marker{display:none}details[is-~=accordion]>summary::marker{content:"";display:none}details[is-~=accordion]>summary:focus{text-decoration:underline}details[is-~=accordion][open] summary:before{content:"Λ… "}}
0
2
β–Όwebtui/css/components/badge.css
+1-0
1
+@layer components{[is-~=badge]{color:var(--badge-text);background-image:linear-gradient(90deg,transparent 0,transparent calc(1ch - 1px),var(--badge-color) calc(1ch - 1px),var(--badge-color) calc(100% - 1ch + 1px),transparent calc(100% - 1ch + 1px),transparent);display:inline-flex;vertical-align:top;white-space:nowrap;height:1lh;line-height:1lh}[is-~=badge][variant-=foreground0],[is-~=badge]:not([variant-]){--badge-color: var(--foreground0);--badge-text: var(--background0)}[is-~=badge][variant-=foreground1]{--badge-color: var(--foreground1);--badge-text: var(--background0)}[is-~=badge][variant-=foreground2]{--badge-color: var(--foreground2);--badge-text: var(--background0)}[is-~=badge][variant-=background0]{--badge-color: var(--background0);--badge-text: var(--foreground0)}[is-~=badge][variant-=background1]{--badge-color: var(--background1);--badge-text: var(--foreground0)}[is-~=badge][variant-=background2]{--badge-color: var(--background2);--badge-text: var(--foreground0)}[is-~=badge][variant-=background3]{--badge-color: var(--background3);--badge-text: var(--foreground0)}[is-~=badge]:before,[is-~=badge]:after{content:"";width:1ch;height:1lh;display:inline-block;vertical-align:middle;background-color:var(--badge-color);overflow:hidden}[is-~=badge][cap-^=round]:before{border-top-left-radius:1lh;border-bottom-left-radius:1lh}[is-~=badge][cap-$=round]:after{border-top-right-radius:1lh;border-bottom-right-radius:1lh}[is-~=badge][cap-^=triangle]:before{clip-path:polygon(100% 0,100% 100%,0 50%)}[is-~=badge][cap-$=triangle]:after{clip-path:polygon(0 0,0 100%,100% 50%)}[is-~=badge][cap-^=slant-top]:before{clip-path:polygon(0 0,100% 0,100% 100%)}[is-~=badge][cap-$=slant-top]:after{clip-path:polygon(0 0,100% 0,0 100%)}[is-~=badge][cap-^=slant-bottom]:before{clip-path:polygon(100% 0,100% 100%,0 100%)}[is-~=badge][cap-$=slant-bottom]:after{clip-path:polygon(0 0,100% 100%,0 100%)}[is-~=badge][cap-^=ribbon]:before{clip-path:polygon(0 0,100% 0,100% 100%,0 100%,100% 50%)}[is-~=badge][cap-$=ribbon]:after{clip-path:polygon(0 0,100% 0,0% 50%,100% 100%,0% 100%)}}
0
2
β–Όwebtui/css/components/button.css
+1-0
1
+@layer components{button,input[type=button],input[type=submit],input[type=reset],[is-~=button]{--button-primary: var(--foreground0);--button-secondary: var(--background0);--mapped-primary: var(--button-primary);--mapped-secondary: var(--button-secondary);color:var(--mapped-secondary);background-color:var(--mapped-primary);font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);display:inline-flex;align-items:center;justify-content:center;border:none;padding:0 2ch;outline:none}button[box-],input[type=button][box-],input[type=submit][box-],input[type=reset][box-],[is-~=button][box-]{color:var(--mapped-primary);background-color:transparent;--box-border-color: var(--mapped-primary)}button:focus,input[type=button]:focus,input[type=submit]:focus,input[type=reset]:focus,[is-~=button]:focus{font-weight:700;text-decoration:underline}button:active,input[type=button]:active,input[type=submit]:active,input[type=reset]:active,[is-~=button]:active{--mapped-primary: var(--mapped-secondary);--mapped-secondary: var(--mapped-primary)}button:disabled,input[type=button]:disabled,input[type=submit]:disabled,input[type=reset]:disabled,[is-~=button]:disabled{text-decoration:line-through;pointer-events:none}button[size-=small],input[type=button][size-=small],input[type=submit][size-=small],input[type=reset][size-=small],[is-~=button][size-=small]{padding:0 1ch;height:1lh}button:not([size-]):not([box-]),button[size-=default]:not([box-]),input[type=button]:not([size-]):not([box-]),input[type=button][size-=default]:not([box-]),input[type=submit]:not([size-]):not([box-]),input[type=submit][size-=default]:not([box-]),input[type=reset]:not([size-]):not([box-]),input[type=reset][size-=default]:not([box-]),[is-~=button]:not([size-]):not([box-]),[is-~=button][size-=default]:not([box-]){background-color:transparent;background-image:linear-gradient(to bottom,transparent 0,transparent .5lh,var(--mapped-primary) .5lh,var(--mapped-primary) calc(100% - .5lh),transparent calc(100% - .5lh),transparent)}button[size-=large],button[size-=default],button:not([size-]),input[type=button][size-=large],input[type=button][size-=default],input[type=button]:not([size-]),input[type=submit][size-=large],input[type=submit][size-=default],input[type=submit]:not([size-]),input[type=reset][size-=large],input[type=reset][size-=default],input[type=reset]:not([size-]),[is-~=button][size-=large],[is-~=button][size-=default],[is-~=button]:not([size-]){height:3lh}button[variant-=foreground0],button:not([variant-]),input[type=button][variant-=foreground0],input[type=button]:not([variant-]),input[type=submit][variant-=foreground0],input[type=submit]:not([variant-]),input[type=reset][variant-=foreground0],input[type=reset]:not([variant-]),[is-~=button][variant-=foreground0],[is-~=button]:not([variant-]){--button-primary: var(--foreground0);--button-secondary: var(--background0)}button[variant-=foreground1],input[type=button][variant-=foreground1],input[type=submit][variant-=foreground1],input[type=reset][variant-=foreground1],[is-~=button][variant-=foreground1]{--button-primary: var(--foreground1);--button-secondary: var(--background0)}button[variant-=foreground2],input[type=button][variant-=foreground2],input[type=submit][variant-=foreground2],input[type=reset][variant-=foreground2],[is-~=button][variant-=foreground2]{--button-primary: var(--foreground2);--button-secondary: var(--background0)}button[variant-=background0],input[type=button][variant-=background0],input[type=submit][variant-=background0],input[type=reset][variant-=background0],[is-~=button][variant-=background0]{--button-primary: var(--background0);--button-secondary: var(--foreground0)}button[variant-=background1],input[type=button][variant-=background1],input[type=submit][variant-=background1],input[type=reset][variant-=background1],[is-~=button][variant-=background1]{--button-primary: var(--background1);--button-secondary: var(--foreground0)}button[variant-=background2],input[type=button][variant-=background2],input[type=submit][variant-=background2],input[type=reset][variant-=background2],[is-~=button][variant-=background2]{--button-primary: var(--background2);--button-secondary: var(--foreground0)}button[variant-=background3],input[type=button][variant-=background3],input[type=submit][variant-=background3],input[type=reset][variant-=background3],[is-~=button][variant-=background3]{--button-primary: var(--background3);--button-secondary: var(--foreground0)}}
0
2
β–Όwebtui/css/components/checkbox.css
+1-0
1
+@layer components{input[type=checkbox]:not([is-=switch]){appearance:none;-webkit-appearance:none;-moz-appearance:none;display:inline-block;position:relative;width:3ch;min-width:initial;vertical-align:text-top;color:var(--foreground2);font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);outline:none}input[type=checkbox]:not([is-=switch]):before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;display:inline-flex;align-items:center;justify-content:center;width:3ch;height:1lh;background:var(--background1)}input[type=checkbox]:not([is-=switch]):checked:before{content:"X"}label:has(input[type=checkbox]:not([is-=switch])){display:inline-flex;align-items:flex-start;gap:1ch;max-width:fit-content}label:has(input[type=checkbox]:not([is-=switch]):focus){font-weight:700;text-decoration:underline}input[type=checkbox]:not([is-=switch]):disabled{color:var(--foreground2);pointer-events:none}label:has(input[type=checkbox]:not([is-=switch]):disabled){color:var(--foreground2);text-decoration:line-through}}
0
2
β–Όwebtui/css/components/dialog.css
+1-0
1
+@layer components{@property --dialog-offset-x{syntax: "<length>"; inherits: true; initial-value: 0;}@property --dialog-offset-y{syntax: "<length>"; inherits: true; initial-value: 0;}@property --dialog-max-width{syntax: "<length>"; inherits: true; initial-value: 0;}@property --dialog-max-height{syntax: "<length>"; inherits: true; initial-value: 0;}dialog{--dialog-offset-top: auto;--dialog-offset-right: auto;--dialog-offset-bottom: auto;--dialog-offset-left: auto;background-color:var(--background0);color:var(--foreground0);font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);border:none;outline:none;position:absolute;max-width:var(--dialog-max-width);max-height:var(--dialog-max-height);overflow:hidden;inset:var(--dialog-offset-top) var(--dialog-offset-right) var(--dialog-offset-bottom) var(--dialog-offset-left)}dialog[open],dialog:popover-open{display:flex;flex-direction:column}dialog[size-=full]{--dialog-max-width: 100vw;--dialog-max-height: 100vh}dialog[size-=default],dialog:not([size-]){--dialog-max-width: 64ch;--dialog-max-height: 24lh}dialog[size-=small]{--dialog-max-width: 48ch;--dialog-max-height: 16lh}dialog[container-^=auto],dialog:not([container-]){width:auto}dialog[container-^=fill]{width:calc(100% - var(--dialog-offset-x) * 2)}dialog[container-$=auto],dialog:not([container-]){height:auto}dialog[container-$=fill]{height:calc(100% - var(--dialog-offset-y) * 2)}dialog[position-^=start]{--dialog-offset-left: var(--dialog-offset-x);--dialog-offset-right: auto}dialog[position-^=end]{--dialog-offset-left: auto;--dialog-offset-right: var(--dialog-offset-x)}dialog[position-^=center],dialog:not([position-]){--dialog-offset-left: 50%;translate:-50%}dialog[position-$=start]{--dialog-offset-top: var(--dialog-offset-y);--dialog-offset-bottom: auto}dialog[position-$=end]{--dialog-offset-top: auto;--dialog-offset-bottom: var(--dialog-offset-y)}dialog[position-$=center],dialog:not([position-]){--dialog-offset-top: 50%;transform:translateY(-50%)}}
0
2
β–Όwebtui/css/components/input.css
+1-0
1
+@layer components{input[type=text],input[type=email],input[type=password],input[type=search],input[type=tel],input[type=url],input[type=number],input[type=date],input[type=datetime],input[type=datetime-local],input[type=month],input[type=time],input[type=week],input:not([type]),[is-~=input]{background-color:var(--background1);color:var(--foreground0);font-family:var(--font-family);font-size:var(--font-size);font-weight:var(--font-weight-normal);line-height:var(--line-height);outline:none;border:none;min-width:24ch}input[type=text]::placeholder,input[type=email]::placeholder,input[type=password]::placeholder,input[type=search]::placeholder,input[type=tel]::placeholder,input[type=url]::placeholder,input[type=number]::placeholder,input[type=date]::placeholder,input[type=datetime]::placeholder,input[type=datetime-local]::placeholder,input[type=month]::placeholder,input[type=time]::placeholder,input[type=week]::placeholder,input:not([type])::placeholder,[is-~=input]::placeholder{color:var(--foreground2)}input[type=text]:not([size-]),input[type=email]:not([size-]),input[type=password]:not([size-]),input[type=search]:not([size-]),input[type=tel]:not([size-]),input[type=url]:not([size-]),input[type=number]:not([size-]),input[type=date]:not([size-]),input[type=datetime]:not([size-]),input[type=datetime-local]:not([size-]),input[type=month]:not([size-]),input[type=time]:not([size-]),input[type=week]:not([size-]),input:not([type]):not([size-]),[is-~=input]:not([size-]){height:1lh;padding:0 1ch}input[type=text][size-=small],input[type=email][size-=small],input[type=password][size-=small],input[type=search][size-=small],input[type=tel][size-=small],input[type=url][size-=small],input[type=number][size-=small],input[type=date][size-=small],input[type=datetime][size-=small],input[type=datetime-local][size-=small],input[type=month][size-=small],input[type=time][size-=small],input[type=week][size-=small],input:not([type])[size-=small],[is-~=input][size-=small]{height:1lh;padding:0}input[type=text][size-=large],input[type=email][size-=large],input[type=password][size-=large],input[type=search][size-=large],input[type=tel][size-=large],input[type=url][size-=large],input[type=number][size-=large],input[type=date][size-=large],input[type=datetime][size-=large],input[type=datetime-local][size-=large],input[type=month][size-=large],input[type=time][size-=large],input[type=week][size-=large],input:not([type])[size-=large],[is-~=input][size-=large]{height:3lh;padding:1lh 2ch}}
0
2
β–Όwebtui/css/components/mark.css
+1-0
1
+@layer components{mark,[is-~=mark]{background-color:inherit;color:inherit}mark[bg-][bg-=background0],[is-~=mark][bg-][bg-=background0]{background-color:var(--background0)}mark[bg-][bg-=background1],[is-~=mark][bg-][bg-=background1]{background-color:var(--background1)}mark[bg-][bg-=background2],[is-~=mark][bg-][bg-=background2]{background-color:var(--background2)}mark[bg-][bg-=background3],[is-~=mark][bg-][bg-=background3]{background-color:var(--background3)}mark[bg-][bg-=foreground0],[is-~=mark][bg-][bg-=foreground0]{background-color:var(--foreground0)}mark[bg-][bg-=foreground1],[is-~=mark][bg-][bg-=foreground1]{background-color:var(--foreground1)}mark[bg-][bg-=foreground2],[is-~=mark][bg-][bg-=foreground2]{background-color:var(--foreground2)}mark[fg-][fg-=background0],[is-~=mark][fg-][fg-=background0]{color:var(--background0)}mark[fg-][fg-=background1],[is-~=mark][fg-][fg-=background1]{color:var(--background1)}mark[fg-][fg-=background2],[is-~=mark][fg-][fg-=background2]{color:var(--background2)}mark[fg-][fg-=background3],[is-~=mark][fg-][fg-=background3]{color:var(--background3)}mark[fg-][fg-=foreground0],[is-~=mark][fg-][fg-=foreground0]{color:var(--foreground0)}mark[fg-][fg-=foreground1],[is-~=mark][fg-][fg-=foreground1]{color:var(--foreground1)}mark[fg-][fg-=foreground2],[is-~=mark][fg-][fg-=foreground2]{color:var(--foreground2)}}
0
2
β–Όwebtui/css/components/popover.css
+1-0
1
+@layer components{@property --popover-offset-x{syntax: "<length>"; inherits: true; initial-value: 0;}@property --popover-offset-y{syntax: "<length>"; inherits: true; initial-value: 0;}details[is-~=popover]{--popover-backdrop-color: transparent;position:relative}details[is-~=popover] summary:focus{text-decoration:underline}details[is-~=popover] summary::-webkit-details-marker{display:none}details[is-~=popover] summary::marker{content:"";display:none}details[is-~=popover][open] summary:before{content:"";display:block;width:100vw;height:100vh;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1;background-color:var(--popover-backdrop-color)}details[is-~=popover][open] summary+*{position:absolute;z-index:2}details[is-~=popover][open][position-~=baseline-left] summary+*,details[is-~=popover][open]:not([position-]) summary+*{left:0%}details[is-~=popover][open][position-~=baseline-right] summary+*{left:100%;translate:-100%}details[is-~=popover][open][position-~=left] summary+*{left:calc(var(--popover-offset-x) * -1);translate:-100%}details[is-~=popover][open][position-~=right] summary+*{left:calc(100% + var(--popover-offset-x))}details[is-~=popover][open][position-~=baseline-top] summary+*{top:0%}details[is-~=popover][open][position-~=baseline-bottom] summary+*{top:100%;transform:translateY(-100%)}details[is-~=popover][open][position-~=top] summary+*{top:calc(var(--popover-offset-y) * -1);transform:translateY(-100%)}details[is-~=popover][open][position-~=bottom] summary+*,details[is-~=popover][open]:not([position-]) summary+*{top:calc(100% + var(--popover-offset-y))}}
0
2
β–Όwebtui/css/components/pre.css
+1-0
1
+@layer components{pre,[is-~=pre]{--pre-background: var(--background1);background-color:var(--pre-background);white-space:pre-wrap;color:var(--foreground0);font-family:var(--font-family);font-size:var(--font-size);font-weight:var(--font-weight-normal);line-height:var(--line-height);outline:none;border:none;padding:1lh 1ch}pre[size-=small],[is-~=pre][size-=small]{padding:0 1ch}pre:not([size-]),[is-~=pre]:not([size-]){background-color:transparent;background-image:linear-gradient(to bottom,transparent,transparent .5lh,var(--pre-background) .5lh,var(--pre-background) calc(100% - .5lh),transparent calc(100% - .5lh),transparent)}}
0
2
β–Όwebtui/css/components/progress.css
+1-0
1
+@layer components{@property --progress-value{syntax: "<number>"; inherits: true; initial-value: 0;}@property --progress-max{syntax: "<number>"; inherits: true; initial-value: 100;}[is-~=progress]{--progress-value-background: var(--foreground0);--progress-value-color: var(--foreground0);--progress-value-content: "";--progress-empty-background: var(--background1);--progress-empty-color: var(--foreground0);--progress-empty-content: "";width:round(down,100%,1ch);height:1lh;margin:0;background-color:var(--progress-background-empty);color:var(--progress-color-empty);color:transparent;position:relative;vertical-align:top;display:inline-flex}[is-~=progress]:before{content:var(--progress-value-content);position:absolute;top:0;left:0;height:1lh;vertical-align:top;color:var(--progress-value-color);width:round(nearest,calc((var(--progress-value) / var(--progress-max)) * 100%),1ch);overflow:hidden;white-space:nowrap;background-color:var(--progress-value-background);z-index:1}[is-~=progress]:after{content:var(--progress-empty-content);position:absolute;top:0;right:0;height:1lh;vertical-align:top;color:var(--progress-empty-color);overflow:hidden;width:round(up,calc(100% - (var(--progress-value) / var(--progress-max) * 100%)),1ch);white-space:nowrap;background-color:var(--progress-empty-background);z-index:0}}
0
2
β–Όwebtui/css/components/radio.css
+1-0
1
+@layer components{input[type=radio]{appearance:none;-webkit-appearance:none;-moz-appearance:none;display:inline-block;position:relative;width:3ch;min-width:initial;vertical-align:text-top;color:var(--foreground2);font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);outline:none}input[type=radio]:before{content:"( )";position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;width:3ch;height:1lh;background:var(--background1)}input[type=radio]:checked:before{content:"(*)"}input[type=radio]:disabled{color:var(--foreground2);pointer-events:none}label:has(input[type=radio]){display:inline-flex;align-items:flex-start;gap:1ch;max-width:fit-content}label:has(input[type=radio]:focus){font-weight:700;text-decoration:underline}label:has(input[type=radio]:disabled){color:var(--foreground2);text-decoration:line-through}}
0
2
β–Όwebtui/css/components/range.css
+1-0
1
+@layer components{input[type=range]{--range-track-color: var(--background1);--range-thumb-color: var(--foreground2);--range-thumb-color-focus: var(--foreground0);--range-track-height: 2px;appearance:none;-webkit-appearance:none;-moz-appearance:none;background:transparent;outline:none;min-width:20ch;height:1lh;font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);position:relative}input[type=range]::-webkit-slider-runnable-track{-moz-appearance:none;appearance:none;-webkit-appearance:none;width:100%;height:var(--range-track-height);background:var(--range-track-color);border:none;outline:none}input[type=range]::-webkit-slider-thumb{-moz-appearance:none;appearance:none;-webkit-appearance:none;width:1ch;height:1lh;background:transparent;border:1px solid var(--range-thumb-color);border-radius:0;box-sizing:border-box;position:relative;top:50%;transform:translateY(-50%)}input[type=range]:not(:disabled):focus::-webkit-slider-thumb,input[type=range]:not(:disabled):active::-webkit-slider-thumb{background:var(--range-thumb-color-focus);border:none}input[type=range]::-moz-range-track{width:100%;height:var(--range-track-height);background:var(--range-track-color);border:none;outline:none}input[type=range]::-moz-range-thumb{width:1ch;height:1lh;background:transparent;border:1px solid var(--range-thumb-color);border-radius:0;box-sizing:border-box}input[type=range]:not(:disabled):focus::-moz-range-thumb,input[type=range]:not(:disabled):active::-moz-range-thumb{background:var(--range-thumb-color-focus);border:none}input[type=range]:disabled{--range-track-color: var(--background2);--range-thumb-color: var(--foreground2);pointer-events:none;opacity:.6}input[type=range][bar-=thick]{--range-track-height: 1lh}input[type=range][bar-=thick]::-webkit-slider-thumb{height:1lh}input[type=range][bar-=thick]::-moz-range-thumb{height:1lh}input[type=range][bar-=line]{--range-track-height: 1px}label:has(input[type=range]){display:inline-flex;align-items:center;gap:1ch;max-width:fit-content}label:has(input[type=range]:focus){font-weight:700;text-decoration:underline}label:has(input[type=range]:disabled){color:var(--foreground2);text-decoration:line-through}}
0
2
β–Όwebtui/css/components/separator.css
+1-0
1
+@layer components{@property --separator-width{syntax: "<length>"; inherits: true; initial-value: 2px;}[is-~=separator]{--separator-color: var(--box-border-color);--separator-background: transparent;display:inline-flex;position:relative}[is-~=separator][direction-=x],[is-~=separator][direction-=horizontal],[is-~=separator]:not([direction-]){height:1lh;background-image:linear-gradient(0deg,var(--separator-background) 0,var(--separator-background) calc(.5lh - var(--separator-width) / 2),var(--separator-color) calc(.5lh - (var(--separator-width) / 2)),var(--separator-color) calc(.5lh + (var(--separator-width) / 2)),var(--separator-background) calc(.5lh + (var(--separator-width) / 2)),var(--separator-background))}[is-~=separator][direction-=x][cap-^=bisect]:before,[is-~=separator][direction-=horizontal][cap-^=bisect]:before,[is-~=separator]:not([direction-])[cap-^=bisect]:before{left:-.5ch}[is-~=separator][direction-=x][cap-$=bisect]:after,[is-~=separator][direction-=horizontal][cap-$=bisect]:after,[is-~=separator]:not([direction-])[cap-$=bisect]:after{right:-.5ch}[is-~=separator][direction-=x][cap-^=edge]:not([cap-=edge]),[is-~=separator][direction-=horizontal][cap-^=edge]:not([cap-=edge]),[is-~=separator]:not([direction-])[cap-^=edge]:not([cap-=edge]){clip-path:inset(0 -.5ch 0 .5ch)}[is-~=separator][direction-=x][cap-$=edge]:not([cap-=edge]),[is-~=separator][direction-=horizontal][cap-$=edge]:not([cap-=edge]),[is-~=separator]:not([direction-])[cap-$=edge]:not([cap-=edge]){clip-path:inset(0 .5ch 0 -.5ch)}[is-~=separator][direction-=x][cap-=edge],[is-~=separator][direction-=horizontal][cap-=edge],[is-~=separator]:not([direction-])[cap-=edge]{clip-path:inset(0 .5ch)}[is-~=separator][direction-=y],[is-~=separator][direction-=vertical]{width:1ch;background-image:linear-gradient(to right,var(--separator-background) 0,var(--separator-background) calc(.5ch - (var(--separator-width) / 2)),var(--separator-color) calc(.5ch - (var(--separator-width) / 2)),var(--separator-color) calc(.5ch + (var(--separator-width) / 2)),var(--separator-background) calc(.5ch + (var(--separator-width) / 2)),var(--separator-background))}[is-~=separator][direction-=y][cap-^=bisect]:before,[is-~=separator][direction-=vertical][cap-^=bisect]:before{top:-.5lh}[is-~=separator][direction-=y][cap-$=bisect]:after,[is-~=separator][direction-=vertical][cap-$=bisect]:after{bottom:-.5lh}[is-~=separator][direction-=y][cap-^=edge]:not([cap-=edge]),[is-~=separator][direction-=vertical][cap-^=edge]:not([cap-=edge]){clip-path:inset(.5lh 0 -.5lh 0)}[is-~=separator][direction-=y][cap-$=edge]:not([cap-=edge]),[is-~=separator][direction-=vertical][cap-$=edge]:not([cap-=edge]){clip-path:inset(-.5lh 0 .5lh 0)}[is-~=separator][direction-=y][cap-=edge],[is-~=separator][direction-=vertical][cap-=edge]{clip-path:inset(.5lh 0)}[is-~=separator][cap-]:before,[is-~=separator][cap-]:after{content:"";position:absolute;width:1ch;height:1lh;background-image:inherit}[is-~=separator][variant-=foreground0]{--separator-color: var(--foreground0)}[is-~=separator][variant-=foreground1]{--separator-color: var(--foreground1)}[is-~=separator][variant-=foreground2]{--separator-color: var(--foreground2)}[is-~=separator][variant-=background0]{--separator-color: var(--background0)}[is-~=separator][variant-=background1]{--separator-color: var(--background1)}[is-~=separator][variant-=background2]{--separator-color: var(--background2)}[is-~=separator][variant-=background3]{--separator-color: var(--background3)}}
0
2
β–Όwebtui/css/components/spinner.css
+1-0
1
+@layer components{[is-=spinner]{--spinner-steps: 4;--spinner-chars: "/-\\|";--spinner-duration: 1s;width:1ch;height:1lh;overflow:hidden;position:relative;display:inline-block;vertical-align:top}[is-=spinner][variant-=dots]{--spinner-steps: 8;--spinner-chars: "⣷⣯⣟⑿Ⓙ⣻⣽⣾"}[is-=spinner][variant-=arrows]{--spinner-steps: 8;--spinner-chars: "β†β†–β†‘β†—β†’β†˜β†“β†™"}[is-=spinner][variant-=cross]{--spinner-steps: 2;--spinner-chars: "+x"}[is-=spinner][variant-=square]{--spinner-steps: 4;--spinner-chars: "β—°β—³β—²β—±"}[is-=spinner][variant-=pie]{--spinner-steps: 4;--spinner-chars: "β—΄β—·β—Άβ—΅"}[is-=spinner][variant-=half]{--spinner-steps: 4;--spinner-chars: "◐◓◑◒"}[is-=spinner][variant-=bar-vertical]{--spinner-steps: 12;--spinner-chars: "β–β–ƒβ–„β–…β–†β–‡β–ˆβ–‡β–†β–…β–„β–ƒ"}[is-=spinner][variant-=bar-horizontal]{--spinner-steps: 13;--spinner-chars: "β–‰β–Šβ–‹β–Œβ–β–Žβ–β–Žβ–β–Œβ–‹β–Šβ–‰"}[is-=spinner][speed-=slow]{--spinner-duration: 2s}[is-=spinner][speed-=medium],[is-=spinner][speed-=default],[is-=spinner]:not([speed-]){--spinner-duration: 1s}[is-=spinner][speed-=fast]{--spinner-duration: .5s}[is-=spinner]:before{content:var(--spinner-chars);position:absolute;top:0;left:0;color:inherit;white-space:nowrap;height:1lh;animation:spinner-pan var(--spinner-duration) steps(var(--spinner-steps)) infinite}[is-=spinner][direction-=reverse]:before{animation-direction:reverse}@keyframes spinner-pan{0%{translate:0ch}to{translate:-100%}}}
0
2
β–Όwebtui/css/components/switch.css
+1-0
1
+@layer components{input[type=checkbox][is-=switch]{--switch-thumb-color: var(--foreground2);--switch-track-color: var(--background1);--switch-thin-track-width: 2px;appearance:none;-webkit-appearance:none;-moz-appearance:none;display:inline-block;position:relative;width:4ch;height:1lh;min-width:initial;vertical-align:text-top;font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);background-color:transparent;outline:none}input[type=checkbox][is-=switch]:before{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);width:4ch;height:1lh;background:var(--switch-track-color)}input[type=checkbox][is-=switch]:after{content:"";position:absolute;left:0;width:2ch;height:1lh;background:var(--switch-thumb-color)}input[type=checkbox][is-=switch]:checked:after{left:unset;right:0;--switch-thumb-color: var(--foreground0)}input[type=checkbox][is-=switch]:disabled{--switch-thumb-color: var(--background2);pointer-events:none}input[type=checkbox][is-=switch]:disabled:checked:after{--switch-thumb-color: var(--background2)}input[type=checkbox][is-=switch][size-=small]{width:2ch}input[type=checkbox][is-=switch][size-=small]:before{width:2ch}input[type=checkbox][is-=switch][size-=small]:after{width:1ch}input[type=checkbox][is-=switch][bar-=thin]:before{height:.5lh}input[type=checkbox][is-=switch][bar-=line]:before{height:var(--switch-thin-track-width)}label:has(input[type=checkbox][is-=switch]){display:inline-flex;align-items:flex-start;gap:1ch;max-width:fit-content}label:has(input[type=checkbox][is-=switch]:focus){font-weight:700;text-decoration:underline}label:has(input[type=checkbox][is-=switch]:disabled){color:var(--foreground2);text-decoration:line-through}}
0
2
β–Όwebtui/css/components/table.css
+1-0
1
+@layer components{@property --table-border-width{syntax: "<length>"; inherits: true; initial-value: 2px;}table{--table-border-color: var(--box-border-color);border:none;outline:none;font-family:var(--font-family);font-size:var(--font-size);font-weight:var(--font-weight-normal);line-height:var(--line-height);text-align:left;position:relative;border-spacing:0 0}table th{font-weight:var(--font-weight-bold)}table th,table td{position:relative;padding-right:1ch}table th:last-of-type,table td:last-of-type{padding-right:0}table[divide-=vertical] th:not(:last-of-type):before,table[divide-=vertical] td:not(:last-of-type):before,table[divide-=both] th:not(:last-of-type):before,table[divide-=both] td:not(:last-of-type):before,table:not([divide-]) th:not(:last-of-type):before,table:not([divide-]) td:not(:last-of-type):before{content:"";position:absolute;right:calc(.5ch - var(--table-border-width) / 2);bottom:0;border-left:solid var(--table-border-width) var(--table-border-color);height:100%}table[box-] :is(table[divide-=vertical] th:not(:last-of-type)):before,table[box-] :is(table[divide-=vertical] td:not(:last-of-type)):before,table[box-] :is(table[divide-=both] th:not(:last-of-type)):before,table[box-] :is(table[divide-=both] td:not(:last-of-type)):before,table[box-] :is(table:not([divide-]) th:not(:last-of-type)):before,table[box-] :is(table:not([divide-]) td:not(:last-of-type)):before{bottom:calc(-.5lh - var(--table-border-width) / 2);border-left:solid var(--table-border-width) var(--table-border-color);height:calc(100% + 1lh)}table[divide-=horizontal] tr,table[divide-=both] tr,table:not([divide-]) tr{position:relative}table[divide-=horizontal] tr th,table[divide-=horizontal] tr td,table[divide-=both] tr th,table[divide-=both] tr td,table:not([divide-]) tr th,table:not([divide-]) tr td{padding-bottom:1lh}table[divide-=horizontal] tr th:after,table[divide-=horizontal] tr td:after,table[divide-=both] tr th:after,table[divide-=both] tr td:after,table:not([divide-]) tr th:after,table:not([divide-]) tr td:after{content:"";position:absolute;left:0;bottom:calc(.5lh - var(--table-border-width) / 2);width:100%;border-top:solid var(--table-border-width) var(--table-border-color)}table[box-] :is(table[divide-=horizontal] tr th):after,table[box-] :is(table[divide-=horizontal] tr td):after,table[box-] :is(table[divide-=both] tr th):after,table[box-] :is(table[divide-=both] tr td):after,table[box-] :is(table:not([divide-]) tr th):after,table[box-] :is(table:not([divide-]) tr td):after{left:calc(-.5ch - var(--table-border-width) / 2);width:calc(100% + 1ch);border-top:solid var(--table-border-width) var(--table-border-color)}table[divide-=horizontal] *:last-child tr:last-child th,table[divide-=horizontal] *:last-child tr:last-child td,table[divide-=both] *:last-child tr:last-child th,table[divide-=both] *:last-child tr:last-child td,table:not([divide-]) *:last-child tr:last-child th,table:not([divide-]) *:last-child tr:last-child td{padding-bottom:0}table[divide-=horizontal] *:last-child tr:last-child th:after,table[divide-=horizontal] *:last-child tr:last-child td:after,table[divide-=both] *:last-child tr:last-child th:after,table[divide-=both] *:last-child tr:last-child td:after,table:not([divide-]) *:last-child tr:last-child th:after,table:not([divide-]) *:last-child tr:last-child td:after{display:none}}
0
2
β–Όwebtui/css/components/textarea.css
+1-0
1
+@layer components{textarea,[is-~=textarea]{background-color:var(--background1);color:var(--foreground0);font-family:var(--font-family);font-size:var(--font-size);font-weight:var(--font-weight-normal);line-height:var(--line-height);outline:none;border:none;resize:vertical;min-width:24ch}textarea::placeholder,[is-~=textarea]::placeholder{color:var(--foreground2)}textarea:not([size-]),[is-~=textarea]:not([size-]){min-height:2lh;padding:0 1ch}textarea[size-=small],[is-~=textarea][size-=small]{min-height:2lh;padding:0}textarea[size-=large],[is-~=textarea][size-=large]{min-height:4lh;padding:1lh 2ch}}
0
2
β–Όwebtui/css/components/tooltip.css
+1-0
1
+@layer components{@property --tooltip-offset-x{syntax: "<length>"; inherits: true; initial-value: 0;}@property --tooltip-offset-y{syntax: "<length>"; inherits: true; initial-value: 0;}@property --tooltip-delay{syntax: "<time>"; inherits: true; initial-value: .5s;}[is-~=tooltip]{position:relative}[is-~=tooltip] [is-~=tooltip-content]{opacity:0;transition:all 0s linear 0s;transform:scale(0);overflow:hidden;position:absolute}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content]{opacity:1;z-index:2;transform:scale(1);transition-delay:var(--tooltip-delay)}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=baseline-left],[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content]:not([position-]),[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=baseline-left],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content]:not([position-]),[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=baseline-left],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content]:not([position-]){left:0%}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=baseline-right],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=baseline-right],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=baseline-right]{left:100%;translate:-100%}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=left],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=left],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=left]{left:calc(var(--tooltip-offset-x) * -1);translate:-100%}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=right],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=right],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=right]{left:calc(100% + var(--tooltip-offset-x))}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=baseline-top],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=baseline-top],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=baseline-top]{top:0%}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=baseline-bottom],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=baseline-bottom],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=baseline-bottom]{top:100%;transform:translateY(-100%)}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=top],[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content]:not([position-]),[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=top],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content]:not([position-]),[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=top],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content]:not([position-]){top:calc(var(--tooltip-offset-y) * -1);transform:translateY(-100%)}[is-~=tooltip]:has([is-~=tooltip-trigger]:hover)>[is-~=tooltip-content][position-~=bottom],[is-~=tooltip]:has([is-~=tooltip-trigger]:focus)>[is-~=tooltip-content][position-~=bottom],[is-~=tooltip]:has([is-~=tooltip-content]:hover)>[is-~=tooltip-content][position-~=bottom]{top:calc(100% + var(--tooltip-offset-y))}}
0
2
β–Όwebtui/css/components/typography.css
+1-0
1
+@layer components{h1,h2,h3,h4,h5,h6{font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);font-weight:var(--font-weight-bold)}h1 code,h2 code,h3 code,h4 code,h5 code,h6 code{color:inherit;background-color:var(--background1);padding:0 1ch}ol{list-style-position:inside}ol li>ol{margin-left:2ch}ul{list-style-type:none}ul li:before{color:var(--list-marker-color, inherit);content:"- "}ul[marker-=bullet] li:before{content:"β€’ "}ul[marker-~=tree] li:before{content:"β”œ "}ul[marker-~=tree]:not([marker-^=open]) li:first-of-type:before{content:"β”Œ "}ul[marker-~=tree]:not([marker-$=open]) li:last-of-type:before{content:"β”” "}ul[marker-~=round-tree] li:before{content:"β”œ "}ul[marker-~=round-tree]:not([marker-^=open]) li:first-of-type:before{content:"β•­ "}ul[marker-~=round-tree]:not([marker-$=open]) li:last-of-type:before{content:"β•° "}ul li>ul{margin-left:2ch}p,blockquote,li,[is-~=typography-block]{font-family:var(--font-family);font-size:var(--font-size);line-height:var(--line-height);color:var(--foreground1)}p:is(blockquote),blockquote:is(blockquote),li:is(blockquote),[is-~=typography-block]:is(blockquote){padding-left:2ch;position:relative}p:is(blockquote):before,blockquote:is(blockquote):before,li:is(blockquote):before,[is-~=typography-block]:is(blockquote):before{content:"";position:absolute;top:0;left:.5ch;width:var(--box-border-width, 2px);height:100%;translate:-50%;background-color:var( --blockquote-indent-color, var(--background2) )}p strong,blockquote strong,li strong,[is-~=typography-block] strong{font-weight:var(--font-weight-bold);color:var(--foreground0)}p a,blockquote a,li a,[is-~=typography-block] a{text-decoration:underline;color:var(--foreground2)}p code,blockquote code,li code,[is-~=typography-block] code{display:inline-block;background-color:var(--background1);padding-left:1ch;padding-right:1ch}}
0
2
β–Όwebtui/css/components/view.css
+1-0
1
+@layer components{[is-~=view]{position:relative}[is-~=view-content]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:round(down,100%,1ch);height:round(down,100%,1lh);overflow:hidden;word-break:break-all}}
0
2
β–Όwebtui/css/everforest.css
+1-0
1
+@layer base{[data-webtui-theme|=everforest-dark]{--red: #e67e80;--orange: #e69875;--yellow: #dbbc7f;--green: #a7c080;--blue: #7fbbb3;--aqua: #83c092;--purple: #d699b6;--fg: #d3c6aa;--statusline1: #a7c080;--statusline2: #d3c6aa;--statusline3: #e67e80;--gray0: #7a8478;--gray1: #859289;--gray2: #9da9a0}[data-webtui-theme=everforest-dark-hard]{--bg-dim: #1e2326;--bg0: #272e33;--bg1: #2e383c;--bg2: #374145;--bg3: #414b50;--bg4: #495156;--bg5: #4f5b58;--bg-red: #4c3743;--bg-visual: #493b40;--bg-yellow: #45443c;--bg-green: #3c4841;--bg-blue: #384b55}[data-webtui-theme=everforest-dark-medium],[data-webtui-theme=everforest-dark],[data-webtui-theme=everforest]{--bg-dim: #232a2e;--bg0: #2d353b;--bg1: #343f44;--bg2: #3d484d;--bg3: #475258;--bg4: #4f585e;--bg5: #56635f;--bg-red: #4c3743;--bg-visual: #493b40;--bg-yellow: #45443c;--bg-green: #3c4841;--bg-blue: #384b55}[data-webtui-theme=everforest-dark-soft]{--bg-dim: #293136;--bg0: #333c43;--bg1: #3a464c;--bg2: #434f55;--bg3: #4d5960;--bg4: #555f66;--bg5: #5d6b66;--bg-red: #5c3f4f;--bg-visual: #59464c;--bg-yellow: #55544a;--bg-green: #48584e;--bg-blue: #3f5865}[data-webtui-theme|=everforest-light]{--red: #f85552;--orange: #f57d26;--yellow: #dfa000;--green: #8da101;--blue: #3a94c5;--aqua: #35a77c;--purple: #df69ba;--fg: #5c6a72;--statusline1: #93b259;--statusline2: #708089;--statusline3: #e66868;--gray0: #a6b0a0;--gray1: #939f91;--gray2: #829181}[data-webtui-theme=everforest-light-hard]{--bg-dim: #f2efdf;--bg0: #fffbef;--bg1: #f8f5e4;--bg2: #f2efdf;--bg3: #edeada;--bg4: #e8e5d5;--bg5: #bec5b2;--bg-visual: #f0f2d4;--bg-red: #ffe7de;--bg-green: #f3f5d9;--bg-blue: #ecf5ed;--bg-yellow: #fef2d5}[data-webtui-theme=everforest-light-medium],[data-webtui-theme=everforest-light]{--bg-dim: #efebd4;--bg0: #fdf6e3;--bg1: #f4f0d9;--bg2: #efebd4;--bg3: #e6e2cc;--bg4: #e0dcc7;--bg5: #bdc3af;--bg-visual: #eaedc8;--bg-red: #fbe3da;--bg-green: #f0f1d2;--bg-blue: #e9f0e9;--bg-yellow: #faedcd}[data-webtui-theme=everforest-light-soft]{--bg-dim: #e5dfc5;--bg0: #f3ead3;--bg1: #eae4ca;--bg2: #e5dfc5;--bg3: #ddd8be;--bg4: #d8d3ba;--bg5: #b9c0ab;--bg-visual: #e1e4bd;--bg-red: #f4dbd0;--bg-green: #e5e6c5;--bg-blue: #e1e7dd;--bg-yellow: #f1e4c5}[data-webtui-theme|=everforest]{--background0: var(--bg0);--background1: var(--bg1);--background2: var(--bg2);--background3: var(--bg3);--foreground0: var(--fg);--foreground1: var(--gray2);--foreground2: var(--gray1)}}@layer components{[data-webtui-theme|=everforest] button[variant-=red]{--button-primary: var(--red);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=green]{--button-primary: var(--green);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=yellow]{--button-primary: var(--yellow);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=blue]{--button-primary: var(--blue);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=purple]{--button-primary: var(--purple);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=aqua]{--button-primary: var(--aqua);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=orange]{--button-primary: var(--orange);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=statusline1]{--button-primary: var(--statusline1);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=statusline2]{--button-primary: var(--statusline2);--button-secondary: var(--background0)}[data-webtui-theme|=everforest] button[variant-=statusline3]{--button-primary: var(--statusline3);--button-secondary: var(--background0)}}@layer components{[data-webtui-theme|=everforest] h1,[data-webtui-theme|=everforest] h2,[data-webtui-theme|=everforest] h3,[data-webtui-theme|=everforest] h4,[data-webtui-theme|=everforest] h5,[data-webtui-theme|=everforest] h6{color:var(--ef-green)}[data-webtui-theme|=everforest] p a,[data-webtui-theme|=everforest] blockquote a,[data-webtui-theme|=everforest] li a,[data-webtui-theme|=everforest] [is-~=typography-block] a{color:var(--ef-blue);text-decoration:underline}[data-webtui-theme|=everforest] p a:hover,[data-webtui-theme|=everforest] blockquote a:hover,[data-webtui-theme|=everforest] li a:hover,[data-webtui-theme|=everforest] [is-~=typography-block] a:hover{color:var(--ef-aqua)}[data-webtui-theme|=everforest] p code,[data-webtui-theme|=everforest] blockquote code,[data-webtui-theme|=everforest] li code,[data-webtui-theme|=everforest] [is-~=typography-block] code{color:var(--ef-orange)}}@layer components{[data-webtui-theme|=everforest] [is-~=badge][variant-=red]{--badge-color: var(--red);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=green]{--badge-color: var(--green);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=yellow]{--badge-color: var(--yellow);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=blue]{--badge-color: var(--blue);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=purple]{--badge-color: var(--purple);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=aqua]{--badge-color: var(--aqua);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=orange]{--badge-color: var(--orange);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=statusline1]{--badge-color: var(--statusline1);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=statusline2]{--badge-color: var(--statusline2);--badge-text: var(--background0)}[data-webtui-theme|=everforest] [is-~=badge][variant-=statusline3]{--badge-color: var(--statusline3);--badge-text: var(--background0)}}@layer components{[data-webtui-theme|=everforest] mark[bg-=red],[data-webtui-theme|=everforest] [is-~=mark][bg-=red]{background-color:var(--red)}[data-webtui-theme|=everforest] mark[bg-=orange],[data-webtui-theme|=everforest] [is-~=mark][bg-=orange]{background-color:var(--orange)}[data-webtui-theme|=everforest] mark[bg-=yellow],[data-webtui-theme|=everforest] [is-~=mark][bg-=yellow]{background-color:var(--yellow)}[data-webtui-theme|=everforest] mark[bg-=green],[data-webtui-theme|=everforest] [is-~=mark][bg-=green]{background-color:var(--green)}[data-webtui-theme|=everforest] mark[bg-=blue],[data-webtui-theme|=everforest] [is-~=mark][bg-=blue]{background-color:var(--blue)}[data-webtui-theme|=everforest] mark[bg-=aqua],[data-webtui-theme|=everforest] [is-~=mark][bg-=aqua]{background-color:var(--aqua)}[data-webtui-theme|=everforest] mark[bg-=purple],[data-webtui-theme|=everforest] [is-~=mark][bg-=purple]{background-color:var(--purple)}[data-webtui-theme|=everforest] mark[bg-=statusline1],[data-webtui-theme|=everforest] [is-~=mark][bg-=statusline1]{background-color:var(--statusline1)}[data-webtui-theme|=everforest] mark[bg-=statusline2],[data-webtui-theme|=everforest] [is-~=mark][bg-=statusline2]{background-color:var(--statusline2)}[data-webtui-theme|=everforest] mark[bg-=statusline3],[data-webtui-theme|=everforest] [is-~=mark][bg-=statusline3]{background-color:var(--statusline3)}[data-webtui-theme|=everforest] mark[fg-=red],[data-webtui-theme|=everforest] [is-~=mark][fg-=red]{color:var(--red)}[data-webtui-theme|=everforest] mark[fg-=orange],[data-webtui-theme|=everforest] [is-~=mark][fg-=orange]{color:var(--orange)}[data-webtui-theme|=everforest] mark[fg-=yellow],[data-webtui-theme|=everforest] [is-~=mark][fg-=yellow]{color:var(--yellow)}[data-webtui-theme|=everforest] mark[fg-=green],[data-webtui-theme|=everforest] [is-~=mark][fg-=green]{color:var(--green)}[data-webtui-theme|=everforest] mark[fg-=blue],[data-webtui-theme|=everforest] [is-~=mark][fg-=blue]{color:var(--blue)}[data-webtui-theme|=everforest] mark[fg-=aqua],[data-webtui-theme|=everforest] [is-~=mark][fg-=aqua]{color:var(--aqua)}[data-webtui-theme|=everforest] mark[fg-=purple],[data-webtui-theme|=everforest] [is-~=mark][fg-=purple]{color:var(--purple)}[data-webtui-theme|=everforest] mark[fg-=statusline1],[data-webtui-theme|=everforest] [is-~=mark][fg-=statusline1]{color:var(--statusline1)}[data-webtui-theme|=everforest] mark[fg-=statusline2],[data-webtui-theme|=everforest] [is-~=mark][fg-=statusline2]{color:var(--statusline2)}[data-webtui-theme|=everforest] mark[fg-=statusline3],[data-webtui-theme|=everforest] [is-~=mark][fg-=statusline3]{color:var(--statusline3)}}
0
2
β–Όwebtui/css/nf.css
+1-0
1
+@font-face{font-family:Symbols Nerd Font;src:local("SymbolsNerdFont-Regular"),url(https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@latest/build/fonts/SymbolsNerdFont-Regular.woff2) format("woff2");font-display:swap;font-style:normal;font-weight:400}
0
2
β–Όwebtui/css/nord.css
+1-0
1
+@layer base{[data-webtui-theme=nord]{--nord0: #2e3440;--nord1: #3b4252;--nord2: #434c5e;--nord3: #4c566a;--nord4: #d8dee9;--nord5: #e5e9f0;--nord6: #eceff4;--nord7: #8fbcbb;--nord8: #88c0d0;--nord9: #81a1c1;--nord10: #5e81ac;--nord11: #bf616a;--nord12: #d08770;--nord13: #ebcb8b;--nord14: #a3be8c;--nord15: #b48ead;--background0: var(--nord0);--background1: var(--nord1);--background2: var(--nord2);--background3: var(--nord3);--foreground0: var(--nord6);--foreground1: var(--nord5);--foreground2: var(--nord4)}}@layer components{[data-webtui-theme=nord] button[variant-=nord0]{--button-primary: var(--background0);--button-secondary: var(--foreground0)}[data-webtui-theme=nord] button[variant-=nord1]{--button-primary: var(--background1);--button-secondary: var(--foreground0)}[data-webtui-theme=nord] button[variant-=nord2]{--button-primary: var(--background2);--button-secondary: var(--foreground0)}[data-webtui-theme=nord] button[variant-=nord3]{--button-primary: var(--background3);--button-secondary: var(--foreground0)}[data-webtui-theme=nord] button[variant-=nord6]{--button-primary: var(--foreground0);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord5]{--button-primary: var(--foreground1);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord4]{--button-primary: var(--foreground2);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord7]{--button-primary: var(--nord7);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord8]{--button-primary: var(--nord8);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord9]{--button-primary: var(--nord9);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord10]{--button-primary: var(--nord10);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord11]{--button-primary: var(--nord11);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord12]{--button-primary: var(--nord12);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord13]{--button-primary: var(--nord13);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord14]{--button-primary: var(--nord14);--button-secondary: var(--background0)}[data-webtui-theme=nord] button[variant-=nord15]{--button-primary: var(--nord15);--button-secondary: var(--background0)}}@layer components{[data-webtui-theme=nord] h1,[data-webtui-theme=nord] h2,[data-webtui-theme=nord] h3,[data-webtui-theme=nord] h4,[data-webtui-theme=nord] h5,[data-webtui-theme=nord] h6{color:var(--nord14)}[data-webtui-theme=nord] p a,[data-webtui-theme=nord] blockquote a,[data-webtui-theme=nord] li a,[data-webtui-theme=nord] [is-~=typography-block] a{color:var(--nord7);text-decoration:underline}[data-webtui-theme=nord] p a:hover,[data-webtui-theme=nord] blockquote a:hover,[data-webtui-theme=nord] li a:hover,[data-webtui-theme=nord] [is-~=typography-block] a:hover{color:var(--nord8)}[data-webtui-theme=nord] p code,[data-webtui-theme=nord] blockquote code,[data-webtui-theme=nord] li code,[data-webtui-theme=nord] [is-~=typography-block] code{color:var(--nord12)}}@layer components{[data-webtui-theme=nord] [is-~=badge][variant-=nord0]{--badge-color: var(--background0);--badge-text: var(--foreground0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord1]{--badge-color: var(--background1);--badge-text: var(--foreground0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord2]{--badge-color: var(--background2);--badge-text: var(--foreground0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord3]{--badge-color: var(--background3);--badge-text: var(--foreground0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord6]{--badge-color: var(--foreground0);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord5]{--badge-color: var(--foreground1);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord4]{--badge-color: var(--foreground2);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord7]{--badge-color: var(--nord7);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord8]{--badge-color: var(--nord8);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord9]{--badge-color: var(--nord9);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord10]{--badge-color: var(--nord10);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord11]{--badge-color: var(--nord11);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord12]{--badge-color: var(--nord12);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord13]{--badge-color: var(--nord13);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord14]{--badge-color: var(--nord14);--badge-text: var(--background0)}[data-webtui-theme=nord] [is-~=badge][variant-=nord15]{--badge-color: var(--nord15);--badge-text: var(--background0)}}@layer components{[data-webtui-theme=nord] [is-~=mark][bg-=nord0],[data-webtui-theme=nord] mark[bg-=nord0]{background-color:var(--nord0)}[data-webtui-theme=nord] [is-~=mark][bg-=nord1],[data-webtui-theme=nord] mark[bg-=nord1]{background-color:var(--nord1)}[data-webtui-theme=nord] [is-~=mark][bg-=nord2],[data-webtui-theme=nord] mark[bg-=nord2]{background-color:var(--nord2)}[data-webtui-theme=nord] [is-~=mark][bg-=nord3],[data-webtui-theme=nord] mark[bg-=nord3]{background-color:var(--nord3)}[data-webtui-theme=nord] [is-~=mark][bg-=nord4],[data-webtui-theme=nord] mark[bg-=nord4]{background-color:var(--nord4)}[data-webtui-theme=nord] [is-~=mark][bg-=nord5],[data-webtui-theme=nord] mark[bg-=nord5]{background-color:var(--nord5)}[data-webtui-theme=nord] [is-~=mark][bg-=nord6],[data-webtui-theme=nord] mark[bg-=nord6]{background-color:var(--nord6)}[data-webtui-theme=nord] [is-~=mark][bg-=nord7],[data-webtui-theme=nord] mark[bg-=nord7]{background-color:var(--nord7)}[data-webtui-theme=nord] [is-~=mark][bg-=nord8],[data-webtui-theme=nord] mark[bg-=nord8]{background-color:var(--nord8)}[data-webtui-theme=nord] [is-~=mark][bg-=nord9],[data-webtui-theme=nord] mark[bg-=nord9]{background-color:var(--nord9)}[data-webtui-theme=nord] [is-~=mark][bg-=nord10],[data-webtui-theme=nord] mark[bg-=nord10]{background-color:var(--nord10)}[data-webtui-theme=nord] [is-~=mark][bg-=nord11],[data-webtui-theme=nord] mark[bg-=nord11]{background-color:var(--nord11)}[data-webtui-theme=nord] [is-~=mark][bg-=nord12],[data-webtui-theme=nord] mark[bg-=nord12]{background-color:var(--nord12)}[data-webtui-theme=nord] [is-~=mark][bg-=nord13],[data-webtui-theme=nord] mark[bg-=nord13]{background-color:var(--nord13)}[data-webtui-theme=nord] [is-~=mark][bg-=nord14],[data-webtui-theme=nord] mark[bg-=nord14]{background-color:var(--nord14)}[data-webtui-theme=nord] [is-~=mark][bg-=nord15],[data-webtui-theme=nord] mark[bg-=nord15]{background-color:var(--nord15)}[data-webtui-theme=nord] [is-~=mark][fg-=nord0],[data-webtui-theme=nord] mark[fg-=nord0]{color:var(--nord0)}[data-webtui-theme=nord] [is-~=mark][fg-=nord1],[data-webtui-theme=nord] mark[fg-=nord1]{color:var(--nord1)}[data-webtui-theme=nord] [is-~=mark][fg-=nord2],[data-webtui-theme=nord] mark[fg-=nord2]{color:var(--nord2)}[data-webtui-theme=nord] [is-~=mark][fg-=nord3],[data-webtui-theme=nord] mark[fg-=nord3]{color:var(--nord3)}[data-webtui-theme=nord] [is-~=mark][fg-=nord4],[data-webtui-theme=nord] mark[fg-=nord4]{color:var(--nord4)}[data-webtui-theme=nord] [is-~=mark][fg-=nord5],[data-webtui-theme=nord] mark[fg-=nord5]{color:var(--nord5)}[data-webtui-theme=nord] [is-~=mark][fg-=nord6],[data-webtui-theme=nord] mark[fg-=nord6]{color:var(--nord6)}[data-webtui-theme=nord] [is-~=mark][fg-=nord7],[data-webtui-theme=nord] mark[fg-=nord7]{color:var(--nord7)}[data-webtui-theme=nord] [is-~=mark][fg-=nord8],[data-webtui-theme=nord] mark[fg-=nord8]{color:var(--nord8)}[data-webtui-theme=nord] [is-~=mark][fg-=nord9],[data-webtui-theme=nord] mark[fg-=nord9]{color:var(--nord9)}[data-webtui-theme=nord] [is-~=mark][fg-=nord10],[data-webtui-theme=nord] mark[fg-=nord10]{color:var(--nord10)}[data-webtui-theme=nord] [is-~=mark][fg-=nord11],[data-webtui-theme=nord] mark[fg-=nord11]{color:var(--nord11)}[data-webtui-theme=nord] [is-~=mark][fg-=nord12],[data-webtui-theme=nord] mark[fg-=nord12]{color:var(--nord12)}[data-webtui-theme=nord] [is-~=mark][fg-=nord13],[data-webtui-theme=nord] mark[fg-=nord13]{color:var(--nord13)}[data-webtui-theme=nord] [is-~=mark][fg-=nord14],[data-webtui-theme=nord] mark[fg-=nord14]{color:var(--nord14)}[data-webtui-theme=nord] [is-~=mark][fg-=nord15],[data-webtui-theme=nord] mark[fg-=nord15]{color:var(--nord15)}}
0
2
β–Όwebtui/src/main.css
+436-0
1
+/* ═══════════════════════════════════════════════════════════════════
2
+ JUSTAPI DOCS β€” TRUE TERMINAL UI (WebTUI)
3
+ Pure WebTUI: base + utils + components + nf plugin + themes
4
+ ═══════════════════════════════════════════════════════════════════ */
5
+
6
+@layer base, utils, components;
7
+
8
+/* WebTUI core */
9
+@import './webtui/base.css';
10
+@import './webtui/box.css';
11
+@import './webtui/components/typography.css';
12
+@import './webtui/components/button.css';
13
+@import './webtui/components/badge.css';
14
+@import './webtui/components/mark.css';
15
+@import './webtui/components/pre.css';
16
+@import './webtui/components/progress.css';
17
+@import './webtui/components/spinner.css';
18
+@import './webtui/components/separator.css';
19
+@import './webtui/components/table.css';
20
+@import './webtui/components/accordion.css';
21
+@import './webtui/components/tooltip.css';
22
+@import './webtui/components/view.css';
23
+@import './webtui/nf.css';
24
+
25
+/* Themes */
26
+@import './webtui/catppuccin.css';
27
+@import './webtui/everforest.css';
28
+@import './webtui/nord.css';
29
+
30
+/* ═══════════════════════════════════════════════════════════════════
31
+ BASE β€” 8-bit terminal palette
32
+ ═══════════════════════════════════════════════════════════════════ */
33
+@layer base {
34
+ :root {
35
+ --font-size: 16px;
36
+ --line-height: 1.55;
37
+ --font-weight-normal: 400;
38
+ --font-weight-bold: 700;
39
+ --font-family:
40
+ 'JetBrains Mono', 'Fira Code', 'Cascadia Code', Menlo, Monaco,
41
+ ui-monospace, monospace, 'Symbols Nerd Font';
42
+ }
43
+
44
+ [data-webtui-theme='dark'],
45
+ [data-webtui-theme='light'] {
46
+ --bit0: #000000; --bit1: #800000; --bit2: #008000; --bit3: #808000;
47
+ --bit4: #000080; --bit5: #800080; --bit6: #008080; --bit7: #c0c0c0;
48
+ --bit8: #808080; --bit9: #ff0000; --bit10: #00ff00; --bit11: #ffff00;
49
+ --bit12: #0000ff; --bit13: #ff00ff; --bit14: #00ffff; --bit15: #ffffff;
50
+ }
51
+
52
+ [data-webtui-theme='dark'] {
53
+ --background0: #0a0a0a;
54
+ --background1: #161616;
55
+ --background2: #242424;
56
+ --background3: #333333;
57
+ --foreground0: var(--bit7);
58
+ --foreground1: var(--bit8);
59
+ --foreground2: var(--bit7);
60
+ --box-border-color: var(--bit7);
61
+ --table-border-color: var(--bit7);
62
+ --separator-color: var(--bit8);
63
+ --green: var(--bit10);
64
+ --red: var(--bit9);
65
+ --yellow: var(--bit11);
66
+ --blue: var(--bit14);
67
+ --cyan: var(--bit14);
68
+ --magenta: var(--bit13);
69
+ --white: var(--bit15);
70
+ }
71
+
72
+ [data-webtui-theme='light'] {
73
+ --background0: #ffffff;
74
+ --background1: #e8e8e8;
75
+ --background2: #d0d0d0;
76
+ --background3: #b8b8b8;
77
+ --foreground0: #000000;
78
+ --foreground1: #404040;
79
+ --foreground2: #808080;
80
+ --box-border-color: #000000;
81
+ --table-border-color: #000000;
82
+ --separator-color: #808080;
83
+ --green: #008000;
84
+ --red: #800000;
85
+ --yellow: #808000;
86
+ --blue: #000080;
87
+ --cyan: #008080;
88
+ --magenta: #800080;
89
+ --white: #000000;
90
+ }
91
+}
92
+
93
+/* ═══════════════════════════════════════════════════════════════════
94
+ LAYOUT β€” terminal chrome
95
+ ═══════════════════════════════════════════════════════════════════ */
96
+@layer components {
97
+ * { box-sizing: border-box; }
98
+ html, body { margin: 0; padding: 0; background-color: var(--background0); color: var(--foreground0); }
99
+ body { font-family: var(--font-family); }
100
+
101
+ /* ─── Navbar ─── */
102
+ .ja-navbar {
103
+ position: sticky;
104
+ top: 0;
105
+ z-index: 100;
106
+ border-bottom: 2px solid var(--foreground0);
107
+ background-color: var(--background1);
108
+ }
109
+ .ja-navbar-bar {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 0.75ch;
113
+ padding: 0.3rem 1rem;
114
+ background-color: var(--background2);
115
+ border-bottom: 1px solid var(--background3);
116
+ font-size: 0.75rem;
117
+ color: var(--foreground1);
118
+ }
119
+ .ja-dot { width: 0.8em; height: 0.8em; border-radius: 50%; display: inline-block; }
120
+ .ja-dot:nth-child(1) { background: var(--red, #f00); }
121
+ .ja-dot:nth-child(2) { background: var(--yellow, #ff0); }
122
+ .ja-dot:nth-child(3) { background: var(--green, #0f0); }
123
+ .ja-navbar-title { margin-left: 0.5ch; }
124
+ .ja-navbar-body {
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: space-between;
128
+ padding: 0.6rem 1.5rem;
129
+ gap: 1rem;
130
+ }
131
+ .ja-nav-brand a {
132
+ font-weight: var(--font-weight-bold);
133
+ color: var(--foreground0);
134
+ text-decoration: none;
135
+ font-size: 1.05rem;
136
+ }
137
+ .ja-nav-brand .nf { color: var(--green, var(--foreground0)); }
138
+ .ja-nav-links { display: flex; gap: 1.5rem; align-items: center; flex-wrap: wrap; }
139
+ .ja-nav-links a {
140
+ color: var(--foreground1);
141
+ text-decoration: none;
142
+ font-size: 0.9rem;
143
+ }
144
+ .ja-nav-links a:hover { color: var(--green, var(--foreground0)); text-decoration: underline; }
145
+ .ja-nav-links .nf { font-size: 1rem; }
146
+
147
+ /* ─── Layout grid ─── */
148
+ .ja-layout {
149
+ display: grid;
150
+ grid-template-columns: 260px minmax(0, 1fr) 240px;
151
+ gap: 0;
152
+ min-height: calc(100vh - 70px);
153
+ }
154
+
155
+ /* ─── Left sidebar ─── */
156
+ .ja-sidebar {
157
+ border-right: 2px solid var(--foreground0);
158
+ background-color: var(--background0);
159
+ padding: 1rem 0.5rem;
160
+ overflow-y: auto;
161
+ max-height: calc(100vh - 70px);
162
+ position: sticky;
163
+ top: 70px;
164
+ font-size: 0.85rem;
165
+ }
166
+ .ja-nav-tree a {
167
+ display: block;
168
+ padding: 0.3rem 0.75rem;
169
+ color: var(--foreground1);
170
+ text-decoration: none;
171
+ white-space: nowrap;
172
+ overflow: hidden;
173
+ text-overflow: ellipsis;
174
+ }
175
+ .ja-nav-tree a:hover { color: var(--foreground0); background-color: var(--background1); }
176
+ .ja-nav-tree a.active {
177
+ color: var(--green, var(--foreground0));
178
+ font-weight: var(--font-weight-bold);
179
+ background-color: var(--background1);
180
+ border-left: 3px solid var(--green, var(--foreground0));
181
+ }
182
+ .ja-nav-group {
183
+ margin: 0.75rem 0 0.25rem;
184
+ padding: 0.25rem 0.75rem;
185
+ font-weight: var(--font-weight-bold);
186
+ color: var(--foreground2);
187
+ text-transform: uppercase;
188
+ font-size: 0.7rem;
189
+ letter-spacing: 0.05em;
190
+ }
191
+ .ja-nav-sub { margin-left: 0.75rem; border-left: 1px solid var(--background2); }
192
+
193
+ /* ─── Content ─── */
194
+ .ja-content {
195
+ padding: 2rem 2.5rem 3rem;
196
+ min-width: 0;
197
+ }
198
+ .ja-content article { max-width: 52rem; }
199
+
200
+ /* Typography */
201
+ .ja-content h1 { font-size: 1.7rem; margin: 0 0 1.5rem; }
202
+ .ja-content h2 {
203
+ font-size: 1.3rem;
204
+ margin: 2.5rem 0 0.75rem;
205
+ padding-bottom: 0.4rem;
206
+ border-bottom: 1px solid var(--background2);
207
+ }
208
+ .ja-content h3 { font-size: 1.05rem; margin: 2rem 0 0.5rem; }
209
+ .ja-content h4 { font-size: 0.95rem; margin: 1.5rem 0 0.5rem; }
210
+ .ja-content h1::before, .ja-content h2::before, .ja-content h3::before, .ja-content h4::before {
211
+ color: var(--green, var(--foreground0));
212
+ }
213
+ .ja-content h1::before { content: '# '; }
214
+ .ja-content h2::before { content: '## '; }
215
+ .ja-content h3::before { content: '### '; }
216
+ .ja-content h4::before { content: '#### '; }
217
+
218
+ .ja-content p, .ja-content li { color: var(--foreground1); line-height: var(--line-height); }
219
+ .ja-content a { color: var(--blue, var(--foreground2)); text-decoration: underline; text-underline-offset: 3px; }
220
+ .ja-content a:hover { color: var(--cyan, var(--foreground0)); }
221
+
222
+ .ja-content :not(pre) > code {
223
+ background-color: var(--background2);
224
+ color: var(--red, var(--foreground0));
225
+ padding: 0 0.75ch;
226
+ }
227
+
228
+ /* Code blocks */
229
+ .ja-content pre {
230
+ background-color: var(--background1);
231
+ border: 2px solid var(--foreground0);
232
+ border-radius: 0;
233
+ padding: 1rem 1.25rem;
234
+ overflow-x: auto;
235
+ font-family: var(--font-family);
236
+ font-size: 0.85rem;
237
+ line-height: 1.6;
238
+ }
239
+ .ja-content pre code { background: none; color: var(--foreground0); padding: 0; }
240
+
241
+ /* Tables */
242
+ .ja-content table { border-collapse: collapse; margin: 1.25rem 0; font-size: 0.9rem; }
243
+ .ja-content th, .ja-content td {
244
+ border: 1px solid var(--table-border-color, var(--foreground0));
245
+ padding: 0.45rem 1ch;
246
+ text-align: left;
247
+ }
248
+ .ja-content th { color: var(--foreground0); font-weight: var(--font-weight-bold); background-color: var(--background1); }
249
+ .ja-content td { color: var(--foreground1); }
250
+ .ja-content tbody tr:hover { background-color: var(--background1); }
251
+
252
+ /* Blockquote / callouts */
253
+ .ja-content blockquote {
254
+ margin: 1.25rem 0;
255
+ padding: 0.75rem 1.5ch;
256
+ border: 2px solid var(--foreground0);
257
+ border-left: 0.75ch solid var(--green, var(--foreground0));
258
+ background-color: var(--background1);
259
+ color: var(--foreground1);
260
+ }
261
+ .ja-content blockquote p { margin: 0; }
262
+
263
+ /* Lists */
264
+ .ja-content ul, .ja-content ol { color: var(--foreground1); }
265
+
266
+ /* Footer */
267
+ .ja-footer {
268
+ margin-top: 3rem;
269
+ padding-top: 1rem;
270
+ border-top: 2px solid var(--background2);
271
+ font-size: 0.8rem;
272
+ color: var(--foreground2);
273
+ }
274
+ .ja-footer a { color: var(--foreground2); }
275
+
276
+ /* ─── Right TOC ─── */
277
+ .ja-toc-sidebar {
278
+ border-left: 2px solid var(--foreground0);
279
+ background-color: var(--background0);
280
+ padding: 1.5rem 1rem;
281
+ font-size: 0.82rem;
282
+ overflow-y: auto;
283
+ max-height: calc(100vh - 70px);
284
+ position: sticky;
285
+ top: 70px;
286
+ }
287
+ .ja-toc-title {
288
+ font-weight: var(--font-weight-bold);
289
+ color: var(--foreground2);
290
+ text-transform: uppercase;
291
+ font-size: 0.7rem;
292
+ letter-spacing: 0.05em;
293
+ margin-bottom: 0.75rem;
294
+ }
295
+ .ja-toc a {
296
+ display: block;
297
+ padding: 0.25rem 0;
298
+ color: var(--foreground1);
299
+ text-decoration: none;
300
+ }
301
+ .ja-toc a:hover { color: var(--green, var(--foreground0)); }
302
+ .ja-toc a.h2 { padding-left: 0.75rem; }
303
+ .ja-toc a.h3 { padding-left: 1.5rem; }
304
+
305
+ /* ─── Theme switcher ─── */
306
+ .ja-theme-switcher {
307
+ position: fixed;
308
+ bottom: 1rem;
309
+ right: 1rem;
310
+ z-index: 1000;
311
+ display: flex;
312
+ gap: 0.25rem;
313
+ padding: 0.4rem;
314
+ border: 2px solid var(--foreground0);
315
+ background-color: var(--background0);
316
+ }
317
+ .ja-theme-btn {
318
+ font-family: 'Symbols Nerd Font';
319
+ font-size: 1.05rem;
320
+ line-height: 1;
321
+ width: 2.1rem;
322
+ height: 2.1rem;
323
+ padding: 0;
324
+ border: 1px solid var(--foreground2);
325
+ background: none;
326
+ color: var(--foreground0);
327
+ cursor: pointer;
328
+ transition: transform 0.15s ease, background-color 0.15s ease;
329
+ }
330
+ .ja-theme-btn:hover { transform: scale(1.15); background-color: var(--background2); }
331
+ .ja-theme-btn.active { border: 2px solid var(--foreground0); color: var(--green, var(--foreground0)); }
332
+
333
+ /* ─── Landing components ─── */
334
+ .ja-hero { text-align: center; padding: 3rem 1rem 2rem; }
335
+ .ja-hero h1 { font-size: clamp(2rem, 5vw, 3rem); margin: 1rem 0; }
336
+ .ja-hero h1::before { content: ''; }
337
+ .ja-hero h1 .accent { color: var(--green, var(--foreground0)); }
338
+ .ja-hero-sub { max-width: 42rem; margin: 0 auto 2rem; color: var(--foreground1); font-size: 1.05rem; }
339
+ .ja-hero-actions { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; margin-bottom: 1rem; }
340
+ .ja-hero-actions a { font-family: var(--font-family); font-size: var(--font-size); }
341
+
342
+ .ja-stats {
343
+ display: grid;
344
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
345
+ gap: 0.75rem;
346
+ margin: 2rem 0;
347
+ }
348
+ .ja-stat {
349
+ border: 2px solid var(--foreground0);
350
+ background-color: var(--background1);
351
+ padding: 1rem 0.5rem;
352
+ text-align: center;
353
+ }
354
+ .ja-stat-value { font-size: 1.5rem; font-weight: var(--font-weight-bold); color: var(--green, var(--foreground0)); }
355
+ .ja-stat-label { font-size: 0.8rem; color: var(--foreground1); }
356
+
357
+ .ja-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0.75rem; margin: 1.5rem 0; }
358
+ .ja-card {
359
+ border: 2px solid var(--foreground0);
360
+ background-color: var(--background0);
361
+ padding: 1.25rem;
362
+ transition: background-color 0.15s ease;
363
+ }
364
+ .ja-card:hover { background-color: var(--background1); }
365
+ .ja-card-icon { font-family: 'Symbols Nerd Font'; font-size: 1.3rem; color: var(--green, var(--foreground0)); margin-bottom: 0.5rem; }
366
+ .ja-card h3 { margin: 0 0 0.5rem; font-size: 1rem; }
367
+ .ja-card h3::before { content: ''; }
368
+ .ja-card p { font-size: 0.85rem; color: var(--foreground1); margin: 0; line-height: 1.5; }
369
+
370
+ .ja-terminal { border: 2px solid var(--foreground0); background-color: var(--background1); margin: 1.5rem 0; }
371
+ .ja-terminal-bar {
372
+ display: flex; align-items: center; gap: 1ch;
373
+ padding: 0.5rem 1rem;
374
+ border-bottom: 2px solid var(--foreground0);
375
+ background-color: var(--background2);
376
+ font-size: 0.8rem;
377
+ }
378
+ .ja-terminal-title { color: var(--foreground0); margin-left: 1ch; }
379
+ .ja-terminal-body { padding: 1rem 1.5rem; font-size: 0.85rem; line-height: 1.7; color: var(--green, var(--foreground0)); overflow-x: auto; }
380
+ .ja-terminal-body .cm { color: var(--foreground2); }
381
+ .ja-terminal-body .ok { color: var(--green, var(--foreground0)); }
382
+ .ja-terminal-body .dim { color: var(--foreground2); }
383
+
384
+ .ja-bars { margin: 1.5rem 0; display: flex; flex-direction: column; gap: 0.6rem; }
385
+ .ja-bar-row { display: grid; grid-template-columns: 8rem 1fr 4.5rem; align-items: center; gap: 1ch; font-size: 0.85rem; }
386
+ .ja-bar-label { text-align: right; color: var(--foreground1); }
387
+ .ja-bar-row.winner .ja-bar-label { color: var(--green, var(--foreground0)); font-weight: var(--font-weight-bold); }
388
+ .ja-bar-track { height: 1.5rem; border: 2px solid var(--foreground0); background-color: var(--background0); padding: 2px; }
389
+ .ja-bar-fill { height: 100%; width: 0%; background-color: var(--green, var(--foreground0)); transition: width 1.2s cubic-bezier(0.16, 1, 0.3, 1); }
390
+ .ja-bar-value { color: var(--foreground0); text-align: right; }
391
+
392
+ .ja-section { display: flex; align-items: center; gap: 1ch; margin: 2.5rem 0 1rem; font-size: 1.05rem; font-weight: var(--font-weight-bold); color: var(--foreground0); }
393
+ .ja-section::before { content: '\f489'; font-family: 'Symbols Nerd Font'; color: var(--green, var(--foreground0)); }
394
+ .ja-section::after { content: ''; flex: 1; border-bottom: 2px solid var(--background2); }
395
+
396
+ .ja-cta { border: 2px solid var(--foreground0); background-color: var(--background1); padding: 2.5rem 1rem; text-align: center; margin: 2rem 0; }
397
+ .ja-cta h2 { border: none; margin: 0 0 0.5rem; }
398
+ .ja-cta h2::before { content: ''; }
399
+ .ja-cta p { color: var(--foreground1); margin: 0 0 1.5rem; }
400
+
401
+ /* Responsive */
402
+ @media (max-width: 1100px) {
403
+ .ja-layout { grid-template-columns: 220px minmax(0, 1fr); }
404
+ .ja-toc-sidebar { display: none; }
405
+ }
406
+ @media (max-width: 800px) {
407
+ .ja-layout { grid-template-columns: 1fr; }
408
+ .ja-sidebar { display: none; }
409
+ .ja-content { padding: 1.5rem 1rem 2rem; }
410
+ .ja-bar-row { grid-template-columns: 5.5rem 1fr 3.5rem; }
411
+ .ja-stats { grid-template-columns: repeat(2, 1fr); }
412
+ }
413
+
414
+ ::selection { background: var(--foreground0); color: var(--background0); }
415
+ :focus-visible { outline: 2px solid var(--foreground0); outline-offset: 2px; }
416
+}
417
+
418
+/* ─── Landing tabs ─── */
419
+@layer components {
420
+ .ja-tabs { border: 2px solid var(--foreground0); background-color: var(--background0); margin: 1.5rem 0; }
421
+ .ja-tabs-bar { display: flex; border-bottom: 2px solid var(--foreground0); background-color: var(--background1); overflow-x: auto; }
422
+ .ja-tab-btn {
423
+ font-family: var(--font-family);
424
+ padding: 0.5rem 2ch;
425
+ font-size: 0.85rem;
426
+ color: var(--foreground1);
427
+ background: none;
428
+ border: none;
429
+ border-right: 1px solid var(--background2);
430
+ cursor: pointer;
431
+ }
432
+ .ja-tab-btn:hover { background-color: var(--background2); color: var(--foreground0); }
433
+ .ja-tab-btn[data-active='true'] { background-color: var(--foreground0); color: var(--background0); font-weight: var(--font-weight-bold); }
434
+ .ja-tab-panel { padding: 1rem; }
435
+ .ja-tab-panel pre { margin: 0; border: none; background: none; }
436
+}
0
437
β–Όwebtui/src/main.js
+93-0
1
+// JustAPI Docs β€” terminal UI (pure JS, no framework)
2
+(function () {
3
+ 'use strict';
4
+
5
+ // ─── Theme switcher ───
6
+ var THEMES = ['dark', 'light', 'catppuccin', 'everforest', 'nord'];
7
+ var ICONS = {
8
+ dark: '\uf1db', // nf-fa-moon
9
+ light: '\uf185', // nf-fa-sun
10
+ catppuccin: '\uf870', // nf-md-palette
11
+ everforest: '\uf1a0', // nf-fa-leaf
12
+ nord: '\uf0c8', // nf-fa-square
13
+ };
14
+
15
+ function applyTheme(t) {
16
+ document.documentElement.setAttribute('data-webtui-theme', t);
17
+ document.documentElement.setAttribute('data-theme', t);
18
+ try { localStorage.setItem('ja-theme', t); } catch (e) {}
19
+ document.querySelectorAll('.ja-theme-btn').forEach(function (b) {
20
+ b.classList.toggle('active', b.dataset.theme === t);
21
+ });
22
+ }
23
+
24
+ function initThemeSwitcher() {
25
+ var saved = null;
26
+ try { saved = localStorage.getItem('ja-theme'); } catch (e) {}
27
+ var w = document.getElementById('themeSwitcher');
28
+ if (!w) return;
29
+ THEMES.forEach(function (t) {
30
+ var b = document.createElement('button');
31
+ b.className = 'ja-theme-btn';
32
+ b.dataset.theme = t;
33
+ b.title = t;
34
+ b.setAttribute('aria-label', 'Theme: ' + t);
35
+ b.innerHTML = ICONS[t];
36
+ b.addEventListener('click', function () { applyTheme(t); });
37
+ w.appendChild(b);
38
+ });
39
+ applyTheme(saved && THEMES.indexOf(saved) !== -1 ? saved : 'catppuccin');
40
+ }
41
+
42
+ // ─── Terminal tabs ───
43
+ function initTabs() {
44
+ document.querySelectorAll('.ja-tabs').forEach(function (tabs) {
45
+ var btns = tabs.querySelectorAll('[data-tab-btn]');
46
+ var panels = tabs.querySelectorAll('[data-tab-panel]');
47
+ btns.forEach(function (btn) {
48
+ btn.addEventListener('click', function () {
49
+ var target = btn.dataset.tabBtn;
50
+ btns.forEach(function (b) { b.setAttribute('data-active', b === btn ? 'true' : 'false'); });
51
+ panels.forEach(function (p) { p.style.display = p.dataset.tabPanel === target ? 'block' : 'none'; });
52
+ });
53
+ });
54
+ });
55
+ }
56
+
57
+ // ─── Benchmark bars ───
58
+ function initBars() {
59
+ var bars = document.querySelectorAll('.ja-bar-fill');
60
+ if (!bars.length || !('IntersectionObserver' in window)) return;
61
+ var io = new IntersectionObserver(function (entries) {
62
+ entries.forEach(function (e) {
63
+ if (e.isIntersecting) {
64
+ e.target.style.width = e.target.getAttribute('data-w') || '0%';
65
+ io.unobserve(e.target);
66
+ }
67
+ });
68
+ }, { threshold: 0.4 });
69
+ bars.forEach(function (b) { io.observe(b); });
70
+ }
71
+
72
+ // ─── Sidebar active link + mobile toggle ───
73
+ function initSidebar() {
74
+ var path = window.location.pathname;
75
+ document.querySelectorAll('.ja-nav-tree a').forEach(function (a) {
76
+ if (a.getAttribute('href') === path) a.classList.add('active');
77
+ });
78
+ }
79
+
80
+ if (document.readyState === 'loading') {
81
+ document.addEventListener('DOMContentLoaded', function () {
82
+ initThemeSwitcher();
83
+ initTabs();
84
+ initBars();
85
+ initSidebar();
86
+ });
87
+ } else {
88
+ initThemeSwitcher();
89
+ initTabs();
90
+ initBars();
91
+ initSidebar();
92
+ }
93
+})();
0
94
β–Όwebtui/templates/base.html
+69-0
1
+<!DOCTYPE html>
2
+<html lang="en" data-webtui-theme="catppuccin" data-theme="catppuccin">
3
+<head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>{{ title }} β€” JustAPI</title>
7
+ <meta name="description" content="{{ description }}">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/css/nerd-fonts-generated.css">
12
+ <link rel="stylesheet" href="/css/main.css">
13
+</head>
14
+<body>
15
+ <!-- ─── Terminal Navbar ─── -->
16
+ <header class="ja-navbar">
17
+ <div class="ja-navbar-bar">
18
+ <span class="ja-dot"></span><span class="ja-dot"></span><span class="ja-dot"></span>
19
+ <span class="ja-navbar-title">justapi β€” /docs</span>
20
+ </div>
21
+ <div class="ja-navbar-body">
22
+ <div class="ja-nav-brand">
23
+ <a href="/"><span class="nf">&#xf120;</span> JUSTAPI</a>
24
+ </div>
25
+ <nav class="ja-nav-links">
26
+ <a href="/">Home</a>
27
+ <a href="/getting-started/">Getting Started</a>
28
+ <a href="/tutorials/">Tutorials</a>
29
+ <a href="/api-reference/">API</a>
30
+ <a href="/deployment/">Deploy</a>
31
+ <a href="https://github.com/swadhinbiswas/JustAPI" target="_blank"><span class="nf">&#xf09b;</span></a>
32
+ </nav>
33
+ </div>
34
+ </header>
35
+
36
+ <div class="ja-layout">
37
+ <!-- ─── Left sidebar: docs nav ─── -->
38
+ <aside class="ja-sidebar">
39
+ <nav class="ja-nav-tree">
40
+ {{ sidebar }}
41
+ </nav>
42
+ </aside>
43
+
44
+ <!-- ─── Main content ─── -->
45
+ <main class="ja-content">
46
+ <article>
47
+ {{ content }}
48
+ </article>
49
+
50
+ <footer class="ja-footer">
51
+ <p>JustAPI v2.0.9 β€” MIT License Β· <a href="https://github.com/swadhinbiswas/JustAPI">github.com/swadhinbiswas/JustAPI</a></p>
52
+ </footer>
53
+ </main>
54
+
55
+ <!-- ─── Right sidebar: page TOC ─── -->
56
+ <aside class="ja-toc-sidebar">
57
+ <div class="ja-toc">
58
+ <div class="ja-toc-title">On this page</div>
59
+ {{ toc }}
60
+ </div>
61
+ </aside>
62
+ </div>
63
+
64
+ <!-- ─── Theme switcher ─── -->
65
+ <div class="ja-theme-switcher" id="themeSwitcher"></div>
66
+
67
+ <script src="/js/main.js"></script>
68
+</body>
69
+</html>
0
70
β–Όwebtui/templates/landing.html
+168-0
1
+<!DOCTYPE html>
2
+<html lang="en" data-webtui-theme="catppuccin" data-theme="catppuccin">
3
+<head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>JustAPI β€” Python writes the logic. Rust does everything else.</title>
7
+ <meta name="description" content="JustAPI β€” Python web framework with a Rust core. 766k req/s hello-world. FastAPI-compatible.">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/css/nerd-fonts-generated.css">
12
+ <link rel="stylesheet" href="/css/main.css">
13
+</head>
14
+<body>
15
+ <header class="ja-navbar">
16
+ <div class="ja-navbar-bar">
17
+ <span class="ja-dot"></span><span class="ja-dot"></span><span class="ja-dot"></span>
18
+ <span class="ja-navbar-title">justapi β€” /home</span>
19
+ </div>
20
+ <div class="ja-navbar-body">
21
+ <div class="ja-nav-brand">
22
+ <a href="/"><span class="nf">&#xf120;</span> JUSTAPI</a>
23
+ </div>
24
+ <nav class="ja-nav-links">
25
+ <a href="/">Home</a>
26
+ <a href="/getting-started/">Getting Started</a>
27
+ <a href="/tutorials/">Tutorials</a>
28
+ <a href="/api-reference/">API</a>
29
+ <a href="/deployment/">Deploy</a>
30
+ <a href="https://github.com/swadhinbiswas/JustAPI" target="_blank"><span class="nf">&#xf09b;</span></a>
31
+ </nav>
32
+ </div>
33
+ </header>
34
+
35
+ <main class="ja-content ja-landing">
36
+ <!-- ─── Hero ─── -->
37
+ <div class="ja-hero">
38
+ <span is="badge" variant="green">&#xf17d; v2.0.9 Β· published on PyPI Β· 8 platforms</span>
39
+ <h1>Python writes the logic.<br><span class="accent">Rust does everything else.</span></h1>
40
+ <p class="ja-hero-sub">JustAPI is a Python web framework with a Rust core. One import, zero config β€” routing, TLS, validation, serialization and the server itself run in Rust.</p>
41
+ <div class="ja-hero-actions">
42
+ <a href="/getting-started/overview/" is="button" size="large">&#xf061; Get Started</a>
43
+ <a href="https://github.com/swadhinbiswas/JustAPI" is="button" box size="large">&#xf09b; GitHub</a>
44
+ </div>
45
+ </div>
46
+
47
+ <!-- ─── Performance stats ─── -->
48
+ <div class="ja-section">Performance</div>
49
+ <div class="ja-stats">
50
+ <div class="ja-stat"><div class="ja-stat-value">766k</div><div class="ja-stat-label">req/s hello-world</div></div>
51
+ <div class="ja-stat"><div class="ja-stat-value">782k</div><div class="ja-stat-label">req/s JSON echo</div></div>
52
+ <div class="ja-stat"><div class="ja-stat-value">2.4x</div><div class="ja-stat-label">faster than Granian</div></div>
53
+ <div class="ja-stat"><div class="ja-stat-value">53x</div><div class="ja-stat-label">native async DB</div></div>
54
+ <div class="ja-stat"><div class="ja-stat-value">181k</div><div class="ja-stat-label">native CRUD SELECT</div></div>
55
+ <div class="ja-stat"><div class="ja-stat-value">12MB</div><div class="ja-stat-label">resident memory</div></div>
56
+ </div>
57
+
58
+ <!-- ─── Quick start ─── -->
59
+ <div class="ja-section">Quick start</div>
60
+ <div class="ja-tabs">
61
+ <div class="ja-tabs-bar">
62
+ <button class="ja-tab-btn" data-tab-btn="pip" data-active="true">&#xf472; pip</button>
63
+ <button class="ja-tab-btn" data-tab-btn="uv">&#xf42e; uv</button>
64
+ <button class="ja-tab-btn" data-tab-btn="scaffold">&#xf4c2; scaffold</button>
65
+ </div>
66
+ <div class="ja-tab-panel" data-tab-panel="pip">
67
+<pre>pip install justapi
68
+python main.py
69
+# βœ“ JustAPI running on http://127.0.0.1:8000</pre>
70
+ </div>
71
+ <div class="ja-tab-panel" data-tab-panel="uv" style="display:none">
72
+<pre>uv pip install justapi
73
+uv run main.py</pre>
74
+ </div>
75
+ <div class="ja-tab-panel" data-tab-panel="scaffold" style="display:none">
76
+<pre>uvx justapi create my_app
77
+cd my_app && python app/main.py</pre>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- ─── Minimal app ─── -->
82
+ <div class="ja-section">Minimal app</div>
83
+ <div class="ja-terminal">
84
+ <div class="ja-terminal-bar">
85
+ <span class="ja-dot"></span><span class="ja-dot"></span><span class="ja-dot"></span>
86
+ <span class="ja-terminal-title">&#xf1c5; main.py</span>
87
+ </div>
88
+ <div class="ja-terminal-body">
89
+ <span class="cm">from</span> justapi <span class="cm">import</span> JustAPIApp<br><br>
90
+ app = JustAPIApp()<br><br>
91
+ <span class="cm">@app.get</span>(<span class="ok">"/"</span>)<br>
92
+ <span class="cm">def</span> hello():<br>
93
+ &nbsp;&nbsp;&nbsp;&nbsp;<span class="cm">return</span> &#123;<span class="ok">"Hello"</span>: <span class="ok">"World"</span>&#125;<br><br>
94
+ app.run()<br>
95
+ <span class="ok">&#xf00c;</span> <span class="dim">server started Β· /docs Β· /redoc Β· OpenAPI auto</span>
96
+ </div>
97
+ </div>
98
+
99
+ <!-- ─── Benchmarks ─── -->
100
+ <div class="ja-section">Benchmarks</div>
101
+ <div class="ja-bars">
102
+ <div class="ja-bar-row winner">
103
+ <span class="ja-bar-label">JustAPI</span>
104
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-w="100%"></div></div>
105
+ <span class="ja-bar-value">766k</span>
106
+ </div>
107
+ <div class="ja-bar-row">
108
+ <span class="ja-bar-label">Granian</span>
109
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-w="41%"></div></div>
110
+ <span class="ja-bar-value">314k</span>
111
+ </div>
112
+ <div class="ja-bar-row">
113
+ <span class="ja-bar-label">Robyn</span>
114
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-w="5%"></div></div>
115
+ <span class="ja-bar-value">39k</span>
116
+ </div>
117
+ <div class="ja-bar-row">
118
+ <span class="ja-bar-label">FastAPI</span>
119
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-w="5%"></div></div>
120
+ <span class="ja-bar-value">36k</span>
121
+ </div>
122
+ </div>
123
+
124
+ <blockquote>hello-world, 100 concurrent connections, 30s, same hardware β€” full methodology in BENCHMARKS.md</blockquote>
125
+
126
+ <!-- ─── Features ─── -->
127
+ <div class="ja-section">Features</div>
128
+ <div class="ja-grid">
129
+ <div class="ja-card"><div class="ja-card-icon">&#xf40d;</div><h3>Rust core</h3><p>Networking, TLS, routing, serialization run in Rust. Python executes only your logic.</p></div>
130
+ <div class="ja-card"><div class="ja-card-icon">&#xf471;</div><h3>Native async DB</h3><p><code>await app.db.query_async()</code> runs SQL on Rust's tokio runtime β€” 53Γ— faster on slow queries.</p></div>
131
+ <div class="ja-card"><div class="ja-card-icon">&#xf295;</div><h3>Rust-native SSE</h3><p><code>app.sse_native()</code> streams events with zero Python per event.</p></div>
132
+ <div class="ja-card"><div class="ja-card-icon">&#xf085;</div><h3>Multi-worker</h3><p><code>justapi serve --workers N</code> β€” 1.88Γ— throughput at 4 workers, auto-scale.</p></div>
133
+ <div class="ja-card"><div class="ja-card-icon">&#xf4e7;</div><h3>Free-threaded</h3><p>CPython 3.14t wheels β€” CPU-bound Python scales with cores, 12.4Γ— measured.</p></div>
134
+ <div class="ja-card"><div class="ja-card-icon">&#xf4ab;</div><h3>Security built-in</h3><p>JWT, rate limiting, CORS, security headers β€” all in Rust middleware.</p></div>
135
+ <div class="ja-card"><div class="ja-card-icon">&#xf4dc;</div><h3>Production-ready</h3><p>Circuit breakers, OTel tracing, Prometheus metrics, health checks.</p></div>
136
+ <div class="ja-card"><div class="ja-card-icon">&#xf487;</div><h3>Type-checked</h3><p><code>py.typed</code> + complete stubs β€” mypy-clean on user code.</p></div>
137
+ <div class="ja-card"><div class="ja-card-icon">&#xf44f;</div><h3>FastAPI-compatible</h3><p>Same decorators, Pydantic models, Depends. Migrate with one import.</p></div>
138
+ </div>
139
+
140
+ <!-- ─── Comparison ─── -->
141
+ <div class="ja-section">Framework comparison</div>
142
+ <table>
143
+ <thead><tr><th></th><th>FastAPI</th><th>JustAPI</th></tr></thead>
144
+ <tbody>
145
+ <tr><td><strong>Server</strong></td><td>uvicorn (Python)</td><td>Rust (tokio + hyper)</td></tr>
146
+ <tr><td><strong>Hello-world</strong></td><td>~36k req/s</td><td><strong>~766k req/s</strong></td></tr>
147
+ <tr><td><strong>JSON echo</strong></td><td>~33k req/s</td><td><strong>~782k req/s</strong></td></tr>
148
+ <tr><td><strong>Native DB SELECT</strong></td><td>Python driver</td><td><strong>181k req/s (Γ—125)</strong></td></tr>
149
+ <tr><td><strong>Memory</strong></td><td>29 MB</td><td><strong>12 MB</strong></td></tr>
150
+ </tbody>
151
+ </table>
152
+
153
+ <!-- ─── CTA ─── -->
154
+ <div class="ja-cta">
155
+ <h2>Ready to build something fast?</h2>
156
+ <p>One import. Rust underneath. Zero config.</p>
157
+ <a href="/getting-started/overview/" is="button" size="large">&#xf061; Get Started</a>
158
+ </div>
159
+
160
+ <footer class="ja-footer">
161
+ <p>JustAPI v2.0.9 β€” MIT License Β· <a href="https://github.com/swadhinbiswas/JustAPI">github.com/swadhinbiswas/JustAPI</a></p>
162
+ </footer>
163
+ </main>
164
+
165
+ <div class="ja-theme-switcher" id="themeSwitcher"></div>
166
+ <script src="/js/main.js"></script>
167
+</body>
168
+</html>
0
169