Parcourir la source

refactor(example): extract CSS to FastResource for optimal caching

Move inline styles from CounterTemplate to a separate COUNTER_CSS constant
and serve via FastResource endpoint at /styles.css. This enables browser
caching with ETag support and pre-compression for smaller payloads while
separating concerns between HTML structure and styling.
Billy Barrow il y a 1 semaine
Parent
commit
e577acde4e
1 fichiers modifiés avec 163 ajouts et 143 suppressions
  1. 163 143
      examples/DocumentBuilderTemplate.vala

+ 163 - 143
examples/DocumentBuilderTemplate.vala

@@ -57,16 +57,164 @@ class AppState : Object {
     }
 }
 
+/**
+ * CSS content for the counter page.
+ * Served as a FastResource for optimal performance with pre-compression.
+ */
+private const string COUNTER_CSS = """
+body {
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+    max-width: 700px;
+    margin: 0 auto;
+    padding: 20px;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    min-height: 100vh;
+}
+.card {
+    background: white;
+    border-radius: 12px;
+    padding: 25px;
+    margin: 15px 0;
+    box-shadow: 0 10px 40px rgba(0,0,0,0.2);
+}
+h1 {
+    color: #333;
+    margin-top: 0;
+}
+.counter-display {
+    text-align: center;
+    padding: 30px;
+    background: #f8f9fa;
+    border-radius: 8px;
+    margin: 20px 0;
+}
+.counter-value {
+    font-size: 72px;
+    font-weight: bold;
+    color: #667eea;
+    line-height: 1;
+}
+.counter-label {
+    color: #666;
+    font-size: 14px;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+}
+.button-group {
+    display: flex;
+    gap: 10px;
+    justify-content: center;
+    margin: 20px 0;
+}
+button {
+    padding: 12px 24px;
+    border: none;
+    border-radius: 6px;
+    cursor: pointer;
+    font-size: 16px;
+    font-weight: 600;
+    transition: all 0.2s;
+}
+button:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+}
+.btn-primary {
+    background: #667eea;
+    color: white;
+}
+.btn-primary:hover {
+    background: #5a6fd6;
+}
+.btn-danger {
+    background: #e74c3c;
+    color: white;
+}
+.btn-danger:hover {
+    background: #c0392b;
+}
+.btn-success {
+    background: #2ecc71;
+    color: white;
+}
+.btn-success:hover {
+    background: #27ae60;
+}
+.info-grid {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 15px;
+    margin: 20px 0;
+}
+.info-item {
+    background: #f8f9fa;
+    padding: 15px;
+    border-radius: 6px;
+    text-align: center;
+}
+.info-label {
+    font-size: 12px;
+    color: #666;
+    text-transform: uppercase;
+    letter-spacing: 1px;
+}
+.info-value {
+    font-size: 18px;
+    font-weight: 600;
+    color: #333;
+    margin-top: 5px;
+}
+.status-positive {
+    color: #2ecc71 !important;
+}
+.status-negative {
+    color: #e74c3c !important;
+}
+.status-zero {
+    color: #666 !important;
+}
+code {
+    background: #e8e8e8;
+    padding: 2px 6px;
+    border-radius: 4px;
+    font-size: 14px;
+}
+pre {
+    background: #263238;
+    color: #aed581;
+    padding: 15px;
+    border-radius: 6px;
+    overflow-x: auto;
+    font-size: 13px;
+}
+.feature-list {
+    margin: 0;
+    padding-left: 20px;
+}
+.feature-list li {
+    margin: 8px 0;
+    color: #555;
+}
+a {
+    color: #667eea;
+    text-decoration: none;
+}
+a:hover {
+    text-decoration: underline;
+}
+""";
+
 /**
  * CounterTemplate - A cached HTML template for the counter page.
  * 
  * This class extends MarkupTemplate to provide a reusable, cached template.
  * The HTML is parsed once and cached; new_instance() returns efficient copies.
+ * CSS is served separately via CounterStyles FastResource for optimal caching.
  */
 class CounterTemplate : MarkupTemplate {
     /// <summary>
     /// Returns the HTML markup for this template.
-    /// This could also use read_file() to load from disk.
+    /// CSS is linked externally via /styles.css for better caching.
     /// </summary>
     protected override string get_markup() {
         return """<!DOCTYPE html>
@@ -75,148 +223,7 @@ class CounterTemplate : MarkupTemplate {
     <meta charset="UTF-8"/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
     <title>Document Template Example</title>
-    <style>
-        body {
-            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-            max-width: 700px;
-            margin: 0 auto;
-            padding: 20px;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            min-height: 100vh;
-        }
-        .card {
-            background: white;
-            border-radius: 12px;
-            padding: 25px;
-            margin: 15px 0;
-            box-shadow: 0 10px 40px rgba(0,0,0,0.2);
-        }
-        h1 {
-            color: #333;
-            margin-top: 0;
-        }
-        .counter-display {
-            text-align: center;
-            padding: 30px;
-            background: #f8f9fa;
-            border-radius: 8px;
-            margin: 20px 0;
-        }
-        .counter-value {
-            font-size: 72px;
-            font-weight: bold;
-            color: #667eea;
-            line-height: 1;
-        }
-        .counter-label {
-            color: #666;
-            font-size: 14px;
-            text-transform: uppercase;
-            letter-spacing: 2px;
-        }
-        .button-group {
-            display: flex;
-            gap: 10px;
-            justify-content: center;
-            margin: 20px 0;
-        }
-        button {
-            padding: 12px 24px;
-            border: none;
-            border-radius: 6px;
-            cursor: pointer;
-            font-size: 16px;
-            font-weight: 600;
-            transition: all 0.2s;
-        }
-        button:hover {
-            transform: translateY(-2px);
-            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
-        }
-        .btn-primary {
-            background: #667eea;
-            color: white;
-        }
-        .btn-primary:hover {
-            background: #5a6fd6;
-        }
-        .btn-danger {
-            background: #e74c3c;
-            color: white;
-        }
-        .btn-danger:hover {
-            background: #c0392b;
-        }
-        .btn-success {
-            background: #2ecc71;
-            color: white;
-        }
-        .btn-success:hover {
-            background: #27ae60;
-        }
-        .info-grid {
-            display: grid;
-            grid-template-columns: repeat(2, 1fr);
-            gap: 15px;
-            margin: 20px 0;
-        }
-        .info-item {
-            background: #f8f9fa;
-            padding: 15px;
-            border-radius: 6px;
-            text-align: center;
-        }
-        .info-label {
-            font-size: 12px;
-            color: #666;
-            text-transform: uppercase;
-            letter-spacing: 1px;
-        }
-        .info-value {
-            font-size: 18px;
-            font-weight: 600;
-            color: #333;
-            margin-top: 5px;
-        }
-        .status-positive {
-            color: #2ecc71 !important;
-        }
-        .status-negative {
-            color: #e74c3c !important;
-        }
-        .status-zero {
-            color: #666 !important;
-        }
-        code {
-            background: #e8e8e8;
-            padding: 2px 6px;
-            border-radius: 4px;
-            font-size: 14px;
-        }
-        pre {
-            background: #263238;
-            color: #aed581;
-            padding: 15px;
-            border-radius: 6px;
-            overflow-x: auto;
-            font-size: 13px;
-        }
-        .feature-list {
-            margin: 0;
-            padding-left: 20px;
-        }
-        .feature-list li {
-            margin: 8px 0;
-            color: #555;
-        }
-        a {
-            color: #667eea;
-            text-decoration: none;
-        }
-        a:hover {
-            text-decoration: underline;
-        }
-    </style>
+    <link rel="stylesheet" href="/styles.css"/>
 </head>
 <body>
     <div class="card" id="main-card">
@@ -477,6 +484,7 @@ void main(string[] args) {
     print("╠══════════════════════════════════════════════════════════════╣\n");
     print("║  Endpoints:                                                  ║\n");
     print("║    /           - Counter page (template with modifications)  ║\n");
+    print("║    /styles.css - CSS stylesheet (FastResource)               ║\n");
     print("║    /increment  - Increase counter (POST)                     ║\n");
     print("║    /decrement  - Decrease counter (POST)                     ║\n");
     print("║    /reset      - Reset counter (POST)                        ║\n");
@@ -496,6 +504,18 @@ void main(string[] args) {
         // Endpoints use field initializer injection with Inversion.inject<T>()
         application.add_singleton<CounterTemplate>();
         
+        // Register CSS as a FastResource - pre-compressed, cached in memory
+        // This demonstrates serving static content efficiently with ETag caching
+        application.add_singleton_endpoint<FastResource>(new EndpointRoute("/styles.css"), () => {
+            try {
+                return new FastResource.from_string(COUNTER_CSS)
+                    .with_content_type("text/css; charset=utf-8")
+                    .with_default_compressors();
+            } catch (Error e) {
+                error("Failed to create CSS resource: %s", e.message);
+            }
+        });
+        
         // Register endpoints
         application.add_endpoint<HomePageEndpoint>(new EndpointRoute("/"));
         application.add_endpoint<IncrementEndpoint>(new EndpointRoute("/increment"));