1<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="description" content="Taubyte is a fullstack workspace for developers and coding agents, moving software from local creation to production on infrastructure you control."><meta name="viewport" content="width=device-width"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="canonical" href="https://taubyte.com/"><meta name="generator" content="Astro v5.15.1"><meta property="og:type" content="website"><meta property="og:url" content="https://taubyte.com/"><meta property="og:title" content="Taubyte - Fullstack Workspace for Humans & Machines"><meta property="og:description" content="Taubyte is a fullstack workspace for developers and coding agents, moving software from local creation to production on infrastructure you control."><meta property="og:image" content="https://taubyte.com/screenshots/home-preview.png"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Taubyte - Fullstack Workspace for Humans & Machines"><meta name="twitter:description" content="Taubyte is a fullstack workspace for developers and coding agents, moving software from local creation to production on infrastructure you control."><meta name="twitter:image" content="https://taubyte.com/screenshots/home-preview.png"><meta property="article:modified_time" content="2026-05-10T07:52:12.220Z"><meta property="og:updated_time" content="2026-05-10T07:52:12.220Z"><link rel="alternate" type="application/rss+xml" title="Taubyte â White Papers & Research" href="/rss.xml">
1<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"Taubyte","alternateName":"Taubyte - Fullstack Workspace for Humans & Machines","url":"https://taubyte.com","description":"Taubyte is a fullstack workspace for developers and coding agents, moving software from local creation to production on infrastructure you control.","inLanguage":"en","publisher":{"@id":"https://taubyte.com/#organization"},"dateModified":"2026-05-10T07:52:12.220Z"}</script>
1<script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","@id":"https://taubyte.com/#organization","name":"Taubyte","url":"https://taubyte.com","description":"Taubyte is a fullstack workspace for developers and coding agents, moving software from local creation to production on infrastructure you control.","logo":{"@type":"ImageObject","url":"https://taubyte.com/taubyte-logo.svg"},"sameAs":["https://github.com/taubyte/tau","https://x.com/dtaubyte","https://www.linkedin.com/company/taubyte-page","https://discord.com/invite/KbN3KN7kpQ","https://www.instagram.com/dtaubyte"],"contactPoint":{"@type":"ContactPoint","contactType":"customer support","url":"https://taubyte.com/contact/"}}</script>
1<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"What is Taubyte?","acceptedAnswer":{"@type":"Answer","text":"Taubyte is a fullstack workspace for developers and coding agents that keeps software grounded from local creation to production, then ships and runs it on infrastructure the customer controls."}},{"@type":"Question","name":"Where does Taubyte run?","acceptedAnswer":{"@type":"Answer","text":"Taubyte runs on infrastructure you control: on-prem, private, hybrid, or provider environments, including the edge."}},{"@type":"Question","name":"How does deployment work in Taubyte?","acceptedAnswer":{"@type":"Answer","text":"Taubyte is Git-native. Push to Git and Taubyte handles deployment, routing, scaling, and recovery automatically â every commit moves through one grounded path toward production."}},{"@type":"Question","name":"What can you build with Taubyte?","acceptedAnswer":{"@type":"Answer","text":"Functions, websites, databases, storage, and other infrastructure primitives â all defined in one fullstack workspace and deployed from Git."}},{"@type":"Question","name":"How do I develop and test locally?","acceptedA
1nswer":{"@type":"Answer","text":"Use Dream CLI to spin up a local runtime that mirrors production. Iterate with MCP and desktop tools, then push to Git when ready to ship."}},{"@type":"Question","name":"Is Taubyte open source?","acceptedAnswer":{"@type":"Answer","text":"The Taubyte runtime, tau, is open source and available at github.com/taubyte/tau."}}]}</script>
vendor: 63 bytes, line 1
1<script async src="https://www.googletagmanager.com/gtag/js?id=
1G-1Y0G0GVEQP
vendor: 11 bytes, line 1
1"></script>
1<script> 2
vendor: 145 bytes, lines 2-5
2window.dataLayer = window.dataLayer || []; 3 function gtag(){dataLayer.push(arguments);} 4 gtag('js', new Date()); 5 gtag('config', '
5G-1Y0G0GVEQP
vendor: 8 bytes, lines 5-6
5'); 6
6</script>
6<script> 7 !function(key) {if (window.reb2b) return;window.reb2b = {loaded: true};var s = document.createElement("script");s.async = true;s.src = "https://ddwl4m2hdecbv.cloudfront.net/b/" + key + "/" + key + ".js.gz";document.getElementsByTagName("script")[0].parentNode.insertBefore(s, document.getElementsByTagName("script")[0]);}("DNXY8HDR10O0"); 8 </script>
8<script async src="https://t.contentsquare.net/uxa/3c0c3e6933d89.js"></script>
8<script type="module">(function(){try{var h=localStorage.getItem("theme"),m=window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches,l=h||(m?"light":"dark"),t=l==="dark";t?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark");var e=document.querySelector('link[rel="icon"]');if(e&&e instanceof HTMLLinkElement){var a=t?'<svg width="120mm" height="120mm" version="1.1" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="m0.64401 10.882c2.0263 11.607 11.55 18.974 20.476 21.646v62.992c0 7.5331 6.0654 13.597 13.598 13.597h16.845l5.8002-21.647h-14.596v-54.942h19.736l5.8002-21.647zm76.444 0-5.8002 21.647h26.421v54.942h-31.561l-5.8002 21.647h45.411c7.5332 0 13.597-6.0642 13.597-13.597v-71.04c0-7.5332-6.064-13.598-13.597-13.598z"/></svg>':'<svg width="120mm" height="120mm" version="1.1" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"><path fill="#000000" d="m0.64401 10.882c2.0263 11.607 11.55 18.974 20.476 21.646v62.992c0 7.5331 6.0654 13.597 13.598 13.597h16.845l5.8002-21.647h-14.596v-54.942h19.736l5.8002-21.647zm76.444 0-5.8002 21.647h26.421v54.942h-31.561l-5.8002 21.647h45.411c7.5332 0 13.597-6.0642 13.597-13.597v-71.04c0-7.5332-6.064-13.598-13.597-13.598z"/></svg>';e.href="data:image/svg+xml;base64,"+btoa(a)}}catch{}})();</script>
8<title>Taubyte - Fullstack Workspace for Humans & Machines</title><meta name="astro-view-transitions-enabled" content="true"><meta name="astro-view-transitions-fallback" content="animate">
8<script type="module" src="/_astro/ClientRouter.astro_astro_type_script_index_0_lang.elfUsALL.js"></script>
8<style>#mobile-menu-main[data-astro-cid-3ef6ksr2],#mobile-menu-resources[data-astro-cid-3ef6ksr2]{transition:opacity .2s ease,transform .2s ease}#mobile-menu-main[data-astro-cid-3ef6ksr2].hidden,#mobile-menu-resources[data-astro-cid-3ef6ksr2].hidden{display:none}@keyframes astroFadeInOut{0%{opacity:1}to{opacity:0}}@keyframes astroFadeIn{0%{opacity:0;mix-blend-mode:plus-lighter}to{opacity:1;mix-blend-mode:plus-lighter}}@keyframes astroFadeOut{0%{opacity:1;mix-blend-mode:plus-lighter}to{opacity:0;mix-blend-mode:plus-lighter}}@keyframes astroSlideFromRight{0%{transform:translate(100%)}}@keyframes astroSlideFromLeft{0%{transform:translate(-100%)}}@keyframes astroSlideToRight{to{transform:translate(100%)}}@keyframes astroSlideToLeft{to{transform:translate(-100%)}}@media(prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}[data-astro-transition-scope]{animation:none!important}} 9</style> 10<link rel="stylesheet" href="/_astro/about.BMvEtlz4.css"> 11<style>:root{--ds-background-100: #000000;--ds-background-200: transparent;--ds-gray-900: #333333;--guide-color: #555;--color: #099a40}.code-tab[data-astro-cid-ynql2ou5]{transition:all .2s ease-in-out;color:#999}
11.code-tab[data-astro-cid-ynql2ou5].active{border-bottom-color:#666;color:#fff}.code-tab[data-astro-cid-ynql2ou5]:hover:not(.active){color:#fff}.code-tab[data-astro-cid-ynql2ou5] svg[data-astro-cid-ynql2ou5]{width:14px;height:14px;display:block}.tab-content[data-astro-cid-ynql2ou5]{display:none;opacity:0;transform:translateY(10px);transition:opacity .3s ease,transform .3s ease}.tab-content[data-astro-cid-ynql2ou5].active{display:block;opacity:1;transform:translateY(0)}.platform-desktop[data-astro-cid-mw4b5lvp]{transition:opacity .4s ease-in-out;opacity:1}.platform-desktop[data-astro-cid-mw4b5lvp].fade-out{opacity:0}.platform-desktop[data-astro-cid-mw4b5lvp].fade-in{opacity:1}.platform-desktop[data-astro-cid-6vxrobne]{transition:opacity .4s ease-in-out}.platform-desktop[data-astro-cid-6vxrobne]:not([data-platform="${platform}"]){display:none}.platform-desktop[data-astro-cid-6vxrobne][data-platform="${platform}"]{opacity:1} 12@keyframes fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in[data-astro-cid-tkm6qkaw]{animation:fadeIn .3s ease-out}@keyframes slideInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes slideInFromRight{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.floating-avatar-enter[data-astro-cid-tkm6qkaw]{animation:slideInUp .4s ease-out}.floating-message-enter[data-astro-cid-tkm6qkaw]{animation:slideInFromRight .4s ease-out} 13</style> 14<link rel="stylesheet" href="/_astro/index.D2FX36cn.css"></head> <body class="bg-white dark:bg-[#151515] text-black dark:text-white antialiased transition-colors" style="font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif;"> <header class="fixed top-0 left-0 right-0 z-50 bg-white dark:bg-[#151515] transition-colors" id="main-header" data-astro-cid-3ef6ksr2 data-astro-transition-persist="astro-c22sul6k-1"> <nav class="py-3" data-astro-cid-3ef6ksr2> <div class="px-6" data-astro-cid-3ef6ksr2> <div class="container mx-auto max-w-7xl" data-astro-cid-3ef6ksr2> <div class="flex items-center justify-between relative" data-astro-cid-3ef6ksr2> <!-- Logo --> <div class="flex items-center logo-wrapper -ml-2 -mt-1" data-astro-cid-3ef6ksr2> <a href="/" class="block cursor-pointer relative" id="logo-link" aria-label="Taubyte Home" data-astro-cid-3ef6ksr2> <svg class="h-6 w-auto dark:brightness-0 dark:invert" version="1.1" viewBox="0 0 198 41.6" xmlns="http://www.w3.org/2000/svg" data-astro-cid-3ef6ksr2> <defs data-astro-cid-3ef6ksr2> <style> 15 svg:hover .logo-right { opacity: 0.3; transition: opacity 0.5s ease; } 16 .logo-right { transition: opacity 0.5s ease; } 17 </style> </defs> <g transform="matrix(1,0,0,0.998,0,-255)" stroke-width="0.179px" aria-label="taubyte" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2> <path d="m 69.1,287 q -0.688,0.619 -1.69,0.963 -0.998,0.31 -2.1,0.31 -2.55,0 -3.92,-1.38 -1.38,-1.38 -1.38,-3.89 v -17 h 2.44 v 3.96 h 5.47 v 2.06 h -5.47 v 10.8 q 0,1.62 0.791,2.48 0.791,0.826 2.3,0.826 1.65,0 2.68,-0.929 z" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2></path> <path d="m 89.9,270 v 18.1 h -2.34 v -3.58 q -1.1,1.82 -2.92,2.79 -1.79,0.963 -4.06,0.963 -2.55,0 -4.61,-1.17 -2.06,-1.17 -3.23,-3.27 -1.17,-2.1 -1.17,-4.78 0,-2.68 1.17,-4.78 1.17,-2.1 3.23,-3.23 2.06,-1.17 4.61,-1.17 2.2,0 3.96,0.929 1.79,0.894 2.92,2.65 v -3.44 z m -9.12,16.1 q 1.89,0 3.41,-0.86 1.55,-0.894 2.41,-2.51 0.894,-1.62 0.894,-3.68 0,-2.06 -0.894,-3.68 -0.86,-1.62 -2.41,-2.48 -1.51,-0.894 -3.41,-0.894 -1.93,0 -3.47,0.894 -1.51,0.86 -2.41,2.48 -0.86,1.62 -0.86,3.68 0,2.06 0.86,3.68 0.894,1.62 2.41,2.51 1.55,0.86 3.47,0.86 z" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2></path> <path d="m 113,270 v 18.1 h -2.34 v -3.3 q -0.963,1.65 -2.65,2.58 -1.69,0.894 -3.85,0.894 -3.54,0 -5.61,-1.96 -2.03,-2 -2.03,-5.81 v -10.5 h 2.44 v 10.3 q 0,2.86 1.41,4.33 1.41,1.47 4.02,1.48 2.86,0 4.51,-1.72 1.65,-1.75 1.65,-4.85 v -9.49 z" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2></path> <path d="m 130,269 q 2.55,0 4.61,1.17 2.06,1.14 3.23,3.23 1.17,2.1 1.17,4.78 0,2.68 -1.17,4.78 -1.17,2.1 -3.23,3.27 -2.06,1.17 -4.61,1.17 -2.27,0 -4.09,-0.963 -1.79,-0.963 -2.89,-2.79 v 3.58 h -2.34 v -25.5 h 2.44 v 10.9 q 1.14,-1.75 2.89,-2.65 1.79,-0.929 3.99,-0.929 z m -0.172,16.2 q 1.89,0 3.44,-0.86 1.55,-0.894 2.41,-2.51 0.894,-1.62 0.894,-3.68 0,-2.06 -0.894,-3.68 -0.86,-1.62 -2.41,-2.48 -1.55,-0.894 -3.44,-0.894 -1.93,0 -3.47,0.894 -1.51,0.86 -2.41,2.48 -0.86,1.62 -0.86,3.68 0,2.06 0.86,3.68 0.894,1.62 2.41,2.51 1.55,0.86 3.47,0.86 z" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2></path> <path d="m 161,270 v 15.9 q 0,4.61 -2.1,6.81 -2.1,2.24 -6.33,2.24 -2.37,0 -4.51,-0.757 -2.1,-0.722 -3.41,-2.03 l 1.24,-1.86 q 1.17,1.17 2.89,1.82 1.72,0.654 3.68,0.654 3.13,0 4.61,-1.62 1.48,-1.58 1.48,-4.95 v -2.1 q -0.963,1.58 -2.65,2.44 -1.65,0.86 -3.75,0.86 -3.54,0 -5.61,-1.96 -2.03,-2 -2.03,-5.81 v -9.63 h 2.44 v 9.39 q 0,2.86 1.41,4.33 1.41,1.47 4.02,1.48 2.86,0 4.51,-1.72 1.65,-1.75 1.65,-4.85 v -8.63 z" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2></path> <path d="m 177,287 q -0.688,0.619 -1.69,0.963 -0.998,0.31 -2.1,0.31 -2.55,0
17-3.92,-1.38 -1.37,-1.38 -1.38,-3.89 v -17 h 2.44 v 3.96 h 5.47 v 2.06 h -5.47 v 10.8 q 0,1.62 0.791,2.48 0.791,0.826 2.3,0.826 1.65,0 2.68,-0.929 z" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2></path> <path d="m 189,286 q 1.69,0 3.1,-0.585 1.44,-0.619 2.44,-1.79 l 1.38,1.58 q -1.2,1.44 -2.99,2.2 -1.79,0.757 -3.96,0.757 -2.79,0 -4.95,-1.17 -2.17,-1.2 -3.37,-3.3 -1.2,-2.1 -1.2,-4.75 0,-2.65 1.14,-4.75 1.17,-2.1 3.2,-3.27 2.03,-1.17 4.61,-1.17 2.41,0 4.33,1.1 1.96,1.1 3.1,3.06 1.17,1.96 1.24,4.47 l -14.9,2.89 q 0.688,2.2 2.48,3.47 1.82,1.24 4.33,1.24 z m -0.619,-14.1 q -1.89,0 -3.41,0.894 -1.48,0.86 -2.34,2.44 -0.826,1.55 -0.826,3.58 0,0.516 0.0344,0.757 l 12.8,-2.48 q -0.413,-2.27 -2.1,-3.72 -1.69,-1.48 -4.13,-1.48 z" style="fill:#101010;fill-opacity:1" data-astro-cid-3ef6ksr2></path> </g> <g class="logo-container" data-astro-cid-3ef6ksr2> <path class="logo-left" d="M 0,4.2347e-4 C 0.858687,4.9191349 4.894556,8.0410561 8.677137,9.1733727 V 35.867559 c 0,3.192311 2.57034,5.762016 5.762438,5.762016 H 21.578 l 2.457956,-9.173372 H 17.850593 V 9.1733727 h 8.363546 L 28.672096,0 Z" style="stroke-width:0.423772;fill:#101010;fill-opacity:1;" data-astro-cid-3ef6ksr2></path> <path class="logo-right" d="m 32.394755,4.2347e-4 -2.457956,9.17337283 h 11.196452 v 23.2832522 H 27.758616 L 25.300658,41.63 h 19.243867 c 3.192354,0 5.762017,-2.569834 5.762017,-5.762016 V 5.7632873 c 0,-3.1923524 -2.569749,-5.76244035 -5.762017,-5.76244035 z" style="stroke-width:0.423772;fill:#101010;fill-opacity:1;" data-astro-cid-3ef6ksr2></path> </g> </svg> </a> </div> <!-- Desktop Navigation Links --> <div class="hidden md:flex items-center space-x-8 absolute left-1/2 -translate-x-1/2" data-astro-cid-3ef6ksr2> <a href="/features" class="text-sm text-black dark:text-white hover:text-gray-400 dark:hover:text-gray-300 transition-colors" data-astro-cid-3ef6ksr2>Features</a> <a href="/enterprise" class="text-sm text-black dark:text-white hover:text-gray-400 dark:hover:text-gray-300 transition-colors" data-astro-cid-3ef6ksr2>Enterprise</a> <!-- Resources Dropdown --> <div class="relative" onmouseenter="showResourcesDropdown()" onmouseleave="hideResourcesDropdown()" data-astro-cid-3ef6ksr2> <button id="resources-desktop-button" class="text-sm text-black dark:text-white hover:text-gray-400 dark:hover:text-gray-300 transition-colors flex items-center gap-1" aria-expanded="false" aria-haspopup="true" data-astro-cid-3ef6ksr2> 18Resources 19<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" data-astro-cid-3ef6ksr2> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" data-astro-cid-3ef6ksr2></path> </svg> </button> <div id="resources-desktop-dropdown" class="hidden absolute top-full left-0 pt-2 w-80 bg-transparent z-50" onmouseenter="showResourcesDropdown()" onmouseleave="hideResourcesDropdown()" data-astro-cid-3ef6ksr2> <div class="bg-white/95 dark:bg-[#151515]/95 backdrop-blur-md rounded-lg border border-black/10 dark:border-white/10 py-2" data-astro-cid-3ef6ksr2> <div class="grid grid-cols-2 gap-0" data-astro-cid-3ef6ksr2> <a href="/blog" class="block px-4 py-2 text-sm text-black dark:text-white hover:text-gray-400 dark:hover:text-gray-300 transition-colors" data-astro-cid-3ef6ksr2>Blog</a> <a href="/doc" class="block px-4 py-2 text-sm text-black dark:text-white hover:text-gray-400 dark:hover:text-gray-300 transition-colors" data-astro-cid-3ef6ksr2>Documentation</a> <a href="/white-papers" class="block px-4 py-2 text-sm text-black dark:text-white hover:text-gray-400 dark:hover:text-gray-300 transition-colors" data-astro-cid-3ef6ksr2>White Papers</a> <a href="/community" class="block px-4 py-2 text-sm text-black dark:text-white hover:text-gray-400 dark:hover:text-gray-300 transition-colors" data-astro-cid-3ef6ksr2>Community</a> </div> </div> </div> </div> </div> <!-- Desktop CTA Buttons --> <div class="hidden md:flex items-center space-x-2" data-astro-cid-3ef6ksr2> <a href="https://console.taubyte.com" target="_blank" class="px-2.5 py-1 rounded-full border border-black text-black hover:bg-gray-100 dark:border-white dark:text-white dark:hover:bg-white/10 text-sm font-medium transition-colors" data-webmcp-toolname="signin-console" data-webmcp-tooldescription="Sign in to the Taubyte cloud console." data-astro-cid-3ef6ksr2> 20Sign in 21</a> <a href="/book-a-demo" class="px-2.5 py-1 rounded-full bg-black text-white dark:bg-white dark:text-black text-sm font-medium hover:bg-black/90 dark:hover:bg-white/90 hover:text-white dark:hover:text-black no-underline transition-colors" data-webmcp-toolname="book-demo" data-webmcp-tooldescription="Schedule a technical demo with the Taubyte team." data-astro-cid-3ef6ksr2> 22Book a demo <i class="ri-arrow-right-up-line" data-astro-cid-3ef6ksr2></i> </a> </div> <!-- Mobile Hamburger Button --> <div class="flex md:hidden items-center" data-astro-cid-3ef6ksr2> <button id="mobile-menu-button" type="button" class="inline-flex items-center justify-center p-2 rounded-md text-black dark:text-white hover:bg-gray-100 dark:hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" aria-expanded="false" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> <span class="sr-only" data-astro-cid-3ef6ksr2>
22Open main menu</span> <!-- Hamburger Icon --> <svg id="hamburger-icon" class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" data-astro-cid-3ef6ksr2> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" data-astro-cid-3ef6ksr2></path> </svg> <!-- Close Icon --> <svg id="close-icon" class="hidden h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" data-astro-cid-3ef6ksr2> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" data-astro-cid-3ef6ksr2></path> </svg> </button> </div> </div> </div> </div></nav> <!-- Mobile Menu Backdrop --> <div id="mobile-menu-backdrop" class="hidden md:hidden fixed inset-0 bg-black/50 dark:bg-black/70 z-40 transition-opacity duration-300" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2></div> <!-- Mobile Menu --> <div id="mobile-menu" class="hidden md:hidden fixed inset-0 z-50 bg-white dark:bg-[#1a1a1a] overflow-hidden" data-astro-cid-3ef6ksr2> <!-- Main Menu --> <div id="mobile-menu-main" class="h-full flex flex-col" data-astro-cid-3ef6ksr2> <div class="flex justify-end p-6" data-astro-cid-3ef6ksr2> <button onclick="toggleMobileMenu()" class="inline-flex items-center justify-center p-2 rounded-md text-black dark:text-white hover:opacity-70 focus:outline-none transition-opacity" aria-label="Close menu" data-astro-cid-3ef6ksr2> <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" data-astro-cid-3ef6ksr2> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" data-astro-cid-3ef6ksr2></path> </svg> </button> </div> <div class="flex-1 overflow-y-auto px-6 pb-12" data-astro-cid-3ef6ksr2> <nav class="space-y-8" data-astro-cid-3ef6ksr2> <a href="/features" class="block text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 23Features 24</a> <a href="/enterprise" class="block text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 25Enterprise 26</a> <button id="resources-mobile-button" onclick="showResourcesSubmenu()" class="flex w-full text-left text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity items-center justify-between" data-astro-cid-3ef6ksr2> <span data-astro-cid-3ef6ksr2>Resources</span> <i class="ri-arrow-right-s-line text-xl" data-astro-cid-3ef6ksr2></i> </button> <a href="#signin" class="block text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 27Sign in 28</a> <a href="/book-a-demo" class="flex px-6 py-3 rounded-full bg-white dark:bg-white text-black font-normal text-xl md:text-2xl hover:opacity-90 transition-opacity items-center gap-2 mt-4" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 29Book a demo 30<i class="ri-arrow-right-up-line text-xl" data-astro-cid-3ef6ksr2></i> </a> </nav> </div> </div> <!-- Resources Submenu Page --> <div id="mobile-menu-resources" class="hidden h-full" data-astro-cid-3ef6ksr2> <div class="flex justify-end p-6" data-astro-cid-3ef6ksr2> <button onclick="toggleMobileMenu()" class="inline-flex items-center justify-center p-2 rounded-md text-black dark:text-white hover:opacity-70 focus:outline-none transition-opacity" aria-label="Close menu" data-astro-cid-3ef6ksr2> <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" data-astro-cid-3ef6ksr2> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" data-astro-cid-3ef6ksr2></path> </svg> </button> </div> <div class="flex-1 overflow-y-auto px-6 pb-12" data-astro-cid-3ef6ksr2> <button onclick="showMainMenu()" class="mb-8 text-lg font-normal text-black dark:text-white hover:opacity-70 transition-opacity flex items-center gap-2" data-astro-cid-3ef6ksr2> <i class="ri-arrow-left-s-line text-xl" data-astro-cid-3ef6ksr2></i> <span data-astro-cid-3ef6ksr2>Back</span> </button> <nav class="space-y-8" data-astro-cid-3ef6ksr2> <a href="/blog" class="block text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 31Blog 32</a> <a href="/doc" class="block text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 33Documentation 34</a> <a href="/white-papers" class="block text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 35White Papers 36</a> <a href="/community" class="block text-2xl md:text-3xl font-normal text-black dark:text-white hover:opacity-70 transition-opacity" onclick="toggleMobileMenu()" data-astro-cid-3ef6ksr2> 37Community 38</a> </nav> </div> </div> </div> </header>
38<script> 39 function toggleMobileMenu() { 40 const menu = document.getElementById('mobile-menu'); 41 const button = document.getElementById('mobile-menu-button'); 42 const hamburgerIcon = document.getElementById('hamburger-icon'); 43 const closeIcon = document.getElementById('close-icon'); 44 const mainMenu = document.getElementById('mobile-menu-main'); 45 const resourcesMenu = document.getElementById('mobile-menu-resources'); 46 const isOpen = !menu?.classList.contains('hidden'); 47 48 if (isOpen) { 49 menu?.classList.add('hidden'); 50 hamburgerIcon?.classList.remove('hidden'); 51 hamburgerIcon?.classList.add('block'); 52 closeIcon?.classList.add('hidden'); 53 closeIcon?.classList.remove('block'); 54 button?.setAttribute('aria-expanded', 'false'); 55 // Reset to main menu 56 showMainMenu(); 57 // Re-enable body scrolling 58 document.body.style.overflow = ''; 59 } else { 60 menu?.classList.remove('hidden'); 61 hamburgerIcon?.classList.add('hidden'); 62 hamburgerIcon?.classList.remove('block'); 63 closeIcon?.classList.remove('hidden'); 64 closeIcon?.classList.add('block'); 65 button?.setAttribute('aria-expanded', 'true'); 66 // Show main menu 67 showMainMenu(); 68 // Prevent body scrolling when menu is open 69 document.body.style.overflow = 'hidden'; 70 } 71 } 72 73 function showMainMenu() { 74 const mainMenu = document.getElementById('mobile-menu-main'); 75 const resourcesMenu = document.getElementById('mobile-menu-resources'); 76 77 if (mainMenu) mainMenu.classList.remove('hidden'); 78 if (resourcesMenu) { 79 resourcesMenu.classList.add('hidden'); 80 resourcesMenu.classList.remove('flex', 'flex-col'); 81 } 82 } 83 84 function showResourcesSubmenu() { 85 const mainMenu = document.getElementById('mobile-menu-main'); 86 const resourcesMenu = document.getElementById('mobile-menu-resources'); 87 88 if (mainMenu) mainMenu.classList.add('hidden'); 89 if (resourcesMenu) { 90 resourcesMenu.classList.remove('hidden'); 91 resourcesMenu.classList.add('flex', 'flex-col'); 92 } 93 } 94 95 // Desktop Resources Dropdown 96 let resourcesDropdownTimeout = null; 97 98 function showResourcesDropdown() { 99 const dropdown = document.getElementById('resources-desktop-dropdown'); 100 const button = document.getElementById('resources-desktop-button'); 101 102 // Clear any pending hide timeout 103 if (resourcesDropdownTimeout) { 104 clearTimeout(resourcesDropdownTimeout); 105 resourcesDropdownTimeout = null; 106 } 107 108 if (dropdown && button) { 109 dropdown.classList.remove('hidden'); 110 button.classList.add('text-gray-400', 'dark:text-gray-300'); 111 button.classList.remove('text-black', 'dark:text-white'); 112 button.setAttribute('aria-expanded', 'true'); 113 } 114 } 115 116 function hideResourcesDropdown() { 117 const dropdown = document.getElementById('resources-desktop-dropdown'); 118 const button = document.getElementById('resources-desktop-button'); 119 120 // Clear any existing timeout 121 if (resourcesDropdownTimeout) { 122 clearTimeout(resourcesDropdownTimeout); 123 } 124 125 // Add a small delay before hiding to make it easier to catch 126 resourcesDropdownTimeout = setTimeout(() => { 127 if (dropdown && button) { 128 dropdown.classList.add('hidden'); 129 button.classList.remove('text-gray-400', 'dark:text-gray-300'); 130 button.classList.add('text-black', 'dark:text-white'); 131 button.setAttribute('aria-expanded', 'false'); 132 } 133 resourcesDropdownTimeout = null; 134 }, 150); 135 } 136 137 138</script>
138 <main> <section id="hero-section" class="relative pt-24 pb-12 px-6 bg-white dark:bg-[#151515] transition-colors"> <div class="container mx-auto max-w-7xl"> <!-- Main Heading --> <div class="mb-16 mt-8"> <h1 class="text-lg md:text-xl lg:text-2xl font-normal leading-tight mb-6 max-w-4xl text-black dark:text-white transition-colors"> 139Fullstack workspace for humans and machines. 140</h1> <p class="text-base md:text-lg text-black/70 dark:text-white/70 max-w-3xl mb-8"> 141Taubyte keeps software grounded from local creation to production, then ships and runs it on infrastructure you control. 142</p> <a href="/download?os=linux&binary=dream" class="hidden md:inline-flex items-center gap-2 px-6 py-3 bg-black dark:bg-white text-white dark:text-black text-sm font-medium rounded-full hover:bg-black/90 dark:hover:bg-white/90 hover:text-white dark:hover:text-black no-underline transition-colors" id="hero-download-btn" data-webmcp-toolname="download-taubyte" data-webmcp-tooldescription="Download Taubyte and the Dream CLI for local development."> <span id="hero-download-text">Download for Linux</span> <i class="ri-download-line"></i> </a>
142<script type="module" src="/_astro/DownloadButton.astro_astro_type_script_index_0_lang.D1cddKQx.js"></script>
142 <a href="/getting-started" class="inline-flex items-center px-6 py-3 border-1 border-black dark:border-white text-black dark:text-white bg-transparent text-sm font-medium rounded-full hover:bg-gray-100 dark:hover:bg-white/10 no-underline transition-colors md:ml-3 ml-0" id="hero-documentation-btn" data-webmcp-toolname="get-started-guide" data-webmcp-tooldescription="Open the step-by-step Taubyte getting-started guide."> 143Step-by-step 144</a> </div> <!-- Demo Container --> <div class="relative hidden lg:block"> <!-- Demo Container --><div class="relative overflow-visible" style="height: min(650px, 60vh); min-height: 600px;" data-astro-cid-6vxrobne> <!-- Desktop Instances for each platform --> <!-- Your Cloud Desktop --><div class="platform-desktop absolute inset-0 flex flex-col items-center justify-center z-1" data-platform="your-cloud" style="display: block;" data-astro-cid-ynql2ou5> <!-- Desktop Background with border and gradient --> <div class="absolute inset-0 bg-gradient-to-t from-[#0f0f0f] via-[#151515] to-[#1a1a1a] rounded-2xl border border-white/10 desktop-background" data-platform="your-cloud" data-astro-cid-ynql2ou5></div> <!-- Content --> <div class="relative w-full h-full flex justify-center items-start pt-24" data-astro-cid-ynql2ou5> <!-- Code viewer window --> <div class="absolute top-12 z-10 bg-[#1a1a1a] rounded-lg w-96 md:w-[28rem] shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] border border-white/10 overflow-hidden" data-astro-cid-ynql2ou5> <!-- Window title bar --> <div class="flex items-center justify-between px-4 py-1.5 bg-[#222] border-b border-white/10" data-astro-cid-ynql2ou5> <div class="flex items-center gap-1" data-astro-cid-ynql2ou5> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-ynql2ou5></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-ynql2ou5></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-ynql2ou5></span> </div> <div class="text-xs text-white/60" data-astro-cid-ynql2ou5> </div> <div class="w-20" data-astro-cid-ynql2ou5></div> </div> <!-- Tabs --> <div class="flex border-b border-white/10" role="tablist" aria-label="Code examples" data-astro-cid-ynql2ou5> <button type="button" class="code-tab px-3 py-2 text-xs font-mono border-b-2 border-transparent transition-colors cursor-pointer flex items-center gap-1.5 hover:bg-white/5 active" role="tab" aria-selected="true" aria-controls="tab-panel-ai" id="tab-ai" data-tab-id="ai" data-astro-cid-ynql2ou5> <span data-astro-cid-ynql2ou5><svg width="24" height="24" fill="currentColor" viewBox="0 0 32 32"><path d="M 16 1 C 14.894531 1 14 1.894531 14 3 C 14 3.738281 14.402344 4.371094 15 4.71875 L 15 7 L 10 7 C 7.25 7 5 9.25 5 12 L 5 14 L 2 14 L 2 22 L 5 22 L 5 27 L 27 27 L 27 22 L 30 22 L 30 14 L 27 14 L 27 12 C 27 9.25 24.75 7 22 7 L 17 7 L 17 4.71875 C 17.597656 4.371094 18 3.738281 18 3 C 18 1.894531 17.105469 1 16 1 Z M 10 9 L 22 9 C 23.667969 9 25 10.332031 25 12 L 25 25 L 23 25 L 23 20 L 9 20 L 9 25 L 7 25 L 7 12 C 7 10.332031 8.332031 9 10 9 Z M 12 13 C 10.894531 13 10 13.894531 10 15 C 10 16.105469 10.894531 17 12 17 C 13.105469 17 14 16.105469 14 15 C 14 13.894531 13.105469 13 12 13 Z M 20 13 C 18.894531 13 18 13.894531 18 15 C 18 16.105469 18.894531 17 20 17 C 21.105469 17 22 16.105469 22 15 C 22 13.894531 21.105469 13 20 13 Z M 4 16 L 5 16 L 5 20 L 4 20 Z M 27 16 L 28 16 L 28 20 L 27 20 Z M 11 22 L 13 22 L 13 25 L 11 25 Z M 15 22 L 17 22 L 17 25 L 15 25 Z M 19 22 L 21 22 L 21 25 L 19 25 Z"/></svg></span> AI </button><button type="button" class="code-tab px-3 py-2 text-xs font-mono border-b-2 border-transparent transition-colors cursor-pointer flex items-center gap-1.5 hover:bg-white/5 " role="tab" aria-selected="false" aria-controls="tab-panel-cli" id="tab-cli" data-tab-id="cli" data-astro-cid-ynql2ou5> <span data-astro-cid-ynql2ou5><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 17L11 12L6 7M13 17H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> CLI </button><button type="button" class="code-tab px-3 py-2 text-xs font-mono border-b-2 border-transparent transition-colors cursor-pointer flex items-center gap-1.5 hover:bg-white/5 " role="tab" aria-selected="false" aria-controls="tab-panel-code" id="tab-code" data-tab-id="code" data-astro-cid-ynql2ou5> <span data-astro-cid-ynql2ou5>
144<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7 8L3 12L7 16M17 8L21 12L17 16M14 4L10 20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> Code </button> </div> <!-- Tab content --> <div class="p-4 bg-[#1a1a1a] rounded-b-lg" data-astro-cid-ynql2ou5> <div class="tab-content active" role="tabpanel" aria-labelledby="tab-ai" id="tab-panel-ai" data-tab-panel="ai" data-astro-cid-ynql2ou5> <div class="text-left" data-astro-cid-ynql2ou5> 145 <div class="space-y-2 text-xs font-mono text-white"> 146 <!-- User message --> 147 <div class="flex items-start gap-2"> 148 <div class="shrink-0"> 149 <svg width="24" height="24" fill="none" stroke="white" stroke-width="1.5" viewBox="0 0 24 24"> 150 <circle cx="12" cy="12" r="10" /> 151 <circle cx="12" cy="10" r="4" /> 152 <path d="M6 18a6 6 0 0 1 12 0" /> 153 </svg> 154 </div> 155 <p class="rounded-xl border border-[#2a2a2a] px-3 py-1 text-white">Add 1.2.3.4 as a compute host</p> 156 </div> 157 158 <!-- AI messages --> 159 <div class="flex justify-end"> 160 <p class="text-gray-400 py-0.5 animate-pulse" id="ai-thinking">Thinking...</p> 161 </div> 162 163 <div class="flex justify-end"> 164 <div class="rounded-xl border border-[#2a2a2a] px-3 py-1 flex items-center gap-3 text-white"> 165 <span>Run <span class="font-semibold">spore-drive displace</span>?</span> 166 <div class="flex gap-1"> 167 <span class="px-2 py-1 text-xs rounded border border-[#2a2a2a] bg-[#1a1a1a] text-white">Reject</span> 168 <span class="px-2 py-1 text-xs rounded border border-[#2a2a2a] bg-[#2a2a2a] text-white">Accept</span> 169 </div> 170 </div> 171 </div> 172 </div> 173 </div> </div><div class="tab-content " role="tabpanel" aria-labelledby="tab-cli" id="tab-panel-cli" data-tab-panel="cli" data-astro-cid-ynql2ou5> <pre class="text-left text-gray-300 font-mono text-xs leading-relaxed whitespace-pre-wrap" data-astro-cid-ynql2ou5>$ spore new cloud --fqdn ... 174$ spore add host ... 175$ spore displace</pre> </div><div class="tab-content " role="tabpanel" aria-labelledby="tab-code" id="tab-panel-code" data-tab-panel="code" data-astro-cid-ynql2ou5> <pre class="text-left text-gray-300 font-mono text-xs leading-relaxed whitespace-pre-wrap" data-astro-cid-ynql2ou5>import "@taubyte/spore-drive"; 176const config = new Config("..."); 177const drive = new Drive(config, TauLatest); 178await drive.displace();</pre> </div> </div> </div> <!-- Wireframe cloud SVG --> <svg aria-hidden="true" height="70%" viewBox="-1 -1 802 402" width="100%" class="absolute bottom-0 left-0 right-0" data-astro-cid-ynql2ou5> <g data-testid="globe-wireframe" mask="url(#fade-mask)" data-astro-cid-ynql2ou5> <circle cx="400" cy="400" fill="var(--ds-background-200)" r="400" data-astro-cid-ynql2ou5></circle> <path d="M 400 800 A -400 400 0 0 0 400 0" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 800 A -328.701 400 0 0 0 400 0" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 800 A -235.355 400 0 0 0 400 0" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 800 A -123.097 400 0 0 0 400 0" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 800 A 0 400 0 0 0 400 0" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 0 A 123.097 400 0 0 0 400 800" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 0 A 235.355 400 0 0 0 400 800" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 0 A 328.701 400 0 0 0 400 800" fill="none" stroke="url
178(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M 400 0 A 400 400 0 0 0 400 800" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M160,80 h480" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M80,160 h640" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M33.394,240 h733.212" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M8.082,320 h783.837" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M0,400 h800" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" stroke-opacity="0" data-astro-cid-ynql2ou5></path><path d="M8.082,480 h783.837" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M33.394,560 h733.212" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M80,640 h640" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path><path d="M160,720 h480" fill="none" stroke="url(#globe-gradient)" stroke-width="1" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> </g> <g data-testid="globe-nodes" data-astro-cid-ynql2ou5> <g data-testid="dynamic-node-0" data-astro-cid-ynql2ou5> <defs data-astro-cid-ynql2ou5> <radialGradient id="conn-grad-0" gradientUnits="userSpaceOnUse" r="0" style="--normal-color: #45DEC4;" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="0.4" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" stop-opacity="0" data-astro-cid-ynql2ou5></stop> <animate attributeName="cx" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="400;597.2206;597.2206" data-astro-cid-ynql2ou5></animate> <animate attributeName="cy" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="0;80;80" data-astro-cid-ynql2ou5></animate> <animate attributeName="r" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.5;0.5;1" values="0;120;120;0" data-astro-cid-ynql2ou5></animate> </radialGradient> </defs> <path d="M 400 0 A 328.701 400 0 0 1 597.2206 80" fill="none" stroke="url(#conn-grad-0)" stroke-width="3" stroke-linecap="round" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> <!-- Chip-shaped glow animation --> <g transform="translate(597.2206, 80)" data-astro-cid-ynql2ou5> <rect x="-12" y="-12" width="24" height="24" rx="3" fill="none" stroke="var(--color)" stroke-width="2" opacity="0" data-astro-cid-ynql2ou5> <animate attributeName="opacity" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="0;1;0.5;0;0;0" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="width" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="height" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="x" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="y" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> </rect> </g> <rect x="581.2206" y="64" width="32" height="32" rx="4" fill="#151515" stroke="url(#node-gradient)" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></rect> <!-- Chip icon --> <g transform="translate(590.2206, 73)" fill="white" stroke="white" data-astro-cid-ynql2ou5> <!-- Chip body --> <rect x="0" y="0" width="14" height="14" rx="2" fill="none" stroke-width="1.2" data-astro-cid-ynql2ou5></rect> <!-- Central processor area --> <rect x="3" y="3" width="8" height="8" rx="1" fill="none" stroke-width="0.8" data-astro-cid-ynql2ou5></rect>
178 <!-- Circuit lines --> <line x1="3" y1="6" x2="1" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="3" y1="8" x2="1" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="6" x2="11" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="8" x2="11" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="3" x2="6" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="3" x2="8" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="13" x2="6" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="13" x2="8" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <!-- Pin connections --> <circle cx="1" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="1" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> </g> </g><g data-testid="dynamic-node-1" data-astro-cid-ynql2ou5> <defs data-astro-cid-ynql2ou5> <radialGradient id="conn-grad-1" gradientUnits="userSpaceOnUse" r="0" style="--normal-color: #45DEC4;" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="0.4" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" stop-opacity="0" data-astro-cid-ynql2ou5></stop> <animate attributeName="cx" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.8117686738908175;1;1" values="400;169.40013663490606;77.94010882297061;77.94010882297061" data-astro-cid-ynql2ou5></animate> <animate attributeName="cy" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.8117686738908175;1;1" values="0;320;320;320" data-astro-cid-ynql2ou5></animate> <animate attributeName="r" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.5;0.5;1" values="0;120;120;0" data-astro-cid-ynql2ou5></animate> </radialGradient> </defs> <path d="M 400 0 A 235.355 400 0 0 0 169.40013663490606 320 L 77.94010882297061 320" fill="none" stroke="url(#conn-grad-1)" stroke-width="3" stroke-linecap="round" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> <!-- Chip-shaped glow animation --> <g transform="translate(77.94010882297061, 320)" data-astro-cid-ynql2ou5> <rect x="-12" y="-12" width="24" height="24" rx="3" fill="none" stroke="var(--color)" stroke-width="2" opacity="0" data-astro-cid-ynql2ou5> <animate attributeName="opacity" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="0;1;0.5;0;0;0" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="width" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="height" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="x" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="y" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> </rect> </g> <rect x="61.940108822970615" y="304" width="32" height="32" rx="4" fill="#151515" stroke="url(#node-gradient)" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></rect> <!-- Chip icon --> <g transform="translate(70.94010882297061, 313)" fill="white" stroke="white" data-astro-cid-ynql2ou5> <!-- Chip body --> <rect x="0" y="0" width="14" height="14" rx="2" fill="none" stroke-width="1.2" data-astro-cid-ynql2ou5></rect> <!-- Central processor area --> <rect x="3" y="3" width="8" height="8" rx="1" fill="none" stroke-width="0.8" data-astro-cid-ynql2ou5></rect>
178 <!-- Circuit lines --> <line x1="3" y1="6" x2="1" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="3" y1="8" x2="1" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="6" x2="11" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="8" x2="11" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="3" x2="6" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="3" x2="8" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="13" x2="6" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="13" x2="8" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <!-- Pin connections --> <circle cx="1" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="1" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> </g> </g><g data-testid="dynamic-node-2" data-astro-cid-ynql2ou5> <defs data-astro-cid-ynql2ou5> <radialGradient id="conn-grad-2" gradientUnits="userSpaceOnUse" r="0" style="--normal-color: #45DEC4;" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="0.4" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" stop-opacity="0" data-astro-cid-ynql2ou5></stop> <animate attributeName="cx" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="400;400;400" data-astro-cid-ynql2ou5></animate> <animate attributeName="cy" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="0;80;80" data-astro-cid-ynql2ou5></animate> <animate attributeName="r" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.5;0.5;1" values="0;120;120;0" data-astro-cid-ynql2ou5></animate> </radialGradient> </defs> <path d="M 400 0 A 0 400 0 0 1 400 80" fill="none" stroke="url(#conn-grad-2)" stroke-width="3" stroke-linecap="round" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> <!-- Chip-shaped glow animation --> <g transform="translate(400, 80)" data-astro-cid-ynql2ou5> <rect x="-12" y="-12" width="24" height="24" rx="3" fill="none" stroke="var(--color)" stroke-width="2" opacity="0" data-astro-cid-ynql2ou5> <animate attributeName="opacity" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="0;1;0.5;0;0;0" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="width" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="height" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="x" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="y" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> </rect> </g> <rect x="384" y="64" width="32" height="32" rx="4" fill="#151515" stroke="url(#node-gradient)" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></rect> <!-- Chip icon --> <g transform="translate(393, 73)" fill="white" stroke="white" data-astro-cid-ynql2ou5> <!-- Chip body --> <rect x="0" y="0" width="14" height="14" rx="2" fill="none" stroke-width="1.2" data-astro-cid-ynql2ou5></rect> <!-- Central processor area --> <rect x="3" y="3" width="8" height="8" rx="1" fill="none" stroke-width="0.8" data-astro-cid-ynql2ou5></rect> <!-- Circuit lines --> <line x1="3" y1="6" x2="1" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="3" y1="8" x2="1" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="6" x2="11" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="8" x2="11" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="3" x2="6" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="3" x2="8" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="13" x2="6" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="13" x2="8" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <!-- Pin connections --> <circle cx="1" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="1" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> </g> </g><g data-testid="dynamic-node-3" data-astro-cid-ynql2ou5> <defs data-astro-cid-ynql2ou5> <radialGradient id="conn-grad-3" gradientUnits="userSpaceOnUse" r="0" style="--normal-color: #45DEC4;" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="0.4" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" stop-opacity="0" data-astro-cid-ynql2ou5></stop>
178 <animate attributeName="cx" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.7904363824519829;1;1" values="400;615.7064205372664;701.2594427015359;701.2594427015359" data-astro-cid-ynql2ou5></animate> <animate attributeName="cy" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.7904363824519829;1;1" values="0;240;240;240" data-astro-cid-ynql2ou5></animate> <animate attributeName="r" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.5;0.5;1" values="0;120;120;0" data-astro-cid-ynql2ou5></animate> </radialGradient> </defs> <path d="M 400 0 A 235.355 400 0 0 1 615.7064205372664 240 L 701.2594427015359 240" fill="none" stroke="url(#conn-grad-3)" stroke-width="3" stroke-linecap="round" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> <!-- Chip-shaped glow animation --> <g transform="translate(701.2594427015359, 240)" data-astro-cid-ynql2ou5> <rect x="-12" y="-12" width="24" height="24" rx="3" fill="none" stroke="var(--color)" stroke-width="2" opacity="0" data-astro-cid-ynql2ou5> <animate attributeName="opacity" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="0;1;0.5;0;0;0" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="width" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="height" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="x" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="y" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> </rect> </g> <rect x="685.2594427015359" y="224" width="32" height="32" rx="4" fill="#151515" stroke="url(#node-gradient)" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></rect> <!-- Chip icon --> <g transform="translate(694.2594427015359, 233)" fill="white" stroke="white" data-astro-cid-ynql2ou5> <!-- Chip body --> <rect x="0" y="0" width="14" height="14" rx="2" fill="none" stroke-width="1.2" data-astro-cid-ynql2ou5></rect> <!-- Central processor area --> <rect x="3" y="3" width="8" height="8" rx="1" fill="none" stroke-width="0.8" data-astro-cid-ynql2ou5></rect> <!-- Circuit lines --> <line x1="3" y1="6" x2="1" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="3" y1="8" x2="1" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="6" x2="11" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="8" x2="11" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="3" x2="6" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="3" x2="8" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="13" x2="6" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="13" x2="8" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <!-- Pin connections --> <circle cx="1" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="1" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> </g> </g><g data-testid="dynamic-node-4" data-astro-cid-ynql2ou5> <defs data-astro-cid-ynql2ou5> <radialGradient id="conn-grad-4" gradientUnits="userSpaceOnUse" r="0" style="--normal-color: #45DEC4;" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="0.4" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" stop-opacity="0" data-astro-cid-ynql2ou5></stop>
178 <animate attributeName="cx" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="400;137.0392;137.0392" data-astro-cid-ynql2ou5></animate> <animate attributeName="cy" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="0;160;160" data-astro-cid-ynql2ou5></animate> <animate attributeName="r" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.5;0.5;1" values="0;120;120;0" data-astro-cid-ynql2ou5></animate> </radialGradient> </defs> <path d="M 400 0 A 328.701 400 0 0 0 137.0392 160" fill="none" stroke="url(#conn-grad-4)" stroke-width="3" stroke-linecap="round" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> <!-- Chip-shaped glow animation --> <g transform="translate(137.0392, 160)" data-astro-cid-ynql2ou5> <rect x="-12" y="-12" width="24" height="24" rx="3" fill="none" stroke="var(--color)" stroke-width="2" opacity="0" data-astro-cid-ynql2ou5> <animate attributeName="opacity" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="0;1;0.5;0;0;0" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="width" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="height" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="x" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="y" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> </rect> </g> <rect x="121.0392" y="144" width="32" height="32" rx="4" fill="#151515" stroke="url(#node-gradient)" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></rect> <!-- Chip icon --> <g transform="translate(130.0392, 153)" fill="white" stroke="white" data-astro-cid-ynql2ou5> <!-- Chip body --> <rect x="0" y="0" width="14" height="14" rx="2" fill="none" stroke-width="1.2" data-astro-cid-ynql2ou5></rect> <!-- Central processor area --> <rect x="3" y="3" width="8" height="8" rx="1" fill="none" stroke-width="0.8" data-astro-cid-ynql2ou5></rect> <!-- Circuit lines --> <line x1="3" y1="6" x2="1" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="3" y1="8" x2="1" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="6" x2="11" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="8" x2="11" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="3" x2="6" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="3" x2="8" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="13" x2="6" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="13" x2="8" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <!-- Pin connections --> <circle cx="1" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="1" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> </g> </g><g data-testid="dynamic-node-5" data-astro-cid-ynql2ou5> <defs data-astro-cid-ynql2ou5> <radialGradient id="conn-grad-5" gradientUnits="userSpaceOnUse" r="0" style="--normal-color: #45DEC4;" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="0.4" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" stop-opacity="0" data-astro-cid-ynql2ou5></stop> <animate attributeName="cx" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="400;301.5224;301.5224" data-astro-cid-ynql2ou5></animate>
178 <animate attributeName="cy" dur="4.5s" repeatCount="indefinite" keyTimes="0;1;1" values="0;160;160" data-astro-cid-ynql2ou5></animate> <animate attributeName="r" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.5;0.5;1" values="0;120;120;0" data-astro-cid-ynql2ou5></animate> </radialGradient> </defs> <path d="M 400 0 A 123.097 400 0 0 0 301.5224 160" fill="none" stroke="url(#conn-grad-5)" stroke-width="3" stroke-linecap="round" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> <!-- Chip-shaped glow animation --> <g transform="translate(301.5224, 160)" data-astro-cid-ynql2ou5> <rect x="-12" y="-12" width="24" height="24" rx="3" fill="none" stroke="var(--color)" stroke-width="2" opacity="0" data-astro-cid-ynql2ou5> <animate attributeName="opacity" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="0;1;0.5;0;0;0" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="width" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="height" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="x" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="y" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> </rect> </g> <rect x="285.5224" y="144" width="32" height="32" rx="4" fill="#151515" stroke="url(#node-gradient)" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></rect> <!-- Chip icon --> <g transform="translate(294.5224, 153)" fill="white" stroke="white" data-astro-cid-ynql2ou5> <!-- Chip body --> <rect x="0" y="0" width="14" height="14" rx="2" fill="none" stroke-width="1.2" data-astro-cid-ynql2ou5></rect> <!-- Central processor area --> <rect x="3" y="3" width="8" height="8" rx="1" fill="none" stroke-width="0.8" data-astro-cid-ynql2ou5></rect> <!-- Circuit lines --> <line x1="3" y1="6" x2="1" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="3" y1="8" x2="1" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="6" x2="11" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="8" x2="11" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="3" x2="6" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="3" x2="8" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="13" x2="6" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="13" x2="8" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <!-- Pin connections --> <circle cx="1" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="1" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> </g> </g><g data-testid="dynamic-node-6" data-astro-cid-ynql2ou5> <defs data-astro-cid-ynql2ou5> <radialGradient id="conn-grad-6" gradientUnits="userSpaceOnUse" r="0" style="--normal-color: #45DEC4;" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="0.4" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" stop-opacity="0" data-astro-cid-ynql2ou5></stop> <animate attributeName="cx" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.42091446746981026;0.6415407555438832;1;1" values="400;498.4776;400;400;400" data-astro-cid-ynql2ou5></animate>
178 <animate attributeName="cy" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.42091446746981026;0.6415407555438832;1;1" values="0;160;160;320;320" data-astro-cid-ynql2ou5></animate> <animate attributeName="r" dur="4.5s" repeatCount="indefinite" keyTimes="0;0.5;0.5;1" values="0;120;120;0" data-astro-cid-ynql2ou5></animate> </radialGradient> </defs> <path d="M 400 0 A 123.097 400 0 0 1 498.4776 160 L 400 160 L 400 320" fill="none" stroke="url(#conn-grad-6)" stroke-width="3" stroke-linecap="round" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></path> <!-- Chip-shaped glow animation --> <g transform="translate(400, 320)" data-astro-cid-ynql2ou5> <rect x="-12" y="-12" width="24" height="24" rx="3" fill="none" stroke="var(--color)" stroke-width="2" opacity="0" data-astro-cid-ynql2ou5> <animate attributeName="opacity" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="0;1;0.5;0;0;0" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="width" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="height" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="24;32;40;48;24;24" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="x" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> <animate attributeName="y" dur="4.5s" keyTimes="0;0.167;0.333;0.5;0.667;1" values="-12;-16;-20;-24;-12;-12" repeatCount="indefinite" data-astro-cid-ynql2ou5></animate> </rect> </g> <rect x="384" y="304" width="32" height="32" rx="4" fill="#151515" stroke="url(#node-gradient)" vector-effect="non-scaling-stroke" data-astro-cid-ynql2ou5></rect> <!-- Chip icon --> <g transform="translate(393, 313)" fill="white" stroke="white" data-astro-cid-ynql2ou5> <!-- Chip body --> <rect x="0" y="0" width="14" height="14" rx="2" fill="none" stroke-width="1.2" data-astro-cid-ynql2ou5></rect> <!-- Central processor area --> <rect x="3" y="3" width="8" height="8" rx="1" fill="none" stroke-width="0.8" data-astro-cid-ynql2ou5></rect> <!-- Circuit lines --> <line x1="3" y1="6" x2="1" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="3" y1="8" x2="1" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="6" x2="11" y2="6" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="13" y1="8" x2="11" y2="8" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="3" x2="6" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="3" x2="8" y2="1" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="6" y1="13" x2="6" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <line x1="8" y1="13" x2="8" y2="11" stroke-width="0.8" data-astro-cid-ynql2ou5></line> <!-- Pin connections --> <circle cx="1" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="1" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="6" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="13" cy="8" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="1" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="6" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> <circle cx="8" cy="13" r="0.4" data-astro-cid-ynql2ou5></circle> </g> </g> </g> <defs data-astro-cid-ynql2ou5> <linearGradient id="globe-gradient" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="400" data-astro-cid-ynql2ou5> <stop offset="0%" stop-color="var(--guide-color)" data-astro-cid-ynql2ou5></stop> <stop offset="100%" stop-color="var(--guide-color)" data-astro-cid-ynql2ou5></stop> </linearGradient> <!-- Fade mask for bottom fade-out --> <linearGradient id="fade-gradient" x1="0" y1="0" x2="0" y2="1" gradientUnits="objectBoundingBox" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="white" data-astro-cid-ynql2ou5></stop> <stop offset="0.75" stop-color="white" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="black" data-astro-cid-ynql2ou5></stop> </linearGradient> <mask id="fade-mask" maskUnits="objectBoundingBox" data-astro-cid-ynql2ou5> <rect x="0" y="0" width="100%" height="100%" fill="url(#fade-gradient)" data-astro-cid-ynql2ou5></rect> </mask> <!-- Default gradient reused by node circles --> <linearGradient id="node-gradient" x1="0" y1="0" x2="0" y2="1" data-astro-cid-ynql2ou5> <stop offset="0" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> <stop offset="1" stop-color="var(--color)" data-astro-cid-ynql2ou5></stop> </linearGradient> </defs> </svg> </div> </div>
178<script type="module">function f(){const t=document.querySelector('[role="tablist"]');if(!t)return;const r=t.querySelectorAll('[role="tab"]');document.querySelectorAll('[role="tabpanel"]');function l(o){const n=o.getAttribute("data-tab-id");if(!n||!t)return;const s=t.querySelectorAll('[role="tab"]'),i=document.querySelectorAll('[role="tabpanel"]');s.forEach(e=>{const a=e===o;e.setAttribute("aria-selected",a?"true":"false"),e.classList.toggle("active",a)}),i.forEach(e=>{e.getAttribute("data-tab-panel")===n?e.classList.add("active"):e.classList.remove("active")}),o.focus()}r.forEach(o=>{const n=o,s=n;s._tabBound!==!0&&(s._tabBound=!0,n.addEventListener("click",i=>{i.preventDefault(),l(n)}),n.addEventListener("keydown",i=>{const e=i,a=t.querySelectorAll('[role="tab"]'),c=Array.from(a).indexOf(n);let u=c;switch(e.key){case"ArrowLeft":e.preventDefault(),u=c>0?c-1:a.length-1;break;case"ArrowRight":e.preventDefault(),u=c<a.length-1?c+1:0;break;case"Home":e.preventDefault(),u=0;break;case"End":e.preventDefault(),u=a.length-1;break;default:return}l(a[u])}))})}let d=null;const b=["Thinking...","Is the user using Code or not?","User is using configuration files","Reading Configuration","Configuration file updated"];function v(){const t=document.getElementById("ai-thinking");if(!t)return;d&&clearInterval(d);let r=0;d=setInterval(()=>{t.style.opacity="0",setTimeout(()=>{t.textContent=b[r],t.style.opacity="1",r=(r+1)%b.length},150)},1e3)}function g(){const t=document.querySelector('[data-tab-id="ai"]'),r=document.getElementById("tab-panel-ai");if(t&&r){const l=r,o=l._observer;o&&o.disconnect();const n=new IntersectionObserver(s=>{s.forEach(i=>{i.isIntersecting&&v()})},{threshold:.1});l._observer=n,n.observe(r)}}typeof window<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{f(),g()}):(f(),g()));document.addEventListener("astro:page-load",()=>{requestAnimationFrame(()=>{f(),g()})});</script>
178 <!-- Build Desktop --><div class="platform-desktop absolute inset-0 z-1" data-platform="build" style="display: none;"> <!-- Desktop Background --> <!-- Desktop Background --><div class="absolute inset-0 bg-cover bg-center rounded-2xl desktop-background" style="background-image: url('/amazigh-desert-background.png')"></div> <!-- Web Console Window --> <!-- Taubyte Console Window (Floating) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-5xl shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg console-window" id="draggable-window-build" data-platform="build"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col" style="height: 520px;"> <!-- Browser Tabs --> <div class="bg-[#222] border-b border-white/10 px-4 py-1.5 flex items-center gap-1 cursor-move drag-handle" data-platform="build"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="ml-4 text-white/60 text-xs">Console</span> </div> <!-- Top Header Bar --> <div class="bg-[#2d2d2d] h-10 px-4 flex items-center justify-between border-b border-white/10"> <!-- Left: Logo and Menu --> <div class="flex items-center gap-4"> <i class="ri-menu-2-line text-white/60"></i> </div> <!-- Right: Icons and Actions --> <div class="flex items-center gap-4"> <div class="flex items-center gap-1.5 text-white/60 text-xs bg-white/5 px-2 py-1 rounded"> <i class="ri-git-branch-line text-white/60"></i> <span>main</span> </div> <i class="ri-notification-3-line text-white/60"></i> <i class="ri-question-line text-white/60"></i> <div class="flex items-center gap-2 text-white/60"> <img src="/samy-git-avatar.png" alt="Avatar" class="w-6 h-6 rounded-full border border-gray-500"> </div> </div> </div> <!-- Main Content --> <div class="flex flex-1 overflow-hidden"> <!-- Left Sidebar --> <div class="w-40 bg-[#252525] border-r border-white/10 p-3 space-y-0.5 overflow-auto flex flex-col"> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-home-line text-xs"></i> <span class="text-xs">Home</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-function-line text-xs"></i> <span class="text-xs">Applications</span> </div> <div class="flex items-center gap-1.5 px-2 py-1 rounded text-green-500 bg-white/10"> <i class="ri-flashlight-line text-xs"></i> <span class="text-xs">Functions</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-stack-line text-xs"></i> <span class="text-xs">Libraries</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-global-line text-xs"></i> <span class="text-xs">Websites</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-hard-drive-2-line text-xs"></i> <span class="text-xs">Storages</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-mail-line text-xs"></i> <span class="text-xs">Messaging</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-database-2-line text-xs"></i> <span class="text-xs">Databases</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-compass-3-line text-xs"></i> <span class="text-xs">Domains</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-server-line text-xs"></i> <span class="text-xs">Services</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-settings-4-line text-xs"></i> <span class="text-xs">SmartOps</span> </div> <div class="border-t border-white/10 my-1"></div> <div class="flex items-center gap-1.5 px-2 py-1 rounded text-white/60"> <i class="ri-task-line text-xs"></i> <span class="text-xs">Builds</span> </div> <div class="flex items-center gap-1.5 text-white/60 px-2 py-1 rounded"> <i class="ri-archive-drawer-line text-xs"></i> <span class="text-xs text-gray-500">Config</span> </div> <div class="flex items-center gap-1.5 text-white/60 px-2 py-1 rounded"> <i class="ri-folder-line text-xs"></i> <span class="text-xs text-gray-500">Buckets</span> </div> <div class="mt-auto pt-2 text-[10px] text-gray-500 text-center">Version: 1.0.0</div> </div> <!-- Main Content Area --> <div class="flex-1 bg-[#1a1a1a] p-4 overflow-auto"> <div class="mb-2"> <span class="text-[10px] text-gray-500">Project > Functions</span> </div> <div class="mb-3"> <!-- Functions Content --> 179 <div class="bg-[#252525] rounded-lg p-2 mb-3 flex items-center gap-2"> <div class="text-green-500 text-lg"> <i class="ri-flashlight-line"></i> </div> <div> <div class="text-lg font-bold text-white">3</div> <div class="text-[10px] text-white/60">Unchanged</div> </div> </div> <div class="bg-[#252525] border-2 border-dashed border-white/10 rounded-lg h-10 flex items-center justify-center mb-3 cursor-pointer hover:border-green-500 transition-colors"> <div class="text-green-500 text-2xl font-light">+</div> </div> <div class="bg-[#252525] rounded-lg overflow-hidden"> <table class="w-full"> <thead class="bg-[#2a2a2a]"> <tr> <th class="px-3 py-2 text-left text-[10px] font-medium text-white/60 uppercase tracking-wider"> <div class="flex items-center gap-1"> 180Name 181<i class="ri-arrow-up-line text-xs"></i> </div> </th> <th class="px-3 py-2 text-left text-[10px] font-medium text-white/60 uppercase tracking-wider">Id</th> <th class="px-3 py-2 text-left text-[10px] font-medium text-white/60 uppercase tracking-wider">Description</th> <th class="px-3 py-2 text-left text-[10px] font-medium text-white/60 uppercase tracking-wider">Tags</th> <th class="px-3 py-2 text-left text-[10px] font-medium text-white/60 uppercase tracking-wider">Actions</th> </tr> </thead> <tbody class="divide-y divide-white/10"> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white">login</td> <td class="px-3 py-2 text-[10px] text-white/60 font-mono">QmYditGQeF2g...</td> <td class="px-3 py-2 text-xs text-white/60">login</td> <td class="px-3 py-2 text-xs text-white/60">auth</td> <td class="px-3 py-2"> <div class="flex items-center gap-1"> <button class="text-green-500 hover:text-green-400"> <i class="ri-edit-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-file-copy-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-delete-bin-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-flashlight-line text-sm"></i> </button> </div> </td> </tr> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white">send_email</td> <td class="px-3 py-2 text-[10px] text-white/60 font-mono">QmK8tpRgQ2p...</td> <td class="px-3 py-2 text-xs text-white/60">send_email</td> <td class="px-3 py-2 text-xs text-white/60">email</td> <td class="px-3 py-2"> <div class="flex items-center gap-1"> <button class="text-green-500 hover:text-green-400"> <i class="ri-edit-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-file-copy-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-delete-bin-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-flashlight-line text-sm"></i> </button> </div> </td> </tr> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white">
181upload_file</td> <td class="px-3 py-2 text-[10px] text-white/60 font-mono">QmS9rmHgW3f...</td> <td class="px-3 py-2 text-xs text-white/60">upload_file</td> <td class="px-3 py-2 text-xs text-white/60">storage</td> <td class="px-3 py-2"> <div class="flex items-center gap-1"> <button class="text-green-500 hover:text-green-400"> <i class="ri-edit-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-file-copy-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-delete-bin-line text-sm"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-flashlight-line text-sm"></i> </button> </div> </td> </tr> </tbody> </table> </div> <div class="flex items-center justify-end gap-1 mt-2"> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">«</button> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">â¹</button> <button class="px-2 py-0.5 bg-green-500 text-white text-[10px] rounded">1</button> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">âº</button> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">»</button> </div> </div> </div> </div> <!-- Floating Action Button --> <div class="absolute bottom-6 right-6"> <button class="w-10 h-10 bg-green-500 rounded-full flex items-center justify-center shadow-lg hover:bg-green-600 transition-colors"> <i class="ri-git-pull-request-fill text-2xl text-white"></i> </button> </div> </div> </div>
181<script>(function(){const id = "draggable-window-build"; 182const platform = "build"; 183 184 let _docDrag = null; 185 let _docDragEnd = null; 186 187 function initWebConsoleWindow() { 188 const window = document.getElementById(id); 189 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 190 const container = window?.parentElement; 191 192 if (!window || !dragHandle || !container) return; 193 194 // Check if already bound to prevent duplicates 195 if (dragHandle._dragBound === true) return; 196 dragHandle._dragBound = true; 197 198 let isDragging = false; 199 let initialX = 0; 200 let initialY = 0; 201 202 function dragStart(e) { 203 if (e.button !== 0) return; 204 205 isDragging = true; 206 207 const rect = window.getBoundingClientRect(); 208 const containerRect = container.getBoundingClientRect(); 209 210 // Calculate where we clicked relative to the window 211 initialX = e.clientX - rect.left; 212 initialY = e.clientY - rect.top; 213 214 // Remove transform classes and set initial position 215 window.classList.remove('-translate-x-1/2', '-translate-y-1/2'); 216 window.classList.add('translate-x-0', 'translate-y-0'); 217 218 // Get current position 219 const currentLeft = rect.left - containerRect.left; 220 const currentTop = rect.top - containerRect.top; 221 222 // Set initial positions 223 window.style.left = currentLeft + 'px'; 224 window.style.top = currentTop + 'px'; 225 226 window.classList.add('user-select-none'); 227 document.body.style.cursor = 'grabbing'; 228 } 229 230 function drag(e) { 231 if (!isDragging) return; 232 233 e.preventDefault(); 234 235 const containerRect = container.getBoundingClientRect(); 236 const windowRect = window.getBoundingClientRect(); 237 238 // Calculate new position 239 const newX = e.clientX - containerRect.left - initialX; 240 const newY = e.clientY - containerRect.top - initialY; 241 242 // Keep window within container bounds 243 const maxX = containerRect.width - windowRect.width; 244 const maxY = containerRect.height - windowRect.height; 245 246 const boundedX = Math.max(0, Math.min(newX, maxX)); 247 const boundedY = Math.max(0, Math.min(newY, maxY)); 248 249 window.style.left = boundedX + 'px'; 250 window.style.top = boundedY + 'px'; 251 } 252 253 function dragEnd() { 254 if (!isDragging) return; 255 256 isDragging = false; 257 window.classList.remove('user-select-none'); 258 document.body.style.cursor = ''; 259 } 260 261 dragHandle.addEventListener('mousedown', dragStart); 262 if (!_docDrag) { 263 _docDrag = drag; 264 _docDragEnd = dragEnd; 265 document.addEventListener('mousemove', _docDrag); 266 document.addEventListener('mouseup', _docDragEnd); 267 document.addEventListener('mouseleave', _docDragEnd); 268 } 269 } 270 271 // Initialize on page load 272 if (typeof window !== 'undefined') { 273 if (document.readyState === 'loading') { 274 document.addEventListener('DOMContentLoaded', initWebConsoleWindow); 275 } else { 276 initWebConsoleWindow(); 277 } 278 } 279 280 // Re-initialize after view transitions 281 document.addEventListener('astro:page-load', () => { 282 requestAnimationFrame(initWebConsoleWindow); 283 }); 284 285 document.addEventListener('astro:before-swap', () => { 286 if (_docDrag) { 287 document.removeEventListener('mousemove', _docDrag); 288 document.removeEventListener('mouseup', _docDragEnd); 289 document.removeEventListener('mouseleave', _docDragEnd); 290 _docDrag = null; 291 _docDragEnd = null; 292 } 293 }); 294})();</script>
294 </div> <!-- Test Desktop --><div class="platform-desktop absolute inset-0 z-1" data-platform="test" style="display: none;"> <!-- Desktop Background --> <!-- Desktop Background --><div class="absolute inset-0 bg-cover bg-center rounded-2xl desktop-background" style="background-image: url('/test-background.png')"></div> <!-- Web Console Window --> <!-- Dream Desktop Window (Floating) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg max-w-4xl max-h-xl" id="draggable-dream-window-lnco2c2"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col h-full"> <!-- Browser-style title bar - spans full width --> <div class="bg-[#222] px-4 py-1.5 flex items-center border-b border-white/10 drag-handle cursor-move"> <div class="flex items-center gap-1"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> </div> <span class="ml-4 text-white/60 text-xs">Dream Desktop</span> </div> <div class="flex flex-1 overflow-hidden"> <!-- Left persistent panel --> <div class="w-60 bg-[#252525] border-r border-white/10 flex flex-col"> <!-- Stats --> <div class="grid grid-cols-3 gap-1 p-3 text-center border-b border-white/10"> <div class="bg-[#2a2a2a] rounded px-2 py-1"> <div class="text-[10px] text-white/50 uppercase">Memory</div> <div class="text-xs text-white font-medium">0%</div> <div class="text-[9px] text-white/40">77MB</div> </div> <div class="bg-[#2a2a2a] rounded px-2 py-1"> <div class="text-[10px] text-white/50 uppercase">CPU</div> <div class="text-xs text-white font-medium">7%</div> </div> <div class="bg-[#2a2a2a] rounded px-2 py-1"> <div class="text-[10px] text-white/50 uppercase">Universes</div> <div class="text-xs text-white font-medium">1</div> </div> </div> <!-- Sidebar list --> <div class="px-3 py-3 flex-1"> <div class="flex items-center justify-between mb-2"> <div class="text-[10px] text-white/50 uppercase tracking-wide">Persistent</div> <div class="flex items-center gap-1"> <button class="text-white/40 hover:text-white"><i class="ri-refresh-line text-xs"></i></button> <button class="text-white/40 hover:text-white"><i class="ri-more-2-fill text-xs"></i></button> </div> </div> <button class="w-full flex items-center justify-between bg-[#2a2a2a] hover:bg-[#2d2d2d] text-white text-sm px-2 py-2 rounded mb-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 295blackhole 296</span> <i class="ri-arrow-right-s-line text-white/60"></i> </button> </div> <div class="mt-auto p-3 space-y-2 border-t border-white/10"> <button class="w-full bg-linear-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 text-white py-2 rounded text-sm font-medium flex items-center justify-center gap-2"> <i class="ri-add-line"></i> 297Create New 298</button> <div class="text-[10px] text-white/40 uppercase tracking-wide mb-1 mt-3">Services</div> <div class="space-y-1"> <div class="flex items-center justify-between text-[11px] text-white/80 py-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 299API Service 300</span> <button class="text-white/30 hover:text-white"><i class="ri-more-2-fill"></i></button> </div> <div class="flex items-center justify-between text-[11px] text-white/80 py-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 301MCP Server 302</span> <button class="text-white/30 hover:text-white"><i class="ri-more-2-fill"></i></button> </div> <div class="flex items-center justify-between text-[11px] text-white/80 py-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 303Name Server (DNS) 304</span> <button class="text-white/30 hover:text-white"><i class="ri-more-2-fill"></i></button> </div> </div> </div> </div> <!-- Main content area --> <div class="flex-1 flex flex-col"> <!-- Top Header Bar with blackhole --> <div class="bg-[#2d2d2d] h-10 px-4 flex items-center justify-between border-b border-white/10"> <div class="flex items-center gap-3"> <i class="ri-cloud-line text-white"></i> <span class="text-white font-medium text-sm">blackhole</span> <span class="text-[10px] bg-green-900/40 text-green-400 px-2 py-0.5 rounded">RUNNING</span> </div> <div class="flex items-center gap-2"> <button class="px-3 py-1 text-xs bg-red-600/80 hover:bg-red-600 text-white rounded flex items-center gap-1"> <i class="ri-stop-fill"></i> 305Stop 306</button> <button class="px-3 py-1 text-xs bg-white/5 hover:bg-white/10 text-white/60 rounded flex items-center gap-1"> <i class="ri-delete-bin-line"></i> 307Delete 308</button> <button class="text-white/60 hover:text-white"><i class="ri-refresh-line"></i></button> <button class="text-white/60 hover:text-white"><i class="ri-close-line"></i></button> </div> </div> <!-- Navigation tabs --> <div class="px-4 flex items-center gap-6 border-b border-white/10 bg-[#252525] text-[13px]"> <button class="flex items-center gap-2 text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors py-2"> <i class="ri-home-line text-sm"></i>Overview 309</button> <button class="flex items-center gap-2 text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors py-2"> <i class="ri-radar-line text-sm"></i>Network 310</button> <button class="flex items-center gap-2 text-blue-400 border-b-2 border-blue-400 -mb-[2px] py-2"> <i class="ri-folder-3-line text-sm"></i>Projects 311</button> <button class="flex items-center gap-2 text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors py-2"> <i class="ri-file-list-3-line text-sm"></i>Logs 312</button> </div> <!-- Content area --> <div class="flex-1 flex overflow-hidden bg-[#1a1a1a]"> <!-- Column 1: Projects list --> <div class="w-64 border-r border-white/10 flex flex-col bg-[#252525]"> <div class="px-4 py-3 border-b border-white/10 flex items-center justify-between"> <div class="flex items-center gap-2"> <span class="text-white text-sm font-medium">Projects</span> <span class="bg-white/10 text-white/60 text-[10px] px-1.5 py-0.5 rounded">1</span> </div> <button class="text-white/60 hover:text-white"><i class="ri-refresh-line"></i></button> </div> <div class="p-2"> <button class="w-full text-left text-green-500 text-sm px-3 py-2 rounded bg-green-500/10 hover:bg-green-500/20"> 313demo 314</button> </div> </div> <!-- Column 2 & 3: Project details --> <div class="flex-1 flex flex-col"> <div class="px-4 py-3 border-b border-white/10 flex items-center justify-between bg-[#252525]"> <span class="text-white text-sm font-medium">demo</span> <button class="px-3 py-1 text-xs bg-red-600/80 hover:bg-red-600 text-white rounded flex items-center gap-1"> <i class="ri-delete-bin-line"></i> 315Delete 316</button> </div> <div class="flex-1 overflow-auto p-4"> <!-- Info grid --> <div class="grid grid-cols-2 gap-x-6 gap-y-2 text-xs mb-4"> <div class="flex gap-2"> <span class="text-white/50">ID:</span> <span class="text-white font-mono">QmUrnA...1s5FTV</span> <button class="text-white/40 hover:text-white"><i class="ri-file-copy-line text-xs"></i></button> </div> <div class="flex gap-2"> <span class="text-white/50">Provider:</span> <span class="text-white">github</span> </div> <div class="flex gap-2"> <span class="text-white/50">Email:</span> <span class="text-white">[email protected]</span> </div> <div class="flex gap-2"> <span class="text-white/50">Description:</span> <span class="text-white/60">Demo project</span> </div> </div> <!-- Sub tabs --> <div class="flex border-b border-white/10 mb-4 -mx-4"> <button class="flex items-center gap-2 px-4 py-2 text-xs text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors"> <i class="ri-git-repository-line"></i> 317Repositories 318</button> <button class="flex items-center gap-2 px-4 py-2 text-xs text-blue-400 border-b-2 border-blue-400 -mb-[2px]">
318 <i class="ri-grid-line"></i> 319Resources 320</button> <button class="flex items-center gap-2 px-4 py-2 text-xs text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors"> <i class="ri-folder-3-line"></i> 321Assets 322</button> </div> <!-- Resources content --> <div class="bg-[#252525] rounded-lg border border-white/10 overflow-hidden"> <!-- GLOBAL section --> <div class="border-b border-white/10"> <div class="px-3 py-1 flex items-center gap-2 bg-[#2a2a2a]"> <button class="text-white/60"><i class="ri-arrow-down-s-line text-sm"></i></button> <i class="ri-global-line text-white/60 text-sm"></i> <span class="text-white/70 text-xs uppercase tracking-wide">Global</span> </div> <!-- Functions --> <div class="border-t border-white/10"> <div class="px-3 py-1 flex items-center gap-2"> <i class="ri-flashlight-line text-white/60"></i> <span class="text-white text-xs">Functions</span> <span class="bg-white/10 text-white/60 text-[10px] px-1.5 py-0.5 rounded">1</span> </div> <div class="px-6 py-1 bg-[#1a1a1a]"> <div class="flex items-center justify-between"> <span class="text-white/80 text-xs">ping_pong</span> <div class="flex items-center gap-2"> <span class="text-[10px] bg-green-900/40 text-green-400 px-2 py-0.5 rounded">HTTPS</span> <button class="text-white/40 hover:text-white"><i class="ri-file-list-3-line text-xs"></i></button> <button class="text-white/40 hover:text-white"><i class="ri-more-2-fill text-xs"></i></button> </div> </div> </div> </div> <!-- Domains --> <div class="border-t border-white/10"> <div class="px-3 py-1 flex items-center gap-2"> <i class="ri-earth-line text-white/60"></i> <span class="text-white text-xs">Domains</span> <span class="bg-white/10 text-white/60 text-[10px] px-1.5 py-0.5 rounded">1</span> </div> <div class="px-6 py-1 bg-[#1a1a1a]"> <div class="flex items-center justify-between"> <div class="text-white/80 text-xs">GeneratedDomain</div> <div class="flex items-center gap-2"> <div class="text-white/50 text-[10px]">5d1s5ftv0.g.blackhole.localtau</div> <button class="text-white/40 hover:text-white"><i class="ri-more-2-fill text-xs"></i></button> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div>
322<script type="module" src="/_astro/DreamDesktopWindow.astro_astro_type_script_index_0_lang.C8vB3kjN.js"></script>
322
322<script>(function(){const id = "draggable-dream-window-lnco2c2"; 323const activeTab = "projects"; 324 325 let _docDrag = null; 326 let _docDragEnd = null; 327 328 function initDreamDesktopDrag() { 329 const windowEl = document.getElementById(id); 330 const dragHandle = windowEl?.querySelector('.drag-handle'); 331 const container = windowEl?.parentElement; 332 if (!windowEl || !dragHandle || !container) return; 333 334 // Check if already bound to prevent duplicates 335 if (dragHandle._dragBound === true) return; 336 dragHandle._dragBound = true; 337 338 let isDragging = false; 339 let initialX = 0; 340 let initialY = 0; 341 342 function dragStart(e) { 343 if (e.button !== 0) return; 344 isDragging = true; 345 const rect = windowEl.getBoundingClientRect(); 346 const containerRect = container.getBoundingClientRect(); 347 initialX = e.clientX - rect.left; 348 initialY = e.clientY - rect.top; 349 windowEl.classList.remove('-translate-x-1/2', '-translate-y-1/2'); 350 windowEl.classList.add('translate-x-0', 'translate-y-0'); 351 const currentLeft = rect.left - containerRect.left; 352 const currentTop = rect.top - containerRect.top; 353 windowEl.style.left = currentLeft + 'px'; 354 windowEl.style.top = currentTop + 'px'; 355 document.body.style.cursor = 'grabbing'; 356 } 357 358 function drag(e) { 359 if (!isDragging) return; 360 e.preventDefault(); 361 const containerRect = container.getBoundingClientRect(); 362 const windowRect = windowEl.getBoundingClientRect(); 363 const newX = e.clientX - containerRect.left - initialX; 364 const newY = e.clientY - containerRect.top - initialY; 365 const maxX = containerRect.width - windowRect.width; 366 const maxY = containerRect.height - windowRect.height; 367 const boundedX = Math.max(0, Math.min(newX, maxX)); 368 const boundedY = Math.max(0, Math.min(newY, maxY)); 369 windowEl.style.left = boundedX + 'px'; 370 windowEl.style.top = boundedY + 'px'; 371 } 372 373 function dragEnd() { 374 if (!isDragging) return; 375 isDragging = false; 376 document.body.style.cursor = ''; 377 } 378 379 dragHandle.addEventListener('mousedown', dragStart); 380 if (!_docDrag) { 381 _docDrag = drag; 382 _docDragEnd = dragEnd; 383 document.addEventListener('mousemove', _docDrag); 384 document.addEventListener('mouseup', _docDragEnd); 385 document.addEventListener('mouseleave', _docDragEnd); 386 } 387 } 388 389 // Initialize drag on page load 390 if (typeof window !== 'undefined') { 391 if (document.readyState === 'loading') { 392 document.addEventListener('DOMContentLoaded', initDreamDesktopDrag); 393 } else { 394 initDreamDesktopDrag(); 395 } 396 } 397 398 // Re-initialize after view transitions 399 document.addEventListener('astro:page-load', () => { 400 requestAnimationFrame(initDreamDesktopDrag); 401 }); 402 403 document.addEventListener('astro:before-swap', () => { 404 if (_docDrag) { 405 document.removeEventListener('mousemove', _docDrag); 406 document.removeEventListener('mouseup', _docDragEnd); 407 document.removeEventListener('mouseleave', _docDragEnd); 408 _docDrag = null; 409 _docDragEnd = null; 410 } 411 }); 412 413 // Initialize vis-network in Network tab 414 function initNetworkIfNeeded(retries = 8) { 415 if (activeTab !== 'network') return; 416 const containerEl = document.getElementById(`${id}-network`); 417 if (!containerEl) { 418 if (retries > 0) setTimeout(() => initNetworkIfNeeded(retries - 1), 120); 419 return; 420 } 421 if ((containerEl)._visInit) return; 422 try { 423 function withVis(callback) { 424 const g = window; 425 if (g.vis && g.vis.Network && g.vis.DataSet) { 426 callback(g.vis); 427 return; 428 } 429 // Wait for npm import to load 430 setTimeout(() => { 431 if (g.vis && g.vis.Network && g.vis.DataSet) { 432 callback(g.vis); 433 } else { 434 console.error('vis-network failed to load'); 435 } 436 }, 100); 437 } 438 439 withVis((vis) => { 440 441 const data = { 442 nodes: [ 443 { id: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', name: 'elder@blackhole', category: 0, value: { p2p: 13151 } }, 444 { id: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', name: 'monkey@blackhole', category: 1, value: { copies: 1, dns: 13404, http: 13100, p2p: 13263 } }, 445 { id: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', name: 'hoarder@blackhole', category: 2, value: { copies: 1, dns: 13404, http: 13100, p2p: 13242 } },
446 { id: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak', name: 'tns@blackhole', category: 3, value: { copies: 1, dns: 13404, http: 13566, p2p: 13326 } }, 447 { id: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85', name: 'patrick@blackhole', category: 4, value: { copies: 1, dns: 13404, http: 13524, p2p: 13284 } }, 448 { id: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ', name: 'auth@blackhole', category: 5, value: { copies: 1, dns: 13404, http: 13545, p2p: 13221 } }, 449 { id: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P', name: 'seer@blackhole', category: 6, value: { copies: 1, dns: 13404, http: 13503, p2p: 13305 } }, 450 { id: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd', name: 'gateway@blackhole', category: 7, value: { p2p: 13152 } }, 451 { id: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', name: 'substrate@blackhole', category: 8, value: { copies: 1, dns: 13404, http: 13629, p2p: 13382 } }, 452 ], 453 links: [ 454 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 455 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 456 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 457 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 458 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 459 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP' }, 460 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 461 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 462 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 463 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 464 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 465 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 466 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 467 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP' }, 468 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 469 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 470 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 471 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 472 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 473 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 474 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 475 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWAn9uLqFu3xiJywFpypp
475DxumfM4bztmReoBrKt68L5Dak' }, 476 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 477 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 478 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 479 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 480 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 481 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 482 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 483 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 484 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 485 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 486 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 487 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 488 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP' }, 489 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 490 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 491 ], 492 }; 493 494 const isDark = (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)); 495 const accentColor = '#099a40'; 496 const linkColor = '#777777'; 497 498 const nodes = new vis.DataSet( 499 data.nodes.map((n) => ({ 500 id: n.id, 501 label: n.name.split('@')[0], 502 shape: 'image', 503 image: '/taubyte-node.svg', 504 size: 25 505 })) 506 ); 507 508 const dedupedEdges = []; 509 const seenPairs = new Set(); 510 data.links.forEach((l) => { 511 const key = [l.source, l.target].sort().join('|'); 512 if (!seenPairs.has(key)) { 513 seenPairs.add(key); 514 dedupedEdges.push({ from: l.source, to: l.target }); 515 } 516 }); 517 const edges = new vis.DataSet(dedupedEdges); 518 519 const options = { 520 autoResize: true, 521 height: '100%', 522 width: '100%', 523 interaction: { hover: true, tooltipDelay: 50, zoomView: false, dragView: false }, 524 nodes: { 525 shape: 'image', 526 shadow: { enabled: true, color: 'rgba(9,154,64,0.35)', size: 10, x: 0, y: 0 }, 527 borderWidth: 0, 528 size: 25, 529 font: { size: 14, color: accentColor, vadjust: 2 }, 530 }, 531 edges: { width: 1, shadow: false, color: linkColor }, 532 physics: { 533 solver: 'repulsion', 534 repulsion: { 535 nodeDistance: 100, 536 centralGravity: 0.15, 537 springLength: 220, 538 springConstant: 0.02, 539 damping: 0.09 540 } 541 }, 542 }; 543 544 const network = new vis.Network(containerEl, { nodes, edges }, options); 545 network.once('afterDrawing', () => network.fit({ maxZoomLevel: 0.8 })); 546 (containerEl)._visInit = true; 547 (containerEl)._visNetwork = network; 548 }); 549 } catch (e) { 550 console.error('vis init error', e); 551 } 552 } 553 554 // Tear down the existing vis-network instance before a re-init so WebGL/canvas 555 // resources don't accumulate across view transitions. 556 function resetNetwork() { 557 const containerEl = document.getElementById(`${id}-network`); 558 if (!containerEl) return; 559 try { (containerEl)._visNetwork?.destroy?.(); } catch (_) {} 560 (containerEl)._visNetwork = null; 561 (containerEl)._visInit = false;
562 } 563 564 // Initialize on page load 565 if (typeof window !== 'undefined') { 566 if (document.readyState === 'loading') { 567 document.addEventListener('DOMContentLoaded', () => { 568 initNetworkIfNeeded(); 569 }); 570 } else { 571 initNetworkIfNeeded(); 572 } 573 } 574 575 // Re-initialize network after view transitions 576 document.addEventListener('astro:page-load', () => { 577 requestAnimationFrame(() => { 578 resetNetwork(); 579 initNetworkIfNeeded(); 580 }); 581 }); 582 document.addEventListener('astro:after-swap', () => { 583 requestAnimationFrame(() => { 584 resetNetwork(); 585 initNetworkIfNeeded(); 586 }); 587 }); 588})();</script>
588 </div> <!-- Ship Desktop --><div class="platform-desktop absolute inset-0 z-1" data-platform="ship" style="display: none;"> <!-- Desktop Background --> <!-- Desktop Background --><div class="absolute inset-0 bg-cover bg-center rounded-2xl desktop-background" style="background-image: url('/desktop-ship-sail.png')"></div> <!-- Web Console Window --> <!-- Taubyte Console Window (Floating) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-5xl shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg console-window" id="draggable-window-ship" data-platform="ship"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col" style="height: 520px;"> <!-- Browser Tabs --> <div class="bg-[#222] border-b border-white/10 px-4 py-1.5 flex items-center gap-1 cursor-move drag-handle" data-platform="ship"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="ml-4 text-white/60 text-xs">Console</span> </div> <!-- Top Header Bar --> <div class="bg-[#2d2d2d] h-10 px-4 flex items-center justify-between border-b border-white/10"> <!-- Left: Logo and Menu --> <div class="flex items-center gap-4"> <i class="ri-menu-2-line text-white/60"></i> </div> <!-- Right: Icons and Actions --> <div class="flex items-center gap-4"> <div class="flex items-center gap-1.5 text-white/60 text-xs bg-white/5 px-2 py-1 rounded"> <i class="ri-git-branch-line text-white/60"></i> <span>main</span> </div> <i class="ri-notification-3-line text-white/60"></i> <i class="ri-question-line text-white/60"></i> <div class="flex items-center gap-2 text-white/60"> <img src="/samy-git-avatar.png" alt="Avatar" class="w-6 h-6 rounded-full border border-gray-500"> </div> </div> </div> <!-- Main Content --> <div class="flex flex-1 overflow-hidden"> <!-- Left Sidebar --> <div class="w-40 bg-[#252525] border-r border-white/10 p-3 space-y-0.5 overflow-auto flex flex-col"> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-home-line text-xs"></i> <span class="text-xs">Home</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-function-line text-xs"></i> <span class="text-xs">Applications</span> </div> <div class="flex items-center gap-1.5 px-2 py-1 rounded text-white/60 hover:text-white hover:bg-white/10"> <i class="ri-flashlight-line text-xs"></i> <span class="text-xs">Functions</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-stack-line text-xs"></i> <span class="text-xs">Libraries</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-global-line text-xs"></i> <span class="text-xs">Websites</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-hard-drive-2-line text-xs"></i> <span class="text-xs">Storages</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-mail-line text-xs"></i> <span class="text-xs">Messaging</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-database-2-line text-xs"></i> <span class="text-xs">Databases</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-compass-3-line text-xs"></i> <span class="text-xs">Domains</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-server-line text-xs"></i> <span class="text-xs">Services</span> </div> <div class="flex items-center gap-1.5 text-white/60 hover:text-white hover:bg-white/10 px-2 py-1 rounded"> <i class="ri-settings-4-line text-xs"></i> <span class="text-xs">SmartOps</span> </div> <div class="border-t border-white/10 my-1"></div> <div class="flex items-center gap-1.5 px-2 py-1 rounded text-green-500 bg-white/10"> <i class="ri-task-line text-xs"></i> <span class="text-xs">Builds</span> </div> <div class="flex items-center gap-1.5 text-white/60 px-2 py-1 rounded"> <i class="ri-archive-drawer-line text-xs"></i> <span class="text-xs text-gray-500">Config</span> </div> <div class="flex items-center gap-1.5 text-white/60 px-2 py-1 rounded"> <i class="ri-folder-line text-xs"></i> <span class="text-xs text-gray-500">Buckets</span> </div> <div class="mt-auto pt-2 text-[10px] text-gray-500 text-center">Version: 1.0.0</div> </div> <!-- Main Content Area --> <div class="flex-1 bg-[#1a1a1a] p-4 overflow-auto"> <div class="mb-2"> <span class="text-[10px] text-gray-500">Project > Builds</span> </div> <div class="mb-3"> <!-- Builds Content --> 589 <div class="flex items-center justify-between mb-4"> <h1 class="text-white text-base font-semibold tracking-wide">BUILDS</h1> <div class="flex items-center gap-3 text-xs text-white/60"> <div class="flex items-center gap-1"> <span>Show</span> <select class="bg-[#252525] border border-white/10 text-white text-xs px-1.5 py-0.5 rounded"> <option>5</option> <option>10</option> <option>25</option> <option>50</option> </select> <span>entries</span> </div> <div class="flex items-center gap-1"> <span>Search:</span> <input type="text" placeholder="..." class="bg-[#252525] border border-white/10 text-white text-xs px-2 py-0.5 rounded w-32"> </div> </div> </div> <div class="bg-[#252525] rounded-lg overflow-hidden"> <table class="w-full"> <thead class="bg-[#2a2a2a]"> <tr> <th class="px-4 py-2 text-left text-xs font-medium text-white/70 uppercase tracking-wide"> <div class="flex items-center gap-1"> 590Date 591<i class="ri-arrow-up-down-line text-xs opacity-50"></i> </div> </th> <th class="px-4 py-2 text-left text-xs font-medium text-white/70 uppercase tracking-wide"> <div class="flex items-center gap-1"> 592Type 593<i class="ri-arrow-up-down-line text-xs opacity-50"></i> </div> </th> <th class="px-4 py-2 text-left text-xs font-medium text-white/70 uppercase tracking-wide"> <div class="flex items-center gap-1"> 594Build Status 595<i class="ri-arrow-up-down-line text-xs opacity-50"></i> </div> </th> <th class="px-4 py-2 text-left text-xs font-medium text-white/70 uppercase tracking-wide"> <div class="flex items-center gap-1"> 596Commit ID 597<i class="ri-arrow-up-down-line text-xs opacity-50"></i> </div> </th> <th class="px-4 py-2 text-left text-xs font-medium text-white/70 uppercase tracking-wide">Actions</th> </tr> </thead> <tbody class="divide-y divide-white/10"> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white/80">05/10/2026 2:52:12 AM</td> <td class="px-3 py-2 text-xs text-white/80">Config</td> <td class="px-3 py-2"> <span class="bg-green-900/40 text-green-400 px-2 py-1 rounded text-xs">Success</span> </td> <td class="px-3 py-2 text-xs text-white/60 font-mono">f694e4f4105f23266d5d2eda6fc1836f1b7ad12e</td> <td class="px-3 py-2"> <div class="flex items-center gap-2"> <button class="text-green-500 hover:text-green-400"> <i class="ri-eye-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-external-link-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-forbid-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-refresh-line"></i> </button> </div> </td> </tr> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white/80">05/10/2026 2:52:12 AM</td> <td class="px-3 py-2 text-xs text-white/80">Website</td> <td class="px-3 py-2"> <span class="bg-green-900/40 text-green-400 px-2 py-1 rounded text-xs">Success</span> </td> <td class="px-3 py-2 text-xs text-white/60 font-mono">a1b2c3d4e5f6789012345678901234567890abcd</td> <td class="px-3 py-2"> <div class="flex items-center gap-2"> <button class="text-green-500 hover:text-green-400"> <i class="ri-eye-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-external-link-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-forbid-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-refresh-line"></i> </button> </div> </td> </tr> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white/80">05/10/2026 2:52:12 AM</td> <td class="px-3 py-2 text-xs text-white/80">Config</td> <td class="px-3 py-2"> <span class="bg-green-900/40 text-green-400 px-2 py-1 rounded text-xs">Success</span> </td> <td class="px-3 py-2 text-xs text-white/60 font-mono">9876543210abcdef1234567890abcdef12345678</td> <td class="px-3 py-2"> <div class="flex items-center gap-2"> <button class="text-green-500 hover:text-green-400"> <i class="ri-eye-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-external-link-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-forbid-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-refresh-line"></i> </button> </div> </td> </tr> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white/80">05/10/2026 2:52:12 AM</td> <td class="px-3 py-2 text-xs text-white/80">Library</td> <td class="px-3 py-2"> <span class="bg-green-900/40 text-green-400 px-2 py-1 rounded text-xs">Success</span> </td> <td class="px-3 py-2 text-xs text-white/60 font-mono">
597aabbccddeeff1122334455667788990011223344</td> <td class="px-3 py-2"> <div class="flex items-center gap-2"> <button class="text-green-500 hover:text-green-400"> <i class="ri-eye-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-external-link-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-forbid-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-refresh-line"></i> </button> </div> </td> </tr> <tr class="hover:bg-[#2a2a2a]"> <td class="px-3 py-2 text-xs text-white/80">05/10/2026 2:52:12 AM</td> <td class="px-3 py-2 text-xs text-white/80">Library</td> <td class="px-3 py-2"> <span class="bg-green-900/40 text-green-400 px-2 py-1 rounded text-xs">Success</span> </td> <td class="px-3 py-2 text-xs text-white/60 font-mono">5566778899001122334455667788990011223344</td> <td class="px-3 py-2"> <div class="flex items-center gap-2"> <button class="text-green-500 hover:text-green-400"> <i class="ri-eye-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-external-link-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-forbid-line"></i> </button> <button class="text-green-500 hover:text-green-400"> <i class="ri-refresh-line"></i> </button> </div> </td> </tr> </tbody> </table> </div> <div class="flex items-center justify-between mt-4"> <button class="bg-green-600 hover:bg-green-700 text-white px-3 py-1 rounded text-xs font-medium"> 598Refresh 599</button> <!-- Pagination --> <div class="flex items-center gap-1"> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">«</button> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">â¹</button> <button class="px-2 py-0.5 bg-green-500 text-white text-[10px] rounded">1</button> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">âº</button> <button class="px-2 py-0.5 text-white/60 hover:text-white text-[10px]">»</button> </div> </div> </div> </div> </div> <!-- Floating Action Button --> </div> </div>
599<script>(function(){const id = "draggable-window-ship"; 600const platform = "ship"; 601 602 let _docDrag = null; 603 let _docDragEnd = null; 604 605 function initWebConsoleWindow() { 606 const window = document.getElementById(id); 607 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 608 const container = window?.parentElement; 609 610 if (!window || !dragHandle || !container) return; 611 612 // Check if already bound to prevent duplicates 613 if (dragHandle._dragBound === true) return; 614 dragHandle._dragBound = true; 615 616 let isDragging = false; 617 let initialX = 0; 618 let initialY = 0; 619 620 function dragStart(e) { 621 if (e.button !== 0) return; 622 623 isDragging = true; 624 625 const rect = window.getBoundingClientRect(); 626 const containerRect = container.getBoundingClientRect(); 627 628 // Calculate where we clicked relative to the window 629 initialX = e.clientX - rect.left; 630 initialY = e.clientY - rect.top; 631 632 // Remove transform classes and set initial position 633 window.classList.remove('-translate-x-1/2', '-translate-y-1/2'); 634 window.classList.add('translate-x-0', 'translate-y-0'); 635 636 // Get current position 637 const currentLeft = rect.left - containerRect.left; 638 const currentTop = rect.top - containerRect.top; 639 640 // Set initial positions 641 window.style.left = currentLeft + 'px'; 642 window.style.top = currentTop + 'px'; 643 644 window.classList.add('user-select-none'); 645 document.body.style.cursor = 'grabbing'; 646 } 647 648 function drag(e) { 649 if (!isDragging) return; 650 651 e.preventDefault(); 652 653 const containerRect = container.getBoundingClientRect(); 654 const windowRect = window.getBoundingClientRect(); 655 656 // Calculate new position 657 const newX = e.clientX - containerRect.left - initialX; 658 const newY = e.clientY - containerRect.top - initialY; 659 660 // Keep window within container bounds 661 const maxX = containerRect.width - windowRect.width; 662 const maxY = containerRect.height - windowRect.height; 663 664 const boundedX = Math.max(0, Math.min(newX, maxX)); 665 const boundedY = Math.max(0, Math.min(newY, maxY)); 666 667 window.style.left = boundedX + 'px'; 668 window.style.top = boundedY + 'px'; 669 } 670 671 function dragEnd() { 672 if (!isDragging) return; 673 674 isDragging = false; 675 window.classList.remove('user-select-none'); 676 document.body.style.cursor = ''; 677 } 678 679 dragHandle.addEventListener('mousedown', dragStart); 680 if (!_docDrag) { 681 _docDrag = drag; 682 _docDragEnd = dragEnd; 683 document.addEventListener('mousemove', _docDrag); 684 document.addEventListener('mouseup', _docDragEnd); 685 document.addEventListener('mouseleave', _docDragEnd); 686 } 687 } 688 689 // Initialize on page load 690 if (typeof window !== 'undefined') { 691 if (document.readyState === 'loading') { 692 document.addEventListener('DOMContentLoaded', initWebConsoleWindow); 693 } else { 694 initWebConsoleWindow(); 695 } 696 } 697 698 // Re-initialize after view transitions 699 document.addEventListener('astro:page-load', () => { 700 requestAnimationFrame(initWebConsoleWindow); 701 }); 702 703 document.addEventListener('astro:before-swap', () => { 704 if (_docDrag) { 705 document.removeEventListener('mousemove', _docDrag); 706 document.removeEventListener('mouseup', _docDragEnd); 707 document.removeEventListener('mouseleave', _docDragEnd); 708 _docDrag = null; 709 _docDragEnd = null; 710 } 711 }); 712})();</script>
712 </div> <!-- Switcher - Only show if not in screenshot mode --> <!-- Switcher - Half floating, half inside --><div class="absolute -top-6 left-1/2 -translate-x-1/2 z-10 flex justify-center" data-astro-cid-mw4b5lvp> <div class="platform-switcher-container relative inline-flex rounded-full p-1 bg-[#151515]/80 dark:bg-[#151515]/80 backdrop-blur-md dark:border dark:border-white/10 transition-colors shadow-lg" role="tablist" aria-label="Platform options" data-astro-cid-mw4b5lvp> <!-- Sliding indicator --> <div class="platform-switcher-indicator absolute rounded-full bg-white shadow-sm transition-all duration-300 ease-linear" style="height: calc(100% - 8px); top: 4px; left: 4px; width: calc(25% - 8px);" data-astro-cid-mw4b5lvp></div> <button class="platform-switcher-btn relative px-6 py-2 text-black font-medium text-sm transition-all duration-300 ease-linear" role="tab" aria-selected="true" aria-controls="panel-your-cloud" data-astro-cid-mw4b5lvp> 713Your Cloud 714</button> <button class="platform-switcher-btn relative px-6 py-2 text-sm font-medium transition-all duration-300 ease-linear text-white" role="tab" aria-selected="false" aria-controls="panel-build" data-astro-cid-mw4b5lvp> 715Build 716</button> <button class="platform-switcher-btn relative px-6 py-2 text-sm font-medium transition-all duration-300 ease-linear text-white" role="tab" aria-selected="false" aria-controls="panel-test" data-astro-cid-mw4b5lvp> 717Test 718</button> <button class="platform-switcher-btn relative px-6 py-2 text-sm font-medium transition-all duration-300 ease-linear text-white" role="tab" aria-selected="false" aria-controls="panel-ship" data-astro-cid-mw4b5lvp> 719Ship 720</button> </div> </div>
720<script type="module">function p(){const r=document.querySelector(".platform-switcher-container");if(!r)return;const a=r.querySelectorAll(".platform-switcher-btn"),l=r.querySelector(".platform-switcher-indicator");document.querySelectorAll(".platform-desktop").forEach(o=>{const t=o;t.style.transition="opacity 0.4s ease-in-out",t.dataset.platform==="your-cloud"?(t.style.display="block",t.style.opacity="1",t.classList.add("fade-in"),t.classList.remove("fade-out")):(t.style.opacity="0",t.classList.add("fade-out"),t.classList.remove("fade-in"),t.style.display="none")});function c(o){if(!l)return;const t=o.closest(".platform-switcher-container");if(!t)return;const i=o.getBoundingClientRect(),d=t.getBoundingClientRect(),n=i.left-d.left,e=i.width;l.style.left=`${n}px`,l.style.width=`${e}px`}l&&a.length>0&&c(a[0]),a.forEach(o=>{const t=o;t._platformBound!==!0&&(t._platformBound=!0,t.addEventListener("click",()=>{a.forEach(e=>{e.classList.remove("text-black"),e.classList.add("text-white"),e.setAttribute("aria-selected","false")}),t.classList.remove("text-white"),t.classList.add("text-black"),t.setAttribute("aria-selected","true"),c(t);const i=t.getAttribute("aria-controls"),n={"panel-your-cloud":"your-cloud","panel-build":"build","panel-test":"test","panel-ship":"ship"}[i||""];if(n){const e=document.querySelector(`.platform-desktop[data-platform="${n}"]`);if(!e)return;document.querySelectorAll(".platform-desktop").forEach(f=>{const s=f;s!==e&&(s.style.transition="opacity 0.4s ease-in-out",s.style.opacity="0",s.classList.add("fade-out"),s.classList.remove("fade-in"),s.style.display!=="none"&&setTimeout(()=>{s.style.display="none"},400))}),e.style.display==="none"&&(e.style.display="block",e.style.opacity="0",e.classList.remove("fade-out"),e.offsetHeight,requestAnimationFrame(()=>{e.style.opacity="1",e.classList.add("fade-in")}))}}))})}document.addEventListener("astro:page-load",()=>{p()});document.addEventListener("reinit-switchers",()=>{p()});</script>
720 <!-- Platform Info Bar --> <!-- Platform Info Bar --><div class="absolute -bottom-10 left-0 right-0 bg-[#101010]/90 backdrop-blur-md rounded-b-2xl px-4 pt-24 pb-2 shadow-lg z-0"> <!-- Platform-specific content --> <div class="platform-content text-center"> <div class="platform-info text-gray-300 text-sm md:text-base lg:text-lg " data-platform="your-cloud"> Run Taubyte on infrastructure you control: private, hybrid, or provider environments â including the edge </div><div class="platform-info text-gray-300 text-sm md:text-base lg:text-lg hidden" data-platform="build"> Create software locally with developers and coding agents in one fullstack workspace </div><div class="platform-info text-gray-300 text-sm md:text-base lg:text-lg hidden" data-platform="test"> Keep local work grounded in production-like behavior with Dream CLI, MCP, and desktop tools </div><div class="platform-info text-gray-300 text-sm md:text-base lg:text-lg hidden" data-platform="ship"> Push to Git and let Taubyte deploy, route, and run it on your infrastructure </div> </div> </div>
720<script type="module">function i(){const e=document.querySelector(".platform-content");if(!e||e.dataset.initialized==="true")return;e.dataset.initialized="true";const l=document.querySelectorAll(".platform-info");function a(o){l.forEach(n=>{const t=n;t.dataset.platform===o?t.classList.remove("hidden"):t.classList.add("hidden")})}document.querySelectorAll(".platform-switcher-btn").forEach(o=>{o.addEventListener("click",()=>{const n={"panel-your-cloud":"your-cloud","panel-build":"build","panel-test":"test","panel-ship":"ship"},t=o.getAttribute("aria-controls"),r=n[t||""];r&&a(r)})}),a("your-cloud")}document.addEventListener("astro:page-load",()=>{i()});</script>
720 </div> </div> <!-- Mobile Platform Cards --> <!-- Mobile Platform Cards --><div class="lg:hidden"> <div class="mt-8"> <div class="space-y-12"> <div class="relative"> <!-- Floating Title --> <div class="absolute -top-6 left-1/2 -translate-x-1/2 z-10"> <div class="bg-[#151515]/80 dark:bg-[#151515]/80 backdrop-blur-md border border-white/10 rounded-full px-6 py-2 shadow-lg"> <h3 class="text-sm font-medium text-white"> Your Cloud </h3> </div> </div> <div class="bg-[#1a1a1a] border border-white/10 rounded-lg overflow-hidden hover:border-white/20 transition-colors"> <!-- Screenshot preview --> <div class="aspect-video bg-[#2a2a2a] flex items-center justify-center"> <img src="/screenshots/your-cloud-desktop.png" alt="Your Cloud desktop interface" class="w-full h-full object-cover" loading="lazy"> </div> <!-- Content --> <div class="p-3 text-center"> <p class="text-sm md:text-base lg:text-lg text-gray-300"> Run Taubyte on infrastructure you control: private, hybrid, or provider environments â including the edge </p> </div> </div> </div><div class="relative"> <!-- Floating Title --> <div class="absolute -top-6 left-1/2 -translate-x-1/2 z-10"> <div class="bg-[#151515]/80 dark:bg-[#151515]/80 backdrop-blur-md border border-white/10 rounded-full px-6 py-2 shadow-lg"> <h3 class="text-sm font-medium text-white"> Build </h3> </div> </div> <div class="bg-[#1a1a1a] border border-white/10 rounded-lg overflow-hidden hover:border-white/20 transition-colors"> <!-- Screenshot preview --> <div class="aspect-video bg-[#2a2a2a] flex items-center justify-center"> <img src="/screenshots/build-desktop.png" alt="Build desktop interface" class="w-full h-full object-cover" loading="lazy"> </div> <!-- Content --> <div class="p-3 text-center"> <p class="text-sm md:text-base lg:text-lg text-gray-300"> Create software locally with developers and coding agents in one fullstack workspace </p> </div> </div> </div><div class="relative"> <!-- Floating Title --> <div class="absolute -top-6 left-1/2 -translate-x-1/2 z-10"> <div class="bg-[#151515]/80 dark:bg-[#151515]/80 backdrop-blur-md border border-white/10 rounded-full px-6 py-2 shadow-lg"> <h3 class="text-sm font-medium text-white"> Test </h3> </div> </div> <div class="bg-[#1a1a1a] border border-white/10 rounded-lg overflow-hidden hover:border-white/20 transition-colors"> <!-- Screenshot preview --> <div class="aspect-video bg-[#2a2a2a] flex items-center justify-center"> <img src="/screenshots/test-desktop.png" alt="Test desktop interface" class="w-full h-full object-cover" loading="lazy"> </div> <!-- Content --> <div class="p-3 text-center"> <p class="text-sm md:text-base lg:text-lg text-gray-300"> Keep local work grounded in production-like behavior with Dream CLI, MCP, and desktop tools </p> </div> </div> </div><div class="relative"> <!-- Floating Title --> <div class="absolute -top-6 left-1/2 -translate-x-1/2 z-10"> <div class="bg-[#151515]/80 dark:bg-[#151515]/80 backdrop-blur-md border border-white/10 rounded-full px-6 py-2 shadow-lg"> <h3 class="text-sm font-medium text-white"> Ship </h3> </div> </div> <div class="bg-[#1a1a1a] border border-white/10 rounded-lg overflow-hidden hover:border-white/20 transition-colors"> <!-- Screenshot preview --> <div class="aspect-video bg-[#2a2a2a] flex items-center justify-center"> <img src="/screenshots/ship-desktop.png" alt="Ship desktop interface" class="w-full h-full object-cover" loading="lazy"> </div> <!-- Content --> <div class="p-3 text-center"> <p class="text-sm md:text-base lg:text-lg text-gray-300"> Push to Git and let Taubyte deploy, route, and run it on your infrastructure </p> </div> </div> </div> </div> </div> </div> </div> </section> <section id="features-section" class="relative px-6 mt-16" aria-labelledby="features-heading"> <div class="container mx-auto max-w-7xl"> <h2 id="features-heading" class="sr-only"> 721Build, test, and ship in one workspace 722</h2> <div id="feature-your-own-cloud" class="rounded-2xl bg-black/10 dark:bg-[#111] p-6 md:p-8 mb-8"> <div class="flex flex-col lg:flex-row gap-8 items-center"> <div class="order-1 lg:order-1 shrink-0 w-full lg:w-1/3"> <div> <div> <a href="/features#your-own-cloud" class="inline-flex items-center gap-2 text-xl md:text-2xl font-semibold leading-tight text-black dark:text-white hover:opacity-80"> Customer-Owned Cloud <i class="ri-arrow-right-line mt-1" aria-hidden="true"></i> </a> <p class="mt-4 text-base md:text-lg text-black/70 dark:text-white/70 max-w-2xl">
722Run Taubyte on infrastructure you control: on-prem, private, hybrid, or provider environments, including the edge.</p> </div> </div> </div> <div class="order-2 lg:order-2 grow w-full lg:w-2/3"> <div> <!-- Mobile: Static screenshot --> <div class="lg:hidden relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full"> <img src="/screenshots/your-own-cloud-feature-desktop.png" alt="Customer-Owned Cloud feature interface" class="w-full h-full object-cover" loading="lazy"> </div> <!-- Desktop: Interactive component --> <div class="hidden lg:block relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full"> <img src="/amazigh-desert-background.png" alt="" class="absolute inset-0 h-full w-full object-cover pointer-events-none" loading="lazy"> <!-- Desktop windows --> <div class="absolute inset-0"> <div class="relative w-full h-full overflow-hidden"> <!-- Login Window - positioned away from top and left --> <div class="absolute inset-0" style="transform: translate(-5%, -5%);"> <!-- Browser Window with Login Page (Floating) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-xl shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg" id="draggable-window-cloud-login" data-platform="cloud-login"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col" style="height: 380px;"> <!-- Browser Window Controls --> <div class="bg-[#222] border-b border-white/10 px-4 py-1 flex items-center gap-1 cursor-move drag-handle" data-platform="cloud-login"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span id="loginWindowTitle" class="ml-4 text-white/60 text-xs">Taubyte Cloud Login</span> </div> <!-- Browser Address Bar --> <div class="bg-[#2d2d2d] px-4 py-1 border-b border-white/10"> <div class="flex items-center gap-2"> <!-- Navigation Buttons --> <div class="flex items-center gap-1"> <button class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded"> <i class="ri-arrow-left-s-line text-xs"></i> </button> <button class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded"> <i class="ri-arrow-right-s-line text-xs"></i> </button> <button class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded"> <i class="ri-refresh-line text-xs"></i> </button> </div> <!-- Address Bar --> <div class="flex-1 bg-[#1a1a1a] border border-white/10 rounded px-2 py-1 flex items-center gap-1.5"> <i id="loginLockIcon" class="ri-lock-line text-green-500 text-[10px]"></i> <span id="loginAddressUrl" class="text-white/70 text-[10px]">https://cloud.taubyte.com/login</span> </div> <!-- Browser Menu --> <div class="flex items-center gap-2"> <button class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded"> <i class="ri-more-2-line text-xs"></i> </button> </div> </div> </div> <!-- Browser Content - Login Page --> <div class="flex-1 bg-[#1a1a1a] overflow-auto"> <!-- Login Page Content --> <div class="min-h-full flex items-center justify-center bg-gradient-to-br from-[#1a1a1a] to-[#2d2d2d] p-3"> <div class="w-full max-w-sm"> <!-- Login Form --> <div class="bg-[#252525] rounded-lg border border-white/10 p-3 shadow-lg"> <form class="space-y-2"> <!-- Logo --> <div class="flex justify-center mb-2"> <img id="loginLogo" src="/taubyte-logo-icon.svg" alt="Taubyte" class="max-h-8 w-auto"> </div> <!-- Email/Username Field --> <div> <input type="text" id="email" placeholder="Email or Username" class="w-full bg-[#1a1a1a] border border-white/10 rounded-md px-2 py-1 text-white placeholder:text-white/40 text-[10px] focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all"> </div> <!-- Password Field --> <div> <div class="relative"> <input type="password" id="password" placeholder="Password" class="w-full bg-[#1a1a1a] border border-white/10 rounded-md px-2 py-1 text-white placeholder:text-white/40 text-[10px] focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all pr-8"> <button type="button" class="absolute right-2 top-1/2 -translate-y-1/2 text-white/40 hover:text-white/70"> <i class="ri-eye-off-line text-[10px]"></i> </button> </div> </div> <!-- Remember Me & Forgot Password --> <div class="flex items-center justify-between text-[10px]"> <label class="flex items-center gap-1 cursor-pointer"> <input type="checkbox" class="w-3 h-3 rounded border-white/20 bg-[#1a1a1a] text-green-500 focus:ring-green-500 focus:ring-offset-0"> <span class="text-white/70">Remember me</span> </label> <a id="loginForgotPasswordLink" href="#" class="text-green-500 hover:text-green-400 transition-colors text-[10px]"> 723Forgot password? 724</a> </div> <!-- Sign In Button --> <button type="submit" id="loginSignInButton" class="w-full bg-green-600 hover:bg-green-700 text-white font-medium py-1 px-3 rounded-md transition-colors shadow-lg text-[10px]"> 725Sign In 726</button> <!-- Divider --> <div class="relative my-2"> <div class="absolute inset-0 flex items-center"> <div class="w-full border-t border-white/10"></div> </div> <div class="relative flex justify-center text-[10px]"> <span class="px-2 bg-[#252525] text-white/40">Or continue with</span> </div> </div> <!-- Social Login Buttons --> <div class="grid grid-cols-3 gap-1.5"> <button type="button" class="flex items-center justify-center gap-1 bg-[#1a1a1a] border border-white/10 text-white/70 hover:text-white hover:border-white/20 px-2 py-1 rounded-md transition-colors text-[10px]"> <i class="ri-github-fill text-xs"></i> <span>GitHub</span> </button> <button type="button" class="flex items-center justify-center gap-1 bg-[#1a1a1a] border border-white/10 text-white/70 hover:text-white hover:border-white/20 px-2 py-1 rounded-md transition-colors text-[10px]"> <i class="ri-gitlab-fill text-xs"></i> <span>GitLab</span> </button> <button type="button" class="flex items-center justify-center gap-1 bg-[#1a1a1a] border border-white/10 text-white/70 hover:text-white hover:border-white/20 px-2 py-1 rounded-md transition-colors text-[10px]">
726 <i class="ri-code-s-slash-line text-xs"></i> <span>Bitbucket</span> </button> </div> <!-- Sign Up Link --> <div class="text-center text-[10px] mt-2"> <span class="text-white/60">Don't have an account? </span> <a id="loginSignUpLink" href="#" class="text-green-500 hover:text-green-400 font-medium transition-colors"> 727Sign up 728</a> </div> </form> </div> </div> </div> </div> </div> </div>
728<script>(function(){const id = "draggable-window-cloud-login"; 729const platform = "cloud-login"; 730 731 let _docDrag = null; 732 let _docDragEnd = null; 733 734 function initLoginWindow() { 735 const window = document.getElementById(id); 736 if (!window) { 737 // Retry after a short delay if element isn't ready (for view transitions) 738 setTimeout(initLoginWindow, 50); 739 return; 740 } 741 742 // Skip if already initialized 743 if (window.dataset.loginInitialized === 'true') { 744 return; 745 } 746 window.dataset.loginInitialized = 'true'; 747 748 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 749 const container = window?.parentElement; 750 751 if (!dragHandle || !container) { 752 // Elements might not be ready yet, retry 753 window.dataset.loginInitialized = 'false'; 754 setTimeout(initLoginWindow, 50); 755 return; 756 } 757 758 let isDragging = false; 759 let initialX = 0; 760 let initialY = 0; 761 762 function dragStart(e) { 763 if (e.button !== 0) return; 764 765 isDragging = true; 766 767 const rect = window.getBoundingClientRect(); 768 const containerRect = container.getBoundingClientRect(); 769 770 // Calculate where we clicked relative to the window 771 initialX = e.clientX - rect.left; 772 initialY = e.clientY - rect.top; 773 774 // Remove transform classes and set initial position 775 window.classList.remove('-translate-x-1/2', '-translate-y-1/2'); 776 window.classList.add('translate-x-0', 'translate-y-0'); 777 778 // Get current position 779 const currentLeft = rect.left - containerRect.left; 780 const currentTop = rect.top - containerRect.top; 781 782 // Set initial positions 783 window.style.left = currentLeft + 'px'; 784 window.style.top = currentTop + 'px'; 785 786 window.classList.add('user-select-none'); 787 document.body.style.cursor = 'grabbing'; 788 } 789 790 function drag(e) { 791 if (!isDragging) return; 792 793 e.preventDefault(); 794 795 const containerRect = container.getBoundingClientRect(); 796 const windowRect = window.getBoundingClientRect(); 797 798 // Calculate new position 799 const newX = e.clientX - containerRect.left - initialX; 800 const newY = e.clientY - containerRect.top - initialY; 801 802 // Keep window within container bounds 803 const maxX = containerRect.width - windowRect.width; 804 const maxY = containerRect.height - windowRect.height; 805 806 const boundedX = Math.max(0, Math.min(newX, maxX)); 807 const boundedY = Math.max(0, Math.min(newY, maxY)); 808 809 window.style.left = boundedX + 'px'; 810 window.style.top = boundedY + 'px'; 811 } 812 813 function dragEnd() { 814 if (!isDragging) return; 815 816 isDragging = false; 817 window.classList.remove('user-select-none'); 818 document.body.style.cursor = ''; 819 } 820 821 dragHandle.addEventListener('mousedown', dragStart); 822 if (!_docDrag) { 823 _docDrag = drag; 824 _docDragEnd = dragEnd; 825 document.addEventListener('mousemove', _docDrag); 826 document.addEventListener('mouseup', _docDragEnd); 827 document.addEventListener('mouseleave', _docDragEnd); 828 } 829 } 830 831 // Initialize on DOM ready 832 if (document.readyState === 'loading') { 833 document.addEventListener('DOMContentLoaded', initLoginWindow); 834 } else { 835 initLoginWindow(); 836 } 837 838 // Also initialize after view transitions 839 document.addEventListener('astro:page-load', initLoginWindow); 840 document.addEventListener('astro:after-swap', initLoginWindow); 841 842 document.addEventListener('astro:before-swap', () => { 843 if (_docDrag) { 844 document.removeEventListener('mousemove', _docDrag); 845 document.removeEventListener('mouseup', _docDragEnd); 846 document.removeEventListener('mouseleave', _docDragEnd); 847 _docDrag = null; 848 _docDragEnd = null; 849 } 850 }); 851})();</script>
851 </div> <!-- Settings Window - positioned to the right and slightly down --> <div class="absolute inset-0" style="transform: translate(20%, 15%);"> <!-- Settings Window (Floating) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-xs shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg" id="draggable-settings-window-cloud-settings" data-platform="cloud-settings"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col"> <!-- Window Title Bar --> <div class="bg-[#222] border-b border-white/10 px-4 py-1 flex items-center gap-1 cursor-move drag-handle" data-platform="cloud-settings"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="ml-4 text-white/60 text-xs">Settings</span> </div> <!-- Window Content --> <div class="flex-1 bg-[#1a1a1a] overflow-auto"> <!-- Settings Content --> <div class="p-4"> <div class="space-y-3"> <!-- Logo Drop Zone --> <div> <div id="logoDropZone" class="w-full h-20 bg-[#252525] border-2 border-dashed border-white/20 rounded-md flex items-center justify-center cursor-pointer hover:border-green-500 hover:bg-[#2d2d2d] transition-colors"> <input type="file" id="logoUpload" accept="image/*" class="hidden"> <div id="logoDropText" class="text-center"> <i class="ri-image-add-line text-white/40 text-lg block mb-1"></i> <span class="text-[10px] text-white/60">Drop logo here or click to browse</span> </div> </div> </div> <!-- Main Color --> <div class="flex items-center gap-2"> <label for="mainColor" class="text-[10px] text-white/60 whitespace-nowrap">Main Color</label> <input type="color" id="mainColor" value="#22c55e" class="flex-1 h-8 bg-[#252525] border border-white/10 rounded-md cursor-pointer"> </div> <!-- Domain --> <div class="flex items-center gap-2"> <label for="domain" class="text-[10px] text-white/60 whitespace-nowrap">Domain</label> <input type="text" id="domain" placeholder="cloud.taubyte.com" value="cloud.taubyte.com" class="flex-1 bg-[#252525] border border-white/10 rounded-md px-2 py-1 text-white placeholder:text-white/40 text-[10px] focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all"> </div> <!-- Company Name --> <div class="flex items-center gap-2"> <label for="companyName" class="text-[10px] text-white/60 whitespace-nowrap">Company Name</label> <input type="text" id="companyName" placeholder="Company Name" value="Taubyte" class="flex-1 bg-[#252525] border border-white/10 rounded-md px-2 py-1 text-white placeholder:text-white/40 text-[10px] focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all"> </div> </div> </div> </div> </div> </div>
851<script>(function(){const id = "draggable-settings-window-cloud-settings"; 852const platform = "cloud-settings"; 853 854 let _docDrag = null; 855 let _docDragEnd = null; 856 857 function initSettingsWindow() { 858 const window = document.getElementById(id); 859 if (!window) { 860 // Retry after a short delay if element isn't ready (for view transitions) 861 setTimeout(initSettingsWindow, 50); 862 return; 863 } 864 865 // Skip if already initialized (check for data attribute) 866 if (window.dataset.settingsInitialized === 'true') { 867 return; 868 } 869 window.dataset.settingsInitialized = 'true'; 870 871 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 872 const container = window?.parentElement; 873 874 if (!dragHandle || !container) { 875 // Elements might not be ready yet, retry 876 window.dataset.settingsInitialized = 'false'; 877 setTimeout(initSettingsWindow, 50); 878 return; 879 } 880 881 let isDragging = false; 882 let initialX = 0; 883 let initialY = 0; 884 885 function dragStart(e) { 886 if (e.button !== 0) return; 887 888 isDragging = true; 889 890 const rect = window.getBoundingClientRect(); 891 const containerRect = container.getBoundingClientRect(); 892 893 initialX = e.clientX - rect.left; 894 initialY = e.clientY - rect.top; 895 896 window.classList.remove('-translate-x-1/2', '-translate-y-1/2'); 897 window.classList.add('translate-x-0', 'translate-y-0'); 898 899 const currentLeft = rect.left - containerRect.left; 900 const currentTop = rect.top - containerRect.top; 901 902 window.style.left = currentLeft + 'px'; 903 window.style.top = currentTop + 'px'; 904 905 window.classList.add('user-select-none'); 906 document.body.style.cursor = 'grabbing'; 907 } 908 909 function drag(e) { 910 if (!isDragging) return; 911 912 e.preventDefault(); 913 914 const containerRect = container.getBoundingClientRect(); 915 const windowRect = window.getBoundingClientRect(); 916 917 const newX = e.clientX - containerRect.left - initialX; 918 const newY = e.clientY - containerRect.top - initialY; 919 920 const maxX = containerRect.width - windowRect.width; 921 const maxY = containerRect.height - windowRect.height; 922 923 const boundedX = Math.max(0, Math.min(newX, maxX)); 924 const boundedY = Math.max(0, Math.min(newY, maxY)); 925 926 window.style.left = boundedX + 'px'; 927 window.style.top = boundedY + 'px'; 928 } 929 930 function dragEnd() { 931 if (!isDragging) return; 932 933 isDragging = false; 934 window.classList.remove('user-select-none'); 935 document.body.style.cursor = ''; 936 } 937 938 dragHandle.addEventListener('mousedown', dragStart); 939 if (!_docDrag) { 940 _docDrag = drag; 941 _docDragEnd = dragEnd; 942 document.addEventListener('mousemove', _docDrag); 943 document.addEventListener('mouseup', _docDragEnd); 944 document.addEventListener('mouseleave', _docDragEnd); 945 } 946 947 // Logo upload functionality 948 const logoDropZone = document.getElementById('logoDropZone'); 949 const logoUpload = document.getElementById('logoUpload'); 950 951 if (logoDropZone && logoUpload) { 952 // Click to browse 953 logoDropZone.addEventListener('click', () => { 954 logoUpload.click(); 955 }); 956 957 // File input change 958 logoUpload.addEventListener('change', (e) => { 959 const file = e.target.files?.[0]; 960 if (file && file.type.startsWith('image/')) { 961 handleLogoFile(file); 962 } 963 }); 964 965 // Drag and drop 966 logoDropZone.addEventListener('dragover', (e) => { 967 e.preventDefault(); 968 e.stopPropagation(); 969 logoDropZone.classList.add('border-green-500', 'bg-[#2d2d2d]'); 970 }); 971 972 logoDropZone.addEventListener('dragleave', (e) => { 973 e.preventDefault(); 974 e.stopPropagation(); 975 logoDropZone.classList.remove('border-green-500', 'bg-[#2d2d2d]'); 976 }); 977 978 logoDropZone.addEventListener('drop', (e) => { 979 e.preventDefault(); 980 e.stopPropagation(); 981 logoDropZone.classList.remove('border-green-500', 'bg-[#2d2d2d]'); 982 983 const file = e.dataTransfer.files?.[0]; 984 if (file && file.type.startsWith('image/')) { 985 logoUpload.files = e.dataTransfer.files; 986 handleLogoFile(file); 987 } 988 }); 989 990 function handleLogoFile(file) { 991 const reader = new FileReader(); 992 reader.onload = (e) => {
993 // Update login window logo directly 994 const loginLogo = document.getElementById('loginLogo'); 995 if (loginLogo) { 996 loginLogo.src = e.target.result; 997 } 998 }; 999 reader.readAsDataURL(file); 1000 } 1001 } 1002 1003 // Update login window when settings change 1004 const mainColorInput = document.getElementById('mainColor'); 1005 const domainInput = document.getElementById('domain'); 1006 const companyNameInput = document.getElementById('companyName'); 1007 1008 // Update main color 1009 if (mainColorInput) { 1010 mainColorInput.addEventListener('input', (e) => { 1011 const color = e.target.value; 1012 1013 // Update sign in button 1014 const signInButton = document.getElementById('loginSignInButton'); 1015 if (signInButton) { 1016 signInButton.style.backgroundColor = color; 1017 // Remove old hover listeners if they exist 1018 const hoverColor = adjustBrightness(color, -20); 1019 signInButton.onmouseenter = () => { 1020 signInButton.style.backgroundColor = hoverColor; 1021 }; 1022 signInButton.onmouseleave = () => { 1023 signInButton.style.backgroundColor = color; 1024 }; 1025 } 1026 1027 // Update sign up link 1028 const signUpLink = document.getElementById('loginSignUpLink'); 1029 if (signUpLink) { 1030 signUpLink.style.color = color; 1031 // Remove old hover listeners if they exist 1032 const hoverLinkColor = adjustBrightness(color, 20); 1033 signUpLink.onmouseenter = () => { 1034 signUpLink.style.color = hoverLinkColor; 1035 }; 1036 signUpLink.onmouseleave = () => { 1037 signUpLink.style.color = color; 1038 }; 1039 } 1040 1041 // Update forgot password link 1042 const forgotPasswordLink = document.getElementById('loginForgotPasswordLink'); 1043 if (forgotPasswordLink) { 1044 forgotPasswordLink.style.color = color; 1045 const hoverLinkColor = adjustBrightness(color, 20); 1046 forgotPasswordLink.onmouseenter = () => { 1047 forgotPasswordLink.style.color = hoverLinkColor; 1048 }; 1049 forgotPasswordLink.onmouseleave = () => { 1050 forgotPasswordLink.style.color = color; 1051 }; 1052 } 1053 }); 1054 } 1055 1056 // Update domain 1057 if (domainInput) { 1058 domainInput.addEventListener('input', (e) => { 1059 const domain = e.target.value || 'cloud.taubyte.com'; 1060 1061 // Update address bar 1062 const loginUrl = document.getElementById('loginAddressUrl'); 1063 if (loginUrl) { 1064 loginUrl.textContent = `https://${domain}/login`; 1065 } 1066 }); 1067 } 1068 1069 // Update company name in title 1070 if (companyNameInput) { 1071 // Set initial title on load 1072 const initialCompanyName = companyNameInput.value.trim(); 1073 const loginTitle = document.getElementById('loginWindowTitle'); 1074 if (loginTitle && initialCompanyName) { 1075 loginTitle.textContent = `${initialCompanyName} Cloud Login`; 1076 } 1077 1078 companyNameInput.addEventListener('input', (e) => { 1079 const companyName = e.target.value.trim(); 1080 if (loginTitle) { 1081 if (companyName) { 1082 loginTitle.textContent = `${companyName} Cloud Login`; 1083 } else { 1084 loginTitle.textContent = 'Taubyte Cloud Login'; 1085 } 1086 } 1087 }); 1088 } 1089 1090 // Helper function to adjust color brightness 1091 function adjustBrightness(hex, percent) { 1092 const num = parseInt(hex.replace('#', ''), 16); 1093 const amt = Math.round(2.55 * percent); 1094 const R = Math.min(255, Math.max(0, (num >> 16) + amt)); 1095 const G = Math.min(255, Math.max(0, ((num >> 8) & 0x00FF) + amt)); 1096 const B = Math.min(255, Math.max(0, (num & 0x0000FF) + amt)); 1097 return '#' + (0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1); 1098 } 1099 } 1100 1101 // Initialize on DOM ready 1102 if (document.readyState === 'loading') { 1103 document.addEventListener('DOMContentLoaded', initSettingsWindow); 1104 } else { 1105 initSettingsWindow(); 1106 } 1107 1108 // Also initialize after view transitions 1109 document.addEventListener('astro:page-load', initSettingsWindow); 1110 document.addEventListener('astro:after-swap', initSettingsWindow); 1111 1112 document.addEventListener('astro:before-swap', () => { 1113 if (_docDrag) { 1114 document.removeEventListener('mousemove', _docDrag); 1115 document.removeEventListener('mouseup', _docDragEnd); 1116 document.removeEventListener('mouseleave', _docDragEnd); 1117 _docDrag = null; 1118 _docDragEnd = null; 1119 } 1120 }); 1121})();</script>
1121 </div> </div> </div> </div> </div> </div> </div> </div> <div id="feature-autonomous" class="rounded-2xl bg-black/10 dark:bg-[#111] p-6 md:p-8 mb-8"> <div class="flex flex-col lg:flex-row gap-8 items-center"> <div class="order-1 lg:order-2 shrink-0 w-full lg:w-1/3"> <div> <div> <a href="/features#autonomous" class="inline-flex items-center gap-2 text-xl md:text-2xl font-semibold leading-tight text-black dark:text-white hover:opacity-80"> Low-Ops Runtime <i class="ri-arrow-right-line mt-1" aria-hidden="true"></i> </a> <p class="mt-4 text-base md:text-lg text-black/70 dark:text-white/70 max-w-2xl">Taubyte handles routing, scaling, deployment, and recovery loops so platform teams do less glue work as software volume grows.</p> <div class="mt-4 flex flex-wrap gap-2"> <span class="inline-flex items-center rounded-full border border-black/20 dark:border-white/20 bg-black/5 dark:bg-white/5 px-3 py-1 text-xs font-medium text-black/70 dark:text-white/70"> Route </span><span class="inline-flex items-center rounded-full border border-black/20 dark:border-white/20 bg-black/5 dark:bg-white/5 px-3 py-1 text-xs font-medium text-black/70 dark:text-white/70"> Scale </span><span class="inline-flex items-center rounded-full border border-black/20 dark:border-white/20 bg-black/5 dark:bg-white/5 px-3 py-1 text-xs font-medium text-black/70 dark:text-white/70"> Recover </span> </div> </div> </div> </div> <div class="order-2 lg:order-1 grow w-full lg:w-2/3"> <div> <!-- Mobile: Static screenshot --> <div class="lg:hidden relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full"> <img src="/screenshots/autonomous-feature-desktop.png" alt="Low-Ops Runtime feature interface" class="w-full h-full object-cover" loading="lazy"> </div> <!-- Desktop: Interactive component --> <div class="hidden lg:block relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full"> <img src="/soc-background.png" alt="" class="absolute inset-0 h-full w-full object-cover pointer-events-none" loading="lazy"> <!-- Dream desktop window centered inside container --> <div class="absolute inset-0"> <div class="relative w-full h-full"> <div class="absolute inset-0"> <!-- Browser Window with Nodes Map (Floating) --><div class="absolute bottom-8 left-8 w-full max-w-lg shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg z-10" id="draggable-window-nodes-map" data-platform="nodes-map"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col" style="height: 320px;"> <!-- Browser Window Controls --> <div class="bg-[#222] border-b border-white/10 px-4 py-1 flex items-center gap-1 cursor-move drag-handle" data-platform="nodes-map"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span id="nodesMapWindowTitle" class="ml-4 text-white/60 text-xs">Console</span> </div> <!-- Map Container --> <div id="nodes-map-nodes-map" class="flex-1 w-full bg-[#1a1a1a] min-h-0"></div> <!-- Nodes List --> <div class="bg-[#1a1a1a] border-t border-white/10 px-4 py-2 relative"> <div class="flex gap-4 items-start"> <!-- Online Section --> <div class="flex-1" style="flex-grow: 2;"> <div class="text-[10px] text-white/60 mb-1.5 font-medium">Online</div> <div class="flex flex-wrap items-start gap-2 node-section" data-section="online"> <div class="flex items-center gap-2 px-3 py-1.5 rounded-md bg-[#222]/50 border border-green-500/30 hover:bg-[#222] hover:border-green-500/50 transition-colors cursor-grab shrink-0 node-item draggable-node" draggable="true" data-node-name="Dallas" data-node-lat="32.7767" data-node-lng="-96.797" data-online="true"> <div class="w-2 h-2 rounded-full bg-green-500 shrink-0"></div> <span class="text-white/80 text-[10px] font-medium whitespace-nowrap">Dallas</span> </div><div class="flex items-center gap-2 px-3 py-1.5 rounded-md bg-[#222]/50 border border-green-500/30 hover:bg-[#222] hover:border-green-500/50 transition-colors cursor-grab shrink-0 node-item draggable-node" draggable="true" data-node-name="Paris" data-node-lat="48.8566" data-node-lng="2.3522" data-online="true"> <div class="w-2 h-2 rounded-full bg-green-500 shrink-0"></div> <span class="text-white/80 text-[10px] font-medium whitespace-nowrap">Paris</span> </div><div class="flex items-center gap-2 px-3 py-1.5 rounded-md bg-[#222]/50 border border-green-500/30 hover:bg-[#222] hover:border-green-500/50 transition-colors cursor-grab shrink-0 node-item draggable-node" draggable="true" data-node-name="Moscow" data-node-lat="55.7558" data-node-lng="37.6173" data-online="true"> <div class="w-2 h-2 rounded-full bg-green-500 shrink-0"></div> <span class="text-white/80 text-[10px] font-medium whitespace-nowrap">Moscow</span> </div> </div> </div> <!-- Vertical Divider --> <div class="absolute left-[66.666%] top-0 bottom-0 w-px bg-white/10 -translate-x-1/2"></div> <!-- Offline Section --> <div class="flex-1 pl-4"> <div class="text-[10px] text-white/60 mb-1.5 font-medium">Offline</div> <div class="relative flex flex-wrap items-start gap-2 node-section" data-section="offline"> <div class="w-full flex items-center justify-center text-white/40 text-xs italic py-1 offline-placeholder"> 1122Move one of the nodes here 1123</div> </div> </div> </div> </div> </div> </div>
1123<script type="module" src="/_astro/NodesMapWindow.astro_astro_type_script_index_0_lang.ByY7CPJp.js"></script>
1123
1123<script>(function(){const id = "draggable-window-nodes-map"; 1124const platform = "nodes-map"; 1125const mapId = "nodes-map-nodes-map"; 1126const nodes = [{"name":"Dallas","lat":32.7767,"lng":-96.797,"online":true},{"name":"Paris","lat":48.8566,"lng":2.3522,"online":true},{"name":"Moscow","lat":55.7558,"lng":37.6173,"online":true}]; 1127 1128 let _docDrag = null; 1129 let _docDragEnd = null; 1130 1131 function initNodesMapWindow() { 1132 const windowEl = document.getElementById(id); 1133 const mapContainer = document.getElementById(mapId); 1134 1135 if (!windowEl || !mapContainer) { 1136 // Retry after a short delay if element isn't ready (for view transitions) 1137 setTimeout(initNodesMapWindow, 50); 1138 return; 1139 } 1140 1141 // Check if map actually exists (for view transitions) 1142 const mapExists = mapContainer._leafletMap && typeof mapContainer._leafletMap.remove === 'function'; 1143 1144 // Initialize Leaflet map if it doesn't exist 1145 if (!mapExists) { 1146 // Wait for Leaflet to be available 1147 if (window.Leaflet) { 1148 // Clean up any existing map instance if it exists 1149 if (mapContainer._leafletMap) { 1150 mapContainer._leafletMap.remove(); 1151 mapContainer._leafletMap = null; 1152 mapContainer._leafletMarkers = null; 1153 mapContainer._leafletMarkerMap = null; 1154 } 1155 1156 const L = window.Leaflet; 1157 1158 // Create map centered on world view 1159 const map = L.map(mapId, { 1160 center: [20, 0], 1161 zoom: 2, 1162 zoomControl: true, 1163 attributionControl: false 1164 }); 1165 1166 // Add dark mode tile layer (CartoDB Dark Matter) 1167 L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { 1168 maxZoom: 19, 1169 attribution: '© OpenStreetMap contributors, © CARTO', 1170 subdomains: 'abcd' 1171 }).addTo(map); 1172 1173 // Create custom icons for nodes 1174 const createNodeIcon = (isOnline) => { 1175 const color = isOnline ? '#22c55e' : '#6b7280'; 1176 const shadowColor = isOnline ? 'rgba(34, 197, 94, 0.3)' : 'rgba(107, 114, 128, 0.2)'; 1177 return L.divIcon({ 1178 className: 'custom-node-marker', 1179 html: `<div style="width: 12px; height: 12px; background-color: ${color}; border-radius: 50%; border: 2px solid #1a1a1a; box-shadow: 0 0 0 2px ${shadowColor};"></div>`, 1180 iconSize: [12, 12], 1181 iconAnchor: [6, 6] 1182 }); 1183 }; 1184 1185 // Add markers for each node and collect them with node data 1186 const markers = []; 1187 const markerMap = {}; // Map node names to markers 1188 nodes.forEach(node => { 1189 const icon = createNodeIcon(node.online); 1190 const marker = L.marker([node.lat, node.lng], { icon: icon }) 1191 .addTo(map) 1192 .bindPopup(node.name + (node.online ? ' (Online)' : ' (Offline)')); 1193 marker._nodeData = node; 1194 markers.push(marker); 1195 markerMap[node.name] = marker; 1196 }); 1197 1198 // Store marker map for updates 1199 mapContainer._leafletMarkerMap = markerMap; 1200 1201 // Fit map to show all nodes with padding 1202 if (markers.length > 0) { 1203 const group = new L.featureGroup(markers); 1204 map.fitBounds(group.getBounds().pad(0.1), { 1205 maxZoom: 8, 1206 padding: [30, 30] 1207 }); 1208 } 1209 1210 // Store map instance for use in click handlers 1211 mapContainer._leafletMap = map; 1212 mapContainer._leafletMarkers = markers; 1213 mapContainer.dataset.mapInitialized = 'true'; 1214 1215 // Invalidate map size after a short delay to ensure proper rendering 1216 setTimeout(() => { 1217 map.invalidateSize(); 1218 // Re-fit bounds after size is validated 1219 if (markers.length > 0) { 1220 const group = new L.featureGroup(markers); 1221 map.fitBounds(group.getBounds().pad(0.1), { 1222 maxZoom: 8, 1223 padding: [30, 30] 1224 }); 1225 } 1226 }, 100); 1227 } else { 1228 // Leaflet not ready yet, retry map initialization later 1229 setTimeout(() => { 1230 if (!mapContainer._leafletMap) { 1231 initNodesMapWindow(); 1232 } 1233 }, 100); 1234 // Continue to initialize dragging even if map isn't ready 1235 } 1236 } 1237 1238 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 1239 const container = windowEl?.parentElement; 1240 1241 if (!dragHandle || !container) { 1242 // Elements might not be ready yet, retry 1243 setTimeout(initNodesMapWindow, 50); 1244 return; 1245 } 1246 1247 // Skip dragging initialization if already done (for view transitions, allow reinitialization) 1248 // Check if event listeners are already attached by checking if dragHandle has a data attribute 1249 if (windowEl.dataset.draggingInitialized === 'true' && mapExists) { 1250 return; 1251 } 1252 windowEl.dataset.draggingInitialized = 'true'; 1253 1254 let isDragging = false;
1255 let initialX = 0; 1256 let initialY = 0; 1257 1258 function dragStart(e) { 1259 if (e.button !== 0) return; 1260 1261 isDragging = true; 1262 1263 const rect = windowEl.getBoundingClientRect(); 1264 const containerRect = container.getBoundingClientRect(); 1265 1266 // Calculate where we clicked relative to the window 1267 initialX = e.clientX - rect.left; 1268 initialY = e.clientY - rect.top; 1269 1270 // Remove positioning classes that might conflict 1271 windowEl.classList.remove('-translate-x-1/2', '-translate-y-1/2', 'bottom-8', 'left-8'); 1272 windowEl.classList.add('translate-x-0', 'translate-y-0'); 1273 1274 // Get current position 1275 const currentLeft = rect.left - containerRect.left; 1276 const currentTop = rect.top - containerRect.top; 1277 1278 // Set initial positions using explicit pixel values 1279 windowEl.style.left = currentLeft + 'px'; 1280 windowEl.style.top = currentTop + 'px'; 1281 windowEl.style.bottom = 'auto'; 1282 windowEl.style.right = 'auto'; 1283 1284 windowEl.classList.add('user-select-none'); 1285 document.body.style.cursor = 'grabbing'; 1286 } 1287 1288 function drag(e) { 1289 if (!isDragging) return; 1290 1291 e.preventDefault(); 1292 1293 const containerRect = container.getBoundingClientRect(); 1294 const windowRect = windowEl.getBoundingClientRect(); 1295 1296 // Calculate new position 1297 const newX = e.clientX - containerRect.left - initialX; 1298 const newY = e.clientY - containerRect.top - initialY; 1299 1300 // Keep window within container bounds 1301 const maxX = containerRect.width - windowRect.width; 1302 const maxY = containerRect.height - windowRect.height; 1303 1304 const boundedX = Math.max(0, Math.min(newX, maxX)); 1305 const boundedY = Math.max(0, Math.min(newY, maxY)); 1306 1307 windowEl.style.left = boundedX + 'px'; 1308 windowEl.style.top = boundedY + 'px'; 1309 } 1310 1311 function dragEnd() { 1312 if (!isDragging) return; 1313 1314 isDragging = false; 1315 windowEl.classList.remove('user-select-none'); 1316 document.body.style.cursor = ''; 1317 } 1318 1319 dragHandle.addEventListener('mousedown', dragStart); 1320 if (!_docDrag) { 1321 _docDrag = drag; 1322 _docDragEnd = dragEnd; 1323 document.addEventListener('mousemove', _docDrag); 1324 document.addEventListener('mouseup', _docDragEnd); 1325 document.addEventListener('mouseleave', _docDragEnd); 1326 } 1327 1328 // Drag and drop functionality for nodes 1329 let draggedElement = null; 1330 1331 // Make nodes draggable 1332 const draggableNodes = windowEl.querySelectorAll('.draggable-node'); 1333 draggableNodes.forEach(node => { 1334 node.addEventListener('dragstart', function(e) { 1335 draggedElement = this; 1336 this.style.opacity = '0.5'; 1337 this.style.cursor = 'grabbing'; 1338 document.body.style.cursor = 'grabbing'; 1339 e.dataTransfer.effectAllowed = 'move'; 1340 e.dataTransfer.setData('text/html', this.innerHTML); 1341 }); 1342 1343 node.addEventListener('dragend', function() { 1344 this.style.opacity = '1'; 1345 this.style.cursor = ''; 1346 document.body.style.cursor = ''; 1347 }); 1348 1349 // Click to zoom to node 1350 node.addEventListener('click', function() { 1351 const lat = parseFloat(this.dataset.nodeLat); 1352 const lng = parseFloat(this.dataset.nodeLng); 1353 1354 if (mapContainer._leafletMap && !isNaN(lat) && !isNaN(lng)) { 1355 const map = mapContainer._leafletMap;
1356 map.setView([lat, lng], 6, { 1357 animate: true, 1358 duration: 0.5 1359 }); 1360 1361 const markerMap = mapContainer._leafletMarkerMap; 1362 if (markerMap && markerMap[this.dataset.nodeName]) { 1363 markerMap[this.dataset.nodeName].openPopup(); 1364 } 1365 } 1366 }); 1367 }); 1368 1369 // Drop zones 1370 const dropZones = windowEl.querySelectorAll('.node-section'); 1371 dropZones.forEach(zone => { 1372 zone.addEventListener('dragover', function(e) { 1373 e.preventDefault(); 1374 const targetSection = this.dataset.section; 1375 const isOnline = targetSection === 'online'; 1376 1377 // Check if offline section already has a node 1378 if (!isOnline) { 1379 const existingOfflineNodes = this.querySelectorAll('.draggable-node'); 1380 if (existingOfflineNodes.length > 0 && draggedElement && draggedElement.dataset.online !== 'false') { 1381 e.dataTransfer.dropEffect = 'none'; 1382 this.style.backgroundColor = 'rgba(239, 68, 68, 0.1)'; 1383 return; 1384 } 1385 } 1386 1387 e.dataTransfer.dropEffect = 'move'; 1388 this.style.backgroundColor = 'rgba(34, 197, 94, 0.1)'; 1389 }); 1390 1391 zone.addEventListener('dragleave', function() { 1392 this.style.backgroundColor = ''; 1393 }); 1394 1395 zone.addEventListener('drop', function(e) { 1396 e.preventDefault(); 1397 this.style.backgroundColor = ''; 1398 1399 if (draggedElement) { 1400 const targetSection = this.dataset.section; 1401 const isOnline = targetSection === 'online'; 1402 const nodeName = draggedElement.dataset.nodeName; 1403 1404 // Prevent moving to offline if it already has a node (unless moving from offline) 1405 if (!isOnline) { 1406 const existingOfflineNodes = this.querySelectorAll('.draggable-node'); 1407 if (existingOfflineNodes.length > 0 && draggedElement.dataset.online !== 'false') { 1408 draggedElement.style.opacity = '1'; 1409 draggedElement = null; 1410 return; 1411 } 1412 } 1413 1414 // Remove placeholder message if it exists 1415 const placeholder = this.querySelector('.offline-placeholder'); 1416 if (placeholder) { 1417 placeholder.remove(); 1418 } 1419 1420 // Move the node element 1421 this.appendChild(draggedElement); 1422 1423 // Update node data 1424 draggedElement.dataset.online = isOnline; 1425 1426 // Update visual appearance 1427 const indicator = draggedElement.querySelector('div[class*="rounded-full"]'); 1428 const label = draggedElement.querySelector('span'); 1429 1430 if (isOnline) { 1431 indicator.className = 'w-2 h-2 rounded-full bg-green-500 shrink-0'; 1432 indicator.style.backgroundColor = '#22c55e'; 1433 // Update border classes 1434 draggedElement.classList.remove('border-white/5', 'hover:border-white/10'); 1435 draggedElement.classList.add('border-green-500/30', 'hover:border-green-500/50'); 1436 label.className = 'text-white/80 text-[10px] font-medium whitespace-nowrap'; 1437 } else { 1438 indicator.className = 'w-2 h-2 rounded-full bg-gray-500 shrink-0'; 1439 indicator.style.backgroundColor = '#6b7280'; 1440 // Update border classes 1441 draggedElement.classList.remove('border-green-500/30', 'hover:border-green-500/50'); 1442 draggedElement.classList.add('border-white/5', 'hover:border-white/10'); 1443 label.className = 'text-white/60 text-[10px] font-medium whitespace-nowrap'; 1444 } 1445 1446 // Update marker on map 1447 const markerMap = mapContainer._leafletMarkerMap; 1448 if (markerMap && markerMap[nodeName]) { 1449 const marker = markerMap[nodeName]; 1450 const L = window.Leaflet; 1451 const newIcon = L.divIcon({ 1452 className: 'custom-node-marker', 1453 html: `<div style="width: 12px; height: 12px; background-color: ${isOnline ? '#22c55e' : '#6b7280'}; border-radius: 50%; border: 2px solid #1a1a1a; box-shadow: 0 0 0 2px ${isOnline ? 'rgba(34, 197, 94, 0.3)' : 'rgba(107, 114, 128, 0.2)'};"></div>`, 1454 iconSize: [12, 12], 1455 iconAnchor: [6, 6] 1456 }); 1457 marker.setIcon(newIcon); 1458 marker.setPopupContent(nodeName + (isOnline ? ' (Online)' : ' (Offline)')); 1459 marker._nodeData.online = isOnline; 1460 } 1461 1462 // Add placeholder back to offline section if it becomes empty 1463 if (isOnline) { 1464 const offlineSection = windowEl.querySelector('[data-section="offline"]'); 1465 const offlineNodes = offlineSection?.querySelectorAll('.draggable-node'); 1466 if (offlineSection && (!offlineNodes || offlineNodes.length === 0)) { 1467 const placeholder = document.createElement('div'); 1468 placeholder.className = 'w-full flex items-center justify-center text-white/40 text-xs italic py-1 offline-placeholder'; 1469 placeholder.textContent = 'Move one of the nodes here'; 1470 offlineSection.appendChild(placeholder); 1471 } 1472 } 1473 1474 // Dispatch event with updated node states 1475 const allNodes = windowEl.querySelectorAll('.draggable-node'); 1476 const nodeStates = Array.from(allNodes).map(node => ({ 1477 name: node.dataset.nodeName, 1478 online: node.dataset.online === 'true' 1479 })); 1480 1481 const event = new CustomEvent('nodes-state-changed', { 1482 detail: { nodes: nodeStates, platform: platform } 1483 }); 1484 document.dispatchEvent(event); 1485 1486 draggedElement = null; 1487 } 1488 }); 1489 }); 1490 } 1491 1492 // Initialize on DOM ready 1493 if (document.readyState === 'loading') { 1494 document.addEventListener('DOMContentLoaded', initNodesMapWindow); 1495 } else { 1496 initNodesMapWindow(); 1497 } 1498 1499 // Also initialize after view transitions 1500 document.addEventListener('astro:page-load', initNodesMapWindow); 1501 document.addEventListener('astro:after-swap', initNodesMapWindow); 1502 1503 document.addEventListener('astro:before-swap', () => { 1504 if (_docDrag) { 1505 document.removeEventListener('mousemove', _docDrag); 1506 document.removeEventListener('mouseup', _docDragEnd); 1507 document.removeEventListener('mouseleave', _docDragEnd); 1508 _docDrag = null; 1509 _docDragEnd = null; 1510 } 1511 }); 1512})();</script>
1512 <!-- Terminal Window (Floating) --><div class="absolute top-8 right-8 w-full max-w-sm shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg z-30" id="terminal-window-autonomous-terminal" data-platform="autonomous-terminal"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col" style> <!-- Browser Window Controls --> <div class="bg-[#222] border-b border-white/10 px-4 py-1 flex items-center gap-1 cursor-move drag-handle" data-platform="autonomous-terminal"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="ml-4 text-white/60 text-xs">Terminal</span> </div> <!-- Terminal Content --> <div class="w-full bg-[#1a1a1a] p-4 font-mono text-xs text-white/80" style> <div id="terminal-content-nodes-map" class="space-y-1"> <div class="text-white/80">$ nslookup your.app.domain</div> <div class="text-white/80" id="addresses-label">Addresses:</div> <div class="text-white/80 node-address" data-node="Dallas"> 45.76.142.123 dallas.your.cloud</div> <div class="text-white/80 node-address" data-node="Paris"> 185.199.108.42 paris.your.cloud</div> <div class="text-white/80 node-address" data-node="Moscow"> 178.154.213.87 moscow.your.cloud</div> <div class="text-white/80"></div> <div class="text-white/80">$</div> </div> </div> </div> </div>
1512<script>(function(){const id = "terminal-window-autonomous-terminal"; 1513const platform = "autonomous-terminal"; 1514 1515 let _docDrag = null; 1516 let _docDragEnd = null; 1517 1518 function initTerminalWindow() { 1519 const windowEl = document.getElementById(id); 1520 1521 if (!windowEl) { 1522 // Retry after a short delay if element isn't ready (for view transitions) 1523 setTimeout(initTerminalWindow, 50); 1524 return; 1525 } 1526 1527 // Skip if already initialized 1528 if (windowEl.dataset.terminalInitialized === 'true') { 1529 return; 1530 } 1531 windowEl.dataset.terminalInitialized = 'true'; 1532 1533 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 1534 const container = windowEl?.parentElement; 1535 1536 if (!dragHandle || !container) { 1537 // Elements might not be ready yet, retry 1538 windowEl.dataset.terminalInitialized = 'false'; 1539 setTimeout(initTerminalWindow, 50); 1540 return; 1541 } 1542 1543 let isDragging = false; 1544 let initialX = 0; 1545 let initialY = 0; 1546 1547 function dragStart(e) { 1548 if (e.button !== 0) return; 1549 1550 isDragging = true; 1551 1552 const rect = windowEl.getBoundingClientRect(); 1553 const containerRect = container.getBoundingClientRect(); 1554 1555 // Calculate where we clicked relative to the window 1556 initialX = e.clientX - rect.left; 1557 initialY = e.clientY - rect.top; 1558 1559 // Remove positioning classes that might conflict 1560 windowEl.classList.remove('-translate-x-1/2', '-translate-y-1/2', 'top-8', 'right-8', 'bottom-8', 'left-8'); 1561 windowEl.classList.add('translate-x-0', 'translate-y-0'); 1562 1563 // Get current position 1564 const currentLeft = rect.left - containerRect.left; 1565 const currentTop = rect.top - containerRect.top; 1566 1567 // Set initial positions using explicit pixel values 1568 windowEl.style.left = currentLeft + 'px'; 1569 windowEl.style.top = currentTop + 'px'; 1570 windowEl.style.bottom = 'auto'; 1571 windowEl.style.right = 'auto'; 1572 1573 windowEl.classList.add('user-select-none'); 1574 document.body.style.cursor = 'grabbing'; 1575 } 1576 1577 function drag(e) { 1578 if (!isDragging) return; 1579 1580 e.preventDefault(); 1581 1582 const containerRect = container.getBoundingClientRect(); 1583 const windowRect = windowEl.getBoundingClientRect(); 1584 1585 // Calculate new position 1586 const newX = e.clientX - containerRect.left - initialX; 1587 const newY = e.clientY - containerRect.top - initialY; 1588 1589 // Keep window within container bounds 1590 const maxX = containerRect.width - windowRect.width; 1591 const maxY = containerRect.height - windowRect.height; 1592 1593 const boundedX = Math.max(0, Math.min(newX, maxX)); 1594 const boundedY = Math.max(0, Math.min(newY, maxY)); 1595 1596 windowEl.style.left = boundedX + 'px'; 1597 windowEl.style.top = boundedY + 'px'; 1598 } 1599 1600 function dragEnd() { 1601 if (!isDragging) return; 1602 1603 isDragging = false; 1604 windowEl.classList.remove('user-select-none'); 1605 document.body.style.cursor = ''; 1606 } 1607 1608 dragHandle.addEventListener('mousedown', dragStart); 1609 if (!_docDrag) { 1610 _docDrag = drag; 1611 _docDragEnd = dragEnd; 1612 document.addEventListener('mousemove', _docDrag); 1613 document.addEventListener('mouseup', _docDragEnd); 1614 document.addEventListener('mouseleave', _docDragEnd); 1615 } 1616 } 1617 1618 // Initialize on DOM ready 1619 if (document.readyState === 'loading') { 1620 document.addEventListener('DOMContentLoaded', initTerminalWindow); 1621 } else { 1622 initTerminalWindow(); 1623 } 1624 1625 // Also initialize after view transitions 1626 document.addEventListener('astro:page-load', initTerminalWindow); 1627 document.addEventListener('astro:after-swap', initTerminalWindow); 1628 1629 document.addEventListener('astro:before-swap', () => { 1630 if (_docDrag) { 1631 document.removeEventListener('mousemove', _docDrag); 1632 document.removeEventListener('mouseup', _docDragEnd); 1633 document.removeEventListener('mouseleave', _docDragEnd); 1634 _docDrag = null; 1635 _docDragEnd = null; 1636 } 1637 }); 1638})();</script>
1638 </div> </div> </div> </div> </div> </div> </div> </div>
1638<script type="module">const m={Dallas:"45.76.142.123",Paris:"185.199.108.42",Moscow:"178.154.213.87"};function l(o){const n=document.getElementById("terminal-content-nodes-map");if(!n)return;const t=document.getElementById("addresses-label");if(!t)return;n.querySelectorAll(".node-address").forEach(e=>e.remove());const r=o.filter(e=>e.online);if(r.forEach(e=>{const c=e.name.toLowerCase(),i=m[e.name];if(i){const d=document.createElement("div");d.className="text-white/80 node-address",d.dataset.node=e.name,d.textContent=` ${i} ${c}.your.cloud`,t.after(d)}}),r.length===0){const e=document.createElement("div");e.className="text-white/80 node-address italic",e.textContent=" (no online nodes)",t.after(e)}}function a(){document.addEventListener("nodes-state-changed",n=>{const t=n;t.detail.platform==="nodes-map"&&l(t.detail.nodes)});const o=document.getElementById("draggable-window-nodes-map");if(o){const n=o.querySelectorAll(".draggable-node"),t=Array.from(n).map(s=>({name:s.dataset.nodeName||"",online:s.dataset.online==="true"}));t.length>0&&l(t)}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",a):a();document.addEventListener("astro:page-load",a);document.addEventListener("astro:after-swap",a);</script>
1638 <div id="feature-git-native" class="rounded-2xl bg-black/10 dark:bg-[#111] p-6 md:p-8 mb-8"> <div class="flex flex-col lg:flex-row gap-8 items-center"> <div class="order-1 lg:order-1 shrink-0 w-full lg:w-1/3"> <div> <div> <a href="/features#git-native" class="inline-flex items-center gap-2 text-xl md:text-2xl font-semibold leading-tight text-black dark:text-white hover:opacity-80"> Git-Native Delivery <i class="ri-arrow-right-line mt-1" aria-hidden="true"></i> </a> <p class="mt-4 text-base md:text-lg text-black/70 dark:text-white/70 max-w-2xl">Git is the control plane. Code, config, routing, and deployment history stay tracked, traceable, and versioned.</p> </div> </div> </div> <div class="order-2 lg:order-2 grow w-full lg:w-2/3"> <div data-astro-cid-acpfs63d><!-- Mobile: Static screenshot --><div class="lg:hidden relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full" data-astro-cid-acpfs63d><img src="/screenshots/gitnative-feature-desktop.png" alt="Git-Native Delivery feature interface" class="w-full h-full object-cover" loading="lazy" data-astro-cid-acpfs63d></div><!-- Desktop: Interactive component --><div class="hidden lg:block relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full" data-astro-cid-acpfs63d><img src="/git-native-background.png" alt="" class="absolute inset-0 h-full w-full object-cover pointer-events-none" loading="lazy" data-astro-cid-acpfs63d><!-- Dream desktop window centered inside container --><div class="absolute inset-0" data-astro-cid-acpfs63d><div class="relative w-full h-full" data-astro-cid-acpfs63d><div class="absolute inset-0" data-astro-cid-acpfs63d><!-- Web Application --><!-- Generic Browser Window (Floating) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-sm shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg !top-[8%] !left-[8%] !translate-x-0 !translate-y-0" id="draggable-window-fullstack-chat" data-platform="fullstack-chat" data-astro-cid-o7mjncmp> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col" style="height: 378px;" data-astro-cid-o7mjncmp> <!-- Browser Window Controls --> <div class="bg-[#222] border-b border-white/10 px-4 py-1 flex items-center gap-1 cursor-move drag-handle" data-platform="fullstack-chat" data-astro-cid-o7mjncmp> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-o7mjncmp></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-o7mjncmp></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-o7mjncmp></span> <span class="ml-4 text-white/60 text-xs" data-astro-cid-o7mjncmp>Web Application</span> </div> <!-- Browser Address Bar --> <div class="bg-[#2d2d2d] px-4 py-1 border-b border-white/10" data-astro-cid-o7mjncmp> <div class="flex items-center gap-2" data-astro-cid-o7mjncmp> <!-- Navigation Buttons --> <div class="flex items-center gap-1" data-astro-cid-o7mjncmp> <button class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded" data-astro-cid-o7mjncmp> <i class="ri-arrow-left-s-line text-xs" data-astro-cid-o7mjncmp></i> </button> <button class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded" data-astro-cid-o7mjncmp> <i class="ri-arrow-right-s-line text-xs" data-astro-cid-o7mjncmp></i> </button> <button id="refresh-btn-fullstack-chat" class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded" data-astro-cid-o7mjncmp> <i class="ri-refresh-line text-xs" data-astro-cid-o7mjncmp></i> </button> </div> <!-- Address Bar --> <div class="flex-1 bg-[#1a1a1a] border border-white/10 rounded px-2 py-1 flex items-center gap-1.5" data-astro-cid-o7mjncmp> <i class="ri-lock-line text-green-500 text-[10px]" data-astro-cid-o7mjncmp></i> <span class="text-white/70 text-[10px]" data-astro-cid-o7mjncmp>https://example.com</span> </div> <!-- Browser Menu --> <div class="flex items-center gap-2" data-astro-cid-o7mjncmp> <button class="w-5 h-5 flex items-center justify-center text-white/40 hover:text-white/70 hover:bg-white/10 rounded" data-astro-cid-o7mjncmp> <i class="ri-more-2-line text-xs" data-astro-cid-o7mjncmp></i> </button> </div> </div> </div> <!-- Browser Content --> <div class="flex-1 bg-[#1a1a1a] overflow-auto" data-astro-cid-o7mjncmp> <div class="h-full flex flex-col bg-[#0b0b0b] overflow-y-auto scrollbar-sleek" data-astro-cid-acpfs63d><!-- Header --><header class="bg-[#1a1a1a] border-b border-white/10 px-4 py-3" data-astro-cid-acpfs63d><div class="flex items-center justify-between" data-astro-cid-acpfs63d><div class="flex items-center gap-3" data-astro-cid-acpfs63d><div class="w-8 h-8 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-4 w-24 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div></div><div class="flex items-center gap-2" data-astro-cid-acpfs63d><div class="w-6 h-6 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="w-6 h-6 bg-white/10 rounded-full animate-pulse" data-astro-cid-acpfs63d></div></div></div></header><!-- Hero Section --><div class="bg-gradient-to-br from-blue-500 to-purple-600 px-4 py-8" data-astro-cid-acpfs63d><h1 id="hero-title-gitnative" class="text-white text-xl font-bold text-center mb-4" data-pending-title="" data-astro-cid-acpfs63d>Git Native Workflow</h1><div class="flex gap-2 justify-center" data-astro-cid-acpfs63d><div class="h-8 w-20 bg-white/20 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-8 w-20 bg-white/20 rounded animate-pulse" data-astro-cid-acpfs63d></div></div></div><!-- Content Section --><div class="flex-1 px-4 py-4 space-y-4" data-astro-cid-acpfs63d><!-- Skeleton Card 1 --><div class="bg-[#1a1a1a] rounded-lg border border-white/10 p-4" data-astro-cid-acpfs63d><div class="flex items-start gap-3" data-astro-cid-acpfs63d><div class="w-12 h-12 bg-white/10 rounded-lg animate-pulse shrink-0" data-astro-cid-acpfs63d></div><div class="flex-1 space-y-2" data-astro-cid-acpfs63d><div class="h-4 w-3/4 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-3 w-full bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-3 w-5/6 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div></div></div></div><!-- Skeleton Card 2 --><div class="bg-[#1a1a1a] rounded-lg border border-white/10 p-4" data-astro-cid-acpfs63d><div class="flex items-start gap-3" data-astro-cid-acpfs63d><div class="w-12 h-12 bg-white/10 rounded-lg animate-pulse shrink-0" data-astro-cid-acpfs63d></div><div class="flex-1 space-y-2" data-astro-cid-acpfs63d><div class="h-4 w-2/3 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-3 w-full bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-3 w-4/5 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div></div></div></div><!-- Skeleton Card 3 --><div class="bg-[#1a1a1a] rounded-lg border border-white/10 p-4" data-astro-cid-acpfs63d><div class="space-y-3" data-astro-cid-acpfs63d><div class="h-4 w-1/2 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="grid grid-cols-3 gap-2" data-astro-cid-acpfs63d><div class="h-16 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-16 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div><div class="h-16 bg-white/10 rounded animate-pulse" data-astro-cid-acpfs63d></div></div></div></div></div></div> </div> </div> </div>
1638<script>(function(){const id = "draggable-window-fullstack-chat"; 1639const platform = "fullstack-chat"; 1640 1641 // Module-scope refs so astro:before-swap can detach the document listeners 1642 // installed by initBrowserWindow without leaking on every navigation. 1643 let _docDrag = null; 1644 let _docDragEnd = null; 1645 1646 function initBrowserWindow() { 1647 const window = document.getElementById(id); 1648 if (!window) { 1649 // Retry after a short delay if element isn't ready (for view transitions) 1650 setTimeout(initBrowserWindow, 50); 1651 return; 1652 } 1653 1654 // Skip if already initialized 1655 if (window.dataset.browserInitialized === 'true') { 1656 return; 1657 } 1658 window.dataset.browserInitialized = 'true'; 1659 1660 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 1661 const container = window?.parentElement; 1662 1663 if (!dragHandle || !container) { 1664 // Elements might not be ready yet, retry 1665 window.dataset.browserInitialized = 'false'; 1666 setTimeout(initBrowserWindow, 50); 1667 return; 1668 } 1669 1670 let isDragging = false; 1671 let initialX = 0; 1672 let initialY = 0; 1673 1674 function dragStart(e) { 1675 if (e.button !== 0) return; 1676 1677 isDragging = true; 1678 1679 const rect = window.getBoundingClientRect(); 1680 const containerRect = container.getBoundingClientRect(); 1681 1682 // Calculate where we clicked relative to the window 1683 initialX = e.clientX - rect.left; 1684 initialY = e.clientY - rect.top; 1685 1686 // Remove transform classes and set initial position 1687 window.classList.remove('-translate-x-1/2', '-translate-y-1/2', 'translate-x-0', 'translate-y-0'); 1688 1689 // Get current position 1690 const currentLeft = rect.left - containerRect.left; 1691 const currentTop = rect.top - containerRect.top; 1692 1693 // Set initial positions using inline styles with !important to override !important classes 1694 window.style.setProperty('left', currentLeft + 'px', 'important'); 1695 window.style.setProperty('top', currentTop + 'px', 'important'); 1696 window.style.setProperty('right', 'auto', 'important'); 1697 window.style.setProperty('bottom', 'auto', 'important'); 1698 1699 window.classList.add('user-select-none'); 1700 document.body.style.cursor = 'grabbing'; 1701 } 1702 1703 function drag(e) { 1704 if (!isDragging) return; 1705 1706 e.preventDefault(); 1707 1708 const containerRect = container.getBoundingClientRect(); 1709 const windowRect = window.getBoundingClientRect(); 1710 1711 // Calculate new position 1712 const newX = e.clientX - containerRect.left - initialX; 1713 const newY = e.clientY - containerRect.top - initialY; 1714 1715 // Keep window within container bounds 1716 const maxX = containerRect.width - windowRect.width; 1717 const maxY = containerRect.height - windowRect.height; 1718 1719 const boundedX = Math.max(0, Math.min(newX, maxX)); 1720 const boundedY = Math.max(0, Math.min(newY, maxY)); 1721 1722 window.style.setProperty('left', boundedX + 'px', 'important'); 1723 window.style.setProperty('top', boundedY + 'px', 'important'); 1724 } 1725 1726 function dragEnd() { 1727 if (!isDragging) return; 1728 1729 isDragging = false; 1730 window.classList.remove('user-select-none'); 1731 document.body.style.cursor = ''; 1732 } 1733 1734 dragHandle.addEventListener('mousedown', dragStart); 1735 if (!_docDrag) { 1736 _docDrag = drag; 1737 _docDragEnd = dragEnd; 1738 document.addEventListener('mousemove', _docDrag); 1739 document.addEventListener('mouseup', _docDragEnd); 1740 document.addEventListener('mouseleave', _docDragEnd); 1741 } 1742 } 1743 1744 // Initialize on DOM ready 1745 if (document.readyState === 'loading') { 1746 document.addEventListener('DOMContentLoaded', initBrowserWindow); 1747 } else { 1748 initBrowserWindow(); 1749 } 1750 1751 // Also initialize after view transitions 1752 document.addEventListener('astro:page-load', initBrowserWindow); 1753 document.addEventListener('astro:after-swap', initBrowserWindow); 1754 1755 // Detach document-level drag listeners before the page swaps so they don't 1756 // accumulate across navigations. 1757 document.addEventListener('astro:before-swap', () => { 1758 if (_docDrag) { 1759 document.removeEventListener('mousemove', _docDrag); 1760 document.removeEventListener('mouseup', _docDragEnd); 1761 document.removeEventListener('mouseleave', _docDragEnd); 1762 _docDrag = null; 1763 _docDragEnd = null; 1764 } 1765 }); 1766})();</script>
1766 <!-- Git Interface (GitKraken/VSCode Style) --><!-- Git Interface Window (Simple, No Nav) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-sm shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg top-[60%] left-[70%] -translate-x-1/2 -translate-y-1/2" id="git-window-git-native" data-platform="git-native" data-astro-cid-3a3eqpua> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col" style="height: 300px;" data-astro-cid-3a3eqpua> <!-- Title Bar --> <div class="bg-[#222] border-b border-white/10 px-4 py-1 flex items-center gap-1 cursor-move drag-handle" data-platform="git-native" data-astro-cid-3a3eqpua> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-3a3eqpua></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-3a3eqpua></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20" data-astro-cid-3a3eqpua></span> <span class="ml-4 text-white/60 text-xs" data-astro-cid-3a3eqpua>Source Control</span> </div> <!-- Commit Section --> <div class="px-3 py-2 border-b border-white/10 bg-[#161616]" data-astro-cid-3a3eqpua> <div class="flex items-center gap-2" data-astro-cid-3a3eqpua> <div class="flex-1 flex items-center gap-0 bg-[#1a1a1a] border border-white/10 rounded overflow-hidden" data-astro-cid-3a3eqpua> <span class="pl-2 pr-0.5 py-1.5 text-white text-[10px] shrink-0" data-astro-cid-3a3eqpua>Changed title to</span> <input id="title-input-git-window-git-native" type="text" placeholder="put your title here" value="" class="flex-1 bg-transparent border-0 pl-0.5 pr-1 py-1.5 text-white placeholder:text-white/30 text-[10px] focus:outline-none focus:ring-0 resize-none" data-astro-cid-3a3eqpua> </div> <button class="px-2 py-1 bg-white/5 hover:bg-white/10 text-white/70 hover:text-white text-[9px] rounded shrink-0" data-astro-cid-3a3eqpua> 1767Cancel 1768</button> <button id="commit-btn-git-window-git-native" class="px-2 py-1 bg-green-600 hover:bg-green-700 text-white text-[9px] rounded flex items-center gap-1 shrink-0" data-astro-cid-3a3eqpua> <i class="ri-check-line text-xs" data-astro-cid-3a3eqpua></i> 1769Commit 1770</button> </div> </div> <!-- Main Content - Everything in one view --> <div class="flex-1 overflow-y-auto scrollbar-sleek flex flex-col" data-astro-cid-3a3eqpua> <!-- Changes Section --> <div data-astro-cid-3a3eqpua> <!-- Modified Files Section --> <div class="px-2 pt-2 pb-1" data-astro-cid-3a3eqpua> <div class="text-white/50 text-[9px] uppercase tracking-wider mb-1 px-2" data-astro-cid-3a3eqpua>Changes</div> <!-- Modified File --> <div class="flex items-center gap-2 px-2 py-1.5 hover:bg-white/5 rounded cursor-pointer group" data-astro-cid-3a3eqpua> <i class="ri-file-edit-line text-orange-400 text-xs" data-astro-cid-3a3eqpua></i> <span class="text-white text-[10px] flex-1" data-astro-cid-3a3eqpua>src/component/title.tsx</span> <span class="text-white/40 text-[9px] group-hover:text-white/60" data-astro-cid-3a3eqpua>M</span> </div> </div> </div> <!-- History Section --> <div class="border-t border-white/5 mt-2" data-astro-cid-3a3eqpua> <div class="px-3 py-2" data-astro-cid-3a3eqpua> <div id="git-tree-container-git-window-git-native" data-astro-cid-3a3eqpua> <div class="relative " style="min-height: 112px;"> <!-- SVG for lines and circles --> <svg class="absolute inset-0 w-full h-full pointer-events-none z-0" style="overflow: visible;"><line x1="11" y1="0" x2="11" y2="112" stroke="#60a5fa" stroke-width="2" /><line x1="23" y1="42" x2="23" y2="112" stroke="#a855f7" stroke-width="2" /><line x1="11" y1="42" x2="23" y2="42" stroke="#a855f7" stroke-width="2" /><circle cx="11" cy="14" r="5" fill="#60a5fa" stroke="#1a1a1a" stroke-width="2" /><circle cx="11" cy="42" r="5" fill="#60a5fa" stroke="#1a1a1a" stroke-width="2" /><circle cx="23" cy="70" r="5" fill="#a855f7" stroke="#1a1a1a" stroke-width="2" /><circle cx="23" cy="98" r="5" fill="#a855f7" stroke="#1a1a1a" stroke-width="2" /></svg> <div class="flex items-center gap-3 relative z-10" style="height: 28px; margin-top: 0;"> <div class="w-6 shrink-0 h-6 flex items-center justify-center"></div> <div class="flex-1"> <div class="text-white text-[11px] leading-snug" style> Changed the title to Git Native Workflow </div> </div> </div><div class="flex items-center gap-3 relative z-10" style="height: 28px; "> <div class="w-6 shrink-0 h-6 flex items-center justify-center"></div> <div class="flex-1"> <div class="text-white/80 text-[11px] leading-snug" style> merge: git graph component </div> </div> </div><div class="flex items-center gap-3 relative z-10" style="height: 28px; "> <div class="w-6 shrink-0 h-6 flex items-center justify-center"></div> <div class="flex-1"> <div class="text-white text-[11px] leading-snug" style="color: #ec4899;"> fix: graph_rendrer() calls </div> </div> </div><div class="flex items-center gap-3 relative z-10" style="height: 28px; "> <div class="w-6 shrink-0 h-6 flex items-center justify-center"></div> <div class="flex-1"> <div class="text-white text-[11px] leading-snug" style="color: #ec4899;"> add: git graph component </div> </div> </div> </div> </div> </div> </div> </div> </div> </div>
1770<script>(function(){const id = "git-window-git-native"; 1771const platform = "git-native"; 1772 1773 let _docDrag = null; 1774 let _docDragEnd = null; 1775 1776 function initGitWindow() { 1777 const window = document.getElementById(id); 1778 if (!window) { 1779 // Retry after a short delay if element isn't ready (for view transitions) 1780 setTimeout(initGitWindow, 50); 1781 return; 1782 } 1783 1784 // Skip if already initialized 1785 if (window.dataset.gitInitialized === 'true') { 1786 return; 1787 } 1788 window.dataset.gitInitialized = 'true'; 1789 1790 const dragHandle = document.querySelector(`.drag-handle[data-platform="${platform}"]`); 1791 const container = window?.parentElement; 1792 1793 if (!dragHandle || !container) { 1794 // Elements might not be ready yet, retry 1795 window.dataset.gitInitialized = 'false'; 1796 setTimeout(initGitWindow, 50); 1797 return; 1798 } 1799 1800 let isDragging = false; 1801 let initialX = 0; 1802 let initialY = 0; 1803 1804 function dragStart(e) { 1805 if (e.button !== 0) return; 1806 1807 isDragging = true; 1808 1809 const rect = window.getBoundingClientRect(); 1810 const containerRect = container.getBoundingClientRect(); 1811 1812 // Calculate where we clicked relative to the window 1813 initialX = e.clientX - rect.left; 1814 initialY = e.clientY - rect.top; 1815 1816 // Remove transform classes and set initial position 1817 window.classList.remove('-translate-x-1/2', '-translate-y-1/2'); 1818 window.classList.add('translate-x-0', 'translate-y-0'); 1819 1820 // Get current position 1821 const currentLeft = rect.left - containerRect.left; 1822 const currentTop = rect.top - containerRect.top; 1823 1824 // Set initial positions 1825 window.style.left = currentLeft + 'px'; 1826 window.style.top = currentTop + 'px'; 1827 1828 window.classList.add('user-select-none'); 1829 document.body.style.cursor = 'grabbing'; 1830 } 1831 1832 function drag(e) { 1833 if (!isDragging) return; 1834 1835 e.preventDefault(); 1836 1837 const containerRect = container.getBoundingClientRect(); 1838 const windowRect = window.getBoundingClientRect(); 1839 1840 // Calculate new position 1841 const newX = e.clientX - containerRect.left - initialX; 1842 const newY = e.clientY - containerRect.top - initialY; 1843 1844 // Keep window within container bounds 1845 const maxX = containerRect.width - windowRect.width; 1846 const maxY = containerRect.height - windowRect.height; 1847 1848 const boundedX = Math.max(0, Math.min(newX, maxX)); 1849 const boundedY = Math.max(0, Math.min(newY, maxY)); 1850 1851 window.style.left = boundedX + 'px'; 1852 window.style.top = boundedY + 'px'; 1853 } 1854 1855 function dragEnd() { 1856 if (!isDragging) return; 1857 1858 isDragging = false; 1859 window.classList.remove('user-select-none'); 1860 document.body.style.cursor = ''; 1861 } 1862 1863 dragHandle.addEventListener('mousedown', dragStart); 1864 if (!_docDrag) { 1865 _docDrag = drag; 1866 _docDragEnd = dragEnd; 1867 document.addEventListener('mousemove', _docDrag); 1868 document.addEventListener('mouseup', _docDragEnd); 1869 document.addEventListener('mouseleave', _docDragEnd); 1870 } 1871 } 1872 1873 // Initialize on DOM ready 1874 if (document.readyState === 'loading') { 1875 document.addEventListener('DOMContentLoaded', initGitWindow); 1876 } else { 1877 initGitWindow(); 1878 } 1879 1880 // Also initialize after view transitions 1881 document.addEventListener('astro:page-load', initGitWindow); 1882 document.addEventListener('astro:after-swap', initGitWindow); 1883 1884 document.addEventListener('astro:before-swap', () => { 1885 if (_docDrag) { 1886 document.removeEventListener('mousemove', _docDrag); 1887 document.removeEventListener('mouseup', _docDragEnd); 1888 document.removeEventListener('mouseleave', _docDragEnd); 1889 _docDrag = null; 1890 _docDragEnd = null; 1891 } 1892 }); 1893 1894 // Commit functionality - update hero title and add to git graph 1895 function initCommitButton() { 1896 const commitBtn = document.getElementById(`commit-btn-${id}`); 1897 const titleInput = document.getElementById(`title-input-${id}`); 1898 const heroTitle = document.getElementById('hero-title-gitnative'); 1899 const gitTreeContainer = document.getElementById(`git-tree-container-${id}`); 1900 1901 if (!commitBtn || !titleInput || !heroTitle || !gitTreeContainer) { 1902 setTimeout(initCommitButton, 50); 1903 return; 1904 } 1905 1906 commitBtn.addEventListener('click', () => { 1907 const titleValue = titleInput.value.trim(); 1908 if (titleValue) { 1909 // Store pending title (don't update yet) 1910 heroTitle.setAttribute('data-pending-title', titleValue); 1911 1912 // Add commit to git graph 1913 const commitMessage = `Changed title to ${titleValue}`; 1914 addCommitToGraph(gitTreeContainer, commitMessage, 0); 1915 1916 // Clear the input 1917 titleInput.value = ''; 1918 1919 // Glow the refresh button as a hint 1920 const refreshBtn = document.getElementById('refresh-btn-fullstack-chat'); 1921 if (refreshBtn) { 1922 refreshBtn.classList.add('refresh-glow'); 1923 setTimeout(() => { 1924 refreshBtn.classList.remove('refresh-glow'); 1925 }, 3000); 1926 } 1927 } 1928 }); 1929 1930 // Handle browser refresh button 1931 const refreshBtn = document.getElementById('refresh-btn-fullstack-chat'); 1932 if (refreshBtn) { 1933 refreshBtn.addEventListener('click', () => { 1934 const pendingTitle = heroTitle.getAttribute('data-pending-title'); 1935 if (pendingTitle) { 1936 // Update hero title on refresh 1937 heroTitle.textContent = pendingTitle; 1938 heroTitle.removeAttribute('data-pending-title'); 1939 // Remove glow if still active 1940 refreshBtn.classList.remove('refresh-glow'); 1941 } 1942 }); 1943 } 1944 } 1945 1946 function addCommitToGraph(container, message, column) { 1947 const rowHeight = 28; 1948 const firstColumnX = 11; 1949 const columnSpacing = 12; 1950 const mainBranchColor = '#60a5fa'; 1951 1952 // Get the SVG and commit list 1953 const svg = container.querySelector('svg'); 1954 const commitList = container.querySelector('div[class*="relative"]'); 1955 1956 if (!svg || !commitList) return; 1957 1958 // Get current commit count 1959 const existingCommits = commitList.querySelectorAll('div[class*="flex items-center gap-3"]'); 1960 const newIndex = 0; // New commit goes at the top 1961 1962 // Shift all existing circles and lines down by one row 1963 const allCircles = Array.from(svg.querySelectorAll('circle'));
1964 allCircles.forEach((circle) => { 1965 const currentY = parseFloat(circle.getAttribute('cy') || '0'); 1966 const newY = currentY + rowHeight; 1967 circle.setAttribute('cy', newY.toString()); 1968 }); 1969 1970 // Shift horizontal connection lines and branch start positions down 1971 const allLines = Array.from(svg.querySelectorAll('line')); 1972 allLines.forEach((line) => { 1973 const x1 = parseFloat(line.getAttribute('x1') || '0'); 1974 const x2 = parseFloat(line.getAttribute('x2') || '0'); 1975 const y1 = parseFloat(line.getAttribute('y1') || '0'); 1976 const y2 = parseFloat(line.getAttribute('y2') || '0'); 1977 1978 // Skip main branch vertical line (it starts at y1=0 and we'll update y2 separately) 1979 if (x1 === firstColumnX && x2 === firstColumnX && y1 === 0) { 1980 // This is the main branch line - just extend it 1981 const newHeight = (existingCommits.length + 1) * rowHeight; 1982 line.setAttribute('y2', newHeight.toString()); 1983 } else { 1984 // Shift all other lines (horizontal connections, branch lines) 1985 if (y1 > 0) line.setAttribute('y1', (y1 + rowHeight).toString()); 1986 if (y2 > 0) line.setAttribute('y2', (y2 + rowHeight).toString()); 1987 } 1988 }); 1989 1990 // Add new circle at the top (position 0) 1991 const newY = rowHeight / 2; 1992 const x = firstColumnX + (column * columnSpacing); 1993 const newCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 1994 newCircle.setAttribute('cx', x.toString()); 1995 newCircle.setAttribute('cy', newY.toString()); 1996 newCircle.setAttribute('r', '5'); 1997 newCircle.setAttribute('fill', mainBranchColor); 1998 newCircle.setAttribute('stroke', '#1a1a1a'); 1999 newCircle.setAttribute('stroke-width', '2'); 2000 svg.insertBefore(newCircle, svg.firstChild); 2001 2002 // Add new commit row at the top 2003 const newRow = document.createElement('div'); 2004 newRow.className = 'flex items-center gap-3 relative z-10'; 2005 newRow.style.height = `${rowHeight}px`; 2006 newRow.style.marginTop = '0'; 2007 2008 const spacer = document.createElement('div'); 2009 spacer.className = 'w-6 shrink-0 h-6 flex items-center justify-center'; 2010 2011 const messageContainer = document.createElement('div'); 2012 messageContainer.className = 'flex-1'; 2013 2014 const messageText = document.createElement('div'); 2015 messageText.className = 'text-white text-[11px] leading-snug'; 2016 messageText.textContent = message; 2017 2018 messageContainer.appendChild(messageText); 2019 newRow.appendChild(spacer); 2020 newRow.appendChild(messageContainer); 2021 2022 // Remove margin-top from first existing commit if any 2023 if (existingCommits.length > 0) { 2024 existingCommits[0].style.marginTop = ''; 2025 } 2026 2027 // Insert at the beginning 2028 commitList.insertBefore(newRow, commitList.firstChild); 2029 2030 // Update container height 2031 const newTotalHeight = (existingCommits.length + 1) * rowHeight; 2032 commitList.style.minHeight = `${newTotalHeight}px`; 2033 } 2034 2035 // Initialize commit button 2036 if (document.readyState === 'loading') { 2037 document.addEventListener('DOMContentLoaded', initCommitButton); 2038 } else {
2039 initCommitButton(); 2040 } 2041 2042 document.addEventListener('astro:page-load', initCommitButton); 2043 document.addEventListener('astro:after-swap', initCommitButton); 2044})();</script>
2044</div></div></div></div></div> </div> </div> </div> <div id="feature-build-local" class="rounded-2xl bg-black/10 dark:bg-[#111] p-6 md:p-8 mb-8"> <div class="flex flex-col lg:flex-row gap-8 items-center"> <div class="order-1 lg:order-2 shrink-0 w-full lg:w-1/3"> <div> <div> <a href="/features#build-local" class="inline-flex items-center gap-2 text-xl md:text-2xl font-semibold leading-tight text-black dark:text-white hover:opacity-80"> Production Parity From The Start <i class="ri-arrow-right-line mt-1" aria-hidden="true"></i> </a> <p class="mt-4 text-base md:text-lg text-black/70 dark:text-white/70 max-w-2xl">Dream gives developers and agents a local environment that behaves like production before anything ships.</p> </div> </div> </div> <div class="order-2 lg:order-1 grow w-full lg:w-2/3"> <div> <!-- Mobile: Static screenshot --> <div class="lg:hidden relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full"> <img src="/screenshots/build-local-feature-desktop.png" alt="Production Parity From The Start feature interface" class="w-full h-full object-cover" loading="lazy"> </div> <!-- Desktop: Interactive component --> <div class="hidden lg:block relative rounded-xl overflow-hidden bg-[#0b0b0b] aspect-16/10 w-full"> <img src="/test-background.png" alt="" class="absolute inset-0 h-full w-full object-cover pointer-events-none" loading="lazy"> <!-- Dream desktop window centered inside container --> <div class="absolute inset-0"> <!-- Dream Desktop Window (Floating) --><div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full shadow-[0_35px_60px_-12px_rgba(0,0,0,0.5)] rounded-lg max-w-[43.2rem] max-h-xl" id="draggable-dream-window-f58l14l"> <div class="bg-[#1a1a1a] rounded-lg overflow-hidden border border-white/10 flex flex-col h-full"> <!-- Browser-style title bar - spans full width --> <div class="bg-[#222] px-4 py-1.5 flex items-center border-b border-white/10 drag-handle cursor-move"> <div class="flex items-center gap-1"> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> <span class="w-2.5 h-2.5 rounded-full bg-white/20"></span> </div> <span class="ml-4 text-white/60 text-xs">Dream Desktop</span> </div> <div class="flex flex-1 overflow-hidden"> <!-- Left persistent panel --> <div class="w-60 bg-[#252525] border-r border-white/10 flex flex-col"> <!-- Stats --> <div class="grid grid-cols-3 gap-1 p-3 text-center border-b border-white/10"> <div class="bg-[#2a2a2a] rounded px-2 py-1"> <div class="text-[10px] text-white/50 uppercase">Memory</div> <div class="text-xs text-white font-medium">0%</div> <div class="text-[9px] text-white/40">77MB</div> </div> <div class="bg-[#2a2a2a] rounded px-2 py-1"> <div class="text-[10px] text-white/50 uppercase">CPU</div> <div class="text-xs text-white font-medium">7%</div> </div> <div class="bg-[#2a2a2a] rounded px-2 py-1"> <div class="text-[10px] text-white/50 uppercase">Universes</div> <div class="text-xs text-white font-medium">1</div> </div> </div> <!-- Sidebar list --> <div class="px-3 py-3 flex-1"> <div class="flex items-center justify-between mb-2"> <div class="text-[10px] text-white/50 uppercase tracking-wide">Persistent</div> <div class="flex items-center gap-1"> <button class="text-white/40 hover:text-white"><i class="ri-refresh-line text-xs"></i></button> <button class="text-white/40 hover:text-white"><i class="ri-more-2-fill text-xs"></i></button> </div> </div> <button class="w-full flex items-center justify-between bg-[#2a2a2a] hover:bg-[#2d2d2d] text-white text-sm px-2 py-2 rounded mb-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 2045blackhole 2046</span> <i class="ri-arrow-right-s-line text-white/60"></i> </button> </div> <div class="mt-auto p-3 space-y-2 border-t border-white/10"> <button class="w-full bg-linear-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 text-white py-2 rounded text-sm font-medium flex items-center justify-center gap-2"> <i class="ri-add-line"></i> 2047Create New 2048</button> <div class="text-[10px] text-white/40 uppercase tracking-wide mb-1 mt-3">Services</div> <div class="space-y-1"> <div class="flex items-center justify-between text-[11px] text-white/80 py-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 2049API Service 2050</span> <button class="text-white/30 hover:text-white"><i class="ri-more-2-fill"></i></button> </div> <div class="flex items-center justify-between text-[11px] text-white/80 py-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 2051MCP Server 2052</span> <button class="text-white/30 hover:text-white"><i class="ri-more-2-fill"></i></button> </div> <div class="flex items-center justify-between text-[11px] text-white/80 py-1"> <span class="flex items-center gap-2"> <span class="w-2 h-2 rounded-full bg-green-500"></span> 2053Name Server (DNS) 2054</span> <button class="text-white/30 hover:text-white"><i class="ri-more-2-fill"></i></button> </div> </div> </div> </div> <!-- Main content area --> <div class="flex-1 flex flex-col"> <!-- Top Header Bar with blackhole --> <div class="bg-[#2d2d2d] h-10 px-4 flex items-center justify-between border-b border-white/10"> <div class="flex items-center gap-3"> <i class="ri-cloud-line text-white"></i> <span class="text-white font-medium text-sm">blackhole</span> <span class="text-[10px] bg-green-900/40 text-green-400 px-2 py-0.5 rounded">RUNNING</span> </div> <div class="flex items-center gap-2"> <button class="px-3 py-1 text-xs bg-red-600/80 hover:bg-red-600 text-white rounded flex items-center gap-1"> <i class="ri-stop-fill"></i> 2055Stop 2056</button> <button class="px-3 py-1 text-xs bg-white/5 hover:bg-white/10 text-white/60 rounded flex items-center gap-1"> <i class="ri-delete-bin-line"></i> 2057Delete 2058</button> <button class="text-white/60 hover:text-white"><i class="ri-refresh-line"></i></button> <button class="text-white/60 hover:text-white"><i class="ri-close-line"></i></button> </div> </div> <!-- Navigation tabs --> <div class="px-4 flex items-center gap-6 border-b border-white/10 bg-[#252525] text-[13px]"> <button class="flex items-center gap-2 text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors py-2"> <i class="ri-home-line text-sm"></i>Overview 2059</button> <button class="flex items-center gap-2 text-blue-400 border-b-2 border-blue-400 -mb-[2px] py-2"> <i class="ri-radar-line text-sm"></i>Network 2060</button> <button class="flex items-center gap-2 text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors py-2"> <i class="ri-folder-3-line text-sm"></i>Projects 2061</button> <button class="flex items-center gap-2 text-white/60 hover:text-white border-b-2 border-transparent hover:border-white/30 transition-colors py-2"> <i class="ri-file-list-3-line text-sm"></i>Logs 2062</button> </div> <!-- Content area --> <div class="flex-1 overflow-hidden bg-[#1a1a1a]"> <div id="draggable-dream-window-f58l14l-network" class="w-full h-full"></div> </div> </div> </div> </div> </div>
2062<script>(function(){const id = "draggable-dream-window-f58l14l"; 2063const activeTab = "network"; 2064 2065 let _docDrag = null; 2066 let _docDragEnd = null; 2067 2068 function initDreamDesktopDrag() { 2069 const windowEl = document.getElementById(id); 2070 const dragHandle = windowEl?.querySelector('.drag-handle'); 2071 const container = windowEl?.parentElement; 2072 if (!windowEl || !dragHandle || !container) return; 2073 2074 // Check if already bound to prevent duplicates 2075 if (dragHandle._dragBound === true) return; 2076 dragHandle._dragBound = true; 2077 2078 let isDragging = false; 2079 let initialX = 0; 2080 let initialY = 0; 2081 2082 function dragStart(e) { 2083 if (e.button !== 0) return; 2084 isDragging = true; 2085 const rect = windowEl.getBoundingClientRect(); 2086 const containerRect = container.getBoundingClientRect(); 2087 initialX = e.clientX - rect.left; 2088 initialY = e.clientY - rect.top; 2089 windowEl.classList.remove('-translate-x-1/2', '-translate-y-1/2'); 2090 windowEl.classList.add('translate-x-0', 'translate-y-0'); 2091 const currentLeft = rect.left - containerRect.left; 2092 const currentTop = rect.top - containerRect.top; 2093 windowEl.style.left = currentLeft + 'px'; 2094 windowEl.style.top = currentTop + 'px'; 2095 document.body.style.cursor = 'grabbing'; 2096 } 2097 2098 function drag(e) { 2099 if (!isDragging) return; 2100 e.preventDefault(); 2101 const containerRect = container.getBoundingClientRect(); 2102 const windowRect = windowEl.getBoundingClientRect(); 2103 const newX = e.clientX - containerRect.left - initialX; 2104 const newY = e.clientY - containerRect.top - initialY; 2105 const maxX = containerRect.width - windowRect.width; 2106 const maxY = containerRect.height - windowRect.height; 2107 const boundedX = Math.max(0, Math.min(newX, maxX)); 2108 const boundedY = Math.max(0, Math.min(newY, maxY)); 2109 windowEl.style.left = boundedX + 'px'; 2110 windowEl.style.top = boundedY + 'px'; 2111 } 2112 2113 function dragEnd() { 2114 if (!isDragging) return; 2115 isDragging = false; 2116 document.body.style.cursor = ''; 2117 } 2118 2119 dragHandle.addEventListener('mousedown', dragStart); 2120 if (!_docDrag) { 2121 _docDrag = drag; 2122 _docDragEnd = dragEnd; 2123 document.addEventListener('mousemove', _docDrag); 2124 document.addEventListener('mouseup', _docDragEnd); 2125 document.addEventListener('mouseleave', _docDragEnd); 2126 } 2127 } 2128 2129 // Initialize drag on page load 2130 if (typeof window !== 'undefined') { 2131 if (document.readyState === 'loading') { 2132 document.addEventListener('DOMContentLoaded', initDreamDesktopDrag); 2133 } else { 2134 initDreamDesktopDrag(); 2135 } 2136 } 2137 2138 // Re-initialize after view transitions 2139 document.addEventListener('astro:page-load', () => { 2140 requestAnimationFrame(initDreamDesktopDrag); 2141 }); 2142 2143 document.addEventListener('astro:before-swap', () => { 2144 if (_docDrag) { 2145 document.removeEventListener('mousemove', _docDrag); 2146 document.removeEventListener('mouseup', _docDragEnd); 2147 document.removeEventListener('mouseleave', _docDragEnd); 2148 _docDrag = null; 2149 _docDragEnd = null; 2150 } 2151 }); 2152 2153 // Initialize vis-network in Network tab 2154 function initNetworkIfNeeded(retries = 8) { 2155 if (activeTab !== 'network') return; 2156 const containerEl = document.getElementById(`${id}-network`); 2157 if (!containerEl) { 2158 if (retries > 0) setTimeout(() => initNetworkIfNeeded(retries - 1), 120); 2159 return; 2160 } 2161 if ((containerEl)._visInit) return; 2162 try { 2163 function withVis(callback) { 2164 const g = window; 2165 if (g.vis && g.vis.Network && g.vis.DataSet) { 2166 callback(g.vis); 2167 return; 2168 } 2169 // Wait for npm import to load 2170 setTimeout(() => { 2171 if (g.vis && g.vis.Network && g.vis.DataSet) { 2172 callback(g.vis); 2173 } else { 2174 console.error('vis-network failed to load'); 2175 } 2176 }, 100); 2177 } 2178 2179 withVis((vis) => { 2180 2181 const data = { 2182 nodes: [ 2183 { id: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', name: 'elder@blackhole', category: 0, value: { p2p: 13151 } }, 2184 { id: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', name: 'monkey@blackhole', category: 1, value: { copies: 1, dns: 13404, http: 13100, p2p: 13263 } }, 2185 { id: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', name: 'hoarder@blackhole', category: 2, value: { copies: 1, dns: 13404, http: 13100, p2p: 13242 } },
2186 { id: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak', name: 'tns@blackhole', category: 3, value: { copies: 1, dns: 13404, http: 13566, p2p: 13326 } }, 2187 { id: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85', name: 'patrick@blackhole', category: 4, value: { copies: 1, dns: 13404, http: 13524, p2p: 13284 } }, 2188 { id: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ', name: 'auth@blackhole', category: 5, value: { copies: 1, dns: 13404, http: 13545, p2p: 13221 } }, 2189 { id: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P', name: 'seer@blackhole', category: 6, value: { copies: 1, dns: 13404, http: 13503, p2p: 13305 } }, 2190 { id: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd', name: 'gateway@blackhole', category: 7, value: { p2p: 13152 } }, 2191 { id: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', name: 'substrate@blackhole', category: 8, value: { copies: 1, dns: 13404, http: 13629, p2p: 13382 } }, 2192 ], 2193 links: [ 2194 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 2195 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 2196 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 2197 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 2198 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 2199 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP' }, 2200 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 2201 { source: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 2202 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 2203 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 2204 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 2205 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 2206 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 2207 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP' }, 2208 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 2209 { source: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 2210 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 2211 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 2212 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 2213 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 2214 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 2215 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWAn9uLqFu3xiJywFpypp
2215DxumfM4bztmReoBrKt68L5Dak' }, 2216 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 2217 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 2218 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 2219 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K' }, 2220 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 2221 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 2222 { source: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 2223 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWLE7HGRCJoq7Nfzp55YV9AdLoBpdmJat9qDjmXsmWcbbd' }, 2224 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWDoxzWtQTmRBycxpAqQ3UXCiCx6Qt8KYF6KcfWDN7Pj8P' }, 2225 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWAn9uLqFu3xiJywFpyppDxumfM4bztmReoBrKt68L5Dak' }, 2226 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWJ75qxH6HpBHQZNqJjHyns7R2nQBCdgAdwbwPZdrBH9Dc' }, 2227 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWDShcNAro86QDo5HiWTbXnoG66YWK66PH3B2uA6Wz1x85' }, 2228 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWCKkKpKxj4GfgnVnYG8NrcTQsJdWWWTENkDCgP2cynyxP' }, 2229 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWKAqfJ8S7LzJMUsSB7MkefgDqHnucx6u2EsGDzGSeBxnJ' }, 2230 { source: '12D3KooWLBzChxCSmcW98w2FWbRYAjFcQH6Ahi8duHJPQF4pY46K', target: '12D3KooWFvAAt8dRbE6CQdU8mFHBxmz8uLhMgta6EgkAmqGMMmjs' }, 2231 ], 2232 }; 2233 2234 const isDark = (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)); 2235 const accentColor = '#099a40'; 2236 const linkColor = '#777777'; 2237 2238 const nodes = new vis.DataSet( 2239 data.nodes.map((n) => ({ 2240 id: n.id, 2241 label: n.name.split('@')[0], 2242 shape: 'image', 2243 image: '/taubyte-node.svg', 2244 size: 25 2245 })) 2246 ); 2247 2248 const dedupedEdges = []; 2249 const seenPairs = new Set(); 2250 data.links.forEach((l) => { 2251 const key = [l.source, l.target].sort().join('|'); 2252 if (!seenPairs.has(key)) { 2253 seenPairs.add(key); 2254 dedupedEdges.push({ from: l.source, to: l.target }); 2255 } 2256 }); 2257 const edges = new vis.DataSet(dedupedEdges); 2258 2259 const options = { 2260 autoResize: true, 2261 height: '100%', 2262 width: '100%', 2263 interaction: { hover: true, tooltipDelay: 50, zoomView: false, dragView: false }, 2264 nodes: { 2265 shape: 'image', 2266 shadow: { enabled: true, color: 'rgba(9,154,64,0.35)', size: 10, x: 0, y: 0 }, 2267 borderWidth: 0, 2268 size: 25, 2269 font: { size: 14, color: accentColor, vadjust: 2 }, 2270 }, 2271 edges: { width: 1, shadow: false, color: linkColor }, 2272 physics: { 2273 solver: 'repulsion', 2274 repulsion: { 2275 nodeDistance: 100, 2276 centralGravity: 0.15, 2277 springLength: 220, 2278 springConstant: 0.02, 2279 damping: 0.09 2280 } 2281 }, 2282 }; 2283 2284 const network = new vis.Network(containerEl, { nodes, edges }, options); 2285 network.once('afterDrawing', () => network.fit({ maxZoomLevel: 0.8 })); 2286 (containerEl)._visInit = true; 2287 (containerEl)._visNetwork = network; 2288 }); 2289 } catch (e) { 2290 console.error('vis init error', e); 2291 } 2292 } 2293 2294 // Tear down the existing vis-network instance before a re-init so WebGL/canvas 2295 // resources don't accumulate across view transitions. 2296 function resetNetwork() { 2297 const containerEl = document.getElementById(`${id}-network`); 2298 if (!containerEl) return; 2299 try { (containerEl)._visNetwork?.destroy?.(); } catch (_) {} 2300 (containerEl)._visNetwork = null; 2301 (containerEl)._visInit = false;
2302 } 2303 2304 // Initialize on page load 2305 if (typeof window !== 'undefined') { 2306 if (document.readyState === 'loading') { 2307 document.addEventListener('DOMContentLoaded', () => { 2308 initNetworkIfNeeded(); 2309 }); 2310 } else { 2311 initNetworkIfNeeded(); 2312 } 2313 } 2314 2315 // Re-initialize network after view transitions 2316 document.addEventListener('astro:page-load', () => { 2317 requestAnimationFrame(() => { 2318 resetNetwork(); 2319 initNetworkIfNeeded(); 2320 }); 2321 }); 2322 document.addEventListener('astro:after-swap', () => { 2323 requestAnimationFrame(() => { 2324 resetNetwork(); 2325 initNetworkIfNeeded(); 2326 }); 2327 }); 2328})();</script>
2328 </div> </div> </div> </div> </div> </div> </div> </section> <!-- Chat Section --><section id="taure-section" class="mt-12 px-6 pt-12 pb-6 bg-gray-50 dark:bg-[#1a1a1a] transition-colors" data-astro-cid-tkm6qkaw> <div class="container mx-auto max-w-4xl" data-astro-cid-tkm6qkaw> <!-- Chat Messages Container --> <div id="chat-messages" class="space-y-4 mb-6" data-astro-cid-tkm6qkaw> <!-- Messages will be added here dynamically --> </div> <!-- Dynamic Prompt Area --> <div id="prompt-area" class="scroll-mt-4 mb-8 mt-12" data-astro-cid-tkm6qkaw> <!-- Newsletter Form (Initial State) --> <form id="newsletter-form" data-astro-cid-tkm6qkaw> <div class="flex gap-2 flex-col sm:flex-row" data-astro-cid-tkm6qkaw> <input type="text" name="name" id="newsletter-name" placeholder="Your name" required class="flex-1 px-4 py-3 rounded-full border border-black/10 dark:border-white/10 bg-white dark:bg-[#151515] text-black dark:text-white placeholder-black/40 dark:placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-black dark:focus:ring-white transition-all text-sm" data-astro-cid-tkm6qkaw> <input type="email" name="email" id="newsletter-email" placeholder="Your email" required class="flex-1 px-4 py-3 rounded-full border border-black/10 dark:border-white/10 bg-white dark:bg-[#151515] text-black dark:text-white placeholder-black/40 dark:placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-black dark:focus:ring-white transition-all text-sm" data-astro-cid-tkm6qkaw> <button type="submit" id="newsletter-submit" name="action" value="subscribe" class="px-6 py-3 bg-black dark:bg-white text-white dark:text-black text-sm font-medium rounded-full hover:bg-black/90 dark:hover:bg-white/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap" data-astro-cid-tkm6qkaw> 2329Subscribe 2330</button> <button type="button" id="chat-button" class="px-6 py-3 bg-white dark:bg-[#151515] text-black dark:text-white text-sm font-medium rounded-full border border-black/10 dark:border-white/10 hover:bg-black/5 dark:hover:bg-white/5 transition-colors whitespace-nowrap" data-astro-cid-tkm6qkaw> 2331Not today 2332</button> </div> <div id="newsletter-message" class="mt-3 text-sm hidden" data-astro-cid-tkm6qkaw></div> </form> <!-- Regular Chat Input (Hidden Initially) --> <div id="regular-chat-prompt" class="hidden" data-astro-cid-tkm6qkaw> <div class="flex gap-2" data-astro-cid-tkm6qkaw> <input type="text" id="chat-input" placeholder="Ask me anything about Taubyte..." class="flex-1 px-4 py-3 rounded-full border border-black/10 dark:border-white/10 bg-white dark:bg-[#151515] text-black dark:text-white placeholder-black/40 dark:placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-black dark:focus:ring-white transition-all text-sm" data-astro-cid-tkm6qkaw> <button type="button" id="chat-send" class="px-6 py-3 bg-black dark:bg-white text-white dark:text-black text-sm font-medium rounded-full hover:bg-black/90 dark:hover:bg-white/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" data-astro-cid-tkm6qkaw> 2333Send 2334</button> </div> </div> <!-- Name/Email Collection Prompt (Hidden Initially) --> <div id="name-email-prompt" class="hidden" data-astro-cid-tkm6qkaw> <form id="name-email-form" class="flex gap-2 flex-col sm:flex-row" data-astro-cid-tkm6qkaw> <input type="text" id="name-input" name="name" placeholder="Your name" required class="flex-1 px-4 py-3 rounded-full border border-black/10 dark:border-white/10 bg-white dark:bg-[#151515] text-black dark:text-white placeholder-black/40 dark:placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-black dark:focus:ring-white transition-all text-sm" data-astro-cid-tkm6qkaw> <input type="email" id="email-input" name="email" placeholder="Your email" required class="flex-1 px-4 py-3 rounded-full border border-black/10 dark:border-white/10 bg-white dark:bg-[#151515] text-black dark:text-white placeholder-black/40 dark:placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-black dark:focus:ring-white transition-all text-sm" data-astro-cid-tkm6qkaw> <button type="submit" id="name-email-submit" class="px-6 py-3 bg-black dark:bg-white text-white dark:text-black text-sm font-medium rounded-full hover:bg-black/90 dark:hover:bg-white/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap" data-astro-cid-tkm6qkaw> 2335Continue 2336</button> </form> <div id="name-email-message" class="mt-3 text-sm hidden" data-astro-cid-tkm6qkaw></div> </div> </div> </div> </section> <!-- Floating Taure Avatar --> <div id="floating-taure-avatar" class="fixed bottom-6 right-6 z-50 hidden" data-astro-cid-tkm6qkaw> <div class="flex items-center gap-0 relative" data-astro-cid-tkm6qkaw> <!-- Message Bubble --> <div id="floating-taure-message" class="hidden h-[64px] pl-4 pr-12 flex items-center bg-white dark:bg-[#151515] rounded-l-full rounded-r-lg shadow-lg border border-black/10 dark:border-white/10 -mr-[32px] relative z-0" data-astro-cid-tkm6qkaw> <p class="text-sm text-black dark:text-white" data-astro-cid-tkm6qkaw>howdy</p> </div> <!-- Avatar --> <div class="w-[64px] h-[64px] rounded-full overflow-hidden shadow-lg border-2 border-white dark:border-[#151515] cursor-pointer hover:scale-110 transition-transform duration-200 shrink-0 relative z-10" data-astro-cid-tkm6qkaw> <img id="floating-taure-avatar-img" src="/taure-avatar.png" alt="Taure" class="w-full h-full object-cover" data-astro-cid-tkm6qkaw> </div> </div> </div>
2336<script type="module" src="/_astro/Taure.astro_astro_type_script_index_0_lang.CWNAM_wp.js"></script>
2336 </main> <footer class="bg-white dark:bg-[#151515] transition-colors border-t border-black/10 dark:border-white/10" data-astro-transition-persist="astro-tvayyljk-2"> <div class="container mx-auto max-w-7xl px-6 py-8 md:py-12"> <div class="flex flex-col gap-8"> <!-- Main Footer Content --> <div class="grid grid-cols-2 md:grid-cols-5 gap-8 md:gap-6"> <!-- Logo and Brand --> <div class="col-span-2 md:col-span-1 flex flex-col gap-4"> <a href="/" class="block cursor-pointer w-fit"> <object data="/taubyte-logo-static.svg" type="image/svg+xml" class="h-6 w-auto dark:brightness-0 dark:invert pointer-events-none"></object> </a> <p class="text-xs text-black/60 dark:text-white/60 max-w-xs"> 2337Fullstack workspace for humans & machines. 2338</p> </div> <!-- Product Section --> <div class="flex flex-col gap-3"> <h3 class="text-sm font-semibold text-black dark:text-white">Product</h3> <nav class="flex flex-col gap-2"> <a href="/download" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Download</a> <a href="/features" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Features</a> <a href="/getting-started" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Getting Started</a> </nav> </div> <!-- Tools Section --> <div class="flex flex-col gap-3"> <h3 class="text-sm font-semibold text-black dark:text-white">Tools</h3> <nav class="flex flex-col gap-2"> <a href="/tools/toon" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">TOON Converter</a> <a href="/tools/tokenizer" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Tokenizer Playground</a> <a href="/tools/embedder" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Text Embedder</a> <a href="/tools/wasm" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">WebAssembly Playground</a> <a href="/tools/k8s-tax" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">K8s Tax Calculator</a> <a href="/tools/hf-mem" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Model Memory Estimator</a> <a href="/tools/cloud-opportunity" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">What Taubyte Opens</a> </nav> </div> <!-- Resources Section --> <div class="flex flex-col gap-3"> <h3 class="text-sm font-semibold text-black dark:text-white">Resources</h3> <nav class="flex flex-col gap-2"> <a href="/doc" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Documentation</a> <a href="/blog" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Blog</a> <a href="/community" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Community</a> </nav> </div> <!-- Legal & Company Section --> <div class="flex flex-col gap-3"> <h3 class="text-sm font-semibold text-black dark:text-white">Company</h3> <nav class="flex flex-col gap-2"> <a href="/about" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">About</a> <a href="/contact" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Contact</a> <a href="/book-a-demo" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Book a Demo</a> <a href="/privacy" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Privacy</a> <a href="/terms" class="text-sm text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors">Terms</a> </nav> </div> </div> <!-- Bottom Bar: Social Links, Theme Toggle, and Copyright --> <div class="flex flex-col md:flex-row justify-between items-center gap-4 pt-6"> <!-- Copyright --> <p class="text-xs text-black/60 dark:text-white/60 text-center md:text-left"> 2339© 2026 Taubyte. All rights reserved. 2340</p> <!-- Social Links and Theme Toggle --> <div class="flex items-center justify-center space-x-4"> <div class="flex items-center gap-2"> <button id="theme-toggle" class="p-2 rounded-md hover:bg-gray-100 dark:hover:bg-white/10 transition-colors text-gray-700 dark:text-gray-300" aria-label="Toggle theme" type="button"> <svg id="sun-icon" class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path> </svg> <svg id="moon-icon" class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path> </svg> </button> </div>
2340<script type="module">document.addEventListener("DOMContentLoaded",function(){const t=document.getElementById("theme-toggle"),e=document.documentElement;t&&t.addEventListener("click",()=>{e.classList.contains("dark")?(e.classList.remove("dark"),localStorage.setItem("theme","light")):(e.classList.add("dark"),localStorage.setItem("theme","dark"))})});</script>
2340 <a href="https://x.com/dtaubyte" target="_blank" rel="noopener noreferrer" class="text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors" aria-label="X (Twitter)"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"> <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path> </svg> </a> <a href="https://github.com/taubyte/tau" target="_blank" rel="noopener noreferrer" class="text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors" aria-label="GitHub"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"> <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path> </svg> </a> <a href="https://www.linkedin.com/company/taubyte-page" target="_blank" rel="noopener noreferrer" class="text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors" aria-label="LinkedIn"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"> <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.063 2.063 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path> </svg> </a> <a href="https://discord.com/invite/KbN3KN7kpQ" target="_blank" rel="noopener noreferrer" class="text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors" aria-label="Discord"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"> <path d="M20.317 4.492c-1.53-.69-3.17-1.2-4.885-1.49a.075.075 0 0 0-.079.036c-.21.369-.444.85-.608 1.23a18.566 18.566 0 0 0-5.487 0 12.36 12.36 0 0 0-.617-1.23A.077.077 0 0 0 8.562 3c-1.714.29-3.354.8-4.885 1.491a.07.07 0 0 0-.032.027C.533 9.093-.32 13.555.099 17.961a.08.08 0 0 0 .031.055 20.03 20.03 0 0 0 5.993 2.98.078.078 0 0 0 .084-.026c.462-.62.874-1.275 1.226-1.963.021-.04.001-.088-.041-.104a13.201 13.201 0 0 1-1.872-.878.075.075 0 0 1-.008-.125c.126-.093.252-.19.372-.287a.075.075 0 0 1 .078-.01c3.927 1.764 8.18 1.764 12.061 0a.075.075 0 0 1 .079.009c.12.098.245.195.372.288a.075.075 0 0 1-.006.125c-.598.344-1.22.635-1.873.877a.075.075 0 0 0-.041.105c.36.687.772 1.341 1.225 1.962a.077.077 0 0 0 .084.028 19.963 19.963 0 0 0 6.002-2.981.076.076 0 0 0 .032-.054c.5-5.094-.838-9.52-3.549-13.442a.06.06 0 0 0-.031-.028zM8.02 15.278c-1.182 0-2.157-1.069-2.157-2.38 0-1.312.956-2.38 2.157-2.38 1.21 0 2.176 1.077 2.157 2.38 0 1.312-.956 2.38-2.157 2.38zm7.975 0c-1.183 0-2.157-1.069-2.157-2.38 0-1.312.955-2.38 2.157-2.38 1.21 0 2.176 1.077 2.157 2.38 0 1.312-.946 2.38-2.157 2.38z"></path> </svg> </a> <a href="https://www.instagram.com/dtaubyte" target="_blank" rel="noopener noreferrer" class="text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white transition-colors" aria-label="Instagram"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"> <path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259
23400-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"></path> </svg> </a> </div> </div> </div> </div> </footer>
2340<script> 2341 // Function to update favicon based on theme 2342 function updateFavicon() { 2343 var isDark = document.documentElement.classList.contains('dark'); 2344 var favicon = document.querySelector('link[rel="icon"]'); 2345 if (favicon && favicon instanceof HTMLLinkElement) { 2346 // Update via Data URI for immediate effect 2347 var svgContent = isDark 2348 ? '<svg width="120mm" height="120mm" version="1.1" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="m0.64401 10.882c2.0263 11.607 11.55 18.974 20.476 21.646v62.992c0 7.5331 6.0654 13.597 13.598 13.597h16.845l5.8002-21.647h-14.596v-54.942h19.736l5.8002-21.647zm76.444 0-5.8002 21.647h26.421v54.942h-31.561l-5.8002 21.647h45.411c7.5332 0 13.597-6.0642 13.597-13.597v-71.04c0-7.5332-6.064-13.598-13.597-13.598z"/></svg>' 2349 : '<svg width="120mm" height="120mm" version="1.1" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"><path fill="#000000" d="m0.64401 10.882c2.0263 11.607 11.55 18.974 20.476 21.646v62.992c0 7.5331 6.0654 13.597 13.598 13.597h16.845l5.8002-21.647h-14.596v-54.942h19.736l5.8002-21.647zm76.444 0-5.8002 21.647h26.421v54.942h-31.561l-5.8002 21.647h45.411c7.5332 0 13.597-6.0642 13.597-13.597v-71.04c0-7.5332-6.064-13.598-13.597-13.598z"/></svg>'; 2350 favicon.href = 'data:image/svg+xml;base64,' + btoa(svgContent); 2351 } 2352 } 2353 2354 // Carry current theme class to the incoming document during view transitions 2355 document.addEventListener('astro:before-swap', (event) => { 2356 const isDark = document.documentElement.classList.contains('dark'); 2357 if (isDark) { 2358 event.newDocument.documentElement.classList.add('dark'); 2359 } else { 2360 event.newDocument.documentElement.classList.remove('dark'); 2361 } 2362 }); 2363 2364 // Ensure theme is re-applied after navigation as well 2365 document.addEventListener('astro:page-load', () => { 2366 try { 2367 var saved = localStorage.getItem('theme'); 2368 var prefersLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches; 2369 var theme = saved || (prefersLight ? 'light' : 'dark'); 2370 if (theme === 'dark') { 2371 document.documentElement.classList.add('dark'); 2372 } else { 2373 document.documentElement.classList.remove('dark'); 2374 } 2375 updateFavicon(); 2376 } catch (e) {} 2377 2378 // Track pageview for Google Analytics on View Transition navigation 2379 try { 2380 if (typeof window !== 'undefined' && window.gtag) { 2381 window.gtag('config', 'G-1Y0G0GVEQP', { 2382 page_path: window.location.pathname + window.location.search 2383 }); 2384 } 2385 } catch (e) {} 2386 }); 2387 2388 // Watch for theme changes 2389 document.addEventListener('DOMContentLoaded', () => { 2390 updateFavicon(); 2391 2392 // Create a MutationObserver to watch for class changes on html element 2393 const observer = new MutationObserver(() => { 2394 updateFavicon(); 2395 }); 2396 2397 observer.observe(document.documentElement, { 2398 attributes: true, 2399 attributeFilter: ['class'] 2400 }); 2401 }); 2402</script>
2402 </body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.