Skip to content

Instantly share code, notes, and snippets.

@simonw
Created November 11, 2024 01:55
Show Gist options
  • Save simonw/1af1cd4f51c3dc2fa84cca0fa4746a7e to your computer and use it in GitHub Desktop.
Save simonw/1af1cd4f51c3dc2fa84cca0fa4746a7e to your computer and use it in GitHub Desktop.

Web Locks API Browser Support Timeline

human (Nov 10, 2024, 05:35 PM)

Build an artifact to render this data in a timeline

From https://bcd.developer.mozilla.org/bcd/api/v0/current/api.Lock.json

paste.txt

{"data":{"__compat":{"mdn_url":"/en-US/docs/Web/API/Lock","source_file":"api/Lock.json","spec_url":"https://w3c.github.io/web-locks/#api-lock","status":{"deprecated":false,"experimental":false,"standard_track":true},"support":{"chrome":[{"version_added":"69","release_date":"2018-09-04"}],"chrome_android":[{"version_added":"69","release_date":"2018-09-04"}],"edge":[{"version_added":"79","release_date":"2020-01-15"}],"firefox":[{"version_added":"96","release_date":"2022-01-11"}],"firefox_android":[{"version_added":"96","release_date":"2022-01-11"}],"ie":[{"version_added":false}],"oculus":[{"version_added":"6.0"}],"opera":[{"version_added":"56","release_date":"2018-09-25"}],"opera_android":[{"version_added":"48","release_date":"2018-11-08"}],"safari":[{"version_added":"15.4","release_date":"2022-03-14"}],"safari_ios":[{"version_added":"15.4","release_date":"2022-03-14"}],"samsunginternet_android":[{"version_added":"10.0","release_date":"2019-08-22"}],"webview_android":[{"version_added":"69","release_date":"2018-09-04"}],"webview_ios":[{"version_added":"15.4","release_date":"2022-03-14"}]},"tags":["web-features:web-locks"]},"mode":{"__compat":{"mdn_url":"/en-US/docs/Web/API/Lock/mode","source_file":"api/Lock.json","spec_url":"https://w3c.github.io/web-locks/#dom-lock-mode","status":{"deprecated":false,"experimental":false,"standard_track":true},"support":{"chrome":[{"version_added":"69","release_date":"2018-09-04"}],"chrome_android":[{"version_added":"69","release_date":"2018-09-04"}],"edge":[{"version_added":"79","release_date":"2020-01-15"}],"firefox":[{"version_added":"96","release_date":"2022-01-11"}],"firefox_android":[{"version_added":"96","release_date":"2022-01-11"}],"ie":[{"version_added":false}],"oculus":[{"version_added":"6.0"}],"opera":[{"version_added":"56","release_date":"2018-09-25"}],"opera_android":[{"version_added":"48","release_date":"2018-11-08"}],"safari":[{"version_added":"15.4","release_date":"2022-03-14"}],"safari_ios":[{"version_added":"15.4","release_date":"2022-03-14"}],"samsunginternet_android":[{"version_added":"10.0","release_date":"2019-08-22"}],"webview_android":[{"version_added":"69","release_date":"2018-09-04"}],"webview_ios":[{"version_added":"15.4","release_date":"2022-03-14"}]},"tags":["web-features:web-locks"]}},"name":{"__compat":{"mdn_url":"/en-US/docs/Web/API/Lock/name","source_file":"api/Lock.json","spec_url":"https://w3c.github.io/web-locks/#dom-lock-name","status":{"deprecated":false,"experimental":false,"standard_track":true},"support":{"chrome":[{"version_added":"69","release_date":"2018-09-04"}],"chrome_android":[{"version_added":"69","release_date":"2018-09-04"}],"edge":[{"version_added":"79","release_date":"2020-01-15"}],"firefox":[{"version_added":"96","release_date":"2022-01-11"}],"firefox_android":[{"version_added":"96","release_date":"2022-01-11"}],"ie":[{"version_added":false}],"oculus":[{"version_added":"6.0"}],"opera":[{"version_added":"56","release_date":"2018-09-25"}],"opera_android":[{"version_added":"48","release_date":"2018-11-08"}],"safari":[{"version_added":"15.4","release_date":"2022-03-14"}],"safari_ios":[{"version_added":"15.4","release_date":"2022-03-14"}],"samsunginternet_android":[{"version_added":"10.0","release_date":"2019-08-22"}],"webview_android":[{"version_added":"69","release_date":"2018-09-04"}],"webview_ios":[{"version_added":"15.4","release_date":"2022-03-14"}]},"tags":["web-features:web-locks"]}}},"query":"api.Lock","browsers":{"chrome":{"accepts_flags":true,"accepts_webextensions":true,"name":"Chrome","pref_url":"chrome://flags","preview_name":"Canary","releases":{"131":{"engine":"Blink","engine_version":"131","release_date":"2024-11-12","status":"current"},"132":{"engine":"Blink","engine_version":"132","release_date":"2025-01-14","status":"beta"},"133":{"engine":"Blink","engine_version":"133","release_date":"2025-02-04","status":"nightly"},"134":{"engine":"Blink","engine_version":"134","status":"planned"}},"type":"desktop"},"chrome_android":{"accepts_flags":true,"accepts_webextensions":false,"name":"Chrome Android","pref_url":"chrome://flags","releases":{"131":{"engine":"Blink","engine_version":"131","release_date":"2024-11-12","status":"current"},"132":{"engine":"Blink","engine_version":"132","release_date":"2025-01-14","status":"beta"},"133":{"engine":"Blink","engine_version":"133","release_date":"2025-02-04","status":"nightly"},"134":{"engine":"Blink","engine_version":"134","status":"planned"}},"type":"mobile","upstream":"chrome"},"deno":{"accepts_flags":true,"accepts_webextensions":false,"name":"Deno","releases":{"1.45":{"engine":"V8","engine_version":"12.7","release_date":"2024-07-11","release_notes":"https://deno.com/blog/v1.45","status":"current"}},"type":"server"},"edge":{"accepts_flags":true,"accepts_webextensions":true,"name":"Edge","pref_url":"about:flags","releases":{"130":{"engine":"Blink","engine_version":"130","release_date":"2024-10-17","release_notes":"https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-1300284946-october-17-2024","status":"current"},"131":{"engine":"Blink","engine_version":"131","release_date":"2024-11-14","status":"beta"},"132":{"engine":"Blink","engine_version":"132","release_date":"2025-01-09","status":"nightly"},"133":{"engine":"Blink","engine_version":"133","release_date":"2025-02-06","status":"planned"}},"type":"desktop","upstream":"chrome"},"firefox":{"accepts_flags":true,"accepts_webextensions":true,"name":"Firefox","pref_url":"about:config","preview_name":"Nightly","releases":{"128":{"engine":"Gecko","engine_version":"128","release_date":"2024-07-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/128","status":"esr"},"132":{"engine":"Gecko","engine_version":"132","release_date":"2024-10-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/132","status":"current"},"133":{"engine":"Gecko","engine_version":"133","release_date":"2024-11-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/133","status":"beta"},"134":{"engine":"Gecko","engine_version":"134","release_date":"2025-01-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/134","status":"nightly"},"135":{"engine":"Gecko","engine_version":"135","release_date":"2025-02-04","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/135","status":"planned"}},"type":"desktop"},"firefox_android":{"accepts_flags":false,"accepts_webextensions":true,"name":"Firefox for Android","pref_url":"about:config","releases":{"128":{"engine":"Gecko","engine_version":"128","release_date":"2024-07-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/128","status":"esr"},"132":{"engine":"Gecko","engine_version":"132","release_date":"2024-10-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/132","status":"current"},"133":{"engine":"Gecko","engine_version":"133","release_date":"2024-11-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/133","status":"beta"},"134":{"engine":"Gecko","engine_version":"134","release_date":"2025-01-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/134","status":"nightly"},"135":{"engine":"Gecko","engine_version":"135","release_date":"2025-02-04","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/135","status":"planned"}},"type":"mobile","upstream":"firefox"},"ie":{"accepts_flags":false,"accepts_webextensions":false,"name":"Internet Explorer","releases":{},"type":"desktop"},"nodejs":{"accepts_flags":true,"accepts_webextensions":false,"name":"Node.js","releases":{"22.0.0":{"engine":"V8","engine_version":"12.4","release_date":"2024-04-24","release_notes":"https://nodejs.org/en/blog/release/v22.0.0","status":"esr"},"23.0.0":{"engine":"V8","engine_version":"12.9","release_date":"2024-10-16","release_notes":"https://nodejs.org/en/blog/release/v23.0.0","status":"current"}},"type":"server"},"oculus":{"accepts_flags":true,"accepts_webextensions":false,"name":"Quest Browser","pref_url":"chrome://flags","releases":{"23.0":{"engine":"Blink","engine_version":"104","release_date":"2022-08-15","status":"current"}},"type":"xr","upstream":"chrome_android"},"opera":{"accepts_flags":true,"accepts_webextensions":true,"name":"Opera","pref_url":"opera://flags","releases":{"113":{"engine":"Blink","engine_version":"127","release_date":"2024-08-22","release_notes":"https://blogs.opera.com/desktop/2024/08/opera-113/","status":"current"},"114":{"engine":"Blink","engine_version":"128","status":"beta"},"115":{"engine":"Blink","engine_version":"129","status":"nightly"}},"type":"desktop","upstream":"chrome"},"opera_android":{"accepts_flags":false,"accepts_webextensions":false,"name":"Opera Android","releases":{"84":{"engine":"Blink","engine_version":"127","release_date":"2024-08-26","status":"current"}},"type":"mobile","upstream":"chrome_android"},"safari":{"accepts_flags":true,"accepts_webextensions":true,"name":"Safari","preview_name":"TP","releases":{"18.1":{"engine":"WebKit","engine_version":"619.2.8","release_date":"2024-10-28","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-18_1-release-notes","status":"current"},"18.2":{"engine":"WebKit","engine_version":"620.1.11","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-18_2-release-notes","status":"beta"}},"type":"desktop"},"safari_ios":{"accepts_flags":true,"accepts_webextensions":true,"name":"Safari on iOS","releases":{"18.1":{"engine":"WebKit","engine_version":"619.2.8","release_date":"2024-10-28","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-18_1-release-notes","status":"current"},"18.2":{"engine":"WebKit","engine_version":"620.1.11","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-18_2-release-notes","status":"beta"}},"type":"mobile","upstream":"safari"},"samsunginternet_android":{"accepts_flags":false,"accepts_webextensions":false,"name":"Samsung Internet","releases":{"26.0":{"engine":"Blink","engine_version":"122","release_date":"2024-06-07","status":"current"},"27.0":{"engine":"Blink","engine_version":"125","status":"beta"}},"type":"mobile","upstream":"chrome_android"},"webview_android":{"accepts_flags":false,"accepts_webextensions":false,"name":"WebView Android","releases":{"131":{"engine":"Blink","engine_version":"131","release_date":"2024-11-12","status":"current"},"132":{"engine":"Blink","engine_version":"132","release_date":"2025-01-14","status":"beta"},"133":{"engine":"Blink","engine_version":"133","release_date":"2025-02-04","status":"nightly"},"134":{"engine":"Blink","engine_version":"134","status":"planned"}},"type":"mobile","upstream":"chrome_android"},"webview_ios":{"accepts_flags":false,"accepts_webextensions":false,"name":"WebView on iOS","releases":{"18.1":{"engine":"WebKit","engine_version":"619.2.8","release_date":"2024-10-28","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-18_1-release-notes","status":"current"},"18.2":{"engine":"WebKit","engine_version":"620.1.11","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-18_2-release-notes","status":"beta"}},"type":"mobile","upstream":"safari_ios"}}}

assistant (Nov 10, 2024, 05:35 PM)

I'll create a timeline visualization to show when different browsers added support for the Web Locks API. I'll use React with Tailwind CSS to create this visualization.

create Browser Support Timeline for Web Locks API

import React from 'react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';

const Timeline = () => {
  // Parse and sort the browser support data
  const supportData = [
    { browser: 'Chrome', date: '2018-09-04', version: '69' },
    { browser: 'Chrome Android', date: '2018-09-04', version: '69' },
    { browser: 'Opera', date: '2018-09-25', version: '56' },
    { browser: 'Opera Android', date: '2018-11-08', version: '48' },
    { browser: 'Samsung Internet', date: '2019-08-22', version: '10.0' },
    { browser: 'Edge', date: '2020-01-15', version: '79' },
    { browser: 'Firefox', date: '2022-01-11', version: '96' },
    { browser: 'Firefox Android', date: '2022-01-11', version: '96' },
    { browser: 'Safari', date: '2022-03-14

human (Nov 10, 2024, 05:36 PM)

don't use React, just HTML and CSS and JavaScript. Give me a textarea I can paste the JSON into

assistant (Nov 10, 2024, 05:36 PM)

I'll create a simple HTML page with a textarea for JSON input and a visualization that updates when the JSON changes.

create Browser Support Timeline

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      font-family: system-ui, -apple-system, sans-serif;
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
    }
    
    #input-section {
      margin-bottom: 20px;
    }
    
    #json-input {
      width: 100%;
      height: 150px;
      margin-bottom: 10px;
      font-family: monospace;
      padding: 8px;
    }
    
    #timeline {
      position: relative;
      border-left: 2px solid #ccc;
      margin-left: 20px;
      padding-left: 20px;
    }
    
    .event {
      margin-bottom: 20px;
      position: relative;
    }
    
    .event::before {
      content: '';
      position: absolute;
      left: -26px;
      top: 5px;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: #3b82f6;
    }
    
    .event-date {
      font-size: 0.875rem;
      color: #666;
    }
    
    .event-browser {
      font-weight: 600;
      color: #1f2937;
    }
    
    .event-version {
      color: #059669;
    }

    .error {
      color: #dc2626;
      margin-top: 8px;
      display: none;
    }
  </style>
</head>
<body>
  <div id="input-section">
    <h2>Browser Support Timeline Viewer</h2>
    <p>Paste BCD JSON data below to visualize browser support timeline:</p>
    <textarea id="json-input" placeholder="Paste BCD JSON here..."></textarea>
    <div id="error" class="error"></div>
  </div>
  <div id="timeline"></div>

  <script>
    const extractSupportData = (data) => {
      const browsers = data.browsers;
      const support = data.data.__compat.support;
      
      const supportData = [];
      
      for (const [browserName, supportInfo] of Object.entries(support)) {
        if (!supportInfo[0]?.version_added || !supportInfo[0]?.release_date) continue;
        
        supportData.push({
          browser: browsers[browserName]?.name || browserName,
          version: supportInfo[0].version_added,
          date: supportInfo[0].release_date
        });
      }
      
      return supportData.sort((a, b) => new Date(a.date) - new Date(b.date));
    };

    const formatDate = (dateStr) => {
      return new Date(dateStr).toLocaleDateString('en-US', {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      });
    };

    const renderTimeline = (supportData) => {
      const timeline = document.getElementById('timeline');
      timeline.innerHTML = '';
      
      supportData.forEach(item => {
        const event = document.createElement('div');
        event.className = 'event';
        event.innerHTML = `
          <div class="event-date">${formatDate(item.date)}</div>
          <div>
            <span class="event-browser">${item.browser}</span>
            <span class="event-version">v${item.version}</span>
          </div>
        `;
        timeline.appendChild(event);
      });
    };

    const jsonInput = document.getElementById('json-input');
    const errorDiv = document.getElementById('error');

    jsonInput.addEventListener('input', () => {
      try {
        const data = JSON.parse(jsonInput.value);
        const supportData = extractSupportData(data);
        renderTimeline(supportData);
        errorDiv.style.display = 'none';
      } catch (err) {
        errorDiv.textContent = 'Invalid JSON data: ' + err.message;
        errorDiv.style.display = 'block';
      }
    });
  </script>
</body>
</html>

I've created a simple HTML page with:

  1. A textarea where you can paste the BCD (Browser Compatibility Data) JSON
  2. A vertical timeline visualization that shows:
    • When each browser added support for the Web Locks API
    • The browser name and version number
    • The release date in a readable format

Features:

  • Automatically updates as you paste/edit the JSON
  • Sorts events chronologically
  • Shows error messages if the JSON is invalid
  • Responsive design that works on different screen sizes
  • Clean, minimal styling

You can paste the JSON data into the textarea and the timeline will automatically update to show when each browser added support for the Web Locks API. The visualization shows both the browser version and the exact date when support was added.

Would you like me to make any adjustments to the visualization or add any additional features?

human (Nov 10, 2024, 05:37 PM)

An error occurred while trying to run the generated artifact. Unexpected token (79:7)

Can you fix this error in browser-timeline?

assistant (Nov 10, 2024, 05:37 PM)

The error seems to be related to a partial content cutoff. Let me fix this by providing the complete HTML code.

rewrite Browser Support Timeline

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      font-family: system-ui, -apple-system, sans-serif;
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
    }
    
    #input-section {
      margin-bottom: 20px;
    }
    
    #json-input {
      width: 100%;
      height: 150px;
      margin-bottom: 10px;
      font-family: monospace;
      padding: 8px;
    }
    
    #timeline {
      position: relative;
      border-left: 2px solid #ccc;
      margin-left: 20px;
      padding-left: 20px;
    }
    
    .event {
      margin-bottom: 20px;
      position: relative;
    }
    
    .event::before {
      content: '';
      position: absolute;
      left: -26px;
      top: 5px;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: #3b82f6;
    }
    
    .event-date {
      font-size: 0.875rem;
      color: #666;
    }
    
    .event-browser {
      font-weight: 600;
      color: #1f2937;
    }
    
    .event-version {
      color: #059669;
      margin-left: 4px;
    }

    .error {
      color: #dc2626;
      margin-top: 8px;
      display: none;
      padding: 8px;
      background: #fef2f2;
      border-radius: 4px;
    }

    .not-supported {
      margin-top: 20px;
      padding: 12px;
      background: #f3f4f6;
      border-radius: 4px;
    }
  </style>
</head>
<body>
  <div id="input-section">
    <h2>Browser Support Timeline Viewer</h2>
    <p>Paste BCD JSON data below to visualize browser support timeline:</p>
    <textarea id="json-input" placeholder="Paste BCD JSON here..."></textarea>
    <div id="error" class="error"></div>
  </div>
  <div id="timeline"></div>

  <script>
    const extractSupportData = (data) => {
      const browsers = data.browsers;
      const support = data.data.__compat.support;
      
      const supportData = [];
      const notSupported = [];
      
      for (const [browserName, supportInfo] of Object.entries(support)) {
        if (!supportInfo[0]) continue;
        
        if (supportInfo[0].version_added === false) {
          notSupported.push(browsers[browserName]?.name || browserName);
          continue;
        }
        
        if (!supportInfo[0].version_added || !supportInfo[0].release_date) continue;
        
        supportData.push({
          browser: browsers[browserName]?.name || browserName,
          version: supportInfo[0].version_added,
          date: supportInfo[0].release_date
        });
      }
      
      return {
        supported: supportData.sort((a, b) => new Date(a.date) - new Date(b.date)),
        notSupported
      };
    };

    const formatDate = (dateStr) => {
      return new Date(dateStr).toLocaleDateString('en-US', {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      });
    };

    const renderTimeline = (data) => {
      const timeline = document.getElementById('timeline');
      timeline.innerHTML = '';
      
      // Add supported browsers timeline
      data.supported.forEach(item => {
        const event = document.createElement('div');
        event.className = 'event';
        event.innerHTML = `
          <div class="event-date">${formatDate(item.date)}</div>
          <div>
            <span class="event-browser">${item.browser}</span>
            <span class="event-version">v${item.version}</span>
          </div>
        `;
        timeline.appendChild(event);
      });

      // Add not supported browsers section if any
      if (data.notSupported.length > 0) {
        const notSupportedDiv = document.createElement('div');
        notSupportedDiv.className = 'not-supported';
        notSupportedDiv.innerHTML = `
          <strong>Not Supported:</strong> ${data.notSupported.join(', ')}
        `;
        timeline.appendChild(notSupportedDiv);
      }
    };

    const jsonInput = document.getElementById('json-input');
    const errorDiv = document.getElementById('error');

    jsonInput.addEventListener('input', () => {
      try {
        const data = JSON.parse(jsonInput.value);
        const supportData = extractSupportData(data);
        renderTimeline(supportData);
        errorDiv.style.display = 'none';
      } catch (err) {
        errorDiv.textContent = 'Invalid JSON data: ' + err.message;
        errorDiv.style.display = 'block';
      }
    });
  </script>
</body>
</html>

I've made several improvements to the code:

  1. Fixed the content cutoff issue
  2. Added better handling for browsers that don't support the feature
  3. Improved error message styling
  4. Added a separate section for browsers that explicitly don't support the feature
  5. Improved spacing and visual hierarchy

You can now paste the JSON into the textarea and it should work correctly. The timeline will show when each browser added support, and there's a separate section at the bottom that lists browsers that don't support the feature.

Would you like me to make any other adjustments to the visualization?

@simonw
Copy link
Author

simonw commented Nov 11, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment