CINXE.COM
Visual Blocks for ML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Visual Blocks for ML</title> <base href="." /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" type="image/svg+xml" href="images/favicon.svg" /> <!-- Needed for Material icons --> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> <!-- Global styles --> <link rel="stylesheet" href="styles_v27.css" /> <!-- Import styles for Cookie Consent Bar --> <link href="//www.gstatic.com/glue/v25_0/ccb.min.css" rel="stylesheet" /> <!-- Import fonts for Cookie Consent Bar --> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preload" href="https://fonts.googleapis.com/css2?family=Product+Sans&family=Google+Sans+Display:ital,wght@0,400;0,700;1,400;1,700&family=Google+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Google+Sans+Text:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap" as="style" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Product+Sans&family=Google+Sans+Display:ital,wght@0,400;0,700;1,400;1,700&family=Google+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Google+Sans+Text:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap" /> <!-- Primary Meta Tags --> <meta name="title" content="Visual Blocks" /> <meta name="description" content="Drag and drop off-the-shelf ML components with Visual Blocks. A fast, easy way to prototype ML pipelines – no expertise or coding required." /> <!-- Open Graph / Facebook / LinkedIn --> <meta property="og:type" content="website" /> <meta property="og:url" content="https://visualblocks.withgoogle.com/" /> <meta property="og:title" content="Visual Blocks" /> <meta property="og:description" content="Drag and drop off-the-shelf ML components with Visual Blocks. A fast, easy way to prototype ML pipelines – no expertise or coding required." /> <meta property="og:image" content="https://visualblocks.withgoogle.com/images/visual_blocks_social.png" /> <!-- Twitter --> <meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:url" content="https://visualblocks.withgoogle.com/" /> <meta property="twitter:title" content="Visual Blocks" /> <meta property="twitter:description" content="Drag and drop off-the-shelf ML components with Visual Blocks. A fast, easy way to prototype ML pipelines – no expertise or coding required." /> <meta property="twitter:image" content="https://visualblocks.withgoogle.com/images/visual_blocks_social.png" /> </head> <body> <script> globalThis.visualblocks = { createAsyncFunction(param, code) { const AsyncFunction = async function () {}.constructor; return AsyncFunction(param, code); }, loadedCustomNodeUrls: new Set(), loadScript: async (url) => { if (visualblocks.loadedCustomNodeUrls.has(url)) { return; } visualblocks.loadedCustomNodeUrls.add(url); visualblocks.currentCustomNodeUrl = url; function sleep(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } const LOAD_TIMEOUT = 5_000; const TIMEOUT_MESSAGE = `Custom node script "${url}"'s didn't register any custom nodes in ${LOAD_TIMEOUT} ms`; async function pollScriptLoaded() { // Wait for the script to call the node registration function before resolving the // promise. This is to wait for the possible async functions in the loaded script to // resolve. // // Note that window['visualblocks_registerCustomNodeCalled'] is set to true in the // node registration function `registerCustomNode` in project_page.ts. const start = Date.now(); while(true) { if (window['visualblocks_registerCustomNodeCalled']) { window['visualblocks_registerCustomNodeCalled'] = false; return; } await sleep(100); if (Date.now() - start > LOAD_TIMEOUT) { throw new Error(TIMEOUT_MESSAGE); } } } function loadWithScriptTag() { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = url; if ((new URL(url)).searchParams.get('credentials') === '1') { script.setAttribute('crossorigin', 'use-credentials'); } script.onload = async () => { try { await pollScriptLoaded(); resolve(); } catch (e) { // Timed out registering custom nodes. reject(e); } finally { script.remove(); } }; script.onerror = () => { reject(new Error( `Failed to load "${url}". See console log for more info.`)); }; document.body.appendChild(script); }); } let customNodeModule; try { customNodeModule = await import(url); } catch (e) { // Fully failed to load as ESM. Fall back to script tag. console.warn(e); console.warn(`Failed to load "${url}" with an ESModules async import. Falling back to a script tag.`); return loadWithScriptTag(); } // ESModule case. If registerCustomNodes is defined, we know it's an ESModule. const registerCustomNodes = customNodeModule?.registerCustomNodes ?? customNodeModule?.default?.registerCustomNodes; if (registerCustomNodes != null) { let called = false; let resolved = false; let timedOut = false; const register = (info) => { if (resolved) { throw new Error(`Custom node script "${url}"'s registerCustomNodes function called 'register' after resolving.`); } if (timedOut) { throw new Error(`Custom node script "${url}"'s registerCustomNodes function called 'register' after timing out.`); } // Pass 'false' for 'isLastNode' because the ESModule case tracks load status // without using the global 'visualblocks_registerCustomNodeCalled' variable. visualblocks.registerCustomNodes(info, false); called = true; }; // Wait for the custom node script to register its nodes or the timeout to expire. await Promise.race([ // Support async and sync functions by wrapping in an async function. (async () => registerCustomNodes(register))().then(() => { if (!timedOut) { resolved = true; } }), sleep(LOAD_TIMEOUT).then(() => { if (!resolved) { timedOut = true; } throw new Error(TIMEOUT_MESSAGE); }) ]); if (!called) { // Do not fall back to script tag because we are sure this is an ESModule. throw new Error(`Custom node script "${url}"'s registerCustomNodes function resolved without calling 'register'`); } } else { try { await pollScriptLoaded(); } catch (e) { // We are not sure if this is an ESModule or not. Fall back to script tag. console.warn(e); console.warn(`Failed to load "${url}" with an ESModules async import. Falling back to a script tag.`); return loadWithScriptTag(); } } } }; </script> <app-root> <div class="app-loading-msg">Please wait...</div> </app-root> <script src="app_bundle_v27.js"></script> <!-- Import script for Cookie Consent Banner: go/glue/users/components/cookie-consent-banner --> <script data-autoload-cookie-consent-bar="true" src="https://www.gstatic.com/brandstudio/kato/cookie_choice_component/cookie_consent_bar.v3.js" ></script> </body> </html>