Module: Tina4::ErrorOverlay
- Defined in:
- lib/tina4/error_overlay.rb
Constant Summary collapse
- BG =
── Colour palette (Catppuccin Mocha) ──────────────────────────────
"#1e1e2e"- SURFACE =
"#313244"- OVERLAY_COLOR =
"#45475a"- TEXT_COLOR =
"#cdd6f4"- SUBTEXT =
"#a6adc8"- RED =
"#f38ba8"- YELLOW =
"#f9e2af"- BLUE =
"#89b4fa"- GREEN =
"#a6e3a1"- LAVENDER =
"#b4befe"- PEACH =
"#fab387"- ERROR_LINE_BG =
"rgba(243,139,168,0.15)"- CONTEXT_LINES =
7
Class Method Summary collapse
-
.is_debug_mode ⇒ Object
Return true if TINA4_DEBUG is enabled.
-
.render_error_overlay(exception, request: nil) ⇒ String
Render a rich HTML error overlay.
-
.render_production_error(status_code: 500, message: "Internal Server Error", path: "") ⇒ Object
Render a safe, generic error page for production.
Class Method Details
.is_debug_mode ⇒ Object
Return true if TINA4_DEBUG is enabled.
158 159 160 |
# File 'lib/tina4/error_overlay.rb', line 158 def is_debug_mode Tina4::Env.is_truthy(ENV.fetch("TINA4_DEBUG", "")) end |
.render_error_overlay(exception, request: nil) ⇒ String
Render a rich HTML error overlay.
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 68 69 70 71 72 73 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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/tina4/error_overlay.rb', line 41 def (exception, request: nil) exc_type = exception.class.name exc_msg = exception. # ── Stack trace ── frames_html = +"" backtrace = exception.backtrace || [] backtrace.each do |line| file, lineno, method = parse_backtrace_line(line) frames_html << format_frame(file, lineno, method) end # ── Request info ── request_pairs = [] if request.is_a?(Hash) request.each do |k, v| key = k.to_s if v.is_a?(Hash) v.each { |hk, hv| request_pairs << ["#{key}.#{hk}", hv.to_s] } elsif key.start_with?("HTTP_") || %w[REQUEST_METHOD REQUEST_URI SERVER_PROTOCOL REMOTE_ADDR SERVER_PORT QUERY_STRING CONTENT_TYPE CONTENT_LENGTH method url path].include?(key) request_pairs << [key, v.to_s] end end end request_section = request_pairs.empty? ? "" : collapsible("Request Details", table(request_pairs)) # ── Environment ── env_pairs = [ ["Framework", "Tina4 Ruby"], ["Version", defined?(Tina4::VERSION) ? Tina4::VERSION : "unknown"], ["Ruby", RUBY_VERSION], ["Platform", RUBY_PLATFORM], ["Debug", ENV.fetch("TINA4_DEBUG", "false")], ["Log Level", ENV.fetch("TINA4_LOG_LEVEL", "ERROR")] ] env_section = collapsible("Environment", table(env_pairs)) stack_section = collapsible("Stack Trace", frames_html, open_by_default: true) <<~HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Tina4 Error — #{esc(exc_type)}</title> <style> *{margin:0;padding:0;box-sizing:border-box;} body{background:#{BG};color:#{TEXT_COLOR};font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px;line-height:1.5;} </style> </head> <body> <div style="max-width:960px;margin:0 auto;"> <div style="margin-bottom:24px;"> <div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;"> <span style="background:#{RED};color:#{BG};padding:4px 12px;border-radius:4px;font-weight:700;font-size:13px;text-transform:uppercase;">Error</span> <span style="color:#{SUBTEXT};font-size:14px;">Tina4 Debug Overlay</span> </div> <h1 style="color:#{RED};font-size:28px;font-weight:700;margin-bottom:8px;">#{esc(exc_type)}</h1> <p style="color:#{TEXT_COLOR};font-size:18px;font-family:'SF Mono','Fira Code','Consolas',monospace;background:#{SURFACE};padding:12px 16px;border-radius:6px;border-left:4px solid #{RED};">#{esc(exc_msg)}</p> </div> #{stack_section} #{request_section} #{env_section} <div style="margin-top:32px;padding-top:16px;border-top:1px solid #{OVERLAY_COLOR};color:#{SUBTEXT};font-size:12px;"> Tina4 Debug Overlay — This page is only shown in debug mode. Set TINA4_DEBUG=false in production. </div> </div> </body> </html> HTML end |
.render_production_error(status_code: 500, message: "Internal Server Error", path: "") ⇒ Object
Render a safe, generic error page for production.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/tina4/error_overlay.rb', line 116 def render_production_error(status_code: 500, message: "Internal Server Error", path: "") # Determine color based on status code code_color = case status_code when 403 then "#f59e0b" when 404 then "#3b82f6" else "#ef4444" end <<~HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>#{status_code} — #{esc()}</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; min-height: 100vh; display: flex; align-items: center; justify-content: center; } .error-card { background: #1e293b; border: 1px solid #334155; border-radius: 1rem; padding: 3rem; text-align: center; max-width: 520px; width: 90%; } .error-code { font-size: 8rem; font-weight: 900; color: #{code_color}; opacity: 0.6; line-height: 1; margin-bottom: 0.5rem; } .error-title { font-size: 1.5rem; font-weight: 700; margin-bottom: 0.75rem; } .error-msg { color: #94a3b8; font-size: 1rem; margin-bottom: 1.5rem; line-height: 1.5; } .error-path { font-family: 'SF Mono', monospace; background: #0f172a; color: #{code_color}; padding: 0.5rem 1rem; border-radius: 0.5rem; font-size: 0.85rem; word-break: break-all; margin-bottom: 1.5rem; display: inline-block; } .error-home { display: inline-block; padding: 0.6rem 2rem; background: #3b82f6; color: #fff; text-decoration: none; border-radius: 0.5rem; font-size: 0.9rem; font-weight: 600; } .error-home:hover { opacity: 0.9; } .logo { font-size: 1.5rem; margin-bottom: 1rem; opacity: 0.5; } </style> </head> <body> <div class="error-card"> <div class="error-code">#{status_code}</div> <div class="error-title">#{esc()}</div> <div class="error-msg">Something went wrong while processing your request.</div> #{path.to_s.empty? ? '' : "<div class=\"error-path\">#{esc(path)}</div><br>"} <a href="/" class="error-home">Go Home</a> </div> </body> </html> HTML end |