UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

135 lines • 4.46 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ export class SnapshotFormatter { #snapshot; constructor(snapshot) { this.#snapshot = snapshot; } toString() { const chunks = []; const root = this.#snapshot.root; // Top-level content of the snapshot. if (!this.#snapshot.verbose && this.#snapshot.hasSelectedElement && !this.#snapshot.selectedElementUid) { chunks.push(`Note: there is a selected element in the DevTools Elements panel but it is not included into the current a11y tree snapshot. Get a verbose snapshot to include all elements if you are interested in the selected element.\n\n`); } chunks.push(this.#formatNode(root, 0)); return chunks.join(''); } toJSON() { return this.#nodeToJSON(this.#snapshot.root); } #formatNode(node, depth = 0) { const chunks = []; const attributes = this.#getAttributes(node); const line = ' '.repeat(depth * 2) + attributes.join(' ') + (node.id === this.#snapshot.selectedElementUid ? ' [selected in the DevTools Elements panel]' : '') + '\n'; chunks.push(line); for (const child of node.children) { chunks.push(this.#formatNode(child, depth + 1)); } return chunks.join(''); } #nodeToJSON(node) { const rawAttrs = this.#getAttributesMap(node); const children = node.children.map(child => this.#nodeToJSON(child)); const result = structuredClone(rawAttrs); if (children.length > 0) { result.children = children; } return result; } #getAttributes(serializedAXNodeRoot) { const attributes = [`uid=${serializedAXNodeRoot.id}`]; if (serializedAXNodeRoot.role) { attributes.push(serializedAXNodeRoot.role === 'none' ? 'ignored' : serializedAXNodeRoot.role); } if (serializedAXNodeRoot.name) { attributes.push(`"${serializedAXNodeRoot.name}"`); } const simpleAttrs = this.#getAttributesMap(serializedAXNodeRoot, /* excludeSpecial */ true); for (const attr of Object.keys(serializedAXNodeRoot).sort()) { if (excludedAttributes.has(attr)) { continue; } const mapped = booleanPropertyMap[attr]; if (mapped && simpleAttrs[mapped]) { attributes.push(mapped); } const val = simpleAttrs[attr]; if (val === true) { attributes.push(attr); } else if (typeof val === 'string' || typeof val === 'number') { attributes.push(`${attr}="${val}"`); } } return attributes; } #getAttributesMap(node, excludeSpecial = false) { const result = {}; if (!excludeSpecial) { result.id = node.id; if (node.role) { result.role = node.role; } if (node.name) { result.name = node.name; } } // Re-implementing the exact logic from original function for #getAttributes to be safe: return { ...result, ...this.#extractedAttributes(node), }; } #extractedAttributes(node) { const result = {}; for (const attr of Object.keys(node).sort()) { if (excludedAttributes.has(attr)) { continue; } const value = node[attr]; if (typeof value === 'boolean') { if (booleanPropertyMap[attr]) { result[booleanPropertyMap[attr]] = true; } if (value) { result[attr] = true; } } else if (typeof value === 'string' || typeof value === 'number') { result[attr] = value; } } return result; } } const booleanPropertyMap = { disabled: 'disableable', expanded: 'expandable', focused: 'focusable', selected: 'selectable', }; const excludedAttributes = new Set([ 'id', 'role', 'name', 'elementHandle', 'children', 'backendNodeId', 'loaderId', ]); //# sourceMappingURL=SnapshotFormatter.js.map