spb/metrika Public
Stata-class statistics, GPU-accelerated by Apple Silicon. Native Swift — no Electron, no Python runtime, no compromises.
Swift 92.4%
HTML 3.3%
R 3%
Shell 1.3%
1//2// DocsCommand.swift3// Metrika4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7// Copyright © 2026 Simon-Pierre Boucher. All rights reserved.8//910import ArgumentParser11import Foundation12import ZQEngine1314/// `metrika-cli docs --output docs/` — renders the shared command15/// reference (the same registry behind the app's Manual pane and the16/// console `help`) into a self-contained static site.17struct DocsCommand: ParsableCommand {18 static let configuration = CommandConfiguration(19 commandName: "docs",20 abstract: "Generate the static documentation site from the command reference."21 )2223 @Option(name: .shortAndLong, help: "Output directory.")24 var output: String = "docs"2526 func run() throws {27 let directory = URL(fileURLWithPath: output, isDirectory: true)28 try FileManager.default.createDirectory(29 at: directory, withIntermediateDirectories: true30 )31 let html = Self.render()32 try Data(html.utf8).write(to: directory.appendingPathComponent("index.html"))33 print("wrote \(output)/index.html (\(ZQCommandReference.all.count) commands)")34 }3536 static func escape(_ text: String) -> String {37 text.replacingOccurrences(of: "&", with: "&")38 .replacingOccurrences(of: "<", with: "<")39 .replacingOccurrences(of: ">", with: ">")40 }4142 static func render() -> String {43 var nav = ""44 var body = ""4546 for category in ZQCommandReference.categories {47 let docs = ZQCommandReference.all.filter { $0.category == category }48 guard !docs.isEmpty else { continue }49 nav += "<h3>\(escape(category))</h3><ul>"50 body += "<h2 class=\"category\">\(escape(category))</h2>\n"51 for doc in docs {52 nav += "<li><a href=\"#\(doc.verb)\"><code>\(doc.verb)</code></a></li>"53 body += renderCommand(doc)54 }55 nav += "</ul>"56 }5758 return """59 <!DOCTYPE html>60 <html lang="en">61 <head>62 <meta charset="utf-8">63 <meta name="viewport" content="width=device-width, initial-scale=1">64 <title>Metrika — Command Reference</title>65 <style>66 :root { color-scheme: light dark;67 --accent: #4f8cff; --muted: #7a8199; --border: #d9dce6;68 --code-bg: rgba(120, 130, 180, 0.12); }69 @media (prefers-color-scheme: dark) {70 :root { --border: #2c3145; } }71 * { box-sizing: border-box; }72 body { margin: 0; font: 15px/1.55 -apple-system, "Helvetica Neue", sans-serif;73 display: flex; min-height: 100vh; }74 nav { width: 230px; flex-shrink: 0; padding: 24px 18px; overflow-y: auto;75 position: sticky; top: 0; height: 100vh; border-right: 1px solid var(--border); }76 nav h3 { font-size: 11px; text-transform: uppercase; letter-spacing: .06em;77 color: var(--muted); margin: 18px 0 6px; }78 nav ul { list-style: none; margin: 0; padding: 0; }79 nav a { text-decoration: none; color: inherit; }80 nav a:hover code { color: var(--accent); }81 main { flex: 1; max-width: 760px; padding: 32px 40px 80px; }82 h1 { font-size: 28px; margin: 0 0 4px; }83 .subtitle { color: var(--muted); margin-bottom: 28px; }84 h2.category { font-size: 13px; text-transform: uppercase; letter-spacing: .08em;85 color: var(--muted); border-bottom: 1px solid var(--border);86 padding-bottom: 6px; margin-top: 44px; }87 article { margin: 26px 0; }88 article h2 { font-size: 20px; margin: 0 0 2px; font-family: ui-monospace, monospace; }89 .abbr { font-size: 12px; color: var(--muted); margin-left: 8px; }90 pre { background: var(--code-bg); border-radius: 8px; padding: 10px 14px;91 overflow-x: auto; font: 13px ui-monospace, "SF Mono", monospace; }92 code { font: 13px ui-monospace, "SF Mono", monospace; }93 table { border-collapse: collapse; margin: 8px 0; }94 td { padding: 3px 14px 3px 0; vertical-align: top; font-size: 14px; }95 td:first-child { font-family: ui-monospace, monospace; white-space: nowrap; }96 .notes { color: var(--muted); font-size: 14px; }97 footer { color: var(--muted); font-size: 12px; margin-top: 56px;98 border-top: 1px solid var(--border); padding-top: 12px; }99 </style>100 </head>101 <body>102 <nav>\(nav)</nav>103 <main>104 <h1>Metrika</h1>105 <p class="subtitle">Command reference — Stata-class syntax, GPU-accelerated by Apple Silicon.106 Every command follows one grammar:107 <code>command [varlist] [if] [in] [, options]</code></p>108 \(body)109 <footer>© 2026 Simon-Pierre Boucher. Generated from the in-app command110 reference by <code>metrika-cli docs</code>.</footer>111 </main>112 </body>113 </html>114 """115 }116117 static func renderCommand(_ doc: ZQCommandDoc) -> String {118 var html = "<article id=\"\(doc.verb)\">"119 html += "<h2>\(escape(doc.verb))"120 if let abbreviation = doc.abbreviation {121 html += "<span class=\"abbr\">abbreviation: \(escape(abbreviation))</span>"122 }123 html += "</h2>"124 html += "<p>\(escape(doc.summary))</p>"125 html += "<pre>\(escape(doc.syntax))</pre>"126 if !doc.options.isEmpty {127 html += "<table>"128 for option in doc.options {129 html += "<tr><td>\(escape(option.name))</td><td>\(escape(option.meaning))</td></tr>"130 }131 html += "</table>"132 }133 if !doc.examples.isEmpty {134 html += "<pre>"135 + doc.examples.map { ". " + escape($0) }.joined(separator: "\n")136 + "</pre>"137 }138 if let notes = doc.notes {139 html += "<p class=\"notes\">\(escape(notes))</p>"140 }141 html += "</article>"142 return html143 }144}145