Skip to content

Formatting Demo

Comprehensive showcase of formatting options including presets, colors, timestamps, and object display with live configuration builder.

📄 Source Code

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Loggical Formatting Showcase</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Oxygen, Ubuntu, sans-serif;
        line-height: 1.6;
        color: #333;
        background: #f8f9fa;
        padding: 20px;
      }

      .container {
        max-width: 1400px;
        margin: 0 auto;
        background: white;
        border-radius: 12px;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
        overflow: hidden;
      }

      .header {
        background: linear-gradient(135deg, #6f42c1 0%, #e83e8c 100%);
        color: white;
        padding: 30px;
        text-align: center;
      }

      .header h1 {
        font-size: 2.5rem;
        margin-bottom: 10px;
      }

      .header p {
        font-size: 1.1rem;
        opacity: 0.9;
      }

      .nav {
        background: #e9ecef;
        padding: 15px 30px;
        border-bottom: 1px solid #dee2e6;
      }

      .nav a {
        color: #495057;
        text-decoration: none;
        margin-right: 20px;
        font-weight: 500;
      }

      .nav a:hover {
        color: #6f42c1;
      }

      .content {
        padding: 30px;
      }

      .comparison-section {
        margin-bottom: 40px;
        padding: 25px;
        background: #f8f9fa;
        border-radius: 8px;
        border-left: 4px solid #6f42c1;
      }

      .comparison-section h2 {
        color: #2c3e50;
        margin-bottom: 15px;
        display: flex;
        align-items: center;
        gap: 10px;
      }

      .comparison-section .icon {
        font-size: 1.5rem;
      }

      .comparison-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
        gap: 20px;
        margin-top: 20px;
      }

      .format-example {
        background: white;
        border: 1px solid #dee2e6;
        border-radius: 8px;
        overflow: hidden;
      }

      .format-header {
        background: #6f42c1;
        color: white;
        padding: 12px 15px;
        font-weight: 600;
        font-size: 14px;
      }

      .format-output {
        background: #1e1e1e;
        color: #d4d4d4;
        padding: 15px;
        font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas,
          monospace;
        font-size: 12px;
        line-height: 1.4;
        overflow-x: auto;
        white-space: pre-wrap;
        min-height: 120px;
      }

      .controls-panel {
        background: white;
        border: 1px solid #dee2e6;
        border-radius: 8px;
        padding: 20px;
        margin-bottom: 20px;
      }

      .controls-row {
        display: flex;
        gap: 20px;
        margin-bottom: 15px;
        align-items: center;
        flex-wrap: wrap;
      }

      .control-group {
        display: flex;
        flex-direction: column;
        gap: 5px;
      }

      .control-group label {
        font-weight: 600;
        color: #495057;
        font-size: 14px;
      }

      .control-group select,
      .control-group input {
        padding: 8px 12px;
        border: 1px solid #ced4da;
        border-radius: 4px;
        font-size: 14px;
      }

      .checkbox-group {
        display: flex;
        align-items: center;
        gap: 8px;
      }

      .checkbox-group input[type="checkbox"] {
        width: auto;
        margin: 0;
      }

      .demo-button {
        background: linear-gradient(135deg, #6f42c1 0%, #e83e8c 100%);
        color: white;
        border: none;
        padding: 10px 20px;
        border-radius: 6px;
        cursor: pointer;
        font-weight: 500;
        transition: all 0.2s ease;
        margin-right: 10px;
        margin-bottom: 10px;
      }

      .demo-button:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 12px rgba(111, 66, 193, 0.3);
      }

      .preset-buttons {
        display: flex;
        gap: 10px;
        margin-bottom: 20px;
        flex-wrap: wrap;
      }

      .preset-button {
        background: #6c757d;
        color: white;
        border: none;
        padding: 8px 16px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 12px;
        transition: all 0.2s ease;
      }

      .preset-button:hover {
        background: #5a6268;
      }

      .preset-button.active {
        background: #6f42c1;
      }

      .explanation {
        background: #e8f4fd;
        border-left: 4px solid #17a2b8;
        padding: 15px;
        margin: 15px 0;
        border-radius: 4px;
      }

      .explanation h4 {
        color: #0c5460;
        margin-bottom: 8px;
      }

      .explanation p {
        color: #0c5460;
        margin: 0;
      }

      /* ANSI color classes */
      .ansi-black {
        color: #2d3748;
      }
      .ansi-red {
        color: #f56565;
      }
      .ansi-green {
        color: #48bb78;
      }
      .ansi-yellow {
        color: #ed8936;
      }
      .ansi-blue {
        color: #4299e1;
      }
      .ansi-magenta {
        color: #9f7aea;
      }
      .ansi-cyan {
        color: #38b2ac;
      }
      .ansi-white {
        color: #f7fafc;
      }
      .ansi-gray {
        color: #a0aec0;
      }
      .ansi-bright-red {
        color: #fc8181;
      }
      .ansi-bright-green {
        color: #68d391;
      }
      .ansi-bright-yellow {
        color: #f6e05e;
      }
      .ansi-bright-blue {
        color: #63b3ed;
      }
      .ansi-bright-magenta {
        color: #b794f6;
      }
      .ansi-bright-cyan {
        color: #4fd1c7;
      }
      .ansi-bright-white {
        color: #ffffff;
      }
      .ansi-dim {
        opacity: 0.6;
      }
      .ansi-bold {
        font-weight: bold;
      }

      @media (max-width: 768px) {
        .comparison-grid {
          grid-template-columns: 1fr;
        }

        .controls-row {
          flex-direction: column;
          align-items: stretch;
        }
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="header">
        <h1>🎨 Formatting Showcase</h1>
        <p>Explore all formatting options with side-by-side comparisons</p>
      </div>

      <div class="nav">
        <a href="index.html">← Back to Demos</a>
        <a href="basic-demo.html">Basic Usage</a>
        <a href="playground/">Interactive Playground</a>
      </div>

      <div class="content">
        <!-- Preset Comparison -->
        <div class="comparison-section">
          <h2><span class="icon">🎭</span>Preset Comparison</h2>
          <div class="explanation">
            <h4>Built-in Presets</h4>
            <p>
              Loggical comes with 4 built-in presets optimized for different use
              cases. Compare them side-by-side to see the differences.
            </p>
          </div>

          <div class="preset-buttons">
            <button class="preset-button active" onclick="showPresets()">
              Show All Presets
            </button>
            <button class="preset-button" onclick="showPreset('standard')">
              Standard Only
            </button>
            <button class="preset-button" onclick="showPreset('compact')">
              Compact Only
            </button>
            <button class="preset-button" onclick="showPreset('readable')">
              Readable Only
            </button>
            <button class="preset-button" onclick="showPreset('server')">
              Server Only
            </button>
          </div>

          <div class="comparison-grid" id="preset-comparison">
            <div class="format-example">
              <div class="format-header">Standard Preset</div>
              <div class="format-output" id="standard-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Compact Preset</div>
              <div class="format-output" id="compact-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Readable Preset</div>
              <div class="format-output" id="readable-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Server Preset</div>
              <div class="format-output" id="server-output"></div>
            </div>
          </div>

          <button class="demo-button" onclick="demonstratePresets()">
            🎯 Run Preset Demo
          </button>
        </div>

        <!-- Color Level Comparison -->
        <div class="comparison-section">
          <h2><span class="icon">🌈</span>Color Level Comparison</h2>
          <div class="explanation">
            <h4>Color Levels</h4>
            <p>
              Control the amount of colorization in your logs. From no colors
              (server-friendly) to enhanced colors (development-friendly).
            </p>
          </div>

          <div class="comparison-grid">
            <div class="format-example">
              <div class="format-header">None (No Colors)</div>
              <div class="format-output" id="color-none-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Basic Colors</div>
              <div class="format-output" id="color-basic-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Enhanced Colors</div>
              <div class="format-output" id="color-enhanced-output"></div>
            </div>
          </div>

          <button class="demo-button" onclick="demonstrateColors()">
            🎨 Run Color Demo
          </button>
        </div>

        <!-- Timestamp Formats -->
        <div class="comparison-section">
          <h2><span class="icon">⏰</span>Timestamp Formats</h2>
          <div class="explanation">
            <h4>Timestamp Options</h4>
            <p>
              Choose between different timestamp formats based on your needs.
              Short timestamps save space, while full timestamps provide
              complete information.
            </p>
          </div>

          <div class="comparison-grid">
            <div class="format-example">
              <div class="format-header">No Timestamps</div>
              <div class="format-output" id="timestamp-none-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Short Timestamps</div>
              <div class="format-output" id="timestamp-short-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Full Timestamps</div>
              <div class="format-output" id="timestamp-full-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Relative Timestamps</div>
              <div class="format-output" id="timestamp-relative-output"></div>
            </div>
          </div>

          <button class="demo-button" onclick="demonstrateTimestamps()">
            ⏱️ Run Timestamp Demo
          </button>
        </div>

        <!-- Object Formatting -->
        <div class="comparison-section">
          <h2><span class="icon">📦</span>Object Formatting</h2>
          <div class="explanation">
            <h4>Object Display Options</h4>
            <p>
              Control how objects and complex data are displayed. Compact
              formatting saves space, while expanded formatting is more
              readable.
            </p>
          </div>

          <div class="comparison-grid">
            <div class="format-example">
              <div class="format-header">Compact Objects</div>
              <div class="format-output" id="object-compact-output"></div>
            </div>
            <div class="format-example">
              <div class="format-header">Expanded Objects</div>
              <div class="format-output" id="object-expanded-output"></div>
            </div>
          </div>

          <button class="demo-button" onclick="demonstrateObjects()">
            📋 Run Object Demo
          </button>
        </div>

        <!-- Pre-configured Instances -->
        <div class="comparison-section">
          <h2><span class="icon">⚡</span>Pre-configured Instances</h2>
          <div class="explanation">
            <h4>Ready-to-Use Logger Instances</h4>
            <p>
              Import and use these pre-configured logger instances directly.
              Each is optimized for specific use cases and environments.
            </p>
            <div class="code-example">
              <code
                >import { logger, compactLogger, readableLogger, serverLogger }
                from 'loggical'</code
              >
            </div>
          </div>
          <div class="demo-controls">
            <button class="demo-button" onclick="demonstrateInstances()">
              ⚡ Show Pre-configured Instances
            </button>
          </div>
          <div class="output-container">
            <div id="instances-output" class="output"></div>
          </div>
        </div>

        <!-- Custom Configuration -->
        <div class="comparison-section">
          <h2><span class="icon">⚙️</span>Custom Configuration</h2>
          <div class="explanation">
            <h4>Build Your Own Format</h4>
            <p>
              Mix and match different formatting options to create your perfect
              logging format. See the results in real-time.
            </p>
          </div>

          <div class="controls-panel">
            <div class="controls-row">
              <div class="control-group">
                <label>Color Level</label>
                <select id="custom-color">
                  <option value="NONE">None</option>
                  <option value="BASIC">Basic</option>
                  <option value="ENHANCED" selected>Enhanced</option>
                </select>
              </div>
              <div class="control-group">
                <label>Prefix</label>
                <input
                  type="text"
                  id="custom-prefix"
                  placeholder="e.g., CUSTOM"
                  value="DEMO"
                />
              </div>
              <div class="control-group">
                <div class="checkbox-group">
                  <input type="checkbox" id="custom-timestamps" checked />
                  <label for="custom-timestamps">Timestamps</label>
                </div>
              </div>
              <div class="control-group">
                <div class="checkbox-group">
                  <input type="checkbox" id="custom-short-timestamp" checked />
                  <label for="custom-short-timestamp">Short Timestamps</label>
                </div>
              </div>
            </div>
            <div class="controls-row">
              <div class="control-group">
                <div class="checkbox-group">
                  <input type="checkbox" id="custom-symbols" checked />
                  <label for="custom-symbols">Use Symbols</label>
                </div>
              </div>
              <div class="control-group">
                <div class="checkbox-group">
                  <input type="checkbox" id="custom-compact" />
                  <label for="custom-compact">Compact Objects</label>
                </div>
              </div>
              <div class="control-group">
                <div class="checkbox-group">
                  <input type="checkbox" id="custom-space" />
                  <label for="custom-space">Space Messages</label>
                </div>
              </div>
              <div class="control-group">
                <div class="checkbox-group">
                  <input type="checkbox" id="custom-separators" />
                  <label for="custom-separators">Show Separators</label>
                </div>
              </div>
            </div>
          </div>

          <div class="format-example">
            <div class="format-header">Your Custom Format</div>
            <div class="format-output" id="custom-output"></div>
          </div>

          <button class="demo-button" onclick="demonstrateCustom()">
            🔧 Test Custom Format
          </button>
          <button class="demo-button" onclick="autoUpdateCustom()">
            🔄 Auto Update
          </button>
        </div>
      </div>
    </div>

    <script type="module">
      import {
        Logger,
        ColorLevel,
        logger,
        compactLogger,
        readableLogger,
        serverLogger,
      } from "loggical";

      // ANSI to HTML conversion function
      function ansiToHtml(text) {
        const ansiMap = {
          0: "reset",
          1: "ansi-bold",
          2: "ansi-dim",
          30: "ansi-black",
          31: "ansi-red",
          32: "ansi-green",
          33: "ansi-yellow",
          34: "ansi-blue",
          35: "ansi-magenta",
          36: "ansi-cyan",
          37: "ansi-white",
          90: "ansi-gray",
          91: "ansi-bright-red",
          92: "ansi-bright-green",
          93: "ansi-bright-yellow",
          94: "ansi-bright-blue",
          95: "ansi-bright-magenta",
          96: "ansi-bright-cyan",
          97: "ansi-bright-white",
          39: "reset-fg",
        };

        let html = text
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");

        let openSpans = [];

        html = html.replace(
          /(?:\033\[|\x1b\[|\[)([0-9;]+)m/g,
          (match, codes) => {
            const codeList = codes.split(";");
            let result = "";

            codeList.forEach((code) => {
              if (code === "0" || code === "39") {
                result += "</span>".repeat(openSpans.length);
                openSpans = [];
              } else if (ansiMap[code]) {
                const className = ansiMap[code];
                if (className !== "reset" && className !== "reset-fg") {
                  result += `<span class="${className}">`;
                  openSpans.push(className);
                }
              }
            });

            return result;
          }
        );

        html += "</span>".repeat(openSpans.length);
        return html;
      }

      // Custom transport for capturing output
      class DemoTransport {
        constructor(outputElementId) {
          this.name = "demo";
          this.outputElementId = outputElementId;
          this.messages = [];
          this.colorLevel = ColorLevel.ENHANCED;
        }

        write(formattedMessage, metadata) {
          this.messages.push(formattedMessage);
          const element = document.getElementById(this.outputElementId);
          if (element) {
            if (this.colorLevel === ColorLevel.NONE) {
              const plainText = formattedMessage.replace(
                /(?:\033\[|\x1b\[|\[)([0-9;]+)m/g,
                ""
              );
              element.textContent += plainText + "\n";
            } else {
              const htmlOutput = ansiToHtml(formattedMessage);
              element.innerHTML += htmlOutput + "\n";
            }
          }
        }

        setColorLevel(colorLevel) {
          this.colorLevel = colorLevel;
        }

        clear() {
          this.messages = [];
          const element = document.getElementById(this.outputElementId);
          if (element) {
            element.innerHTML = "";
          }
        }
      }

      // Preset configurations
      const presets = {
        standard: { preset: "standard" },
        compact: { preset: "compact" },
        readable: { preset: "readable" },
        server: { preset: "server" },
      };

      // Sample data for demonstrations
      const sampleData = {
        user: {
          id: 12345,
          name: "John Doe",
          email: "john@example.com",
          preferences: {
            theme: "dark",
            notifications: true,
            language: "en",
          },
        },
        metrics: {
          responseTime: "125ms",
          memoryUsage: "45MB",
          cpuUsage: "12%",
        },
      };

      // Global functions for demo buttons
      window.demonstratePresets = function () {
        Object.keys(presets).forEach((presetName) => {
          const transport = new DemoTransport(`${presetName}-output`);
          transport.clear();

          const logger = new Logger({
            ...presets[presetName],
            transports: [transport],
          });

          logger.debug("Debug message for testing");
          logger.info("Application started successfully", { version: "1.0.0" });
          logger.warn("Configuration fallback used");
          logger.error("Database connection failed", { retries: 3 });
          logger.info("User data", sampleData.user);
        });
      };

      window.demonstrateColors = function () {
        const colorLevels = ["NONE", "BASIC", "ENHANCED"];

        colorLevels.forEach((colorLevel) => {
          const outputId = `color-${colorLevel.toLowerCase()}-output`;
          const transport = new DemoTransport(outputId);
          transport.setColorLevel(ColorLevel[colorLevel]);
          transport.clear();

          const logger = new Logger({
            colorLevel: ColorLevel[colorLevel],
            transports: [transport],
          });

          logger.debug("Debug message with colors");
          logger.info("Info message with colors");
          logger.warn("Warning message with colors");
          logger.error("Error message with colors");
          logger.highlight("Highlighted message");
        });
      };

      window.demonstrateTimestamps = function () {
        const timestampConfigs = [
          { id: "timestamp-none-output", timestamped: false },
          {
            id: "timestamp-short-output",
            timestamped: true,
            shortTimestamp: true,
          },
          {
            id: "timestamp-full-output",
            timestamped: true,
            shortTimestamp: false,
          },
          {
            id: "timestamp-relative-output",
            timestamped: true,
            relativeTimestamps: true,
          },
        ];

        timestampConfigs.forEach((config) => {
          const transport = new DemoTransport(config.id);
          transport.clear();

          const logger = new Logger({
            ...config,
            transports: [transport],
          });

          logger.info("First message");
          setTimeout(() => {
            logger.info("Second message (+100ms)");
          }, 100);
          setTimeout(() => {
            logger.warn("Third message (+250ms)");
          }, 250);
        });
      };

      window.demonstrateObjects = function () {
        const objectConfigs = [
          { id: "object-compact-output", compactObjects: true },
          { id: "object-expanded-output", compactObjects: false },
        ];

        objectConfigs.forEach((config) => {
          const transport = new DemoTransport(config.id);
          transport.clear();

          const logger = new Logger({
            ...config,
            transports: [transport],
          });

          logger.info("Simple object", { status: "success", count: 42 });
          logger.info("Complex object", sampleData.user);
          logger.info("Array data", ["apple", "banana", "cherry"]);
          logger.info("Mixed data", {
            timestamp: new Date(),
            metrics: sampleData.metrics,
            tags: ["important", "user-action"],
          });
        });
      };

      window.demonstrateCustom = function () {
        const colorLevel = document.getElementById("custom-color").value;
        const prefix = document.getElementById("custom-prefix").value;
        const timestamped =
          document.getElementById("custom-timestamps").checked;
        const shortTimestamp = document.getElementById(
          "custom-short-timestamp"
        ).checked;
        const useSymbols = document.getElementById("custom-symbols").checked;
        const compactObjects =
          document.getElementById("custom-compact").checked;
        const spaceMessages = document.getElementById("custom-space").checked;
        const showSeparators =
          document.getElementById("custom-separators").checked;

        const transport = new DemoTransport("custom-output");
        transport.setColorLevel(ColorLevel[colorLevel]);
        transport.clear();

        const logger = new Logger({
          colorLevel: ColorLevel[colorLevel],
          prefix: prefix || undefined,
          timestamped,
          shortTimestamp,
          useSymbols,
          compactObjects,
          spaceMessages,
          showSeparators,
          transports: [transport],
        });

        logger.debug("Debug message with custom format");
        logger.info("Application event", {
          action: "user_login",
          userId: "user123",
        });
        logger.warn("Performance warning", sampleData.metrics);
        logger.error("Operation failed", {
          error: "Network timeout",
          retries: 3,
        });
        logger.highlight("Important notification");
      };

      window.showPresets = function () {
        document
          .querySelectorAll(".preset-button")
          .forEach((btn) => btn.classList.remove("active"));
        event.target.classList.add("active");

        const grid = document.getElementById("preset-comparison");
        grid.innerHTML = `
                <div class="format-example">
                    <div class="format-header">Standard Preset</div>
                    <div class="format-output" id="standard-output"></div>
                </div>
                <div class="format-example">
                    <div class="format-header">Compact Preset</div>
                    <div class="format-output" id="compact-output"></div>
                </div>
                <div class="format-example">
                    <div class="format-header">Readable Preset</div>
                    <div class="format-output" id="readable-output"></div>
                </div>
                <div class="format-example">
                    <div class="format-header">Server Preset</div>
                    <div class="format-output" id="server-output"></div>
                </div>
            `;
      };

      window.showPreset = function (presetName) {
        document
          .querySelectorAll(".preset-button")
          .forEach((btn) => btn.classList.remove("active"));
        event.target.classList.add("active");

        const grid = document.getElementById("preset-comparison");
        const presetTitle =
          presetName.charAt(0).toUpperCase() + presetName.slice(1);
        grid.innerHTML = `
                <div class="format-example" style="grid-column: 1 / -1;">
                    <div class="format-header">${presetTitle} Preset</div>
                    <div class="format-output" id="${presetName}-output"></div>
                </div>
            `;
      };

      let autoUpdateInterval;
      window.autoUpdateCustom = function () {
        if (autoUpdateInterval) {
          clearInterval(autoUpdateInterval);
          autoUpdateInterval = null;
          event.target.textContent = "🔄 Auto Update";
          return;
        }

        event.target.textContent = "⏹️ Stop Auto Update";
        demonstrateCustom();

        autoUpdateInterval = setInterval(() => {
          demonstrateCustom();
        }, 2000);
      };

      window.demonstrateInstances = function () {
        const transport = new DemoTransport("instances-output");
        transport.clear();

        const message = "User session started";
        const data = {
          userId: 12345,
          sessionId: "sess_abc123",
          userAgent: "Mozilla/5.0 (Chrome)",
          timestamp: new Date().toISOString(),
        };

        transport.write("=== Pre-configured Logger Instances ===\n");

        // Standard logger
        transport.write("📋 logger (Standard):");
        const standardTransport = new DemoTransport("instances-output", false);
        const standardLogger = new Logger({ transports: [standardTransport] });
        standardLogger.info(message, data);

        // Compact logger
        transport.write("\n💾 compactLogger (Space-efficient):");
        const compactTransport = new DemoTransport("instances-output", false);
        const demoCompactLogger = new Logger({
          preset: "compact",
          transports: [compactTransport],
        });
        demoCompactLogger.info(message, data);

        // Readable logger
        transport.write("\n👁️ readableLogger (Development-friendly):");
        const readableTransport = new DemoTransport("instances-output", false);
        const demoReadableLogger = new Logger({
          preset: "readable",
          transports: [readableTransport],
        });
        demoReadableLogger.info(message, data);

        // Server logger
        transport.write("\n🖥️ serverLogger (Production-optimized):");
        const serverTransport = new DemoTransport("instances-output", false);
        const demoServerLogger = new Logger({
          preset: "server",
          transports: [serverTransport],
        });
        demoServerLogger.info(message, data);

        transport.write(
          "\n\n💡 Usage: Import and use directly without any configuration!"
        );
        transport.write("   import { compactLogger } from 'loggical'");
        transport.write("   compactLogger.info('Ready to go!')");
      };

      // Add event listeners for custom controls
      document.addEventListener("DOMContentLoaded", function () {
        const customControls = [
          "custom-color",
          "custom-prefix",
          "custom-timestamps",
          "custom-short-timestamp",
          "custom-symbols",
          "custom-compact",
          "custom-space",
          "custom-separators",
        ];

        customControls.forEach((id) => {
          const element = document.getElementById(id);
          if (element) {
            element.addEventListener("change", demonstrateCustom);
            element.addEventListener("input", demonstrateCustom);
          }
        });

        // Initial demonstrations
        demonstratePresets();
        demonstrateColors();
        demonstrateTimestamps();
        demonstrateObjects();
        demonstrateCustom();
      });
    </script>
  </body>
</html>

Features

  • ✅ Side-by-side preset comparisons (compact, readable, server)
  • ✅ Live color level configuration
  • ✅ Dynamic timestamp format switching
  • ✅ Object formatting options showcase
  • ✅ Interactive configuration builder with real-time preview

🚀 Running This Demo

bash
# Build the package first
pnpm run build

# Start the demo server
node examples/serve.js

# Then open in your browser:
# http://localhost:3000/formatting-demo.html

This interactive demo is automatically embedded from examples/formatting-demo.html.

Released under the MIT License.