diff --git a/.symbols/config.json b/.symbols/config.json deleted file mode 100644 index 997206e..0000000 --- a/.symbols/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "apiBaseUrl": "https://api.symbols.app" -} \ No newline at end of file diff --git a/.symbols/lock.json b/.symbols/lock.json deleted file mode 100644 index 34ab3a4..0000000 --- a/.symbols/lock.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "etag": null, - "version": "1.7.387", - "branch": "main", - "projectId": "6874baae0769df64f1a4484d", - "pulledAt": "2026-01-08T22:50:42.678Z" -} \ No newline at end of file diff --git a/.symbols/project.json b/.symbols/project.json deleted file mode 100644 index 079b9ec..0000000 --- a/.symbols/project.json +++ /dev/null @@ -1,7461 +0,0 @@ -{ - "_migrationMeta": { - "hasBasedData": false, - "hasRichData": false, - "sourceVersion": "1.0", - "migratedAt": "2025-07-14T08:07:10.346Z" - }, - "status": "active", - "designTool": "none", - "environments": { - "dev": { - "mode": "latest", - "branch": "main", - "enabled": true, - "label": "Development" - }, - "testing": { - "mode": "version", - "enabled": false, - "label": "Testing" - }, - "staging": { - "mode": "version", - "enabled": false, - "label": "Staging" - }, - "prod": { - "mode": "published", - "enabled": true, - "label": "Production" - } - }, - "ownerType": "user", - "_id": "6874baae0769df64f1a4484d", - "name": "Bigbrother", - "key": "big-brother.symbo.ls", - "tier": "free", - "projectType": "web", - "settings": { - "remote": false, - "async": false, - "inspect": true, - "liveSync": true, - "syncCanvas": true - }, - "designSystem": { - "COLOR": { - "black": "#000", - "white": "#fff", - "softBlack": "#1d1d1d", - "lightGreen": "#9cbc8f", - "codGray": "#131313", - "caption": "#b1b1b1", - "deepFir": "#222222", - "cloudProvider": "#986565", - "SLA": "#007081", - "nodeType": "#8cba83", - "env": "#7081d8" - }, - "GRADIENT": {}, - "THEME": { - "document": { - "@light": { - "color": "black", - "background": "white" - }, - "@dark": { - "color": "white", - "background": "softBlack" - } - }, - "none": { - "color": "none", - "background": "none" - }, - "transparent": { - "color": "currentColor", - "background": "transparent" - }, - "dialog": { - "@dark": { - "color": "white", - "background": "codGray", - "colorKey": "white", - "colorType": null, - "backgroundKey": "codGray", - "backgroundType": null - }, - "@light": { - "color": "white", - "background": "white 0.065" - } - }, - "button": { - "@dark": { - "color": "white", - "background": "transparent", - ":hover": { - "background": "deepFir" - }, - ":active": { - "background": "deepFir +15" - } - } - } - }, - "FONT": {}, - "FONT_FAMILY": {}, - "TYPOGRAPHY": { - "base": 16, - "ratio": 1.25, - "subSequence": true, - "templates": {} - }, - "SPACING": { - "base": 16, - "ratio": 1.618, - "subSequence": true - }, - "TIMING": { - "defaultBezier": "cubic-bezier(.29, .67, .51, .97)" - }, - "CLASS": {}, - "GRID": {}, - "ICONS": { - "github": "", - "gitlab": "", - "logo": "", - "logo2": "", - "logo_optimized": "", - "eyes": "", - "eye": "", - "copy": "", - "edit": "", - "trash": "", - "cloudProvider": "#986565" - }, - "SHAPE": {}, - "RESET": {}, - "ANIMATION": {}, - "MEDIA": { - "mobileSm": "(min-width: 500px)" - }, - "CASES": {}, - "useReset": true, - "useVariable": true, - "useFontImport": true, - "useIconSprite": true, - "useSvgSprite": true, - "useDefaultConfig": true, - "useDocumentTheme": true, - "verbose": false, - "globalTheme": "dark" - }, - "package": "1", - "functions": { - "stringToHexColor": "function(str = '') {\n let hash = 0;\n // Compute a hash value for the string\n for (let i = 0; i < str.length; i++) {\n hash = str.charCodeAt(i) + ((hash << 5) - hash);\n }\n // Convert hash to a hexadecimal color code\n let color = '#';\n for (let i = 0; i < 3; i++) {\n const value = (hash >> (i * 8)) & 0xFF;\n color += ('00' + value.toString(16)).slice(-2);\n }\n return color;\n }", - "filterByEnv": "function filterByEnv(array, value) {\n return this.call('isArray', array) && array.filter(item => item['env'] === value).length\n }", - "calculateWeek": "function calculateWeek(index) {\n\n // Generate date ranges based on index\n // Starting from April 1st and creating week ranges\n if (index === 0) return ''; // First child has no date\n\n const startApril = new Date(2025, 3, 1); // April 1, 2025\n const startDay = new Date(startApril);\n startDay.setDate(startDay.getDate() + (index - 1) * 7); // Add weeks\n\n const endDay = new Date(startDay);\n endDay.setDate(endDay.getDate() + 6); // End is 6 days later\n\n // Format the dates\n const startDate = startDay.getDate();\n const endDate = endDay.getDate();\n const startMonth = startDay.toLocaleString('en-US', {\n month: 'short'\n });\n const endMonth = endDay.toLocaleString('en-US', {\n month: 'short'\n });\n\n if (startMonth === endMonth) {\n return `${startDate}-${endDate} ${startMonth}`;\n } else {\n return `${startDate} ${startMonth}-${endDate} ${endMonth}`;\n }\n }", - "calculateCosts": "function calculateCosts(data) {\n let totalCurrentCost = 0;\n let totalProjectedCost = 0;\n\n data.forEach(item => {\n const allValidators = item.validator_info || [];\n const allRpcs = item.rpc_info || [];\n\n allValidators.forEach(val => {\n if (typeof val.current_spend === 'number') {\n totalCurrentCost += val.current_spend;\n }\n if (typeof val.projected_cost === 'number') {\n totalProjectedCost += val.projected_cost;\n }\n });\n\n allRpcs.forEach(rpc => {\n if (typeof rpc.current_spend === 'number') {\n totalCurrentCost += rpc.current_spend;\n }\n if (typeof rpc.projected_cost === 'number') {\n totalProjectedCost += rpc.projected_cost;\n }\n });\n });\n\n const formatter = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD'\n });\n\n return {\n totalCurrentCost: formatter.format(totalCurrentCost),\n totalProjectedCost: formatter.format(totalProjectedCost)\n };\n }", - "getCostsPerProtocol": "function getCostsPerProtocol(data) {\n return data.map(item => {\n let currentCost = 0;\n let projectedCost = 0;\n\n const allValidators = item.validator_info || [];\n const allRpcs = item.rpc_info || [];\n\n allValidators.forEach(val => {\n if (typeof val.current_spend === 'number') {\n currentCost += val.current_spend;\n }\n if (typeof val.projected_cost === 'number') {\n projectedCost += val.projected_cost;\n }\n });\n\n allRpcs.forEach(rpc => {\n if (typeof rpc.current_spend === 'number') {\n currentCost += rpc.current_spend;\n }\n if (typeof rpc.projected_cost === 'number') {\n projectedCost += rpc.projected_cost;\n }\n });\n\n const formatter = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD'\n });\n\n return {\n protocol: item.protocol,\n validatorCount: allValidators.length,\n rpcCount: allRpcs.length,\n currentCost: formatter.format(currentCost),\n projectedCost: formatter.format(projectedCost)\n };\n });\n }", - "giveMeAnswer": "async function giveMeAnswer(text) {\n const apiUrl = 'https://bigbrother.symbo.ls/api/mcp/query'\n const requestBody = {\n prompt: text\n }\n let responseHtml =\n '

Apologies, something went wrong while contacting the assistant.

'\n\n try {\n console.log('Sending request to API:', JSON.stringify(requestBody, null, 2))\n const response = await fetch(apiUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(requestBody)\n })\n\n const responseText = await response.text() // Get raw response text for debugging\n console.log('Raw API Response:', responseText)\n\n if (!response.ok) {\n let errorDetail = 'Unknown error'\n try {\n const errorData = JSON.parse(responseText)\n errorDetail = errorData.detail || errorData.error || response.statusText\n } catch (e) {\n errorDetail = response.statusText || 'Failed to parse error response'\n }\n console.error('API Error:', response.status, errorDetail)\n responseHtml = `

Error from assistant: ${errorDetail}

`\n return responseHtml\n }\n\n try {\n const data = JSON.parse(responseText)\n return data\n } catch (e) {\n console.error('Error parsing API response:', e)\n return responseText // Return raw response if parsing fails\n }\n } catch (error) {\n console.error('Failed to fetch from API or process response:', error)\n return `

Failed to connect to the assistant: ${error.message}

`\n }\n }", - "getMe": "async (path = '/api/fleet', opts = {}) => {\n const endpointUrl = 'https://bigbrother.symbo.ls/auth/me'\n const options = {\n method: 'GET',\n headers: {\n Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImVtYWlsIjoibmlrYUBzeW1ib2xzLmFwcCIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTc1MjAxMjU3MiwiZXhwIjoxNzUyNjE3MzcyfQ.jw2-gNSFdOL3qpHBgRzSSlV6Kas0p15UTCJhDwRNvCo'\n },\n params: {},\n auth: {}\n }\n\n return await window.fetch(endpointUrl, options).then(r => r.json())\n }", - "getTagsOfNodes": "function(networkObj, tag) {\n\n }", - "edit": "async function edit(item = 'network', protocol, opts = {}) {\n const formData = new FormData(this.node)\n let data = Object.fromEntries(formData)\n\n // For nodes, send flat object directly (not nested)\n if (item === 'node') {\n // Remove nodeType from data since it's in the URL\n delete data.nodeType\n data.projected_cost = parseInt(data.projected_cost)\n // Send the form data directly as flat object\n // data is already correct: { moniker: \"...\", env: \"...\", etc. }\n }\n\n const ROUTE = {\n network: `/${protocol}`,\n node: `/node/${this.state.nodeType}/${this.state.uid}`\n }\n\n console.log('Route:', ROUTE[item])\n console.log('Data being sent:', data)\n\n const res = await this.call('fetch', 'PUT', ROUTE[item], data, opts)\n console.log('Response:', res)\n\n this.state.root.quietUpdate({\n modal: null\n })\n\n const redirectUrl = {\n network: '/network/' + this.state.protocol,\n node: '/node/' + this.state.protocol + '/' + this.state.nodeType + '/' + this.state.uid\n }\n\n this.call('router', redirectUrl[item] || '/', this.__ref.root)\n this.node.reset()\n }", - "remove": "async function remove(item = 'network', protocol, opts = {}) {\n let [, _, urlProtocol, nodeType, uid] = window.location.pathname.split('/')\n\n const ROUTE = {\n network: '/' + protocol,\n node: '/node/' + nodeType + '/' + uid\n }\n\n console.log('/node/' + nodeType + '/' + uid)\n\n const res = await this.call('fetch', 'DELETE', ROUTE[item])\n if (!res) return\n\n this.state.root.quietUpdate({\n modal: null\n })\n\n const REDIRECT = {\n network: '/dashboard',\n node: '/network/' + protocol\n }\n this.call('router', REDIRECT[item], this.__ref.root)\n }", - "fetch": "async function fetch(method = 'GET', path = '', data, opts = {}) {\n // const ENDPOINT = 'https://small-sound-18b4.nika-980.workers.dev/api/fleet'\n // const ENDPOINT = 'https://bigbrother.symbo.ls' + (opts.route || '/api/fleet') + path\n\n const options = {\n method: method || 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n ...opts\n }\n\n const isCanvas = location.href === 'srcdoc'\n const isSymbols = location.host.includes('symbo.ls')\n const isProd = location.host.includes('nodeops.ninja') && !location.host.includes('dev')\n // const isProd = false // location.host.includes('nodeops.ninja') && !location.host.includes('dev')\n\n const URL = `https://${isProd ? '' : 'dev.'}api.nodeops.ninja`\n const ENDPOINT = URL + (opts.route || '/api/fleet') + path\n\n if (isCanvas || isSymbols || !isProd) {\n const API_TOKEN = 'bb_ff64921b7b6dae704a352681c26ae5ed35c8143e18e13f2682cc2be1ab4ebb74'\n options.headers.Authorization = 'Bearer ' + API_TOKEN\n } else {\n // const SESSION_ID = 's%3AwAm91jUNz7Bv3ihqvY9o4AJ_xQDTA6x3.VcEZyPFSdClTokzsu9n3gXULU1qp7pxSNCSEQBUhoIQ'\n // options.headers.credentials = true\n // options.headers.Authorization = 'Bearer ' + SESSION_ID\n options.credentials = 'include'\n options.mode = 'cors'\n }\n\n if (data && (options.method === 'POST' || options.method === 'PUT')) {\n options.body = JSON.stringify(data)\n }\n\n console.log('Fetch request:', ENDPOINT, options)\n const res = await window.fetch(ENDPOINT, options)\n\n if (!res.ok) {\n const errorText = await res.text()\n console.error('Failed to submit:', res.status, errorText)\n throw new Error(`HTTP ${res.status}: ${errorText}`)\n }\n\n // Check if response has content before parsing JSON\n const contentType = res.headers.get('content-type')\n if (contentType && contentType.includes('application/json')) {\n return res.json()\n }\n\n return res.text()\n }", - "add": "async function addNew(item = 'network') {\n const ROUTE = {\n network: '',\n node: '/' + this.state.protocol + '/node'\n }\n\n const formData = new FormData(this.node)\n let data = Object.fromEntries(formData)\n if (item === 'node') {\n data.projected_cost = parseInt(data.projected_cost)\n console.log(data.projected_cost)\n data = {\n nodeType: data.nodeType,\n nodeData: data\n }\n }\n console.log(data)\n\n const res = await this.call('fetch', 'POST', ROUTE[item], data)\n if (!res) return\n\n this.state.root.quietUpdate({\n modal: null\n })\n\n // console.log('here')\n // console.log(this.state)\n // console.log(res)\n const ROUTES = {\n network: '/network/' + data.protocol,\n node: '/node/' + this.state.protocol + '/' + data.nodeType + '/' + res.uid\n }\n this.call('router', ROUTES[item] || '/', this.__ref.root)\n this.node.reset()\n }", - "read": "async function read(path) {\n return await this.call('fetch', 'GET', path)\n }", - "parseNetworkRow": "function parseNetworkRow(data) {\n const result = {\n cloud_provider: new Set(),\n env: new Set(),\n node_types: new Set(),\n status: new Set()\n };\n\n // Parse validators\n if (Array.isArray(data.validators) && data.validators.length > 0) {\n result.node_types.add('Validator');\n data.validators.forEach(validator => {\n if (validator.cloud_provider) result.cloud_provider.add(validator.cloud_provider);\n if (validator.env) result.env.add(validator.env);\n if (validator.status) result.status.add(validator.status);\n });\n }\n\n // Parse rpc_nodes\n if (Array.isArray(data.rpc_nodes) && data.rpc_nodes.length > 0) {\n result.node_types.add('RPC');\n data.rpc_nodes.forEach(node => {\n if (node.cloud_provider) result.cloud_provider.add(node.cloud_provider);\n if (node.env) result.env.add(node.env);\n if (node.status) result.status.add(node.status);\n });\n }\n\n // Convert Sets to Arrays\n return {\n cloud_provider: Array.from(result.cloud_provider),\n env: Array.from(result.env),\n node_types: Array.from(result.node_types),\n status: Array.from(result.status)\n };\n }", - "getFleet": "function getFleet() {\n // console.log(this, this.state, this.context)\n }", - "filterFleet": "function filterFleet() {\n // Search filter\n if (root.search) {\n fleet = fleet.filter(item =>\n item.protocol.toLowerCase().includes(root.search.toLowerCase()) ||\n item.repo_url.includes(root.search)\n )\n }\n\n // Validators filter\n if (root.validators) {\n if (root.validators === 'Mainnet') {\n fleet = fleet.filter(item =>\n item.validator_info?.some(validator => validator.env === 'Mainnet')\n )\n } else if (root.validators === 'Testnet') {\n fleet = fleet.filter(item =>\n item.validator_info?.some(validator => validator.env === 'Testnet')\n )\n } else if (root.validators === 'None') {\n fleet = fleet.filter(item =>\n !item.validator_info?.length\n )\n }\n // 'All' option doesn't need additional filtering\n }\n\n // RPC filter\n if (root.rpc) {\n if (root.rpc === 'Mainnet') {\n fleet = fleet.filter(item =>\n item.rpc_info?.some(rpc => rpc.env === 'Mainnet')\n )\n } else if (root.rpc === 'Testnet') {\n fleet = fleet.filter(item =>\n item.rpc_info?.some(rpc => rpc.env === 'Testnet')\n )\n } else if (root.rpc === 'None') {\n fleet = fleet.filter(item =>\n !item.rpc_info?.length\n )\n }\n // 'All' option doesn't need additional filtering\n }\n\n // Status filter\n if (root.status) {\n fleet = fleet.filter(item => item.status === root.status)\n }\n\n // Last Updated filter\n if (root.lastUpdate && root.lastUpdate !== 'All Time') {\n const now = new Date()\n let targetDate = new Date()\n\n if (root.lastUpdate === 'Today') {\n targetDate.setHours(0, 0, 0, 0) // Start of today\n } else if (root.lastUpdate === 'Last 2 Days') {\n targetDate.setDate(now.getDate() - 2)\n targetDate.setHours(0, 0, 0, 0)\n } else if (root.lastUpdate === 'Last 3 Days') {\n targetDate.setDate(now.getDate() - 3)\n targetDate.setHours(0, 0, 0, 0)\n } else if (root.lastUpdate === 'Last Week') {\n targetDate.setDate(now.getDate() - 7)\n targetDate.setHours(0, 0, 0, 0)\n } else if (root.lastUpdate === 'Last Month') {\n targetDate.setMonth(now.getMonth() - 1)\n targetDate.setHours(0, 0, 0, 0)\n }\n\n fleet = fleet.filter(item => {\n // Check logs in validator_info\n const validatorLogs = item.validator_info?.flatMap(validator =>\n validator.logs?.map(log => new Date(log.created_at)) || []\n ) || []\n\n // Check logs in rpc_info\n const rpcLogs = item.rpc_info?.flatMap(rpc =>\n rpc.logs?.map(log => new Date(log.created_at)) || []\n ) || []\n\n // Combine all logs\n const allLogs = [...validatorLogs, ...rpcLogs]\n\n // Check if any log is newer than the target date\n return allLogs.some(logDate => logDate >= targetDate)\n })\n }\n }", - "setInitialData": "function setInitialData(data = {}) {\n this.state.replace(data, {\n preventUpdate: true,\n preventUpdateListener: true\n })\n\n this.update({}, {\n preventUpdateListener: true\n })\n }", - "getStatusColor": "function getStatusColor(status) {\n const MAP = {\n 'Live': 'green',\n 'Stable/Maintenance': 'green',\n 'Onboarding': 'yellow',\n 'Off': 'gray'\n }\n\n return MAP[status]\n }", - "auth": "async function auth() {\n if (this.state.root.success) {\n if (window.location.pathname === '/') {\n this.call('router', '/dashboard', this.__ref.root)\n }\n } else {\n if (window.location.pathname === '/') {\n const res = await this.call('fetch', 'GET', '', null, {\n route: '/auth/me',\n })\n\n if (res.success) {\n this.state.root.update(res)\n this.call('router', '/dashboard', this.__ref.root)\n }\n return res\n } else {\n this.call('router', '/', this.__ref.root)\n }\n }\n }", - "loadIntercom": "function loadIntercom() {\n // Prevent multiple loads\n if (window.intercomScriptLoaded) {\n Intercom(\"show\");\n return;\n }\n window.intercomScriptLoaded = true;\n\n // Load Intercom widget\n (function () {\n var w = window;\n var ic = w.Intercom;\n if (typeof ic === \"function\") {\n ic('reattach_activator');\n ic('update', {});\n } else {\n var d = document;\n var i = function () {\n i.c(arguments);\n };\n i.q = [];\n i.c = function (args) {\n i.q.push(args);\n };\n w.Intercom = i;\n\n var l = function () {\n var s = d.createElement('script');\n s.type = 'text/javascript';\n s.async = true;\n s.src = 'https://widget.intercom.io/widget/YOUR_APP_ID';\n var x = d.getElementsByTagName('script')[0];\n x.parentNode.insertBefore(s, x);\n };\n l();\n }\n })();\n\n // After load → open automatically\n window.Intercom('boot', {\n app_id: 'YOUR_APP_ID'\n });\n\n // Optional: ensure it opens after load\n setTimeout(() => Intercom('show'), 500);\n}" - }, - "methods": { - "default": {} - }, - "version": "1.7.378", - "versionscount": 849, - "seats": 1, - "dependencies": { - "chart.js": "4.4.9", - "fuse.js": "7.1.0" - }, - "files": { - "Arbitrum.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/689afdd763c13d9548d7710e/download" - }, - "code": "", - "key": "Arbitrum.png", - "type": "file", - "format": "png" - }, - "CRO.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/689afde063c13d9548d77129/download" - }, - "code": "", - "key": "CRO.png", - "type": "file", - "format": "png" - }, - "Aptos.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/689afde863c13d9548d771b1/download" - }, - "code": "", - "key": "Aptos.png", - "type": "file", - "format": "png" - }, - "Allora.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30df9f65883b039b9dbea/download" - }, - "code": "", - "key": "Allora.png", - "type": "file", - "format": "jpg" - }, - "Eth2.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30edbf65883b039b9de92/download" - }, - "code": "", - "key": "Eth2.png", - "type": "file", - "format": "png" - }, - "Chromia.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30eaff65883b039b9dd79/download" - }, - "code": "", - "key": "Chromia.png", - "type": "file", - "format": "png" - }, - "Canton.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30e38f65883b039b9dc64/download" - }, - "code": "", - "key": "Canton.png", - "type": "file", - "format": "jpg" - }, - "Polygon.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a310bef65883b039b9e3be/download" - }, - "code": "", - "key": "Polygon.png", - "type": "file", - "format": "svg" - }, - "Haven1.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30efbf65883b039b9deff/download" - }, - "code": "", - "key": "Haven1.png", - "type": "file", - "format": "jpg" - }, - "Pharos.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a310a8f65883b039b9e365/download" - }, - "code": "", - "key": "Pharos.png", - "type": "file", - "format": "jpg" - }, - "Nillion.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30fcbf65883b039b9e13d/download" - }, - "code": "", - "key": "Nillion.png", - "type": "file", - "format": "jpg" - }, - "Near.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30fb6f65883b039b9e0e8/download" - }, - "code": "", - "key": "Near.png", - "type": "file", - "format": "webp" - }, - "Story.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a311abf65883b039b9e5eb/download" - }, - "code": "", - "key": "Story.png", - "type": "file", - "format": "png" - }, - "Solana.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a31112f65883b039b9e4d9/download" - }, - "code": "", - "key": "Solana.png", - "type": "file", - "format": "jpg" - }, - "SXT.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a311dbf65883b039b9e6a5/download" - }, - "code": "", - "key": "SXT.png", - "type": "file", - "format": "jpg" - }, - "Radix.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a310f4f65883b039b9e46e/download" - }, - "code": "", - "key": "Radix.png", - "type": "file", - "format": "jpg" - }, - "Polymesh.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a310e2f65883b039b9e419/download" - }, - "code": "", - "key": "Polymesh.png", - "type": "file", - "format": "png" - }, - "Supra.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a311c2f65883b039b9e642/download" - }, - "code": "", - "key": "Supra.png", - "type": "file", - "format": "webp" - }, - "Tezos.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a311f2f65883b039b9e6fa/download" - }, - "code": "", - "key": "Tezos.png", - "type": "file", - "format": "png" - }, - "Vanar.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a31200f65883b039b9e74f/download" - }, - "code": "", - "key": "Vanar.png", - "type": "file", - "format": "png" - }, - "Wemix.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a31268f65883b039b9e843/download" - }, - "code": "", - "key": "Wemix.png", - "type": "file", - "format": "png" - }, - "Zenrock.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a31279f65883b039b9e8a0/download" - }, - "code": "", - "key": "Zenrock.png", - "type": "file", - "format": "jpg" - }, - "Zetachain.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a3128af65883b039b9e903/download" - }, - "code": "", - "key": "Zetachain.png", - "type": "file", - "format": "png" - }, - "Aleo.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30dbaf65883b039b9db1b/download" - }, - "code": "", - "key": "Aleo.png", - "type": "file", - "format": "jpg" - }, - "Cardano.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30e7af65883b039b9dd06/download" - }, - "code": "", - "key": "Cardano.png", - "type": "file", - "format": "png" - }, - "Ika.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30f3af65883b039b9df7d/download" - }, - "code": "", - "key": "Ika.png", - "type": "file", - "format": "jpg" - }, - "Kaia.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30f64f65883b039b9dfd7/download" - }, - "code": "", - "key": "Kaia.png", - "type": "file", - "format": "png" - }, - "Mantra.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30f7af65883b039b9e034/download" - }, - "code": "", - "key": "Mantra.png", - "type": "file", - "format": "jpg" - }, - "Midnight.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a30f9bf65883b039b9e08f/download" - }, - "code": "", - "key": "Midnight.png", - "type": "file", - "format": "jpg" - }, - "Somnia.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a31127f65883b039b9e566/download" - }, - "code": "", - "key": "Somnia.png", - "type": "file", - "format": "png" - }, - "Warden.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a31216f65883b039b9e79e/download" - }, - "code": "", - "key": "Warden.png", - "type": "file", - "format": "png" - }, - "ZkCloud.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68a3129ef65883b039b9e958/download" - }, - "code": "", - "key": "ZkCloud.png", - "type": "file", - "format": "png" - }, - "google.svg": { - "content": { - "src": "https://api.symbols.app/core/files/public/687723c00769df64f1a749e3/download" - }, - "code": "", - "key": "google.svg", - "type": "file", - "format": "svg" - }, - "Autonity.png": { - "content": { - "src": "https://api.symbols.app/core/files/public/68959a0763c13d9548d20d48/download" - }, - "code": "", - "key": "autonity.png", - "type": "file", - "format": "png" - }, - "walrus": { - "content": { - "filename": "KC6luiJ-_wNASRFXds4Zc.png", - "originalName": "apple-touch-icon.png", - "mimeType": "image/png", - "size": 3380, - "storageUrl": "https://storage.googleapis.com/smbls-api-media/media/projects/6874baae0769df64f1a4484d/users/6868484c0cf470c5890933d5/KC6luiJ-_wNASRFXds4Zc.png", - "bucket": "smbls-api-media", - "category": "image", - "tags": [], - "uploadedBy": "6868484c0cf470c5890933d5", - "project": "6874baae0769df64f1a4484d", - "visibility": "public", - "status": "active", - "version": 1, - "downloadCount": 0, - "_id": "691722cceff651dd5dde3586", - "previousVersions": [], - "createdAt": "2025-11-14T12:38:36.273Z", - "updatedAt": "2025-11-14T12:38:36.273Z", - "__v": 0, - "extension": "png", - "humanSize": "3.3 KB", - "age": 55, - "id": "691722cceff651dd5dde3586", - "urls": { - "api": { - "base": "/core/files", - "file": "/core/files/691722cceff651dd5dde3586", - "download": "/core/files/public/691722cceff651dd5dde3586/download", - "publicDownload": "/core/files/public/691722cceff651dd5dde3586/download" - }, - "absolute": { - "base": "https://api.symbols.app/core/files", - "file": "https://api.symbols.app/core/files/691722cceff651dd5dde3586", - "download": "https://api.symbols.app/core/files/public/691722cceff651dd5dde3586/download", - "publicDownload": "https://api.symbols.app/core/files/public/691722cceff651dd5dde3586/download" - }, - "storage": "https://storage.googleapis.com/smbls-api-media/media/projects/6874baae0769df64f1a4484d/users/6868484c0cf470c5890933d5/KC6luiJ-_wNASRFXds4Zc.png" - }, - "src": "https://api.symbols.app/core/files/public/691722cceff651dd5dde3586/download" - }, - "code": "", - "key": "walrus", - "type": "files", - "format": "walrus" - } - }, - "snippets": { - "default": {} - }, - "access": "account", - "isSharedLibrary": false, - "framework": "platform", - "language": "javascript", - "visibility": "public", - "versions": [ - "6874baae0769df64f1a44852" - ], - "pullRequests": [], - "members": [ - { - "_id": "6874baae0769df64f1a4484f", - "user": { - "_id": "6868484c0cf470c5890933d5", - "email": "toko@symbols.app", - "name": "Tornike", - "avatar": "https://lh3.googleusercontent.com/a/ACg8ocIJUaJjaHnDukKYkIZ_0POhuyuiPQv_HxQ9InABK4W9nOYukr8=s96-c", - "username": "toko", - "id": "6868484c0cf470c5890933d5" - }, - "project": "6874baae0769df64f1a4484d", - "role": "owner", - "joinedAt": "2025-07-14T08:07:10.366Z", - "createdAt": "2025-07-14T08:07:10.366Z", - "updatedAt": "2025-07-14T08:07:10.366Z", - "__v": 0, - "id": "6874baae0769df64f1a4484f" - }, - { - "_id": "6874baff0769df64f1a44a4e", - "user": { - "_id": "6868484c0cf470c5890933cc", - "email": "nika@symbols.app", - "name": "Nika", - "username": "nikoloza", - "id": "6868484c0cf470c5890933cc" - }, - "project": "6874baae0769df64f1a4484d", - "role": "admin", - "joinedAt": "2025-07-14T08:08:31.086Z", - "createdAt": "2025-07-14T08:08:31.086Z", - "updatedAt": "2025-07-14T08:08:31.086Z", - "__v": 0, - "id": "6874baff0769df64f1a44a4e" - }, - { - "_id": "6946a10c4c0341ca0852b1ac", - "user": { - "_id": "6868484c0cf470c5890933cf", - "email": "thomas@symbols.app", - "name": "Thomas", - "avatar": "https://lh3.googleusercontent.com/a/ACg8ocLzOMpMtLVoWtCdx2aabhbbK1jKL7EkbAC2UDStgMmw2t1x7tg=s96-c", - "username": "thomas", - "id": "6868484c0cf470c5890933cf" - }, - "project": "6874baae0769df64f1a4484d", - "role": "editor", - "joinedAt": "2025-12-20T13:13:48.773Z", - "createdAt": "2025-12-20T13:13:48.773Z", - "updatedAt": "2025-12-20T13:13:48.773Z", - "__v": 0, - "id": "6946a10c4c0341ca0852b1ac" - } - ], - "payments": [], - "useLibraries": [], - "usedByProjects": [], - "createdAt": "2025-07-14T08:07:10.346Z", - "updatedAt": "2025-12-21T20:11:56.172Z", - "__v": 2, - "publishedVersion": "69171bb503fef75cff661362", - "canvas": { - "pages": { - "Symbols": { - "title": "Symbols", - "freeform": true, - "pages": [], - "components": [ - "FilterStatus", - "LabelTag", - "FormModal", - "ValidatorFooter", - "ValidatorRow", - "FieldCaption", - "CopyButton", - "Column", - "FilterSidebar", - "PreviewItem", - "Dropdown", - "Row", - "ActiveRow", - "ValidatorsList", - "Search", - "NetworkRow", - "Page", - "NavButton", - "ModalWindow", - "MetricsSection", - "NetworkRowLabel", - "Header", - "MetaSectionCopy", - "Select", - "ValidatorInfo", - "ValidatorContent", - "Modal", - "ModalForm", - "MetaSection", - "ValidatorLogs", - "Aside", - "OldNotes", - "Graphs", - "Chart", - "Chart.Block", - "Prompt", - "AIThread", - "AIMessage", - "Uptime", - "LogItem", - "MetaSectionBars", - "Logs", - "Table", - "SearchItem", - "SearchDropdown", - "Content", - "Layout" - ], - "functions": [ - "parseNetworkRow", - "getStatusColor" - ], - "methods": [], - "snippets": [], - "atom": [], - "files": [], - "dependencies": [], - "secrets": [] - } - }, - "clients": {} - }, - "colors": {}, - "components": { - "Row": { - "Flex": { - "Status": { - "props": "(el, s) => ({\n order: '-1',\n background: el.call('getStatusColor', s.status),\n round: 'C',\n boxSize: 'Y2'\n })" - }, - "Title": { - "tag": "strong", - "flexFlow": "x", - "gap": "X2", - "text": "(el, s) => s.protocol" - }, - "Validator": { - "extends": "NetworkRowLabel", - "text": "Validator", - "color": "white", - "background": "white .1", - "theme": null, - "fontSize": "Z2", - "hide": "(el, s) => !s.validator_info?.length" - }, - "Rpc": { - "extends": "NetworkRowLabel", - "text": "RPC", - "color": "white", - "background": "white .1", - "theme": null, - "fontSize": "Z2", - "hide": "(el, s) => !s.rpc_info?.length" - }, - "Mainnet": { - "extends": "NetworkRowLabel", - "text": "Mainnet", - "color": "white", - "background": "white .1", - "theme": null, - "fontSize": "Z2", - "hide": "(el, s) => !el.call('filterByEnv', s.validator_info, 'Mainnet') && !el.call('filterByEnv', s.rpc_info, 'Mainnet')", - "order": 12 - }, - "Testnet": { - "extends": "NetworkRowLabel", - "text": "Testnet", - "color": "white", - "background": "white .1", - "theme": null, - "fontSize": "Z2", - "hide": "(el, s) => !el.call('filterByEnv', s.validator_info, 'Testnet') && !el.call('filterByEnv', s.rpc_info, 'Testnet')", - "order": 12 - }, - "gap": "B", - "align": "center", - "padding": "A", - "width": "100%", - "Icon": { - "order": 13, - "icon": "(el, s) => 'chevron' + (s.isActive ? 'Up' : 'Down')" - }, - "Spacer": { - "flex": 1, - "order": 10 - } - }, - "props": { - "href": "(el, s) => '/network/' + s.protocol", - "align": "center", - "gap": "Z2", - "childProps": {}, - "padding": "A A2", - "width": "100%", - "flexFlow": "y", - "userSelect": "none", - "cursor": "pointer" - } - }, - "Column": { - "text": "{{ value }}", - "props": { - "padding": "Z" - } - }, - "LabelTag": { - "tag": "label", - "props": { - "text": "-2.901", - "theme": "warning", - "round": "B", - "padding": "X Y", - "lineHeight": "1em" - } - }, - "ValidatorInfo": { - "extend": "Flex", - "props": "(el, s) => ({\n flexFlow: 'y',\n gap: 'C1',\n padding: 'A A2',\n round: 'A2 A2 B+X B+X',\n transition: 'B1 defaultBezier opacity',\n })", - "Header": { - "extends": "Flex", - "align": "center", - "gap": "Z", - "Avatar": { - "src": "{{ protocol }}.png", - "boxSize": "B1" - }, - "H5": { - "lineHeight": 1, - "text": "{{ protocol }}" - } - }, - "Flex": { - "if": "() => false", - "Add": { - "extends": "Button", - "theme": "transparent", - "padding": "Z A", - ":hover": { - "theme": "dialog" - }, - "icon": "plus", - "text": "Add Node", - "gap": "Y1", - "onClick": "(ev, el, s) => {\n ev.stopPropagation()\n s.root.update({\n editMode: true\n })\n }" - }, - "align": "center space-between", - "gap": "B2", - "Repo": { - "extends": [ - "IconText", - "Link" - ], - "fontWeight": "300", - "order": 10, - "icon": "(el, s) => s.repo_url?.includes('gitlab') ? 'gitlab' : 'github'", - "gap": "X2", - "href": "{{repo_url}}", - "text": "(el, s) => s.repo_url?.split('.com/')[1] || '...'", - ":hover": { - "textDecoration": "underline" - }, - "margin": "- - - auto", - "target": "_blank" - }, - "padding": "- A - -" - }, - "Grid": { - "childExtends": "Hgroup", - "templateColumns": "repeat(4, 1fr)", - "gap": "C", - "childProps": { - "H": { - "tag": "h6", - "order": 2 - }, - "P": { - "color": "caption", - "fontSize": "Z1" - } - }, - "children": [ - { - "H": { - "text": "{{ network_type }}" - }, - "P": { - "text": "Network Type" - } - }, - { - "H": { - "text": "{{ network_layer }}" - }, - "P": { - "text": "Network Layer" - } - }, - { - "H": { - "text": "{{ participation }}" - }, - "P": { - "text": "Network access" - } - }, - { - "gridColumn": "span 3", - "H": { - "text": null, - "Link": { - "href": "{{repo_url}}", - "text": "(el, s) => s.repo_url?.split('.com/')[1] || '...'", - "target": "_blank", - ":hover": { - "textDecoration": "underline" - } - } - }, - "P": { - "text": "Repo URL" - } - } - ] - }, - "NoContent": { - "show": "(el, s) => !s.validators?.length && !s.rpc_nodes?.length", - "padding": "A A2", - "textAlign": "center", - "text": "Network is offline" - }, - "Both": { - "margin": "B -Z2 A", - "flexFlow": "y", - "gap": "Z", - "Title": { - "fontSize": "Z2", - "margin": "- - - Z2", - "text": "Nodes ", - "Span": { - "fontWeight": "100", - "text": "(el, s) => `(${(s.validators?.length + s.rpc_nodes?.length) || 0})`" - } - }, - "Validators": { - "show": "(el, s) => s.validators?.length", - "children": "(el, s) => s.validators", - "extends": "ValidatorsList" - }, - "RPC": { - "show": "(el, s) => s.rpc_nodes?.length", - "children": "(el, s) => s.rpc_nodes", - "extends": "ValidatorsList" - } - }, - "Communication": { - "flexFlow": "y", - "gap": "Z", - "Title": { - "fontSize": "Z2", - "text": "Communication Channels" - }, - "Grid": { - "margin": "A - - -", - "hide": "(el, s) => !s.communication_channels", - "templateColumns": "repeat(4, 1fr)", - "childProps": { - "Link": { - "href": "{{ value }}", - "text": "{{ key }}", - "target": "_blank", - ":hover": { - "textDecoration": "underline" - } - } - }, - "childrenAs": "state", - "children": "(el, s) => {\n if (s.communication_channels) {\n const channels = JSON.parse(s.communication_channels)[0]\n const keyVal = Object.entries(channels).filter(v => v[1])\n return keyVal.map(v => ({\n key: v[0],\n value: v[1]\n }))\n }\n }" - }, - "NoContent": { - "hide": "(el, s) => s.communication_channels", - "text": "n/a", - "tag": "h6", - "margin": "0", - "fontWeight": "300", - "color": "caption" - } - } - }, - "ValidatorContent": { - "Grid": { - "childExtends": "PreviewItem", - "gap": "C", - "columns": "repeat(4, 1fr)", - "Monkier": { - "P": { - "text": "Moniker" - }, - "H": { - "isNan": "(el, s) => !s.moniker", - "text": "(el, s) => s.moniker || 'n/a'", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "Env": { - "P": { - "text": "Environment" - }, - "H": { - "isNan": "(el, s) => !s.env", - "text": "(el, s) => s.env || 'n/a'", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "Status": { - "P": { - "text": "Status" - }, - "H": { - "flexFlow": "x", - "gap": "X2", - "flexAlign": "center start", - "alignSelf": "start", - "text": null, - "Status": { - "props": "(el, s) => ({\n hide: (el, s) => !s.status,\n order: '-1',\n background: el.call('getStatusColor', s.status),\n round: 'C',\n boxSize: 'Y2'\n })" - }, - "Text": { - "isNan": "(el, s) => !s.status", - "text": "(el, s) => s.status || 'n/a'", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - } - }, - "Owner": { - "P": { - "text": "Node Operator" - }, - "H": { - "hide": "(el, s) => s.owner", - "text": "n/a", - "fontWeight": "300", - "color": "caption" - }, - "LabelTag": { - "hide": "(el, s) => !s.owner", - "flexFlow": "x", - "gap": "X2", - "flexAlign": "center start", - "alignSelf": "start", - "background": "white", - "color": "black", - "fontSize": "Z2", - "Avatar": { - "boxSize": "A" - }, - "Text": { - "text": "(el, s) => s.owner" - }, - "text": null - } - }, - "Version": { - "P": { - "text": "Client version" - }, - "H": { - "text": "(el, s) => s.client_version || 'n/a'", - "isNan": "(el, s) => !s.owner", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "RewardClaim": { - "P": { - "text": "Reward Claim" - }, - "H": { - "isNan": "(el, s) => !s.reward_claim", - "text": "(el, s) => s.reward_claim || 'n/a'", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "Category": { - "P": { - "text": "Category" - }, - "H": { - "isNan": "(el, s) => !s.category", - "text": "(el, s) => s.category || 'n/a'", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "CloudProvider": { - "P": { - "text": "Cloud Provider" - }, - "H": { - "isNan": "(el, s) => !s.cloud_provider", - "text": "(el, s) => s.cloud_provider || 'n/a'", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "Reward_address": { - "gridColumn": "span 2", - "P": { - "text": "Reward Address" - }, - "H": { - "extends": "Flex", - "flexAlign": "center start", - "Text": { - "overflow": "hidden", - "maxWidth": "95%", - "textOverflow": "ellipsis", - "text": "{{reward_address}}" - }, - "Text_no": { - "color": "caption", - "hide": "(el, s) => s.reward_address", - "fontWeight": "300", - "text": "n/a" - }, - "CopyButton": { - "hide": "(el, s) => !s.reward_address", - "value": "(el, s) => s.reward_address" - }, - "text": null - } - }, - "PublicKey_address": { - "gridColumn": "span 2", - "P": { - "text": "Public key" - }, - "H": { - "extends": "Flex", - "flexAlign": "center start", - "Text": { - "overflow": "hidden", - "maxWidth": "95%", - "textOverflow": "ellipsis", - "text": "{{public_key}}" - }, - "Text_no": { - "color": "caption", - "hide": "(el, s) => s.public_key", - "fontWeight": "300", - "text": "n/a" - }, - "CopyButton": { - "hide": "(el, s) => !s.public_key", - "value": "(el, s) => s.public_key" - }, - "text": null - } - }, - "Hr": { - "ignoreChildExtend": true, - "margin": "A 0", - "opacity": ".05", - "gridColumn": "span 4" - }, - "OurProposal": { - "P": { - "text": "Proposals managed by us" - }, - "H": { - "isNan": "(el, s) => !s.do_we_manage_proposals", - "text": "(el, s) => s.do_we_manage_proposals || 'n/a'", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "CurrentSpent": { - "P": { - "text": "Current Spent" - }, - "H": { - "text": "(el, s) => s.projected_cost ? `$${ s.current_spend?.toFixed(2) }` : 'n/a'" - } - }, - "ProjectedCost": { - "P": { - "text": "Projected Cost" - }, - "H": { - "text": "(el, s) => s.projected_cost ? `$${ s.projected_cost?.toFixed(2) }` : 'n/a'", - "isNan": "(el, s) => !s.owner", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "Consensus": { - "P": { - "text": "Consensus" - }, - "H": { - "text": "(el, s) => s.consensus || 'n/a'", - "isNan": "(el, s) => !s.consensus", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "SLA": { - "P": { - "text": "SLA" - }, - "H": { - "text": "(el, s) => s.sla || 'n/a'", - "isNan": "(el, s) => !s.sla", - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "WarehouseSupport": { - "if": "(el, s) => s.nodeType === 'rpc'", - "P": { - "text": "Data warehouse support" - }, - "H": { - "text": "(el, s) => s.data_warehouse_support ? 'Yes' : 'No'" - } - }, - "WarehouseUrl": { - "if": "(el, s) => s.nodeType === 'rpc'", - "gridColumn": "span 2", - "P": { - "text": "Data warehouse URL" - }, - "H": null, - "Link": { - "isNan": "(el, s) => !s.data_warehouse_url", - "fontSize": "B", - "target": "_blank", - "href": "(el, s) => s.data_warehouse_url", - "text": "(el, s) => s.data_warehouse_url ? s.data_warehouse_url.slice(0, 56) + '...' : 'n/a'", - "fontWeight": "400", - "[href]:hover": { - "textDecoration": "underline" - }, - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "KnowledgeBase": { - "gridColumn": "span 2", - "P": { - "text": "Knowledge base" - }, - "H": null, - "Link": { - "isNan": "(el, s) => !s.knowledge_base", - "fontSize": "B", - "target": "_blank", - "href": "(el, s) => s.knowledge_base", - "text": "(el, s) => s.knowledge_base ? s.knowledge_base.slice(0, 56) + '...' : 'n/a'", - "fontWeight": "400", - "[href]:hover": { - "textDecoration": "underline" - }, - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - }, - "Explorer": { - "gridColumn": "span 2", - "P": { - "text": "Explorer link" - }, - "H": null, - "Link": { - "isNan": "(el, s) => !s.explorer_link", - "fontSize": "B", - "target": "_blank", - "href": "(el, s) => s.explorer_link", - "text": "(el, s) => s.explorer_link ? s.explorer_link.slice(0, 56) + '...' : 'n/a'", - "fontWeight": "400", - "[href]:hover": { - "textDecoration": "underline" - }, - ".isNan": { - "fontWeight": "300", - "color": "caption" - } - } - } - }, - "Hr": { - "ignoreChildExtend": true, - "margin": "-B2 0", - "opacity": ".05", - "gridColumn": "span 4" - }, - "Uptime": {}, - "Graphs": {}, - "props": { - "width": "100%", - "flow": "y", - "gap": "E", - "padding": "A2", - "onClick": "(ev, el, s) => {\n ev.stopPropagation()\n ev.preventDefault()\n }" - }, - "extend": "Flex" - }, - "PreviewItem": { - "props": { - "flexFlow": "y", - "align": "start start" - }, - "P": { - "text": "RAM", - "fontSize": "Z1", - "color": "caption" - }, - "H": { - "text": "100%", - "margin": "0", - "lineHeight": 1, - "tag": "h6", - "order": 2 - } - }, - "ValidatorRow": { - "extend": "Link", - "props": { - "flexFlow": "x", - "gap": "A2", - "padding": "A1 B", - "flexAlign": "center start", - "cursor": "pointer", - "background": "deepFir", - "href": "(el, s) => '/node/' + s.parent.protocol + '/' + (el.__ref.path.includes('RPC') ? 'rpc' : 'validator') + '/{{ uid }}'", - ":hover": { - "background": "deepFir 1 +5" - }, - "transition": "B defaultBezier background", - "round": "C1" - }, - "Status": { - "props": "(el, s) => ({\n order: '-1',\n background: el.call('getStatusColor', s.status),\n round: 'C',\n boxSize: 'Y2'\n })" - }, - "Strong": { - "text": "{{moniker}}" - }, - "Version": { - "text": "({{ client_version }})", - "color": "white", - "fontSize": "Z2", - "fontWeight": "200" - }, - "PublicKey": { - "extends": "Flex", - "flexAlign": "center start", - "hide": "(el, s) => !s.public_key", - "Text": { - "text": "{{public_key}}", - "overflow": "hidden", - "maxWidth": "95%", - "textOverflow": "ellipsis" - }, - "CopyButton": { - "value": "(el, s) => s.public_key" - } - }, - "Env": { - "extends": "NetworkRowLabel", - "fontWeight": "500", - "fontSize": "Z2", - "text": "(el, s) => s.env", - "background": "white .25", - "color": "white" - } - }, - "ValidatorFooter": { - "props": { - "childProps": { - "theme": "transparent", - "padding": "Z A", - ":hover": { - "theme": "dialog" - } - }, - "padding": "A", - "gap": "C1", - "margin": "0 -Y", - "onClick": "(ev, el, s) => {\n ev.stopPropagation()\n ev.preventDefault()\n }" - }, - "extend": "Flex", - "Edit": { - "icon": "check", - "text": "Edit", - "gap": "Y1", - "onClick": "(ev, el, s) => {\n s.root.update({\n editMode: true\n })\n }" - }, - "childExtend": "Button" - }, - "FormModal": { - "extend": "ModalWindow", - "props": { - "maxHeight": "95dvh", - "overflow": "hidden auto", - "widthRange": "H1", - "@mobileS": { - "fontSize": "Z2" - }, - "@mobileXS": { - "fontSize": "Z1" - } - }, - "Hgroup": { - "margin": "- W B+V2 W", - "H": {}, - "P": {} - }, - "XBtn": {}, - "Form": { - "extends": "ModalForm" - } - }, - "EditForm": { - "extend": "FormModal", - "props": { - "gap": "C", - "width": "80%", - "maxWidth": "H3", - "onSubmit": "async (ev, el, s) => {\n ev.preventDefault()\n\n await el.call('addNew')\n }" - }, - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Update Network" - }, - "P": { - "text": "Edit properties for existing Network" - } - }, - "Form": { - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Protocol',\n },\n Field: {\n Input: {\n name: 'protocol',\n placeholder: 'E.g. Polygon',\n type: 'text',\n value: '{{ protocol }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Network Layer',\n },\n Field: {\n Input: {\n placeholder: 'v0.1.2',\n value: '{{ network_layer }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Network Type',\n },\n Field: {\n Input: {\n placeholder: 'v0.1.2',\n value: '{{ network_type }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Repository',\n },\n Field: {\n Input: {\n placeholder: 'https://github.com',\n type: 'url',\n value: '{{ repo_url }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "Button": { - "text": "Save", - "theme": "primary", - "type": "submit" - }, - "tag": "form" - }, - "FieldCaption": { - "extend": "Flex", - "props": { - "flow": "column", - "boxSize": "fit-content fit-content" - }, - "Caption": { - "tag": "caption", - "text": "Caption", - "lineHeight": "1em", - "fontSize": "A", - "fontWeight": "400", - "padding": "- Y2 Z X", - "alignSelf": "flex-start", - "whiteSpace": "nowrap", - "textAlign": "left" - }, - "Field": { - "width": "100%", - "Input": { - "width": "100%" - }, - "Icon": {} - } - }, - "FilterSidebar": { - "props": { - "gap": "A", - "flexFlow": "x", - "flexAlign": "center start" - }, - "extend": "DropdownParent", - "Button": { - "text": "(el, s) => s.isUpdate ? 'Updates' : 'All validators'", - "theme": "transparent", - "gap": "Z2", - "Icon": { - "name": "chevronDown", - "order": 2, - "minWidth": "Z2" - }, - "fontWeight": "300", - "paddingInline": "0", - "width": "E3", - "flexAlign": "center start" - }, - "Dropdown": { - "left": "-X", - "backdropFilter": "blur(3px)", - "background": "softBlack .9 +65", - "childExtends": "Button", - "childProps": { - "theme": "transparent", - "align": "start", - "padding": "Z2 A" - }, - "flexFlow": "y" - } - }, - "FilterStatus": { - "extend": "Flex", - "Stable": { - "Box": { - "background": "green", - "round": "C", - "boxSize": "Y2" - }, - "padding": "Z2", - ":hover": { - "theme": "field" - }, - "round": "C1", - "onClick": "(ev, el, s) => s.update({\n status: s.status === 'Stable/Maintenance' ? null : 'Stable/Maintenance'\n })", - ".isInactive": { - "opacity": "0.35" - }, - "isInactive": "(el, s) => s.status && s.status !== 'Stable/Maintenance'", - "isActive": "(el, s) => s.status === 'Stable/Maintenance'", - ".isActive": { - "theme": "field" - } - }, - "Off": { - "Box": { - "background": "gray", - "round": "C", - "boxSize": "Y2" - }, - "padding": "Z2", - ":hover": { - "theme": "field" - }, - "round": "C1", - "onClick": "(ev, el, s) => s.update({\n status: s.status === 'Off' ? null : 'Off'\n })", - ".isInactive": { - "opacity": "0.35" - }, - "isInactive": "(el, s) => s.status && s.status !== 'Off'", - "isActive": "(el, s) => s.status === 'Off'", - ".isActive": { - "theme": "field" - } - } - }, - "Dropdown": { - "props": { - "isDropdownRoot": true, - "theme": "dialog", - "round": "Z2", - "position": "absolute", - "left": "50%", - "transform": "translate3d(0, -10px, 0)", - "top": "102%", - "minWidth": "F", - "maxHeight": "H", - "transition": "A defaultBezier", - "transitionProperty": "visibility, transform, opacity", - "overflow": "hidden auto", - "opacity": "0", - "visibility": "hidden", - "boxSizing": "border-box", - "zIndex": 1000, - "@dark": { - "boxShadow": "black .20, 0px, 5px, 10px, 5px" - }, - "@light": { - "boxShadow": "gray5 .20, 0px, 5px, 10px, 5px" - } - }, - "attr": { - "dropdown": true - }, - "tag": "section" - }, - "CopyButton": { - "extend": "IconButton", - "props": { - "icon": "copy outline", - "background": "transparent", - "color": "currentColor", - "isActive": false, - "padding": "Y1", - "fontSize": "Z", - "--spacing-ratio": 1.2, - "onClick": "async (ev, el, s, ctx) => {\n ev.preventDefault()\n ev.stopPropagation()\n el.setProps({\n Icon: {\n name: 'check outline'\n }\n })\n const t = setTimeout(() => {\n el.setProps({\n Icon: {\n name: 'copy outline'\n }\n })\n clearTimeout(t)\n }, 1000)\n\n await el.call(\n 'copyStringToClipboard',\n el.call('exec', el.props.value, el) ||\n el.call('exec', s.key, el) ||\n el.call('exec', s.value, el)\n )\n }" - } - }, - "ValidatorLogs": { - "props": { - "gap": "B2", - "columns": "repeat(3, 1fr)", - "margin": "0 -Y", - "padding": "A2 Z2+X", - "onClick": "(ev, el, s) => {\n ev.stopPropagation()\n ev.preventDefault()\n }" - }, - "extend": "Grid", - "childExtend": "LogItem", - "RAM": { - "Title": { - "text": "Client Version" - }, - "Strong": { - "text": "{{ client_version }}" - } - }, - "Reward_address": { - "Title": { - "text": "Reward Address" - }, - "Strong": { - "text": "{{ reward_address }}" - } - }, - "CloudProvider": { - "Title": { - "text": "Cloud Provider" - }, - "Strong": { - "text": "{{ cloud_provider }}" - } - }, - "Owner": { - "Title": { - "text": "Owner" - }, - "Strong": { - "text": "{{ owner }}" - } - }, - "Monkier": { - "Title": { - "text": "Monkier" - }, - "Strong": { - "text": "{{ moniker }}" - } - } - }, - "LogItem": { - "props": { - "padding": "A2", - "flexFlow": "y", - "gap": "Z2", - "theme": "dialog", - "round": "A" - }, - "Title": { - "text": "RAM" - }, - "H3": { - "text": "100%", - "margin": "0", - "lineHeight": 1 - } - }, - "Chart": { - "props": { - "onRender": "async (el, s) => {\n const Chart = await import('chart.js')\n const ctx = el.node.getContext('2d');\n\n const maxPoints = 50;\n const dataPoints = Array(maxPoints).fill(0);\n\n const labels = Array.from({\n length: maxPoints\n }, (_, i) => i.toString());\n\n // Initialize with some starting data\n for (let i = 0; i < dataPoints.length; i++) {\n dataPoints[i] = 30 + Math.sin(i * 0.2) * 20;\n }\n\n const data = {\n labels: labels,\n datasets: [{\n label: 'CPU',\n data: dataPoints.map((value, index) => ({\n x: index,\n y: value\n })),\n borderColor: 'rgb(86, 154, 67)',\n fill: false,\n tension: 0.4,\n pointRadius: 0,\n borderWidth: 2,\n cubicInterpolationMode: 'monotone'\n }]\n };\n\n const options = {\n responsive: true,\n animation: false,\n interaction: {\n intersect: false,\n mode: 'index'\n },\n plugins: {\n legend: {\n display: false\n },\n tooltip: {\n enabled: false\n }\n },\n scales: {\n x: {\n display: false,\n type: 'linear',\n min: 0,\n max: maxPoints - 1\n },\n y: {\n display: false,\n min: 0,\n max: 100\n }\n },\n elements: {\n line: {\n borderJoinStyle: 'round'\n }\n },\n maintainAspectRatio: false\n };\n\n const chart = new Chart(ctx, {\n type: 'line',\n data: data,\n options: options\n });\n\n // Color functions\n function getColor(value) {\n if (value < 60) return 'rgb(86, 154, 67)';\n if (value < 80) return 'rgb(245, 158, 11)';\n return 'rgb(220, 38, 38)';\n }\n\n function interpolateColor(color1, color2, factor) {\n const c1 = color1.match(/\\d+/g).map(Number);\n const c2 = color2.match(/\\d+/g).map(Number);\n const r = Math.round(c1[0] + (c2[0] - c1[0]) * factor);\n const g = Math.round(c1[1] + (c2[1] - c1[1]) * factor);\n const b = Math.round(c1[2] + (c2[2] - c1[2]) * factor);\n return `rgb(${r}, ${g}, ${b})`;\n }\n\n let animationFrame = null;\n let isAnimating = false;\n\n function updateCPUChart(newValue) {\n if (isAnimating) return;\n\n isAnimating = true;\n const dataset = chart.data.datasets[0];\n const originalData = [...dataset.data];\n const lastPoint = originalData[originalData.length - 1];\n const startTime = performance.now();\n const duration = 1000;\n\n // Get colors for transition\n const startColor = dataset.borderColor;\n const endColor = getColor(newValue);\n\n function animate(currentTime) {\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / duration, 1);\n const easeProgress = easeInOutCubic(progress);\n\n // Create new animated data\n const animatedData = [];\n\n // Animate all existing points shifting left (keep their y values)\n for (let i = 0; i < originalData.length; i++) {\n animatedData.push({\n x: originalData[i].x - easeProgress,\n y: originalData[i].y // Keep original y value unchanged\n });\n }\n\n // Add new point entering from the right, transitioning from lastPoint.y to newValue\n animatedData.push({\n x: maxPoints - 1 + (1 - easeProgress),\n y: lastPoint.y + (newValue - lastPoint.y) * easeProgress\n });\n\n dataset.data = animatedData;\n\n // Animate color if crossing threshold\n if (getColor(lastPoint.y) !== getColor(newValue)) {\n dataset.borderColor = interpolateColor(startColor, endColor, easeProgress);\n }\n\n chart.update('none');\n\n if (progress < 1) {\n animationFrame = requestAnimationFrame(animate);\n } else {\n // Final state: shift all data and add new point\n dataset.data = [\n ...originalData.slice(1).map((point, i) => ({\n x: i,\n y: point.y\n })),\n {\n x: maxPoints - 1,\n y: newValue\n }\n ];\n dataset.borderColor = endColor;\n chart.update('none');\n isAnimating = false;\n }\n }\n\n animationFrame = requestAnimationFrame(animate);\n }\n\n function easeInOutCubic(t) {\n return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;\n }\n\n // Simulate CPU data updates\n const interval = setInterval(() => {\n const fakeCPU = Math.floor(Math.random() * 100);\n updateCPUChart(fakeCPU);\n }, 1100);\n\n // Cleanup function\n return () => {\n clearInterval(interval);\n if (animationFrame) {\n cancelAnimationFrame(animationFrame);\n }\n chart.destroy();\n };\n }", - "tag": "canvas", - "minWidth": "G", - "minHeight": "D" - }, - "data": {}, - "Block": { - "data": {}, - "Number": { - "text": "5,104,599", - "fontSize": "D", - "position": "absolute", - "top": "50%", - "left": "50%", - "transform": "translate(-50%, -50%)", - "textShadow": "0 0 10px rgba(0, 0, 0, 0.5)", - "fontWeight": "500" - }, - "Box": { - "tag": "canvas", - "minWidth": "G", - "minHeight": "D", - "onRender": "async (el, s) => {\n const Chart = window.Chart;\n const ctx = el.node.getContext('2d');\n const canvas = el.node;\n\n const maxPoints = 100;\n let currentHeight = 5104596;\n\n // Create initial data with a slight upward trend\n const data = {\n labels: Array(maxPoints).fill(''),\n datasets: [{\n label: 'Chain height',\n data: Array.from({\n length: maxPoints\n }, (_, i) =>\n currentHeight - (maxPoints - i) * 10\n ),\n borderColor: 'rgb(86, 154, 67)',\n backgroundColor: 'transparent',\n borderWidth: 2,\n fill: false,\n tension: 0.4,\n pointRadius: 0,\n }]\n };\n\n // Chart configuration\n const config = {\n type: 'line',\n data: data,\n options: {\n responsive: true,\n maintainAspectRatio: false,\n animation: false,\n scales: {\n x: {\n display: false,\n grid: {\n display: false\n }\n },\n y: {\n display: false,\n grid: {\n display: false\n }\n }\n },\n plugins: {\n legend: {\n display: false\n },\n tooltip: {\n enabled: false\n }\n },\n elements: {\n line: {\n borderCapStyle: 'round',\n borderJoinStyle: 'round'\n }\n }\n }\n };\n\n // Create the chart\n const chart = new Chart(ctx, config);\n\n // Update function\n const updateChart = () => {\n // Simulate block height increase\n currentHeight += Math.floor(Math.random() * 3) + 1;\n\n // Update the data\n const newData = [...chart.data.datasets[0].data];\n newData.shift();\n newData.push(currentHeight);\n chart.data.datasets[0].data = newData;\n\n // Update the chart\n chart.update('none');\n\n // Update the overlay\n const Number = el.parent.Number\n Number.setProps({\n text: currentHeight.toLocaleString()\n })\n };\n\n // Start updating every second\n const interval = setInterval(updateChart, 3000);\n\n // Clean up\n const cleanup = () => {\n clearInterval(interval);\n chart.destroy();\n };\n\n // Handle unmount\n canvas.addEventListener('unmount', cleanup);\n\n // Also clean up if the canvas is removed from DOM\n const observer = new MutationObserver((mutations) => {\n if (!document.contains(canvas)) {\n cleanup();\n observer.disconnect();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true\n });\n }" - }, - "props": { - "position": "relative" - } - } - }, - "Uptime": { - "extend": "Flex", - "Flex": { - "maxWidth": "100%", - "childProps": { - "minWidth": "Z1", - "boxSize": "Z1", - "background": "green .3", - "border": "1px, solid, green", - "round": "W", - "style": { - "&:hover": { - "opacity": "1 !important" - } - }, - "transition": "A defaultBezier opacity" - }, - "gap": "X", - "children": "(el, s) => new Array(300).fill({})", - "flow": "row wrap", - "height": "2.8em", - "overflow": "hidden", - ":hover > div": { - "opacity": 0.5 - } - }, - "props": { - "flow": "y", - "gap": "Z" - }, - "Title": { - "fontSize": "Z", - "text": "Uptime", - "order": "-1" - } - }, - "Logs": { - "extend": "Flex", - "props": { - "flexFlow": "y", - "borderWidth": "0 0 0 2px", - "borderStyle": "solid", - "borderColor": "--theme-document-dark-background", - "minWidth": "G3", - "padding": "A A2", - "gap": "A2", - "onRender": "(el, s) => {\n window.requestAnimationFrame(async () => {\n let [, _, nodeType, uid] = window.location.pathname.split('/')\n if (window.location.pathname === 'srcdoc') {\n uid = '02841d62-e018-45e7-92f4-7928f832cd30'\n }\n\n const logs = await el.call('read', `/node/${uid}/logs`)\n\n el.call('setInitialData', {\n logs\n })\n })\n }", - "@tabletM": { - "hide": true - } - }, - "H6": { - "fontSize": "Z2", - "text": "Logs", - "fontWeight": "bold" - }, - "Flex": { - "flow": "y", - "children": "(el, s) => s.logs", - "childrenAs": "state", - "padding": "Z2", - "gap": "B2", - "childProps": { - "flexFlow": "y", - "gap": "Y", - "width": "100%", - "position": "relative", - ":hover .buttons": { - "opacity": 1 - }, - ":not(:last-child)": { - "border": "0 0 1px 0, dashed, gray" - }, - "Date": { - "fontWeight": "300", - "fontSize": "Z1", - "color": "caption", - "text": "{{ created_at }}", - "position": "relative", - "Status": { - "round": "C1", - "boxSize": "Z", - "border": "1px, solid, caption", - "position": "absolute", - "top": "Y1", - "left": "-A2" - } - }, - "Notes": { - "text": "{{ status_notes }}" - }, - "Title": { - "text": "{{ action_items }}" - } - } - } - }, - "Graphs": { - "extend": "Flex", - "Flex": { - "children": "(el, s) => ['RAM', 'CPU']", - "childrenAs": "state", - "childExtends": "Flex", - "gap": "D1", - "childProps": { - "flex": 1, - "flow": "y", - "gap": "Z", - "Title": { - "fontSize": "Z", - "text": "{{ value }}", - "textTransform": "uppercase", - "whiteSpace": "nowrap" - }, - "Chart": { - "heightRange": "D3" - } - }, - "flex": 2, - "order": 2 - }, - "props": { - "gap": "D1" - }, - "BlockHeight": { - "extends": "Flex", - "flex": 1, - "flow": "y", - "gap": "Z", - "Title": { - "fontSize": "Z", - "whiteSpace": "nowrap", - "textTransform": "uppercase", - "text": "Block Height" - }, - "Chart.Block": { - "heightRange": "D3", - "variant": "Block", - "position": "relative" - }, - "position": "relative" - } - }, - "Chart.Block": { - "data": {}, - "props": { - "position": "relative" - }, - "Number": { - "text": "5,104,599", - "fontSize": "D", - "position": "absolute", - "top": "50%", - "left": "50%", - "transform": "translate(-50%, -50%)", - "textShadow": "0 0 10px rgba(0, 0, 0, 0.5)", - "fontWeight": "500" - }, - "Box": { - "tag": "canvas", - "minWidth": "G", - "minHeight": "D", - "onRender": "async (el, s) => {\n const Chart = window.Chart;\n const ctx = el.node.getContext('2d');\n const canvas = el.node;\n\n const maxPoints = 100;\n let currentHeight = 5104596;\n\n // Create initial data with a slight upward trend\n const data = {\n labels: Array(maxPoints).fill(''),\n datasets: [{\n label: 'Chain height',\n data: Array.from({\n length: maxPoints\n }, (_, i) =>\n currentHeight - (maxPoints - i) * 10\n ),\n borderColor: 'rgb(86, 154, 67)',\n backgroundColor: 'transparent',\n borderWidth: 2,\n fill: false,\n tension: 0.4,\n pointRadius: 0,\n }]\n };\n\n // Chart configuration\n const config = {\n type: 'line',\n data: data,\n options: {\n responsive: true,\n maintainAspectRatio: false,\n animation: false,\n scales: {\n x: {\n display: false,\n grid: {\n display: false\n }\n },\n y: {\n display: false,\n grid: {\n display: false\n }\n }\n },\n plugins: {\n legend: {\n display: false\n },\n tooltip: {\n enabled: false\n }\n },\n elements: {\n line: {\n borderCapStyle: 'round',\n borderJoinStyle: 'round'\n }\n }\n }\n };\n\n // Create the chart\n const chart = new Chart(ctx, config);\n\n // Update function\n const updateChart = () => {\n // Simulate block height increase\n currentHeight += Math.floor(Math.random() * 3) + 1;\n\n // Update the data\n const newData = [...chart.data.datasets[0].data];\n newData.shift();\n newData.push(currentHeight);\n chart.data.datasets[0].data = newData;\n\n // Update the chart\n chart.update('none');\n\n // Update the overlay\n const Number = el.parent.Number\n Number.setProps({\n text: currentHeight.toLocaleString()\n })\n };\n\n // Start updating every second\n const interval = setInterval(updateChart, 3000);\n\n // Clean up\n const cleanup = () => {\n clearInterval(interval);\n chart.destroy();\n };\n\n // Handle unmount\n canvas.addEventListener('unmount', cleanup);\n\n // Also clean up if the canvas is removed from DOM\n const observer = new MutationObserver((mutations) => {\n if (!document.contains(canvas)) {\n cleanup();\n observer.disconnect();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true\n });\n }" - } - }, - "ValidatorsList": { - "props": { - "childrenAs": "state", - "childProps": { - "flexFlow": "y", - "ValidatorRow": {}, - "ValidatorContent": { - "padding": "B C3 C3", - "hide": "(el, s) => !s.isActive" - } - }, - "gap": "Z", - "flexFlow": "y" - } - }, - "Aside": { - "InputField": { - "Icon": { - "name": "search", - "fontSize": "Z", - "minWidth": "A", - "margin": "- -B2 - Z2", - "opacity": ".35" - }, - "Input": { - "theme": "transparent", - "placeholder": "Filter...", - "fontWeight": "300", - "value": "{{ search }}", - "onInput": "(ev, el, s) => s.update({\n search: el.node.value,\n activeIndex: null\n })", - "padding": "Y Z Y B2", - "width": "F3", - "flex": 1 - }, - "flexFlow": "x", - "flexAlign": "center", - "margin": "- - - -Z2" - }, - "FilterSidebar_validators": { - "Button": { - "text": "(el, s) => s.validators || 'All Validators'" - }, - "Dropdown": { - "childProps": { - ".isActive": { - "background": "white .1" - }, - "isActive": "(el, s) => s.validators === el.text", - "onClick": "(ev, el, s) => {\n s.update({\n validators: el.text,\n activeIndex: null\n })\n }" - }, - "All": { - "text": "All Validators" - }, - "Mainnet": { - "text": "Mainnet" - }, - "Testnet": { - "text": "Testnet" - }, - "None": { - "text": "None" - } - } - }, - "FilterSidebar_rpc": { - "Button": { - "text": "(el, s) => s.rpc || 'All RPC'" - }, - "Dropdown": { - "childProps": { - ".isActive": { - "background": "white .1" - }, - "isActive": "(el, s) => s.rpc === el.text", - "onClick": "(ev, el, s) => {\n s.update({\n rpc: el.text,\n activeIndex: null\n })\n }" - }, - "All": { - "text": "All RPC" - }, - "Mainnet": { - "text": "Mainnet" - }, - "Testnet": { - "text": "Testnet" - }, - "None": { - "text": "None" - } - } - }, - "FilterSidebar_update": { - "Button": { - "text": "(el, s) => s.lastUpdate || 'Last update'" - }, - "Dropdown": { - "childProps": { - ".isActive": { - "background": "white .1" - }, - "isActive": "(el, s) => s.lastUpdate === el.text", - "onClick": "(ev, el, s) => {\n s.update({\n lastUpdate: el.text,\n activeIndex: null\n })\n }" - }, - "All": { - "text": "All Time" - }, - "Today": { - "text": "Today" - }, - "Last2Days": { - "text": "Last 2 Days" - }, - "Last3Days": { - "text": "Last 3 Days" - }, - "LastWeek": { - "text": "Last Week" - }, - "LastMonth": { - "text": "Last Month" - } - } - }, - "FilterSidebar_status": { - "Button": { - "text": "(el, s) => s.status || 'Status'" - }, - "Dropdown": { - "childProps": { - ".isActive": { - "background": "white .1" - }, - "isActive": "(el, s) => s.status === el.text", - "onClick": "(ev, el, s) => {\n s.update({\n status: el.text,\n activeIndex: null\n })\n }" - }, - "All": { - "text": "Stable/Maintenance" - }, - "Today": { - "text": "Off" - } - } - }, - "props": { - "flexFlow": "y", - "gap": "B2", - "fontSize": "Z2+V", - "margin": "Z2 - -" - } - }, - "AIMessage": { - "extend": [ - "LabelTag", - "Focusable" - ], - "props": { - "theme": "field", - "tag": "div", - "padding": "Z1 B1 Z1 A2", - "contentEditable": true, - "color": "caption", - "lineHeight": "1.55em", - "fontWeight": "400", - "text": null, - "html": "(el, s) => s.prompt", - "whiteSpace": "wrap", - "wordBreaking": "break-all", - ":empty:before": { - "opacity": 0.35, - "color": "disabled", - "content": "\"Ask me . . .\"" - } - }, - "tag": "div", - "text": null, - "html": "(el, s) => s.prompt" - }, - "AIThread": { - "extend": "Flex", - "props": { - "padding": "A1", - "width": "100%", - "maxWidth": "100%", - "margin": "- auto", - "height": "100%", - "transition": "A defaultBezier height", - "maxHeight": "50dvh", - "flow": "y", - "gap": "A2", - "overflow": "auto", - "::-webkit-scrollbar": { - "display": "none" - }, - "children": "(el, s) => s.thread", - "childrenAs": "state" - }, - "childExtend": { - "props": "(el, s) => ({\n alignSelf: s.role === 'user' ? 'start' : 'end',\n ':first-child': {\n margin: 'auto - -'\n },\n onRender: el => {\n // prevent scrolling in platform\n // if (location.host === 'symbols.app') return\n\n // run setTimout to run it after everything else\n const t = setTimeout(() => {\n el.log('Scrolling', el.node, el)\n // el.node.scrollIntoView()\n window.scrollBy(0, 80)\n clearTimeout(t)\n }, 35)\n }\n })", - "content": "(el, s) => {\n if (s.role === 'user') {\n return {\n extend: 'AIMessage',\n props: {\n contentEditable: false,\n shape: 'bubble',\n shapeDirection: 'top left',\n round: 'X C C C',\n padding: 'Y2 A',\n margin: '- -Z',\n // round: 'C',\n maxWidth: 'fit-content'\n },\n Message: {\n props: {},\n html: (el, s) => s.message\n },\n }\n }\n\n return {\n extend: 'P',\n props: {\n color: 'paragraph',\n maxWidth: 'I1'\n },\n html: (el, s) => s.message\n }\n }" - } - }, - "Prompt": { - "state": { - "keyword": "", - "thread": [], - "images": [], - "selectedOption": 0 - }, - "tag": "form", - "props": { - "position": "relative", - "margin": "Z 0 -", - "zIndex": "10", - "transition": "Z defaultBezier margin", - "fontSize": "Z2", - "round": 0, - "theme": "transparent", - "onSubmit": "(ev, el, s) => {\n // javascript prevent default to prevent default behaviour\n ev.preventDefault()\n console.log(1)\n\n // get value from state (that we kept in keyup)\n const value = s.keyword\n\n // if value is empty, do nothing (break the function)\n if (!value) return\n\n // how state.apply works https://symbols.app/docs/api/state#methods\n // - instead of object, you can use function and make custom logic\n const makeUserChatData = s => {\n s.thread.push({\n role: 'user', // differenciate if message is by user or agent\n message: value,\n })\n s.keyword = ''\n }\n\n // when 'makeUserChatData' is done, run s.update\n s.apply(makeUserChatData)\n\n // make textarea empty again\n // e.g. reset form\n el.Relative.Textarea.value = ''\n\n // fake functionity that chat is answering\n // reuse\n const answer = setTimeout(async () => {\n const res = await el.call('giveMeAnswer', value)\n console.log(res)\n\n s.apply(s => {\n s.thread.push({\n role: 'agent',\n message: res.summary\n })\n })\n\n const promptedData = res.data[0].data\n s.root.replace({\n promptedData: el.call('isArray', promptedData) ? promptedData : [promptedData]\n })\n\n clearTimeout(answer)\n }, 1000)\n }" - }, - "Relative": { - "position": "relative", - "Textarea": { - "id": "prompt", - "display": "flex", - "padding": "Z2 A", - "maxWidth": "none", - "maxHeight": "E3", - "minHeight": "C3", - "overflow": "hidden auto", - "outline": "none", - "width": "100%", - "borderWidth": "0 0 1px", - "whiteSpace": "pre-wrap", - "borderStyle": "solid", - "borderColor": "line", - "color": "title", - "transition": "Z defaultBezier padding", - "placeholder": "\"Ask, Search, Prompt…\"", - "theme": null, - "background": "transparent", - "round": "0", - "onInput": "(ev, el, s) => {\n let prompt = el.node.value.trim()\n if (prompt === '\\n') prompt = ''\n // s.replace({ keyword: prompt, thread: [] }) // Todo: maybe keep the thread\n s.replace({\n keyword: prompt\n })\n }", - "onKeydown": "(ev, el, s) => {\n if (ev.key === 'Enter' && !ev.shiftKey) {\n ev.preventDefault()\n // console.log(el.parent.parent.node)\n // console.log(s)\n // el.parent.node.submit(ev, el, s)\n }\n // TODO: I think this does not work\n if (ev.key === 'Escape') {\n ev.stopPropagation()\n el.node.blur()\n }\n }" - }, - "Submit": { - "extends": "SquareButton", - "theme": "transparent", - "icon": "check", - "type": "submit", - "position": "absolute", - "bottom": "Z2", - "right": "Z2", - "zIndex": 2 - } - }, - "AIThread": { - "hide": "(el, s) => !s.thread.length" - } - }, - "Search": { - "extend": [ - "Flex" - ], - "tag": "search", - "state": {}, - "props": { - "minWidth": "G2", - "gap": "Z", - "icon": "search", - "align": "center flex-start", - "position": "relative", - "theme": "field", - "round": "D2", - "@mobileS": { - "minWidth": "G1" - } - }, - "Icon": { - "icon": "search", - "right": "A+V2" - }, - "Input": { - "type": "search", - "placeholder": "Type a command or search", - "width": "100%", - "padding": "Z2 C Z2 A2+W", - "theme": "transparent", - ":focus ~ button": { - "opacity": "1" - }, - "onInput": "(ev, el, s) => {\n console.log(el.node.value)\n s.update({\n searchTerm: el.node.value\n })\n }" - } - }, - "NetworkRow": { - "extend": "Grid", - "props": { - "templateColumns": "3fr 3fr 3fr 2fr 2fr", - "gap": "Z2", - "href": "(el, s) => '/network/' + s.protocol", - "align": "center", - "padding": "A1 A2", - "width": "100%", - "poisition": "relative", - "childProps": { - "gap": "Z2", - "flexAlign": "center" - }, - "borderWidth": "0 0 1px 0", - "borderStyle": "solid", - "borderColor": "--theme-document-dark-background", - "userSelect": "none", - "cursor": "pointer", - "transition": "background, defaultBezier, A", - ":hover": { - "background": "deepFir" - }, - ":active": { - "background": "deepFir 1 +5" - }, - "onInit": "(el, s) => {\n const parsed = el.call('parseNetworkRow', s)\n s.parsed = parsed\n }" - }, - "Name": { - "Avatar": { - "src": "{{ protocol }}.png", - "boxSize": "B1" - }, - "Title": { - "tag": "strong", - "flexFlow": "x", - "gap": "X2", - "text": "(el, s) => s.protocol" - } - }, - "Env": { - "childExtends": "NetworkRowLabel", - "childProps": { - "background": "env .25" - }, - "children": "(el, s) => s.parsed?.env" - }, - "NodeTypes": { - "childExtends": "NetworkRowLabel", - "childProps": { - "color": "white", - "theme": null, - "fontSize": "Z2", - "background": "nodeType .25" - }, - "children": "(el, s) => s.parsed?.node_types" - }, - "CloudProvider": { - "childExtends": "NetworkRowLabel", - "childProps": { - "color": "white", - "theme": null, - "fontSize": "Z2", - "background": "cloudProvider .25" - }, - "children": "(el, s) => s.parsed?.cloud_provider" - }, - "Status": { - "childExtends": "NetworkRowLabel", - "childProps": { - "color": "white", - "theme": null, - "fontSize": "Z2", - "background": "SLA .25" - }, - "children": "(el, s) => s.parsed?.status" - }, - "on": { - "init": "(el, s) => {\n const parsed = el.call('parseNetworkRow', s)\n s.parsed = parsed\n }" - } - }, - "Page": { - "props": { - "flexFlow": "y", - "position": "relative", - "heightRange": "100dvh", - "overflow": "hidden" - } - }, - "NavButton": { - "extend": [ - "Link", - "Button" - ], - "props": { - "theme": "button", - "padding": "Z1 A1", - "fontSize": "Z1", - "gap": "Y", - "fontWeight": "300", - "transition": "background, defaultBezier, A", - "text": "300", - "@dark": { - "color": "white", - "background": "transparent", - ":hover": { - "background": "deepFir" - }, - ":active": { - "background": "deepFir 1 +5" - } - } - } - }, - "ModalWindow": { - "extend": "Flex", - "tag": "field", - "props": { - "boxSize": "fit-content", - "align": "stretch flex-start", - "minWidth": "G3", - "position": "relative", - "round": "B1", - "theme": "dialog", - "flow": "y", - "padding": "A2 A2 A1 A2", - "borderStyle": "none", - "@mobileS": { - "fontSize": "Z2", - "minWidth": "G2" - } - }, - "Hgroup": { - "gap": "Y", - "H": { - "tag": "h5", - "margin": "0", - "fontWeight": "700" - }, - "P": { - "margin": "0" - } - }, - "XBtn": { - "position": "absolute", - "right": "Z1+W", - "top": "Z+W", - "round": "100%", - "fontSize": "A1", - "$isSafari": { - "top": "Z2", - "right": "Z2" - }, - "Icon": { - "name": "x" - }, - "onClick": "(ev, el, s) => {\n s.root.update({\n editMode: false\n })\n el.lookup('Modal').removeContent()\n }" - } - }, - "Modal": { - "props": "(el, s) => ({\n position: 'absolute',\n inset: '0',\n background: 'black 0.95 +15',\n backdropFilter: 'blur(3px)',\n flexAlign: 'center center',\n zIndex: 99,\n transition: 'all defaultBezier B',\n pointerEvents: 'auto',\n ...(s.root?.modal ? {\n opacity: 1,\n visibility: 'visible',\n } : {\n opacity: 0,\n visibility: 'hidden',\n }),\n ':empty': {\n opacity: 0,\n visibility: 'hidden',\n pointerEvents: 'none',\n },\n onClick: (ev, el, s) => {\n console.log(1)\n s.root.update({\n modal: false\n })\n\n if (el.key === 'Modal') el.removeContent()\n else el.lookup('Modal').removeContent()\n },\n })", - "content": "(el, s) => ({\n Box: s.root.modal && {\n extend: s.root.modal,\n props: {\n onClick: (ev) => ev.stopPropagation()\n }\n } || {}\n }) || {}" - }, - "MetaSection": { - "state": { - "data": [], - "metrics": [ - { - "title": "Status", - "items": [ - { - "caption": "Live", - "value": "14" - }, - { - "caption": "Onboarding", - "value": "4" - }, - { - "caption": "Degraded", - "value": "0" - } - ] - }, - { - "title": "Fleet cloud distribution", - "items": [ - { - "caption": "GCP", - "value": "4" - }, - { - "caption": "AWS", - "value": "4" - }, - { - "caption": "Rame", - "value": "0" - }, - { - "caption": "OVH", - "value": "14" - } - ] - } - ] - }, - "props": { - "flexFlow": "y", - "borderWidth": "0 0 0 2px", - "borderStyle": "solid", - "borderColor": "--theme-document-dark-background", - "minWidth": "G3", - "padding": "A A2", - "gap": "B2", - "@tabletM": { - "hide": true - } - }, - "H6": { - "fontSize": "Z2", - "text": "Fleet Summary", - "fontWeight": "bold" - }, - "Flex": { - "flow": "y", - "gap": "C1", - "children": "(el, s) => s.metrics", - "childrenAs": "state", - "childProps": { - "flexFlow": "y", - "gap": "A2", - "H6": { - "fontWeight": "700", - "fontSize": "Y1", - "textTransform": "uppercase", - "text": "{{ title }}" - }, - "Grid": { - "templateColumns": "repeat(3, 1fr)", - "gap": "B", - "childExtends": "Hgroup", - "childProps": { - "gap": "X", - "H": { - "tag": "h5", - "text": "{{ value }}" - }, - "P": { - "order": "-1", - "text": "{{ caption }}" - } - }, - "childrenAs": "state", - "children": "(el, s) => s.items" - } - } - } - }, - "NetworkRowLabel": { - "extend": "LabelTag", - "props": { - "color": "white", - "theme": null, - "fontSize": "Z2", - "fontWeight": 300, - "padding": "X1 Y1", - "background": "env .25" - } - }, - "Header": { - "extend": "Flex", - "props": { - "minWidth": "G2", - "icon": "search", - "align": "center flex-start", - "position": "relative", - "theme": "dialog", - "round": "D2", - ":focus-visible": { - "outline": "solid, X, blue .3" - }, - ":focus-within ~ .content": { - "opacity": 0.3, - "pointerEvents": "none" - }, - ":focus-within ~ .search-results": { - "opacity": 0.3 - }, - ":focus-within": { - "@dark": { - "background": "softBlack .95 +4" - }, - "@light": { - "background": "gray1 .95" - } - } - }, - "Link": { - "extends": [ - "Link", - "IconButton" - ], - "paddingInline": "A Z1", - "icon": "chevron left", - "href": "/", - "theme": null, - "hide": "() => window.location.pathname === '/'", - "borderRadius": "0", - "border": null, - "borderWidth": "0 1px 0 0", - "borderStyle": "solid", - "borderColor": "deepFir", - "style": null - }, - "Search": { - "flex": 1, - "theme": "dialog", - "paddingInline": "Z Z1", - "Input": { - "paddingInline": "Z1", - ":focus-visible": null - } - }, - "Avatar": { - "boxSize": "B", - "margin": "Z", - "src": "(el, s) => s.root.user?.picture || `https://avatars.symbo.ls/initials/png?seed=${s.root.user?.name}`" - } - }, - "OldNotes": { - "extend": "Flex", - "Flex": { - "flow": "y", - "children": "(el, s) => s.logs", - "childrenAs": "state", - "padding": "Z2", - "childExtends": "Flex", - "childProps": { - "flow": "y", - "padding": "Z2", - "gap": "Z", - ":not(:last-child)": { - "border": "0 0 1px 0, dashed, gray" - }, - "Notes": { - "order": 0, - "text": "{{ status_notes }}" - }, - "Date": { - "order": 1, - "fontWeight": "300", - "fontSize": "Z1", - "opacity": "0.5", - "margin": "X2 - -", - "text": "{{ created_at }}" - }, - "width": "100%", - ":hover .buttons": { - "opacity": 1 - }, - "Flex": { - "class": "buttons", - "position": "absolute", - "top": "X2", - "right": "X2", - "childExtends": "IconButton", - "Add": { - "icon": "plus", - "onClick": "(ev, el, s) => s.parent.toggle('adding')" - }, - "Edit": { - "icon": "moreHorizontal", - "onClick": "(ev, el, s) => window.alert('Edit soon')" - }, - "Remove": { - "icon": "x", - "onClick": "(ev, el, s) => window.confirm('You sure want to remove this log?')" - }, - "opacity": 0, - "childProps": { - "theme": "transparent" - }, - "gap": "X2" - }, - "position": "relative", - "Title": { - "order": "-1", - "text": "{{ action_items }}" - } - } - }, - "props": { - "flow": "y", - "hide": "(el, s) => !s.logs?.length", - "background": "black .15", - "round": "B", - "margin": "0 -A" - }, - "Addnew": { - "Notes": { - "placeholder": "Status notes", - "value": "(el, s) => s.status_notes" - }, - "childExtends": "Textarea", - "extends": "Flex", - "align": "stretch start", - "childProps": { - "width": "100%", - "maxWidth": "none", - "heightRange": "D1" - }, - "Actions": { - "placeholder": "Action items", - "value": "(el, s) => s.action_items" - }, - "flow": "y", - "gap": "Z2", - "padding": "Z2", - "hide": "(el, s) => !s.adding" - } - }, - "MetaSectionBars": { - "props": { - "flexFlow": "y", - "borderWidth": "0 0 0 2px", - "borderStyle": "solid", - "borderColor": "--theme-document-dark-background", - "minWidth": "G3", - "padding": "A A2", - "gap": "B2", - "@tabletM": { - "hide": true - } - }, - "H6": { - "fontSize": "Z2", - "text": "Fleet Summary", - "fontWeight": "bold" - }, - "Flex": { - "flow": "y", - "gap": "C1", - "children": "(el, s) => s.metrics", - "childrenAs": "state", - "childProps": { - "flexFlow": "y", - "gap": "A", - "H6": { - "fontWeight": "700", - "fontSize": "Y1", - "textTransform": "uppercase", - "text": "{{ title }}" - }, - "Flex": { - "gap": "X", - "style": { - "mixBlendMode": "luminosity" - }, - "childExtends": { - "extend": "TooltipParent", - "TooltipHidden": { - "fontSize": "Z", - "whiteSpace": "nowrap", - "top": "150%", - "padding": "0 A X2", - "shapeDirection": "top", - "text": "{{ caption }} ({{ value }})" - } - }, - "childProps": "(el, s) => ({\n height: 'X2',\n position: 'relative',\n flex: s.value,\n background: el.call('stringToHexColor', s.caption),\n ':after': {\n content: '\"\"',\n position: 'absolute',\n zIndex: '2',\n inset: '-Y2 0',\n }\n })", - "childrenAs": "state", - "children": "(el, s) => s.items" - } - } - } - }, - "Select": { - "tag": "label", - "extend": "Flex", - "props": { - "theme": "transparent", - "position": "relative", - "round": "0", - "align": "center flex-start" - }, - "Selects": { - "tag": "select", - "extends": "Flex", - "fontSize": "A", - "fontWeight": "500", - "boxSize": "100%", - "border": "none", - "padding": "- B - -", - "cursor": "pointer", - "outline": "none", - "pointerEvents": "All", - "appearance": "none", - "height": "100%", - "background": "none", - "color": "title", - "lineHeight": 1, - "zIndex": "2", - "flex": "1", - ":focus-visible": { - "outline": "none" - }, - "childExtends": { - "tag": "option" - }, - "onInit": "(el, s) => {\n el.attr.name = el.call('exec', el.props.name)\n el.attr.value = el.call('exec', el.props.value)\n }", - "childProps": { - "onInit": "(el, s) => {\n el.attr.disabled = el.call('exec', el.props.disabled)\n el.attr.selected = el.call('exec', el.props.selected)\n el.attr.value = el.call('exec', el.props.value)\n }" - }, - "children": "() => [{\n text: 'Please select',\n disabled: 'disabled',\n selected: true\n }, {\n text: 'One',\n value: 'One',\n },\n {\n text: 'Two',\n value: 'Two',\n },\n {\n text: 'Three',\n value: 'Three',\n },\n ]" - }, - "Icon": { - "name": "chevronDown", - "position": "absolute", - "fontSize": "B", - "right": "0" - } - }, - "ModalForm": { - "tag": "form", - "extend": "Grid", - "childExtend": "FieldCaption", - "props": { - "gap": "B", - "justifyItems": "stretch", - "alignItems": "stretch", - "childProps": { - "minWidth": "100%", - "maxWidth": "100%", - "align": "flex-start flex-start", - "> label": { - "width": "100%" - }, - "> *": { - "width": "100%" - } - }, - "children": [ - {} - ], - "childrenAs": "props" - } - }, - "Table": { - "props": { - "extends": "Flex", - "childExtends": [ - "NetworkRow", - "Link" - ], - "width": "100%", - "children": "(el, s) => s.fleet", - "childrenAs": "state", - "flow": "y", - "position": "relative", - "zIndex": 2, - "text": null - } - }, - "Asdasd": { - "state": { - "keyword": "", - "thread": [], - "images": [], - "selectedOption": 0 - }, - "tag": "form", - "props": { - "position": "relative", - "margin": "Z 0 -", - "zIndex": "10", - "transition": "Z defaultBezier margin", - "fontSize": "Z2", - "round": 0, - "theme": "transparent" - }, - "Label": { - "position": "relative", - "zIndex": "1", - "attr": { - "for": "prompt" - }, - "Textarea": { - "id": "prompt", - "display": "flex", - "padding": "Z2 A", - "position": "relative", - "maxWidth": "none", - "maxHeight": "E3", - "overflow": "hidden auto", - "outline": "none", - "width": "100%", - "contentEditable": true, - "borderWidth": "0 0 1px", - "whiteSpace": "pre-wrap", - "borderStyle": "solid", - "borderColor": "line", - "color": "title", - "transition": "Z defaultBezier padding", - "background": "0", - "round": "0", - "placeholder": "\"Ask, Search, Prompt…\"", - "onInput": "(ev, el, s) => {\n let prompt = el.node.innerText.trim()\n if (prompt === '\\n') prompt = ''\n // s.replace({ keyword: prompt, thread: [] }) // Todo: maybe keep the thread\n s.replace({\n keyword: prompt\n }) // Todo: maybe keep the thread\n }", - "onKeydown": "(ev, el, s) => {\n if (ev.key === 'Enter' && !ev.shiftKey) {\n ev.preventDefault()\n }\n // TODO: I think this does not work\n if (ev.key === 'Escape') {\n ev.stopPropagation()\n el.node.blur()\n }\n }" - }, - "SearchOverlay": { - "class": "overlay", - "pointerEvents": "none", - "position": "absolute", - "top": "100%", - "left": "0", - "zIndex": "10", - "right": "0", - "opacity": "0", - "backdropFilter": "blur(10px)", - "transition": "A, defaultBezier", - "transitionProperty": "opacity, visibility", - "theme": "dialog-elevated", - "round": "0 0 --canvas-round --canvas-round", - "boxShadow": "0, 10px, 26px, -4px, document", - "visibility": "hidden", - "childProps": { - "transition": "Z2 defaultBezier", - "transitionProp": "opacity, transform", - ".isVisible": { - "pointerEvents": "auto", - "opacity": "1", - "transform": "translateY(0)" - }, - "!isVisible": { - "transform": "translateY(6px)", - "pointerEvents": "none", - "opacity": "0" - } - }, - "AIThread": { - "pointerEvents": "auto", - "hide": "(el, s) => !s.thread.length", - "isVisible": true - }, - "Flex": { - "flow": "y", - "padding": "X X A", - "gap": "X", - "theme": "transparent", - "hide": "(el, s) => s.images.length || s.thread.length", - "isVisible": "(el, s) =>\n !s.images.length &&\n s.keyword &&\n s.keyword.length < 10 &&\n !s.thread.length", - "childExtends": { - "extend": "Button", - "props": { - "theme": "tertiary" - } - }, - "childProps": { - "flow": "x", - "padding": "Z2", - "margin": "0", - "gap": "A", - "color": "placeholder", - "fontWeight": "200", - "width": "100%", - "align": "center start", - "isActive": "(el, s) => {\n if (s.selectedOption === Number(el.key)) {\n s.update({\n selectedElement: el.node\n }, {\n preventUpdate: true\n })\n return true\n }\n return false\n }", - ".isActive": { - "theme": "secondary-highlight", - "@dark": { - "background": "--color-line-highlight-dark" - }, - "@light": { - "background": "--color-line-highlight-light" - }, - "& .return": { - "opacity": "1" - } - }, - "style": { - "border": "none" - }, - "Icon": { - "widthRange": "1em", - "fontSize": "Z2" - }, - "Icon_enter": { - "class": "return", - "opacity": "0", - "order": 10, - "fontSize": "Z", - "margin": "- X - auto", - "name": "return" - } - }, - "children": [ - { - "icon": "magicstar outline", - "text": "Prompt", - "type": "submit" - }, - { - "icon": "search", - "text": null, - "children": [ - "Search for “", - { - "extends": "Strong", - "margin": "- -Z2", - "fontWeight": "500", - "color": "title", - "text": "{{ keyword }}" - }, - "”" - ], - "onClick": "(ev, el, s) => {\n el.node.blur()\n\n el.call('openNotification', {\n title: 'Sorry',\n message: 'Search is not working yet:)',\n type: 'transparentPattern',\n duration: 500\n })\n\n // el.lookup('Overflow')?.state.update({ search: s.keyword })\n }" - }, - { - "icon": "arrow angle right", - "text": "el => 'Command “...”'", - "onClick": "(ev, el, s) => {\n el.node.blur()\n\n el.call('openNotification', {\n title: 'Uh, oh',\n message: 'Command runner too...',\n type: 'transparentPattern',\n duration: 500\n })\n }" - } - ] - }, - "P": { - "isVisible": "(el, s) =>\n !s.images.length && !s.keyword && !s.thread.length", - "position": "absolute", - "inset": "0", - "flexAlign": "center", - "textAlign": "center", - "margin": "B2 auto", - "color": "placeholder", - "maxWidth": "F1", - "opacity": "1", - "text": "Try asking questions and requests...", - "onRender": "el => window.requestAnimationFrame(() => el.update())" - }, - "Suggestions": { - "position": "absolute", - "inset": "0", - "flexAlign": "center", - "isVisible": "(el, s) =>\n s.images.length ||\n (s.keyword && s.keyword?.length >= 10 && !s.thread.length)", - "P": { - "textAlign": "center", - "color": "placeholder", - "margin": "B2 auto", - "maxWidth": "F1", - "text": "Ask me anything..." - } - } - } - }, - "on": { - "submit": "(ev, el, s) => {\n // javascript prevent default to prevent default behaviour\n ev.preventDefault()\n\n // get value from state (that we kept in keyup)\n const value = s.keyword\n\n // if value is empty, do nothing (break the function)\n if (!value) return\n\n // how state.apply works https://symbols.app/docs/api/state#methods\n // - instead of object, you can use function and make custom logic\n const images = s.images\n\n const makeUserChatData = s => {\n s.thread.push({\n role: 'user', // differenciate if message is by user or agent\n message: value,\n images: [...images]\n })\n s.images = []\n s.keyword = ''\n }\n\n // when 'makeUserChatData' is done, run s.update\n s.apply(makeUserChatData)\n\n // make textarea empty again\n // e.g. reset form\n el.Label.Span.node.innerText = ''\n\n // fake functionity that chat is answering\n // reuse\n const answer = setTimeout(async () => {\n const res = await el.call('giveMeAnswer', value, images)\n console.log(res)\n\n s.apply(s => {\n s.thread.push({\n role: 'agent',\n message: res\n })\n })\n\n clearTimeout(answer)\n }, 1000)\n }" - } - }, - "EditFormCopy": { - "extend": "FormModal", - "props": { - "gap": "C", - "width": "80%", - "maxWidth": "H3", - "onSubmit": "async (ev, el, s) => {\n ev.preventDefault()\n\n const URL = 'https://bigbrother.symbo.ls/api/fleet'\n\n const formData = new FormData(el.node);\n const data = Object.fromEntries(formData)\n // output as an object\n\n console.log(data)\n await window.fetch(URL, {\n body: JSON.stringify(data)\n })\n\n // s.root.update({\n // editMode: false\n // })\n // el.lookup('ModalFade').removeContent()\n }" - }, - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Update Network" - }, - "P": { - "text": "Edit properties for existing Network" - } - }, - "Form": { - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Protocol',\n },\n Field: {\n Input: {\n name: 'protocol',\n placeholder: 'E.g. Polygon',\n type: 'text',\n value: '{{ protocol }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Status',\n },\n Field: {\n Input: null,\n SelectField: {\n width: '100%',\n Selects: {\n name: 'status',\n value: '{{ status }}',\n children: [{\n value: 'Onboarding',\n text: 'Onboarding'\n },\n {\n value: 'Maintenance',\n text: 'Maintenance'\n },\n {\n value: 'Off',\n text: 'Off'\n }\n ]\n },\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Environment',\n },\n Field: {\n Input: {\n name: 'env',\n placeholder: 'mainnet',\n value: '{{ env }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Version',\n },\n Field: {\n Input: {\n placeholder: 'v0.1.2',\n value: '{{ version }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Repository',\n },\n Field: {\n Input: {\n placeholder: 'https://github.com',\n type: 'url',\n value: '{{ repo_url }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "Button": { - "text": "Save", - "theme": "primary", - "type": "submit" - }, - "tag": "form" - }, - "SearchDropdown": { - "extend": [ - "Flex", - "Dropdown" - ], - "attr": { - "dropdown": true - }, - "props": { - "flow": "y", - "gap": "A", - "padding": "Z1 A", - "fontSize": "Z2", - "theme": null, - "@dark": { - "backdropFilter": "blur(8px)", - "boxShadow": "black .35, 0px, 15px, 150px, 280px", - "background": "#1D1D1D .95" - }, - "@light": { - "background": "gray1 .95", - "backdropFilter": "blur(8px)", - "boxShadow": "white .68, 0px, 15px, 150px, 280px" - }, - "onMousedown": "(ev, el, s) => {\n ev.preventDefault()\n }", - "onClick": "(ev, el, s) => {\n ev.stopPropagation()\n el.parent.Header.Search.Input.node.focus()\n }" - }, - "CaptionTitle": { - "Text": { - "text": "Search results" - } - }, - "Flex": { - "gap": "Z", - "flow": "y", - "margin": "- -Z2", - "children": "(el, s) => {\n const fuse = el.props.fuse\n\n function searchAll(query) {\n const results = fuse.search(query);\n\n return results.map(r => ({\n key: r.item.key,\n title: r.item.title,\n code: r.item.code,\n type: r.item.type, // component / page / function\n score: r.score,\n }));\n }\n\n const result = searchAll(s.searchTerm)\n console.log(result, s.searchTerm)\n return result\n }", - "childrenAs": "state", - "childExtends": "SearchItem", - "onInit": "async (el, s) => {\n const fuse = await import('fuse.js')\n const Fuse = fuse.default\n\n const fuseOptions = {\n isCaseSensitive: false,\n shouldSort: true,\n findAllMatches: true,\n includeScore: true,\n threshold: 0.4, // Adjust for fuzzy matching sensitivity\n keys: [{\n name: \"title\",\n weight: 2\n }, // Higher weight for titles\n {\n name: \"networkName\",\n weight: 1.5\n },\n {\n name: \"moniker\",\n weight: 1.5\n },\n {\n name: \"owner\",\n weight: 1\n },\n {\n name: \"env\",\n weight: 1\n },\n {\n name: \"cloudProvider\",\n weight: 0.5\n },\n {\n name: \"publicKey\",\n weight: 0.3\n },\n ]\n }\n\n // Function to create searchable items from fleet data\n function flattenFleetData(fleet) {\n const searchItems = []\n\n fleet.forEach(network => {\n // Add network itself as searchable\n searchItems.push({\n id: network.id,\n key: `network-${network.id}`,\n title: network.protocol,\n subtitle: `${network.network_type} • ${network.network_layer}`,\n type: 'network',\n networkName: network.protocol,\n networkType: network.network_type,\n networkLayer: network.network_layer,\n participation: network.participation,\n original: network\n })\n\n // Add validators\n network.validators?.forEach(validator => {\n searchItems.push({\n id: validator.uid,\n key: `validator-${validator.uid}`,\n title: validator.moniker || 'Unknown',\n subtitle: `Validator • ${network.protocol} • ${validator.env}`,\n type: 'validator',\n networkName: network.protocol,\n moniker: validator.moniker,\n owner: validator.owner,\n env: validator.env,\n cloudProvider: validator.cloud_provider,\n publicKey: validator.public_key,\n clientVersion: validator.client_version,\n original: validator,\n networkId: network.id\n })\n })\n\n // Add RPC nodes\n network.rpc_nodes?.forEach(rpc => {\n searchItems.push({\n id: rpc.uid,\n key: `rpc-${rpc.uid}`,\n title: rpc.moniker || 'RPC Node',\n subtitle: `RPC • ${network.protocol} • ${rpc.env}`,\n type: 'rpc',\n networkName: network.protocol,\n moniker: rpc.moniker,\n env: rpc.env,\n clientVersion: rpc.client_version,\n original: rpc,\n networkId: network.id\n })\n })\n })\n\n return searchItems\n }\n\n // Get fleet data from root state\n const fleet = window.fleet || s.parent.fleet || s.root.fleet || []\n const searchItems = flattenFleetData(fleet)\n\n console.log('Indexed items:', searchItems.length)\n el.props.fuse = new Fuse(searchItems, fuseOptions)\n }" - }, - "Filters": { - "hide": "(el, s) => s.searchTerm" - }, - "Results": { - "CaptionTitle": { - "margin": "- - Z", - "Text": { - "text": "(el, s) => s.searchTerm ? 'Search results' : 'Recent searches'" - } - }, - "Flex": { - "gap": "Z", - "flow": "y", - "margin": "- -Z2", - "fontSize": "Z2", - "children": "(el, s) => {\n if (!s.searchTerm && !s.filters?.length) {\n return s.recents\n }\n\n const fuse = el.props.fuse\n\n function searchAll(query) {\n const results = fuse.search(query);\n\n return results.map(r => ({\n id: r.item.id,\n title: r.item.title,\n type: r.item.type, // component / page / function\n score: r.score,\n }));\n }\n\n let term = s.searchTerm || ''\n if (s.filters) term = s.searchTerm + ' ' + s.filters.join(' ')\n\n const result = searchAll(term)\n return result\n }", - "childrenAs": "state", - "childExtends": "SearchItem", - "onInit": "async (el, s) => {\n const fuse = await import('fuse.js')\n const Fuse = fuse.default\n\n const fuseOptions = {\n isCaseSensitive: false,\n shouldSort: true,\n findAllMatches: true,\n includeScore: true,\n threshold: 0.4, // Adjust for fuzzy matching sensitivity\n keys: [{\n name: \"title\",\n weight: 2\n }, // Higher weight for titles\n {\n name: \"networkName\",\n weight: 1.5\n },\n {\n name: \"moniker\",\n weight: 1.5\n },\n {\n name: \"owner\",\n weight: 1\n },\n {\n name: \"env\",\n weight: 1\n },\n {\n name: \"cloudProvider\",\n weight: 0.5\n },\n {\n name: \"publicKey\",\n weight: 0.3\n },\n ]\n }\n\n // Function to create searchable items from fleet data\n function flattenFleetData(fleet) {\n const searchItems = []\n\n fleet.forEach(network => {\n // Add network itself as searchable\n searchItems.push({\n id: network.id,\n key: `network-${network.id}`,\n title: network.protocol,\n subtitle: `${network.network_type} • ${network.network_layer}`,\n type: 'network',\n networkName: network.protocol,\n networkType: network.network_type,\n networkLayer: network.network_layer,\n participation: network.participation,\n original: network\n })\n\n // Add validators\n network.validators?.forEach(validator => {\n searchItems.push({\n id: validator.uid,\n key: `validator-${validator.uid}`,\n title: validator.moniker || 'Unknown',\n subtitle: `Validator • ${network.protocol} • ${validator.env}`,\n type: 'validator',\n networkName: network.protocol,\n moniker: validator.moniker,\n owner: validator.owner,\n env: validator.env,\n cloudProvider: validator.cloud_provider,\n publicKey: validator.public_key,\n clientVersion: validator.client_version,\n original: validator,\n networkId: network.id\n })\n })\n\n // Add RPC nodes\n network.rpc_nodes?.forEach(rpc => {\n searchItems.push({\n id: rpc.uid,\n key: `rpc-${rpc.uid}`,\n title: rpc.moniker || 'RPC Node',\n subtitle: `RPC • ${network.protocol} • ${rpc.env}`,\n type: 'rpc',\n networkName: network.protocol,\n moniker: rpc.moniker,\n env: rpc.env,\n clientVersion: rpc.client_version,\n original: rpc,\n networkId: network.id\n })\n })\n })\n\n return searchItems\n }\n\n // Get fleet data from root state\n const fleet = window.fleet || s.parent.fleet || s.root.fleet || []\n const searchItems = flattenFleetData(fleet)\n\n el.props.fuse = new Fuse(searchItems, fuseOptions)\n }" - } - } - }, - "SearchItem": { - "extend": [ - "Flex" - ], - "props": { - "gap": "Z1", - "align": "start", - "padding": "Z A X", - "theme": "transparent", - ":hover": { - "style": { - "svg": { - "opacity": 1 - } - }, - "theme": "tertiary" - }, - "onClick": "(ev, el, s) => {\n ev.stopPropagation()\n\n if (!s.parent.recents) s.parent.recents = []\n s.parent.recents.unshift(s.parse())\n\n if (s.type === 'network')\n el.call('router', `/network/${s.title}`, el.__ref.root)\n }" - }, - "Avatar": { - "if": "(el, s) => s.type === 'network'", - "margin": "-W2 - -", - "src": "{{ title }}.png", - "boxSize": "B1" - }, - "Hgroup": { - "gap": "W", - "H": { - "text": "{{ title }}", - "margin": "- C - -", - "tag": "h6" - }, - "P": { - "text": "{{ type }}", - "textAlign": "start" - } - }, - "Flex": { - "flow": "y", - "gap": "W", - "Strong": { - "tag": "", - "text": "{{ title }}", - "margin": "- C - -" - }, - "P": { - "extends": "Flex", - "gap": "X1", - "align": "center", - "margin": "0", - "textAlign": "start", - "color": "caption", - "Span_network": { - "if": "(el, s) => s.type !== 'network'", - "extends": "Flex", - "align": "center", - "gap": "Y", - "Avatar": { - "margin": "-W2 - -", - "src": "{{ network }}.png", - "boxSize": "Z2" - }, - "Strong": { - "text": "{{ network }}" - }, - "Span_sep": { - "text": "・" - } - }, - "Span_type": { - "text": "{{ type }}" - }, - "lineHeight": 1.3 - } - } - }, - "Content": { - "extend": "Flex", - "props": { - "class": "content", - "position": "relative", - "width": "100%", - "round": "A A2", - "theme": "dialog", - "flow": "y", - "flex": 1 - } - }, - "Layout": { - "props": { - "position": "relative" - }, - "Header": {}, - "SearchDropdown": { - "width": "100%", - "left": "0", - "extends": [ - "SearchDropdown", - "Dropdown" - ] - }, - "Content": {} - }, - "DropdownSiblingFocus": { - "props": { - "position": "relative", - "tabindex": "0", - "style": { - "&:focus-within": { - "zIndex": 1000, - "& ~ [dropdown]": { - "transform": "translate3d(0,0,0)", - "opacity": 1, - "visibility": "visible" - } - } - } - }, - "Input_trigger": { - "type": "checkbox", - "opacity": "0", - "visibility": "hidden", - "position": "absolute", - "inset": "0", - "onUpdate": "(el) => el.node.blur()" - }, - "style": { - "&:focus-within": { - "zIndex": 1000, - "& ~ [dropdown]": { - "transform": "translate3d(0,0,0)", - "opacity": 1, - "visibility": "visible" - } - } - } - }, - "CaptionTitle": { - "extend": "Flex", - "props": { - "align": "center", - "color": "disabled", - "fontSize": "Y", - "textTransform": "uppercase" - }, - "Text": { - "text": "Active Project" - } - }, - "FiltersSection": { - "props": { - "key": "Environment", - "options": [] - }, - "CaptionTitle": { - "margin": "- - B", - "Text": { - "text": "el => el.parent.parent.props.key" - } - }, - "Flex": { - "flow": "y", - "gap": "Z", - "align": "start", - "childExtends": "NetworkRowLabel", - "childProps": { - "color": "white", - "background": "white .1", - "theme": null, - "fontSize": "Z2", - "order": 12, - "onClick": "(ev, el, s) => {\n if (!s.filters) s.filters = []\n s.apply(() => {\n s.filters.push(el.props.text)\n })\n }", - "isDisabled": "(el, s) => s.filters?.includes(el.props.text)", - ".isDisabled": { - "pointerEvents": "none", - "opacity": 0.35 - } - }, - "children": "el => el.parent.props.options" - }, - "key": "Environment" - }, - "Filters": { - "extend": "Flex", - "props": { - "gap": "D1" - }, - "childExtend": "FiltersSection", - "Env": { - "key": "Environment", - "options": [ - "Mainnet", - "Testnet" - ], - "Flex": { - "childProps": { - "background": "env .25" - } - } - }, - "NodeType": { - "key": "Node Type", - "options": [ - "Validator", - "RPC" - ], - "Flex": { - "childProps": { - "color": "white", - "theme": null, - "fontSize": "Z2", - "background": "nodeType .25" - } - } - }, - "Cloud": { - "key": "Cloud Provider", - "options": [ - "GCP", - "AWS" - ], - "Flex": { - "childProps": { - "color": "white", - "theme": null, - "fontSize": "Z2", - "background": "cloudProvider .25", - "style": { - "justifySelf": "start" - } - }, - "extends": [ - "Grid" - ], - "display": "grid", - "style": { - "gridTemplateColumns": "repeat(2, 1fr)" - } - } - }, - "NodeOp": { - "key": "Node Operator", - "options": [ - "Tornike", - "Peter", - "Yan", - "Patrick", - "Prashant", - "Ankit", - "Raja", - "Reza", - "Tommy" - ], - "Flex": { - "extends": [ - "Grid" - ], - "childProps": { - "style": { - "justifySelf": "start" - } - }, - "style": { - "gridTemplateColumns": "repeat(3, 1fr)" - }, - "display": "grid" - } - } - } - }, - "icons": {}, - "integrations": {}, - "packages": {}, - "pages": { - "/": { - "extend": "Page", - "props": { - "width": "100%", - "margin": "0", - "flexFlow": "x", - "align": "stretch", - "onRender": "async (el, s) => {\n await el.call('auth')\n }" - }, - "Cover": { - "backgroundImage": "https://images.unsplash.com/photo-1751601454754-68dce3c26795?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHx0b3BpYy1mZWVkfDI2fGJvOGpRS1RhRTBZfHxlbnwwfHx8fHw%3D", - "backgroundSize": "cover", - "flex": 2 - }, - "Login": { - "flex": 5, - "flexAlign": "center center", - "Window": { - "theme": "dialog", - "padding": "A A2", - "margin": "auto", - "round": "A2", - "H3": { - "maxWidth": "F", - "lineHeight": "1.2", - "fontWeight": "300", - "children": [ - "Welcome to", - "Tech dashboard" - ] - }, - "P": { - "fontWeight": "300", - "color": "caption", - "margin": "X2 - B2", - "text": "Sign in using Google to continue" - }, - "Link": { - "margin": "0 -W", - "href": "https://api.nodeops.ninja/auth/google", - "Img": { - "src": "google.svg" - } - } - } - } - }, - "/network": { - "extend": [ - "Page", - "Layout" - ], - "props": { - "width": "100%", - "padding": "X", - "flexFlow": "y", - "gap": "X", - "onRender": "(el, s) => {\n window.requestAnimationFrame(async () => {\n let [, _, protocol] = window.location.pathname.split('/')\n if (window.location.pathname === 'srcdoc') {\n protocol = 'Eth2'\n }\n\n const network = s.fleet?.filter(v => v.protocol === protocol)[0]\n if (network) {\n network.protocol = protocol\n }\n\n el.call('setInitialData', {\n network\n })\n\n window.requestAnimationFrame(async () => {\n const network = await el.call('read', `/${protocol}`)\n console.log({\n network\n })\n network.protocol = protocol\n\n el.call('setInitialData', {\n protocol,\n network\n })\n })\n })\n }" - }, - "Header": { - "Link": { - "href": "/" - } - }, - "Content": { - "align": "stretch start", - "state": "network", - "Box": { - "flex": 1, - "position": "relative", - "Flex": { - "padding": "Z", - "gap": "A", - "NavButton": { - "theme": "button", - "icon": "chevron left", - "href": "/", - "fontWeight": "bold", - "text": "All Networks" - }, - "NavButton_add": { - "theme": "button", - "flow": "row-reverse", - "margin": "- - - auto", - "icon": "plus", - "text": "Add node", - "onClick": "(ev, el, s) => {\n s.root.update({\n modal: '/add-node'\n })\n }" - }, - "DropdownParentFocus": { - "Input_trigger": { - "visibility": "hidden" - }, - "IconButton_add": { - "theme": "button", - "icon": "moreVertical", - "Icon": { - "pointerEvents": "none" - } - }, - "Dropdown": { - "left": "auto", - "right": "0", - "padding": "X2 -", - "DropdownList": { - "childExtends": "Button", - "childProps": { - "theme": "button", - "align": "start", - "gap": "Y" - }, - "CopyURL": { - "icon": "copy", - "text": "Copy", - "onClick": "(ev, el, s) => {\n\n }" - }, - "Update": { - "icon": "edit", - "text": "Edit", - "onClick": "(ev, el, s) => {\n s.root.update({\n modal: '/edit-network'\n })\n }" - }, - "Delete": { - "icon": "trash", - "text": "Delete", - "onClick": "(ev, el, s, ctx) => {\n const y = window.confirm('You sure you want to delete this network?')\n if (y) el.call('remove', 'network', s.protocol)\n }" - } - } - } - } - }, - "Hr": { - "opacity": "0.05", - "margin": "0 0 B" - }, - "ValidatorInfo": {} - }, - "Modal": {} - } - }, - "/node": { - "extend": [ - "Page", - "Layout" - ], - "props": { - "width": "100%", - "padding": "X", - "flexFlow": "y", - "gap": "X", - "onRender": "(el, s) => {\n window.requestAnimationFrame(async () => {\n let [, _, protocol, nodeType, uid] = window.location.pathname.split('/')\n if (window.location.pathname === 'srcdoc') {\n protocol = 'Eth2'\n nodeType = 'validator'\n uid = '6a596043-2744-4d36-b10a-c8c2a35f6a7c'\n }\n\n s.protocol = protocol\n if (!s.parent.network) {\n s.parent.network = {\n protocol\n }\n } else {\n s.parent.network.protocol = protocol\n }\n\n const TYPE_MAP = {\n validator: 'validators',\n rpc: 'rpc_nodes'\n }\n const node = s.network?.[TYPE_MAP[nodeType]]?.filter(v => v.uid === uid)[0]\n if (node) {\n node.nodeType = nodeType\n node.protocol = protocol\n }\n\n el.call('setInitialData', {\n node\n })\n\n window.requestAnimationFrame(async () => {\n const node = await el.call('read', `/node/${nodeType}/${uid}`)\n node.nodeType = nodeType\n node.protocol = protocol\n el.call('setInitialData', {\n node\n })\n })\n })\n }" - }, - "Header": { - "Link": { - "href": "/" - } - }, - "Content": { - "state": "node", - "PageHead": { - "state": "../network", - "extends": "Flex", - "gap": "A", - "flexFlow": "x", - "padding": "Z A", - "width": "100%", - "Protocol": { - "extends": [ - "Link", - "Flex" - ], - "align": "center", - "href": "/network/{{ protocol }}", - "gap": "Z", - "Avatar": { - "src": "{{ protocol }}.png", - "boxSize": "A2" - }, - "Strong": { - "lineHeight": 1, - "text": "{{ protocol }}" - } - }, - "NavButton": { - "theme": "button", - "icon": "chevron down", - "flow": "row-reverse", - "href": "/network/{{ protocol }}", - "fontWeight": "bold", - "text": "Show All Nodes" - }, - "NavButton_edit": { - "theme": "button", - "flow": "row-reverse", - "margin": "- - - auto", - "icon": "edit", - "text": "Edit node", - "onClick": "(ev, el, s) => {\n s.root.update({\n modal: '/edit-node'\n })\n }" - }, - "DropdownParentFocus": { - "Input_trigger": { - "visibility": "hidden" - }, - "IconButton_add": { - "theme": "button", - "icon": "moreVertical", - "Icon": { - "pointerEvents": "none" - } - }, - "Dropdown": { - "left": "auto", - "right": "0", - "padding": "X2 -", - "DropdownList": { - "childExtends": "Button", - "childProps": { - "theme": "button", - "align": "start", - "gap": "Y2" - }, - "CopyURL": { - "icon": "copy", - "text": "Copy", - "onClick": "(ev, el, s) => {\n\n }" - }, - "Delete": { - "icon": "trash", - "text": "Remove node", - "onClick": "(ev, el, s, ctx) => {\n const y = window.confirm('You sure you want to delete this node?')\n if (y) el.call('remove', 'node', s.protocol, s.uid)\n }" - } - } - } - } - }, - "Modal": {}, - "Hr": { - "opacity": "0.05", - "margin": "0" - }, - "Flex": { - "align": "stretch start", - "flex": 1, - "Box": { - "flex": 1, - "position": "relative", - "Overflow": { - "overflow": "hidden auto", - "position": "absolute", - "inset": "0", - "ValidatorContent": {} - } - }, - "Logs": { - "minWidth": "G3" - } - } - } - }, - "/add-network": { - "extend": "FormModal", - "props": { - "extends": "FormModal", - "tag": "form", - "gap": "C", - "width": "80%", - "maxWidth": "I", - "onSubmit": "async (ev, el, s) => {\n ev.preventDefault()\n await el.call('add', 'network')\n }", - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Add Network" - }, - "P": { - "text": "Add new network" - } - }, - "Form": { - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Protocol',\n },\n Field: {\n Input: {\n name: 'protocol',\n required: true,\n placeholder: 'E.g. Polygon',\n type: 'text',\n value: '{{ protocol }}'\n },\n },\n },\n {\n Caption: {\n text: 'Network Layer',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'network_layer',\n value: '{{ network_layer }}',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'L1',\n value: 'L1',\n },\n {\n text: 'L2',\n value: 'L2',\n },\n {\n text: 'L3',\n value: 'L3',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.network_layer === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Network type',\n },\n Field: {\n Input: {\n name: 'network_type',\n placeholder: 'E.g. Costmos SDK',\n required: true,\n type: 'text',\n value: '{{ network_type }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Network access',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'participation',\n value: '{{ participation }}',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Permissioned',\n value: 'Permissioned',\n },\n {\n text: 'Semi-permissioned',\n value: 'Semi-permissioned'\n },\n {\n text: 'Permissionless',\n value: 'Permissionless',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.participation === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Repository',\n },\n Field: {\n Input: {\n name: 'repo_url',\n placeholder: 'https://github.com',\n type: 'url',\n value: '{{ repo_url }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "Button": { - "text": "Save", - "theme": "primary", - "type": "submit" - } - }, - "tag": "form", - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Add Network" - }, - "P": { - "text": "Add new network" - } - }, - "Form": { - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Protocol',\n },\n Field: {\n Input: {\n name: 'protocol',\n required: true,\n placeholder: 'E.g. Polygon',\n type: 'text',\n value: '{{ protocol }}'\n },\n },\n },\n {\n Caption: {\n text: 'Network Layer',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'network_layer',\n value: '{{ network_layer }}',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'L1',\n value: 'L1',\n },\n {\n text: 'L2',\n value: 'L2',\n },\n {\n text: 'L3',\n value: 'L3',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.network_layer === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Network type',\n },\n Field: {\n Input: {\n name: 'network_type',\n placeholder: 'E.g. Costmos SDK',\n required: true,\n type: 'text',\n value: '{{ network_type }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Network access',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'participation',\n value: '{{ participation }}',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Permissioned',\n value: 'Permissioned',\n },\n {\n text: 'Semi-permissioned',\n value: 'Semi-permissioned'\n },\n {\n text: 'Permissionless',\n value: 'Permissionless',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.participation === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Repository',\n },\n Field: {\n Input: {\n name: 'repo_url',\n placeholder: 'https://github.com',\n type: 'url',\n value: '{{ repo_url }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "Button": { - "text": "Save", - "theme": "primary", - "type": "submit" - } - }, - "/add-node": { - "extend": "FormModal", - "tag": "form", - "props": { - "gap": "C", - "width": "80%", - "maxWidth": "I", - "onSubmit": "async (ev, el, s) => {\n ev.preventDefault()\n await el.call('add', 'node')\n }", - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Add node" - }, - "P": { - "text": "Add node in {{ protocol }} network" - } - }, - "Form": { - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Moniker',\n },\n Field: {\n Input: {\n name: 'moniker',\n value: '{{ moniker }}',\n placeholder: 'E.g. Bcw-Technologies',\n type: 'text',\n required: true,\n value: '{{ moniker }}'\n },\n },\n },\n {\n Caption: {\n text: 'Node type',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n value: '{{ nodeType }}',\n name: 'nodeType',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Validator',\n value: 'validator',\n },\n {\n text: 'RPC',\n value: 'rpc',\n },\n {\n text: 'Archival-RPC',\n value: 'archival-RPC',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.nodeType === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Cloud Provider',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n value: '{{ cloud_provider }}',\n name: 'cloud_provider',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'AWS',\n value: 'AWS',\n },\n {\n text: 'GCP',\n value: 'GCP',\n },\n {\n text: 'Latitude',\n value: 'Latitude',\n },\n {\n text: 'OVH',\n value: 'OVH',\n },\n {\n text: 'Nirvana',\n value: 'Nirvana',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.cloud_provider === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Priority',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'category',\n value: '{{ category }}',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'P1',\n value: 'P1',\n },\n {\n text: 'P2',\n value: 'P2',\n },\n {\n text: 'P3',\n value: 'P3',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.category === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Do we manage proposals?',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'do_we_manage_proposals',\n value: '{{ do_we_manage_proposals }}',\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Yes',\n value: 'Yes',\n },\n {\n text: 'No',\n value: 'No',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.do_we_manage_proposals === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'SLA',\n },\n Field: {\n Input: {\n name: 'sla',\n value: '{{ sla }}',\n placeholder: '99.99%',\n value: '{{ sla }}'\n },\n },\n },\n {\n Caption: {\n text: 'Projected Cost',\n },\n Field: {\n Input: {\n name: 'projected_cost',\n value: '{{ projected_cost }}',\n placeholder: '$2,911.00',\n value: '{{ projected_cost }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "Button": { - "text": "Save", - "theme": "primary", - "type": "submit" - } - }, - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Add node" - }, - "P": { - "text": "Add node in {{ protocol }} network" - } - }, - "Form": { - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Moniker',\n },\n Field: {\n Input: {\n name: 'moniker',\n value: '{{ moniker }}',\n placeholder: 'E.g. Bcw-Technologies',\n type: 'text',\n required: true,\n value: '{{ moniker }}'\n },\n },\n },\n {\n Caption: {\n text: 'Node type',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n value: '{{ nodeType }}',\n name: 'nodeType',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Validator',\n value: 'validator',\n },\n {\n text: 'RPC',\n value: 'rpc',\n },\n {\n text: 'Archival-RPC',\n value: 'archival-RPC',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.nodeType === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Cloud Provider',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n value: '{{ cloud_provider }}',\n name: 'cloud_provider',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'AWS',\n value: 'AWS',\n },\n {\n text: 'GCP',\n value: 'GCP',\n },\n {\n text: 'Latitude',\n value: 'Latitude',\n },\n {\n text: 'OVH',\n value: 'OVH',\n },\n {\n text: 'Nirvana',\n value: 'Nirvana',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.cloud_provider === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Priority',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'category',\n value: '{{ category }}',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'P1',\n value: 'P1',\n },\n {\n text: 'P2',\n value: 'P2',\n },\n {\n text: 'P3',\n value: 'P3',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.category === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Do we manage proposals?',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'do_we_manage_proposals',\n value: '{{ do_we_manage_proposals }}',\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Yes',\n value: 'Yes',\n },\n {\n text: 'No',\n value: 'No',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.do_we_manage_proposals === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'SLA',\n },\n Field: {\n Input: {\n name: 'sla',\n value: '{{ sla }}',\n placeholder: '99.99%',\n value: '{{ sla }}'\n },\n },\n },\n {\n Caption: {\n text: 'Projected Cost',\n },\n Field: {\n Input: {\n name: 'projected_cost',\n value: '{{ projected_cost }}',\n placeholder: '$2,911.00',\n value: '{{ projected_cost }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "Button": { - "text": "Save", - "theme": "primary", - "type": "submit" - } - }, - "/edit-network": { - "extend": "/add-network", - "props": { - "onSubmit": "async (ev, el, s) => {\n ev.preventDefault()\n\n await el.call('edit', 'network', s.root.protocol)\n }", - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Edit Network" - }, - "P": { - "text": "Edit properties for existing {{ protocol }} network" - } - } - }, - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Edit Network" - }, - "P": { - "text": "Edit properties for existing {{ protocol }} network" - } - } - }, - "/edit-node": { - "Hgroup": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Edit Node" - }, - "P": { - "text": "Edit properties for existing node" - } - }, - "Form": {}, - "Hr": { - "margin": "-A1 0 X", - "opacity": "0.05" - }, - "Form_2": { - "extends": "ModalForm", - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Status',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n required: true,\n Selects: {\n name: 'status',\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Off',\n value: 'Off',\n }, {\n text: 'Onboarding',\n value: 'Onboarding',\n },\n {\n text: 'Stable/Mainterance',\n value: 'Stable/Mainterance',\n },\n {\n text: 'Live',\n value: 'Live',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.status === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n }, {\n Caption: {\n text: 'Client version',\n },\n Field: {\n Input: {\n name: 'client_version',\n placeholder: '1.2.3...',\n required: true,\n type: 'text',\n value: '{{ client_version }}'\n },\n },\n }, {\n Caption: {\n text: 'Reward claim',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'reward_claim',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Automatic',\n value: 'Automatic',\n },\n {\n text: 'Manual',\n value: 'Manual',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.reward_claim === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n }, {\n Caption: {\n text: 'Node Operator',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'owner',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Tornike',\n value: 'Tornike',\n }, {\n text: 'Peter',\n value: 'Peter',\n }, {\n text: 'Yan',\n value: 'Yan',\n }, {\n text: 'Patrick',\n value: 'Patrick',\n }, {\n text: 'Prashant',\n value: 'Prashant',\n }, {\n text: 'Ankit',\n value: 'Ankit',\n }, {\n text: 'Raja',\n value: 'Raja',\n }, {\n text: 'Reza',\n value: 'Reza',\n }, {\n text: 'Tommy',\n value: 'Tommy',\n }, ],\n childProps: {\n selected: (el, s) => {\n return s.node_operator === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n }, {\n Caption: {\n text: 'Env',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n\n Selects: {\n name: 'env',\n required: true,\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Testnet',\n value: 'Testnet',\n }, {\n text: 'Mainnet',\n value: 'Mainnet',\n }, {\n text: 'Devnet',\n value: 'Devnet',\n }, ],\n childProps: {\n selected: (el, s) => {\n return s.env === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Public key',\n },\n Field: {\n Input: {\n name: 'public_key',\n placeholder: 'Public key',\n value: '{{ public_key }}'\n },\n },\n }, {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Reward address',\n },\n Field: {\n Input: {\n name: 'reward_address',\n placeholder: 'Reward address',\n value: '{{ reward_address }}'\n },\n },\n }, {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Explorer Link',\n },\n Field: {\n Input: {\n name: 'explorer_link',\n placeholder: 'Explorer link',\n value: '{{ explorer_link }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "extend": "/add-node", - "props": { - "onSubmit": "async (ev, el, s) => {\n ev.preventDefault()\n await el.call('edit', 'node', s.protocol)\n }" - } - }, - "/dashboard": { - "extend": [ - "Page" - ], - "props": { - "width": "100%", - "padding": "X", - "flexFlow": "y", - "gap": "X", - "onRender": "(el, s) => {\n window.requestAnimationFrame(async () => {\n const fleet = await el.call('read')\n el.call('setInitialData', {\n fleet\n })\n })\n }" - }, - "Header": {}, - "Flex": { - "width": "100%", - "align": "stretch start", - "theme": "dialog", - "round": "A", - "flex": 1, - "position": "relative", - "Box": { - "position": "relative", - "flex": 1, - "Overflow": { - "overflow": "hidden auto", - "position": "absolute", - "inset": "0", - "PageHead": { - "flexFlow": "x", - "padding": "A A2 Z", - "width": "100%", - "Title": { - "fontSize": "Z2", - "Strong": { - "fontWeight": "700", - "text": "Network " - }, - "Span": { - "fontWeight": "100" - } - }, - "NavButton": { - "theme": "button", - "flow": "row-reverse", - "margin": "-Y1 -Z2 - auto", - "icon": "plus", - "text": "Add network" - } - }, - "Tr": { - "extends": "Grid", - "zIndex": 3, - "position": "sticky", - "background": "black .001", - "backdropFilter": "blur(10px)", - "top": "0", - "padding": "A A2", - "templateColumns": "3fr 3fr 3fr 2fr 2fr", - "color": "caption", - "childProps": { - "fontSize": "Z1" - }, - "children": [ - "Network", - "Environment", - "Node types", - "Cloud provider", - "" - ] - }, - "Hr": { - "margin": "0", - "opacity": ".035" - }, - "Table": { - "round": "C1" - } - }, - "Modal": {} - }, - "MetaSectionBars": { - "minWidth": "G3" - } - } - }, - "/add-network-copy": { - "extend": "FormModal", - "props": { - "tag": "form", - "gap": "C", - "width": "80%", - "maxWidth": "I", - "onSubmit": "async (ev, el, s) => {\n ev.preventDefault()\n await el.call('give', ...rest)\n await el.give()\n }" - }, - "Section": { - "margin": "0", - "H": { - "tag": "strong", - "text": "Add Network" - }, - "P": { - "text": "Add new network" - } - }, - "Form": { - "columns": "repeat(2, 1fr)", - "@mobileM": { - "columns": "repeat(1, 1fr)" - }, - "children": "() => [{\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Protocol',\n },\n Field: {\n Input: {\n name: 'protocol',\n placeholder: 'E.g. Polygon',\n type: 'text',\n value: '{{ protocol }}'\n },\n },\n },\n {\n Caption: {\n text: 'Network Layer',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'network_layer',\n value: '{{ network_layer }}',\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'L1',\n value: 'L1',\n },\n {\n text: 'L2',\n value: 'L2',\n },\n {\n text: 'L3',\n value: 'L3',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.network_layer === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n Caption: {\n text: 'Network type',\n },\n Field: {\n Input: {\n name: 'network_type',\n placeholder: 'E.g. Costmos SDK',\n type: 'text',\n value: '{{ network_type }}'\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Network access',\n },\n Field: {\n Input: null,\n Select: {\n padding: 'A A2',\n round: 'C1',\n theme: 'field',\n Selects: {\n name: 'participation',\n value: '{{ participation }}',\n children: [{\n text: 'Please select',\n selected: true,\n disabled: 'disabled'\n }, {\n text: 'Permissioned',\n value: 'Permissioned',\n },\n {\n text: 'Semi-permissioned',\n value: 'Semi-permissioned'\n },\n {\n text: 'Permissionless',\n value: 'Permissionless',\n },\n ],\n childProps: {\n selected: (el, s) => {\n return s.participation === el.props.value\n }\n }\n },\n Icon: {\n color: 'caption',\n right: 'Z'\n }\n },\n },\n },\n {\n gridColumn: '1 / span 2',\n Caption: {\n text: 'Repository',\n },\n Field: {\n Input: {\n name: 'repo_url',\n placeholder: 'https://github.com',\n type: 'url',\n value: '{{ repo_url }}'\n },\n },\n },\n ]", - "tag": "div" - }, - "Button": { - "text": "Save", - "theme": "primary", - "type": "submit" - } - } - }, - "state": { - "metrics": [ - { - "title": "Status", - "items": [ - { - "caption": "Live", - "value": "14" - }, - { - "caption": "Onboarding", - "value": "4" - }, - { - "caption": "Degraded", - "value": "0" - } - ] - }, - { - "title": "Fleet cloud distribution", - "items": [ - { - "caption": "GCP", - "value": "4" - }, - { - "caption": "AWS", - "value": "14" - }, - { - "caption": "Latitude", - "value": "1" - }, - { - "caption": "OVH", - "value": "4" - } - ] - }, - { - "title": "Nodes by Layer", - "items": [ - { - "caption": "Live", - "value": "4" - } - ] - }, - { - "title": "Nodes by type", - "items": [ - { - "caption": "Live", - "value": "4" - } - ] - } - ] - }, - "domains": { - "custom": [ - "www.bb.nodeops.ninja", - "bb.nodeops.ninja" - ] - }, - "system": {}, - "define": {}, - "project": {}, - "environmentHosts": [], - "multiEnvironment": { - "active": false, - "wildcard": null, - "totalTls": null - }, - "id": "6874baae0769df64f1a4484d", - "schema": { - "components": { - "Row": { - "key": "Row", - "type": "component", - "settings": { - "gridOptions": { - "x": 11, - "y": 0, - "w": 3, - "h": 1 - } - }, - "uses": [ - "Flex", - "Status", - "Title", - "Validator", - "NetworkRowLabel", - "Rpc", - "Mainnet", - "Testnet", - "Icon", - "Spacer" - ], - "state": "data/0", - "title": "Row", - "description": "", - "category": "", - "tags": [], - "props": { - "demoComponent": { - "state": { - "protocol": "kaia", - "env": "Mainnet", - "role": "Validator", - "status": "Stable/Maintenance", - "repo_url": "https://github.com/kaiachain/kaia/releases/", - "version": "v1.2.3", - "validator_info": [ - { - "moniker": "BCW Technologies", - "public_key": "mantravaloper1r3s4pefpz69fk4rq8sn0zt2wxnv09uffz06nxu" - } - ], - "validator_count": 1 - } - } - }, - "interactivity": [], - "dataTypes": [], - "advanced": true, - "error": null - }, - "Column": { - "key": "Column", - "type": "component", - "settings": { - "gridOptions": { - "x": 15, - "y": 1, - "w": 1, - "h": 1 - } - }, - "uses": [] - }, - "LabelTag": { - "type": "component", - "key": "LabelTag", - "title": "LabelTag", - "description": "", - "category": "", - "tags": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "x": 7, - "y": 2, - "w": 1, - "h": 1 - } - }, - "interactivity": [], - "dataTypes": [], - "touched": true, - "uses": [] - }, - "ValidatorInfo": { - "key": "ValidatorInfo", - "type": "component", - "title": "ValidatorInfo", - "description": "", - "category": "", - "tags": [], - "state": null, - "props": { - "demoComponent": {} - }, - "settings": { - "gridOptions": { - "x": 0, - "y": 0, - "w": 2, - "h": 2 - } - }, - "interactivity": [], - "dataTypes": [], - "uses": [ - "Flex", - "Header", - "Avatar", - "H5", - "Add", - "Button", - "Repo", - "IconText", - "Link", - "Grid", - "H", - "P", - "NoContent", - "Both", - "Title", - "Span", - "Validators", - "ValidatorsList", - "RPC", - "Communication" - ], - "advanced": true, - "error": null - }, - "ValidatorContent": { - "key": "ValidatorContent", - "type": "component", - "title": "Validator Content", - "description": "", - "category": "", - "tags": [], - "state": {}, - "props": { - "demoComponent": {} - }, - "settings": { - "gridOptions": { - "x": 3, - "y": 0, - "w": 3, - "h": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "uses": [ - "Grid", - "Monkier", - "P", - "H", - "Env", - "Status", - "Text", - "Owner", - "LabelTag", - "Avatar", - "Version", - "RewardClaim", - "Category", - "CloudProvider", - "Reward", - "Flex", - "CopyButton", - "PublicKey", - "Hr", - "OurProposal", - "CurrentSpent", - "ProjectedCost", - "Consensus", - "SLA", - "WarehouseSupport", - "WarehouseUrl", - "Link", - "KnowledgeBase", - "Explorer", - "Uptime", - "Graphs" - ], - "advanced": true, - "error": null, - "code": "export default {/////n Grid: {/////n childExtends: 'PreviewItem',/////n gap: 'C',/////n columns: 'repeat(4, 1fr)',/////n Monkier: {/////n P: {/////n text: 'Moniker',/////n },/////n H: {/////n isNan: (el, s) => !s.moniker,/////n text: (el, s) => s.moniker || 'n/a',/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n Env: {/////n P: {/////n text: 'Environment',/////n },/////n H: {/////n isNan: (el, s) => !s.env,/////n text: (el, s) => s.env || 'n/a',/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n Status: {/////n P: {/////n text: 'Status',/////n },/////n H: {/////n flexFlow: 'x',/////n gap: 'X2',/////n flexAlign: 'center start',/////n alignSelf: 'start',/////n text: null,/////n Status: {/////n props: (el, s) => ({/////n hide: (el, s) => !s.status,/////n order: '-1',/////n background: el.call('getStatusColor', s.status),/////n round: 'C',/////n boxSize: 'Y2'/////n }),/////n },/////n Text: {/////n isNan: (el, s) => !s.status,/////n text: (el, s) => s.status || 'n/a',/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n },/////n Owner: {/////n P: {/////n text: 'Node Operator',/////n },/////n H: {/////n hide: (el, s) => s.owner,/////n text: 'n/a',/////n fontWeight: '300',/////n color: 'caption',/////n },/////n LabelTag: {/////n hide: (el, s) => !s.owner,/////n flexFlow: 'x',/////n gap: 'X2',/////n flexAlign: 'center start',/////n alignSelf: 'start',/////n background: 'white',/////n color: 'black',/////n fontSize: 'Z2',/////n Avatar: {/////n boxSize: 'A',/////n },/////n Text: {/////n text: (el, s) => s.owner,/////n },/////n text: null,/////n },/////n },/////n Version: {/////n P: {/////n text: 'Client version',/////n },/////n H: {/////n text: (el, s) => s.client_version || 'n/a',/////n isNan: (el, s) => !s.owner,/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n RewardClaim: {/////n P: {/////n text: 'Reward Claim',/////n },/////n H: {/////n isNan: (el, s) => !s.reward_claim,/////n text: (el, s) => s.reward_claim || 'n/a',/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n Category: {/////n P: {/////n text: 'Category',/////n },/////n H: {/////n isNan: (el, s) => !s.category,/////n text: (el, s) => s.category || 'n/a',/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n CloudProvider: {/////n P: {/////n text: 'Cloud Provider',/////n },/////n H: {/////n isNan: (el, s) => !s.cloud_provider,/////n text: (el, s) => s.cloud_provider || 'n/a',/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n Reward_address: {/////n gridColumn: 'span 2',/////n P: {/////n text: 'Reward Address',/////n },/////n H: {/////n extends: 'Flex',/////n flexAlign: 'center start',/////n Text: {/////n overflow: 'hidden',/////n maxWidth: '95%',/////n textOverflow: 'ellipsis',/////n text: '{{reward_address}}',/////n },/////n Text_no: {/////n color: 'caption',/////n hide: (el, s) => s.reward_address,/////n fontWeight: '300',/////n text: 'n/a',/////n },/////n CopyButton: {/////n hide: (el, s) => !s.reward_address,/////n value: (el, s) => s.reward_address,/////n },/////n text: null,/////n },/////n },/////n PublicKey_address: {/////n gridColumn: 'span 2',/////n P: {/////n text: 'Public key',/////n },/////n H: {/////n extends: 'Flex',/////n flexAlign: 'center start',/////n Text: {/////n overflow: 'hidden',/////n maxWidth: '95%',/////n textOverflow: 'ellipsis',/////n text: '{{public_key}}',/////n },/////n Text_no: {/////n color: 'caption',/////n hide: (el, s) => s.public_key,/////n fontWeight: '300',/////n text: 'n/a',/////n },/////n CopyButton: {/////n hide: (el, s) => !s.public_key,/////n value: (el, s) => s.public_key,/////n },/////n text: null,/////n },/////n },/////n Hr: {/////n ignoreChildExtend: true,/////n margin: 'A 0',/////n opacity: '.05',/////n gridColumn: 'span 4',/////n },/////n OurProposal: {/////n P: {/////n text: 'Proposals managed by us',/////n },/////n H: {/////n isNan: (el, s) => !s.do_we_manage_proposals,/////n text: (el, s) => s.do_we_manage_proposals || 'n/a',/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n CurrentSpent: {/////n P: {/////n text: 'Current Spent',/////n },/////n H: {/////n text: (el, s) => s.projected_cost ? /////tilde/////dlrsgn/////dlrsgn{ s.current_spend?.toFixed(2) }/////tilde : 'n/a',/////n },/////n },/////n ProjectedCost: {/////n P: {/////n text: 'Projected Cost',/////n },/////n H: {/////n text: (el, s) => s.projected_cost ? /////tilde/////dlrsgn/////dlrsgn{ s.projected_cost?.toFixed(2) }/////tilde : 'n/a',/////n isNan: (el, s) => !s.owner,/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n Consensus: {/////n P: {/////n text: 'Consensus',/////n },/////n H: {/////n text: (el, s) => s.consensus || 'n/a',/////n isNan: (el, s) => !s.consensus,/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n SLA: {/////n P: {/////n text: 'SLA',/////n },/////n H: {/////n text: (el, s) => s.sla || 'n/a',/////n isNan: (el, s) => !s.sla,/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n WarehouseSupport: {/////n if: (el, s) => s.nodeType === 'rpc',/////n P: {/////n text: 'Data warehouse support',/////n },/////n H: {/////n text: (el, s) => s.data_warehouse_support ? 'Yes' : 'No',/////n },/////n },/////n WarehouseUrl: {/////n if: (el, s) => s.nodeType === 'rpc',/////n gridColumn: 'span 2',/////n P: {/////n text: 'Data warehouse URL',/////n },/////n H: null,/////n Link: {/////n isNan: (el, s) => !s.data_warehouse_url,/////n fontSize: 'B',/////n target: '_blank',/////n href: (el, s) => s.data_warehouse_url,/////n text: (el, s) => s.data_warehouse_url ? s.data_warehouse_url.slice(0, 56) + '...' : 'n/a',/////n fontWeight: '400',/////n '[href]:hover': {/////n textDecoration: 'underline',/////n },/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n KnowledgeBase: {/////n gridColumn: 'span 2',/////n P: {/////n text: 'Knowledge base',/////n },/////n H: null,/////n Link: {/////n isNan: (el, s) => !s.knowledge_base,/////n fontSize: 'B',/////n target: '_blank',/////n href: (el, s) => s.knowledge_base,/////n text: (el, s) => s.knowledge_base ? s.knowledge_base.slice(0, 56) + '...' : 'n/a',/////n fontWeight: '400',/////n '[href]:hover': {/////n textDecoration: 'underline',/////n },/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n Explorer: {/////n gridColumn: 'span 2',/////n P: {/////n text: 'Explorer link',/////n },/////n H: null,/////n Link: {/////n isNan: (el, s) => !s.explorer_link,/////n fontSize: 'B',/////n target: '_blank',/////n href: (el, s) => s.explorer_link,/////n text: (el, s) => s.explorer_link ? s.explorer_link.slice(0, 56) + '...' : 'n/a',/////n fontWeight: '400',/////n '[href]:hover': {/////n textDecoration: 'underline',/////n },/////n '.isNan': {/////n fontWeight: '300',/////n color: 'caption',/////n },/////n },/////n },/////n },/////n Hr: {/////n ignoreChildExtend: true,/////n margin: '-B2 0',/////n opacity: '.05',/////n gridColumn: 'span 4',/////n },/////n Uptime: {},/////n Graphs: {},/////n props: {/////n width: '100%',/////n flow: 'y',/////n gap: 'E',/////n padding: 'A2',/////n onClick: (ev, el, s) => {/////n ev.stopPropagation()/////n ev.preventDefault()/////n },/////n },/////n extend: 'Flex',/////n}" - }, - "PreviewItem": { - "key": "PreviewItem", - "type": "component", - "settings": { - "gridOptions": { - "x": 17, - "y": 1, - "w": 1, - "h": 1 - } - }, - "uses": [ - "P", - "H" - ], - "error": null - }, - "ValidatorRow": { - "key": "ValidatorRow", - "type": "component", - "settings": { - "gridOptions": { - "x": 11, - "y": 1, - "w": 3, - "h": 1 - } - }, - "uses": [ - "Link", - "Status", - "Strong", - "Version", - "PublicKey", - "Flex", - "Text", - "CopyButton", - "Env", - "NetworkRowLabel" - ], - "error": null - }, - "ValidatorFooter": { - "key": "ValidatorFooter", - "type": "component", - "settings": { - "gridOptions": { - "x": 11, - "y": 2, - "w": 3, - "h": 1 - } - }, - "uses": [ - "Flex", - "Edit" - ] - }, - "FormModal": { - "key": "FormModal", - "title": "FormModal", - "description": "", - "category": "comp", - "extends": [ - "Modal", - "Modal" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "x": 19, - "y": 2, - "w": 2, - "h": 1 - } - }, - "interactivity": [], - "dataTypes": [], - "uses": [ - "ModalWindow", - "Hgroup", - "H", - "P", - "XBtn", - "Form", - "ModalForm" - ], - "type": "component", - "error": null, - "code": "export default {/////n extend: 'ModalWindow',/////n props: {/////n maxHeight: '95dvh',/////n overflow: 'hidden auto',/////n widthRange: 'H1',/////n '@mobileS': {/////n fontSize: 'Z2',/////n },/////n '@mobileXS': {/////n fontSize: 'Z1',/////n },/////n },/////n Hgroup: {/////n margin: '- W B+V2 W',/////n H: {},/////n P: {},/////n },/////n XBtn: {},/////n Form: {/////n extends: 'ModalForm',/////n },/////n}" - }, - "EditForm": { - "key": "EditForm", - "title": "ContactSection", - "description": "", - "category": "block", - "extends": [ - "Flex", - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "x": 0, - "y": 7, - "w": 2, - "h": 2 - } - }, - "interactivity": [], - "dataTypes": [], - "variants": [ - { - "key": "ContactSection.Two", - "title": "ContactSection.Two", - "description": "", - "category": "block", - "code": "", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 5 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "align": "flex-start flex-start", - "theme": "dialog", - "boxSize": "content-box", - "padding": "C B2 B B2", - "round": "A", - "alignSelf": "flex-start", - "@mobileL": { - "fontSize": "Z2", - "minWidth": "100%" - } - }, - "SectionHgroup": { - "props": { - "align": "flex-start", - "gap": "A" - }, - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "Form": { - "0": { - "props": { - "gridColumn": "1", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "1" - } - }, - "Caption": { - "text": "First name" - }, - "Field": { - "Input": { - "placeholder": "First name" - } - } - }, - "1": { - "props": { - "gridColumn": "2", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "2" - } - }, - "Caption": { - "text": "Last name" - }, - "Field": { - "Input": { - "placeholder": "Last name" - } - } - }, - "2": { - "props": { - "gridColumn": "1", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "3" - } - }, - "Caption": { - "text": "Email" - }, - "Field": { - "Input": { - "placeholder": "Email" - } - } - }, - "3": { - "props": { - "gridColumn": "2", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "4" - } - }, - "Caption": { - "text": "Phone number" - }, - "Field": { - "Input": { - "type": "number", - "placeholder": "000000000", - "::-webkit-inner-spin-button": { - "appearance": "none" - } - } - } - }, - "4": { - "props": { - "gridColumn": "1 / span 2" - }, - "Caption": { - "text": "Choose a topic" - }, - "SelectField": { - "minWidth": "100%", - "margin": "- -Y1", - "@mobileL": { - "minWidth": "100%" - } - }, - "Field": null - }, - "5": { - "props": { - "gap": "A", - "gridColumn": "1 / span 2" - }, - "Caption": { - "text": "Which best describes you?" - }, - "Field": null, - "RadioAnsList": {} - }, - "6": { - "props": { - "gridColumn": "1 / span 2" - }, - "Caption": { - "text": "Message" - }, - "TextAreaField": { - "minWidth": "100%", - "margin": "- -Y1", - "minHeight": "E" - }, - "Field": null - }, - "extend": "Grid", - "props": { - "gap": "B2", - "width": "100%", - "margin": "C1 - A2 -", - "columns": "repeat(2, 1fr)" - }, - "childExtend": { - "extend": "FieldCaption", - "props": { - "minWidth": "100%" - }, - "Caption": {}, - "Field": { - "theme": "field", - "minWidth": "100%" - } - } - }, - "PBtnCheck": { - "props": {}, - "Check": {}, - "PBtn": { - "P": { - "text": "I accept the" - }, - "Btn": { - "text": "terms and conditions" - } - } - }, - "Btn": { - "props": { - "padding": "A C1", - "margin": "C1 - - -", - "@mobileS": { - "minWidth": "100%" - } - }, - "text": "Submit" - } - } - }, - { - "key": "ContactSection.Three", - "title": "ContactSection.Three", - "description": "", - "category": "block", - "code": "", - "extends": [ - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "align": "center flex-start", - "gap": "D2", - "padding": "B1 B1 B1 B2", - "theme": "dialog", - "round": "A", - "alignSelf": "flex-start", - "@tabletM": { - "flow": "y" - } - }, - "ContactSection": { - "theme": "transparent", - "padding": "0", - "align": "flex-start", - "flex": "1", - "@mobileL": { - "minWidth": "100%" - }, - "SectionHgroup": { - "align": "flex-start", - "textAlign": "left" - }, - "Form": {}, - "PBtnCheck": {}, - "Btn": { - "padding": "A C1", - "@mobileM": { - "alignSelf": "center", - "minWidth": "100%" - } - } - }, - "BannerImg": { - "flex": "1", - "boxSize": "I H1", - "@tabletS": { - "boxSize": "I 100%" - } - } - } - }, - { - "key": "ContactSection.Four", - "title": "ContactSection.Four", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 5 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "padding": "B1", - "theme": "dialog", - "textAlign": "left", - "round": "A", - "align": "center flex-start", - "gap": "A2", - "flex": "1", - "@tabletM": { - "flow": "y", - "reverse": true - } - }, - "BannerImg": { - "props": { - "boxSize": "I2+D1 H2", - "gridColumn": "1", - "gridRow": "span 3", - "flex": "1", - "@tabletM": { - "boxSize": "I 100%" - } - } - }, - "ContactSection.Two": { - "theme": "transparent", - "flex": "1", - "@tabletS": { - "minWidth": "100%" - }, - "@mobileM": { - "padding": "0" - }, - "SectionHgroup": { - "align": "flex-start", - "minWidth": "100%", - "textAlign": "left" - }, - "Form": { - "@mobileL": { - "columns": "repeat(1, 1fr)" - } - }, - "PBtnCheck": { - "alignSelf": "flex-start" - }, - "Btn": { - "alignSelf": "flex-start", - "padding": "A C2" - } - } - } - }, - { - "key": "ContactSection.Five", - "title": "ContactSection.Five", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "C C1 C C", - "round": "A", - "gap": "F", - "@screenS": { - "minWidth": "100%", - "gap": "0", - "align": "flex-start space-between" - }, - "@tabletS": { - "flow": "y", - "gap": "D", - "padding": "C" - }, - "@mobileM": { - "padding": "B2" - } - }, - "Article": { - "extend": "Flex", - "props": { - "flow": "y", - "gap": "D" - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You", - "@tabletM": { - "maxWidth": "G2" - } - } - }, - "ContactInfos": {} - }, - "ContactSection": { - "props": { - "padding": "0", - "theme": "transparent" - }, - "SectionHgroup": null, - "Form": { - "props": { - "margin": "- - - -", - "childProps": { - "Caption": {}, - "Field": { - "minWidth": "G3", - "@mobileL": { - "minWidth": "100%" - } - }, - "TextAreaField": { - "minWidth": "G3", - "@mobileL": { - "minWidth": "100%" - } - } - } - } - }, - "PBtnCheck": { - "props": { - "alignSelf": "flex-start", - "padding": "Y - - -" - } - }, - "Btn": { - "props": { - "alignSelf": "flex-start", - "padding": "A C" - } - } - } - } - }, - { - "key": "ContactSection.Seven", - "title": "ContactSection.Seven", - "description": "", - "category": "block", - "code": "", - "extends": [ - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "C B2 B2 B2", - "round": "A", - "flow": "y", - "gap": "D", - "@mobileM": { - "padding": "B2 B1 B1 B1" - }, - "@mobileS": { - "padding": "B1 B B B" - } - }, - "Flex": { - "props": { - "align": "flex-end space-between", - "padding": "- Y - W", - "gap": "C", - "@tabletM": { - "flow": "y", - "align": "flex-start", - "gap": "D" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You", - "maxWidth": "G1" - } - }, - "ContactInfos.Two": { - "gap": "C2" - } - }, - "BannerImg": { - "boxSize": "H2 J", - "flex": "1", - "@tabletM": { - "boxSize": "H2 100%" - } - } - } - }, - { - "key": "ContactSection.Eight", - "title": "ContactSection.Eight", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "C B1 B1 B1", - "round": "A", - "flow": "y", - "gap": "D" - }, - "Flex": { - "props": { - "align": "flex-end space-between", - "padding": "- Y - W", - "gap": "C", - "@tabletS": { - "flow": "y", - "align": "flex-start", - "gap": "C2" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You", - "maxWidth": "G1" - } - }, - "ContactInfos": {} - }, - "BannerImg": { - "boxSize": "H2 J", - "flex": "1", - "@tabletM": { - "boxSize": "H2 100%" - } - } - } - }, - { - "key": "ContactSection.Nine", - "title": "ContactSection.Nine", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "B2", - "round": "A", - "gap": "E", - "@tabletM": { - "flow": "y", - "gap": "D" - }, - "@mobileM": { - "padding": "B1" - } - }, - "Article": { - "extend": "Flex", - "props": { - "flow": "y", - "gap": "D2", - "margin": "B - - -", - "@tabletM": { - "margin": "0", - "gap": "D" - } - }, - "SectionHgroup": { - "gap": "A1", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "ContactInfos": {} - }, - "BannerImg": { - "@tabletM": { - "flex": "1", - "width": "100%" - } - } - } - }, - { - "key": "ContactSection.Ten", - "title": "ContactSection.Ten", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "B2", - "round": "A", - "gap": "E", - "@tabletM": { - "flow": "y", - "minWidth": "100%" - }, - "@mobileM": { - "gap": "D", - "padding": "B2 B1 B1 B1" - } - }, - "Article": { - "extend": "Flex", - "props": { - "flow": "y", - "gap": "D2", - "margin": "B - - -", - "@mobileM": { - "gap": "D", - "margin": "0" - } - }, - "SectionHgroup": { - "gap": "A1", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "ContactInfos.Two": { - "flow": "y", - "gap": "C", - "@tabletM": { - "flow": "x" - } - } - }, - "BannerImg": { - "flex": "1", - "@tabletM": { - "width": "100%" - } - } - } - }, - { - "key": "ContactSection.Eleven", - "title": "ContactSection.Eleven", - "description": "", - "category": "block", - "code": "", - "extends": [ - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "gap": "D1", - "padding": "B2 B2 C B2", - "theme": "dialog", - "round": "A" - }, - "SectionHgroup": { - "gap": "A1", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "ContacInfos.Three": {} - } - }, - { - "key": "ContactSection.Thirteen", - "title": "ContactSection.Thirteen", - "description": "", - "category": "block", - "code": "", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "theme": "dialog", - "padding": "B2 C", - "gap": "C2", - "round": "A", - "@mobileS": { - "fontSize": "Z2" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "Flex": { - "0": {}, - "1": {}, - "props": { - "gap": "D", - "@tabletS": { - "flow": "y", - "minWidth": "100%", - "align": "center flex-start", - "gap": "B" - }, - "childProps": { - "@tabletS": { - "minWidth": "100%" - }, - "Map": { - "theme": "field" - }, - "HgroupLink": { - "@tabletS": { - "alignSelf": "flex-start" - } - } - } - }, - "childExtend": "LocationInfo" - } - } - }, - { - "key": "ContactSection.Fourteen", - "title": "ContactSection.Fourteen", - "description": "", - "category": "block", - "code": "", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "theme": "dialog", - "padding": "B2 B2 B2 C1", - "round": "A", - "gap": "C1", - "@screenS": { - "minWidth": "100%" - }, - "@tabletM": { - "padding": "B2 C B C" - }, - "@mobileM": { - "padding": "B2 B1 A B1" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "Flex": { - "props": { - "gap": "F", - "@tabletM": { - "flow": "y", - "gap": "C2" - } - }, - "Aside": { - "0": {}, - "1": {}, - "2": {}, - "3": {}, - "extend": "Flex", - "props": { - "flow": "y", - "gap": "C2", - "padding": "B - - -", - "@tabletM": { - "gap": "C", - "padding": "0 - - -" - } - }, - "childExtend": { - "extend": "HgroupLink", - "props": {} - } - }, - "Map": { - "extend": "Flex", - "props": { - "theme": "field", - "minWidth": "I", - "minHeight": "H2", - "align": "center center", - "round": "A", - "@screenS": { - "flex": "1" - }, - "@tabletM": { - "minWidth": "100%", - "minHeight": "G2", - "margin": "- -A" - }, - "@mobileM": { - "minHeight": "G" - } - }, - "Icon": { - "props": { - "name": "send", - "fontSize": "E2" - } - } - } - } - } - }, - { - "key": "ContactSection.Two", - "title": "ContactSection.Two", - "description": "", - "category": "block", - "code": "", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 5 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "align": "flex-start flex-start", - "theme": "dialog", - "boxSize": "content-box", - "padding": "C B2 B B2", - "round": "A", - "alignSelf": "flex-start", - "@mobileL": { - "fontSize": "Z2", - "minWidth": "100%" - } - }, - "SectionHgroup": { - "props": { - "align": "flex-start", - "gap": "A" - }, - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "Form": { - "0": { - "props": { - "gridColumn": "1", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "1" - } - }, - "Caption": { - "text": "First name" - }, - "Field": { - "Input": { - "placeholder": "First name" - } - } - }, - "1": { - "props": { - "gridColumn": "2", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "2" - } - }, - "Caption": { - "text": "Last name" - }, - "Field": { - "Input": { - "placeholder": "Last name" - } - } - }, - "2": { - "props": { - "gridColumn": "1", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "3" - } - }, - "Caption": { - "text": "Email" - }, - "Field": { - "Input": { - "placeholder": "Email" - } - } - }, - "3": { - "props": { - "gridColumn": "2", - "@mobileL": { - "gridColumn": "1 / span 2", - "gridRow": "4" - } - }, - "Caption": { - "text": "Phone number" - }, - "Field": { - "Input": { - "type": "number", - "placeholder": "000000000", - "::-webkit-inner-spin-button": { - "appearance": "none" - } - } - } - }, - "4": { - "props": { - "gridColumn": "1 / span 2" - }, - "Caption": { - "text": "Choose a topic" - }, - "SelectField": { - "minWidth": "100%", - "margin": "- -Y1", - "@mobileL": { - "minWidth": "100%" - } - }, - "Field": null - }, - "5": { - "props": { - "gap": "A", - "gridColumn": "1 / span 2" - }, - "Caption": { - "text": "Which best describes you?" - }, - "Field": null, - "RadioAnsList": {} - }, - "6": { - "props": { - "gridColumn": "1 / span 2" - }, - "Caption": { - "text": "Message" - }, - "TextAreaField": { - "minWidth": "100%", - "margin": "- -Y1", - "minHeight": "E" - }, - "Field": null - }, - "extend": "Grid", - "props": { - "gap": "B2", - "width": "100%", - "margin": "C1 - A2 -", - "columns": "repeat(2, 1fr)" - }, - "childExtend": { - "extend": "FieldCaption", - "props": { - "minWidth": "100%" - }, - "Caption": {}, - "Field": { - "theme": "field", - "minWidth": "100%" - } - } - }, - "PBtnCheck": { - "props": {}, - "Check": {}, - "PBtn": { - "P": { - "text": "I accept the" - }, - "Btn": { - "text": "terms and conditions" - } - } - }, - "Btn": { - "props": { - "padding": "A C1", - "margin": "C1 - - -", - "@mobileS": { - "minWidth": "100%" - } - }, - "text": "Submit" - } - } - }, - { - "key": "ContactSection.Three", - "title": "ContactSection.Three", - "description": "", - "category": "block", - "code": "", - "extends": [ - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "align": "center flex-start", - "gap": "D2", - "padding": "B1 B1 B1 B2", - "theme": "dialog", - "round": "A", - "alignSelf": "flex-start", - "@tabletM": { - "flow": "y" - } - }, - "ContactSection": { - "theme": "transparent", - "padding": "0", - "align": "flex-start", - "flex": "1", - "@mobileL": { - "minWidth": "100%" - }, - "SectionHgroup": { - "align": "flex-start", - "textAlign": "left" - }, - "Form": {}, - "PBtnCheck": {}, - "Btn": { - "padding": "A C1", - "@mobileM": { - "alignSelf": "center", - "minWidth": "100%" - } - } - }, - "BannerImg": { - "flex": "1", - "boxSize": "I H1", - "@tabletS": { - "boxSize": "I 100%" - } - } - } - }, - { - "key": "ContactSection.Four", - "title": "ContactSection.Four", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 5 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "padding": "B1", - "theme": "dialog", - "textAlign": "left", - "round": "A", - "align": "center flex-start", - "gap": "A2", - "flex": "1", - "@tabletM": { - "flow": "y", - "reverse": true - } - }, - "BannerImg": { - "props": { - "boxSize": "I2+D1 H2", - "gridColumn": "1", - "gridRow": "span 3", - "flex": "1", - "@tabletM": { - "boxSize": "I 100%" - } - } - }, - "ContactSection.Two": { - "theme": "transparent", - "flex": "1", - "@tabletS": { - "minWidth": "100%" - }, - "@mobileM": { - "padding": "0" - }, - "SectionHgroup": { - "align": "flex-start", - "minWidth": "100%", - "textAlign": "left" - }, - "Form": { - "@mobileL": { - "columns": "repeat(1, 1fr)" - } - }, - "PBtnCheck": { - "alignSelf": "flex-start" - }, - "Btn": { - "alignSelf": "flex-start", - "padding": "A C2" - } - } - } - }, - { - "key": "ContactSection.Five", - "title": "ContactSection.Five", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "C C1 C C", - "round": "A", - "gap": "F", - "@screenS": { - "minWidth": "100%", - "gap": "0", - "align": "flex-start space-between" - }, - "@tabletS": { - "flow": "y", - "gap": "D", - "padding": "C" - }, - "@mobileM": { - "padding": "B2" - } - }, - "Article": { - "extend": "Flex", - "props": { - "flow": "y", - "gap": "D" - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You", - "@tabletM": { - "maxWidth": "G2" - } - } - }, - "ContactInfos": {} - }, - "ContactSection": { - "props": { - "padding": "0", - "theme": "transparent" - }, - "SectionHgroup": null, - "Form": { - "props": { - "margin": "- - - -", - "childProps": { - "Caption": {}, - "Field": { - "minWidth": "G3", - "@mobileL": { - "minWidth": "100%" - } - }, - "TextAreaField": { - "minWidth": "G3", - "@mobileL": { - "minWidth": "100%" - } - } - } - } - }, - "PBtnCheck": { - "props": { - "alignSelf": "flex-start", - "padding": "Y - - -" - } - }, - "Btn": { - "props": { - "alignSelf": "flex-start", - "padding": "A C" - } - } - } - } - }, - { - "key": "ContactSection.Seven", - "title": "ContactSection.Seven", - "description": "", - "category": "block", - "code": "", - "extends": [ - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "C B2 B2 B2", - "round": "A", - "flow": "y", - "gap": "D", - "@mobileM": { - "padding": "B2 B1 B1 B1" - }, - "@mobileS": { - "padding": "B1 B B B" - } - }, - "Flex": { - "props": { - "align": "flex-end space-between", - "padding": "- Y - W", - "gap": "C", - "@tabletM": { - "flow": "y", - "align": "flex-start", - "gap": "D" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You", - "maxWidth": "G1" - } - }, - "ContactInfos.Two": { - "gap": "C2" - } - }, - "BannerImg": { - "boxSize": "H2 J", - "flex": "1", - "@tabletM": { - "boxSize": "H2 100%" - } - } - } - }, - { - "key": "ContactSection.Eight", - "title": "ContactSection.Eight", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "C B1 B1 B1", - "round": "A", - "flow": "y", - "gap": "D" - }, - "Flex": { - "props": { - "align": "flex-end space-between", - "padding": "- Y - W", - "gap": "C", - "@tabletS": { - "flow": "y", - "align": "flex-start", - "gap": "C2" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You", - "maxWidth": "G1" - } - }, - "ContactInfos": {} - }, - "BannerImg": { - "boxSize": "H2 J", - "flex": "1", - "@tabletM": { - "boxSize": "H2 100%" - } - } - } - }, - { - "key": "ContactSection.Nine", - "title": "ContactSection.Nine", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "B2", - "round": "A", - "gap": "E", - "@tabletM": { - "flow": "y", - "gap": "D" - }, - "@mobileM": { - "padding": "B1" - } - }, - "Article": { - "extend": "Flex", - "props": { - "flow": "y", - "gap": "D2", - "margin": "B - - -", - "@tabletM": { - "margin": "0", - "gap": "D" - } - }, - "SectionHgroup": { - "gap": "A1", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "ContactInfos": {} - }, - "BannerImg": { - "@tabletM": { - "flex": "1", - "width": "100%" - } - } - } - }, - { - "key": "ContactSection.Ten", - "title": "ContactSection.Ten", - "description": "", - "category": "block", - "code": "", - "extends": [ - "ContactSection" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "theme": "dialog", - "padding": "B2", - "round": "A", - "gap": "E", - "@tabletM": { - "flow": "y", - "minWidth": "100%" - }, - "@mobileM": { - "gap": "D", - "padding": "B2 B1 B1 B1" - } - }, - "Article": { - "extend": "Flex", - "props": { - "flow": "y", - "gap": "D2", - "margin": "B - - -", - "@mobileM": { - "gap": "D", - "margin": "0" - } - }, - "SectionHgroup": { - "gap": "A1", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "ContactInfos.Two": { - "flow": "y", - "gap": "C", - "@tabletM": { - "flow": "x" - } - } - }, - "BannerImg": { - "flex": "1", - "@tabletM": { - "width": "100%" - } - } - } - }, - { - "key": "ContactSection.Eleven", - "title": "ContactSection.Eleven", - "description": "", - "category": "block", - "code": "", - "extends": [ - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 3 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "gap": "D1", - "padding": "B2 B2 C B2", - "theme": "dialog", - "round": "A" - }, - "SectionHgroup": { - "gap": "A1", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "ContacInfos.Three": {} - } - }, - { - "key": "ContactSection.Thirteen", - "title": "ContactSection.Thirteen", - "description": "", - "category": "block", - "code": "", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "theme": "dialog", - "padding": "B2 C", - "gap": "C2", - "round": "A", - "@mobileS": { - "fontSize": "Z2" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "Flex": { - "0": {}, - "1": {}, - "props": { - "gap": "D", - "@tabletS": { - "flow": "y", - "minWidth": "100%", - "align": "center flex-start", - "gap": "B" - }, - "childProps": { - "@tabletS": { - "minWidth": "100%" - }, - "Map": { - "theme": "field" - }, - "HgroupLink": { - "@tabletS": { - "alignSelf": "flex-start" - } - } - } - }, - "childExtend": "LocationInfo" - } - } - }, - { - "key": "ContactSection.Fourteen", - "title": "ContactSection.Fourteen", - "description": "", - "category": "block", - "code": "", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "colspan": 12, - "rowspan": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "component": { - "extend": [ - "Flex" - ], - "props": { - "flow": "y", - "theme": "dialog", - "padding": "B2 B2 B2 C1", - "round": "A", - "gap": "C1", - "@screenS": { - "minWidth": "100%" - }, - "@tabletM": { - "padding": "B2 C B C" - }, - "@mobileM": { - "padding": "B2 B1 A B1" - } - }, - "SectionHgroup": { - "gap": "A", - "H": { - "text": "Contact us" - }, - "P": { - "text": "We Value Your Connection and Look Forward to Hearing from You" - } - }, - "Flex": { - "props": { - "gap": "F", - "@tabletM": { - "flow": "y", - "gap": "C2" - } - }, - "Aside": { - "0": {}, - "1": {}, - "2": {}, - "3": {}, - "extend": "Flex", - "props": { - "flow": "y", - "gap": "C2", - "padding": "B - - -", - "@tabletM": { - "gap": "C", - "padding": "0 - - -" - } - }, - "childExtend": { - "extend": "HgroupLink", - "props": {} - } - }, - "Map": { - "extend": "Flex", - "props": { - "theme": "field", - "minWidth": "I", - "minHeight": "H2", - "align": "center center", - "round": "A", - "@screenS": { - "flex": "1" - }, - "@tabletM": { - "minWidth": "100%", - "minHeight": "G2", - "margin": "- -A" - }, - "@mobileM": { - "minHeight": "G" - } - }, - "Icon": { - "props": { - "name": "send", - "fontSize": "E2" - } - } - } - } - } - } - ], - "thumbnail": { - "screenshot": "https://storage.screenshotapi.net/default_symbo_ls_component_key_contactsection_widt_51480f8953fd.webp", - "url": "https://default.symbo.ls/component?key=ContactSection&width=1096&height=1096&hide_ui=true", - "created_at": "2024-08-31T12:13:17.567Z", - "is_fresh": true, - "token": "SGG5Q3D-WZN437D-NCC3ZZ2-Q15ZMZG", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36", - "width": "1096", - "height": "1096", - "dark_mode": "true", - "fresh": "true", - "file_type": "webp", - "retina": "true", - "wait_for_event": "domcontentloaded", - "ttl": "2024-09-30T12:13:12.387Z" - }, - "uses": [ - "FormModal", - "Hgroup", - "H", - "P", - "Form", - "Button" - ], - "type": "component", - "error": null - }, - "FieldCaption": { - "key": "FieldCaption", - "title": "FieldCaption", - "description": "", - "category": "comp", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "x": 15, - "y": 0, - "w": 1, - "h": 1 - } - }, - "interactivity": [], - "dataTypes": [], - "variants": [], - "uses": [ - "Flex", - "Caption", - "Field", - "Input", - "Icon" - ], - "type": "component" - }, - "FilterSidebar": { - "key": "FilterSidebar", - "type": "component", - "settings": { - "gridOptions": { - "x": 16, - "y": 1, - "w": 1, - "h": 1 - } - }, - "uses": [ - "DropdownParent", - "Button", - "Icon", - "Dropdown" - ] - }, - "FilterStatus": { - "key": "FilterStatus", - "type": "component", - "settings": { - "gridOptions": { - "x": 11, - "y": 6, - "w": 1, - "h": 1 - } - }, - "uses": [ - "Flex", - "Stable", - "Box", - "Off" - ] - }, - "Dropdown": { - "key": "Dropdown", - "type": "component", - "settings": { - "gridOptions": { - "x": 16, - "y": 0, - "w": 1, - "h": 1 - } - }, - "uses": [], - "code": "export default {/////n props: {/////n isDropdownRoot: true,/////n theme: 'dialog',/////n round: 'Z2',/////n position: 'absolute',/////n left: '50%',/////n transform: 'translate3d(0, -10px, 0)',/////n top: '102%',/////n minWidth: 'F',/////n maxHeight: 'H',/////n transition: 'A defaultBezier',/////n transitionProperty: 'visibility, transform, opacity',/////n overflow: 'hidden auto',/////n opacity: '0',/////n visibility: 'hidden',/////n boxSizing: 'border-box',/////n zIndex: 1000,/////n '@dark': {/////n boxShadow: 'black .20, 0px, 5px, 10px, 5px',/////n },/////n '@light': {/////n boxShadow: 'gray5 .20, 0px, 5px, 10px, 5px',/////n },/////n },/////n attr: {/////n dropdown: true/////n },/////n tag: 'section',/////n}" - }, - "CopyButton": { - "key": "CopyButton", - "type": "component", - "settings": { - "gridOptions": { - "x": 15, - "y": 2, - "w": 1, - "h": 1 - } - }, - "uses": [ - "IconButton" - ] - }, - "ValidatorLogs": { - "key": "ValidatorLogs", - "type": "component", - "settings": { - "gridOptions": { - "x": 22, - "y": 0, - "w": 2, - "h": 2 - } - }, - "uses": [ - "Grid", - "Monkier", - "Title", - "Strong", - "Owner", - "CloudProvider", - "Reward", - "RAM" - ] - }, - "LogItem": { - "key": "LogItem", - "type": "component", - "settings": { - "gridOptions": { - "x": 23, - "y": 2, - "w": 1, - "h": 1 - } - }, - "uses": [ - "Title", - "H3" - ] - }, - "Chart": { - "key": "Chart", - "type": "component", - "settings": { - "gridOptions": { - "x": 3, - "y": 5, - "w": 2, - "h": 1 - } - }, - "uses": [], - "error": null, - "code": "export default {/////n props: {/////n onRender: async (el, s) => {/////n const Chart = await import('chart.js')/////n const ctx = el.node.getContext('2d');/////n/////n const maxPoints = 50;/////n const dataPoints = Array(maxPoints).fill(0);/////n/////n const labels = Array.from({/////n length: maxPoints/////n }, (_, i) => i.toString());/////n/////n // Initialize with some starting data/////n for (let i = 0; i < dataPoints.length; i++) {/////n dataPoints[i] = 30 + Math.sin(i * 0.2) * 20;/////n }/////n/////n const data = {/////n labels: labels,/////n datasets: [{/////n label: 'CPU',/////n data: dataPoints.map((value, index) => ({/////n x: index,/////n y: value/////n })),/////n borderColor: 'rgb(86, 154, 67)',/////n fill: false,/////n tension: 0.4,/////n pointRadius: 0,/////n borderWidth: 2,/////n cubicInterpolationMode: 'monotone'/////n }]/////n };/////n/////n const options = {/////n responsive: true,/////n animation: false,/////n interaction: {/////n intersect: false,/////n mode: 'index'/////n },/////n plugins: {/////n legend: {/////n display: false/////n },/////n tooltip: {/////n enabled: false/////n }/////n },/////n scales: {/////n x: {/////n display: false,/////n type: 'linear',/////n min: 0,/////n max: maxPoints - 1/////n },/////n y: {/////n display: false,/////n min: 0,/////n max: 100/////n }/////n },/////n elements: {/////n line: {/////n borderJoinStyle: 'round'/////n }/////n },/////n maintainAspectRatio: false/////n };/////n/////n const chart = new Chart(ctx, {/////n type: 'line',/////n data: data,/////n options: options/////n });/////n/////n // Color functions/////n function getColor(value) {/////n if (value < 60) return 'rgb(86, 154, 67)';/////n if (value < 80) return 'rgb(245, 158, 11)';/////n return 'rgb(220, 38, 38)';/////n }/////n/////n function interpolateColor(color1, color2, factor) {/////n const c1 = color1.match(/\\d+/g).map(Number);/////n const c2 = color2.match(/\\d+/g).map(Number);/////n const r = Math.round(c1[0] + (c2[0] - c1[0]) * factor);/////n const g = Math.round(c1[1] + (c2[1] - c1[1]) * factor);/////n const b = Math.round(c1[2] + (c2[2] - c1[2]) * factor);/////n return /////tildergb(/////dlrsgn{r}, /////dlrsgn{g}, /////dlrsgn{b})/////tilde;/////n }/////n/////n let animationFrame = null;/////n let isAnimating = false;/////n/////n function updateCPUChart(newValue) {/////n if (isAnimating) return;/////n/////n isAnimating = true;/////n const dataset = chart.data.datasets[0];/////n const originalData = [...dataset.data];/////n const lastPoint = originalData[originalData.length - 1];/////n const startTime = performance.now();/////n const duration = 1000;/////n/////n // Get colors for transition/////n const startColor = dataset.borderColor;/////n const endColor = getColor(newValue);/////n/////n function animate(currentTime) {/////n const elapsed = currentTime - startTime;/////n const progress = Math.min(elapsed / duration, 1);/////n const easeProgress = easeInOutCubic(progress);/////n/////n // Create new animated data/////n const animatedData = [];/////n/////n // Animate all existing points shifting left (keep their y values)/////n for (let i = 0; i < originalData.length; i++) {/////n animatedData.push({/////n x: originalData[i].x - easeProgress,/////n y: originalData[i].y // Keep original y value unchanged/////n });/////n }/////n/////n // Add new point entering from the right, transitioning from lastPoint.y to newValue/////n animatedData.push({/////n x: maxPoints - 1 + (1 - easeProgress),/////n y: lastPoint.y + (newValue - lastPoint.y) * easeProgress/////n });/////n/////n dataset.data = animatedData;/////n/////n // Animate color if crossing threshold/////n if (getColor(lastPoint.y) !== getColor(newValue)) {/////n dataset.borderColor = interpolateColor(startColor, endColor, easeProgress);/////n }/////n/////n chart.update('none');/////n/////n if (progress < 1) {/////n animationFrame = requestAnimationFrame(animate);/////n } else {/////n // Final state: shift all data and add new point/////n dataset.data = [/////n ...originalData.slice(1).map((point, i) => ({/////n x: i,/////n y: point.y/////n })),/////n {/////n x: maxPoints - 1,/////n y: newValue/////n }/////n ];/////n dataset.borderColor = endColor;/////n chart.update('none');/////n isAnimating = false;/////n }/////n }/////n/////n animationFrame = requestAnimationFrame(animate);/////n }/////n/////n function easeInOutCubic(t) {/////n return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;/////n }/////n/////n // Simulate CPU data updates/////n const interval = setInterval(() => {/////n const fakeCPU = Math.floor(Math.random() * 100);/////n updateCPUChart(fakeCPU);/////n }, 1100);/////n/////n // Cleanup function/////n return () => {/////n clearInterval(interval);/////n if (animationFrame) {/////n cancelAnimationFrame(animationFrame);/////n }/////n chart.destroy();/////n };/////n },/////n tag: 'canvas',/////n minWidth: 'G',/////n minHeight: 'D',/////n },/////n data: {},/////n}" - }, - "Uptime": { - "key": "Uptime", - "type": "component", - "settings": { - "gridOptions": { - "x": 3, - "y": 4, - "w": 3, - "h": 1 - } - }, - "uses": [ - "Flex", - "Title" - ], - "title": "Uptime", - "description": "", - "category": "", - "tags": [], - "state": { - "isActive": true - }, - "props": {}, - "interactivity": [], - "dataTypes": [], - "advanced": true, - "error": null - }, - "Logs": { - "key": "Logs", - "type": "component", - "settings": { - "gridOptions": { - "x": 8, - "y": 0, - "w": 2, - "h": 1 - } - }, - "uses": [ - "Flex", - "H6", - "Date", - "Status", - "Notes", - "Title" - ], - "title": "Logs", - "description": "", - "category": "", - "tags": [], - "state": { - "isActive": true, - "logs": [ - { - "action_items": "Check disk space usage; Implement log rotation", - "created_at": "2025-05-04T00:05:40.283Z", - "status_notes": "Node operating normally with stable performance" - } - ] - }, - "props": {}, - "interactivity": [], - "dataTypes": [], - "advanced": true, - "error": null - }, - "Graphs": { - "key": "Graphs", - "type": "component", - "settings": { - "gridOptions": { - "x": 3, - "y": 3, - "w": 3, - "h": 1 - } - }, - "uses": [ - "Flex", - "Title", - "Chart", - "BlockHeight" - ], - "title": "Graphs", - "description": "", - "category": "", - "tags": [], - "state": { - "isActive": true - }, - "props": {}, - "interactivity": [], - "dataTypes": [], - "advanced": true, - "error": null - }, - "Chart.Block": { - "key": "Chart.Block", - "type": "component", - "settings": { - "gridOptions": { - "x": 3, - "y": 6, - "w": 2, - "h": 1 - } - }, - "uses": [ - "Number", - "Box" - ] - }, - "ValidatorsList": { - "key": "ValidatorsList", - "type": "component", - "settings": { - "gridOptions": { - "x": 11, - "y": 3, - "w": 3, - "h": 2 - } - }, - "uses": [ - "ValidatorRow", - "ValidatorContent" - ], - "title": "ValidatorsList", - "description": "", - "category": "", - "tags": [], - "state": "", - "props": { - "demoComponent": { - "children": [ - {}, - {} - ] - } - }, - "interactivity": [], - "dataTypes": [], - "advanced": true, - "error": null - }, - "Aside": { - "key": "Aside", - "type": "component", - "settings": { - "gridOptions": { - "x": 21, - "y": 0, - "w": 1, - "h": 2 - } - }, - "uses": [ - "InputField", - "Icon", - "Input", - "FilterSidebar", - "Button", - "Dropdown", - "All", - "Mainnet", - "Testnet", - "None", - "Today", - "Last2Days", - "Last3Days", - "LastWeek", - "LastMonth" - ] - }, - "AIMessage": { - "key": "AIMessage", - "type": "component", - "settings": { - "gridOptions": { - "x": 25, - "y": 2, - "w": 1, - "h": 1 - } - }, - "error": null, - "uses": [ - "LabelTag", - "Focusable" - ] - }, - "AIThread": { - "key": "AIThread", - "type": "component", - "settings": { - "gridOptions": { - "x": 26, - "y": 2, - "w": 2, - "h": 2 - } - }, - "error": null, - "uses": [ - "Flex" - ] - }, - "Prompt": { - "key": "Prompt", - "type": "component", - "settings": { - "gridOptions": { - "x": 25, - "y": 0, - "w": 3, - "h": 2 - } - }, - "error": null, - "uses": [ - "Relative", - "Textarea", - "Submit", - "SquareButton", - "AIThread" - ] - }, - "Search": { - "key": "Search", - "title": "Search", - "description": "", - "category": "comp", - "extends": [], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "x": 15, - "y": 9, - "w": 2, - "h": 2 - } - }, - "interactivity": [], - "dataTypes": [], - "pdf": { - "screenshot": "https://storage.screenshotapi.net/default_symbo_ls_component_key_search_width_1007_h_dcb2d641c14a.pdf", - "url": "https://default.symbo.ls/component?key=Search&width=1007&height=504&hide_ui=true", - "created_at": "2024-10-03T06:56:38.591Z", - "is_fresh": true, - "token": "SGG5Q3D-WZN437D-NCC3ZZ2-Q15ZMZG", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36", - "width": "1007", - "height": "504", - "dark_mode": "true", - "fresh": "true", - "file_type": "pdf", - "pdf_options": { - "format": "A4", - "media": "print", - "landscape": false, - "print_background": false - }, - "wait_for_event": "domcontentloaded", - "ttl": "2024-11-02T06:56:34.246Z", - "sizes": null, - "smblsUrl": "https://screenshots.symbo.ls/default_symbo_ls_component_key_search_width_1007_h_dcb2d641c14a.pdf" - }, - "uses": [ - "Flex", - "Icon", - "Input" - ], - "type": "component", - "error": null, - "code": "export default {/////n extend: [/////n 'Flex',/////n ],/////n tag: 'search',/////n state: {},/////n/////n props: {/////n minWidth: 'G2',/////n gap: 'Z',/////n icon: 'search',/////n align: 'center flex-start',/////n position: 'relative',/////n theme: 'field',/////n round: 'D2',/////n '@mobileS': {/////n minWidth: 'G1',/////n },/////n },/////n Icon: {/////n icon: 'search',/////n right: 'A+V2',/////n },/////n Input: {/////n type: 'search',/////n placeholder: 'Type a command or search',/////n width: '100%',/////n padding: 'Z2 C Z2 A2+W',/////n theme: 'transparent',/////n ':focus ~ button': {/////n opacity: '1',/////n },/////n onInput: (ev, el, s) => {/////n console.log(el.node.value)/////n s.update({/////n searchTerm: el.node.value/////n })/////n },/////n },/////n}" - }, - "NetworkRow": { - "key": "NetworkRow", - "type": "component", - "title": "Row", - "description": "", - "category": "", - "tags": [], - "state": "data/0", - "props": { - "demoComponent": { - "state": { - "protocol": "kaia", - "env": "Mainnet", - "role": "Validator", - "status": "Stable/Maintenance", - "repo_url": "https://github.com/kaiachain/kaia/releases/", - "version": "v1.2.3", - "validator_info": [ - { - "moniker": "BCW Technologies", - "public_key": "mantravaloper1r3s4pefpz69fk4rq8sn0zt2wxnv09uffz06nxu" - } - ], - "validator_count": 1 - } - } - }, - "settings": { - "gridOptions": { - "x": 11, - "y": 5, - "w": 3, - "h": 1 - } - }, - "interactivity": [], - "dataTypes": [], - "uses": [ - "Grid", - "Name", - "Avatar", - "Title", - "Env", - "NodeTypes", - "CloudProvider", - "Status" - ], - "advanced": true, - "error": null - }, - "Page": { - "key": "Page", - "type": "component", - "settings": { - "gridOptions": { - "x": 0, - "y": 4, - "w": 2, - "h": 2 - } - }, - "error": null, - "uses": [], - "code": "export default {/////n props: {/////n flexFlow: 'y',/////n position: 'relative',/////n heightRange: '100dvh',/////n overflow: 'hidden',/////n },/////n}" - }, - "NavButton": { - "key": "NavButton", - "type": "component", - "settings": { - "gridOptions": { - "x": 7, - "y": 1, - "w": 1, - "h": 1 - } - }, - "error": null, - "uses": [ - "Link", - "Button" - ] - }, - "ModalWindow": { - "key": "ModalWindow", - "title": "Modal", - "description": "", - "category": "comp", - "extends": [ - "Flex" - ], - "state": "", - "props": {}, - "settings": { - "gridOptions": { - "x": 19, - "y": 3, - "w": 2, - "h": 1 - } - }, - "interactivity": [], - "dataTypes": [], - "uses": [ - "Flex", - "Hgroup", - "H", - "P", - "XBtn", - "Icon" - ], - "type": "component", - "error": null - }, - "Modal": { - "key": "Modal", - "type": "component", - "title": "Modal Fade", - "description": "", - "category": "", - "tags": [], - "state": { - "root": { - "modal": "/add-network" - } - }, - "props": { - "demoComponent": { - "editMode": true - } - }, - "settings": { - "gridOptions": { - "x": 19, - "y": 0, - "w": 2, - "h": 2 - } - }, - "interactivity": [], - "dataTypes": [], - "uses": [], - "error": null, - "advanced": true - }, - "MetaSection": { - "description": "A section aggregating multiple MetricItem components.", - "key": "MetaSection", - "title": "MetricsSection Component", - "type": "component", - "settings": { - "gridOptions": { - "x": 0, - "y": 2, - "w": 2, - "h": 2 - } - }, - "error": null, - "uses": [ - "H6", - "Flex", - "Grid", - "H", - "P" - ] - }, - "NetworkRowLabel": { - "key": "NetworkRowLabel", - "type": "component", - "settings": { - "gridOptions": { - "x": 7, - "y": 0, - "w": 1, - "h": 1 - } - }, - "error": null, - "uses": [ - "LabelTag" - ] - }, - "Header": { - "key": "Header", - "type": "component", - "settings": { - "gridOptions": { - "x": 15, - "y": 8, - "w": 4, - "h": 1 - } - }, - "error": null, - "uses": [ - "Flex", - "Link", - "IconButton", - "Search", - "Input", - "Avatar" - ], - "code": "export default {/////n extend: 'Flex',/////n props: {/////n minWidth: 'G2',/////n icon: 'search',/////n align: 'center flex-start',/////n position: 'relative',/////n theme: 'dialog',/////n round: 'D2',/////n ':focus-visible': {/////n outline: 'solid, X, blue .3',/////n },/////n ':focus-within ~ .search-results': {/////n opacity: 0.3/////n },/////n ':focus-within ~ .content': {/////n opacity: 0.3/////n }/////n },/////n Link: {/////n extends: [/////n 'Link',/////n 'IconButton',/////n ],/////n paddingInline: 'A Z1',/////n icon: 'chevron left',/////n href: '/',/////n theme: null,/////n hide: () => window.location.pathname === '/',/////n borderRadius: '0',/////n border: null,/////n borderWidth: '0 1px 0 0',/////n borderStyle: 'solid',/////n borderColor: 'deepFir',/////n style: null,/////n },/////n Search: {/////n flex: 1,/////n theme: 'dialog',/////n paddingInline: 'Z Z1',/////n Input: {/////n paddingInline: 'Z1',/////n ':focus-visible': null,/////n },/////n },/////n Avatar: {/////n boxSize: 'B',/////n margin: 'Z',/////n src: (el, s) => s.root.user?.picture || /////tildehttps://avatars.symbo.ls/initials/png?seed=/////dlrsgn{s.root.user?.name}/////tilde,/////n },/////n}" - }, - "OldNotes": { - "key": "OldNotes", - "type": "component", - "settings": { - "gridOptions": { - "x": 3, - "y": 7, - "w": 2, - "h": 1 - } - }, - "uses": [ - "Flex", - "Title", - "Notes", - "Date", - "Add", - "Edit", - "Remove", - "Addnew", - "Actions" - ], - "title": "Logs", - "description": "", - "category": "", - "tags": [], - "state": { - "isActive": true, - "logs": [ - { - "action_items": "Check disk space usage; Implement log rotation", - "created_at": "2025-05-04T00:05:40.283Z", - "status_notes": "Node operating normally with stable performance" - } - ] - }, - "props": {}, - "interactivity": [], - "dataTypes": [], - "advanced": true - }, - "MetaSectionBars": { - "description": "A section aggregating multiple MetricItem components.", - "key": "MetaSectionBars", - "title": "MetricsSection Component", - "type": "component", - "settings": { - "gridOptions": { - "x": 8, - "y": 1, - "w": 2, - "h": 2 - } - }, - "error": null, - "uses": [ - "H6", - "Flex", - "TooltipParent", - "TooltipHidden" - ] - }, - "Select": { - "key": "Select", - "type": "component", - "settings": { - "gridOptions": { - "x": 17, - "y": 0, - "w": 1, - "h": 1 - } - }, - "error": null, - "uses": [ - "Flex", - "Selects", - "Icon" - ] - }, - "ModalForm": { - "key": "ModalForm", - "type": "component", - "settings": { - "gridOptions": { - "x": 19, - "y": 4, - "w": 1, - "h": 1 - } - }, - "error": null, - "uses": [ - "Grid" - ] - }, - "Test": { - "settings": { - "gridOptions": { - "w": "3", - "h": "2", - "x": "12", - "y": "0" - } - } - }, - "Table": { - "key": "Table", - "type": "component", - "title": "Table", - "description": "", - "category": "", - "tags": [], - "state": "", - "props": { - "demoComponent": {} - }, - "settings": { - "gridOptions": { - "x": 7, - "y": 4, - "w": 3, - "h": 4 - } - }, - "interactivity": [], - "dataTypes": [], - "uses": [ - "Flex", - "NetworkRow", - "Link" - ], - "error": null, - "advanced": true, - "metadata": "copiedFromSymbols", - "code": "export default {/////n props: {/////n extends: 'Flex',/////n childExtends: [/////n 'NetworkRow',/////n 'Link',/////n ],/////n width: '100%',/////n children: (el, s) => s.fleet,/////n childrenAs: 'state',/////n flow: 'y',/////n position: 'relative',/////n zIndex: 2,/////n text: null/////n }/////n}" - }, - "SearchDropdown": { - "key": "SearchDropdown", - "type": "components", - "settings": { - "gridOptions": { - "w": 2, - "h": 2, - "x": 17, - "y": 9 - } - }, - "error": null, - "uses": [ - "Flex", - "CaptionTitle", - "Text", - "SearchItem" - ], - "title": "", - "description": "", - "category": "", - "tags": [], - "state": { - "searchTerm": "Header" - }, - "props": {}, - "interactivity": [], - "dataTypes": [], - "metadata": "copiedFromSymbols", - "code": "export default {/////n extend: 'Flex',/////n attr: {/////n dropdown: true,/////n },/////n props: {/////n flow: 'y',/////n gap: 'A',/////n padding: 'Z1 A',/////n fontSize: 'Z2',/////n theme: null,/////n class: 'search-results',/////n '@dark': {/////n background: '#1D1D1D .95',/////n backdropFilter: 'blur(8px)',/////n boxShadow: 'black .35, 0px, 15px, 150px, 280px',/////n },/////n '@light': {/////n background: 'gray1 .95',/////n backdropFilter: 'blur(8px)',/////n boxShadow: 'white .68, 0px, 15px, 150px, 280px',/////n },/////n },/////n CaptionTitle: {/////n Text: {/////n text: 'Search results',/////n },/////n },/////n Flex: {/////n gap: 'Z',/////n flow: 'y',/////n margin: '- -Z2',/////n children: (el, s) => {/////n const fuse = el.props.fuse/////n/////n function searchAll(query) {/////n const results = fuse.search(query);/////n/////n return results.map(r => ({/////n key: r.item.key,/////n title: r.item.title,/////n code: r.item.code,/////n type: r.item.type, // component / page / function/////n score: r.score,/////n }));/////n }/////n/////n const result = searchAll(s.searchTerm)/////n console.log(result, s.searchTerm)/////n return result/////n },/////n childrenAs: 'state',/////n childExtends: 'SearchItem',/////n onInit: async (el, s) => {/////n const fuse = await import('fuse.js')/////n const Fuse = fuse.default/////n/////n const fuseOptions = {/////n isCaseSensitive: false,/////n shouldSort: true,/////n findAllMatches: true,/////n includeScore: true,/////n threshold: 0.4, // Adjust for fuzzy matching sensitivity/////n keys: [{/////n name: \"title\",/////n weight: 2/////n }, // Higher weight for titles/////n {/////n name: \"networkName\",/////n weight: 1.5/////n },/////n {/////n name: \"moniker\",/////n weight: 1.5/////n },/////n {/////n name: \"owner\",/////n weight: 1/////n },/////n {/////n name: \"env\",/////n weight: 1/////n },/////n {/////n name: \"cloudProvider\",/////n weight: 0.5/////n },/////n {/////n name: \"publicKey\",/////n weight: 0.3/////n },/////n ]/////n }/////n/////n // Function to create searchable items from fleet data/////n function flattenFleetData(fleet) {/////n const searchItems = []/////n/////n fleet.forEach(network => {/////n // Add network itself as searchable/////n searchItems.push({/////n id: network.id,/////n key: /////tildenetwork-/////dlrsgn{network.id}/////tilde,/////n title: network.protocol,/////n subtitle: /////tilde/////dlrsgn{network.network_type} • /////dlrsgn{network.network_layer}/////tilde,/////n type: 'network',/////n networkName: network.protocol,/////n networkType: network.network_type,/////n networkLayer: network.network_layer,/////n participation: network.participation,/////n original: network/////n })/////n/////n // Add validators/////n network.validators?.forEach(validator => {/////n searchItems.push({/////n id: validator.uid,/////n key: /////tildevalidator-/////dlrsgn{validator.uid}/////tilde,/////n title: validator.moniker || 'Unknown',/////n subtitle: /////tildeValidator • /////dlrsgn{network.protocol} • /////dlrsgn{validator.env}/////tilde,/////n type: 'validator',/////n networkName: network.protocol,/////n moniker: validator.moniker,/////n owner: validator.owner,/////n env: validator.env,/////n cloudProvider: validator.cloud_provider,/////n publicKey: validator.public_key,/////n clientVersion: validator.client_version,/////n original: validator,/////n networkId: network.id/////n })/////n })/////n/////n // Add RPC nodes/////n network.rpc_nodes?.forEach(rpc => {/////n searchItems.push({/////n id: rpc.uid,/////n key: /////tilderpc-/////dlrsgn{rpc.uid}/////tilde,/////n title: rpc.moniker || 'RPC Node',/////n subtitle: /////tildeRPC • /////dlrsgn{network.protocol} • /////dlrsgn{rpc.env}/////tilde,/////n type: 'rpc',/////n networkName: network.protocol,/////n moniker: rpc.moniker,/////n env: rpc.env,/////n clientVersion: rpc.client_version,/////n original: rpc,/////n networkId: network.id/////n })/////n })/////n })/////n/////n return searchItems/////n }/////n/////n // Get fleet data from root state/////n const fleet = window.fleet || s.parent.fleet || s.root.fleet || []/////n const searchItems = flattenFleetData(fleet)/////n/////n console.log('Indexed items:', searchItems.length)/////n el.props.fuse = new Fuse(searchItems, fuseOptions)/////n },/////n },/////n}" - }, - "SearchItem": { - "key": "SearchItem", - "type": "components", - "settings": { - "gridOptions": { - "w": 3, - "h": 1, - "x": 15, - "y": 11 - } - }, - "error": null, - "uses": [ - "Button", - "Avatar", - "Hgroup", - "H", - "P" - ], - "title": "", - "description": "", - "category": "", - "tags": [], - "state": { - "type": "network", - "title": "Solana" - }, - "props": {}, - "interactivity": [], - "dataTypes": [], - "code": "export default {/////n extend: [/////n 'Flex',/////n ],/////n props: {/////n gap: 'Z1',/////n align: 'start',/////n padding: 'Z A X',/////n theme: 'transparent',/////n ':hover': {/////n theme: 'tertiary',/////n style: {/////n svg: {/////n opacity: 1,/////n },/////n },/////n },/////n },/////n Avatar: {/////n if: (el, s) => s.type === 'network',/////n margin: '-W2 - -',/////n src: '{{ title }}.png',/////n boxSize: 'B1',/////n },/////n Hgroup: {/////n gap: 'W',/////n H: {/////n tag: 'h6',/////n text: '{{ title }}',/////n margin: '- C - -',/////n },/////n P: {/////n textAlign: 'start',/////n text: '{{ type }}'/////n }/////n }/////n}", - "metadata": "copiedFromSymbols" - }, - "Content": { - "key": "Content", - "type": "components", - "settings": { - "gridOptions": { - "w": 4, - "h": 2, - "x": 15, - "y": 12 - } - }, - "code": "export default {/////n extend: 'Flex',/////n props: {/////n class: 'content',/////n flex: 1,/////n position: 'relative',/////n width: '100%',/////n round: 'A A2',/////n theme: 'dialog',/////n flow: 'y',/////n }/////n}", - "uses": [ - "Flex" - ] - }, - "Layout": { - "key": "Layout", - "type": "components", - "settings": { - "gridOptions": { - "x": 15, - "y": 6, - "w": 4, - "h": 2 - } - }, - "error": null, - "uses": [ - "Header", - "SearchDropdown", - "Dropdown", - "Content" - ], - "code": "export default {/////n props: {/////n position: 'relative',/////n },/////n Header: {},/////n SearchDropdown: {/////n extends: [/////n 'SearchDropdown',/////n 'Dropdown',/////n ],/////n width: '100%',/////n left: '0', /////n },/////n Content: {}/////n}" - } - }, - "integrations": {}, - "snippets": {}, - "pages": { - "/network": { - "key": "/network", - "type": "page", - "settings": { - "gridOptions": { - "x": 6, - "y": 0, - "w": 3, - "h": 2 - } - }, - "error": null, - "uses": [ - "Page", - "Layout", - "Header", - "Link", - "Content", - "Box", - "Flex", - "NavButton", - "DropdownParentFocus", - "Input", - "IconButton", - "Icon", - "Dropdown", - "DropdownList", - "Button", - "CopyURL", - "Update", - "Delete", - "Hr", - "ValidatorInfo", - "Modal" - ] - }, - "/node": { - "key": "/node", - "type": "page", - "settings": { - "gridOptions": { - "x": 9, - "y": 0, - "w": 3, - "h": 2 - } - }, - "error": null, - "uses": [ - "Page", - "Layout", - "Header", - "Link", - "Content", - "PageHead", - "Flex", - "Protocol", - "Avatar", - "Strong", - "NavButton", - "DropdownParentFocus", - "Input", - "IconButton", - "Icon", - "Dropdown", - "DropdownList", - "Button", - "CopyURL", - "Delete", - "Modal", - "Hr", - "Box", - "Overflow", - "ValidatorContent", - "Logs" - ], - "network": { - "protocol": "Eth2" - } - }, - "/add-network": { - "key": "/add-network", - "type": "page", - "settings": { - "gridOptions": { - "x": 0, - "y": 3, - "w": 2, - "h": 2 - } - }, - "error": null, - "uses": [ - "FormModal", - "Hgroup", - "H", - "P", - "Form", - "Button" - ] - }, - "/add-node": { - "key": "/add-node", - "type": "page", - "settings": { - "gridOptions": { - "x": 3, - "y": 3, - "w": 2, - "h": 2 - } - }, - "error": null, - "uses": [ - "FormModal", - "Hgroup", - "H", - "P", - "Form", - "Button" - ] - }, - "/edit-network": { - "key": "/edit-network", - "type": "page", - "settings": { - "gridOptions": { - "x": 0, - "y": 5, - "w": 2, - "h": 2 - } - }, - "error": null, - "uses": [ - "/add-network", - "Hgroup", - "H", - "P" - ], - "title": "Edit network", - "advanced": true, - "description": "", - "category": "", - "tags": [], - "state": "", - "props": {}, - "interactivity": [], - "dataTypes": [], - "touched": true - }, - "/edit-node": { - "key": "/edit-node", - "type": "page", - "settings": { - "gridOptions": { - "x": 3, - "y": 5, - "w": 2, - "h": 3 - } - }, - "error": null, - "uses": [ - "/add-node", - "Hgroup", - "H", - "P", - "Form", - "Hr", - "ModalForm" - ], - "title": "Edit network", - "advanced": true, - "description": "", - "category": "", - "tags": [], - "state": "", - "props": {}, - "interactivity": [], - "dataTypes": [], - "touched": true - }, - "/dashboard": { - "key": "/dashboard", - "type": "page", - "settings": { - "gridOptions": { - "x": 3, - "y": 0, - "w": 3, - "h": 2 - } - }, - "uses": [ - "Page", - "TestItem", - "Header", - "Flex", - "Box", - "Overflow", - "PageHead", - "Title", - "Strong", - "Span", - "NavButton", - "Tr", - "Grid", - "Hr", - "Table", - "Modal", - "MetaSectionBars" - ], - "error": null, - "hover": false - }, - "/": { - "title": "/", - "key": "/", - "type": "page", - "settings": { - "gridOptions": { - "x": 0, - "y": 0, - "w": 3, - "h": 2 - } - }, - "props": {}, - "interactivity": [], - "dataTypes": [] - } - }, - "state": { - "mockData": { - "method": "GET", - "type": "state", - "headers": {}, - "headersArr": [], - "params": {}, - "paramsArr": [], - "auth": {}, - "authArr": [], - "body": {}, - "runtime": false, - "title": "Mock data", - "key": "mockData", - "endpointUrl": "https://tim-sodium-pets-owen.trycloudflare.com/api/fleet" - }, - "mockedData": { - "method": "GET", - "type": "state", - "headers": {}, - "headersArr": [], - "params": {}, - "paramsArr": [], - "auth": {}, - "authArr": [], - "body": {}, - "runtime": false, - "title": "mockedData", - "key": "mockedData", - "endpointUrl": "http://localhost:3000/api/fleet" - }, - "fleetCopy": { - "key": "fleetCopy", - "type": "function", - "editing": false - } - }, - "files": { - "Autonity.png": { - "key": "Autonity.png", - "type": "file" - } - }, - "dependencies": { - "chart.js": { - "key": "chart.js", - "resolvedVersion": "4.4.9", - "type": "dependency", - "version": "4.4.9", - "status": "done" - }, - "fuse.js": { - "key": "fuse.js", - "resolvedVersion": "7.1.0", - "type": "dependencies", - "version": "latest" - } - }, - "methods": {}, - "functions": { - "stringToHexColor": { - "key": "stringToHexColor", - "type": "function" - }, - "filterByEnv": { - "key": "filterByEnv", - "type": "function" - }, - "calculateWeek": { - "key": "calculateWeek", - "type": "function" - }, - "calculateCosts": { - "key": "calculateCosts", - "type": "function" - }, - "getCostsPerProtocol": { - "key": "getCostsPerProtocol", - "type": "function" - }, - "giveMeAnswer": { - "key": "giveMeAnswer", - "type": "function" - }, - "getMe": { - "key": "getMe", - "type": "function" - }, - "getTagsOfNodes": { - "key": "getTagsOfNodes", - "type": "function" - }, - "edit": { - "key": "edit", - "code": "export default async function edit(item = 'network', protocol, opts = {}) {/////n const formData = new FormData(this.node)/////n let data = Object.fromEntries(formData)/////n/////n // For nodes, send flat object directly (not nested)/////n if (item === 'node') {/////n // Remove nodeType from data since it's in the URL/////n delete data.nodeType/////n data.projected_cost = parseInt(data.projected_cost)/////n // Send the form data directly as flat object/////n // data is already correct: { moniker: \"...\", env: \"...\", etc. }/////n }/////n/////n const ROUTE = {/////n network: /////tilde//////dlrsgn{protocol}/////tilde,/////n node: /////tilde/node//////dlrsgn{this.state.nodeType}//////dlrsgn{this.state.uid}/////tilde/////n }/////n/////n console.log('Route:', ROUTE[item])/////n console.log('Data being sent:', data)/////n/////n const res = await this.call('fetch', 'PUT', ROUTE[item], data, opts)/////n console.log('Response:', res)/////n/////n this.state.root.quietUpdate({/////n modal: null/////n })/////n/////n const redirectUrl = {/////n network: '/network/' + this.state.protocol,/////n node: '/node/' + this.state.protocol + '/' + this.state.nodeType + '/' + this.state.uid/////n }/////n/////n this.call('router', redirectUrl[item] || '/', this.__ref.root)/////n this.node.reset()/////n}", - "type": "function", - "error": "SyntaxError: Unexpected end of input", - "err": {} - }, - "remove": { - "key": "remove", - "code": "export default async function remove(item = 'network', protocol, opts = {}) {/////n let [, _, urlProtocol, nodeType, uid] = window.location.pathname.split('/')/////n/////n const ROUTE = {/////n network: '/' + protocol,/////n node: '/node/' + nodeType + '/' + uid/////n }/////n/////n console.log('/node/' + nodeType + '/' + uid)/////n/////n const res = await this.call('fetch', 'DELETE', ROUTE[item])/////n if (!res) return/////n/////n this.state.root.quietUpdate({/////n modal: null/////n })/////n/////n const REDIRECT = {/////n network: '/dashboard',/////n node: '/network/' + protocol/////n }/////n this.call('router', REDIRECT[item], this.__ref.root)/////n}", - "type": "function", - "error": "SyntaxError: Unexpected end of input", - "err": {} - }, - "fetch": { - "key": "fetch", - "code": "export default async function fetch(method = 'GET', path = '', data, opts = {}) {/////n // const ENDPOINT = 'https://small-sound-18b4.nika-980.workers.dev/api/fleet'/////n // const ENDPOINT = 'https://bigbrother.symbo.ls' + (opts.route || '/api/fleet') + path/////n/////n const options = {/////n method: method || 'POST',/////n headers: {/////n 'Content-Type': 'application/json'/////n },/////n ...opts/////n }/////n/////n const isCanvas = location.href === 'srcdoc'/////n const isSymbols = location.host.includes('symbo.ls')/////n const isProd = location.host.includes('nodeops.ninja') && !location.host.includes('dev')/////n // const isProd = false // location.host.includes('nodeops.ninja') && !location.host.includes('dev')/////n/////n const URL = /////tildehttps:///////dlrsgn{isProd ? '' : 'dev.'}api.nodeops.ninja/////tilde/////n const ENDPOINT = URL + (opts.route || '/api/fleet') + path/////n/////n if (isCanvas || isSymbols || !isProd) {/////n const API_TOKEN = 'bb_ff64921b7b6dae704a352681c26ae5ed35c8143e18e13f2682cc2be1ab4ebb74'/////n options.headers.Authorization = 'Bearer ' + API_TOKEN/////n } else {/////n // const SESSION_ID = 's%3AwAm91jUNz7Bv3ihqvY9o4AJ_xQDTA6x3.VcEZyPFSdClTokzsu9n3gXULU1qp7pxSNCSEQBUhoIQ'/////n // options.headers.credentials = true/////n // options.headers.Authorization = 'Bearer ' + SESSION_ID/////n options.credentials = 'include'/////n options.mode = 'cors'/////n }/////n/////n if (data && (options.method === 'POST' || options.method === 'PUT')) {/////n options.body = JSON.stringify(data)/////n }/////n/////n console.log('Fetch request:', ENDPOINT, options)/////n const res = await window.fetch(ENDPOINT, options)/////n/////n if (!res.ok) {/////n const errorText = await res.text()/////n console.error('Failed to submit:', res.status, errorText)/////n throw new Error(/////tildeHTTP /////dlrsgn{res.status}: /////dlrsgn{errorText}/////tilde)/////n }/////n/////n // Check if response has content before parsing JSON/////n const contentType = res.headers.get('content-type')/////n if (contentType && contentType.includes('application/json')) {/////n return res.json()/////n }/////n/////n return res.text()/////n}", - "type": "function", - "err": {}, - "error": "SyntaxError: Unexpected end of input" - }, - "add": { - "key": "add", - "code": "export default async function addNew(item = 'network') {/////n const ROUTE = {/////n network: '',/////n node: '/' + this.state.protocol + '/node'/////n }/////n/////n const formData = new FormData(this.node)/////n let data = Object.fromEntries(formData)/////n if (item === 'node') {/////n data.projected_cost = parseInt(data.projected_cost)/////n console.log(data.projected_cost)/////n data = {/////n nodeType: data.nodeType,/////n nodeData: data/////n }/////n }/////n console.log(data)/////n/////n const res = await this.call('fetch', 'POST', ROUTE[item], data)/////n if (!res) return/////n/////n this.state.root.quietUpdate({/////n modal: null/////n })/////n/////n // console.log('here')/////n // console.log(this.state)/////n // console.log(res)/////n const ROUTES = {/////n network: '/network/' + data.protocol,/////n node: '/node/' + this.state.protocol + '/' + data.nodeType + '/' + res.uid/////n }/////n this.call('router', ROUTES[item] || '/', this.__ref.root)/////n this.node.reset()/////n}", - "type": "function", - "err": {}, - "error": "SyntaxError: Unexpected end of input" - }, - "read": { - "key": "read", - "code": "export default async function read(path) {/////n return await this.call('fetch', 'GET', path)/////n}", - "type": "function", - "error": null, - "err": {} - }, - "parseNetworkRow": { - "key": "parseNetworkRow", - "code": "export default function parseNetworkRow(data) {/////n const result = {/////n cloud_provider: new Set(),/////n env: new Set(),/////n node_types: new Set(),/////n status: new Set()/////n };/////n/////n // Parse validators/////n if (Array.isArray(data.validators) && data.validators.length > 0) {/////n result.node_types.add('Validator');/////n data.validators.forEach(validator => {/////n if (validator.cloud_provider) result.cloud_provider.add(validator.cloud_provider);/////n if (validator.env) result.env.add(validator.env);/////n if (validator.status) result.status.add(validator.status);/////n });/////n }/////n/////n // Parse rpc_nodes/////n if (Array.isArray(data.rpc_nodes) && data.rpc_nodes.length > 0) {/////n result.node_types.add('RPC');/////n data.rpc_nodes.forEach(node => {/////n if (node.cloud_provider) result.cloud_provider.add(node.cloud_provider);/////n if (node.env) result.env.add(node.env);/////n if (node.status) result.status.add(node.status);/////n });/////n }/////n/////n // Convert Sets to Arrays/////n return {/////n cloud_provider: Array.from(result.cloud_provider),/////n env: Array.from(result.env),/////n node_types: Array.from(result.node_types),/////n status: Array.from(result.status)/////n };/////n}", - "type": "function", - "error": "SyntaxError: Unexpected end of input" - }, - "getFleet": { - "key": "getFleet", - "code": "export default function getFleet () {\n // console.log(this, this.state, this.context)\n }", - "type": "function" - }, - "filterFleet": { - "key": "filterFleet", - "code": "export default function filterFleet() {/////n // Search filter/////n if (root.search) {/////n fleet = fleet.filter(item =>/////n item.protocol.toLowerCase().includes(root.search.toLowerCase()) ||/////n item.repo_url.includes(root.search)/////n )/////n }/////n/////n // Validators filter/////n if (root.validators) {/////n if (root.validators === 'Mainnet') {/////n fleet = fleet.filter(item =>/////n item.validator_info?.some(validator => validator.env === 'Mainnet')/////n )/////n } else if (root.validators === 'Testnet') {/////n fleet = fleet.filter(item =>/////n item.validator_info?.some(validator => validator.env === 'Testnet')/////n )/////n } else if (root.validators === 'None') {/////n fleet = fleet.filter(item =>/////n !item.validator_info?.length/////n )/////n }/////n // 'All' option doesn't need additional filtering/////n }/////n/////n // RPC filter/////n if (root.rpc) {/////n if (root.rpc === 'Mainnet') {/////n fleet = fleet.filter(item =>/////n item.rpc_info?.some(rpc => rpc.env === 'Mainnet')/////n )/////n } else if (root.rpc === 'Testnet') {/////n fleet = fleet.filter(item =>/////n item.rpc_info?.some(rpc => rpc.env === 'Testnet')/////n )/////n } else if (root.rpc === 'None') {/////n fleet = fleet.filter(item =>/////n !item.rpc_info?.length/////n )/////n }/////n // 'All' option doesn't need additional filtering/////n }/////n/////n // Status filter/////n if (root.status) {/////n fleet = fleet.filter(item => item.status === root.status)/////n }/////n/////n // Last Updated filter/////n if (root.lastUpdate && root.lastUpdate !== 'All Time') {/////n const now = new Date()/////n let targetDate = new Date()/////n/////n if (root.lastUpdate === 'Today') {/////n targetDate.setHours(0, 0, 0, 0) // Start of today/////n } else if (root.lastUpdate === 'Last 2 Days') {/////n targetDate.setDate(now.getDate() - 2)/////n targetDate.setHours(0, 0, 0, 0)/////n } else if (root.lastUpdate === 'Last 3 Days') {/////n targetDate.setDate(now.getDate() - 3)/////n targetDate.setHours(0, 0, 0, 0)/////n } else if (root.lastUpdate === 'Last Week') {/////n targetDate.setDate(now.getDate() - 7)/////n targetDate.setHours(0, 0, 0, 0)/////n } else if (root.lastUpdate === 'Last Month') {/////n targetDate.setMonth(now.getMonth() - 1)/////n targetDate.setHours(0, 0, 0, 0)/////n }/////n/////n fleet = fleet.filter(item => {/////n // Check logs in validator_info/////n const validatorLogs = item.validator_info?.flatMap(validator =>/////n validator.logs?.map(log => new Date(log.created_at)) || []/////n ) || []/////n/////n // Check logs in rpc_info/////n const rpcLogs = item.rpc_info?.flatMap(rpc =>/////n rpc.logs?.map(log => new Date(log.created_at)) || []/////n ) || []/////n/////n // Combine all logs/////n const allLogs = [...validatorLogs, ...rpcLogs]/////n/////n // Check if any log is newer than the target date/////n return allLogs.some(logDate => logDate >= targetDate)/////n })/////n }/////n}", - "type": "function", - "error": null - }, - "setInitialData": { - "key": "setInitialData", - "code": "export default function setInitialData(data = {}) {/////n this.state.replace(data, {/////n preventUpdate: true,/////n preventUpdateListener: true/////n })/////n/////n this.update({}, {/////n preventUpdateListener: true/////n })/////n}", - "type": "function", - "error": "SyntaxError: Unexpected end of input" - }, - "getStatusColor": { - "key": "getStatusColor", - "code": "export default function getStatusColor(status) {/////n const MAP = {/////n 'Live': 'green',/////n 'Stable/Maintenance': 'green',/////n 'Onboarding': 'yellow',/////n 'Off': 'gray'/////n }/////n/////n return MAP[status]/////n}", - "type": "function", - "error": "SyntaxError: Unexpected end of input", - "err": {} - }, - "auth": { - "key": "auth", - "code": "export default async function auth() {/////n if (this.state.root.success) {/////n if (window.location.pathname === '/') {/////n this.call('router', '/dashboard', this.__ref.root)/////n }/////n } else {/////n if (window.location.pathname === '/') {/////n const res = await this.call('fetch', 'GET', '', null, {/////n route: '/auth/me',/////n })/////n/////n if (res.success) {/////n this.state.root.update(res)/////n this.call('router', '/dashboard', this.__ref.root)/////n }/////n return res/////n } else {/////n this.call('router', '/', this.__ref.root)/////n }/////n }/////n}", - "type": "function", - "err": {}, - "error": "SecurityError: Failed to read a named property 'eval' from 'Window': Blocked a frame with origin \"https://symbols.app\" from accessing a cross-origin frame." - } - }, - "secrets": {}, - "designSystem": { - "COLOR": { - "black": "#000", - "white": "#fff" - }, - "GRADIENT": {}, - "THEME": { - "document": { - "@light": { - "color": "black", - "background": "white" - }, - "@dark": { - "color": "white", - "background": "black" - } - }, - "none": { - "color": "none", - "background": "none" - }, - "transparent": { - "color": "currentColor", - "background": "transparent" - } - }, - "FONT": {}, - "FONT_FAMILY": {}, - "TYPOGRAPHY": { - "base": 16, - "ratio": 1.25, - "subSequence": true, - "templates": {} - }, - "SPACING": { - "base": 16, - "ratio": 1.618, - "subSequence": true - }, - "TIMING": {}, - "CLASS": {}, - "GRID": {}, - "ICONS": {}, - "SHAPE": {}, - "RESET": {}, - "ANIMATION": {}, - "MEDIA": {}, - "CASES": {}, - "useReset": true, - "useVariable": true, - "useFontImport": true, - "useIconSprite": true, - "useSvgSprite": true, - "useDefaultConfig": true, - "useDocumentTheme": true, - "verbose": false - } - }, - "secrets": {}, - "__pending": { - "count": 0, - "uncommitted": false, - "etag": "1.7.378:0" - }, - "sharedLibraries": [], - "icon": null, - "branch": "main", - "latestVersion": "1.7.378", - "isLatest": true, - "projectInfo": { - "id": "6874baae0769df64f1a4484d", - "key": "big-brother.symbo.ls", - "name": "Bigbrother", - "updatedAt": "2025-12-21T20:11:56.172Z" - }, - "projectMeta": { - "id": "6874baae0769df64f1a4484d", - "key": "big-brother.symbo.ls", - "name": "Bigbrother", - "tier": "free", - "visibility": "public", - "access": "account", - "framework": "platform", - "language": "javascript", - "status": "active", - "isSharedLibrary": false, - "icon": null, - "createdAt": "2025-07-14T08:07:10.346Z", - "updatedAt": "2025-12-21T20:11:56.172Z" - }, - "owner": { - "id": "6868484c0cf470c5890933d5", - "username": "toko" - } -} \ No newline at end of file diff --git a/smbls/components/ActionButton.js b/smbls/components/ActionButton.js new file mode 100644 index 0000000..d067183 --- /dev/null +++ b/smbls/components/ActionButton.js @@ -0,0 +1,24 @@ +export const ActionButton = { + extends: 'Flex', + flexAlign: 'center center', + round: '100px', + cursor: 'pointer', + transition: 'transform 0.2s ease, box-shadow 0.2s ease', + style: { + userSelect: 'none' + }, + ':hover': { + style: { + transform: 'scale(1.12)' + } + }, + ':active': { + style: { + transform: 'scale(0.95)' + } + }, + + Icon: { + name: (el, s) => s.icon || 'heart' + } +} diff --git a/smbls/components/ActionButtons.js b/smbls/components/ActionButtons.js new file mode 100644 index 0000000..dd55963 --- /dev/null +++ b/smbls/components/ActionButtons.js @@ -0,0 +1,65 @@ +export const ActionButtons = { + extends: 'Flex', + flow: 'x', + flexAlign: 'center center', + gap: 'A2', + padding: 'A2 0', + + RewindBtn: { + extends: 'ActionButton', + boxSize: 'C2', + background: 'gray3 0.85', + ':hover': { background: 'gray3' }, + Icon: { name: 'rewind', boxSize: 'A', color: 'tinderGold' }, + onClick: (ev, el, s) => { + s.update({ action: 'rewind' }) + } + }, + + NopeBtn: { + extends: 'ActionButton', + boxSize: 'D', + background: 'gray3 0.85', + border: 'tinderRed 2px solid', + ':hover': { background: 'tinderRed 0.15' }, + Icon: { name: 'tinderClose', boxSize: 'A2', color: 'tinderRed' }, + onClick: (ev, el, s) => { + s.update({ action: 'nope' }) + } + }, + + SuperLikeBtn: { + extends: 'ActionButton', + boxSize: 'C2', + background: 'gray3 0.85', + border: 'tinderBlue 2px solid', + ':hover': { background: 'tinderBlue 0.15' }, + Icon: { name: 'star', boxSize: 'A', color: 'tinderBlue' }, + onClick: (ev, el, s) => { + s.update({ action: 'superlike' }) + } + }, + + LikeBtn: { + extends: 'ActionButton', + boxSize: 'D', + background: 'gray3 0.85', + border: 'tinderGreen 2px solid', + ':hover': { background: 'tinderGreen 0.15' }, + Icon: { name: 'heart', boxSize: 'A2', color: 'tinderGreen' }, + onClick: (ev, el, s) => { + s.update({ action: 'like' }) + } + }, + + BoostBtn: { + extends: 'ActionButton', + boxSize: 'C2', + background: 'gray3 0.85', + ':hover': { background: 'tinderPurple 0.15' }, + Icon: { name: 'bolt', boxSize: 'A', color: 'tinderPurple' }, + onClick: (ev, el, s) => { + s.update({ action: 'boost' }) + } + } +} diff --git a/smbls/components/ProfileCard.js b/smbls/components/ProfileCard.js new file mode 100644 index 0000000..ee3e499 --- /dev/null +++ b/smbls/components/ProfileCard.js @@ -0,0 +1,176 @@ +export const ProfileCard = { + extends: 'Flex', + flow: 'y', + width: '100%', + height: '100%', + position: 'relative', + overflow: 'hidden', + round: 'B', + + Photo: { + width: '100%', + height: '100%', + position: 'absolute', + top: '0', + left: '0', + tag: 'img', + style: { objectFit: 'cover' }, + src: (el, s) => s.image || '', + draggable: 'false' + }, + + Gradient: { + position: 'absolute', + bottom: '0', + left: '0', + width: '100%', + height: '55%', + style: { + background: 'linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.4) 50%, transparent 100%)', + pointerEvents: 'none' + } + }, + + Info: { + extends: 'Flex', + flow: 'y', + position: 'absolute', + bottom: '0', + left: '0', + width: '100%', + padding: 'A2', + gap: 'Z', + color: 'white', + + NameRow: { + extends: 'Flex', + flow: 'x', + align: 'center flex-start', + gap: 'Z', + Name: { + tag: 'h2', + fontSize: 'C', + fontWeight: '700', + text: (el, s) => s.name || '' + }, + Age: { + tag: 'span', + fontSize: 'B2', + fontWeight: '300', + text: (el, s) => s.age || '' + } + }, + + JobRow: { + extends: 'Flex', + flow: 'x', + align: 'center flex-start', + gap: 'Z', + if: (el, s) => s.job, + Icon: { name: 'briefcase', boxSize: 'Z2', color: 'white 0.85' }, + Text: { + tag: 'span', + fontSize: 'A', + color: 'white 0.9', + text: (el, s) => s.job || '' + } + }, + + SchoolRow: { + extends: 'Flex', + flow: 'x', + align: 'center flex-start', + gap: 'Z', + if: (el, s) => s.school, + Icon: { name: 'graduationCap', boxSize: 'Z2', color: 'white 0.85' }, + Text: { + tag: 'span', + fontSize: 'A', + color: 'white 0.9', + text: (el, s) => s.school || '' + } + }, + + LocationRow: { + extends: 'Flex', + flow: 'x', + align: 'center flex-start', + gap: 'Z', + if: (el, s) => s.distance, + Icon: { name: 'mapPin', boxSize: 'Z2', color: 'white 0.85' }, + Text: { + tag: 'span', + fontSize: 'Z2', + color: 'white 0.75', + text: (el, s) => s.distance || '' + } + } + }, + + LikeStamp: { + extends: 'Flex', + flexAlign: 'center center', + position: 'absolute', + top: 'B', + left: 'A2', + padding: 'Z A', + border: 'tinderGreen 4px solid', + round: 'Z2', + color: 'tinderGreen', + fontSize: 'C', + fontWeight: '800', + letterSpacing: '2px', + text: 'LIKE', + opacity: '0', + style: { + transform: 'rotate(-25deg)', + pointerEvents: 'none', + textTransform: 'uppercase' + } + }, + + NopeStamp: { + extends: 'Flex', + flexAlign: 'center center', + position: 'absolute', + top: 'B', + right: 'A2', + padding: 'Z A', + border: 'tinderRed 4px solid', + round: 'Z2', + color: 'tinderRed', + fontSize: 'C', + fontWeight: '800', + letterSpacing: '2px', + text: 'NOPE', + opacity: '0', + style: { + transform: 'rotate(25deg)', + pointerEvents: 'none', + textTransform: 'uppercase' + } + }, + + SuperLikeStamp: { + extends: 'Flex', + flexAlign: 'center center', + position: 'absolute', + bottom: 'E', + left: '50%', + padding: 'Z A', + border: 'tinderBlue 4px solid', + round: 'Z2', + color: 'tinderBlue', + fontSize: 'C', + fontWeight: '800', + letterSpacing: '2px', + text: 'SUPER LIKE', + opacity: '0', + style: { + transform: 'translateX(-50%)', + pointerEvents: 'none', + textTransform: 'uppercase', + whiteSpace: 'nowrap' + } + } +} diff --git a/smbls/components/SwipeCard.js b/smbls/components/SwipeCard.js new file mode 100644 index 0000000..2b35943 --- /dev/null +++ b/smbls/components/SwipeCard.js @@ -0,0 +1,148 @@ +export const SwipeCard = { + extends: 'Flex', + position: 'absolute', + width: '100%', + height: '100%', + cursor: 'grab', + style: { + userSelect: 'none', + touchAction: 'none', + willChange: 'transform' + }, + + scope: { + startX: 0, + startY: 0, + currentX: 0, + currentY: 0, + isDragging: false, + + onPointerDown: (el, s, e) => { + el.scope.isDragging = true + el.scope.startX = e.clientX + el.scope.startY = e.clientY + el.scope.currentX = 0 + el.scope.currentY = 0 + el.node.style.cursor = 'grabbing' + el.node.style.transition = 'none' + }, + + onPointerMove: (el, s, e) => { + if (!el.scope.isDragging) return + el.scope.currentX = e.clientX - el.scope.startX + el.scope.currentY = e.clientY - el.scope.startY + var rotation = el.scope.currentX * 0.1 + if (rotation > 15) rotation = 15 + if (rotation < -15) rotation = -15 + el.node.style.transform = 'translate(' + el.scope.currentX + 'px, ' + el.scope.currentY + 'px) rotate(' + rotation + 'deg)' + + var card = el.node.querySelector('[data-profile-card]') + if (!card) return + var likeStamp = card.querySelectorAll('[data-stamp]')[0] + var nopeStamp = card.querySelectorAll('[data-stamp]')[1] + var superLikeStamp = card.querySelectorAll('[data-stamp]')[2] + + var absX = Math.abs(el.scope.currentX) + var absY = Math.abs(el.scope.currentY) + + if (el.scope.currentX > 0 && absX > absY) { + var likeOpacity = Math.min(absX / 100, 1) + if (likeStamp) likeStamp.style.opacity = likeOpacity + if (nopeStamp) nopeStamp.style.opacity = 0 + if (superLikeStamp) superLikeStamp.style.opacity = 0 + } else if (el.scope.currentX < 0 && absX > absY) { + var nopeOpacity = Math.min(absX / 100, 1) + if (nopeStamp) nopeStamp.style.opacity = nopeOpacity + if (likeStamp) likeStamp.style.opacity = 0 + if (superLikeStamp) superLikeStamp.style.opacity = 0 + } else if (el.scope.currentY < -50) { + var superOpacity = Math.min(absY / 100, 1) + if (superLikeStamp) superLikeStamp.style.opacity = superOpacity + if (likeStamp) likeStamp.style.opacity = 0 + if (nopeStamp) nopeStamp.style.opacity = 0 + } else { + if (likeStamp) likeStamp.style.opacity = 0 + if (nopeStamp) nopeStamp.style.opacity = 0 + if (superLikeStamp) superLikeStamp.style.opacity = 0 + } + }, + + onPointerUp: (el, s) => { + if (!el.scope.isDragging) return + el.scope.isDragging = false + el.node.style.cursor = 'grab' + + var threshold = 120 + var dx = el.scope.currentX + var dy = el.scope.currentY + + if (dx > threshold) { + el.scope.animateOut(el, s, 'right') + } else if (dx < -threshold) { + el.scope.animateOut(el, s, 'left') + } else if (dy < -threshold) { + el.scope.animateOut(el, s, 'up') + } else { + el.scope.snapBack(el) + } + }, + + snapBack: (el) => { + el.node.style.transition = 'transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)' + el.node.style.transform = 'translate(0, 0) rotate(0deg)' + var card = el.node.querySelector('[data-profile-card]') + if (card) { + var stamps = card.querySelectorAll('[data-stamp]') + for (var i = 0; i < stamps.length; i++) { + stamps[i].style.transition = 'opacity 0.3s' + stamps[i].style.opacity = 0 + } + } + }, + + animateOut: (el, s, direction) => { + var tx = 0 + var ty = 0 + var rot = 0 + if (direction === 'right') { + tx = window.innerWidth + 200 + rot = 30 + } else if (direction === 'left') { + tx = -(window.innerWidth + 200) + rot = -30 + } else if (direction === 'up') { + ty = -(window.innerHeight + 200) + } + el.node.style.transition = 'transform 0.5s ease-out, opacity 0.5s ease-out' + el.node.style.transform = 'translate(' + tx + 'px, ' + ty + 'px) rotate(' + rot + 'deg)' + el.node.style.opacity = '0' + + setTimeout(function () { + s.update({ swiped: direction }) + }, 500) + } + }, + + onRender: (el, s) => { + var pd = function (e) { el.scope.onPointerDown(el, s, e) } + var pm = function (e) { el.scope.onPointerMove(el, s, e) } + var pu = function () { el.scope.onPointerUp(el, s) } + + el.node.addEventListener('pointerdown', pd) + window.addEventListener('pointermove', pm) + window.addEventListener('pointerup', pu) + + return function () { + el.node.removeEventListener('pointerdown', pd) + window.removeEventListener('pointermove', pm) + window.removeEventListener('pointerup', pu) + } + }, + + ProfileCard: { + attr: { 'data-profile-card': true }, + LikeStamp: { attr: { 'data-stamp': 'like' } }, + NopeStamp: { attr: { 'data-stamp': 'nope' } }, + SuperLikeStamp: { attr: { 'data-stamp': 'superlike' } } + } +} diff --git a/smbls/components/TinderHeader.js b/smbls/components/TinderHeader.js new file mode 100644 index 0000000..11c936e --- /dev/null +++ b/smbls/components/TinderHeader.js @@ -0,0 +1,49 @@ +export const TinderHeader = { + extends: 'Flex', + flow: 'x', + flexAlign: 'center space-between', + padding: 'Z2 A2', + width: '100%', + maxWidth: 'G', + margin: '0 auto', + + ProfileBtn: { + extends: 'Flex', + flexAlign: 'center center', + boxSize: 'B2', + round: '100px', + cursor: 'pointer', + color: 'caption', + ':hover': { color: 'title' }, + Icon: { name: 'accessibility', boxSize: 'A2' } + }, + + Logo: { + extends: 'Flex', + flexAlign: 'center center', + gap: 'Z', + Icon: { name: 'heart', boxSize: 'A2', color: 'tinderPink' }, + Title: { + tag: 'h1', + fontSize: 'B', + fontWeight: '700', + style: { + background: 'linear-gradient(135deg, #FF4B6E, #FF8E6E)', + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent' + }, + text: 'tinder' + } + }, + + ChatBtn: { + extends: 'Flex', + flexAlign: 'center center', + boxSize: 'B2', + round: '100px', + cursor: 'pointer', + color: 'caption', + ':hover': { color: 'title' }, + Icon: { name: 'menu', boxSize: 'A2' } + } +} diff --git a/smbls/components/TinderStack.js b/smbls/components/TinderStack.js new file mode 100644 index 0000000..b1f6aa1 --- /dev/null +++ b/smbls/components/TinderStack.js @@ -0,0 +1,140 @@ +export const TinderStack = { + extends: 'Flex', + flow: 'y', + flexAlign: 'center center', + width: '100%', + maxWidth: 'G', + margin: 'auto', + height: '100%', + + state: { + currentIndex: 0, + action: null, + swiped: null, + liked: [], + noped: [], + superliked: [] + }, + + scope: { + getProfiles: (el, s, ctx) => { + return ctx.snippets.profilesData || [] + }, + + handleAction: (el, s, action) => { + var profiles = el.scope.getProfiles(el, s, el.context) + if (s.currentIndex >= profiles.length) return + + var profile = profiles[s.currentIndex] + + if (action === 'like') { + s.apply(function (st) { st.liked.push(profile) }) + } else if (action === 'nope') { + s.apply(function (st) { st.noped.push(profile) }) + } else if (action === 'superlike') { + s.apply(function (st) { st.superliked.push(profile) }) + } else if (action === 'rewind' && s.currentIndex > 0) { + s.update({ currentIndex: s.currentIndex - 1, action: null, swiped: null }) + return + } + + s.update({ currentIndex: s.currentIndex + 1, action: null, swiped: null }) + } + }, + + onStateUpdate: (changes, el, s) => { + if (changes.action && changes.action !== 'boost') { + el.scope.handleAction(el, s, changes.action) + } + if (changes.swiped) { + var direction = changes.swiped + var action = direction === 'right' ? 'like' : direction === 'left' ? 'nope' : 'superlike' + el.scope.handleAction(el, s, action) + } + }, + + CardArea: { + extends: 'Flex', + position: 'relative', + width: '100%', + flex: '1', + minHeight: 'G', + maxHeight: '500px', + flexAlign: 'center center', + + content: (el, s, ctx) => { + var profiles = ctx.snippets.profilesData || [] + var idx = s.currentIndex + + if (idx >= profiles.length) { + return { + EmptyState: { + extends: 'Flex', + flow: 'y', + flexAlign: 'center center', + gap: 'A', + padding: 'C', + animation: 'fadeIn 0.5s ease', + Icon: { name: 'heartOutline', boxSize: 'D', color: 'tinderPink 0.5' }, + H3: { + fontSize: 'B', + fontWeight: '600', + color: 'title', + text: 'No more profiles' + }, + P: { + fontSize: 'A', + color: 'caption', + text: 'Check back later for more people' + } + } + } + } + + var cards = {} + + var nextIdx = idx + 1 + if (nextIdx < profiles.length) { + cards.BackCard = { + extends: 'Flex', + position: 'absolute', + width: '95%', + height: '95%', + round: 'B', + overflow: 'hidden', + style: { + transform: 'scale(0.95) translateY(10px)', + pointerEvents: 'none' + }, + Photo: { + width: '100%', + height: '100%', + tag: 'img', + style: { objectFit: 'cover', filter: 'brightness(0.7)' }, + src: profiles[nextIdx].image, + draggable: 'false' + } + } + } + + cards.SwipeCard = { + state: { + name: profiles[idx].name, + age: profiles[idx].age, + job: profiles[idx].job, + school: profiles[idx].school, + distance: profiles[idx].distance, + bio: profiles[idx].bio, + image: profiles[idx].image, + interests: profiles[idx].interests, + swiped: null, + action: null + } + } + + return cards + } + }, + + ActionButtons: {} +} diff --git a/smbls/components/index.js b/smbls/components/index.js index 5f1cc05..3d787e9 100644 --- a/smbls/components/index.js +++ b/smbls/components/index.js @@ -59,3 +59,9 @@ export * from './NetListeningChart.js'; export * from './PeerCountChart.js'; export * from './SyncingChart.js'; export * from './UpChart.js'; +export * from './ProfileCard.js'; +export * from './SwipeCard.js'; +export * from './ActionButton.js'; +export * from './ActionButtons.js'; +export * from './TinderStack.js'; +export * from './TinderHeader.js'; diff --git a/smbls/designSystem/animation.js b/smbls/designSystem/animation.js index ddb5591..5fac97d 100644 --- a/smbls/designSystem/animation.js +++ b/smbls/designSystem/animation.js @@ -252,4 +252,58 @@ export default { opacity: 1, }, }, + swipeRight: { + from: { + transform: "translateX(0) rotate(0deg)", + opacity: 1, + }, + to: { + transform: "translateX(150%) rotate(30deg)", + opacity: 0, + }, + }, + swipeLeft: { + from: { + transform: "translateX(0) rotate(0deg)", + opacity: 1, + }, + to: { + transform: "translateX(-150%) rotate(-30deg)", + opacity: 0, + }, + }, + swipeUp: { + from: { + transform: "translateY(0) scale(1)", + opacity: 1, + }, + to: { + transform: "translateY(-150%) scale(0.8)", + opacity: 0, + }, + }, + cardStackIn: { + from: { + transform: "scale(0.9) translateY(30px)", + opacity: 0, + }, + to: { + transform: "scale(1) translateY(0)", + opacity: 1, + }, + }, + pulseGlow: { + "0%": { + transform: "scale(1)", + boxShadow: "0 0 0 0 rgba(255, 75, 110, 0.4)", + }, + "70%": { + transform: "scale(1.05)", + boxShadow: "0 0 0 15px rgba(255, 75, 110, 0)", + }, + "100%": { + transform: "scale(1)", + boxShadow: "0 0 0 0 rgba(255, 75, 110, 0)", + }, + }, }; diff --git a/smbls/designSystem/color.js b/smbls/designSystem/color.js index 824cedb..567fa43 100644 --- a/smbls/designSystem/color.js +++ b/smbls/designSystem/color.js @@ -69,4 +69,10 @@ export default { "line-highlight": ["--gray13 1 +4", "--gray2 1 +16"], blue2: "#4676EC", lightBlue: "#6899D1", + tinderPink: "#FF4B6E", + tinderGold: "#FFD700", + tinderBlue: "#3B82F6", + tinderGreen: "#10B981", + tinderRed: "#EF4444", + tinderPurple: "#8B5CF6", }; diff --git a/smbls/designSystem/icons.js b/smbls/designSystem/icons.js index 796130d..08032ff 100644 --- a/smbls/designSystem/icons.js +++ b/smbls/designSystem/icons.js @@ -1039,4 +1039,24 @@ export default { '', sidebarLeftFill: '', + heart: + '', + heartOutline: + '', + tinderClose: + '', + star: + '', + rewind: + '', + bolt: + '', + mapPin: + '', + briefcase: + '', + graduationCap: + '', + info: + '', } diff --git a/smbls/pages/index.js b/smbls/pages/index.js index ba21313..48925df 100644 --- a/smbls/pages/index.js +++ b/smbls/pages/index.js @@ -8,6 +8,7 @@ import { editNetwork } from './edit-network'; import { editNode } from './edit-node'; import { dashboard } from './dashboard'; import { addNetworkCopy } from './add-network-copy'; +import { tinder } from './tinder'; export default { '/': main, '/network': network, @@ -19,5 +20,6 @@ export default { '/edit-node': editNode, '/dashboard': dashboard, '/add-network-copy': addNetworkCopy, +'/tinder': tinder, } \ No newline at end of file diff --git a/smbls/pages/tinder.js b/smbls/pages/tinder.js new file mode 100644 index 0000000..e9191ff --- /dev/null +++ b/smbls/pages/tinder.js @@ -0,0 +1,13 @@ +export const tinder = { + extends: 'Page', + width: '100%', + height: '100%', + maxWidth: '500px', + margin: '0 auto', + flow: 'y', + overflow: 'hidden', + theme: 'document', + + TinderHeader: {}, + TinderStack: {} +} diff --git a/smbls/snippets/index.js b/smbls/snippets/index.js index 8b13789..fe36bbf 100644 --- a/smbls/snippets/index.js +++ b/smbls/snippets/index.js @@ -1 +1 @@ - +export * from './profilesData.js' diff --git a/smbls/snippets/profilesData.js b/smbls/snippets/profilesData.js new file mode 100644 index 0000000..7e38387 --- /dev/null +++ b/smbls/snippets/profilesData.js @@ -0,0 +1,68 @@ +export const profilesData = [ + { + id: 1, + name: 'Sophia', + age: 26, + distance: '3 miles away', + job: 'UX Designer', + school: 'Stanford University', + bio: 'Adventure seeker. Coffee addict. Dog mom. Let\'s explore the city together!', + image: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=600&h=900&fit=crop&q=80', + interests: ['Travel', 'Photography', 'Yoga', 'Coffee'] + }, + { + id: 2, + name: 'Emma', + age: 24, + distance: '5 miles away', + job: 'Software Engineer', + school: 'MIT', + bio: 'Code by day, cook by night. Looking for someone to taste-test my recipes.', + image: 'https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?w=600&h=900&fit=crop&q=80', + interests: ['Coding', 'Cooking', 'Gaming', 'Music'] + }, + { + id: 3, + name: 'Olivia', + age: 28, + distance: '8 miles away', + job: 'Photographer', + school: 'Parsons School of Design', + bio: 'Capturing moments one click at a time. Nature lover. Art enthusiast.', + image: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=600&h=900&fit=crop&q=80', + interests: ['Art', 'Nature', 'Film', 'Travel'] + }, + { + id: 4, + name: 'Ava', + age: 25, + distance: '2 miles away', + job: 'Marketing Manager', + school: 'NYU', + bio: 'Foodie on a mission. Weekend hiker. Always down for brunch.', + image: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=600&h=900&fit=crop&q=80', + interests: ['Food', 'Hiking', 'Wine', 'Reading'] + }, + { + id: 5, + name: 'Isabella', + age: 27, + distance: '6 miles away', + job: 'Architect', + school: 'Columbia University', + bio: 'Building dreams, one blueprint at a time. Jazz, museums, and rooftop sunsets.', + image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?w=600&h=900&fit=crop&q=80', + interests: ['Design', 'Jazz', 'Museums', 'Architecture'] + }, + { + id: 6, + name: 'Mia', + age: 23, + distance: '4 miles away', + job: 'Dance Instructor', + school: 'Juilliard', + bio: 'Life is better when you dance. Salsa, ballet, or just freestyle in the kitchen.', + image: 'https://images.unsplash.com/photo-1488716820095-cbe80883c496?w=600&h=900&fit=crop&q=80', + interests: ['Dance', 'Fitness', 'Music', 'Meditation'] + } +]