# Project export: Inclusiv.ai

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2024
- Tagline: Inki is a simplified system to navigate the web.
- Devpost: https://devpost.com/software/inclusiv-ai
- GitHub: https://github.com/VolVox99/inklusiv
- Result: winner (Best Use of Monster Generative AI APIs (4x XBox Series S [1st] & 1 million Monster API credits [2nd] & $400 Cash [3rd]))
- Team: 3 GitHub contributor(s) — Ali (25 commits), Shayan Yasir (19 commits), sidereior (12 commits)

## Devpost submission (written by the team)

### Overview

Inclusiv.ai: Empowering Accessibility

### Inspiration

In a world where technology is a cornerstone of daily life, it's crucial that digital access is equitable. However, not everyone experiences technology in the same way. We took this challenge to heart and crafted Inclusiv.ai to revolutionize accessibility, ensuring that individuals with disabilities can navigate the web with ease and confidence. Our main priority was reducing the hassle of different extensions and toggles. Inclusiv.ai has one button and one assistant—Inki.

### What it does

Inclusiv provides a simple way for those with disabilities to navigate the web. We focused on a hassle-free, intuitive approach with only one button. Simply begin a conversation with your assistant, "Inki," by clicking on the microphone button and explain whatever issues have been limiting your experience on the web. With various modes such as colorblind, screen enhancer, screen explainer and summarizer, and an ADHD/Dyslexia mode. Inki is designed to be a dynamic tool, adapting to a variety of needs through its multiple modes: Colorblind Mode: Tailors the web page colors, ensuring that colorblind users can differentiate between colors that are typically hard to distinguish. Screen Enhancer: Amplifies and clarifies website content for those with visual impairments, allowing for easier reading and interaction. Screen Explainer and Summarizer: A mode that not only explains the elements on the screen but also provides concise summaries for quick comprehension, beneficial for users with cognitive disabilities. ADHD/Dyslexia Mode: Alters the web page layout and typography to minimize distractions and optimize for readability, assisting users with attention deficits or dyslexia. Behind these user-facing features, Inclusiv utilizes a combination of large language models and a specific focus on Monster API. This approach has enabled us to create features that are both technically sophisticated and varied, ensuring a broad range of user needs are met.

### How we built it

Inclusiv was meticulously crafted by a synergistic team of two backend developers and one frontend designer, all united by the vision of making web navigation universally accessible. The project was born from a user-centric approach, focusing on the unique challenges faced by individuals with disabilities. Our team's design philosophy hinged on simplicity, leading to the creation of a one-button interface that calls upon Inki, an AI assistant, to activate different accessibility modes including colorblind, screen enhancer, screen explainer, and summarizer, and an ADHD/Dyslexia mode. By leveraging large language models for natural language processing and integrating Monster API's robust algorithms, Inclusiv transcends conventional assistive technologies. This blend of intuitive design and advanced AI capabilities was continuously refined through iterative user testing and feedback, ensuring that the product genuinely resonates with the needs of its users. Inclusiv’s launch is not an end, but a beginning to an ongoing journey of innovation and improvement, with a commitment to evolving and expanding its features to foster an inclusive web experience for all.

### Challenges we ran into

Where to start? Front-end might be harder than the back end! We spent hours fiddling with the optimal UI setup, trying to figure out how best to simplify the experience for a user of any skill level. This is not as simple as it looks, and we spent two hours trying to add a power button. Additionally, we ended up having to pivot our AI model numerous times and switched our approach throughout the project.

### Accomplishments we're proud of

We ended up changing our AI agent multiple times throughout the project to deliver the optimal project. We're really satisfied with our ability to adapt and push ourselves out of our comfort zones throughout the 36 hours. We developed numerous, technically difficult and varied features that we believe will truly aid those with any form of disability.

### What we learned

We learned tons about building a project from scratch, implementing LLMs, and creating an intuitive and appealing front-end experience.

### What's next

We want to launch on the Chrome web store!

## README (from the GitHub repository)

# inklusiv

Disabilities:
adhd
colorblind
dyslexia

Features:
works with different languages
zoom out zoom in
report generation

todo:
general accessibility improvement and standards
answer questions about the site
give explainsite context about users question

## Detected evidence (automated analysis)

Indexed codebase: 28 recognized source files, 615 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- OpenAI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (31 of 31)

```
assets/fonts/css/all.css
assets/fonts/css/all.min.css
assets/fonts/css/brands.css
assets/fonts/css/brands.min.css
assets/fonts/css/fontawesome.css
assets/fonts/css/fontawesome.min.css
assets/fonts/css/regular.css
assets/fonts/css/regular.min.css
assets/fonts/css/solid.css
assets/fonts/css/solid.min.css
assets/fonts/css/svg-with-js.css
assets/fonts/css/svg-with-js.min.css
assets/fonts/css/v4-font-face.css
assets/fonts/css/v4-font-face.min.css
assets/fonts/css/v4-shims.css
assets/fonts/css/v4-shims.min.css
assets/fonts/css/v5-font-face.css
assets/fonts/css/v5-font-face.min.css
explainPage-worker.js
manifest.json
monsterapireferences.txt
package.json
popup.html
popup.js
README.md
scripts/adhd.js
scripts/colorblind.js
scripts/dyslexia.js
scripts/magnifyPage.js
scripts/unMagnifyPage.js
styles.css
```

### Dependencies

- package.json: @emotion/react@^11.11.1, @emotion/styled@^11.11.0, @mui/icons-material@^5.14.15, @mui/material@^5.14.12, @reduxjs/toolkit@^1.9.5, @vitejs/plugin-react@^3.1.0, @vitejs/plugin-react-swc@^3.5.0, autoprefixer@^10.4.14, axios@^1.3.5, classnames@^2.3.2, dompurify@^3.0.5, draft-js@^0.11.7, draftjs-to-html@^0.9.1, esbuild@^0.19.12, eslint@^8.38.0, eslint-config-airbnb@^19.0.4, eslint-plugin-import@^2.27.5, eslint-plugin-jsx-a11y@^6.7.1, eslint-plugin-react@^7.32.2, eslint-plugin-react-hooks@^4.6.0, framer-motion@^10.16.5, html-to-draftjs@^1.5.0, html-to-text@^9.0.5, immer@^10.0.2, jquery@^3.6.4, lodash.debounce@^4.0.8, lodash.uniqueid@^4.0.1, mixpanel-browser@^2.48.1, postcss@^8.4.26, prettier@^3.1.0, prettier-plugin-tailwindcss@^0.5.7, react@^18.2.0, react-collapsed@^4.0.4, react-countdown@^2.3.5, react-datepicker@^4.12.0, react-dom@^18.2.0, react-draft-wysiwyg@^1.15.0, react-icons@^4.12.0, react-markdown@^8.0.7, react-redux@^8.0.5, react-router@^6.10.0, react-router-dom@^6.21.1, react-scroll@^1.9.0, react-select@^5.7.3, redux@^4.2.1, swiper@^11.0.5, tailwindcss@^3.3.3, uuid@^9.0.1, vite@^4.2.1, vite-plugin-eslint@^1.8.1

### Recent commits (newest first)

- biug
- bug
- biug
- fix bool temp val
- prompt
- prompt
- push
- f
- push
- f
- f
- Merge pull request #7 from VolVox99/screenshotSnippet
- nothin
- added power button no problem
- Merge pull request #6 from VolVox99/screenshotSnippet
- added button top right of popup.html
- improov
- improvements
- Merge pull request #5 from VolVox99/screenshotSnippet
- Merge branch 'main' of https://github.com/VolVox99/inklusiv into screenshotSnippet

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
    "name": "starterpack-shayanyasir",
    "version": "1.0.0",
    "description": "*description*",
    "main": "src/index.js",
    "scripts": {
      "dev": "vite",
      "build": "vite build",
      "preview": "vite preview"
    },
    "author": "",
    "license": "ISC",
    "devDependencies": {
      "@vitejs/plugin-react-swc": "^3.5.0",
      "autoprefixer": "^10.4.14",
      "eslint": "^8.38.0",
      "eslint-config-airbnb": "^19.0.4",
      "eslint-plugin-import": "^2.27.5",
      "eslint-plugin-jsx-a11y": "^6.7.1",
      "eslint-plugin-react": "^7.32.2",
      "eslint-plugin-react-hooks": "^4.6.0",
      "postcss": "^8.4.26",
      "prettier": "^3.1.0",
      "prettier-plugin-tailwindcss": "^0.5.7",
      "tailwindcss": "^3.3.3",
      "vite": "^4.2.1",
      "vite-plugin-eslint": "^1.8.1"
    },
    "dependencies": {
      "@emotion/react": "^11.11.1",
      "@emotion/styled": "^11.11.0",
      "@mui/icons-material": "^5.14.15",
      "@mui/material": "^5.14.12",
      "@reduxjs/toolkit": "^1.9.5",
      "@vitejs/plugin-react": "^3.1.0",
      "axios": "^1.3.5",
      "classnames": "^2.3.2",
      "dompurify": "^3.0.5",
      "draft-js": "^0.11.7",
      "draftjs-to-html": "^0.9.1",
      "esbuild": "^0.19.12",
      "framer-motion": "^10.16.5",
      "html-to-draftjs": "^1.5.0",
      "html-to-text": "^9.0.5",
      "immer": "^10.0.2",
      "jquery": "^3.6.4",
      "lodash.debounce": "^4.0.8",
      "lodash.uniqueid": "^4.0.1",
      "mixpanel-browser": "^2.48.1",
      "react": "^18.2.0",
      "react-collapsed": "^4.0.4",
      "react-countdown": "^2.3.5",
      "react-datepicker": "^4.12.0",
      "react-dom": "^18.2.0",
      "react-draft-wysiwyg": "^1.15.0",
      "react-icons": "^4.12.0",
      "react-markdown": "^8.0.7",
      "react-redux": "^8.0.5",
      "react-router": "^6.10.0",
      "react-router-dom": "^6.21.1",
      "react-scroll": "^1.9.0",
      "react-select": "^5.7.3",
      "redux": "^4.2.1",
      "swiper": "^11.0.5",
      "uuid": "^9.0.1"
    }
}
```

### styles.css

```css
body {
    width: 200px;
    padding: 10px;
  }
  input[type="checkbox"] {
    margin-bottom: 5px;
  }
  
```

### explainPage-worker.js

```javascript
const response = 'This page is a basic page with no special features.';

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message?.action === 'explainPage') {
        const user_input = message?.user_input;
        chrome.tabs.captureVisibleTab().then((screenshotDataUrl) => {
            fetch('https://api.openai.com/v1/chat/completions', {
                method: 'POST',
                headers: {
                  'Content-Type': 'application/json',
                  'Authorization': 'Bearer ' + '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'
                },
                body: JSON.stringify({
                  "model": "gpt-4-vision-preview",
                  "max_tokens": 2000,
                  "messages": [
                    {
                        "role": "user",
                        "content": [
                          {"type": "text", "text": `You are a web accessibility partner. Based on the screenshot of the website, describe what is on this website for someone who's vision impaired, and how they can navigate it and what they can do with it. Consider the user's request from you: ${user_input}. If the user's input has a specific question based on the site, be sure to first answer that and accomodate the request. In general, be succint with your language, but explain things in depth and comprehensively so they can fully understand the website. Explain the name of the site, the purpose, how to use it, and more. Imagine you are describing it to a blind person. Speak with conviction, even if you are unsure. Do not use words such as "seems" or "appears" or "likely" and so on. Speak with authority and clarity, even if it's not genuine. Aim to keep the entire thing around 3-4 sentences. Speak in second person tone, directly to the user. At the end, be friendly and maybe ask if they have any more questions.`},
                          {
                            "type": "image_url",
                            "image_url": {
                              "url": screenshotDataUrl,
                            },
                          },
                        ],
                      }
                  ]
                })
              })
              .then(response => response.json())
              .then(data => {    
                var response = data.choices[0].message.content;

                sendResponse({
                    text: response,
                })

            })


            
        });
    } else {
  chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    var activeTab = tabs[0];
    chrome.scripting
      .executeScript({
        target : {tabId : activeTab.id},
        files : [`scripts/${message.action}.js`],
      })
      .then(() => console.log("injected a function"));
  });

  }
    return true;
  });





```

### popup.html

```html
<!DOCTYPE html>
<html lang="en">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@700&display=swap" rel="stylesheet">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<style>
  

  body {
    margin: 0;
    padding: 20px;
    font-family: 'Arial', sans-serif;
    background-color: #292D3E;
    color: white;
    text-align: center;
    }

/* Style the checkbox */
input[type="checkbox"] {
  height: 40px; /* Smaller height */
  width: 40px; /* Smaller width */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border: 5px solid transparent; /* Smaller border */
  background-origin: border-box;
  background-clip: content-box, border-box;
  box-shadow: 
    -2px -2px 4px rgba(243, 11, 245, 0.63),
    2px 2px 4px rgba(70, 70, 70, 0.12);
  border-radius: 50%;
  position: absolute;
  top: 20px; /* Distance from the top */
  right: 20px; /* Distance from the right */
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.3s, transform 0.3s;
}

/* Style the icon inside the checkbox */
input[type="checkbox"]::after {
  content: "\23FB"; /* Unicode power symbol */
  color: #7a7a7a;
  font-size: 30px; /* Adjust the font size as necessary */
  transition: color 0.3s;
}

/* Change icon color when checked */
input[type="checkbox"]:checked::after{
  color: rgb(227, 21, 45);
}
  .logo {
    margin-bottom: 24px;
  }
  .logo img {
    width: 100px; /* Adjust as necessary */
  }
  .title {
    font-size: 24px;
    margin-bottom: 8px;
  }
  .description {
    font-size: 16px;
    margin-bottom: 32px;
    font-weight: bold;
    color: #FFFFFF; /* Explicitly set the color to white */
    opacity: 1;
    background: none; 
    justify-content: space-between;
  }
  .text-gradient { /* Updated class name to match HTML */
    font-size: 60px; /* Adjust the size as needed to ensure the gradient is visible */
    font-weight: bold; /* Optional: to enhance the visibility of the gradient */
    background-image: linear-gradient(to right bottom, #12c2e9, #00baf7, #20afff, #5ba1ff, #8a90ff, #ae81f4, #cc70e2, #e55dcb, #f450af, #fb4991, #fc4975, #f64f59);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text; /* For browsers that support the unprefixed property */
  }

  .mic {
    position: relative; 
    display: inline-block; 
    vertical-align: middle;
    margin-bottom: 70px; /* Adjusted margin */
}
.mic::before, .mic::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 100%;
  z-index: 2;
}

.mic::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 120px;
    height: 120px;
    background-color: #C51162; /* Pink background color */
    border-radius: 50%;
    z-index: 1; /* Below the mic icon */
    box-shadow: 0 0 30px 30px #1c084f; /* Outer glow effect */
    background-image: linear-gradient(to right bottom, #12c2e9, #20afff, #8a90ff, #ae81f4);
    display: block; /* Ensure it's always displayed */
  }

.mic-icon {
  box-sizing: border-box;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(5);
  display: block;
  width: 22.5px;
  height: 22.5px; /* Adjusted height */
  margin: auto;
  z-index: 2;
  visibility: visible; /* Added visibility property */
}

.mic-shadow {
  display: none;
  width: 120px;
  height: 120px;
  position: absolute;
  top: 50%;
  left: 50%;
  border-radius: 100%;
  z-index: 0;
  box-shadow: 9px -49.5px 27px 13.5px #823ca6, 21.6px -9px 42.3px 9px #aab3d2, -18.9px -22.5px 87.3px 9px #5acee3, 45.9px 4.5px 15.3px 9px #1b7d8f, 2.7px 1.8px 69.3px 9px #f30bf5;  
  animation: shadow-rotate 1.5s linear infinite;  
  transform-origin: center;
  display: none;
}
/* The default state without rotating circles */
.mic.no-animation::after,
.mic.no-animation .mic-shadow {
  animation: none;
}




/* Only play animations when the recording class is present */
.mic.recording .mic-shadow {
  display: block;
  animation: shadow-rotate 1.5s linear infinite;
}
.mic.recording::after {
  animation: circle-size 0.8s linear infinite alternate;
}

.loader {
    display: none; /* Initially hidden */
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 150px;
    height: 150px;
    border-radius: 50%;
    z-index: 2; /* Make sure it's above the mic-icon */
    will-change: transform; /* Optimizes the animation */
  }

@keyframes rotate {
  0% {
    transform: rotate(0deg) translateZ(0); /* 3D transform hack to trigger GPU acceleration */
  }
  100% {
    transform: rotate(360deg) translateZ(0); /* 3D transform hack to trigger GPU acceleration */
  }
}

.loader span {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: linear-gradient(#12c2e9, #00baf7, #20afff, #5ba1ff, #8a90ff, #ae81f4, #cc70e2, #e55dcb, #f450af, #fb4991, #fc4975, #f64f59);
  will-change: background, filter; /* Optimizes the animation */
  backface-visibility: hidden; /* 3D transform hack to trigger GPU acceleration */
}

.loader span:nth-child(1) {
  filter: blur(7.5px);
  animation: rotate 2s linear infinite;
}
.loader span:nth-child(2) {
  filter: blur(11.25px);
  animation: rotate 2s linear infinite;
}
.loader span:nth-child(3) {
  filter: blur(30px);
  animation: rotate 2s linear infinite;
}
.loader span:nth-child(4) {
  filter: blur(52.5px);
  animation: rotate 2s linear infinite;
}

.loader::after {
  content: '';
  position: absolute;
  top: 15px;
  left: 15px;
  right: 15px;
  bottom: 15px;
  background: #292D3E; /* Set to the background color of the body for a 'cut-out' effect */
  border-radius: 50%;
  z-index: 2;
}

@keyframe
[truncated — 1509 more characters]
```

### popup.js

```javascript
const playAudio = (url) => {
  loader.style.display = 'none'; // stop loading
  audioElement.src = url
  audioElement.play()
}

const executeUserRequest = (req, user_input) => {
  chrome.runtime.sendMessage({ action: req, user_input: user_input }, response => {
      const text = response?.text;
      text && textToSpeech(text);
    })
}

let mediaRecorder;
let audioChunks = [];
let isRecording = false;
const recordButton = document.getElementById('recordButton');
const micIcon = recordButton.querySelector('.mic-icon');
const micShadow = recordButton.querySelector('.mic-shadow');
const audioElement = document.getElementById('audio');
const loader = recordButton.querySelector('.loader');

recordButton.addEventListener('click', () => {
  if (!isRecording) {
    navigator.mediaDevices.getUserMedia({ audio: true })
      .then(stream => {
        mediaRecorder = new MediaRecorder(stream);
        audioChunks = [];

        mediaRecorder.ondataavailable = event => {
          audioChunks.push(event.data);
        };

        mediaRecorder.onstop = () => {
          const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
          
          document.querySelector('.loader').style.display = 'block';

          loader.style.display = 'block';

          const form = new FormData();
          form.append('file', audioBlob, 'audio.wav');
          form.append('do_sample', 'true');
          form.append('repetition_penalty', '0.9');
          form.append('temperature', '0.9');
          form.append('top_k', '50');
          form.append('top_p', '0.9');

          const options = {
            method: 'POST',
            headers: {
              accept: 'application/json',
              authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjE4ZGIzN2I0MmU5ZTU4OTllNzI1OWM4NzZhZWUwZjAzIiwiY3JlYXRlZF9hdCI6IjIwMjQtMDItMTdUMDg6MTk6MjAuMzg3NzExIn0.XMGqFSpcgakTSAD2TSnSdnWdO15jQhMwErctkp8PUTo'
            },
            body: form
          };

          // First request to MonsterAPI for speech to text conversion
          fetch('https://api.monsterapi.ai/v1/generate/speech2text-v2', options)
            .then(response => response.json())
            .then(data => {
              const processId = data.process_id;
              console.log('Initial request success data:', data);

              // Function to repeatedly check the status of the transcription
              const checkStatus = () => {
                const options2 = {
                  method: 'GET',
                  headers: {
                    accept: 'application/json',
                    authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjE4ZGIzN2I0MmU5ZTU4OTllNzI1OWM4NzZhZWUwZjAzIiwiY3JlYXRlZF9hdCI6IjIwMjQtMDItMTdUMDg6MTk6MjAuMzg3NzExIn0.XMGqFSpcgakTSAD2TSnSdnWdO15jQhMwErctkp8PUTo'
                  }
                };
                fetch(`https://api.monsterapi.ai/v1/status/${processId}`, options2)
                  .then(response => response.json())
                  .then(response => {
                    console.log("Process ID:", response.process_id);
                    console.log("Status:", response.status);

                    if (response.status === "COMPLETED" || response.status === "FAILED") {
                      clearInterval(statusInterval); // Stop checking
                      if (response.status === "COMPLETED") {
                        console.log("Transcription Completed:", response.result);
                        const user_input = response.result.text
                        const jsonString = `
                          {
                            "user_requests": ["adhd", "dyslexia", "colorblind", "explainPage", "magnifyPage", "unMagnifyPage", "reportIssue"],
                            "assistant_response": "A friendly and brief response to the user in second person tone acknowledging what changes you are making to the website based on the user's accessibility needs. Keep in mind you are a friendly accessibility assistant, so this is your response to the user to keep them informed. For instance, if they mentioned something beyond the scope of your capabilities that you can't do, inform them. Speak in singular second person tone. You don't need to greet the person here because this is a continuation of the conversation. Also if they haven't mentioned any disabilities or only one or two, you should ask them if they have any of those other disabilities. Because the user doesn't know what you can do unless you tell them. But also don't say too much try to keep it conversational and encourage questions to keep the user engaged because remember that after this response you will go back and forth again this isn't your final interaction. If the user has requested a command, say something similar to like stand by, I am implementing that right now. Only for if the user activates the explainPage command, make your response in this section brief and quick because the explainPage command will also activate speaking and then you will interupt each other. ", 
                          }`;
                        const promptText = `You are an AI web accessibility assistant and you are tasked with interpreting the user’s most recent statement to you. The user most likely either gave you a brief description of any accessibility needs they may have, and you must take note of that and let them know that you are now changing websites to accomodate that disability. And/or, the user will be asking for a command for you to do a specific action on the current website, and you must also take note of this. Then, you will be generating a JSON object which encodes all this information. Here is what the user said, and you must strictly adhere to this in order to determine which values to give for the JSON fields: "${user_input}”. 
                        If the user didn't clearly mention any of the fields, don't fill them in.
                        If none of the potent
[truncated — 8499 more characters]
```

### scripts/magnifyPage.js

```javascript
// Initialize the zoom level
if (!window.zoomLevel) {
window.zoomLevel = 100;
}

// Function to increase the zoom level by 25%
function increaseZoom() {
    if (window.zoomLevel < 200) {
        window.zoomLevel += 25;
        document.body.style.zoom = window.zoomLevel + '%';
    }
}

// Function to decrease the zoom level by 25%
function decreaseZoom() {
    if (window.zoomLevel > 25) {
        window.zoomLevel -= 25;
        document.body.style.zoom = window.zoomLevel + '%';
    }
}

increaseZoom()
```

### scripts/unMagnifyPage.js

```javascript
// Initialize the zoom level
if (!window.zoomLevel) {
    window.zoomLevel = 100;
    }
    
    // Function to increase the zoom level by 25%
    function increaseZoom() {
        if (window.zoomLevel < 200) {
            window.zoomLevel += 25;
            document.body.style.zoom = window.zoomLevel + '%';
        }
    }
    
    // Function to decrease the zoom level by 25%
    function decreaseZoom() {
        if (window.zoomLevel > 25) {
            window.zoomLevel -= 25;
            document.body.style.zoom = window.zoomLevel + '%';
        }
    }
    
    decreaseZoom()
```

### scripts/dyslexia.js

```javascript
{
const cssRules = `

@font-face {
    font-family: 'OpenDyslexic3';
    src: url(${chrome.runtime.getURL('./assets/fonts/OpenDyslexic3-Bold.ttf')});
    font-weight: bold;
}

  
@font-face {
    font-family: 'OpenDyslexic3';
    src: url(${chrome.runtime.getURL('./assets/fonts/OpenDyslexic3-Regular.ttf')});
    font-weight: normal;
}

* {
    font-family: 'OpenDyslexic3', sans-serif !important;
}
`;

// Create a <style> element
const styleElement = document.createElement('style');

// Set the CSS content
styleElement.textContent = cssRules;
}

// Append the <style> element to the <head> of the document
document.head.appendChild(styleElement);



```

### scripts/colorblind.js

```javascript
{
var cssContentArray = [];

// Function to extract inline styles from HTML elements
function extractInlineStyles() {
  var inlineStyles = [];
  var elementsWithInlineStyles = document.querySelectorAll('[style]');
  elementsWithInlineStyles.forEach(function (element) {
    var inlineStyle = element.getAttribute('style');
    if (inlineStyle) {
      inlineStyles.push(inlineStyle);
    }
  });
  return inlineStyles.join('\n'); // Concatenate inline styles with newline separator
}
var stylesheets = document.styleSheets;
for (var i = 0; i < stylesheets.length; i++) {
  var rules;
  try {
    rules = stylesheets[i].cssRules || stylesheets[i].rules;
  } catch (e) {
    console.error('Access to stylesheet rules blocked by CORS policy:', e);
    continue;
  }
  var cssText = '';
  for (var j = 0; j < rules.length; j++) {
    cssText += rules[j].cssText + '\n';
  }
  cssContentArray.push(cssText);
}


// Extract inline styles
var inlineStyles = extractInlineStyles();
cssContentArray.push(inlineStyles);

console.log('fetching gpt')
// Concatenate all CSS content
var allCSSContent = cssContentArray.join('\n'); // Concatenate CSS content with newline separator
fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'authorization': 'Bearer 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'
  },
  body: JSON.stringify({
    "model": "gpt-4-turbo-preview",
    "messages": [
      {
        "role": "system",
        "content": "You are an expert at generating css code."
      },
      {
        "role": "user",
        "content": `Generate in depth and comprehensive css code to make the following code of a website be accessible to red-green colorblind people. Increase color contrast between red and green elements by adjusting their hues, saturation, or brightness. Replace red and green color combinations with alternative colors that are easier to distinguish, such as blue and yellow. Don't rewrite the entire css only generate as little new css as possible. Use things like !important! to ensure it overrides, and ensure that the css will work properly and update the visuals of the site. Respond only with css code and nothing else in this format: <style>CODE GOES HERE</style>. Current CSS: ${allCSSContent}`
      },
    ]
  })
})
  .then(response => response.json())
  .then(data => {
    console.log('gpt loaded')
    console.log(data);

    var cssCode = data.choices[0].message.content;
    var cssCodeCleaned = cssCode.replace('<style>', '').replace('</style>', '').replace('```html', '').replace('```css', '').replace('```', '').trim();
    var styleElement = document.createElement('style');
    styleElement.textContent = cssCodeCleaned;
    document.head.appendChild(styleElement);
  })
}



```

### scripts/adhd.js

```javascript
{
minWordLength = 4,     // Minimum word length
minTextLength = 20,    // Minimum text length
boldRatio     = 0.4,   // Bold ratio (percentage of letters per word)
doAdhd = true;
const insertTextBefore = (text, node, bold) => {
    if (bold) {
        let span = document.createElement('span');
        span.className = 'bread';
        span.appendChild(document.createTextNode(text));

        node.parentNode.insertBefore(span, node);
    } else {
        node.parentNode.insertBefore(document.createTextNode(text), node);
    }
}
const processNode = root => {
    let walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
        acceptNode: function (node) {
            return (
                node.parentNode.nodeName !== 'INPUT' &&
                node.parentNode.nodeName !== 'NOSCRIPT' &&
                node.parentNode.nodeName !== 'SCRIPT' &&
                node.parentNode.nodeName !== 'STYLE' &&
                node.parentNode.nodeName !== 'TEXTAREA' &&
                node.parentNode.nodeName !== 'TITLE' &&
             (node.parentNode.nodeName === 'A' ||
                node.parentNode.nodeName === 'EM' ||
                node.parentNode.nodeName === 'STRONG' ||
                node.nodeValue.length  >= minTextLength)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
        }
    });

    let node;
    while (node = walker.nextNode()) {
        let text = node.nodeValue;
        let wStart = -1, wLen = 0, eng = null;

        for (let i = 0; i <= text.length; i++) {   // We use <= here because we want to include the last character in the loop
            let cEng = i < text.length ? /[\p{Letter}\p{Mark}]/u.test(text[i]) : false;

            if (i == text.length || eng !== cEng) {
                // State flipped or end of string
                if (eng && wLen >= minWordLength) {
                    let word = text.substring(wStart, wStart + wLen);
                    let numBold = Math.ceil(word.length * boldRatio);
                    let bt = word.substring(0, numBold), nt = word.substring(numBold);
                    insertTextBefore(bt, node, true);
                    insertTextBefore(nt, node, false);
                } else if (wLen > 0) {
                    let word = text.substring(wStart, wStart + wLen);
                    insertTextBefore(word, node, false);
                }
                wStart = i;
                wLen = 1;
                eng = cEng;
            } else {
                wLen++;
            }
        }

        node.nodeValue = '';    // Can't remove the node (otherwise the tree walker will break) so just set it to empty
    }
};

document.head.appendChild(document.createElement('style')).textContent = `
    span.bread {
        display: contents !important;
        font-weight: bold !important;
    }
`;

if (doAdhd){
let breadNode = 'body'
let node = document.querySelector(breadNode);
processNode(node);
}
}

```

[14 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]