import { BlockDefinition } from '../../types/blocks'; export const WEB_BLOCKS: BlockDefinition[] = [ // ============================================================ // โšก EVENTS โ€” "when something happens, do something" // ============================================================ { id: 'js_event_listener', type: 'js_events', label: 'when element', category: 'events', description: 'When you click an element on the page, run the code inside', framework: 'web', shape: 'hat', color: '#FFD500', icon: 'โšก', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'selector', label: 'element', type: 'element', default: '#myButton' }, { id: 'event', label: 'event type', type: 'select', default: 'click', options: [ { label: 'clicked', value: 'click' }, { label: 'double click', value: 'dblclick' }, { label: 'hovered', value: 'mouseenter' }, { label: 'mouse leave', value: 'mouseleave' }, { label: 'key press', value: 'keydown' }, { label: 'key release', value: 'keyup' }, { label: 'change', value: 'change' }, { label: 'type', value: 'input' }, { label: 'form submit', value: 'submit' }, { label: 'focus', value: 'focus' }, { label: 'blur', value: 'blur' }, { label: 'scroll', value: 'scroll' }, ]}, ], compile: (cfg, inputs) => `document.querySelector('${cfg.selector || '#myButton'}').addEventListener('${cfg.event || 'click'}', () => {\n${inputs.handler || ''}\n});`, }, { id: 'js_window_event', type: 'js_events', label: 'when', category: 'events', description: 'Run code when the selected event occurs', framework: 'web', shape: 'hat', color: '#FFD500', icon: '๐ŸŒ', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'event', label: 'when', type: 'select', default: 'DOMContentLoaded', options: [ { label: 'DOM ready', value: 'DOMContentLoaded' }, { label: 'page fully loaded', value: 'load' }, { label: 'window resized', value: 'resize' }, { label: 'page scrolled', value: 'scroll' }, ]}, ], compile: (cfg, inputs) => `window.addEventListener('${cfg.event || 'DOMContentLoaded'}', () => {\n${inputs.handler || ''}\n});`, }, { id: 'js_keyboard_event', type: 'js_events', label: 'when key', category: 'events', description: 'Run code when a keyboard action happens', framework: 'web', shape: 'hat', color: '#FFD500', icon: 'โŒจ', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'key', label: 'key', type: 'text', default: 'Enter', placeholder: 'Enter, Space, a, etc.' }, { id: 'eventType', label: 'action', type: 'select', default: 'keydown', options: [ { label: 'pressed down', value: 'keydown' }, { label: 'released', value: 'keyup' }, ]}, ], compile: (cfg, inputs) => `document.addEventListener('${cfg.eventType || 'keydown'}', (e) => {\n if (e.key === '${cfg.key || 'Enter'}') {\n${inputs.handler || ''}\n }\n});`, }, { id: 'js_mouse_event', type: 'js_events', label: 'when mouse', category: 'events', description: 'Run code when the mouse is clicked anywhere on the page', framework: 'web', shape: 'hat', color: '#FFD500', icon: '๐Ÿ–ฑ', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'button', label: 'button clicked', type: 'select', default: '0', options: [ { label: 'left button', value: '0' }, { label: 'middle button', value: '1' }, { label: 'right button', value: '2' }, ]}, ], compile: (cfg, inputs) => `document.addEventListener('click', (e) => {\n if (e.button === ${cfg.button || 0}) {\n${inputs.handler || ''}\n }\n});`, }, { id: 'js_form_submit', type: 'js_events', label: 'when form submitted', category: 'events', description: 'Run code when a form is submitted', framework: 'web', shape: 'hat', color: '#FFD500', icon: '๐Ÿ“‹', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'selector', label: 'form', type: 'element', default: '#myForm' }, ], compile: (cfg, inputs) => `document.querySelector('${cfg.selector || '#myForm'}').addEventListener('submit', (e) => {\n e.preventDefault();\n${inputs.handler || ''}\n});`, }, { id: 'js_input_change', type: 'js_events', label: 'when input changes', category: 'events', description: 'Run code when a text input field changes', framework: 'web', shape: 'hat', color: '#FFD500', icon: 'โœ', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'selector', label: 'input', type: 'element', default: '#myInput' }, ], compile: (cfg, inputs) => `document.querySelector('${cfg.selector || '#myInput'}').addEventListener('input', () => {\n${inputs.handler || ''}\n});`, }, { id: 'js_timer_timeout', type: 'js_events', label: 'wait then run', category: 'events', description: 'Wait some seconds then run code once', framework: 'web', shape: 'hat', color: '#FFD500', icon: 'โฐ', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'seconds', label: 'seconds', type: 'number', default: 1 }, ], compile: (cfg, inputs) => `setTimeout(() => {\n${inputs.handler || ''}\n}, ${(cfg.seconds ?? 1) * 1000});`, }, { id: 'js_timer_interval', type: 'js_events', label: 'every N seconds', category: 'events', description: 'Run code over and over, every N seconds', framework: 'web', shape: 'hat', color: '#FFD500', icon: '๐Ÿ”„', inputs: [{ id: 'handler', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'seconds', label: 'seconds', type: 'number', default: 1 }, ], compile: (cfg, inputs) => `setInterval(() => {\n${inputs.handler || ''}\n}, ${(cfg.seconds ?? 1) * 1000});`, }, // ============================================================ // ๐ŸŒ BROWSER โ€” "navigate, reload, open tabs" // ============================================================ { id: 'js_open_tab', type: 'js_browser', label: 'open URL in new tab', category: 'browser', description: 'Open a website in a new browser tab', framework: 'web', shape: 'stack', color: '#FF7043', icon: '๐ŸŒ', inputs: [], outputs: [], config: [ { id: 'url', label: 'URL', type: 'text', default: 'https://example.com' }, ], compile: (cfg) => `window.open('${cfg.url || 'https://example.com'}', '_blank');`, }, { id: 'js_reload', type: 'js_browser', label: 'reload page', category: 'browser', description: 'Reload the current page', framework: 'web', shape: 'stack', color: '#FF7043', icon: '๐Ÿ”„', inputs: [], outputs: [], config: [], compile: () => 'location.reload();', }, { id: 'js_go_back', type: 'js_browser', label: 'go back', category: 'browser', description: 'Go to the previous page', framework: 'web', shape: 'stack', color: '#FF7043', icon: 'โฌ…', inputs: [], outputs: [], config: [], compile: () => 'history.back();', }, { id: 'js_go_forward', type: 'js_browser', label: 'go forward', category: 'browser', description: 'Go to the next page (if you went back)', framework: 'web', shape: 'stack', color: '#FF7043', icon: 'โžก', inputs: [], outputs: [], config: [], compile: () => 'history.forward();', }, { id: 'js_set_url', type: 'js_browser', label: 'go to URL', category: 'browser', description: 'Navigate to a different page', framework: 'web', shape: 'stack', color: '#FF7043', icon: '๐Ÿ”—', inputs: [], outputs: [], config: [ { id: 'url', label: 'URL', type: 'text', default: 'https://example.com' }, ], compile: (cfg) => `window.location.href = '${cfg.url || 'https://example.com'}';`, }, // ============================================================ // ๐Ÿ”Œ WEBSOCKET โ€” "real-time connections" // ============================================================ { id: 'js_websocket_connect', type: 'js_websocket', label: 'connect to WebSocket', category: 'websocket', description: 'Connect to a WebSocket server for real-time messages', framework: 'web', shape: 'stack', color: '#22d3ee', icon: '๐Ÿ”Œ', inputs: [{ id: 'onMessage', label: 'when message arrives', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'url', label: 'server URL', type: 'text', default: 'ws://localhost:8080' }, ], compile: (cfg, inputs) => `const ws = new WebSocket('${cfg.url || 'ws://localhost:8080'}');\nws.onmessage = (event) => {\n const data = JSON.parse(event.data);\n${inputs.onMessage || ''}\n};`, }, { id: 'js_websocket_send', type: 'js_websocket', label: 'send via WebSocket', category: 'websocket', description: 'Send a message through a WebSocket connection', framework: 'web', shape: 'stack', color: '#22d3ee', icon: '๐Ÿ“ค', inputs: [], outputs: [], config: [ { id: 'data', label: 'data to send', type: 'text', default: '{ "message": "hello" }' }, ], compile: (cfg) => `ws.send(JSON.stringify(${cfg.data || '{}'}));`, }, { id: 'js_websocket_close', type: 'js_websocket', label: 'close WebSocket', category: 'websocket', description: 'Close a WebSocket connection', framework: 'web', shape: 'stack', color: '#22d3ee', icon: '๐Ÿ”Œ', inputs: [], outputs: [], config: [], compile: () => 'ws.close();', }, // ============================================================ // ๐“ง VARIABLES โ€” "remember a value" // ============================================================ { id: 'js_var', type: 'js_variables', label: 'make variable name', category: 'variables', description: 'Create a new variable that remembers a value. Pick a name and give it a starting value.', framework: 'web', shape: 'stack', color: '#FF8C1A', icon: '๐“ง', inputs: [{ id: 'value', label: '=', type: 'any', position: 'left' }], outputs: [], config: [ { id: 'name', label: 'name', type: 'text', default: 'myVar' }, { id: 'varType', label: 'type', type: 'select', default: 'any', options: [ { label: 'Any', value: 'any' }, { label: 'Text', value: 'string' }, { label: 'Number', value: 'number' }, { label: 'True/False', value: 'boolean' }, { label: 'List', value: 'array' }, ]}, ], compile: (cfg, inputs) => { const defVal = inputs.value || (cfg.varType === 'number' ? '0' : cfg.varType === 'boolean' ? 'false' : cfg.varType === 'array' ? '[]' : cfg.varType === 'string' ? "''" : 'undefined'); return `let ${cfg.name || 'myVar'} = ${defVal};`; }, }, { id: 'js_set_var', type: 'js_variables', label: 'set variable = value', category: 'variables', description: 'Change what a variable remembers', framework: 'web', shape: 'stack', color: '#FF8C1A', icon: 'โœŽ', inputs: [{ id: 'value', label: '=', type: 'any', position: 'left' }], outputs: [], config: [ { id: 'name', label: 'variable', type: 'variable', default: 'myVar' }, ], compile: (cfg, inputs) => `${cfg.name || 'myVar'} = ${inputs.value || 'undefined'};`, }, { id: 'js_var_get', type: 'js_variables', label: 'get variable', category: 'variables', description: 'Get the value a variable remembers', framework: 'web', shape: 'reporter', color: '#FF8C1A', icon: '๐“ง', inputs: [], outputs: [{ id: 'value', label: '', type: 'any', position: 'right' }], config: [ { id: 'name', label: 'variable', type: 'variable', default: 'myVar' }, ], compile: (cfg) => `${cfg.name || 'myVar'}`, }, { id: 'js_change_var', type: 'js_variables', label: 'change variable by N', category: 'variables', description: 'Add a number to a variable', framework: 'web', shape: 'stack', color: '#FF8C1A', icon: 'โž•', inputs: [], outputs: [], config: [ { id: 'name', label: 'variable', type: 'variable', default: 'myVar' }, { id: 'amount', label: 'by', type: 'number', default: 1 }, ], compile: (cfg) => `${cfg.name || 'myVar'} += ${cfg.amount ?? 1};`, }, { id: 'js_get_var', type: 'js_variables', label: 'variable value', category: 'variables', description: 'Get the value a variable remembers', framework: 'web', shape: 'reporter', color: '#FF8C1A', icon: '๐“ง', inputs: [], outputs: [{ id: 'value', label: '', type: 'any', position: 'right' }], config: [ { id: 'name', label: 'variable', type: 'variable', default: 'myVar' }, ], compile: (cfg) => cfg.name || 'myVar', }, { id: 'js_const_string', type: 'js_variables', label: 'text hello', category: 'variables', description: 'A piece of text like "hello" or "hi there"', framework: 'web', shape: 'reporter', color: '#FF8C1A', icon: '๐“', inputs: [], outputs: [{ id: 'value', label: '', type: 'string', position: 'right' }], config: [ { id: 'value', label: 'text', type: 'text', default: 'hello' }, ], compile: (cfg) => JSON.stringify(cfg.value || ''), }, { id: 'js_const_number', type: 'js_variables', label: 'number 0', category: 'variables', description: 'A number like 5 or 100', framework: 'web', shape: 'reporter', color: '#FF8C1A', icon: '#', inputs: [], outputs: [{ id: 'value', label: '', type: 'number', position: 'right' }], config: [ { id: 'value', label: 'number', type: 'number', default: 0 }, ], compile: (cfg) => String(cfg.value ?? 0), }, { id: 'js_const_boolean', type: 'js_variables', label: 'true false', category: 'variables', description: 'A true/false (yes/no) value', framework: 'web', shape: 'boolean', color: '#FF8C1A', icon: 'โ—‡', inputs: [], outputs: [{ id: 'value', label: '', type: 'boolean', position: 'right' }], config: [ { id: 'value', label: 'value', type: 'select', default: 'true', options: [ { label: 'true', value: 'true' }, { label: 'false', value: 'false' }, ]}, ], compile: (cfg) => cfg.value || 'false', }, { id: 'js_null', type: 'js_variables', label: 'null', category: 'variables', description: 'An empty value that means nothing', framework: 'web', shape: 'reporter', color: '#FF8C1A', icon: 'โˆ…', inputs: [], outputs: [{ id: 'value', label: '', type: 'any', position: 'right' }], config: [], compile: () => 'null', }, // ============================================================ // โ—‡ LOGIC โ€” "make decisions" // ============================================================ { id: 'js_if', type: 'js_logic', label: 'if condition then else run', category: 'logic', description: 'If the condition is true, run the first code. Otherwise run the second.', framework: 'web', shape: 'c-block', color: '#4A97FF', icon: 'โ—‡', inputs: [ { id: 'condition', label: 'if this is true', type: 'boolean', position: 'left', shape: 'diamond' }, { id: 'bodyThen', label: 'run this', type: 'code', position: 'bottom' }, { id: 'bodyElse', label: 'otherwise run this', type: 'code', position: 'bottom' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `if (${inputs.condition || 'true'}) {\n${inputs.bodyThen || ''}\n} else {\n${inputs.bodyElse || ''}\n}`, }, { id: 'js_if_then', type: 'js_logic', label: 'if condition then run', category: 'logic', description: 'If the condition is true, run some code', framework: 'web', shape: 'c-block', color: '#4A97FF', icon: 'โ—‡', inputs: [ { id: 'condition', label: 'if this is true', type: 'boolean', position: 'left', shape: 'diamond' }, { id: 'body', label: 'run this', type: 'code', position: 'bottom' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `if (${inputs.condition || 'true'}) {\n${inputs.body || ''}\n}`, }, { id: 'js_and', type: 'js_logic', label: 'and', category: 'logic', description: 'True only if both sides are true', framework: 'web', shape: 'boolean', color: '#4A97FF', icon: 'โˆง', inputs: [ { id: 'a', label: '', type: 'boolean', position: 'left', shape: 'diamond' }, { id: 'b', label: '', type: 'boolean', position: 'left', shape: 'diamond' }, ], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.a || 'true'} && ${inputs.b || 'true'})`, }, { id: 'js_or', type: 'js_logic', label: 'or', category: 'logic', description: 'True if either side is true', framework: 'web', shape: 'boolean', color: '#4A97FF', icon: 'โˆจ', inputs: [ { id: 'a', label: '', type: 'boolean', position: 'left', shape: 'diamond' }, { id: 'b', label: '', type: 'boolean', position: 'left', shape: 'diamond' }, ], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.a || 'true'} || ${inputs.b || 'true'})`, }, { id: 'js_not', type: 'js_logic', label: 'not', category: 'logic', description: 'Flip true to false, or false to true', framework: 'web', shape: 'boolean', color: '#4A97FF', icon: 'ยฌ', inputs: [{ id: 'value', label: '', type: 'boolean', position: 'left', shape: 'diamond' }], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [], compile: (_cfg, inputs) => `!(${inputs.value || 'true'})`, }, { id: 'js_comparison', type: 'js_logic', label: 'compare', category: 'logic', description: 'Compare two values (equal, not equal, bigger, smaller)', framework: 'web', shape: 'boolean', color: '#4A97FF', icon: 'โ‰Ÿ', inputs: [ { id: 'a', label: '', type: 'any', position: 'left' }, { id: 'b', label: '', type: 'any', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [ { id: 'operator', label: 'compare', type: 'select', default: '===', options: [ { label: '=', value: '===' }, { label: 'โ‰ ', value: '!==' }, { label: '>', value: '>' }, { label: '<', value: '<' }, { label: 'โ‰ฅ', value: '>=' }, { label: 'โ‰ค', value: '<=' }, ]}, ], compile: (cfg, inputs) => `(${inputs.a || 'a'} ${cfg.operator || '==='} ${inputs.b || 'b'})`, }, { id: 'js_is_null', type: 'js_logic', label: 'is null?', category: 'logic', description: 'Check if a value is null (nothing)', framework: 'web', shape: 'boolean', color: '#4A97FF', icon: 'โˆ…', inputs: [{ id: 'value', label: '', type: 'any', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.value || 'null'} === null)`, }, { id: 'js_ternary', type: 'js_logic', label: 'if true use A else use B', category: 'logic', description: 'Pick one of two values based on a condition', framework: 'web', shape: 'reporter', color: '#4A97FF', icon: 'โ—‡', inputs: [ { id: 'condition', label: 'if', type: 'boolean', position: 'left', shape: 'diamond' }, { id: 'a', label: 'then use', type: 'any', position: 'left' }, { id: 'b', label: 'else use', type: 'any', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'any', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.condition || 'true'} ? ${inputs.a || 'null'} : ${inputs.b || 'null'})`, }, // ============================================================ // โ†ป LOOPS โ€” "do something many times" // ============================================================ { id: 'js_for', type: 'js_loops', label: 'repeat N times', category: 'loops', description: 'Run the code inside a number of times', framework: 'web', shape: 'c-block', color: '#4CAF50', icon: 'โ†ป', inputs: [{ id: 'body', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'times', label: 'times', type: 'number', default: 10 }, ], compile: (cfg, inputs) => `for (let i = 0; i < ${cfg.times ?? 10}; i++) {\n${inputs.body || ''}\n}`, }, { id: 'js_while', type: 'js_loops', label: 'repeat while true', category: 'loops', description: 'Keep running the code as long as the condition is true', framework: 'web', shape: 'c-block', color: '#4CAF50', icon: 'โ†ป', inputs: [ { id: 'condition', label: 'while this is true', type: 'boolean', position: 'left', shape: 'diamond' }, { id: 'body', label: 'run', type: 'code', position: 'bottom' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `while (${inputs.condition || 'true'}) {\n${inputs.body || ''}\n}`, }, { id: 'js_for_of', type: 'js_loops', label: 'for each item in array', category: 'loops', description: 'Run code for each item in a list', framework: 'web', shape: 'c-block', color: '#4CAF50', icon: 'โ†ป', inputs: [{ id: 'body', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'variable', label: 'item name', type: 'text', default: 'item' }, { id: 'array', label: 'array name', type: 'text', default: 'myArray' }, ], compile: (cfg, inputs) => `for (const ${cfg.variable || 'item'} of ${cfg.array || 'myArray'}) {\n${inputs.body || ''}\n}`, }, { id: 'js_break', type: 'js_loops', label: 'stop loop now', category: 'loops', description: 'Exit the loop early', framework: 'web', shape: 'cap', color: '#4CAF50', icon: 'โ– ', inputs: [], outputs: [], config: [], compile: () => 'break;', }, { id: 'js_continue', type: 'js_loops', label: 'skip to next loop', category: 'loops', description: 'Skip the rest of this loop and go to the next one', framework: 'web', shape: 'cap', color: '#4CAF50', icon: 'โญ', inputs: [], outputs: [], config: [], compile: () => 'continue;', }, // ============================================================ // โˆ‘ MATH โ€” "do math" // ============================================================ { id: 'js_math_op', type: 'js_math', label: 'a + b', category: 'math', description: 'Add, subtract, multiply, or divide two numbers', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: '+', inputs: [ { id: 'a', label: '', type: 'number', position: 'left' }, { id: 'b', label: '', type: 'number', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [ { id: 'op', label: 'operator', type: 'select', default: '+', options: [ { label: '+', value: '+' }, { label: '-', value: '-' }, { label: 'ร—', value: '*' }, { label: 'รท', value: '/' }, { label: '^ power', value: '**' }, { label: '% remainder', value: '%' }, ]}, ], compile: (cfg, inputs) => `(${inputs.a || '0'} ${cfg.op || '+'} ${inputs.b || '0'})`, }, { id: 'js_math_random', type: 'js_math', label: 'random from A to B', category: 'math', description: 'Pick a random whole number between two numbers', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: '๐ŸŽฒ', inputs: [], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [ { id: 'min', label: 'from', type: 'number', default: 1 }, { id: 'max', label: 'to', type: 'number', default: 10 }, ], compile: (cfg) => `Math.floor(Math.random() * (${cfg.max ?? 10} - ${cfg.min ?? 1} + 1)) + ${cfg.min ?? 1}`, }, { id: 'js_math_round', type: 'js_math', label: 'round number', category: 'math', description: 'Round a number up, down, or to the nearest whole number', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: 'โ‰ˆ', inputs: [{ id: 'value', label: '', type: 'number', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [ { id: 'method', label: 'method', type: 'select', default: 'round', options: [ { label: 'round to nearest', value: 'round' }, { label: 'round down', value: 'floor' }, { label: 'round up', value: 'ceil' }, ]}, ], compile: (cfg, inputs) => `Math.${cfg.method || 'round'}(${inputs.value || '0'})`, }, { id: 'js_math_min', type: 'js_math', label: 'smallest of A and B', category: 'math', description: 'Get the smallest number', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: 'โ†“', inputs: [ { id: 'a', label: '', type: 'number', position: 'left' }, { id: 'b', label: '', type: 'number', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [], compile: (_cfg, inputs) => `Math.min(${inputs.a || '0'}, ${inputs.b || '0'})`, }, { id: 'js_math_max', type: 'js_math', label: 'biggest of A and B', category: 'math', description: 'Get the biggest number', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: 'โ†‘', inputs: [ { id: 'a', label: '', type: 'number', position: 'left' }, { id: 'b', label: '', type: 'number', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [], compile: (_cfg, inputs) => `Math.max(${inputs.a || '0'}, ${inputs.b || '0'})`, }, { id: 'js_math_abs', type: 'js_math', label: 'absolute value of', category: 'math', description: 'Make a number positive (distance from zero)', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: '| |', inputs: [{ id: 'value', label: '', type: 'number', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [], compile: (_cfg, inputs) => `Math.abs(${inputs.value || '0'})`, }, { id: 'js_math_sqrt', type: 'js_math', label: 'square root of', category: 'math', description: 'Get the square root of a number', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: 'โˆš', inputs: [{ id: 'value', label: '', type: 'number', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [], compile: (_cfg, inputs) => `Math.sqrt(${inputs.value || '0'})`, }, { id: 'js_math_pi', type: 'js_math', label: 'ฯ€ pi', category: 'math', description: 'The number pi (3.14159...)', framework: 'web', shape: 'reporter', color: '#00BCD4', icon: 'ฯ€', inputs: [], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [], compile: () => 'Math.PI', }, // ============================================================ // ๐“ TEXT โ€” "work with words" // ============================================================ { id: 'js_string_concat', type: 'js_text', label: 'join text A and B', category: 'text', description: 'Join two pieces of text together', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: '๐“', inputs: [ { id: 'a', label: '', type: 'string', position: 'left' }, { id: 'b', label: '', type: 'string', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.a || "''"} + ${inputs.b || "''"})`, }, { id: 'js_string_append', type: 'js_text', label: 'add text to end', category: 'text', description: 'Add text to the end of a value (like +=)', framework: 'web', shape: 'stack', color: '#AB47BC', icon: 'โž•', inputs: [{ id: 'value', label: 'add', type: 'string', position: 'left' }], outputs: [], config: [ { id: 'target', label: 'target', type: 'text', default: 'myVar' }, ], compile: (cfg, inputs) => `${cfg.target || 'myVar'} += ${inputs.value || "''"};`, }, { id: 'js_string_length', type: 'js_text', label: 'length of text', category: 'text', description: 'Count how many characters are in the text', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: '๐Ÿ“', inputs: [{ id: 'string', label: '', type: 'string', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.string || "''"}).length`, }, { id: 'js_string_uppercase', type: 'js_text', label: 'make UPPERCASE', category: 'text', description: 'Convert text to ALL CAPITAL LETTERS', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: 'โ–ฒ', inputs: [{ id: 'string', label: '', type: 'string', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.string || "''"}).toUpperCase()`, }, { id: 'js_string_lowercase', type: 'js_text', label: 'make lowercase', category: 'text', description: 'Convert text to all lowercase letters', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: 'โ–ผ', inputs: [{ id: 'string', label: '', type: 'string', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.string || "''"}).toLowerCase()`, }, { id: 'js_string_contains', type: 'js_text', label: 'text contains word?', category: 'text', description: 'Check if some text contains a word or letter', framework: 'web', shape: 'boolean', color: '#AB47BC', icon: '๐Ÿ”', inputs: [ { id: 'string', label: 'text', type: 'string', position: 'left' }, { id: 'substr', label: 'contains', type: 'string', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.string || "''"}).includes(${inputs.substr || "''"})`, }, { id: 'js_string_starts', type: 'js_text', label: 'text starts with?', category: 'text', description: 'Check if text starts with certain letters', framework: 'web', shape: 'boolean', color: '#AB47BC', icon: '๐Ÿ…ฐ', inputs: [ { id: 'string', label: 'text', type: 'string', position: 'left' }, { id: 'prefix', label: 'starts with', type: 'string', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.string || "''"}).startsWith(${inputs.prefix || "''"})`, }, { id: 'js_string_trim', type: 'js_text', label: 'trim spaces', category: 'text', description: 'Remove extra spaces from the start and end of text', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: 'โœ‚', inputs: [{ id: 'string', label: '', type: 'string', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [], compile: (_cfg, inputs) => `(${inputs.string || "''"}).trim()`, }, { id: 'js_string_char_at', type: 'js_text', label: 'letter at position', category: 'text', description: 'Get the letter at a certain spot in the text', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: '๐Ÿ”ค', inputs: [ { id: 'string', label: 'text', type: 'string', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [ { id: 'index', label: 'position', type: 'number', default: 0 }, ], compile: (cfg, inputs) => `(${inputs.string || "''"}).charAt(${cfg.index ?? 0})`, }, { id: 'js_string_split', type: 'js_text', label: 'split text by separator', category: 'text', description: 'Split text into a list, using a separator', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: 'โŠ˜', inputs: [ { id: 'string', label: 'text', type: 'string', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'array', position: 'right' }], config: [ { id: 'separator', label: 'separator', type: 'text', default: ',' }, ], compile: (cfg, inputs) => `(${inputs.string || "''"}).split('${cfg.separator || ','}')`, }, { id: 'js_string_replace', type: 'js_text', label: 'find and replace', category: 'text', description: 'Replace a word or letter in text with something else', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: '๐Ÿ”„', inputs: [{ id: 'string', label: 'text', type: 'string', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [ { id: 'search', label: 'find', type: 'text', default: 'old' }, { id: 'replace', label: 'replace with', type: 'text', default: 'new' }, ], compile: (cfg, inputs) => `(${inputs.string || "''"}).replaceAll('${cfg.search || 'old'}', '${cfg.replace || 'new'}')`, }, { id: 'js_template_literal', type: 'js_text', label: 'template text', category: 'text', description: 'Combine text and variables in one piece of text. Use $ variableName to put a variable inside.', framework: 'web', shape: 'reporter', color: '#AB47BC', icon: '``', inputs: [], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [ { id: 'template', label: 'text', type: 'text', default: 'Hello, ${name}!' }, ], compile: (cfg) => `\`${cfg.template || ''}\``, }, // ============================================================ // โš™ FUNCTIONS โ€” "make your own command" // ============================================================ { id: 'js_function', type: 'js_functions', label: 'make function name', category: 'functions', description: 'Create a reusable command that you can run later', framework: 'web', shape: 'hat', color: '#E91E63', icon: 'ฦ’', inputs: [{ id: 'body', label: 'run', type: 'code', position: 'bottom' }], outputs: [], config: [ { id: 'name', label: 'name', type: 'text', default: 'myFunction' }, { id: 'params', label: 'inputs', type: 'text', placeholder: 'a, b' }, { id: 'asyncFn', label: 'async', type: 'boolean', default: false }, ], compile: (cfg, inputs) => { const asyncKw = cfg.asyncFn === 'true' || cfg.asyncFn === 'TRUE' || cfg.asyncFn === true ? 'async ' : ''; return `${asyncKw}function ${cfg.name || 'myFunction'}(${cfg.params || ''}) {\n${inputs.body || ''}\n}`; }, }, { id: 'js_arrow_fn', type: 'js_functions', label: 'make arrow function', category: 'functions', description: 'Create a short function with =>', framework: 'web', shape: 'reporter', color: '#E91E63', icon: 'โ‡’', inputs: [], outputs: [{ id: 'result', label: '', type: 'any', position: 'right' }], config: [ { id: 'params', label: 'inputs', type: 'text', default: 'x' }, { id: 'body', label: 'body', type: 'text', default: 'x * 2' }, ], compile: (cfg) => `((${cfg.params || 'x'}) => ${cfg.body || 'x'})`, }, { id: 'js_call_fn', type: 'js_functions', label: 'call function name', category: 'functions', description: 'Run a function you made earlier', framework: 'web', shape: 'stack', color: '#E91E63', icon: 'โ–ถ', inputs: [], outputs: [], config: [ { id: 'name', label: 'function', type: 'text', default: 'myFunction' }, { id: 'args', label: 'with inputs', type: 'text', placeholder: 'arg1, arg2' }, ], compile: (cfg) => `${cfg.name || 'myFunction'}(${cfg.args || ''});`, }, { id: 'js_call_fn_value', type: 'js_functions', label: 'function call result', category: 'functions', description: 'Run a function and use its returned value', framework: 'web', shape: 'reporter', color: '#E91E63', icon: 'ฦ’', inputs: [], outputs: [{ id: 'result', label: '', type: 'any', position: 'right' }], config: [ { id: 'name', label: 'function', type: 'text', default: 'myFunction' }, { id: 'args', label: 'inputs', type: 'text', placeholder: 'arg1, arg2' }, ], compile: (cfg) => `${cfg.name || 'myFunction'}(${cfg.args || ''})`, }, { id: 'js_return', type: 'js_functions', label: 'return value', category: 'functions', description: 'Give back a value from a function and stop', framework: 'web', shape: 'cap', color: '#E91E63', icon: 'โ†ฉ', inputs: [{ id: 'value', label: '', type: 'any', position: 'left' }], outputs: [], config: [], compile: (_cfg, inputs) => `return ${inputs.value || ''};`, }, { id: 'js_return_void', type: 'js_functions', label: 'stop function', category: 'functions', description: 'Stop running the function early (no return value)', framework: 'web', shape: 'cap', color: '#E91E63', icon: 'โ– ', inputs: [], outputs: [], config: [], compile: () => 'return;', }, // ============================================================ // โ—‰ ELEMENTS (DOM) โ€” "find and change things on the page" // ============================================================ { id: 'js_get_element', type: 'js_dom', label: 'find element by CSS', category: 'dom', description: 'Find one element on the page by its CSS selector (like #id or .class)', framework: 'web', shape: 'reporter', color: '#607D8B', icon: '๐Ÿ”', inputs: [], outputs: [{ id: 'element', label: '', type: 'element', position: 'right' }], config: [ { id: 'selector', label: 'CSS selector', type: 'element', default: '#myElement' }, ], compile: (cfg) => `document.querySelector('${cfg.selector || '#myElement'}')`, }, { id: 'js_get_elements', type: 'js_dom', label: 'find all elements', category: 'dom', description: 'Find ALL elements matching a CSS selector', framework: 'web', shape: 'reporter', color: '#607D8B', icon: 'โ—Ž', inputs: [], outputs: [{ id: 'elements', label: '', type: 'array', position: 'right' }], config: [ { id: 'selector', label: 'CSS selector', type: 'element', default: '.item' }, ], compile: (cfg) => `document.querySelectorAll('${cfg.selector || '.item'}')`, }, { id: 'js_create_element', type: 'js_dom', label: 'make new element', category: 'dom', description: 'Create a brand new HTML element', framework: 'web', shape: 'reporter', color: '#607D8B', icon: 'โœจ', inputs: [], outputs: [{ id: 'element', label: '', type: 'element', position: 'right' }], config: [ { id: 'tag', label: 'tag', type: 'text', default: 'div' }, ], compile: (cfg) => `document.createElement('${cfg.tag || 'div'}')`, }, { id: 'js_append_child', type: 'js_dom', label: 'put inside', category: 'dom', description: 'Put one element inside another element', framework: 'web', shape: 'stack', color: '#607D8B', icon: 'โŠž', inputs: [ { id: 'parent', label: 'element', type: 'element', position: 'left' }, { id: 'child', label: 'put this inside', type: 'element', position: 'left' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.parent || 'parent'}.appendChild(${inputs.child || 'child'});`, }, { id: 'js_remove_element', type: 'js_dom', label: 'remove element', category: 'dom', description: 'Remove an element from the page', framework: 'web', shape: 'stack', color: '#607D8B', icon: 'โœ•', inputs: [{ id: 'element', label: '', type: 'element', position: 'left' }], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.element || 'element'}.remove();`, }, { id: 'js_set_text', type: 'js_dom', label: 'change text to', category: 'dom', description: 'Change the text inside an element', framework: 'web', shape: 'stack', color: '#607D8B', icon: 'T', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, { id: 'text', label: 'text', type: 'string', position: 'left' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.element || 'element'}.textContent = ${inputs.text || "''"};`, }, { id: 'js_get_text', type: 'js_dom', label: 'get text', category: 'dom', description: 'Get the text from inside an element', framework: 'web', shape: 'reporter', color: '#607D8B', icon: 'T', inputs: [{ id: 'element', label: 'element', type: 'element', position: 'left' }], outputs: [{ id: 'text', label: '', type: 'string', position: 'right' }], config: [], compile: (_cfg, inputs) => `${inputs.element || 'element'}.textContent`, }, { id: 'js_set_html', type: 'js_dom', label: 'change HTML to', category: 'dom', description: 'Set the inner HTML of an element (can include bold tags)', framework: 'web', shape: 'stack', color: '#607D8B', icon: '', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, { id: 'html', label: 'HTML', type: 'string', position: 'left' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.element || 'element'}.innerHTML = ${inputs.html || "''"};`, }, { id: 'js_get_html', type: 'js_dom', label: 'get HTML', category: 'dom', description: 'Get the inner HTML of an element', framework: 'web', shape: 'reporter', color: '#607D8B', icon: '', inputs: [{ id: 'element', label: 'element', type: 'element', position: 'left' }], outputs: [{ id: 'html', label: '', type: 'string', position: 'right' }], config: [], compile: (_cfg, inputs) => `${inputs.element || 'element'}.innerHTML`, }, { id: 'js_set_attr', type: 'js_dom', label: 'set attribute to', category: 'dom', description: 'Set an attribute (like src, href, class) on an element', framework: 'web', shape: 'stack', color: '#607D8B', icon: 'โš™', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, { id: 'value', label: 'to', type: 'string', position: 'left' }, ], outputs: [], config: [ { id: 'attr', label: 'attribute', type: 'text', default: 'class' }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.setAttribute('${cfg.attr || 'class'}', ${inputs.value || "''"});`, }, { id: 'js_get_attr', type: 'js_dom', label: 'get attribute', category: 'dom', description: 'Get the value of an attribute on an element', framework: 'web', shape: 'reporter', color: '#607D8B', icon: 'โš™', inputs: [{ id: 'element', label: 'element', type: 'element', position: 'left' }], outputs: [{ id: 'value', label: '', type: 'string', position: 'right' }], config: [ { id: 'attr', label: 'attribute', type: 'text', default: 'href' }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.getAttribute('${cfg.attr || 'href'}')`, }, { id: 'js_clone_element', type: 'js_dom', label: 'clone element', category: 'dom', description: 'Make a copy of an element', framework: 'web', shape: 'reporter', color: '#607D8B', icon: '๐Ÿ“‹', inputs: [{ id: 'element', label: 'element', type: 'element', position: 'left' }], outputs: [{ id: 'clone', label: '', type: 'element', position: 'right' }], config: [ { id: 'deep', label: 'copy children too?', type: 'boolean', default: true }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.cloneNode(${cfg.deep !== false})`, }, { id: 'js_insert_before', type: 'js_dom', label: 'insert before', category: 'dom', description: 'Insert an element before another element', framework: 'web', shape: 'stack', color: '#607D8B', icon: 'โฌ†', inputs: [ { id: 'newChild', label: 'new element', type: 'element', position: 'left' }, { id: 'refChild', label: 'before', type: 'element', position: 'left' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.refChild || 'ref'}.parentNode.insertBefore(${inputs.newChild || 'newEl'}, ${inputs.refChild || 'ref'});`, }, { id: 'js_replace_element', type: 'js_dom', label: 'replace element', category: 'dom', description: 'Replace an old element with a new one', framework: 'web', shape: 'stack', color: '#607D8B', icon: 'โ‡„', inputs: [ { id: 'newChild', label: 'new element', type: 'element', position: 'left' }, { id: 'oldChild', label: 'replace', type: 'element', position: 'left' }, ], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.oldChild || 'old'}.replaceWith(${inputs.newChild || 'newEl'});`, }, // ============================================================ // ๐ŸŽจ STYLES (CSS) โ€” "change how things look" // ============================================================ { id: 'js_set_style', type: 'js_css', label: 'set style property', category: 'css', description: 'Change a CSS style on an element (like color, size, position)', framework: 'web', shape: 'stack', color: '#3F51B5', icon: '๐ŸŽจ', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, { id: 'value', label: 'to', type: 'string', position: 'left' }, ], outputs: [], config: [ { id: 'property', label: 'style property', type: 'select', default: 'color', options: [ { label: 'text color', value: 'color' }, { label: 'background color', value: 'background' }, { label: 'width', value: 'width' }, { label: 'height', value: 'height' }, { label: 'font size', value: 'fontSize' }, { label: 'font family', value: 'fontFamily' }, { label: 'text align', value: 'textAlign' }, { label: 'margin', value: 'margin' }, { label: 'padding', value: 'padding' }, { label: 'border', value: 'border' }, { label: 'display', value: 'display' }, { label: 'opacity', value: 'opacity' }, { label: 'transform', value: 'transform' }, ]}, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.style.${cfg.property || 'color'} = ${inputs.value || "''"};`, }, { id: 'js_add_class', type: 'js_css', label: 'add CSS class', category: 'css', description: 'Add a CSS class to an element', framework: 'web', shape: 'stack', color: '#3F51B5', icon: '+C', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, ], outputs: [], config: [ { id: 'class', label: 'class', type: 'text', default: 'active' }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.classList.add('${cfg.class || 'active'}');`, }, { id: 'js_remove_class', type: 'js_css', label: 'remove CSS class', category: 'css', description: 'Remove a CSS class from an element', framework: 'web', shape: 'stack', color: '#3F51B5', icon: '-C', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, ], outputs: [], config: [ { id: 'class', label: 'class', type: 'text', default: 'active' }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.classList.remove('${cfg.class || 'active'}');`, }, { id: 'js_toggle_class', type: 'js_css', label: 'toggle CSS class', category: 'css', description: 'Turn a CSS class on/off (if on, turn off; if off, turn on)', framework: 'web', shape: 'stack', color: '#3F51B5', icon: 'โ‡„', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, ], outputs: [], config: [ { id: 'class', label: 'class', type: 'text', default: 'active' }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.classList.toggle('${cfg.class || 'active'}');`, }, { id: 'js_has_class', type: 'js_css', label: 'has CSS class?', category: 'css', description: 'Check if an element has a CSS class', framework: 'web', shape: 'boolean', color: '#3F51B5', icon: 'โ—‡', inputs: [ { id: 'element', label: 'element', type: 'element', position: 'left' }, ], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [ { id: 'class', label: 'class', type: 'text', default: 'active' }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.classList.contains('${cfg.class || 'active'}')`, }, { id: 'js_show_element', type: 'js_css', label: 'show element', category: 'css', description: 'Make a hidden element visible', framework: 'web', shape: 'stack', color: '#3F51B5', icon: '๐Ÿ‘', inputs: [{ id: 'element', label: 'element', type: 'element', position: 'left' }], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.element || 'element'}.style.display = '';`, }, { id: 'js_hide_element', type: 'js_css', label: 'hide element', category: 'css', description: 'Make an element invisible (still takes up space)', framework: 'web', shape: 'stack', color: '#3F51B5', icon: '๐Ÿšซ', inputs: [{ id: 'element', label: 'element', type: 'element', position: 'left' }], outputs: [], config: [], compile: (_cfg, inputs) => `${inputs.element || 'element'}.style.display = 'none';`, }, { id: 'js_set_opacity', type: 'js_css', label: 'set opacity to %', category: 'css', description: 'Make an element see-through (0=invisible, 100=fully visible)', framework: 'web', shape: 'stack', color: '#3F51B5', icon: 'โ˜€', inputs: [{ id: 'element', label: 'element', type: 'element', position: 'left' }], outputs: [], config: [ { id: 'value', label: 'opacity 0-100', type: 'number', default: 50 }, ], compile: (cfg, inputs) => `${inputs.element || 'element'}.style.opacity = ${(cfg.value ?? 50) / 100};`, }, // ============================================================ // โŠž DATA โ€” "save, show, and fetch data" // ============================================================ { id: 'js_console', type: 'js_data', label: 'log to console', category: 'data', description: 'Print a message to the JavaScript console (F12 > Console tab)', framework: 'web', shape: 'stack', color: '#795548', icon: '๐Ÿ–ฅ', inputs: [{ id: 'value', label: '', type: 'any', position: 'left' }], outputs: [], config: [ { id: 'message', label: 'message', type: 'text', default: 'hello' }, ], compile: (cfg, inputs) => `console.log(${inputs.value || JSON.stringify(cfg.message || '')});`, }, { id: 'js_console_warn', type: 'js_data', label: 'log warning', category: 'data', description: 'Print a warning in yellow', framework: 'web', shape: 'stack', color: '#795548', icon: 'โš ', inputs: [{ id: 'value', label: '', type: 'any', position: 'left' }], outputs: [], config: [ { id: 'message', label: 'message', type: 'text', default: 'warning' }, ], compile: (cfg, inputs) => `console.warn(${inputs.value || JSON.stringify(cfg.message || '')});`, }, { id: 'js_console_error', type: 'js_data', label: 'log error', category: 'data', description: 'Print an error in red', framework: 'web', shape: 'stack', color: '#795548', icon: '๐Ÿšซ', inputs: [{ id: 'value', label: '', type: 'any', position: 'left' }], outputs: [], config: [ { id: 'message', label: 'message', type: 'text', default: 'error' }, ], compile: (cfg, inputs) => `console.error(${inputs.value || JSON.stringify(cfg.message || '')});`, }, { id: 'js_alert', type: 'js_data', label: 'show popup message', category: 'data', description: 'Show a popup window with a message', framework: 'web', shape: 'stack', color: '#795548', icon: '๐Ÿ’ฌ', inputs: [{ id: 'message', label: '', type: 'string', position: 'left' }], outputs: [], config: [ { id: 'text', label: 'message', type: 'text', default: 'Hello!' }, ], compile: (cfg, inputs) => `alert(${inputs.message || JSON.stringify(cfg.text || '')});`, }, { id: 'js_confirm', type: 'js_data', label: 'ask yes or no', category: 'data', description: 'Show a popup with OK and Cancel buttons, returns true if OK pressed', framework: 'web', shape: 'reporter', color: '#795548', icon: 'โ“', inputs: [], outputs: [{ id: 'result', label: '', type: 'boolean', position: 'right' }], config: [ { id: 'question', label: 'question', type: 'text', default: 'Are you sure?' }, ], compile: (cfg) => `confirm('${cfg.question || 'Are you sure?'}')`, }, { id: 'js_prompt', type: 'js_data', label: 'ask for text', category: 'data', description: 'Show a popup that asks the user to type something', framework: 'web', shape: 'reporter', color: '#795548', icon: 'โœ', inputs: [], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [ { id: 'question', label: 'question', type: 'text', default: 'What is your name?' }, { id: 'default', label: 'default answer', type: 'text', default: '' }, ], compile: (cfg) => `prompt('${cfg.question || ''}', '${cfg.default || ''}')`, }, { id: 'js_fetch', type: 'js_data', label: 'fetch data from URL', category: 'data', description: 'Get data from a website or API. Returns the response data so you can use it in other blocks.', framework: 'web', shape: 'reporter', color: '#795548', icon: '๐Ÿ“ก', inputs: [], outputs: [{ id: 'data', label: '', type: 'any', position: 'right' }], config: [ { id: 'url', label: 'URL', type: 'text', default: 'https://api.example.com/data' }, { id: 'method', label: 'method', type: 'select', default: 'GET', options: [ { label: 'GET', value: 'GET' }, { label: 'POST', value: 'POST' }, { label: 'PUT', value: 'PUT' }, { label: 'DELETE', value: 'DELETE' }, ]}, { id: 'useAwait', label: 'await?', type: 'boolean', default: true }, ], compile: (cfg) => { const prefix = cfg.useAwait ? 'await ' : ''; return `${prefix}fetch('${cfg.url || ''}', { method: '${cfg.method || 'GET'}' }).then(r => r.json())`; }, }, { id: 'js_local_storage_set', type: 'js_data', label: 'save to storage', category: 'data', description: 'Save a value in the browser so it remembers even after you close the page', framework: 'web', shape: 'stack', color: '#795548', icon: '๐Ÿ’พ', inputs: [{ id: 'value', label: 'value', type: 'string', position: 'left' }], outputs: [], config: [ { id: 'key', label: 'save as', type: 'text', default: 'myKey' }, ], compile: (cfg, inputs) => `localStorage.setItem('${cfg.key || 'myKey'}', ${inputs.value || "''"});`, }, { id: 'js_local_storage_get', type: 'js_data', label: 'load from storage', category: 'data', description: 'Get a saved value from the browser storage', framework: 'web', shape: 'reporter', color: '#795548', icon: '๐Ÿ’พ', inputs: [], outputs: [{ id: 'value', label: '', type: 'string', position: 'right' }], config: [ { id: 'key', label: 'key', type: 'text', default: 'myKey' }, ], compile: (cfg) => `localStorage.getItem('${cfg.key || 'myKey'}')`, }, { id: 'js_local_storage_remove', type: 'js_data', label: 'remove from storage', category: 'data', description: 'Delete a saved value from browser storage', framework: 'web', shape: 'stack', color: '#795548', icon: '๐Ÿ—‘', inputs: [], outputs: [], config: [ { id: 'key', label: 'key', type: 'text', default: 'myKey' }, ], compile: (cfg) => `localStorage.removeItem('${cfg.key || 'myKey'}');`, }, { id: 'js_json_parse', type: 'js_data', label: 'parse JSON text', category: 'data', description: 'Convert text to a structured object (JSON)', framework: 'web', shape: 'reporter', color: '#795548', icon: '{}', inputs: [{ id: 'text', label: '', type: 'string', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'any', position: 'right' }], config: [], compile: (_cfg, inputs) => `JSON.parse(${inputs.text || "''"})`, }, { id: 'js_json_stringify', type: 'js_data', label: 'make JSON text', category: 'data', description: 'Convert a value to JSON text format', framework: 'web', shape: 'reporter', color: '#795548', icon: '{}', inputs: [{ id: 'value', label: '', type: 'any', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [], compile: (_cfg, inputs) => `JSON.stringify(${inputs.value || 'null'})`, }, // ============================================================ // โ˜ฐ LISTS (ARRAYS) โ€” "work with lists of things" // ============================================================ { id: 'js_array', type: 'js_lists', label: 'list of items', category: 'lists', description: 'Create a new list with items separated by commas', framework: 'web', shape: 'reporter', color: '#FF661A', icon: 'โ˜ฐ', inputs: [], outputs: [{ id: 'value', label: '', type: 'array', position: 'right' }], config: [ { id: 'values', label: 'items', type: 'text', placeholder: '1, 2, 3' }, ], compile: (cfg) => `[${cfg.values || ''}]`, }, { id: 'js_array_empty', type: 'js_lists', label: 'empty list', category: 'lists', description: 'Create a new empty list', framework: 'web', shape: 'reporter', color: '#FF661A', icon: 'โ˜ฐ', inputs: [], outputs: [{ id: 'value', label: '', type: 'array', position: 'right' }], config: [], compile: () => '[]', }, { id: 'js_array_push', type: 'js_lists', label: 'add item to list', category: 'lists', description: 'Add an item to the end of a list', framework: 'web', shape: 'stack', color: '#FF661A', icon: 'โž•', inputs: [{ id: 'value', label: 'item', type: 'any', position: 'left' }], outputs: [], config: [ { id: 'array', label: 'list', type: 'text', default: 'myArray' }, ], compile: (cfg, inputs) => `${cfg.array || 'myArray'}.push(${inputs.value || 'undefined'});`, }, { id: 'js_array_pop', type: 'js_lists', label: 'remove last item', category: 'lists', description: 'Remove and get the last item from a list', framework: 'web', shape: 'reporter', color: '#FF661A', icon: 'โž–', inputs: [], outputs: [{ id: 'result', label: '', type: 'any', position: 'right' }], config: [ { id: 'array', label: 'list', type: 'text', default: 'myArray' }, ], compile: (cfg) => `${cfg.array || 'myArray'}.pop()`, }, { id: 'js_array_length', type: 'js_lists', label: 'length of list', category: 'lists', description: 'Count how many items are in a list', framework: 'web', shape: 'reporter', color: '#FF661A', icon: '๐Ÿ“', inputs: [], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [ { id: 'array', label: 'list', type: 'text', default: 'myArray' }, ], compile: (cfg) => `${cfg.array || 'myArray'}.length`, }, { id: 'js_array_index_of', type: 'js_lists', label: 'find item in list', category: 'lists', description: 'Find where an item is in a list (returns position, or -1 if not found)', framework: 'web', shape: 'reporter', color: '#FF661A', icon: '๐Ÿ”', inputs: [{ id: 'value', label: 'item', type: 'any', position: 'left' }], outputs: [{ id: 'result', label: '', type: 'number', position: 'right' }], config: [ { id: 'array', label: 'list', type: 'text', default: 'myArray' }, ], compile: (cfg, inputs) => `${cfg.array || 'myArray'}.indexOf(${inputs.value || 'undefined'})`, }, { id: 'js_array_join', type: 'js_lists', label: 'join list into text', category: 'lists', description: 'Combine all items in a list into a single piece of text', framework: 'web', shape: 'reporter', color: '#FF661A', icon: 'โŠ”', inputs: [], outputs: [{ id: 'result', label: '', type: 'string', position: 'right' }], config: [ { id: 'array', label: 'list', type: 'text', default: 'myArray' }, { id: 'separator', label: 'with separator', type: 'text', default: ', ' }, ], compile: (cfg) => `${cfg.array || 'myArray'}.join('${cfg.separator || ', '}')`, }, { id: 'js_array_slice', type: 'js_lists', label: 'get part of list', category: 'lists', description: 'Get a portion of a list from start to end', framework: 'web', shape: 'reporter', color: '#FF661A', icon: 'โœ‚', inputs: [], outputs: [{ id: 'result', label: '', type: 'array', position: 'right' }], config: [ { id: 'array', label: 'list', type: 'text', default: 'myArray' }, { id: 'start', label: 'start at', type: 'number', default: 0 }, { id: 'end', label: 'end at', type: 'number', default: 5 }, ], compile: (cfg) => `${cfg.array || 'myArray'}.slice(${cfg.start ?? 0}, ${cfg.end ?? 5})`, }, // ============================================================ // ๐Ÿ“… DATE/TIME โ€” "work with dates and times" // ============================================================ { id: 'js_date_now', type: 'js_date', label: 'current date and time', category: 'date', description: 'Get the current date and time', framework: 'web', shape: 'reporter', color: '#9C27B0', icon: '๐Ÿ“…', inputs: [], outputs: [{ id: 'result', label: '', type: 'any', position: 'right' }], config: [ { id: 'format', label: 'format', type: 'select', default: 'iso', options: [ { label: 'full date string', value: 'iso' }, { label: 'timestamp (ms)', value: 'timestamp' }, { label: 'year', value: 'year' }, { label: 'month (1-12)', value: 'month' }, { label: 'day of month', value: 'day' }, { label: 'hours', value: 'hours' }, { label: 'minutes', value: 'minutes' }, { label: 'seconds', value: 'seconds' }, { label: 'day of week (0=Sun)', value: 'weekday' }, ]}, ], compile: (cfg) => { switch (cfg.format) { case 'timestamp': return 'Date.now()'; case 'year': return 'new Date().getFullYear()'; case 'month': return 'new Date().getMonth() + 1'; case 'day': return 'new Date().getDate()'; case 'hours': return 'new Date().getHours()'; case 'minutes': return 'new Date().getMinutes()'; case 'seconds': return 'new Date().getSeconds()'; case 'weekday': return 'new Date().getDay()'; default: return 'new Date().toISOString()'; } }, }, ];