docit 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,10 +3,12 @@
3
3
  module Docit
4
4
  module UI
5
5
  class BaseRenderer
6
- attr_reader :spec_url, :title, :nav_paths
6
+ attr_reader :spec_url, :system_url, :system_insights_url, :title, :nav_paths
7
7
 
8
- def initialize(spec_url:, nav_paths: {})
8
+ def initialize(spec_url:, system_url: nil, system_insights_url: nil, nav_paths: {})
9
9
  @spec_url = spec_url
10
+ @system_url = system_url
11
+ @system_insights_url = system_insights_url
10
12
  @nav_paths = nav_paths
11
13
  @title = ERB::Util.html_escape(Docit.configuration.title)
12
14
  end
@@ -20,35 +22,66 @@ module Docit
20
22
  def nav_bar(active:)
21
23
  swagger_active = active == :swagger
22
24
  scalar_active = active == :scalar
25
+ system_active = active == :system
23
26
 
24
27
  <<~HTML
25
- <nav style="
26
- display: flex; align-items: center; gap: 8px;
27
- padding: 6px 16px;
28
- background: #1a1a2e; color: #fff;
29
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
30
- font-size: 13px; position: sticky; top: 0; z-index: 9999;
31
- ">
28
+ <style>
29
+ /* Theme-aware nav. Falls back to the original dark values when the
30
+ page has no theme tokens (e.g. Swagger/Scalar renderers). */
31
+ .docit-nav {
32
+ display: flex; align-items: center; gap: 8px;
33
+ padding: 6px 16px;
34
+ background: var(--bg-glass, #111827);
35
+ color: var(--text, #ffffff);
36
+ border-bottom: 1px solid var(--border, transparent);
37
+ backdrop-filter: blur(12px) saturate(150%);
38
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
39
+ font-size: 13px; position: sticky; top: 0; z-index: 9999;
40
+ }
41
+ .docit-nav-link {
42
+ color: var(--haze, rgba(255,255,255,0.7)); text-decoration: none;
43
+ padding: 4px 12px; border-radius: 6px;
44
+ transition: all 150ms;
45
+ font-family: inherit;
46
+ font-size: inherit;
47
+ }
48
+ .docit-nav-link:hover {
49
+ color: var(--text, #ffffff); background: var(--bg-option-hover, rgba(255,255,255,0.15));
50
+ }
51
+ .docit-nav-link.active {
52
+ color: var(--ember, #ffffff); background: var(--bg-option-hover, rgba(255,255,255,0.15)); font-weight: 600;
53
+ }
54
+ </style>
55
+ <nav class="docit-nav">
32
56
  <span style="font-weight: 600; margin-right: auto;">#{title}</span>
33
- #{nav_link("Swagger", nav_paths[:swagger], active: swagger_active)}
34
- #{nav_link("Scalar", nav_paths[:scalar], active: scalar_active)}
57
+ <a href="#{ERB::Util.html_escape(nav_paths[:swagger])}" class="docit-nav-link #{'active' if swagger_active}">Swagger</a>
58
+ <a href="#{ERB::Util.html_escape(nav_paths[:scalar])}" class="docit-nav-link #{'active' if scalar_active}">Scalar</a>
59
+ <a href="#{ERB::Util.html_escape(nav_paths[:system])}" class="docit-nav-link #{'active' if system_active}">System</a>
35
60
  </nav>
36
61
  HTML
37
62
  end
38
63
 
39
- def nav_link(label, path, active:)
40
- escaped_path = ERB::Util.html_escape(path)
41
- style = if active
42
- "color: #fff; text-decoration: none; padding: 4px 12px; border-radius: 4px; background: rgba(255,255,255,0.15); font-weight: 500;"
43
- else
44
- "color: rgba(255,255,255,0.7); text-decoration: none; padding: 4px 12px; border-radius: 4px;"
45
- end
64
+ def spec_url_json
65
+ json_escape(JSON.generate(spec_url))
66
+ end
46
67
 
47
- %(<a href="#{escaped_path}" style="#{style}">#{label}</a>)
68
+ def system_url_json
69
+ json_escape(JSON.generate(system_url))
48
70
  end
49
71
 
50
- def spec_url_json
51
- JSON.generate(spec_url)
72
+ def system_insights_url_json
73
+ json_escape(JSON.generate(system_insights_url))
74
+ end
75
+
76
+ def json_escape(json_string)
77
+ json_string.to_s.gsub(/[&<>'\u2028\u2029]/, {
78
+ '&' => '\u0026',
79
+ '<' => '\u003c',
80
+ '>' => '\u003e',
81
+ "'" => '\u0027',
82
+ "\u2028" => '\u2028',
83
+ "\u2029" => '\u2029'
84
+ })
52
85
  end
53
86
  end
54
87
  end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ module UI
5
+ class SystemRenderer < BaseRenderer
6
+ def render
7
+ <<~HTML
8
+ <!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <title>#{title} – System Architecture</title>
14
+ <script>
15
+ /* Apply the saved theme before first paint to avoid a flash.
16
+ Default is light; only switch to dark when explicitly chosen. */
17
+ (function() {
18
+ try {
19
+ var saved = localStorage.getItem("docit-theme");
20
+ if (saved === "dark") document.documentElement.setAttribute("data-theme", "dark");
21
+ } catch (e) {}
22
+ })();
23
+ </script>
24
+ <style>#{SystemStyles.css}</style>
25
+ </head>
26
+ <body>
27
+ #{nav_bar(active: :system)}
28
+ <div class="system-shell">
29
+ <header class="system-toolbar">
30
+ <div class="toolbar-group toolbar-brand">
31
+ <div class="brand-mark" aria-hidden="true">
32
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
33
+ <rect x="2" y="2" width="5" height="5" rx="1.2"/><rect x="9" y="2" width="5" height="5" rx="1.2"/>
34
+ <rect x="2" y="9" width="5" height="5" rx="1.2"/><rect x="9" y="9" width="5" height="5" rx="1.2"/>
35
+ <path d="M7 4.5h2M4.5 7v2M11.5 7v2"/>
36
+ </svg>
37
+ </div>
38
+ <div class="toolbar-title">
39
+ <strong>System Architecture</strong>
40
+ <span>Interactive diagram · drag, zoom, explore</span>
41
+ </div>
42
+ </div>
43
+ <div class="toolbar-group toolbar-filters" id="diagram-filters">
44
+ <input id="search" class="control" type="search" placeholder="Search nodes…" autocomplete="off">
45
+ <select id="diagram-section-filter" class="control"><option value="">All sections</option></select>
46
+ </div>
47
+ <div class="toolbar-group toolbar-filters" id="docs-filters" style="display: none;">
48
+ <select id="section-filter" class="control"><option value="">All sections</option></select>
49
+ </div>
50
+ <div class="toolbar-group toolbar-view-mode" style="margin-left: 6px; border-left: 1px solid var(--border); padding-left: 10px; gap: 4px;">
51
+ <button id="view-diagram" class="system-btn active" type="button">Diagram</button>
52
+ <button id="view-list" class="system-btn" type="button">Docs</button>
53
+ </div>
54
+ <div class="toolbar-group toolbar-zoom">
55
+ <button id="zoom-out" class="system-btn icon-btn" type="button" title="Zoom out" aria-label="Zoom out">
56
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M3 8h10"/></svg>
57
+ </button>
58
+ <span id="zoom-level" class="zoom-label">100%</span>
59
+ <button id="zoom-in" class="system-btn icon-btn" type="button" title="Zoom in" aria-label="Zoom in">
60
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M8 3v10M3 8h10"/></svg>
61
+ </button>
62
+ <button id="zoom-fit" class="system-btn" type="button">Fit</button>
63
+ </div>
64
+ <div class="toolbar-group toolbar-actions">
65
+ <button id="theme-toggle" class="system-btn icon-btn" type="button" title="Toggle theme" aria-label="Toggle light or dark theme"></button>
66
+ <button id="export-png" class="system-btn" type="button">
67
+ <span class="btn-icon" aria-hidden="true">
68
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v8M5 7.5l3 3 3-3M3 13h10"/></svg>
69
+ </span> Export PNG
70
+ </button>
71
+ </div>
72
+ <div class="toolbar-group toolbar-info">
73
+ <span id="stats" class="stat-pill"></span>
74
+ </div>
75
+ </header>
76
+
77
+ <div class="system-body">
78
+ <main class="canvas-wrap" id="canvas-wrap">
79
+ <div id="canvas" class="canvas"></div>
80
+ <div id="legend" class="legend collapsed">
81
+ <button id="legend-toggle" class="legend-toggle" type="button">Legend ▸</button>
82
+ <div id="legend-content" class="legend-content"></div>
83
+ </div>
84
+ </main>
85
+ <main class="stripe-docs-wrap" id="stripe-docs-wrap" style="display: none;">
86
+ <nav class="stripe-sidebar" id="stripe-sidebar"></nav>
87
+ <div class="stripe-content" id="stripe-content"></div>
88
+ <aside class="stripe-detail" id="stripe-detail">
89
+ <button class="stripe-detail-close" id="stripe-detail-close" type="button" aria-label="Close panel">
90
+ <svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 4l8 8M12 4l-8 8"/></svg>
91
+ </button>
92
+ <div class="stripe-detail-body" id="stripe-detail-body">
93
+ <div class="stripe-detail-empty">
94
+ <div class="stripe-detail-empty-icon" aria-hidden="true">
95
+ <svg width="26" height="26" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 1.5h6L13 5v9.5H3z"/><path d="M9 1.5V5h4"/><path d="M5.5 8.5h5M5.5 11h3"/></svg>
96
+ </div>
97
+ <p>Select an endpoint to see how to call it, or use <strong>Explain section</strong> for an AI overview.</p>
98
+ </div>
99
+ </div>
100
+ </aside>
101
+ </main>
102
+ <aside id="panel" class="panel"></aside>
103
+ </div>
104
+ </div>
105
+ <div id="toast" class="toast" role="status"></div>
106
+ <script>#{SystemScript.javascript(graph_url: system_url, insights_url: system_insights_url)}</script>
107
+ </body>
108
+ </html>
109
+ HTML
110
+ end
111
+ end
112
+ end
113
+ end