<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <link rel="icon" type="image/png" href="/assets/icons/black_circle.png?v=1">
  <link rel="alternate icon" type="image/png" sizes="32x32" href="/assets/icons/black_circle.png?v=1">
  <link rel="apple-touch-icon" href="/assets/icons/black_circle.png?v=1">
  <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
  <meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="referrer" content="no-referrer-when-downgrade">
  <title>Pieces Documentation</title>

  <!-- Static SEO for root (non-JS link preview) -->
  <meta name="description"
    content="Pieces captures and resurfaces your workflow context so you never lose track of what you were doing. Documentation for installation, integration, and mastery.">
  <meta property="og:title" content="Pieces Documentation">
  <meta property="og:description"
    content="Install, integrate, and master Pieces tools. Secure on‑device context with great IDE &amp; browser integrations.">
  <meta property="og:type" content="website">
  <meta property="og:url" content="https://beta.docs.pieces.app/">
  <meta property="og:image"
    content="https://storage.googleapis.com/hashnode_product_documentation_assets/og_images/meet_pieces/meet_pieces.png">
  <meta property="og:site_name" content="Pieces">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:title" content="Pieces Documentation">
  <meta name="twitter:description"
    content="Install, integrate, and master Pieces tools. Secure on‑device context with great IDE &amp; browser integrations.">
  <meta name="twitter:image"
    content="https://storage.googleapis.com/hashnode_product_documentation_assets/og_images/meet_pieces/meet_pieces.png">
  <link rel="canonical" href="https://beta.docs.pieces.app/">

  <!-- Styrene Font - You'll need to add your Styrene font files or CDN link here -->
  <!-- Example: If you have Styrene hosted locally or via a service -->
  <!-- <link rel="stylesheet" href="/fonts/styrene/styrene.css"> -->

  <!-- Enhanced Resource Hints for Performance -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
  <link rel="dns-prefetch" href="https://storage.googleapis.com">
  <link rel="dns-prefetch" href="https://api.github.com">
  <link rel="dns-prefetch" href="https://cdn.hashnode.com">

  <!-- IBM Plex Sans & Mono -->
  <link
    href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=IBM+Plex+Mono:wght@100..700&display=swap"
    rel="stylesheet">

  <!-- Material Symbols (Outlined) — https://fonts.google.com/icons -->
  <link
    href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=block"
    rel="stylesheet">

  <!-- Google Tag Manager + Consent Mode v2: gtag default runs before gtm.js; GTM loads on every page -->
  <script>
    (function () {
      window.dataLayer = window.dataLayer || [];
      function gtag() { window.dataLayer.push(arguments); }
      window.gtag = gtag;

      function readAnalyticsConsentFromStorage() {
        try {
          var raw = localStorage.getItem('pieces_cookie_consent');
          if (!raw) return null;
          if (raw === 'accepted') return true;
          if (raw === 'rejected') return false;
          var prefs = JSON.parse(raw);
          if (prefs && typeof prefs.analytics === 'boolean') return prefs.analytics;
        } catch (e) { }
        return null;
      }

      var consent = readAnalyticsConsentFromStorage();
      var analyticsAllowed = consent === true;

      var defaultConsent = {
        ad_storage: analyticsAllowed ? 'granted' : 'denied',
        ad_user_data: analyticsAllowed ? 'granted' : 'denied',
        ad_personalization: analyticsAllowed ? 'granted' : 'denied',
        analytics_storage: analyticsAllowed ? 'granted' : 'denied',
        functionality_storage: 'granted',
        personalization_storage: analyticsAllowed ? 'granted' : 'denied',
        security_storage: 'granted'
      };
      // Only when preference is unknown: give the CMP time to call consent update before tags fire.
      if (consent === null) {
        defaultConsent.wait_for_update = 500;
      }
      gtag('consent', 'default', defaultConsent);

      if (window.__gtmLoaded) return;
      window.__gtmLoaded = true;
      (function (w, d, s, l, i) {
        w[l] = w[l] || []; w[l].push({
          'gtm.start': new Date().getTime(), event: 'gtm.js'
        }); var f = d.getElementsByTagName(s)[0],
          j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src =
            'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
      })(window, document, 'script', 'dataLayer', 'GTM-K8C6QWB');
    })();
  </script>
  <!-- End Google Tag Manager -->

  <!-- PiecesOS Tracking Script -->
  <!-- 
    PURPOSE: Track PiecesOS usage and send data to Google Analytics
    GOALS: 
    - Detect PiecesOS running on localhost (ports 39300-39399)
    - Fetch user and application data from PiecesOS API
    - Store data locally in IndexedDB for performance
    - Send tracking data to Google Analytics via dataLayer
    - Provide window.__PiecesOS API for debugging
  -->
  <script>
    (function () {
      // ---------------- CONFIG ----------------
      // PiecesOS port scanning range (PiecesOS typically runs on these ports)
      var BASE_PORT = 39300;
      var MAX_PORT_SCAN = 39399; // Scan from 39300 to 39399

      // PiecesOS API endpoints
      var HEALTH_ENDPOINT = "/.well-known/health";  // Health check endpoint
      var USER_ENDPOINT = "/user";                  // User data endpoint
      var APPS_ENDPOINT = "/applications";          // Applications data endpoint

      // Performance settings
      var FETCH_TIMEOUT = 2500;       // 2.5 second timeout for API calls

      // ---------------- helpers: fetch with timeout ----------------
      function fetchWithTimeout(url, ms) {
        // Fetch with timeout to prevent hanging requests
        return new Promise(function (resolve, reject) {
          var controller = new AbortController();
          var id = setTimeout(function () { controller.abort(); }, ms);
          fetch(url, { signal: controller.signal })
            .then(resolve)
            .catch(reject)
            .finally(function () { clearTimeout(id); });
        });
      }

      // ---------------- helpers: port scanning ----------------
      // Scan localhost ports 39300-39399 to find PiecesOS instance.
      // PiecesOS sends access-control-allow-origin: * so direct fetch works from any origin.
      function scanForPiecesOS() {
        var healthUrl = function (port) { return "http://localhost:" + port + HEALTH_ENDPOINT; };
        return new Promise(function (resolve, reject) {
          var currentPort = BASE_PORT;
          var maxConcurrent = 5; // Limit concurrent requests
          var activeRequests = 0;
          var foundPort = null;
          var completed = false;

          // Define allowed PiecesOS ports (inclusive range)
          var ALLOWED_PIECESOS_PORTS = [];
          for (var p = BASE_PORT; p <= MAX_PORT_SCAN; p++) {
            ALLOWED_PIECESOS_PORTS.push(p);
          }

          function tryPort(port) {
            if (completed) return;
            // Validate port is in allowed list
            if (ALLOWED_PIECESOS_PORTS.indexOf(port) === -1) {
              // Skip ports not in the allowed range
              return;
            }
            activeRequests++;
            var url = healthUrl(port);

            fetchWithTimeout(url, 1000).then(function (response) {
              if (completed) return;


              if (response && response.ok) {
                response.text().then(function (text) {
                  if (completed) return;


                  // Check if response looks like PiecesOS health response (contains UUID pattern)
                  // Expected format: "ok:d64a2162-3e8a-4220-888a-ee39bc53da8c"
                  if (text && text.match(/^ok:[a-f0-9-]{36}$/i)) {
                    completed = true;
                    foundPort = port;
                    resolve(port);
                  } else {
                    // Not PiecesOS, continue scanning
                    activeRequests--;
                    if (activeRequests === 0 && currentPort > MAX_PORT_SCAN) {
                      if (!foundPort) {
                        reject(new Error("PiecesOS not found on any port"));
                      }
                    }
                  }
                }).catch(function (err) {
                  // Not PiecesOS, continue scanning
                  activeRequests--;
                  if (activeRequests === 0 && currentPort > MAX_PORT_SCAN) {
                    if (!foundPort) {
                      reject(new Error("PiecesOS not found on any port"));
                    }
                  }
                });
              } else {
                // Not PiecesOS, continue scanning
                activeRequests--;
                if (activeRequests === 0 && currentPort > MAX_PORT_SCAN) {
                  if (!foundPort) {
                    reject(new Error("PiecesOS not found on any port"));
                  }
                }
              }
            }).catch(function (err) {
              // Not PiecesOS, continue scanning
              activeRequests--;
              if (activeRequests === 0 && currentPort > MAX_PORT_SCAN) {
                if (!foundPort) {
                  reject(new Error("PiecesOS not found on any port"));
                }
              }
            });
          }

          function startNextBatch() {
            if (completed || currentPort > MAX_PORT_SCAN) {
              if (activeRequests === 0 && !foundPort) {
                reject(new Error("PiecesOS not found on any port"));
              }
              return;
            }

            var batchSize = Math.min(maxConcurrent - activeRequests, MAX_PORT_SCAN - currentPort + 1);
            for (var i = 0; i < batchSize; i++) {
              if (currentPort <= MAX_PORT_SCAN) {
                tryPort(currentPort);
                currentPort++;
              }
            }

            // Start next batch when current batch completes
            setTimeout(startNextBatch, 100);
          }

          startNextBatch();
        });
      }

      // ---------------- helpers: parse and merge ----------------
      // Transform PiecesOS API responses into consistent format

      function normalizeUserPayload(rawUser) {
        // Extract and normalize user data from PiecesOS API response
        if (!rawUser || !rawUser.user) return null;
        var u = rawUser.user;
        return {
          id: u.id || null,
          descopeId: (u.descope && u.descope.descope_id) || null,
          email: u.email || null,
          name: u.name || null,
          picture: u.picture || null,
          created: (u.created && u.created.value) || null,
          updated: (u.updated && u.updated.value) || null,
          allocation: u.allocation || null,
          providers: u.providers || null,
          version: (u.allocation && u.allocation.version) || null,
          region: (u.allocation && u.allocation.region) || null,
          cloudUrls: (u.allocation && u.allocation.urls) || null
        };
      }

      function normalizeAppsPayload(rawApps) {
        // Extract and normalize applications data from PiecesOS API response
        if (!rawApps || !rawApps.iterable) return [];
        return rawApps.iterable.map(function (a) {
          return {
            id: a.id,
            name: a.name,
            version: a.version,
            platform: a.platform,
            privacy: a.privacy,
            capabilities: a.capabilities
          };
        });
      }

      function createDataObject(userData, appsData, port) {
        // Create data object from fresh PiecesOS API responses
        return {
          user: userData,
          applications: appsData,
          piecesOSPort: port,
          lastFetchedAt: Date.now()
        };
      }

      // ---------------- helpers: IndexedDB storage ----------------
      // Store PiecesOS data locally for performance and offline access

      function openIndexedDB() {
        return new Promise(function (resolve, reject) {
          var request = indexedDB.open("PiecesOSData", 1);
          request.onerror = function () { reject(request.error); };
          request.onsuccess = function () { resolve(request.result); };
          request.onupgradeneeded = function (event) {
            var db = event.target.result;
            if (!db.objectStoreNames.contains("piecesData")) {
              db.createObjectStore("piecesData", { keyPath: "id" });
            }
          };
        });
      }

      function storeInIndexedDB(data) {
        return openIndexedDB().then(function (db) {
          return new Promise(function (resolve, reject) {
            var transaction = db.transaction(["piecesData"], "readwrite");
            var store = transaction.objectStore("piecesData");
            var request = store.put({
              id: "current",
              data: data,
              timestamp: Date.now()
            });
            request.onsuccess = function () { resolve(request.result); };
            request.onerror = function () { reject(request.error); };
          });
        });
      }

      function getFromIndexedDB() {
        return openIndexedDB().then(function (db) {
          return new Promise(function (resolve, reject) {
            var transaction = db.transaction(["piecesData"], "readonly");
            var store = transaction.objectStore("piecesData");
            var request = store.get("current");
            request.onsuccess = function () { resolve(request.result); };
            request.onerror = function () { reject(request.error); };
          });
        });
      }

      // ---------------- helpers: GA4 / dataLayer ----------------
      // Send PiecesOS data to Google Analytics for tracking and analysis

      function getClientIdFrom_ga_cookie() {
        // Extract Google Analytics client ID from _ga cookie
        try {
          var cookie = document.cookie.split("; ").find(function (c) { return c.indexOf("_ga=") === 0; });
          if (!cookie) return null;
          var val = cookie.split("=")[1];
          var parts = val.split(".");
          if (parts.length >= 2) return parts.slice(-2).join(".");
          return val;
        } catch (e) {
          return null;
        }
      }

      function pushToDataLayer(merged) {
        // Send PiecesOS data to Google Tag Manager dataLayer
        var appsIds = (merged.applications || []).map(function (a) { return a.id; }).filter(Boolean);
        var payload = {
          event: "piecesos_data_updated",
          pieces_user_id: merged.user ? merged.user.id : null,
          pieces_descope_id: merged.user ? merged.user.descopeId : null,
          pieces_apps_csv: appsIds.length ? appsIds.join(",") : null,
          pieces_apps_json: JSON.stringify(merged.applications || []),
          pieces_version: merged.user ? merged.user.version : null,
          pieces_region: merged.user ? merged.user.region : null,
          pieces_cloud_url: merged.user && merged.user.cloudUrls ?
            (merged.user.cloudUrls.id && merged.user.cloudUrls.id.url) ||
            (merged.user.cloudUrls.base && merged.user.cloudUrls.base.url) : null,
          pieces_ga4_client_id: getClientIdFrom_ga_cookie()
        };
        window.dataLayer = window.dataLayer || [];
        window.dataLayer.push(payload);
      }

      function setGtagUserProperties(merged) {
        // Set user properties in Google Analytics for better segmentation
        if (typeof window.gtag === "function" && merged && merged.user && merged.user.id) {
          try {
            window.gtag("set", "user_properties", { pieces_user_id: merged.user.id });
          } catch (e) {
            console.warn("PiecesOS: gtag set failed", e);
          }
        }
      }

      // ---------------- main worker ----------------
      // Main execution flow: scan for PiecesOS, fetch data, send to analytics

      // Check if we have recent data in IndexedDB first
      var DATA_CACHE_DURATION = 5 * 60 * 1000; // 5 minutes in milliseconds

      function shouldFetchFreshData(storedData) {
        if (!storedData || !storedData.timestamp) return true;
        var age = Date.now() - storedData.timestamp;
        return age > DATA_CACHE_DURATION;
      }

      function processPiecesOSData(data, port) {
        // Store data in IndexedDB for performance
        storeInIndexedDB(data).then(function () {
        }).catch(function (err) {
          console.warn("PiecesOS: Failed to store in IndexedDB:", err);
        });

        // Send data to analytics
        pushToDataLayer(data);
        setGtagUserProperties(data);
        try {
          // Expose API for debugging: window.__PiecesOS.read() and window.__PiecesOS.port
          window.__PiecesOS = window.__PiecesOS || {};
          window.__PiecesOS.read = function () { return Promise.resolve(data); };
          window.__PiecesOS.port = port; // Expose the port
          window.__PiecesOS.getStoredData = function () { return getFromIndexedDB(); };
        } catch (e) { }
      }

      // First, check if we have recent data in IndexedDB
      getFromIndexedDB().then(function (storedData) {
        if (!shouldFetchFreshData(storedData)) {
          processPiecesOSData(storedData.data, storedData.data.piecesOSPort);
          return;
        }

        console.log("PiecesOS: Fetching fresh data from PiecesOS API");
        // Scan for PiecesOS port and fetch fresh data
        scanForPiecesOS().then(function (port) {

          var USER_URL = "http://localhost:" + port + USER_ENDPOINT;
          var APPS_URL = "http://localhost:" + port + APPS_ENDPOINT;

          // Fetch user and applications data from PiecesOS API
          Promise.all([
            fetchWithTimeout(USER_URL, FETCH_TIMEOUT).catch(function () { return null; }),
            fetchWithTimeout(APPS_URL, FETCH_TIMEOUT).catch(function () { return null; })
          ]).then(function (responses) {
            var uResp = responses[0], aResp = responses[1];
            var uJsonPromise = uResp && uResp.ok ? uResp.json().catch(function () { return null; }) : Promise.resolve(null);
            var aJsonPromise = aResp && aResp.ok ? aResp.json().catch(function () { return null; }) : Promise.resolve(null);

            Promise.all([uJsonPromise, aJsonPromise]).then(function (jsons) {
              var uJson = jsons[0], aJson = jsons[1];
              var userData = normalizeUserPayload(uJson);
              var appsData = normalizeAppsPayload(aJson);
              var data = createDataObject(userData, appsData, port);

              processPiecesOSData(data, port);
            });
          });
        }).catch(function (err) {
          // If PiecesOS scan fails, try to use cached data as fallback
          if (storedData && storedData.data) {
            console.log("PiecesOS: PiecesOS not available, using cached data");
            processPiecesOSData(storedData.data, storedData.data.piecesOSPort);
          }
        });
      }).catch(function (err) {
        console.log("PiecesOS: No cached data, scanning for PiecesOS");
        // No cached data, scan for PiecesOS
        scanForPiecesOS().then(function (port) {

          var USER_URL = "http://localhost:" + port + USER_ENDPOINT;
          var APPS_URL = "http://localhost:" + port + APPS_ENDPOINT;

          // Fetch user and applications data from PiecesOS API
          Promise.all([
            fetchWithTimeout(USER_URL, FETCH_TIMEOUT).catch(function () { return null; }),
            fetchWithTimeout(APPS_URL, FETCH_TIMEOUT).catch(function () { return null; })
          ]).then(function (responses) {
            var uResp = responses[0], aResp = responses[1];
            var uJsonPromise = uResp && uResp.ok ? uResp.json().catch(function () { return null; }) : Promise.resolve(null);
            var aJsonPromise = aResp && aResp.ok ? aResp.json().catch(function () { return null; }) : Promise.resolve(null);

            Promise.all([uJsonPromise, aJsonPromise]).then(function (jsons) {
              var uJson = jsons[0], aJson = jsons[1];
              var userData = normalizeUserPayload(uJson);
              var appsData = normalizeAppsPayload(aJson);
              var data = createDataObject(userData, appsData, port);

              processPiecesOSData(data, port);
            });
          });
        }).catch(function (err) {
          // Silent error handling - don't break the page if PiecesOS tracking fails
        });
      });
    })();
  </script>
  <!-- End PiecesOS Tracking Script -->

  <!--app-head-->
  <script type="module" crossorigin src="/assets/index-CBrAE949.js"></script>
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-jhAwwpAT.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-ui-By2pBeSP.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-charts-DdnfnuPJ.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-query-oQeDQCRF.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-markdown-DixhsF6e.js">
  <link rel="stylesheet" crossorigin href="/assets/index-sgJTXUkW.css">
</head>

<body>
  <!-- No GTM noscript iframe: inline consent defaults do not run without JS; iframe would load tags without Consent Mode. -->
  <noscript>
    <p style="margin:0;padding:12px 16px;font:14px/1.4 system-ui,sans-serif;background:var(--background,#fafafa);color:var(--foreground,#171717);border-bottom:1px solid #e5e5e5;">
      JavaScript is disabled. Enable it to use the documentation and manage cookie preferences.
    </p>
  </noscript>

  <div id="root"><!--app-html--></div>

  <!-- Tawk.to Chat Widget - Load after DOM is ready -->
  <!-- COMMENTED OUT - Uncomment to enable Tawk.to chat widget -->
  <!--
  <script type="text/javascript">
    (function() {
      // Wait for DOM to be fully loaded
      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initTawk);
      } else {
        initTawk();
      }
      
      function initTawk() {
        // Small delay to ensure React has hydrated
        setTimeout(function() {
          var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
          (function(){
            var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
            s1.async=true;
            s1.src='https://embed.tawk.to/69495e41fb721a19813acdab/1jd39gchf';
            s1.charset='UTF-8';
            s1.setAttribute('crossorigin','*');
            s0.parentNode.insertBefore(s1,s0);
          })();
        }, 1000);
      }
    })();
  </script>
  -->
  <!-- End Tawk.to Script -->



  












<noscript id="seo-fallback">
<main role="main" aria-label="Homepage server snapshot">
<div class="w-full min-w-0 bg-background selection:bg-blue-100 selection:text-slate-900 dark:selection:bg-blue-900/30 dark:selection:text-white"><section class="relative w-full min-w-0 min-h-[85vh] sm:min-h-[90vh] flex flex-col items-stretch justify-center overflow-hidden pt-16 sm:pt-20 pb-20 sm:pb-32"><div class="w-full h-full absolute inset-0 z-0 [mask-image:radial-gradient(ellipse_at_center,white,transparent)] opacity-40"><canvas class="pointer-events-none" style="width:0;height:0"></canvas></div><div class="w-full max-w-screen-2xl mx-auto px-4 sm:px-5 md:px-6 lg:px-8 xl:px-10 2xl:px-12 relative z-10 text-center space-y-8 sm:space-y-12"><div class="animate-in fade-in slide-in-from-bottom-8 duration-1000 ease-out fill-mode-both"><h1 class="text-4xl sm:text-5xl md:text-7xl lg:text-8xl font-bold mb-5 sm:mb-8 text-slate-950 dark:text-white tracking-tight leading-[0.9]">Your Long-Term Memory Platform</h1><p class="text-base sm:text-xl md:text-2xl text-slate-600 dark:text-slate-400 mb-8 sm:mb-12 max-w-2xl mx-auto leading-relaxed font-medium px-2">On-device AI that captures your workflow and brings context into every tool through MCP.</p><div class="flex flex-col sm:flex-row items-center justify-center gap-3"><a class="w-full sm:w-auto" href="/products/meet-pieces"><button class="inline-flex items-center justify-center whitespace-nowrap font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 [&amp;_.ms-glyph]:pointer-events-none [&amp;_.ms-glyph]:size-4 [&amp;_.ms-glyph]:!text-base [&amp;_.ms-glyph]:!leading-none [&amp;_.ms-glyph]:shrink-0 py-2 w-full sm:w-auto h-10 px-5 rounded-lg text-sm gap-2 bg-black hover:bg-neutral-800 text-white dark:bg-white dark:hover:bg-neutral-100 dark:text-black hover:scale-105 active:scale-95 transition-all"><span class="ms-glyph material-symbols-outlined notranslate select-none leading-none w-4 h-4" role="img" aria-hidden="true">menu_book</span>Documentation</button></a><a href="https://pieces.app" target="_blank" rel="noopener noreferrer" class="w-full sm:w-auto"><button class="inline-flex items-center justify-center whitespace-nowrap font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 [&amp;_.ms-glyph]:pointer-events-none [&amp;_.ms-glyph]:size-4 [&amp;_.ms-glyph]:!text-base [&amp;_.ms-glyph]:!leading-none [&amp;_.ms-glyph]:shrink-0 py-2 w-full sm:w-auto h-10 px-5 rounded-lg text-sm gap-2 bg-white hover:bg-neutral-100 text-black border border-neutral-200 dark:bg-neutral-800 dark:hover:bg-neutral-700 dark:text-white dark:border-neutral-700 hover:scale-105 active:scale-95 transition-all"><span class="ms-glyph material-symbols-outlined notranslate select-none leading-none w-4 h-4" role="img" aria-hidden="true">north_east</span>pieces.app</button></a></div></div></div></section><section class="relative z-10 w-full min-w-0 bg-background border-y border-border dark:border-slate-900"><div class="w-full max-w-screen-2xl mx-auto px-4 sm:px-5 md:px-6 lg:px-8 xl:px-10 2xl:px-12 py-16 sm:py-32"><div class="flex flex-col md:flex-row md:items-end justify-between mb-10 sm:mb-16 gap-4 sm:gap-6 w-full min-w-0"><div class="space-y-3 sm:space-y-4"><div class="flex items-center gap-2 text-slate-500 font-bold tracking-widest uppercase text-xs"><span class="ms-glyph material-symbols-outlined notranslate select-none leading-none w-4 h-4" role="img" aria-hidden="true">layers</span>The Ecosystem</div><h2 class="text-3xl sm:text-4xl md:text-5xl font-bold text-slate-900 dark:text-white tracking-tight">Meet the Platform</h2></div></div><div class="grid w-full min-w-0 grid-cols-1 md:grid-cols-3 gap-6 sm:gap-8 lg:gap-10"><a class="group relative block w-full min-w-0 h-full" href="/products/desktop"><div class="absolute inset-0 bg-gradient-to-br from-blue-500/20 to-indigo-500/20 rounded-2xl sm:rounded-[2rem] blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div><div class="relative h-full bg-card dark:bg-slate-800 border border-slate-200 dark:border-slate-700 p-6 sm:p-8 md:p-10 rounded-2xl sm:rounded-[2rem] shadow-sm group-hover:shadow-2xl group-hover:-translate-y-2 transition-all duration-500 flex flex-col"><div class="w-12 h-12 sm:w-16 sm:h-16 mb-5 sm:mb-8 rounded-xl sm:rounded-2xl bg-background border border-border dark:bg-slate-700 dark:border-slate-600 p-2.5 sm:p-3 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500"><img src="/assets/icons/pieces_filled.svg" alt="Pieces" class="w-full h-full"/></div><h3 class="text-xl sm:text-2xl font-bold mb-3 sm:mb-4 text-slate-950 dark:text-white group-hover:text-slate-900 dark:group-hover:text-white flex items-center justify-between">Desktop App<span class="ms-glyph material-symbols-outlined notranslate select-none leading-none w-4 h-4 sm:w-5 sm:h-5 opacity-45 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0 transition-all text-slate-500 dark:text-slate-400 group-hover:text-slate-950 group-hover:dark:text-white" role="img" aria-hidden="true">north_east</span></h3><p class="text-sm sm:text-base text-slate-600 dark:text-slate-400 leading-relaxed font-medium">The hub for your technical assets. Search, generate, and manage code with on-device AI.</p><div class="mt-auto pt-5 sm:pt-8 border-t border-slate-200 dark:border-slate-700 inline-flex items-center text-xs sm:text-sm font-bold tracking-tighter uppercase group-hover:underline">Explore Module</div></div></a><a class="group relative block w-full min-w-0 h-full" href="/products/core-dependencies"><div class="absolute inset-0 bg-gradient-to-br from-emerald-500/20 to-cyan-500/20 rounded-2xl sm:rounded-[2rem] blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div><div class="relative h-full bg-card dark:bg-slate-800 border border-slate-200 dark:border-slate-700 p-6 sm:p-8 md:p-10 rounded-2xl sm:rounded-[2rem] shadow-sm group-hover:shadow-2xl group-hover:-translate-y-2 transition-all duration-500 flex flex-col"><div class="w-12 h-12 sm:w-16 sm:h-16 mb-5 sm:mb-8 rounded-xl sm:rounded-2xl bg-background border border-border dark:bg-slate-700 dark:border-slate-600 p-2.5 sm:p-3 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500"><img src="/assets/icons/pieces_os_light_logo.png" alt="Pieces" class="w-full h-full"/></div><h3 class="text-xl sm:text-2xl font-bold mb-3 sm:mb-4 text-slate-950 dark:text-white group-hover:text-slate-900 dark:group-hover:text-white flex items-center justify-between">PiecesOS<span class="ms-glyph material-symbols-outlined notranslate select-none leading-none w-4 h-4 sm:w-5 sm:h-5 opacity-45 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0 transition-all text-slate-500 dark:text-slate-400 group-hover:text-slate-950 group-hover:dark:text-white" role="img" aria-hidden="true">north_east</span></h3><p class="text-sm sm:text-base text-slate-600 dark:text-slate-400 leading-relaxed font-medium">The secure, local background service that powers the entire Pieces experience.</p><div class="mt-auto pt-5 sm:pt-8 border-t border-slate-200 dark:border-slate-700 inline-flex items-center text-xs sm:text-sm font-bold tracking-tighter uppercase group-hover:underline">Explore Module</div></div></a><a class="group relative block w-full min-w-0 h-full" href="/products/mcp"><div class="absolute inset-0 bg-gradient-to-br from-purple-500/20 to-pink-500/20 rounded-2xl sm:rounded-[2rem] blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div><div class="relative h-full bg-card dark:bg-slate-800 border border-slate-200 dark:border-slate-700 p-6 sm:p-8 md:p-10 rounded-2xl sm:rounded-[2rem] shadow-sm group-hover:shadow-2xl group-hover:-translate-y-2 transition-all duration-500 flex flex-col"><div class="w-12 h-12 sm:w-16 sm:h-16 mb-5 sm:mb-8 rounded-xl sm:rounded-2xl bg-background border border-border dark:bg-slate-700 dark:border-slate-600 p-2.5 sm:p-3 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500"><img src="/assets/icons/cursor.png" class="w-full h-full"/></div><h3 class="text-xl sm:text-2xl font-bold mb-3 sm:mb-4 text-slate-950 dark:text-white group-hover:text-slate-900 dark:group-hover:text-white flex items-center justify-between">MCP Server<span class="ms-glyph material-symbols-outlined notranslate select-none leading-none w-4 h-4 sm:w-5 sm:h-5 opacity-45 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0 transition-all text-slate-500 dark:text-slate-400 group-hover:text-slate-950 group-hover:dark:text-white" role="img" aria-hidden="true">north_east</span></h3><p class="text-sm sm:text-base text-slate-600 dark:text-slate-400 leading-relaxed font-medium">Bridge Pieces context directly into Popular MCP Clients, such as Claude, Cursor, and Windsurf. Just to name a few.</p><div class="mt-auto pt-5 sm:pt-8 border-t border-slate-200 dark:border-slate-700 inline-flex items-center text-xs sm:text-sm font-bold tracking-tighter uppercase group-hover:underline">Explore Module</div></div></a></div></div></section><section class="w-full min-w-0 min-h-[50vh] flex flex-col items-stretch justify-center bg-background border-t border-border dark:border-slate-800/80 relative overflow-hidden"><div class="pointer-events-none absolute z-0 h-[min(80vmax,28rem)] w-[min(80vmax,28rem)] rounded-full transition-[left,top,opacity] duration-[120ms] ease-out will-change-[left,top] opacity-0" style="left:0;top:0;transform:translate(-50%, -50%);background:radial-gradient(circle, rgba(73, 148, 236, 0.2) 0%, rgba(73, 148, 236, 0.08) 40%, transparent 70%);filter:blur(40px)" aria-hidden="true"></div><div class="w-full h-full absolute inset-0 z-0 opacity-20 [mask-image:linear-gradient(to_bottom,transparent_0%,black_30%,black_70%,transparent_100%)]"><canvas class="pointer-events-none" style="width:0;height:0"></canvas></div><div class="w-full max-w-screen-2xl mx-auto px-4 sm:px-5 md:px-6 lg:px-8 xl:px-10 2xl:px-12 relative z-10 text-center py-16 sm:py-32 flex-1 flex flex-col justify-center"><h2 class="text-3xl sm:text-4xl md:text-6xl font-bold mb-6 sm:mb-8 text-slate-950 dark:text-white">Join the evolution.</h2><p class="text-slate-600 dark:text-slate-400 text-base sm:text-xl mb-10 sm:mb-12 max-w-xl mx-auto px-1 sm:px-2">Stay ahead of the curve. Follow our journey as we redefine developer productivity.</p><div class="flex w-full min-w-0 max-w-3xl mx-auto flex-col sm:flex-row flex-wrap justify-center items-center gap-6 sm:gap-x-10 md:gap-x-12 sm:gap-y-8"><a href="https://pieces.app/discord" target="_blank" rel="noopener noreferrer" class="group flex items-center gap-3 text-base sm:text-lg font-semibold text-slate-600 dark:text-slate-400 hover:text-slate-950 dark:hover:text-white transition-all duration-300"><div class="w-10 h-10 rounded-xl bg-background border border-border dark:bg-white/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300 dark:border-white/10"><img src="/assets/icons/discord.svg" alt="Discord" class="w-5 h-5 opacity-90 group-hover:opacity-100 transition-opacity invert dark:invert-0"/></div><span>Discord</span></a><a href="https://twitter.com/getpieces" target="_blank" rel="noopener noreferrer" class="group flex items-center gap-3 text-base sm:text-lg font-semibold text-slate-600 dark:text-slate-400 hover:text-slate-950 dark:hover:text-white transition-all duration-300"><div class="w-10 h-10 rounded-xl bg-background border border-border dark:bg-white/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300 dark:border-white/10"><img src="/assets/icons/twitter.svg" alt="Twitter" class="w-5 h-5 opacity-90 group-hover:opacity-100 transition-opacity invert dark:invert-0"/></div><span>Twitter</span></a><a href="https://github.com/orgs/pieces-app" target="_blank" rel="noopener noreferrer" class="group flex items-center gap-3 text-base sm:text-lg font-semibold text-slate-600 dark:text-slate-400 hover:text-slate-950 dark:hover:text-white transition-all duration-300"><div class="w-10 h-10 rounded-xl bg-background border border-border dark:bg-white/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300 dark:border-white/10"><img src="/assets/icons/github.svg" alt="GitHub" class="w-5 h-5 opacity-90 group-hover:opacity-100 transition-opacity invert dark:invert-0"/></div><span>GitHub</span></a></div></div></section></div>
</main>
</noscript>


</body>

</html>