diff --git a/assets/landing-demo.termshow b/assets/landing-demo.termshow new file mode 100644 index 00000000..25b326df --- /dev/null +++ b/assets/landing-demo.termshow @@ -0,0 +1,142 @@ +{"version": 1, "format": "termshow", "term": {"cols": 80, "rows": 24, "type": "xterm-256color"}, "title": "Great Docs — Init, Build, Preview"} +[1.0, "o", "\u001b[32m\u001b[1m$\u001b[0m "] +[0.4, "o", "c"] +[0.08, "o", "d"] +[0.12, "o", " "] +[0.1, "o", "w"] +[0.08, "o", "e"] +[0.06, "o", "a"] +[0.09, "o", "t"] +[0.07, "o", "h"] +[0.08, "o", "e"] +[0.06, "o", "r"] +[0.1, "o", "v"] +[0.07, "o", "a"] +[0.09, "o", "u"] +[0.06, "o", "l"] +[0.08, "o", "t"] +[0.3, "o", "\r\n"] +[0.3, "o", "\u001b[32m\u001b[1m$\u001b[0m "] +[0.6, "o", "g"] +[0.09, "o", "r"] +[0.07, "o", "e"] +[0.11, "o", "a"] +[0.06, "o", "t"] +[0.08, "o", "-"] +[0.1, "o", "d"] +[0.07, "o", "o"] +[0.09, "o", "c"] +[0.06, "o", "s"] +[0.12, "o", " "] +[0.09, "o", "i"] +[0.07, "o", "n"] +[0.06, "o", "i"] +[0.08, "o", "t"] +[0.35, "o", "\r\n"] +[0.6, "o", "Initializing great-docs...\r\n"] +[0.4, "o", "Found package __init__.py at: weathervault/__init__.py\r\n"] +[0.8, "o", "Discovered 24 public names\r\n"] +[0.3, "o", "Detected docstring style: numpy\r\n"] +[0.2, "o", "Found Click CLI: weathervault in weathervault.cli\r\n"] +[0.5, "o", "\r\n\u001b[32m✅ Great Docs initialization complete!\u001b[0m\r\n"] +[0.1, "o", "\r\nNext steps:\r\n"] +[0.05, "o", "1. Review great-docs.yml to customize your API reference structure\r\n"] +[0.05, "o", "2. Run `great-docs build` to generate and build your documentation site\r\n"] +[0.05, "o", "3. Run `great-docs preview` to view the site locally\r\n"] +[0.0, "m", "init done"] +[1.8, "o", "\r\n\u001b[32m\u001b[1m$\u001b[0m "] +[0.5, "o", "g"] +[0.09, "o", "r"] +[0.07, "o", "e"] +[0.11, "o", "a"] +[0.06, "o", "t"] +[0.08, "o", "-"] +[0.1, "o", "d"] +[0.07, "o", "o"] +[0.09, "o", "c"] +[0.06, "o", "s"] +[0.12, "o", " "] +[0.08, "o", "b"] +[0.07, "o", "u"] +[0.09, "o", "i"] +[0.06, "o", "l"] +[0.08, "o", "d"] +[0.35, "o", "\r\n"] +[0.5, "o", "\u001b[38;2;49;139;252m┌────────────────────────────────────────────────────────────────────────────┐\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m│\u001b[0m great-docs build · weathervault v2.4.1 \u001b[38;2;49;139;252m│\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m│\u001b[0m 18 steps · estimated ~1 min \u001b[38;2;49;139;252m│\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m└────────────────────────────────────────────────────────────────────────────┘\u001b[0m\r\n"] +[0.3, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 1/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mPrepare build directory\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.4, "o", " \u001b[32m✔\u001b[0m great-docs/ ready \u001b[2m0.3s\u001b[0m\r\n"] +[0.2, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 2/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mConfigure API reference\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.8, "o", " \u001b[32m✔\u001b[0m 24 exports in 4 sections \u001b[2m0.8s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 3/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mGenerate llms.txt / llms-full.txt\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.5, "o", " \u001b[32m✔\u001b[0m 2 files (4.2 KB) \u001b[2m0.4s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 4/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mGenerate SKILL.md\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.4, "o", " \u001b[32m✔\u001b[0m SKILL.md written \u001b[2m0.3s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 5/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mGenerate source links\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.6, "o", " \u001b[32m✔\u001b[0m 24 source links resolved \u001b[2m0.5s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 6/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mGenerate changelog\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.5, "o", " \u001b[32m✔\u001b[0m 8 releases from GitHub \u001b[2m0.6s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 7/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mGenerate CLI reference\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.4, "o", " \u001b[32m✔\u001b[0m 6 commands documented \u001b[2m0.3s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 8/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mProcess User Guide\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.3, "o", " \u001b[32m✔\u001b[0m 5 pages configured \u001b[2m0.2s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 9/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mProcess custom sections\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.2, "o", " \u001b[2m⊘\u001b[0m \u001b[2mSkipped (none configured)\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 10/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mProcess custom pages\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.2, "o", " \u001b[2m⊘\u001b[0m \u001b[2mSkipped (none configured)\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 11/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mProcess page tags\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.2, "o", " \u001b[2m⊘\u001b[0m \u001b[2mSkipped (none configured)\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 12/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mProcess page status badges\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.2, "o", " \u001b[2m⊘\u001b[0m \u001b[2mSkipped (none configured)\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 13/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mCopy assets\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.3, "o", " \u001b[32m✔\u001b[0m 42 files copied \u001b[2m0.2s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 14/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mGenerate API reference\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[1.2, "o", " \u001b[32m✔\u001b[0m 24 .qmd pages generated \u001b[2m1.1s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 15/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mBuild site with Quarto\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.6, "o", " \u001b[38;2;49;139;252m►\u001b[0m Rendering pages \u001b[38;2;49;139;252m████████████████████████\u001b[2m\u001b[0m 47/47 100%"] +[2.5, "o", "\r \r"] +[0.1, "o", " \u001b[32m✔\u001b[0m Site rendered \u001b[2m8.2s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 16/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mPost-render processing\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.1, "o", " \u001b[2m├─\u001b[0m \u001b[32m✔\u001b[0m Dark-mode toggle injected\r\n"] +[0.1, "o", " \u001b[2m├─\u001b[0m \u001b[32m✔\u001b[0m Interlinks resolved\r\n"] +[0.1, "o", " \u001b[2m├─\u001b[0m \u001b[32m✔\u001b[0m Responsive tables applied\r\n"] +[0.1, "o", " \u001b[2m├─\u001b[0m \u001b[32m✔\u001b[0m Copy-code buttons added\r\n"] +[0.1, "o", " \u001b[2m└─\u001b[0m \u001b[32m✔\u001b[0m Navbar widgets rendered\r\n"] +[0.3, "o", " \u001b[32m✔\u001b[0m 5 transforms applied \u001b[2m1.4s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 17/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mGenerate SEO files\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.3, "o", " \u001b[32m✔\u001b[0m sitemap.xml + robots.txt \u001b[2m0.2s\u001b[0m\r\n"] +[0.15, "o", "\r\n\u001b[1;38;2;49;139;252m━━\u001b[1;37m Step 18/18 \u001b[1;38;2;49;139;252m─ \u001b[1;37mPrepare freeze cache\u001b[0m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\r\n"] +[0.2, "o", " \u001b[2m⊘\u001b[0m \u001b[2mSkipped (no code cells)\u001b[0m\r\n"] +[0.4, "o", "\r\n\u001b[38;2;49;139;252m┌────────────────────────────────────────────────────────────────────────────┐\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m│\u001b[0m ✔ Build complete — 14/18 steps (4 skipped) \u001b[38;2;49;139;252m│\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m│\u001b[0m Total time: 14.8s \u001b[38;2;49;139;252m│\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m│\u001b[0m \u001b[38;2;49;139;252m│\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m│\u001b[0m 🎉 Site ready → great-docs/_site/index.html \u001b[38;2;49;139;252m│\u001b[0m\r\n"] +[0.05, "o", "\u001b[38;2;49;139;252m└────────────────────────────────────────────────────────────────────────────┘\u001b[0m\r\n"] +[0.0, "m", "build done"] +[1.8, "o", "\r\n\u001b[32m\u001b[1m$\u001b[0m "] +[0.5, "o", "g"] +[0.09, "o", "r"] +[0.07, "o", "e"] +[0.11, "o", "a"] +[0.06, "o", "t"] +[0.08, "o", "-"] +[0.1, "o", "d"] +[0.07, "o", "o"] +[0.09, "o", "c"] +[0.06, "o", "s"] +[0.12, "o", " "] +[0.08, "o", "p"] +[0.07, "o", "r"] +[0.09, "o", "e"] +[0.06, "o", "v"] +[0.08, "o", "i"] +[0.07, "o", "e"] +[0.09, "o", "w"] +[0.35, "o", "\r\n"] +[0.8, "o", "\r\n\u001b[32m🌐 Serving site at \u001b[1mhttp://localhost:3000\u001b[0m\r\n"] +[0.3, "o", "\u001b[2m Press Ctrl+C to stop\u001b[0m\r\n"] +[4.0, "o", ""] +[0.0, "m", "preview done"] diff --git a/assets/landing-demo.termshow.yml b/assets/landing-demo.termshow.yml new file mode 100644 index 00000000..cb0fb52f --- /dev/null +++ b/assets/landing-demo.termshow.yml @@ -0,0 +1,26 @@ +source: assets/landing-demo.termshow + +settings: + window_chrome: colorful + +chapters: + - at: 0 + label: "Initialize" + - at: 11.5 + label: "Build" + - at: 29.23 + label: "Preview" + +snippets: + - at: 1.4 + duration: 9 + text: "great-docs init" + label: "Init" + - at: 12.5 + duration: 15.1 + text: "great-docs build" + label: "Build" + - at: 29.59 + duration: 6.62 + text: "great-docs preview" + label: "Preview" diff --git a/great_docs/_term_player/manifest.py b/great_docs/_term_player/manifest.py index 0b56b1fa..8260a6c6 100644 --- a/great_docs/_term_player/manifest.py +++ b/great_docs/_term_player/manifest.py @@ -153,6 +153,7 @@ def generate_manifest( # Determine render settings from script theme = recording.term.theme font_family = "JetBrains Mono, Fira Code, SF Mono, Menlo, Consolas, monospace" + line_height: float | None = None show_cursor = True window_chrome = "none" @@ -161,6 +162,8 @@ def generate_manifest( theme = script.theme if script.font_family: font_family = script.font_family + if script.line_height is not None: + line_height = script.line_height show_cursor = script.show_cursor window_chrome = script.window_chrome @@ -248,6 +251,7 @@ def generate_manifest( font_family=font_family, show_cursor=show_cursor, window_chrome=window_chrome, + **({"line_height": line_height} if line_height is not None else {}), ) if out_path: diff --git a/great_docs/_term_player/renderer.py b/great_docs/_term_player/renderer.py index 3f0d519d..eb9431c4 100644 --- a/great_docs/_term_player/renderer.py +++ b/great_docs/_term_player/renderer.py @@ -21,6 +21,7 @@ def render_frame( *, font_family: str = DEFAULT_FONT_FAMILY, font_size: int = DEFAULT_FONT_SIZE, + line_height: float = DEFAULT_LINE_HEIGHT, show_cursor: bool = True, window_chrome: str = "none", ) -> str: @@ -36,6 +37,9 @@ def render_frame( CSS font-family for terminal text. font_size Font size in pixels. + line_height + Line height multiplier. Lower values (e.g. 1.2) make box-drawing + characters connect vertically. show_cursor Whether to render the cursor block. window_chrome @@ -50,7 +54,7 @@ def render_frame( theme = Theme() cell_w = font_size * 0.6 # Monospace character width approximation - cell_h = font_size * DEFAULT_LINE_HEIGHT + cell_h = font_size * line_height pad = DEFAULT_PADDING chrome_h = 0.0 # Chrome is rendered as CSS overlay, not in SVG diff --git a/great_docs/_term_player/script.py b/great_docs/_term_player/script.py index 6b62b98a..8ce0be95 100644 --- a/great_docs/_term_player/script.py +++ b/great_docs/_term_player/script.py @@ -83,6 +83,7 @@ class Script: theme_name: str | None = None theme: Theme | None = None font_family: str | None = None + line_height: float | None = None show_cursor: bool = True window_chrome: str = "none" prompt: str | None = None @@ -131,6 +132,7 @@ def _parse_script_data(data: dict[str, Any]) -> Script: script.speed = settings.get("speed", 1.0) script.theme_name = settings.get("theme") script.font_family = settings.get("font_family") + script.line_height = settings.get("line_height") script.show_cursor = settings.get("show_cursor", True) script.window_chrome = settings.get("window_chrome", "none") if "prompt" in settings: diff --git a/great_docs/assets/JetBrainsMono-Bold.subset.woff2 b/great_docs/assets/JetBrainsMono-Bold.subset.woff2 new file mode 100644 index 00000000..aa9ad55a Binary files /dev/null and b/great_docs/assets/JetBrainsMono-Bold.subset.woff2 differ diff --git a/great_docs/assets/JetBrainsMono-Regular.subset.woff2 b/great_docs/assets/JetBrainsMono-Regular.subset.woff2 new file mode 100644 index 00000000..901b8bbe Binary files /dev/null and b/great_docs/assets/JetBrainsMono-Regular.subset.woff2 differ diff --git a/great_docs/assets/termshow.css b/great_docs/assets/termshow.css index 6689e714..eb0a975d 100644 --- a/great_docs/assets/termshow.css +++ b/great_docs/assets/termshow.css @@ -1,5 +1,28 @@ /* Great Docs Termshow Player Styles */ +/* Embedded JetBrains Mono subset (OFL license) — includes box-drawing chars */ +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(JetBrainsMono-Regular.subset.woff2) format("woff2"); + unicode-range: U+0020-007E, U+00A0-00FF, U+2010-2027, U+2190-219F, + U+2500-257F, U+2580-259F, U+25A0-25C0, U+2298, U+2605-2606, + U+2705, U+2714, U+2716, U+2718, U+26A0; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(JetBrainsMono-Bold.subset.woff2) format("woff2"); + unicode-range: U+0020-007E, U+00A0-00FF, U+2010-2027, U+2190-219F, + U+2500-257F, U+2580-259F, U+25A0-25C0, U+2298, U+2605-2606, + U+2705, U+2714, U+2716, U+2718, U+26A0; +} + .gd-termshow { --gd-tp-bg: #1e1e2e; --gd-tp-fg: #cdd6f4; diff --git a/great_docs/core.py b/great_docs/core.py index da75606d..1c094911 100644 --- a/great_docs/core.py +++ b/great_docs/core.py @@ -269,6 +269,15 @@ def _prepare_build_directory(self) -> None: if tp_css_src.exists(): shutil.copy2(tp_css_src, self.project_path / "termshow.css") + # Copy termshow font subsets (JetBrains Mono for box-drawing fidelity) + for font_file in ( + "JetBrainsMono-Regular.subset.woff2", + "JetBrainsMono-Bold.subset.woff2", + ): + font_src = self.assets_path / font_file + if font_src.exists(): + shutil.copy2(font_src, self.project_path / font_file) + # Copy the evolution demo data file demo_json_src = self.assets_path / "api-evolution-demo.json" if demo_json_src.exists(): diff --git a/index.qmd b/index.qmd index 55d1d7ef..0a27e437 100644 --- a/index.qmd +++ b/index.qmd @@ -4,14 +4,7 @@ Give your Python package the documentation site it deserves. Great Docs turns yo ## Three Commands. One Beautiful Site. -```bash -pip install great-docs - -cd your-python-package -great-docs init # scans your package, writes the config -great-docs build # generates everything, renders with Quarto -great-docs preview # live preview at localhost:3000 -``` +{{< termshow file="assets/landing-demo" autoplay="true" loop="true" >}} That's it. Your API reference, CLI docs, and landing page are ready to go. Push to GitHub and you're live.