Class: Bidi2pdf::Bidi::NetworkEventFormatters::NetworkEventHtmlFormatter

Inherits:
Object
  • Object
show all
Includes:
NetworkEventFormatterUtils
Defined in:
lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb

Instance Method Summary collapse

Methods included from NetworkEventFormatterUtils

#format_bytes, #format_timestamp, #parse_timing, #shorten_url

Instance Method Details

#render(events) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb', line 9

def render(events)
  return unless Bidi2pdf.network_events_logger.info?

  <<~HTML
    <!DOCTYPE html>
    <html lang="en" data-bs-theme="light">
    <head>
      <meta charset="UTF-8">
      <title>Network Events</title>
      <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
      <style>
        body { font-family: monospace; padding: 2rem; }
        .event { background: var(--bs-body-bg); border: 1px solid var(--bs-border-color); padding: 1rem; margin-bottom: 2rem; border-radius: .5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
        .bar-container { position: relative; height: 20px; background: var(--bs-secondary-bg); border-radius: 4px; }
        .bar { position: absolute; top: 0; height: 100%; background-color: #0d6efd; opacity: 0.8; }
        .toc a { text-decoration: none; display: block; margin-bottom: 0.5rem; }

        @media print {
          .no-break {
            page-break-inside: avoid;
          }
    #{"    "}
    #{"     "}
          .form-select, .form-label, #theme-select {
            display: none !important; /* Hide theme selector when printing */
          }
        }

        .event {
          word-break: break-word;
          overflow-wrap: anywhere;
        }
      </style>
      <script>
        function toggleTheme(value) {
          document.documentElement.setAttribute('data-bs-theme', value);
        }
      </script>
    </head>
    <body>
      <h1>Network Events</h1>
      <div class="mb-4">
        <label for="theme-select" class="form-label">Theme:</label>
        <select id="theme-select" class="form-select w-auto d-inline-block" onchange="toggleTheme(this.value)">
          <option value="light">Light</option>
          <option value="dark">Dark</option>
        </select>
      </div>

      <h2>Index</h2>
      <div class="toc mb-4">
        #{events.map.with_index { |e, i| toc_entry(e, i) }.join("\n")}
      </div>

      #{events.map.with_index { |e, i| render_event(e, i) }.join("\n")}
    </body>
    </html>
  HTML
end

#render_event(event, index) ⇒ Object

rubocop: disable Metrics/AbcSize



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb', line 74

def render_event(event, index)
  timing = parse_timing(event)
  duration = event.duration_seconds || 0
  duration_str = event.in_progress? ? "in progress" : "#{duration}s"
  status = event.http_status_code || "?"
  method = event.http_method || "?"
  start = format_timestamp(event.start_timestamp)
  finish = event.end_timestamp ? format_timestamp(event.end_timestamp) : "..."
  bytes = event.bytes_received ? format_bytes(event.bytes_received) : "N/A"
  bars = render_timing_bars(timing)
  displayed_url = shorten_url(event.url)

  <<~HTML
    <div class="event no-break" id="event-#{index}">
      <div><strong>Request:</strong> #{method} #{displayed_url}</div>
      <div><strong>Status:</strong> HTTP #{status}</div>
      <div><strong>State:</strong> #{event.state}</div>
      <div><strong>Start:</strong> #{start}</div>
      <div><strong>End:</strong> #{finish}</div>
      <div><strong>Duration:</strong> #{duration_str}</div>
      <div><strong>Received:</strong> #{bytes}</div>
      #{bars}
    </div>
  HTML
end

#render_timing_bars(timing) ⇒ Object

rubocop: enable Metrics/AbcSize



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb', line 102

def render_timing_bars(timing)
  return "" if timing.empty?

  max_ms = timing.map { |t| t[:ms] }.max
  scale = max_ms.zero? ? 0 : 100.0 / max_ms

  bars = timing.map do |t|
    width = (t[:ms] * scale).clamp(1, 100).round(2)
    <<~HTML
      <div>
        <small>#{t[:label]} (#{t[:ms]} ms)</small>
        <div class="bar-container mb-2">
          <div class="bar" style="width: #{width}%"></div>
        </div>
      </div>
    HTML
  end

  "<div class=\"mt-3\"><strong>Timing Waterfall</strong>#{bars.join}</div>"
end

#toc_entry(event, index) ⇒ Object



69
70
71
# File 'lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb', line 69

def toc_entry(event, index)
  "<a href=\"#event-#{index}\">[#{index + 1}] #{event.http_method} #{event.url}</a>"
end