From 8c8020e857244a3e7e045267dde9deda5fd7a949 Mon Sep 17 00:00:00 2001 From: jaydeepsingh25 Date: Fri, 12 Sep 2025 00:08:10 -0700 Subject: [PATCH 1/2] fix: extension refactoring --- src/background-utils.ts | 291 +++++++++++++++ src/background.ts | 539 ++++++++++++--------------- src/content.ts | 47 +-- src/types/background.types.ts | 294 +++++++++++++++ src/types/message-types.constants.ts | 49 +++ 5 files changed, 905 insertions(+), 315 deletions(-) create mode 100644 src/background-utils.ts create mode 100644 src/types/background.types.ts create mode 100644 src/types/message-types.constants.ts diff --git a/src/background-utils.ts b/src/background-utils.ts new file mode 100644 index 00000000..6d410490 --- /dev/null +++ b/src/background-utils.ts @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2021 IMAGE Project, Shared Reality Lab, McGill University + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * and our Additional Terms along with this program. + * If not, see . + */ +import browser from "webextension-polyfill"; +import { v4 as uuidv4 } from "uuid"; +import { IMAGERequest } from "./types/request.schema"; +import { getAllStorageSyncData, getCapabilities, getRenderers, getLanguage, windowsPanel } from './utils'; +import { encryptData, monarchPopUp, saveToLocalStorage } from "./monarch/utils"; +import { TatStorageData } from "./monarch/types"; +import { openRenderingsinWindow } from "./config"; + +/** + * Generates a query for remote resources + * + * @param message - Object containing context, URL, dimensions, and graphic blob + * @returns Promise resolving to an IMAGERequest object + */ +export async function generateQuery(message: { context: string, url: string, dims: [number, number], graphicBlob: string }): Promise { + let renderers = await getRenderers(); + let capabilities = await getCapabilities(); + console.debug("inside generate query"); + return { + "request_uuid": uuidv4(), + "timestamp": Math.round(Date.now() / 1000), + "URL": message.url, + "graphic": message.graphicBlob, + "dimensions": message.dims, + "context": message.context, + "language": await getLanguage(), + "capabilities": capabilities, + "renderers": renderers + } as IMAGERequest; +} + +/** + * Generates a query for local resources + * + * @param message - Object containing context, dimensions, image, and graphic blob + * @returns Promise resolving to an IMAGERequest object + */ +export async function generateLocalQuery(message: { context: string, dims: [number, number], image: string, graphicBlob: string }): Promise { + let renderers = await getRenderers(); + let capabilities = await getCapabilities(); + return { + "request_uuid": uuidv4(), + "timestamp": Math.round(Date.now() / 1000), + "graphic": message.graphicBlob, + "dimensions": message.dims, + "context": message.context, + "language": await getLanguage(), + "capabilities": capabilities, + "renderers": renderers + } as IMAGERequest; +} + +/** + * Generates a query for chart resources + * + * @param message - Object containing highChartsData + * @returns Promise resolving to an IMAGERequest object + */ +export async function generateChartQuery(message: { highChartsData: { [k: string]: unknown } }): Promise { + let renderers = await getRenderers(); + let capabilities = await getCapabilities(); + return { + "request_uuid": uuidv4(), + "timestamp": Math.round(Date.now() / 1000), + "highChartsData": message.highChartsData, + "language": await getLanguage(), + "capabilities": capabilities, + "renderers": renderers + } as IMAGERequest; +} + +/** + * Processes tactile rendering data for Monarch or Tactile Authoring Tool + * + * @param tactileSvgGraphic - SVG graphic data in base64 format + * @param query - The IMAGERequest object + * @param message - The message object containing options + * @param serverUrl - The server URL to send requests to + */ +export async function processTactileRendering(tactileSvgGraphic: string, query: IMAGERequest, message: any, serverUrl: RequestInfo) { + let items = await getAllStorageSyncData(); + let encodedSvg = tactileSvgGraphic.split("data:image/svg+xml;base64,")[1]; + let svgDom = atob(encodedSvg); + console.log("SVG DOM", svgDom); + let reqTitle = items["monarchTitle"]; + let reqSecretKey = items["monarchSecretKey"]; + let reqChannelId = items["monarchChannelId"]; + let encryptionKey = items["monarchEncryptionKey"]; + const flowType = reqChannelId ? "update" : "create"; + let monarchTargetUrl = `${serverUrl}/monarch`; + monarchTargetUrl = monarchTargetUrl.replace(/([^:]\/)\/+/g, "$1"); + let monarchFetchUrl = flowType == "update" ? + `${monarchTargetUrl}/update/${reqChannelId}` : + `${monarchTargetUrl}/create`; + let encryptedGraphicBlob = query["graphic"] && await encryptData(query["graphic"], encryptionKey); + let encryptedCoordinates = query["coordinates"] && await encryptData(JSON.stringify(query["coordinates"]), encryptionKey); + let encryptedPlaceId = query["placeID"] && await encryptData(query["placeID"], encryptionKey); + const reqData = await encryptData(svgDom, encryptionKey); + const reqBody = { + "data": reqData, + "layer": "None", + "title": reqTitle, + "secret": reqSecretKey, + "graphicBlob": encryptedGraphicBlob, + "coordinates": encryptedCoordinates, + "placeID": encryptedPlaceId + }; + + if (message["sendToMonarch"]) { + /** Send Graphic to Monarch flow - Make curl request to monarch */ + const response = await fetch(monarchFetchUrl, + { + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": JSON.stringify(reqBody) + }); + + let responseJSON = { + "id": reqChannelId || "", + "secret": "" + }; + if (flowType == "create") { + responseJSON = await response.json(); + browser.storage.sync.set({ + "monarchChannelId": responseJSON["id"], + "monarchSecretKey": responseJSON["secret"] + }); + } + let currentTab = await browser.tabs.query({ active: true, currentWindow: true }); + + // Check if the current tab is an extension page + if (currentTab[0].url && !currentTab[0].url.startsWith('chrome-extension://')) { + browser.scripting.executeScript({ + target: { tabId: currentTab[0].id || 0 }, + func: monarchPopUp, + args: [responseJSON["id"], flowType] + }); + } else { + // If we're on an extension page, use notifications instead of alert + const message = flowType === "create" + ? `New channel created with code ${responseJSON["id"]}` + : `Graphic in channel ${responseJSON["id"]} has been updated!`; + + browser.notifications.create({ + type: 'basic', + iconUrl: 'image-icon-128.png', + title: 'Monarch Response', + message: message + }); + } + + } else { + /** Handle "Load in Tactile Authoring Tool" flow */ + const tatStorageData: TatStorageData = { + channelId: items["monarchChannelId"], + graphicTitle: items["monarchTitle"], + secretKey: items["monarchSecretKey"], + graphicBlob: query["graphic"], + coordinates: query["coordinates"] && JSON.stringify(query["coordinates"]), + placeID: query["placeID"], + } + let tatTargetUrl = `${serverUrl}/tat/`; + tatTargetUrl = tatTargetUrl.replace(/([^:]\/)\/+/g, "$1"); + let tabs = await browser.tabs.query({ url: tatTargetUrl }); + + /** encrypt data before storing in local storage */ + let encryptedSvgData = await encryptData(svgDom, encryptionKey); + let encryptedTatData: TatStorageData = {channelId:"", graphicTitle:"", secretKey:""}; + for (let key of Object.keys(tatStorageData)) { + let stringToEncrypt = tatStorageData[key as keyof TatStorageData]; + if (stringToEncrypt){ + encryptedTatData[key as keyof TatStorageData] = await encryptData(tatStorageData[key as keyof TatStorageData], encryptionKey); + } + } + + if (tabs && tabs.length > 0) { + let existingTab = tabs[0]; + browser.scripting.executeScript({ + target: { tabId: existingTab.id || 0 }, + func: saveToLocalStorage, + args: [encryptedSvgData, encryptedTatData, existingTab] + }); + browser.tabs.update(existingTab.id, { active: true }); + } + else { + let authoringTool = browser.tabs.create({ + url: tatTargetUrl + }); + authoringTool.then((tab) => { + browser.scripting.executeScript({ + target: { tabId: tab.id || 0 }, + func: saveToLocalStorage, + args: [encryptedSvgData, encryptedTatData] + }); + }, (error) => { console.log(error) }); + } + } +} + +/** + * Converts a Blob to a base64 string + * + * @param blob - The Blob to convert + * @returns Promise resolving to the base64 string + */ +export function blobToBase64(blob: Blob) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.onerror = () => reject(reader.error); + reader.readAsDataURL(blob); + }); +} + +/** + * Creates a panel or tab to display renderings + * + * @param query - The IMAGERequest object + * @param graphicUrl - URL of the graphic + * @returns Promise resolving to the created window or tab + */ +export function createPanel(query: IMAGERequest, graphicUrl: string) { + let window = windowsPanel ? browser.windows.create({ + type: "normal", + url: `info/info.html?uuid=${query["request_uuid"]}&graphicUrl=${graphicUrl}&dimensions=${query["dimensions"]}`, + height: 1080, + width: 1920 + }) : browser.tabs.create({ + url: `info/info.html?uuid=${query["request_uuid"]}&graphicUrl=${graphicUrl}&dimensions=${query["dimensions"]}`, + }); + return window; +} + +/** + * Toggles map options based on debug and monarch settings + * + * @param showDebugOptions - Boolean indicating whether to show debug options + * @param monarchEnabled - Boolean indicating whether monarch is enabled + */ +export function toggleMapOptions(showDebugOptions: Boolean, monarchEnabled: Boolean) { + let mapButtonContainer = document.getElementById("map-button-container"); + let mapSelectContainer = document.getElementById("map-select-container"); + if (mapButtonContainer && mapSelectContainer) { + if (monarchEnabled) { + mapSelectContainer.style.display = "flex"; + mapButtonContainer.style.display = "none"; + } else { + mapSelectContainer.style.display = "none"; + mapButtonContainer.style.display = "flex"; + } + } +} + +/** + * Creates an offscreen document to keep the service worker running + */ +export async function createOffscreen() { + // @ts-ignore + await browser.offscreen.createDocument({ + url: 'offscreen.html', + reasons: ['BLOBS'], + justification: 'keep service worker running', + }).catch(() => { }); +} + +/** + * Callback function for browser.contextMenus.create + */ +export function onCreated(): void { + if (browser.runtime.lastError) { + //console.error(browser.runtime.lastError); + } +} diff --git a/src/background.ts b/src/background.ts index d65d3d23..bedd9a63 100644 --- a/src/background.ts +++ b/src/background.ts @@ -15,231 +15,141 @@ * If not, see . */ import browser, { Runtime } from "webextension-polyfill"; -import { v4 as uuidv4 } from "uuid"; import hash from "object-hash"; import { IMAGEResponse } from "./types/response.schema"; import { IMAGERequest } from "./types/request.schema"; -import { getAllStorageSyncData, getCapabilities, getRenderers, getLanguage, windowsPanel } from './utils'; +import { getAllStorageSyncData, getLanguage, windowsPanel } from './utils'; import { generateMapQuery, generateMapSearchQuery } from "./maps/maps-utils"; import { RENDERERS, SERVER_URL } from './config'; -import { encryptData, monarchPopUp, decryptData, saveToLocalStorage } from "./monarch/utils"; -import { TatStorageData } from "./monarch/types"; - -let ports: { [key: number]: Runtime.Port } = {}; -const responseMap: Map = new Map(); +import { decryptData } from "./monarch/utils"; +import { + Message, + ResourceMessage, + LocalResourceMessage, + ChartResourceMessage, + MapResourceMessage, + MapSearchMessage, + CheckImageSizeMessage, + DataFromAuthoringToolMessage, + InfoMessage, + ResponseMapEntry, + PortsMap +} from "./types/background.types"; +import { MESSAGE_TYPES, RENDER_TYPES } from "./types/message-types.constants"; +import { + generateQuery, + generateLocalQuery, + generateChartQuery, + processTactileRendering, + blobToBase64, + createPanel, + toggleMapOptions, + createOffscreen, + onCreated +} from "./background-utils"; + +// ============================================================================ +// Global state +// ============================================================================ + +/** + * Global variables and constants for the background script + */ +// Connection and communication +let ports: PortsMap = {}; +const responseMap: Map = new Map(); var serverUrl: RequestInfo; -var renderingsPanel: browser.Windows.Window | browser.Tabs.Tab; -var errorPanel: browser.Windows.Window | browser.Tabs.Tab; -let launchPad : browser.Windows.Window | browser.Tabs.Tab; var graphicUrl: string = ""; -var extVersion = process.env.NODE_ENV; -//console.debug("Extension Version background page", extVersion); -//console.debug("Suffix Text", process.env.SUFFIX_TEXT); -async function generateQuery(message: { context: string, url: string, dims: [number, number], graphicBlob: string }): Promise { - let renderers = await getRenderers(); - let capabilities = await getCapabilities(); - console.debug("inside generate query"); - return { - "request_uuid": uuidv4(), - "timestamp": Math.round(Date.now() / 1000), - "URL": message.url, - "graphic": message.graphicBlob, - "dimensions": message.dims, - "context": message.context, - "language": await getLanguage(), - "capabilities": capabilities, - "renderers": renderers - } as IMAGERequest; -} -async function generateLocalQuery(message: { context: string, dims: [number, number], image: string, graphicBlob: string }): Promise { - let renderers = await getRenderers(); - let capabilities = await getCapabilities(); - return { - "request_uuid": uuidv4(), - "timestamp": Math.round(Date.now() / 1000), - "graphic": message.graphicBlob, - "dimensions": message.dims, - "context": message.context, - "language": await getLanguage(), - "capabilities": capabilities, - "renderers": renderers - } as IMAGERequest; -} - -async function generateChartQuery(message: { highChartsData: { [k: string]: unknown } }): Promise { - let renderers = await getRenderers(); - let capabilities = await getCapabilities(); - return { - "request_uuid": uuidv4(), - "timestamp": Math.round(Date.now() / 1000), - "highChartsData": message.highChartsData, - "language": await getLanguage(), - "capabilities": capabilities, - "renderers": renderers - } as IMAGERequest; -} - -async function processTactileRendering(tactileSvgGraphic: string, query: IMAGERequest, message: any, serverUrl: RequestInfo) { - let items = await getAllStorageSyncData(); - let encodedSvg = tactileSvgGraphic.split("data:image/svg+xml;base64,")[1]; - let svgDom = atob(encodedSvg); - console.log("SVG DOM", svgDom); - let reqTitle = items["monarchTitle"]; - let reqSecretKey = items["monarchSecretKey"]; - let reqChannelId = items["monarchChannelId"]; - let encryptionKey = items["monarchEncryptionKey"]; - const flowType = reqChannelId ? "update" : "create"; - let monarchTargetUrl = `${serverUrl}/monarch`; - monarchTargetUrl = monarchTargetUrl.replace(/([^:]\/)\/+/g, "$1"); - let monarchFetchUrl = flowType == "update" ? - `${monarchTargetUrl}/update/${reqChannelId}` : - `${monarchTargetUrl}/create`; - let encryptedGraphicBlob = query["graphic"] && await encryptData(query["graphic"], encryptionKey); - let encryptedCoordinates = query["coordinates"] && await encryptData(JSON.stringify(query["coordinates"]), encryptionKey); - let encryptedPlaceId = query["placeID"] && await encryptData(query["placeID"], encryptionKey); - const reqData = await encryptData(svgDom, encryptionKey); - const reqBody = { - "data": reqData, - "layer": "None", - "title": reqTitle, - "secret": reqSecretKey, - "graphicBlob": encryptedGraphicBlob, - "coordinates": encryptedCoordinates, - "placeID": encryptedPlaceId - }; - - if (message["sendToMonarch"]) { - /** Send Graphic to Monarch flow - Make curl request to monarch */ - const response = await fetch(monarchFetchUrl, - { - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": JSON.stringify(reqBody) - }); +// UI elements +var renderingsPanel: browser.Windows.Window | browser.Tabs.Tab; +var errorPanel: browser.Windows.Window | browser.Tabs.Tab; +let launchPad: browser.Windows.Window | browser.Tabs.Tab; - let responseJSON = { - "id": reqChannelId || "", - "secret": "" - }; - if (flowType == "create") { - responseJSON = await response.json(); - browser.storage.sync.set({ - "monarchChannelId": responseJSON["id"], - "monarchSecretKey": responseJSON["secret"] - }); - } - let currentTab = await browser.tabs.query({ active: true, currentWindow: true }); - - // Check if the current tab is an extension page - if (currentTab[0].url && !currentTab[0].url.startsWith('chrome-extension://')) { - browser.scripting.executeScript({ - target: { tabId: currentTab[0].id || 0 }, - func: monarchPopUp, - args: [responseJSON["id"], flowType] - }); - } else { - // If we're on an extension page, use notifications instead of alert - const message = flowType === "create" - ? `New channel created with code ${responseJSON["id"]}` - : `Graphic in channel ${responseJSON["id"]} has been updated!`; - - browser.notifications.create({ - type: 'basic', - iconUrl: 'image-icon-128.png', - title: 'Monarch Response', - message: message - }); - } +// Extension settings +var extVersion = process.env.NODE_ENV; +var showDebugOptions: Boolean; +var monarchEnabled: Boolean; +var previousMonarchMode: Boolean; +var previousToggleState: Boolean; +var displayInvisibleButtons: Boolean; - } else { - /** Handle "Load in Tactile Authoring Tool" flow */ - const tatStorageData: TatStorageData = { - channelId: items["monarchChannelId"], - graphicTitle: items["monarchTitle"], - secretKey: items["monarchSecretKey"], - graphicBlob: query["graphic"], - coordinates: query["coordinates"] && JSON.stringify(query["coordinates"]), - placeID: query["placeID"], - } - let tatTargetUrl = `${serverUrl}/tat/`; - tatTargetUrl = tatTargetUrl.replace(/([^:]\/)\/+/g, "$1"); - let tabs = await browser.tabs.query({ url: tatTargetUrl }); - - /** encrypt data before storing in local storage */ - let encryptedSvgData = await encryptData(svgDom, encryptionKey); - let encryptedTatData: TatStorageData = {channelId:"", graphicTitle:"", secretKey:""}; - for (let key of Object.keys(tatStorageData)) { - let stringToEncrypt = tatStorageData[key as keyof TatStorageData]; - if (stringToEncrypt){ - encryptedTatData[key as keyof TatStorageData] = await encryptData(tatStorageData[key as keyof TatStorageData], encryptionKey); - } - } - - if (tabs && tabs.length > 0) { - let existingTab = tabs[0]; - browser.scripting.executeScript({ - target: { tabId: existingTab.id || 0 }, - func: saveToLocalStorage, - args: [encryptedSvgData, encryptedTatData, existingTab] - }); - browser.tabs.update(existingTab.id, { active: true }); - } - else { - let authoringTool = browser.tabs.create({ - url: tatTargetUrl - }); - authoringTool.then((tab) => { - browser.scripting.executeScript({ - target: { tabId: tab.id || 0 }, - func: saveToLocalStorage, - args: [encryptedSvgData, encryptedTatData] - }); - }, (error) => { console.log(error) }); - } - } -} +// ============================================================================ +// Message handling +// ============================================================================ -async function handleMessage(p: Runtime.Port, message: any) { +/** + * Handles messages from content scripts + * + * @param p - The port that the message was received on + * @param message - The message object + */ +async function handleMessage(p: Runtime.Port, message: Message) { console.debug("Handling message", message); let query: IMAGERequest | undefined; - graphicUrl = message["sourceURL"]; - switch (message["type"]) { - case "info": - const value = responseMap.get(message["request_uuid"]); + if ('sourceURL' in message) { + graphicUrl = message.sourceURL; + } + + // Use type assertion with switch statement for type narrowing + switch (message.type) { + case MESSAGE_TYPES.INFO: { + const infoMessage = message as InfoMessage; + const value = responseMap.get(infoMessage.request_uuid); p.postMessage(value); - responseMap.delete(message["request_uuid"]); + responseMap.delete(infoMessage.request_uuid); break; - case "resource": - query = await generateQuery(message); + } + case MESSAGE_TYPES.RESOURCE: { + const resourceMessage = message as ResourceMessage; + query = await generateQuery(resourceMessage); break; - case "localResource": - query = await generateLocalQuery(message); + } + case MESSAGE_TYPES.LOCAL_RESOURCE: { + const localResourceMessage = message as LocalResourceMessage; + query = await generateLocalQuery(localResourceMessage); break; - case "mapResource": + } + case MESSAGE_TYPES.MAP_RESOURCE: { console.debug("Generating map query"); - query = await generateMapQuery(message); + const mapResourceMessage = message as MapResourceMessage; + if (mapResourceMessage.context && mapResourceMessage.coordinates) { + query = await generateMapQuery({ + context: mapResourceMessage.context, + coordinates: mapResourceMessage.coordinates + }); + } break; - case "settingsSaved": + } + case MESSAGE_TYPES.SETTINGS_SAVED: { await updateDebugContextMenu(); break; - case "chartResource": - query = await generateChartQuery(message); + } + case MESSAGE_TYPES.CHART_RESOURCE: { + const chartResourceMessage = message as ChartResourceMessage; + query = await generateChartQuery(chartResourceMessage); break; - case "mapSearch": + } + case MESSAGE_TYPES.MAP_SEARCH: { console.debug("Generating map query"); - query = await generateMapSearchQuery(message); + const mapSearchMessage = message as MapSearchMessage; + if (mapSearchMessage.context && mapSearchMessage.placeID) { + query = await generateMapSearchQuery({ + context: mapSearchMessage.context, + placeID: mapSearchMessage.placeID + }); + } break; - case "dataFromAuthoringTool": + } + case MESSAGE_TYPES.DATA_FROM_AUTHORING_TOOL: { console.debug("Data received from Authoring Tool"); + const dataFromAuthoringToolMessage = message as DataFromAuthoringToolMessage; let items = await getAllStorageSyncData(); const encryptionKey = items["monarchEncryptionKey"]; const [title, channelId, secretKey] = await Promise.all([ - decryptData(message.storageData.graphicTitle, encryptionKey), - decryptData(message.storageData.channelId, encryptionKey), - decryptData(message.storageData.secretKey, encryptionKey) + decryptData(dataFromAuthoringToolMessage.storageData.graphicTitle, encryptionKey), + decryptData(dataFromAuthoringToolMessage.storageData.channelId, encryptionKey), + decryptData(dataFromAuthoringToolMessage.storageData.secretKey, encryptionKey) ]); //console.log("Decrypted Data", title, channelId, secretKey); await browser.storage.sync.set({ @@ -248,9 +158,11 @@ async function handleMessage(p: Runtime.Port, message: any) { monarchSecretKey: secretKey }); break; - case "checkImageSize": + } + case MESSAGE_TYPES.CHECK_IMAGE_SIZE: { console.debug("Checking Image Size"); - let blob = await fetch(message["sourceURL"]).then(r => r.blob()); + const checkImageSizeMessage = message as CheckImageSizeMessage; + let blob = await fetch(checkImageSizeMessage.sourceURL).then(r => r.blob()); const blobFile = new File([blob], "buffer.jpg", { type: blob.type }); const sizeMb = blobFile.size / 1024 / 1024; console.debug(`originalFile size ${sizeMb} MB`); @@ -263,9 +175,8 @@ async function handleMessage(p: Runtime.Port, message: any) { let currentTab = tabs[0]; //console.debug("current Tab", currentTab); if (currentTab.id) { - ports[currentTab.id].postMessage({ - "type": "compressImage", + "type": MESSAGE_TYPES.COMPRESS_IMAGE, "tabId": currentTab.id, "graphicBlobStr": graphicBlobStr, "blobType": blob.type @@ -276,22 +187,38 @@ async function handleMessage(p: Runtime.Port, message: any) { return; } else { - const graphicBlobStr = await blobToBase64(blob); - message["graphicBlob"] = graphicBlobStr; - query = await generateQuery(message); + const graphicBlobStr = await blobToBase64(blob) as string; + if (checkImageSizeMessage.context && checkImageSizeMessage.url && checkImageSizeMessage.dims) { + query = await generateQuery({ + context: checkImageSizeMessage.context, + url: checkImageSizeMessage.url, + dims: checkImageSizeMessage.dims, + graphicBlob: graphicBlobStr + }); + } } break; - case "handleMapMonarchOptions": { + } + case MESSAGE_TYPES.HANDLE_MAP_MONARCH_OPTIONS: { console.debug("Handling map monarch options"); getCurrentTabInfo(); + break; } default: - console.debug(message["type"]); + console.debug(message.type); } if (query) { - console.debug("Value of toRender ", message["toRender"]); - switch (message["toRender"]) { - case "full": + // Determine toRender value based on message type + let toRender: typeof RENDER_TYPES.FULL | typeof RENDER_TYPES.PREPROCESS | typeof RENDER_TYPES.NONE | undefined; + + if ('toRender' in message) { + toRender = (message as any).toRender; + } + + console.debug("Value of toRender ", toRender); + if (toRender) { + switch (toRender) { + case RENDER_TYPES.FULL: { let items = await getAllStorageSyncData(); if (items["mcgillServer"] === true) { @@ -303,9 +230,13 @@ async function handleMessage(p: Runtime.Port, message: any) { } // Check if we have specific tactile rendering data and can skip server call - if (message["redirectToTAT"] && message["specificTactileRendering"]) { + const hasRedirectToTAT = 'redirectToTAT' in message && 'specificTactileRendering' in message && + message.redirectToTAT && message.specificTactileRendering; + + if (hasRedirectToTAT) { console.log("Skipping server call - using provided tactile rendering data"); - let tactileSvgGraphic = message["specificTactileRendering"].data.graphic as string; + const specificMessage = message as any; + let tactileSvgGraphic = specificMessage.specificTactileRendering.data.graphic as string; await processTactileRendering(tactileSvgGraphic, query, message, serverUrl); return; } @@ -361,14 +292,18 @@ async function handleMessage(p: Runtime.Port, message: any) { if (json["renderings"].length > 0) { console.log("Inside JSON Renderings"); console.log("message", message); - if (message["redirectToTAT"]) { + const hasRedirectToTAT = 'redirectToTAT' in message && message.redirectToTAT; + + if (hasRedirectToTAT) { console.log("Received TAT data in background script"); let tactileSvgGraphic: string; // Check if specific tactile rendering is provided - if (message["specificTactileRendering"]) { + const specificMessage = message as any; + + if ('specificTactileRendering' in message && specificMessage.specificTactileRendering) { console.log("Using specific tactile rendering provided"); - tactileSvgGraphic = message["specificTactileRendering"].data.graphic as string; + tactileSvgGraphic = specificMessage.specificTactileRendering.data.graphic as string; } else { // Fallback to existing behavior - use first tactile rendering from server response console.log("Using first tactile rendering from server response"); @@ -403,12 +338,12 @@ async function handleMessage(p: Runtime.Port, message: any) { if (renderingsPanel !== undefined) { try { await windowsPanel ? browser.windows.remove(renderingsPanel.id!) : browser.tabs.remove(renderingsPanel.id!); - renderingsPanel = await createPanel(query); + renderingsPanel = await createPanel(query, graphicUrl); } catch { - renderingsPanel = await createPanel(query); + renderingsPanel = await createPanel(query, graphicUrl); } } else { - renderingsPanel = await createPanel(query); + renderingsPanel = await createPanel(query, graphicUrl); } // How to handle if request_uuid was undefined?? } @@ -430,7 +365,7 @@ async function handleMessage(p: Runtime.Port, message: any) { } break; - case "preprocess": + case RENDER_TYPES.PREPROCESS: { let items = await getAllStorageSyncData(); if (items["mcgillServer"] === true) { @@ -453,7 +388,7 @@ async function handleMessage(p: Runtime.Port, message: any) { } } break; - case "none": + case RENDER_TYPES.NONE: { try { await browser.downloads.download({ @@ -469,23 +404,11 @@ async function handleMessage(p: Runtime.Port, message: any) { } } } -function blobToBase64(blob: Blob) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result as string); - reader.onerror = () => reject(reader.error); - reader.readAsDataURL(blob); - }); -}; - -async function createOffscreen() { - // @ts-ignore - await browser.offscreen.createDocument({ - url: 'offscreen.html', - reasons: ['BLOBS'], - justification: 'keep service worker running', - }).catch(() => { }); } +// ============================================================================ +// Context menu management +// ============================================================================ + //browser.runtime.onInstalled.addListener(() => {createOffscreen();}); browser.runtime.onStartup.addListener(() => { createOffscreen(); }); // a message from an offscreen document every 50 second resets the inactivity timer @@ -494,6 +417,12 @@ browser.runtime.onMessage.addListener(msg => { }); +/** + * Updates the context menu based on user settings + * + * This function reads settings from storage and updates the context menu items accordingly. + * It also updates the UI elements in all open tabs to reflect the current settings. + */ async function updateDebugContextMenu() { let items = await getAllStorageSyncData(); //console.log("Saved Items", items); @@ -513,11 +442,11 @@ async function updateDebugContextMenu() { if (tabs[i].url && !tabs[i].url?.startsWith("chrome://") && tabs[i].id) { let tabId = tabs[i].id || 0; if(ports[tabId]){ - ports[tabId].postMessage({ - "type": "handleInvisibleButton", - "displayInvisibleButtons": displayInvisibleButtons, - "monarchEnabled": monarchEnabled - }); + ports[tabId].postMessage({ + "type": MESSAGE_TYPES.HANDLE_INVISIBLE_BUTTON, + "displayInvisibleButtons": displayInvisibleButtons, + "monarchEnabled": monarchEnabled + }); } } } @@ -585,20 +514,16 @@ async function updateDebugContextMenu() { }, function () { }); } -function toggleMapOptions(showDebugOptions: Boolean, monarchEnabled: Boolean) { - let mapButtonContainer = document.getElementById("map-button-container"); - let mapSelectContainer = document.getElementById("map-select-container"); - if (mapButtonContainer && mapSelectContainer) { - if (monarchEnabled) { - mapSelectContainer.style.display = "flex"; - mapButtonContainer.style.display = "none"; - } else { - mapSelectContainer.style.display = "none"; - mapButtonContainer.style.display = "flex"; - } - } -}; +// ============================================================================ +// Connection management +// ============================================================================ + +/** + * Stores a connection to a content script + * + * @param p - The port to store + */ function storeConnection(p: Runtime.Port) { //console.debug("store Connection"); let id = p.sender?.tab?.id; @@ -623,7 +548,12 @@ function storeConnection(p: Runtime.Port) { //console.debug("Store Connection ports", ports); } -/*Enable the context menu options*/ +/** + * Enables the context menu options based on current settings + * + * Updates the context menu items to be enabled and sets their titles + * according to the current extension settings. + */ function enableContextMenu() { browser.contextMenus.update("mwe-item", { enabled: true, @@ -650,7 +580,9 @@ function enableContextMenu() { } } -/*Disable the context menu options*/ +/** + * Disables the context menu options + */ function disableContextMenu() { browser.contextMenus.update("mwe-item", { enabled: false }); if (showDebugOptions) { @@ -663,7 +595,15 @@ function disableContextMenu() { } } -/*Handle the context menu items based on the status of the DOM*/ +/** + * Handle the context menu items based on the status of the DOM + * + * Updates context menu and tab-specific settings when a tab's status changes. + * Enables or disables context menu items and updates UI elements accordingly. + * + * @param tabId - The ID of the tab being updated + * @param changeInfo - Information about the change + */ function handleUpdated(tabId: any, changeInfo: any) { if (changeInfo.status == "complete") { // console.log("handleUpdated called"); @@ -680,7 +620,7 @@ function handleUpdated(tabId: any, changeInfo: any) { // handle invisible buttons ports[tabId].postMessage({ - "type": "handleInvisibleButton", + "type": MESSAGE_TYPES.HANDLE_INVISIBLE_BUTTON, "displayInvisibleButtons": displayInvisibleButtons, "monarchEnabled": monarchEnabled }); @@ -690,7 +630,12 @@ function handleUpdated(tabId: any, changeInfo: any) { } } -/*Handle context menu items based on DOM for the active Tab*/ +/** + * Handle context menu items based on DOM for the active Tab + * + * Retrieves information about the currently active tab and updates + * the context menu items accordingly by calling handleUpdated. + */ function getCurrentTabInfo() { let currentTab = browser.tabs.query({ currentWindow: true, active: true }); currentTab.then( @@ -701,28 +646,24 @@ function getCurrentTabInfo() { ); } +// ============================================================================ +// Event listeners +// ============================================================================ + browser.runtime.onConnect.addListener(storeConnection); browser.tabs.onUpdated.addListener(handleUpdated); browser.tabs.onActivated.addListener(getCurrentTabInfo); -function onCreated(): void { - if (browser.runtime.lastError) { - //console.error(browser.runtime.lastError); - } -} - -// browser.storage.sync.set({ -// mweItem: "mwe-item" -// }) - -var showDebugOptions: Boolean; - -var monarchEnabled: Boolean; -var previousMonarchMode: Boolean; -var previousToggleState: Boolean; - -var displayInvisibleButtons : Boolean; +// ============================================================================ +// Initialization +// ============================================================================ +/** + * Initialize extension settings from storage + * + * Loads user preferences and settings from storage and sets up + * the context menu items accordingly. + */ getAllStorageSyncData().then((items) => { showDebugOptions = items["developerMode"]; monarchEnabled = items["monarchEnabled"]; @@ -756,6 +697,15 @@ getAllStorageSyncData().then((items) => { } }); +/** + * Handle extension installation and updates + * + * Sets up the extension when it's first installed or updated. + * Creates necessary context menu items and shows the first launch page + * for new installations. + * + * @param object - Installation details including the reason for installation + */ browser.runtime.onInstalled.addListener(function (object) { createOffscreen(); let internalUrl = chrome.runtime.getURL("firstLaunch/firstLaunch.html"); @@ -793,6 +743,14 @@ browser.runtime.onInstalled.addListener(function (object) { }); +/** + * Handle keyboard commands + * + * Listens for keyboard shortcuts and performs the appropriate action. + * Currently handles opening the launchpad. + * + * @param command - The command that was triggered + */ browser.commands.onCommand.addListener(async (command) => { console.debug(`Command: ${command}`); try{ @@ -813,6 +771,15 @@ browser.commands.onCommand.addListener(async (command) => { }); +/** + * Handle context menu clicks + * + * Processes clicks on context menu items and sends appropriate messages + * to the content script in the tab where the click occurred. + * + * @param info - Information about the context menu item that was clicked + * @param tab - The tab where the context menu was clicked + */ browser.contextMenus.onClicked.addListener((info, tab) => { //console.debug("Tab", tab); //console.debug("ports", ports); @@ -820,30 +787,30 @@ browser.contextMenus.onClicked.addListener((info, tab) => { // Request image from page if (info.menuItemId === "mwe-item") { ports[tab.id].postMessage({ - "type": "resourceRequest", + "type": MESSAGE_TYPES.RESOURCE_REQUEST, "tabId": tab.id, }); } else if (info.menuItemId === "mwe-item-tat") { ports[tab.id].postMessage({ - "type": "tactileAuthoringTool", + "type": MESSAGE_TYPES.TACTILE_AUTHORING_TOOL, "tabId": tab.id, }); } else if(info.menuItemId === "mwe-item-monarch"){ ports[tab.id].postMessage({ - "type": "sendToMonarch", + "type": MESSAGE_TYPES.SEND_TO_MONARCH, "tabId": tab.id, }); } else if (info.menuItemId === "preprocess-only") { ports[tab.id].postMessage({ - "type": "preprocessRequest", + "type": MESSAGE_TYPES.PREPROCESS_REQUEST, "tabId": tab.id }); } else if (info.menuItemId === "request-only") { ports[tab.id].postMessage({ - "type": "onlyRequest", + "type": MESSAGE_TYPES.ONLY_REQUEST, "tabId": tab.id }); } @@ -858,15 +825,3 @@ browser.contextMenus.onClicked.addListener((info, tab) => { }); } }); - -function createPanel(query: IMAGERequest) { - let window = windowsPanel ? browser.windows.create({ - type: "normal", - url: `info/info.html?uuid=${query["request_uuid"]}&graphicUrl=${graphicUrl}&dimensions=${query["dimensions"]}`, - height: 1080, - width: 1920 - }) : browser.tabs.create({ - url: `info/info.html?uuid=${query["request_uuid"]}&graphicUrl=${graphicUrl}&dimensions=${query["dimensions"]}`, - }); - return window; -} diff --git a/src/content.ts b/src/content.ts index 3f8cea67..f4c23c71 100644 --- a/src/content.ts +++ b/src/content.ts @@ -18,6 +18,7 @@ import imageCompression from "browser-image-compression"; import browser from "webextension-polyfill"; import { processMAPImages, processMaps } from './maps/maps-utils'; import { getContext, showImageOptionsModal } from "./utils"; +import { MESSAGE_TYPES, RENDER_TYPES } from "./types/message-types.constants"; var selectedElement: HTMLElement | null = null; @@ -53,26 +54,26 @@ window.addEventListener("message", function (event) { //console.log(" Message from Authoring Tool", JSON.parse(event.data.storageData)); let storageData = JSON.parse(event.data.storageData); port.postMessage({ - type: "dataFromAuthoringTool", + type: MESSAGE_TYPES.DATA_FROM_AUTHORING_TOOL, storageData: storageData }) } if (event.data.messageFrom && (event.data.messageFrom == "imageCharts")) { port.postMessage({ - "type": "chartResource", + "type": MESSAGE_TYPES.CHART_RESOURCE, "highChartsData": event.data.charts || null, - "toRender": "full" + "toRender": RENDER_TYPES.FULL }); } if (event.data.messageFrom && (event.data.messageFrom == "screenReaderGraphic")) { let imageData = event.data.imageData; port.postMessage({ - "type": "checkImageSize", + "type": MESSAGE_TYPES.CHECK_IMAGE_SIZE, "context": "", "dims": [imageData.naturalWidth, imageData.naturalHeight], "url": window.location.href, "sourceURL": imageData.sourceURL, - "toRender": "full", + "toRender": RENDER_TYPES.FULL, "redirectToTAT": event.data.redirectToTAT, "sendToMonarch": event.data.sendToMonarch, }); @@ -125,7 +126,7 @@ port.onMessage.addListener(async message => { const serializer = new XMLSerializer(); let imageElement: HTMLImageElement; if(message && message.status == "ping" ) return; - if (message["type"] === "handleInvisibleButton") { + if (message["type"] === MESSAGE_TYPES.HANDLE_INVISIBLE_BUTTON) { monarchEnabled = message["monarchEnabled"]; if (monarchEnabled) { @@ -149,26 +150,26 @@ port.onMessage.addListener(async message => { const scheme = imageElement.currentSrc.split(":")[0]; //console.debug("message received", message); let toRender = ""; - if (message["type"] === "resourceRequest") { - toRender = "full"; + if (message["type"] === MESSAGE_TYPES.RESOURCE_REQUEST) { + toRender = RENDER_TYPES.FULL; } - if (message["type"] === "preprocessRequest") { - toRender = "preprocess"; + if (message["type"] === MESSAGE_TYPES.PREPROCESS_REQUEST) { + toRender = RENDER_TYPES.PREPROCESS; } - if (message["type"] === "onlyRequest") { - toRender = "none" + if (message["type"] === MESSAGE_TYPES.ONLY_REQUEST) { + toRender = RENDER_TYPES.NONE; } - if(message["type"] === "tactileAuthoringTool"){ - toRender = "full"; + if(message["type"] === MESSAGE_TYPES.TACTILE_AUTHORING_TOOL){ + toRender = RENDER_TYPES.FULL; message["redirectToTAT"] = true; message["sendToMonarch"] = false; } - if(message["type"] === "sendToMonarch"){ - toRender = "full"; + if(message["type"] === MESSAGE_TYPES.SEND_TO_MONARCH){ + toRender = RENDER_TYPES.FULL; message["redirectToTAT"] = true; message["sendToMonarch"] = true; } - if (message["type"] === "compressImage") { + if (message["type"] === MESSAGE_TYPES.COMPRESS_IMAGE) { console.debug("compressing inside content script"); //let blobFile = new File([new Blob(message["graphicBlobStr"])], "buffer.jpg", {type: message["blobType"]}); let blob = base64toBlob(message["graphicBlobStr"], message["blobType"]); @@ -183,19 +184,19 @@ port.onMessage.addListener(async message => { let graphicBlob = compressedBlob; const graphicBlobStr = await blobToBase64(graphicBlob); port.postMessage({ - "type": "resource", + "type": MESSAGE_TYPES.RESOURCE, "context": selectedElement ? getContext(selectedElement) : null, "dims": [imageElement.naturalWidth, imageElement.naturalHeight], "url": window.location.href, "sourceURL": imageElement.currentSrc, - "toRender": toRender || "full", + "toRender": toRender || RENDER_TYPES.FULL, "graphicBlob": graphicBlobStr }); return; } if (scheme === "http" || scheme === "https" || scheme === "data") { port.postMessage({ - "type": "checkImageSize", + "type": MESSAGE_TYPES.CHECK_IMAGE_SIZE, "context": selectedElement ? getContext(selectedElement) : null, "dims": [imageElement.naturalWidth, imageElement.naturalHeight], "url": window.location.href, @@ -221,7 +222,7 @@ port.onMessage.addListener(async message => { }); }).then(image => { port.postMessage({ - "type": "localResource", + "type": MESSAGE_TYPES.LOCAL_RESOURCE, "context": selectedElement ? serializer.serializeToString(selectedElement) : null, "dims": [imageElement.naturalWidth, imageElement.naturalHeight], "image": image, @@ -283,12 +284,12 @@ document.addEventListener('keydown', function(event) { showImageOptionsModal(selectedElement, imageElement, port); } else { port.postMessage({ - "type": "checkImageSize", + "type": MESSAGE_TYPES.CHECK_IMAGE_SIZE, "context": selectedElement ? getContext(selectedElement) : null, "dims": [imageElement.naturalWidth, imageElement.naturalHeight], "url": window.location.href, "sourceURL": imageElement.currentSrc, - "toRender": "full" + "toRender": RENDER_TYPES.FULL }); } } diff --git a/src/types/background.types.ts b/src/types/background.types.ts new file mode 100644 index 00000000..c6d45f16 --- /dev/null +++ b/src/types/background.types.ts @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2021 IMAGE Project, Shared Reality Lab, McGill University + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * and our Additional Terms along with this program. + * If not, see . + */ +import { Runtime } from "webextension-polyfill"; +import { IMAGERequest } from "./request.schema"; +import { IMAGEResponse } from "./response.schema"; +import { TatStorageData } from "../monarch/types"; +import { MESSAGE_TYPES, RENDER_TYPES } from "./message-types.constants"; + +/** + * Base interface for all message types + */ +export interface BaseMessage { + type: string; + tabId?: number; +} + +/** + * Interface for info message type + */ +export interface InfoMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.INFO; + request_uuid: string; +} + +/** + * Interface for resource message type + */ +export interface ResourceMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.RESOURCE; + context: string; + url: string; + dims: [number, number]; + graphicBlob: string; + sourceURL: string; + toRender: typeof RENDER_TYPES.FULL | typeof RENDER_TYPES.PREPROCESS | typeof RENDER_TYPES.NONE; + redirectToTAT?: boolean; + specificTactileRendering?: SpecificTactileRendering; + sendToMonarch?: boolean; +} + +/** + * Interface for local resource message type + */ +export interface LocalResourceMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.LOCAL_RESOURCE; + context: string; + dims: [number, number]; + image: string; + graphicBlob: string; + toRender: typeof RENDER_TYPES.FULL | typeof RENDER_TYPES.PREPROCESS | typeof RENDER_TYPES.NONE; + redirectToTAT?: boolean; + specificTactileRendering?: SpecificTactileRendering; + sendToMonarch?: boolean; +} + +/** + * Interface for map resource message type + */ +export interface MapResourceMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.MAP_RESOURCE; + toRender: typeof RENDER_TYPES.FULL | typeof RENDER_TYPES.PREPROCESS | typeof RENDER_TYPES.NONE; + context?: string; + coordinates?: [number, number]; + redirectToTAT?: boolean; + specificTactileRendering?: SpecificTactileRendering; + sendToMonarch?: boolean; +} + +/** + * Interface for settings saved message type + */ +export interface SettingsSavedMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.SETTINGS_SAVED; +} + +/** + * Interface for chart resource message type + */ +export interface ChartResourceMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.CHART_RESOURCE; + highChartsData: { [k: string]: unknown }; + toRender: typeof RENDER_TYPES.FULL | typeof RENDER_TYPES.PREPROCESS | typeof RENDER_TYPES.NONE; + redirectToTAT?: boolean; + specificTactileRendering?: SpecificTactileRendering; + sendToMonarch?: boolean; +} + +/** + * Interface for map search message type + */ +export interface MapSearchMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.MAP_SEARCH; + toRender: typeof RENDER_TYPES.FULL | typeof RENDER_TYPES.PREPROCESS | typeof RENDER_TYPES.NONE; + context?: string; + placeID?: string; + redirectToTAT?: boolean; + specificTactileRendering?: SpecificTactileRendering; + sendToMonarch?: boolean; +} + +/** + * Interface for data from authoring tool message type + */ +export interface DataFromAuthoringToolMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.DATA_FROM_AUTHORING_TOOL; + storageData: { + graphicTitle: string; + channelId: string; + secretKey: string; + }; +} + +/** + * Interface for check image size message type + */ +export interface CheckImageSizeMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.CHECK_IMAGE_SIZE; + sourceURL: string; + toRender: typeof RENDER_TYPES.FULL | typeof RENDER_TYPES.PREPROCESS | typeof RENDER_TYPES.NONE; + context?: string; + url?: string; + dims?: [number, number]; + graphicBlob?: string; + redirectToTAT?: boolean; + specificTactileRendering?: SpecificTactileRendering; + sendToMonarch?: boolean; +} + +/** + * Interface for handle map monarch options message type + */ +export interface HandleMapMonarchOptionsMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.HANDLE_MAP_MONARCH_OPTIONS; +} + +/** + * Interface for tactile authoring tool message type + */ +export interface TactileAuthoringToolMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.TACTILE_AUTHORING_TOOL; +} + +/** + * Interface for send to monarch message type + */ +export interface SendToMonarchMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.SEND_TO_MONARCH; +} + +/** + * Interface for preprocess request message type + */ +export interface PreprocessRequestMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.PREPROCESS_REQUEST; +} + +/** + * Interface for only request message type + */ +export interface OnlyRequestMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.ONLY_REQUEST; +} + +/** + * Interface for compress image message type + */ +export interface CompressImageMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.COMPRESS_IMAGE; + graphicBlobStr: string; + blobType: string; +} + +/** + * Interface for handle invisible button message type + */ +export interface HandleInvisibleButtonMessage extends BaseMessage { + type: typeof MESSAGE_TYPES.HANDLE_INVISIBLE_BUTTON; + displayInvisibleButtons: boolean; + monarchEnabled: boolean; +} + +/** + * Interface for ping message type + */ +export interface PingMessage extends BaseMessage { + status: "ping"; +} + +/** + * Union type for all message types + */ +export type Message = + | InfoMessage + | ResourceMessage + | LocalResourceMessage + | MapResourceMessage + | SettingsSavedMessage + | ChartResourceMessage + | MapSearchMessage + | DataFromAuthoringToolMessage + | CheckImageSizeMessage + | HandleMapMonarchOptionsMessage + | TactileAuthoringToolMessage + | SendToMonarchMessage + | PreprocessRequestMessage + | OnlyRequestMessage + | CompressImageMessage + | HandleInvisibleButtonMessage + | PingMessage; + +/** + * Interface for response map entry + */ +export interface ResponseMapEntry { + server: RequestInfo; + response: IMAGEResponse; + request: IMAGERequest; +} + +/** + * Interface for ports object + */ +export interface PortsMap { + [key: number]: Runtime.Port; +} + +/** + * Interface for storage sync data + */ +// export interface StorageSyncData { +// mcgillServer?: boolean; +// inputUrl?: string; +// customServer?: boolean; +// monarchTitle?: string; +// monarchSecretKey?: string; +// monarchChannelId?: string; +// monarchEncryptionKey?: string; +// developerMode?: boolean; +// previousToggleState?: boolean; +// processItem?: string; +// requestItem?: string; +// monarchEnabled?: boolean; +// previousMonarchMode?: boolean; +// displayInvisibleButtons: boolean; +// } + +/** + * Interface for monarch request body + */ +// export interface MonarchRequestBody { +// data: string; +// layer: string; +// title: string; +// secret: string; +// graphicBlob?: string; +// coordinates?: string; +// placeID?: string; +// } + +// /** +// * Interface for monarch response +// */ +// export interface MonarchResponse { +// id: string; +// secret: string; +// } + +/** + * Interface for specific tactile rendering + */ +export interface SpecificTactileRendering { + data: { + graphic: string; + [key: string]: unknown; + }; + [key: string]: unknown; +} + +// Type guards are not needed when using switch-case statements with discriminated unions +// TypeScript will automatically narrow the type within each case block diff --git a/src/types/message-types.constants.ts b/src/types/message-types.constants.ts new file mode 100644 index 00000000..3e913e22 --- /dev/null +++ b/src/types/message-types.constants.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 IMAGE Project, Shared Reality Lab, McGill University + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * and our Additional Terms along with this program. + * If not, see . + */ + +/** + * Constants for message types to avoid string literals in switch statements + */ +export const MESSAGE_TYPES = { + INFO: "info" as const, + RESOURCE: "resource" as const, + RESOURCE_REQUEST: "resourceRequest" as const, + LOCAL_RESOURCE: "localResource" as const, + MAP_RESOURCE: "mapResource" as const, + SETTINGS_SAVED: "settingsSaved" as const, + CHART_RESOURCE: "chartResource" as const, + MAP_SEARCH: "mapSearch" as const, + DATA_FROM_AUTHORING_TOOL: "dataFromAuthoringTool" as const, + CHECK_IMAGE_SIZE: "checkImageSize" as const, + HANDLE_MAP_MONARCH_OPTIONS: "handleMapMonarchOptions" as const, + TACTILE_AUTHORING_TOOL: "tactileAuthoringTool" as const, + SEND_TO_MONARCH: "sendToMonarch" as const, + PREPROCESS_REQUEST: "preprocessRequest" as const, + ONLY_REQUEST: "onlyRequest" as const, + COMPRESS_IMAGE: "compressImage" as const, + HANDLE_INVISIBLE_BUTTON: "handleInvisibleButton" as const, + PING: "ping" as const +}; + +/** + * Constants for render types to avoid string literals in switch statements + */ +export const RENDER_TYPES = { + FULL: "full" as const, + PREPROCESS: "preprocess" as const, + NONE: "none" as const +}; From f4205cfbe5256f6aa625107b497ec797d8dffaf2 Mon Sep 17 00:00:00 2001 From: jaydeepsingh25 Date: Sun, 14 Sep 2025 00:34:33 -0700 Subject: [PATCH 2/2] fix: add documentation --- src/README.md | 31 ++++++++++++++++++++++++ src/_locales/README.md | 24 ++++++++++++++++++ src/audio/README.md | 16 ++++++++++++ src/charts/README.md | 27 +++++++++++++++++++++ src/errors/README.md | 29 ++++++++++++++++++++++ src/feedback/README.md | 30 +++++++++++++++++++++++ src/firstLaunch/README.md | 26 ++++++++++++++++++++ src/info/README.md | 32 ++++++++++++++++++++++++ src/launchpad/README.md | 33 +++++++++++++++++++++++++ src/maps/README.md | 32 ++++++++++++++++++++++++ src/monarch/README.md | 34 ++++++++++++++++++++++++++ src/options/README.md | 38 +++++++++++++++++++++++++++++ src/progressBar/README.md | 33 +++++++++++++++++++++++++ src/types/README.md | 34 ++++++++++++++++++++++++++ src/types/background.types.ts | 43 --------------------------------- src/types/canvas-circle.d.ts | 9 ------- src/types/canvas-rectangle.d.ts | 6 ----- src/types/vector.d.ts | 4 --- 18 files changed, 419 insertions(+), 62 deletions(-) create mode 100644 src/README.md create mode 100644 src/_locales/README.md create mode 100644 src/audio/README.md create mode 100644 src/charts/README.md create mode 100644 src/errors/README.md create mode 100644 src/feedback/README.md create mode 100644 src/firstLaunch/README.md create mode 100644 src/info/README.md create mode 100644 src/launchpad/README.md create mode 100644 src/maps/README.md create mode 100644 src/monarch/README.md create mode 100644 src/options/README.md create mode 100644 src/progressBar/README.md create mode 100644 src/types/README.md delete mode 100644 src/types/canvas-circle.d.ts delete mode 100644 src/types/canvas-rectangle.d.ts delete mode 100644 src/types/vector.d.ts diff --git a/src/README.md b/src/README.md new file mode 100644 index 00000000..f00f7084 --- /dev/null +++ b/src/README.md @@ -0,0 +1,31 @@ +# IMAGE Browser Extension Source Code + +This directory contains the source code for the IMAGE browser extension, which is designed to make images, charts, and maps more accessible through various rendering techniques. + +## Directory Structure + +Each folder in this directory has its own README file with a brief description of its purpose and contents: + +- **_locales**: Localization files for different languages +- **audio**: Audio files for notifications and feedback +- **charts**: Utilities for handling chart data +- **errors**: Error handling and display +- **feedback**: User feedback collection +- **firstLaunch**: First-time user experience +- **info**: Rendering information display +- **launchpad**: Main interface for accessing features +- **maps**: Map processing utilities +- **monarch**: Tactile rendering features +- **options**: Extension preferences +- **progressBar**: Processing status indicators +- **types**: TypeScript type definitions + +## Core Files + +- **background.ts**: Main background script +- **content.ts**: Content script for web page interaction +- **config.ts**: Extension configuration +- **utils.ts**: General utility functions +- **manifest.json**: Extension metadata and permissions + +For more information about the IMAGE project, visit https://image.a11y.mcgill.ca diff --git a/src/_locales/README.md b/src/_locales/README.md new file mode 100644 index 00000000..a3f179f8 --- /dev/null +++ b/src/_locales/README.md @@ -0,0 +1,24 @@ +# Localization Files + +This directory contains localization files for the IMAGE browser extension, allowing the extension to be used in multiple languages. + +## Structure + +- **en/**: English localization + - **messages.json**: Contains all English text strings used in the extension +- **fr/**: French localization + - **messages.json**: Contains all French text strings used in the extension + +## Usage + +The extension uses the WebExtension i18n API to load the appropriate strings based on the user's browser language or their selected preference. Each string has a unique identifier and can include a description to provide context for translators. + +Example from messages.json: +```json +"extensionName": { + "message": "IMAGE Extension", + "description": "Extension name" +} +``` + +These strings are referenced in the code using `browser.i18n.getMessage("extensionName")`. diff --git a/src/audio/README.md b/src/audio/README.md new file mode 100644 index 00000000..dd103733 --- /dev/null +++ b/src/audio/README.md @@ -0,0 +1,16 @@ +# Audio Files + +This directory contains audio files used by the IMAGE browser extension for providing auditory feedback to users during various stages of the rendering process. + +## Files + +- **IMAGE-Error.mp3**: Played when an error occurs during the rendering process +- **IMAGE-Processing.mp3**: Played when the extension is processing an image +- **IMAGE-RequestSent.mp3**: Played when a request is sent to the IMAGE server +- **IMAGE-ResultsArrived.mp3**: Played when results arrive from the IMAGE server + +## Usage + +These audio files provide important non-visual cues to users, particularly those who rely on screen readers or other assistive technologies. They help users understand the current state of the extension without requiring visual feedback. + +The audio files are played at appropriate times during the extension's operation, such as when a request is sent to the server or when results are received. diff --git a/src/charts/README.md b/src/charts/README.md new file mode 100644 index 00000000..d8036425 --- /dev/null +++ b/src/charts/README.md @@ -0,0 +1,27 @@ +# Charts Utilities + +This directory contains utilities for handling and processing chart data in the IMAGE browser extension. + +## Files + +- **charts-utils.js**: Contains functions for extracting data from HighCharts objects to prepare them for rendering + +## Functionality + +The utilities in this folder are responsible for: + +1. **Data Extraction**: Extracting relevant data from HighCharts objects, including: + - Chart title and subtitle + - Axis information (titles, types, ranges) + - Series data (names, types, data points) + - Accessibility text + +2. **Data Sanitization**: Ensuring that extracted data is properly formatted and sanitized for processing + +3. **Chart Analysis**: Analyzing chart structure to determine the type of chart (pie, line, area, etc.) + +## Usage + +These utilities are used when a user requests an accessible rendering of a chart on a webpage. The extension identifies charts created with HighCharts, extracts the relevant data using these utilities, and sends the data to the IMAGE server for processing. + +Currently, the extension supports a subset of HighCharts formats, including pie charts and single-trend area or line charts. diff --git a/src/errors/README.md b/src/errors/README.md new file mode 100644 index 00000000..a7271828 --- /dev/null +++ b/src/errors/README.md @@ -0,0 +1,29 @@ +# Error Handling + +This directory contains files related to error handling and display in the IMAGE browser extension. + +## Files + +- **errors.css**: Styles for error pages +- **errors.ts**: JavaScript for error handling and display +- **http_error.html**: HTML template for HTTP errors (e.g., server connection issues) +- **no_renderings.html**: HTML template for when no renderings are available for an image + +## Functionality + +The files in this directory are responsible for: + +1. **Error Detection**: Identifying various error conditions that may occur during the extension's operation +2. **User Notification**: Displaying appropriate error messages to users +3. **Error Reporting**: Providing users with options to report errors to the IMAGE team +4. **Troubleshooting Guidance**: Offering suggestions for common issues that may prevent successful renderings + +## Usage + +When an error occurs during the extension's operation, such as a failure to connect to the IMAGE server or when no renderings are available for a particular image, the extension displays an appropriate error page to the user. + +These error pages include: +- A description of the error +- Possible reasons for the error +- Contact information for the IMAGE team +- Options for providing feedback about the error diff --git a/src/feedback/README.md b/src/feedback/README.md new file mode 100644 index 00000000..e4756b3d --- /dev/null +++ b/src/feedback/README.md @@ -0,0 +1,30 @@ +# Feedback Collection + +This directory contains files related to collecting user feedback in the IMAGE browser extension. + +## Files + +- **feedback.html**: HTML template for the feedback form +- **feedback.ts**: JavaScript for handling feedback submission + +## Functionality + +The files in this directory are responsible for: + +1. **Feedback Collection**: Providing a form for users to submit feedback about the extension +2. **Data Privacy**: Ensuring users understand how their feedback data will be used +3. **Consent Management**: Obtaining user consent for saving request data for analysis +4. **Form Submission**: Handling the submission of feedback to the IMAGE team + +## Usage + +Users can access the feedback form in several ways: +- From the rendering information page +- From error pages +- From the extension's options page + +The feedback form explains to users that normally, the IMAGE team does not keep or view information such as the graphic, URL, or user settings. However, users can consent to have this information saved to help improve the extension. + +The form emphasizes the importance of not including personal information in the feedback or in the graphics being shared. + +Once the user completes the form, they can submit it to the IMAGE team for review. diff --git a/src/firstLaunch/README.md b/src/firstLaunch/README.md new file mode 100644 index 00000000..dba7c9e1 --- /dev/null +++ b/src/firstLaunch/README.md @@ -0,0 +1,26 @@ +# First Launch Experience + +This directory contains files related to the first-time user experience for the IMAGE browser extension. + +## Files + +- **firstLaunch.css**: Styles for the first launch page +- **firstLaunch.html**: HTML template for the first launch page +- **firstLaunch.ts**: JavaScript for handling the first launch experience + +## Functionality + +The files in this directory are responsible for: + +1. **Welcome Message**: Displaying a welcome message to new users +2. **Onboarding**: Providing initial guidance on how to use the extension +3. **Documentation Links**: Offering links to more detailed documentation +4. **First-time Setup**: Helping users configure the extension for first use + +## Usage + +When a user installs the IMAGE browser extension for the first time, the extension automatically displays the first launch page. This page welcomes the user to the extension and provides a link to the quickstart guide at https://image.a11y.mcgill.ca/pages/quickstart.html. + +The first launch page is designed to be simple and straightforward, providing just enough information to get users started without overwhelming them with details. + +Users can close the first launch page by clicking the closing button, after which they can begin using the extension. diff --git a/src/info/README.md b/src/info/README.md new file mode 100644 index 00000000..5a35f195 --- /dev/null +++ b/src/info/README.md @@ -0,0 +1,32 @@ +# Rendering Information Display + +This directory contains files related to displaying information about renderings in the IMAGE browser extension. + +## Files + +- **info.html**: HTML template for the rendering information page +- **info.ts**: JavaScript for handling the rendering information display +- **info-utils.ts**: Utility functions for the rendering information display +- **info.css**: Styles for the rendering information page + +## Functionality + +The files in this directory are responsible for: + +1. **Rendering Display**: Showing the renderings generated by the IMAGE server +2. **Audio Playback**: Providing controls for playing audio renderings +3. **Text Display**: Showing text renderings for screen readers +4. **User Feedback**: Offering links to provide feedback about renderings +5. **Rendering Selection**: Allowing users to select different renderings if multiple are available + +## Usage + +When a user requests an accessible rendering of an image, chart, or map, and the IMAGE server successfully generates renderings, the extension displays these renderings on the information page. + +The information page includes: +- A title indicating that these are IMAGE extension renderings +- A container for displaying the renderings +- Controls for interacting with the renderings (e.g., playing audio) +- A footer with links to provide feedback + +Users can interact with the renderings in various ways, such as playing audio renderings, reading text renderings, or exploring tactile renderings if available. diff --git a/src/launchpad/README.md b/src/launchpad/README.md new file mode 100644 index 00000000..e242c161 --- /dev/null +++ b/src/launchpad/README.md @@ -0,0 +1,33 @@ +# Launchpad Interface + +This directory contains files related to the launchpad interface in the IMAGE browser extension. + +## Files + +- **launchpad.html**: HTML template for the launchpad interface +- **launchpad.ts**: JavaScript for handling the launchpad functionality +- **launchpad.css**: Styles for the launchpad interface + +## Functionality + +The files in this directory are responsible for: + +1. **Central Hub**: Providing a central interface for accessing various features of the extension +2. **Local File Processing**: Allowing users to upload and process local image files +3. **Tutorial Access**: Offering access to tutorials on how to use the extension +4. **Options Access**: Providing a link to the extension's options page +5. **Tactile Authoring**: Offering access to the tactile authoring tool + +## Usage + +Users can access the launchpad by using the keyboard shortcut "Alt+I", which is defined in the extension manifest. + +The launchpad interface includes: +- A title indicating that this is the IMAGE extension +- A button to access tutorials on how to use the extension +- A file input for uploading local image files +- A button to process uploaded image files +- A button to access the extension's options +- A button to access the tactile authoring tool + +The launchpad serves as a convenient entry point for users to access the various features of the extension without having to navigate through context menus or other interfaces. diff --git a/src/maps/README.md b/src/maps/README.md new file mode 100644 index 00000000..5a01c67a --- /dev/null +++ b/src/maps/README.md @@ -0,0 +1,32 @@ +# Maps Processing + +This directory contains utilities for handling and processing map data in the IMAGE browser extension. + +## Files + +- **maps-utils.ts**: Contains functions for processing maps and generating map queries + +## Functionality + +The utilities in this folder are responsible for: + +1. **Map Detection**: Identifying maps embedded in web pages (currently supports Google Maps and potentially OpenStreetMap) +2. **Map Processing**: Extracting relevant data from maps, such as coordinates and context +3. **Query Generation**: Creating queries to send to the IMAGE server for map rendering +4. **UI Integration**: Adding buttons and controls to maps for accessing IMAGE features + +## Usage + +When a user visits a webpage containing a map (such as a Google Maps embed), the extension: + +1. Detects the map using the `processMaps` and `processMAPImages` functions +2. Adds a button next to the map that allows the user to request an accessible rendering +3. When the user clicks the button, the extension extracts the map's coordinates and context +4. The extension sends this information to the IMAGE server for processing +5. The server returns accessible renderings, which are displayed to the user + +The extension currently supports two types of map requests: +- **Map Resource**: Based on latitude and longitude coordinates +- **Map Search**: Based on a place ID or query string + +Maps are processed differently depending on their type (Google Maps, OpenStreetMap) and how they are embedded in the page (iframe, static image). diff --git a/src/monarch/README.md b/src/monarch/README.md new file mode 100644 index 00000000..bcd3d59d --- /dev/null +++ b/src/monarch/README.md @@ -0,0 +1,34 @@ +# Monarch Integration + +This directory contains utilities for integrating with the Monarch system, which appears to be related to tactile rendering and authoring. + +## Files + +- **types.ts**: Contains TypeScript type definitions for Monarch-related data structures +- **utils.ts**: Contains utility functions for Monarch integration, including encryption and decryption + +## Functionality + +The utilities in this folder are responsible for: + +1. **Data Encryption**: Encrypting data before sending it to Monarch +2. **Data Decryption**: Decrypting data received from Monarch +3. **Local Storage**: Saving SVG data to local storage for use with the Tactile Authoring Tool +4. **User Notifications**: Displaying alerts to users about Monarch operations + +## Usage + +The Monarch integration appears to be an advanced feature of the IMAGE extension that allows users to: + +1. Send graphics to the Monarch system for tactile rendering +2. Load graphics in the Tactile Authoring Tool for editing +3. Create or update channels in the Monarch system + +The integration uses encryption to secure sensitive data, such as channel IDs and secret keys. Users can configure their Monarch settings in the extension's options page, including: + +- Graphic title +- Channel ID +- Secret key +- Encryption key + +When a user chooses to send a graphic to Monarch or load it in the Tactile Authoring Tool, the extension uses the functions in this directory to process the data securely. diff --git a/src/options/README.md b/src/options/README.md new file mode 100644 index 00000000..02473de8 --- /dev/null +++ b/src/options/README.md @@ -0,0 +1,38 @@ +# Extension Options + +This directory contains files related to the options/preferences page of the IMAGE browser extension. + +## Files + +- **options.html**: HTML template for the options page +- **options.ts**: JavaScript for handling the options functionality +- **options.css**: Styles for the options page + +## Functionality + +The files in this directory are responsible for: + +1. **User Preferences**: Allowing users to configure various aspects of the extension +2. **Language Settings**: Enabling users to select their preferred language +3. **Server Configuration**: Providing options for selecting the server to use (McGill or custom) +4. **Rendering Options**: Letting users choose which types of renderings they want (audio, text) +5. **Monarch Settings**: Configuring integration with the Monarch system +6. **Developer Mode**: Enabling advanced options for developers + +## Usage + +Users can access the options page by: +- Clicking the "IMAGE options" button in the launchpad +- Navigating to the extension's options in their browser's extension management page + +The options page includes several sections: + +1. **Interpretation Options**: Controls for enabling/disabling audio and text renderings +2. **Language and Interface Settings**: Language selection and options for displaying invisible buttons +3. **Server Options**: Selection between the McGill server and a custom server +4. **Haptic Devices**: Configuration for Monarch integration +5. **Developer Mode**: Advanced options for debugging and development + +Changes to the options are saved when the user clicks the "Save Changes" button. The user can also cancel their changes by clicking the "Cancel" button. + +The extension uses the browser's storage API to persist these settings across browser sessions. diff --git a/src/progressBar/README.md b/src/progressBar/README.md new file mode 100644 index 00000000..6d867f84 --- /dev/null +++ b/src/progressBar/README.md @@ -0,0 +1,33 @@ +# Progress Bar + +This directory contains files related to the progress bar displayed during the rendering process in the IMAGE browser extension. + +## Files + +- **progressBar.html**: HTML template for the progress bar +- **progressBar.js**: JavaScript for handling the progress bar functionality +- **progressBar.css**: Styles for the progress bar +- **audio-files/**: Directory containing audio files for progress notifications + +## Functionality + +The files in this directory are responsible for: + +1. **Processing Indication**: Showing users that their request is being processed +2. **Progress Visualization**: Providing a visual representation of the processing progress +3. **Audio Feedback**: Playing audio cues at different stages of processing +4. **Status Updates**: Informing users about the current status of their request + +## Usage + +When a user requests an accessible rendering of an image, chart, or map, the extension: + +1. Sends the request to the IMAGE server +2. Opens a progress bar window or tab to indicate that processing is underway +3. Updates the progress bar as the server processes the request +4. Plays audio cues at key points (e.g., when the request is sent, when results arrive) +5. Closes the progress bar when processing is complete or if an error occurs + +The progress bar helps users understand that their request is being processed, especially for complex images or slow network connections where processing might take some time. + +The audio files in the audio-files directory provide non-visual feedback about the processing status, which is particularly helpful for users who rely on screen readers or other assistive technologies. diff --git a/src/types/README.md b/src/types/README.md new file mode 100644 index 00000000..e97b3623 --- /dev/null +++ b/src/types/README.md @@ -0,0 +1,34 @@ +# TypeScript Type Definitions + +This directory contains TypeScript type definitions for the IMAGE browser extension. + +## Files + +- **background.types.ts**: Type definitions for the background script +- **definitions.d.ts**: General type definitions +- **handler-response.schema.d.ts**: Type definitions for handler responses +- **message-types.constants.ts**: Constants for message types +- **preprocessor-response.schema.d.ts**: Type definitions for preprocessor responses +- **request.schema.d.ts**: Type definitions for IMAGE requests +- **response.schema.d.ts**: Type definitions for IMAGE responses +- **simpleaudio.schema.d.ts**: Type definitions for simple audio +- **text.schema.d.ts**: Type definitions for text renderings + +## Functionality + +The type definitions in this directory serve several important purposes: + +1. **Type Safety**: Ensuring that data structures are used consistently throughout the codebase +2. **Code Completion**: Enabling IDE features like auto-completion and parameter hints +3. **Documentation**: Providing information about the structure of various data objects +4. **Error Prevention**: Catching type-related errors at compile time rather than runtime + +## Usage + +These type definitions are imported and used throughout the extension's codebase. For example: + +- Message interfaces in background.types.ts define the structure of messages sent between content scripts and the background script +- Schema definitions like request.schema.d.ts and response.schema.d.ts define the structure of data sent to and received from the IMAGE server +- Constants in message-types.constants.ts provide a centralized definition of message type identifiers + +By using these type definitions, the extension's code is more robust and easier to maintain, as type-related errors can be caught during development rather than in production. diff --git a/src/types/background.types.ts b/src/types/background.types.ts index c6d45f16..a6ba369b 100644 --- a/src/types/background.types.ts +++ b/src/types/background.types.ts @@ -238,47 +238,6 @@ export interface PortsMap { [key: number]: Runtime.Port; } -/** - * Interface for storage sync data - */ -// export interface StorageSyncData { -// mcgillServer?: boolean; -// inputUrl?: string; -// customServer?: boolean; -// monarchTitle?: string; -// monarchSecretKey?: string; -// monarchChannelId?: string; -// monarchEncryptionKey?: string; -// developerMode?: boolean; -// previousToggleState?: boolean; -// processItem?: string; -// requestItem?: string; -// monarchEnabled?: boolean; -// previousMonarchMode?: boolean; -// displayInvisibleButtons: boolean; -// } - -/** - * Interface for monarch request body - */ -// export interface MonarchRequestBody { -// data: string; -// layer: string; -// title: string; -// secret: string; -// graphicBlob?: string; -// coordinates?: string; -// placeID?: string; -// } - -// /** -// * Interface for monarch response -// */ -// export interface MonarchResponse { -// id: string; -// secret: string; -// } - /** * Interface for specific tactile rendering */ @@ -290,5 +249,3 @@ export interface SpecificTactileRendering { [key: string]: unknown; } -// Type guards are not needed when using switch-case statements with discriminated unions -// TypeScript will automatically narrow the type within each case block diff --git a/src/types/canvas-circle.d.ts b/src/types/canvas-circle.d.ts deleted file mode 100644 index 21f7a3a4..00000000 --- a/src/types/canvas-circle.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface canvasCircle { - x: number, - y: number, - vx: number, - vy: number, - radius: number, - color: string, - draw(): void; -} diff --git a/src/types/canvas-rectangle.d.ts b/src/types/canvas-rectangle.d.ts deleted file mode 100644 index a74fef5f..00000000 --- a/src/types/canvas-rectangle.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface canvasRectangle { - x?: number, - y?: number, - color?: string, - draw(): void; -} diff --git a/src/types/vector.d.ts b/src/types/vector.d.ts deleted file mode 100644 index 3ccae4c7..00000000 --- a/src/types/vector.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Vector { - x: number; - y: number; - } \ No newline at end of file