diff --git a/sentience/extension/background.js b/sentience/extension/background.js index 1f64f84..6d0fe4a 100644 --- a/sentience/extension/background.js +++ b/sentience/extension/background.js @@ -6,7 +6,6 @@ import init, { analyze_page_with_options, analyze_page, prune_for_api } from '.. // This runs in an isolated environment, completely immune to page CSP policies -console.log('[Sentience Background] Initializing...'); // Global WASM initialization state let wasmReady = false; @@ -22,7 +21,6 @@ async function initWASM() { wasmInitPromise = (async () => { try { - console.log('[Sentience Background] Loading WASM module...'); // Define the js_click_element function that WASM expects // In Service Workers, use 'globalThis' instead of 'window' @@ -36,8 +34,6 @@ async function initWASM() { await init(); wasmReady = true; - console.log('[Sentience Background] ✓ WASM ready!'); - console.log( '[Sentience Background] Available functions: analyze_page, analyze_page_with_options, prune_for_api' ); } catch (error) { @@ -124,7 +120,6 @@ async function handleScreenshotCapture(_tabId, options = {}) { quality, }); - console.log( `[Sentience Background] Screenshot captured: ${format}, size: ${dataUrl.length} bytes` ); return dataUrl; @@ -166,7 +161,6 @@ async function handleSnapshotProcessing(rawData, options = {}) { throw new Error('WASM module not initialized'); } - console.log( `[Sentience Background] Processing ${rawData.length} elements with options:`, options ); @@ -213,7 +207,6 @@ async function handleSnapshotProcessing(rawData, options = {}) { } const duration = performance.now() - startTime; - console.log( `[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned (${duration.toFixed(1)}ms)` ); @@ -228,7 +221,6 @@ async function handleSnapshotProcessing(rawData, options = {}) { } } -console.log('[Sentience Background] Service worker ready'); // Global error handlers to prevent extension crashes self.addEventListener('error', (event) => { diff --git a/sentience/extension/content.js b/sentience/extension/content.js index 931ef5a..7daa63b 100644 --- a/sentience/extension/content.js +++ b/sentience/extension/content.js @@ -4,12 +4,10 @@ 'use strict'; // content.js - ISOLATED WORLD (Bridge between Main World and Background) - console.log('[Sentience Bridge] Loaded.'); // Detect if we're in a child frame (for iframe support) const isChildFrame = window !== window.top; if (isChildFrame) { - console.log('[Sentience Bridge] Running in child frame:', window.location.href); } // 1. Pass Extension ID to Main World (So API knows where to find resources) @@ -118,7 +116,6 @@ } if (response?.success) { - console.log(`[Sentience Bridge] ✓ WASM processing complete in ${duration.toFixed(1)}ms`); window.postMessage( { type: 'SENTIENCE_SNAPSHOT_RESULT', @@ -292,12 +289,10 @@ shadow.appendChild(box); }); - console.log(`[Sentience Bridge] Overlay shown for ${elements.length} elements`); // Auto-remove after 5 seconds overlayTimeout = setTimeout(() => { removeOverlay(); - console.log('[Sentience Bridge] Overlay auto-cleared after 5 seconds'); }, 5000); } @@ -306,7 +301,6 @@ */ function handleClearOverlay() { removeOverlay(); - console.log('[Sentience Bridge] Overlay cleared manually'); } /** @@ -324,6 +318,5 @@ } } - // console.log('[Sentience Bridge] Ready - Extension ID:', chrome.runtime.id); })(); diff --git a/sentience/extension/injected_api.js b/sentience/extension/injected_api.js index 7438a60..4a99df1 100644 --- a/sentience/extension/injected_api.js +++ b/sentience/extension/injected_api.js @@ -34,6 +34,468 @@ return elements; } + // ============================================================================ + // LABEL INFERENCE SYSTEM + // ============================================================================ + + // Default inference configuration (conservative - Stage 1 equivalent) + const DEFAULT_INFERENCE_CONFIG = { + // Allowed tag names that can be inference sources + allowedTags: ['label', 'span', 'div'], + + // Allowed ARIA roles that can be inference sources + allowedRoles: [], + + // Class name patterns (substring match, case-insensitive) + allowedClassPatterns: [], + + // DOM tree traversal limits + maxParentDepth: 2, // Max 2 levels up DOM tree + maxSiblingDistance: 1, // Only immediate previous/next sibling + + // Container requirements (no distance-based checks) + requireSameContainer: true, // Must share common parent + containerTags: ['form', 'fieldset', 'div'], + + // Enable/disable specific inference methods + methods: { + explicitLabel: true, // el.labels API + ariaLabelledby: true, // aria-labelledby attribute + parentTraversal: true, // Check parent/grandparent + siblingProximity: true, // Check preceding sibling (same container only) + }, + }; + + // Merge user config with defaults + function mergeInferenceConfig(userConfig = {}) { + return { + ...DEFAULT_INFERENCE_CONFIG, + ...userConfig, + methods: { + ...DEFAULT_INFERENCE_CONFIG.methods, + ...(userConfig.methods || {}), + }, + }; + } + + // Check if element matches inference source criteria + function isInferenceSource(el, config) { + if (!el || !el.tagName) return false; + + const tag = el.tagName.toLowerCase(); + const role = el.getAttribute ? el.getAttribute('role') : ''; + const className = ((el.className || '') + '').toLowerCase(); + + // Check tag name + if (config.allowedTags.includes(tag)) { + return true; + } + + // Check role + if (config.allowedRoles.length > 0 && role && config.allowedRoles.includes(role)) { + return true; + } + + // Check class patterns + if (config.allowedClassPatterns.length > 0) { + for (const pattern of config.allowedClassPatterns) { + if (className.includes(pattern.toLowerCase())) { + return true; + } + } + } + + return false; + } + + // Helper: Find common parent element + function findCommonParent(el1, el2) { + if (!el1 || !el2) return null; + + // Get document reference safely for stopping conditions + // eslint-disable-next-line no-undef + const doc = + (typeof global !== 'undefined' && global.document) || + (typeof window !== 'undefined' && window.document) || + (typeof document !== 'undefined' && document) || + null; + + const parents1 = []; + let current = el1; + // Collect all parents (including el1 itself) + while (current) { + parents1.push(current); + // Stop if no parent + if (!current.parentElement) { + break; + } + // Stop at body or documentElement if they exist + if (doc && (current === doc.body || current === doc.documentElement)) { + break; + } + current = current.parentElement; + } + + // Check if el2 or any of its parents are in parents1 + current = el2; + while (current) { + // Use indexOf for more reliable comparison (handles object identity) + if (parents1.indexOf(current) !== -1) { + return current; + } + // Stop if no parent + if (!current.parentElement) { + break; + } + // Stop at body or documentElement if they exist + if (doc && (current === doc.body || current === doc.documentElement)) { + break; + } + current = current.parentElement; + } + + return null; + } + + // Helper: Check if element is a valid container + function isValidContainer(el, validTags) { + if (!el || !el.tagName) return false; + const tag = el.tagName.toLowerCase(); + // Handle both string and object className + let className = ''; + try { + className = (el.className || '') + ''; + } catch (e) { + className = ''; + } + return ( + validTags.includes(tag) || + className.toLowerCase().includes('form') || + className.toLowerCase().includes('field') + ); + } + + // Helper: Check container requirements (no distance-based checks) + function isInSameValidContainer(element, candidate, limits) { + if (!element || !candidate) return false; + + // Check same container requirement + if (limits.requireSameContainer) { + const commonParent = findCommonParent(element, candidate); + if (!commonParent) { + return false; + } + // Check if common parent is a valid container + if (!isValidContainer(commonParent, limits.containerTags)) { + return false; + } + } + + return true; + } + + // Main inference function + function getInferredLabel(el, options = {}) { + if (!el) return null; + + const { + enableInference = true, + inferenceConfig = {}, // User-provided config, merged with defaults + } = options; + + if (!enableInference) return null; + + // OPTIMIZATION: If element already has text or aria-label, skip inference entirely + // Check this BEFORE checking labels, so we don't infer if element already has text + // Note: For INPUT elements, we check value/placeholder, not innerText + // For IMG elements, we check alt, not innerText + // For other elements, innerText is considered explicit text + const ariaLabel = el.getAttribute ? el.getAttribute('aria-label') : null; + const hasAriaLabel = ariaLabel && ariaLabel.trim(); + const hasInputValue = el.tagName === 'INPUT' && (el.value || el.placeholder); + const hasImgAlt = el.tagName === 'IMG' && el.alt; + // For non-input/img elements, check innerText - but only if it's not empty + // Access innerText safely - it might be a getter or property + let innerTextValue = ''; + try { + innerTextValue = el.innerText || ''; + } catch (e) { + // If innerText access fails, treat as empty + innerTextValue = ''; + } + const hasInnerText = + el.tagName !== 'INPUT' && el.tagName !== 'IMG' && innerTextValue && innerTextValue.trim(); + + if (hasAriaLabel || hasInputValue || hasImgAlt || hasInnerText) { + return null; + } + + // Merge config with defaults + const config = mergeInferenceConfig(inferenceConfig); + + // Method 1: Explicit label association (el.labels API) + if (config.methods.explicitLabel && el.labels && el.labels.length > 0) { + const label = el.labels[0]; + if (isInferenceSource(label, config)) { + const text = (label.innerText || '').trim(); + if (text) { + return { + text, + source: 'explicit_label', + }; + } + } + } + + // Method 2: aria-labelledby (supports space-separated list of IDs) + // NOTE: aria-labelledby is an EXPLICIT reference, so it should work with ANY element + // regardless of inference source criteria. The config only controls whether this method runs. + if (config.methods.ariaLabelledby && el.hasAttribute && el.hasAttribute('aria-labelledby')) { + const labelIdsAttr = el.getAttribute('aria-labelledby'); + if (labelIdsAttr) { + // Split by whitespace to support multiple IDs (space-separated list) + const labelIds = labelIdsAttr.split(/\s+/).filter((id) => id.trim()); + const labelTexts = []; + + // Helper function to get document.getElementById from available contexts + const getDocument = () => { + // eslint-disable-next-line no-undef + if (typeof global !== 'undefined' && global.document) { + // eslint-disable-next-line no-undef + return global.document; + } + if (typeof window !== 'undefined' && window.document) { + return window.document; + } + if (typeof document !== 'undefined') { + return document; + } + return null; + }; + + const doc = getDocument(); + if (!doc || !doc.getElementById) ; else { + // Process each ID in the space-separated list + for (const labelId of labelIds) { + if (!labelId.trim()) continue; + + let labelEl = null; + try { + labelEl = doc.getElementById(labelId); + } catch (e) { + // If getElementById fails, skip this ID + continue; + } + + // aria-labelledby is an explicit reference - use ANY element, not just those matching inference criteria + // This follows ARIA spec: aria-labelledby can reference any element in the document + if (labelEl) { + // Extract text from the referenced element + let text = ''; + try { + // Try innerText first (preferred for visible text) + text = (labelEl.innerText || '').trim(); + // Fallback to textContent if innerText is empty + if (!text && labelEl.textContent) { + text = labelEl.textContent.trim(); + } + // Fallback to aria-label if available + if (!text && labelEl.getAttribute) { + const ariaLabel = labelEl.getAttribute('aria-label'); + if (ariaLabel) { + text = ariaLabel.trim(); + } + } + } catch (e) { + // If text extraction fails, skip this element + continue; + } + + if (text) { + labelTexts.push(text); + } + } + } + } + + // Combine multiple label texts (space-separated) + if (labelTexts.length > 0) { + return { + text: labelTexts.join(' '), + source: 'aria_labelledby', + }; + } + } + } + + // Method 3: Parent/grandparent traversal + if (config.methods.parentTraversal) { + let parent = el.parentElement; + let depth = 0; + while (parent && depth < config.maxParentDepth) { + if (isInferenceSource(parent, config)) { + const text = (parent.innerText || '').trim(); + if (text) { + return { + text, + source: 'parent_label', + }; + } + } + parent = parent.parentElement; + depth++; + } + } + + // Method 4: Preceding sibling (no distance-based checks, only DOM structure) + if (config.methods.siblingProximity) { + const prev = el.previousElementSibling; + if (prev && isInferenceSource(prev, config)) { + // Only check if they're in the same valid container (no pixel distance) + if ( + isInSameValidContainer(el, prev, { + requireSameContainer: config.requireSameContainer, + containerTags: config.containerTags, + }) + ) { + const text = (prev.innerText || '').trim(); + if (text) { + return { + text, + source: 'sibling_label', + }; + } + } + } + } + + return null; + } + + // Helper: Check if element is interactable (should have role inferred) + function isInteractableElement(el) { + if (!el || !el.tagName) return false; + + const tag = el.tagName.toLowerCase(); + const role = el.getAttribute ? el.getAttribute('role') : null; + const hasTabIndex = el.hasAttribute ? el.hasAttribute('tabindex') : false; + const hasHref = el.tagName === 'A' && (el.hasAttribute ? el.hasAttribute('href') : false); + + // Native interactive elements + const interactiveTags = [ + 'button', + 'input', + 'textarea', + 'select', + 'option', + 'details', + 'summary', + 'a', + ]; + if (interactiveTags.includes(tag)) { + // For , only if it has href + if (tag === 'a' && !hasHref) return false; + return true; + } + + // Elements with explicit interactive roles + const interactiveRoles = [ + 'button', + 'link', + 'tab', + 'menuitem', + 'checkbox', + 'radio', + 'switch', + 'slider', + 'combobox', + 'textbox', + 'searchbox', + 'spinbutton', + ]; + if (role && interactiveRoles.includes(role.toLowerCase())) { + return true; + } + + // Focusable elements (tabindex makes them interactive) + if (hasTabIndex) { + return true; + } + + // Elements with event handlers (custom interactive elements) + if (el.onclick || el.onkeydown || el.onkeypress || el.onkeyup) { + return true; + } + + // Check for inline event handlers via attributes + if (el.getAttribute) { + const hasInlineHandler = + el.getAttribute('onclick') || + el.getAttribute('onkeydown') || + el.getAttribute('onkeypress') || + el.getAttribute('onkeyup'); + if (hasInlineHandler) { + return true; + } + } + + return false; + } + + // Helper: Infer ARIA role for interactable elements + function getInferredRole(el, options = {}) { + const { + enableInference = true, + // inferenceConfig reserved for future extensibility + } = options; + + if (!enableInference) return null; + + // Only infer roles for interactable elements + if (!isInteractableElement(el)) { + return null; + } + + // CRITICAL: Only infer if element has NO aria-label AND NO explicit role + const hasAriaLabel = el.getAttribute ? el.getAttribute('aria-label') : null; + const hasExplicitRole = el.getAttribute ? el.getAttribute('role') : null; + + if (hasAriaLabel || hasExplicitRole) { + return null; // Skip inference if element already has aria-label or role + } + + // If element is native semantic HTML, it already has a role + const tag = el.tagName.toLowerCase(); + const semanticTags = ['button', 'a', 'input', 'textarea', 'select', 'option']; + if (semanticTags.includes(tag)) { + return null; // Native HTML already has role + } + + // Infer role based on element behavior or context + // Check for click handlers first (most common) + if (el.onclick || (el.getAttribute && el.getAttribute('onclick'))) { + return 'button'; + } + + // Check for keyboard handlers + if ( + el.onkeydown || + el.onkeypress || + el.onkeyup || + (el.getAttribute && + (el.getAttribute('onkeydown') || el.getAttribute('onkeypress') || el.getAttribute('onkeyup'))) + ) { + return 'button'; // Default to button for keyboard-interactive elements + } + + // Focusable div/span likely needs a role + if (el.hasAttribute && el.hasAttribute('tabindex') && (tag === 'div' || tag === 'span')) { + return 'button'; // Default assumption for focusable elements + } + + return null; + } + // --- HELPER: Smart Text Extractor --- function getText(el) { if (el.getAttribute('aria-label')) return el.getAttribute('aria-label'); @@ -42,6 +504,71 @@ return (el.innerText || '').replace(/\s+/g, ' ').trim().substring(0, 100); } + // Enhanced semantic text extractor with inference support + function getSemanticText(el, options = {}) { + if (!el) { + return { + text: '', + source: null, + }; + } + + // First check explicit aria-label (highest priority) + const explicitAriaLabel = el.getAttribute ? el.getAttribute('aria-label') : null; + if (explicitAriaLabel && explicitAriaLabel.trim()) { + return { + text: explicitAriaLabel.trim(), + source: 'explicit_aria_label', + }; + } + + // Check for existing text (visible text, input value, etc.) + // This matches the existing getText() logic + if (el.tagName === 'INPUT') { + const value = (el.value || el.placeholder || '').trim(); + if (value) { + return { + text: value, + source: 'input_value', + }; + } + } + + if (el.tagName === 'IMG') { + const alt = (el.alt || '').trim(); + if (alt) { + return { + text: alt, + source: 'img_alt', + }; + } + } + + const innerText = (el.innerText || '').trim(); + if (innerText) { + return { + text: innerText.substring(0, 100), // Match existing getText() limit + source: 'inner_text', + }; + } + + // Only try inference if we have NO explicit text/label + // Pass inferenceConfig from options to getInferredLabel + const inferred = getInferredLabel(el, { + enableInference: options.enableInference !== false, + inferenceConfig: options.inferenceConfig, // Pass config through + }); + if (inferred) { + return inferred; + } + + // Fallback: return empty with no source + return { + text: '', + source: null, + }; + } + // --- HELPER: Safe Class Name Extractor (Handles SVGAnimatedString) --- function getClassName(el) { if (!el || !el.className) return ''; @@ -745,7 +1272,6 @@ return iframeData; } - console.log(`[SentienceAPI] Found ${iframes.length} iframe(s), requesting snapshots...`); // Request snapshot from each iframe const iframePromises = iframes.map((iframe, idx) => { // OPTIMIZATION: Skip common ad domains to save time @@ -755,7 +1281,6 @@ src.includes('googleadservices') || src.includes('ads system') ) { - console.log(`[SentienceAPI] Skipping ad iframe: ${src.substring(0, 30)}...`); return Promise.resolve(null); } @@ -789,7 +1314,6 @@ resolve(null); } else { const elementCount = event.data.snapshot?.raw_elements?.length || 0; - console.log( `[SentienceAPI] ✓ Received ${elementCount} elements from Iframe ${idx} (id: ${requestId})` ); resolve({ @@ -806,7 +1330,6 @@ // 3. SEND REQUEST with error handling try { if (iframe.contentWindow) { - // console.log(`[SentienceAPI] Sending request to Iframe ${idx} (id: ${requestId})`); iframe.contentWindow.postMessage( { type: 'SENTIENCE_IFRAME_SNAPSHOT_REQUEST', @@ -842,7 +1365,6 @@ results.forEach((result, idx) => { if (result && result.data && !result.error) { iframeData.set(iframes[idx], result.data); - console.log(`[SentienceAPI] ✓ Collected snapshot from iframe ${idx}`); } else if (result && result.error) { console.warn(`[SentienceAPI] Iframe ${idx} snapshot error:`, result.error); } else if (!result) { @@ -928,7 +1450,18 @@ window.sentience_registry[idx] = el; - const textVal = getText(el); + // Use getSemanticText for inference support (falls back to getText if no inference) + const semanticText = getSemanticText(el, { + enableInference: options.enableInference !== false, // Default: true + inferenceConfig: options.inferenceConfig, // Pass configurable inference settings + }); + const textVal = semanticText.text || getText(el); // Fallback to getText for backward compat + + // Infer role for interactable elements (only if no aria-label and no explicit role) + const inferredRole = getInferredRole(el, { + enableInference: options.enableInference !== false, + inferenceConfig: options.inferenceConfig, + }); const inView = isInViewport(rect); // Get computed style once (needed for both occlusion check and data collection) @@ -960,7 +1493,19 @@ attributes: { role: toSafeString(el.getAttribute('role')), type_: toSafeString(el.getAttribute('type')), - aria_label: toSafeString(el.getAttribute('aria-label')), + aria_label: + semanticText?.source === 'explicit_aria_label' + ? semanticText.text + : toSafeString(el.getAttribute('aria-label')), // Keep original for backward compat + inferred_label: + semanticText?.source && + !['explicit_aria_label', 'input_value', 'img_alt', 'inner_text'].includes( + semanticText.source + ) + ? toSafeString(semanticText.text) + : null, + label_source: semanticText?.source || null, // Track source for gateway + inferred_role: inferredRole ? toSafeString(inferredRole) : null, // Inferred role for interactable elements href: toSafeString(el.href || el.getAttribute('href') || null), class: toSafeString(getClassName(el)), // Capture dynamic input state (not just initial attributes) @@ -976,7 +1521,6 @@ }); }); - console.log(`[SentienceAPI] Collected ${rawData.length} elements from main frame`); // Step 1.5: Collect iframe snapshots and FLATTEN immediately // "Flatten Early" architecture: Merge iframe elements into main array before WASM @@ -986,9 +1530,7 @@ if (options.collectIframes !== false) { try { - console.log(`[SentienceAPI] Starting iframe collection...`); const iframeSnapshots = await collectIframeSnapshots(options); - console.log( `[SentienceAPI] Iframe collection complete. Received ${iframeSnapshots.size} snapshot(s)` ); @@ -996,11 +1538,9 @@ // FLATTEN IMMEDIATELY: Don't nest them. Just append them with coordinate translation. iframeSnapshots.forEach((iframeSnapshot, iframeEl) => { // Debug: Log structure to verify data is correct - // console.log(`[SentienceAPI] Processing iframe snapshot:`, iframeSnapshot); if (iframeSnapshot && iframeSnapshot.raw_elements) { const rawElementsCount = iframeSnapshot.raw_elements.length; - console.log( `[SentienceAPI] Processing ${rawElementsCount} elements from iframe (src: ${iframeEl.src || 'unknown'})` ); // Get iframe's bounding rect (offset for coordinate translation) @@ -1045,7 +1585,6 @@ } }); - // console.log(`[SentienceAPI] Merged ${iframeSnapshots.size} iframe(s). Total elements: ${allRawElements.length} (${rawData.length} main + ${totalIframeElements} iframe)`); } } catch (error) { console.warn('[SentienceAPI] Iframe collection failed:', error); @@ -1055,7 +1594,6 @@ // Step 2: Send EVERYTHING to WASM (One giant flat list) // Now WASM prunes iframe elements and main elements in one pass! // No recursion needed - everything is already flat - console.log( `[SentienceAPI] Sending ${allRawElements.length} total elements to WASM (${rawData.length} main + ${totalIframeElements} iframe)` ); const processed = await processSnapshotInBackground(allRawElements, options); @@ -1083,7 +1621,6 @@ const totalRaw = cleanedRawElements.length; const iframeCount = totalIframeElements || 0; - console.log( `[SentienceAPI] ✓ Complete: ${totalCount} Smart Elements, ${totalRaw} Raw Elements (includes ${iframeCount} from iframes) (WASM took ${processed.duration?.toFixed(1)}ms)` ); @@ -1304,10 +1841,8 @@ keyboardShortcut = 'Ctrl+Shift+I', } = options; - console.log( '🔴 [Sentience] Recording Mode STARTED. Click an element to copy its Ground Truth JSON.' ); - console.log(` Press ${keyboardShortcut} or call stopRecording() to stop.`); // Validate registry is populated if (!window.sentience_registry || window.sentience_registry.length === 0) { @@ -1415,7 +1950,6 @@ navigator.clipboard .writeText(jsonString) .then(() => { - console.log('✅ Copied Ground Truth to clipboard:', snippet); // Flash green to indicate success highlightBox.style.border = `2px solid ${successColor}`; @@ -1463,7 +1997,6 @@ delete window.sentience_stopRecording; } - console.log('⚪ [Sentience] Recording Mode STOPPED.'); }; // Keyboard shortcut handler (defined after stopRecording) @@ -1483,7 +2016,6 @@ // Set up auto-disable timeout if (autoDisableTimeout > 0) { timeoutId = setTimeout(() => { - console.log('⏰ [Sentience] Recording Mode auto-disabled after timeout.'); stopRecording(); }, autoDisableTimeout); } @@ -1517,7 +2049,6 @@ '*' ); - console.log(`[Sentience] Overlay requested for ${elements.length} elements`); } /** @@ -1530,7 +2061,6 @@ }, '*' ); - console.log('[Sentience] Overlay cleared'); } // index.js - Main Entry Point for Injected API @@ -1538,7 +2068,6 @@ (async () => { - // console.log('[SentienceAPI] Initializing (CSP-Resistant Mode)...'); // Wait for Extension ID from content.js const getExtensionId = () => document.documentElement.dataset.sentienceExtensionId; @@ -1562,7 +2091,6 @@ return; } - // console.log('[SentienceAPI] Extension ID:', extId); // Registry for click actions (still needed for click() function) window.sentience_registry = []; @@ -1584,7 +2112,6 @@ window.sentience_iframe_handler_setup = true; } - console.log('[SentienceAPI] ✓ Ready! (CSP-Resistant - WASM runs in background)'); })(); })(); diff --git a/sentience/extension/manifest.json b/sentience/extension/manifest.json index 9df85db..456c1f5 100644 --- a/sentience/extension/manifest.json +++ b/sentience/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Sentience Semantic Visual Grounding Extractor", - "version": "2.1.0", + "version": "2.2.0", "description": "Extract semantic visual grounding data from web pages", "permissions": ["activeTab", "scripting"], "host_permissions": [""], diff --git a/sentience/extension/pkg/sentience_core_bg.wasm b/sentience/extension/pkg/sentience_core_bg.wasm index 5b6cbc9..259298c 100644 Binary files a/sentience/extension/pkg/sentience_core_bg.wasm and b/sentience/extension/pkg/sentience_core_bg.wasm differ diff --git a/sentience/extension/release.json b/sentience/extension/release.json index 237d107..89ef3af 100644 --- a/sentience/extension/release.json +++ b/sentience/extension/release.json @@ -1,9 +1,9 @@ { - "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/274139125", - "assets_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/274139125/assets", - "upload_url": "https://uploads.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/274139125/assets{?name,label}", - "html_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/tag/v2.1.0", - "id": 274139125, + "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/274391186", + "assets_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/274391186/assets", + "upload_url": "https://uploads.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/274391186/assets{?name,label}", + "html_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/tag/v2.2.0", + "id": 274391186, "author": { "login": "rcholic", "id": 135060, @@ -25,21 +25,21 @@ "user_view_type": "public", "site_admin": false }, - "node_id": "RE_kwDOQshiJ84QVwf1", - "tag_name": "v2.1.0", + "node_id": "RE_kwDOQshiJ84QWuCS", + "tag_name": "v2.2.0", "target_commitish": "main", - "name": "Release v2.1.0", + "name": "Release v2.2.0", "draft": false, "immutable": false, "prerelease": false, - "created_at": "2026-01-05T06:50:34Z", - "updated_at": "2026-01-05T06:52:25Z", - "published_at": "2026-01-05T06:51:51Z", + "created_at": "2026-01-06T01:49:12Z", + "updated_at": "2026-01-06T01:50:33Z", + "published_at": "2026-01-06T01:49:59Z", "assets": [ { - "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/336451766", - "id": 336451766, - "node_id": "RA_kwDOQshiJ84UDdi2", + "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/336789146", + "id": 336789146, + "node_id": "RA_kwDOQshiJ84UEv6a", "name": "extension-files.tar.gz", "label": "", "uploader": { @@ -65,17 +65,17 @@ }, "content_type": "application/gzip", "state": "uploaded", - "size": 78328, - "digest": "sha256:97f355bdb757cf0d399d3e0efeacbc66fa55f5f3e4e2755c533eeb133c77bc42", + "size": 82734, + "digest": "sha256:c02b99bc87733d0ec292da4d03c9e0d07f929be49fd590cb5988f6609b53bf56", "download_count": 0, - "created_at": "2026-01-05T06:52:25Z", - "updated_at": "2026-01-05T06:52:25Z", - "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v2.1.0/extension-files.tar.gz" + "created_at": "2026-01-06T01:50:33Z", + "updated_at": "2026-01-06T01:50:33Z", + "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v2.2.0/extension-files.tar.gz" }, { - "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/336451765", - "id": 336451765, - "node_id": "RA_kwDOQshiJ84UDdi1", + "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/336789145", + "id": 336789145, + "node_id": "RA_kwDOQshiJ84UEv6Z", "name": "extension-package.zip", "label": "", "uploader": { @@ -101,15 +101,15 @@ }, "content_type": "application/zip", "state": "uploaded", - "size": 80564, - "digest": "sha256:3260b70b684cdb2eddf210d29dc35134438549f7cb08c3557db5c792a126c693", + "size": 84405, + "digest": "sha256:658294bf95b077bea348c99fc2a82093eaeb46c9fc4253ccd3d24ab334a3fc45", "download_count": 0, - "created_at": "2026-01-05T06:52:25Z", - "updated_at": "2026-01-05T06:52:25Z", - "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v2.1.0/extension-package.zip" + "created_at": "2026-01-06T01:50:33Z", + "updated_at": "2026-01-06T01:50:33Z", + "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v2.2.0/extension-package.zip" } ], - "tarball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/tarball/v2.1.0", - "zipball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/zipball/v2.1.0", + "tarball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/tarball/v2.2.0", + "zipball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/zipball/v2.2.0", "body": "" }