Full Code of adnanaga/pushy for AI

main 58aaee303383 cached
13 files
22.0 KB
5.7k tokens
9 symbols
1 requests
Download .txt
Repository: adnanaga/pushy
Branch: main
Commit: 58aaee303383
Files: 13
Total size: 22.0 KB

Directory structure:
gitextract_tpmgppc8/

├── .gitignore
├── LICENSE
├── README.md
├── background.js
├── manifest.json
├── notifications.js
├── popup working.js
├── popup.html
├── popup.js
└── resources/
    ├── SF-Light.otf
    ├── SF-Medium.otf
    ├── stories.json
    └── style.css

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 Adnan Aga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Pushy!

Create awkward situations with my chrome extension that send you messages that look like an iMessage popup

This is a work in progress - Still looking to make a webpage for it.

## Install
1. Open the Extension Management page by navigating to chrome://extensions or click here
[Chrome Exntensions](chrome://extensions)

2. Enable Developer Mode by clicking the toggle switch next to Developer mode.

3. Click the Load unpacked button and select pushy.zip

## Use it!

Click on the extension button to bring up the menu
![Where to click](/images/setup.png)

The menu should pop up and you can choose your story or write your own!
![Menu Popup](/images/menu.png)

Ther menu should pop up and you can choose your story or write your own!
![Click to send](/images/click%20to%20start.png)

The message should then popup on that page!
![Message Popup](/images/message.png)


================================================
FILE: background.js
================================================


================================================
FILE: manifest.json
================================================
{
    "name": "Pushy!",
    "manifest_version": 3,
    "version": "0.1",
    "description": "Get a notification on command",
    "permissions": [
      "activeTab",
      "scripting"
    ],
      "action": {
    "default_popup": "popup.html"
  },
    "web_accessible_resources": [{
        "resources": ["/resources/*"],
        "matches": ["<all_urls>"]
      }]
  }

================================================
FILE: notifications.js
================================================
function sendNotification(person, messageString) {
    
    let notificationBox = document.createElement("div");
    notificationBox.id = "messageBox"
    let imageContainer = document.createElement("div");
    let messageContainer = document.createElement("div");
    let title = document.createElement("div");
    let message = document.createElement("div");

    let img1 = document.createElement("img");
    let img2 = document.createElement("img");

    let profilePic;
    let icon = chrome.runtime.getURL("resources/message.png");

    let link = document.createElement("link");
    link.href = chrome.runtime.getURL("resources/style.css");
    link.type = "text/css";
    link.rel = "stylesheet";
    document.getElementsByTagName("head")[0].appendChild(link);

    title.innerHTML = person
    profilePic = chrome.runtime.getURL(`resources/${person}.jpg`);


    message.innerHTML = messageString


    notificationBox.classList.add("notificationBox");
    messageContainer.classList.add("messageContainer");
    imageContainer.classList.add("imageContainer");
    img1.classList.add("profilePic");
    img2.classList.add("messageIcon");
    title.classList.add("title");
    message.classList.add("message");

    img1.src = profilePic
    img2.src = icon
    imageContainer.appendChild(img1)
    imageContainer.appendChild(img2)
    messageContainer.appendChild(title)
    messageContainer.appendChild(message)
    notificationBox.appendChild(imageContainer)
    notificationBox.appendChild(messageContainer);

    (document.fullscreenElement ?? document.body).appendChild(notificationBox);

    notificationBox.classList.add("slideIn");
}

  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  
  async function playMessage(person, message, duration) {
    sendNotification(person, message);
    await sleep(duration ?? 4000);
    let notificationBox = document.getElementById("messageBox");
    notificationBox.classList.add("slideOut");
    await sleep(500);
    notificationBox.remove()
  }
  
  

async function playStory(request) {
  console.log(request);
  console.log('original', Object.keys(request).length)
  console.log('updated', (Object.keys(request).length-1)/2)

  for(let i=0; i< (Object.keys(request).length-1)/2; i++){
    console.log('delay' + (i+1))
    console.log('message' + (i+1))
    await sleep(request['delay' + (i+1)]);
    playMessage(request.character,request['message' + (i+1)]);
  }
}

let initialized;
if (typeof initialized === 'undefined') {
  initialized = true;
  chrome.runtime.onMessage.addListener(function(request){
    playStory(request)
  });
}

================================================
FILE: popup working.js
================================================
let text1 = document.getElementById("text1");
let text2 = document.getElementById("text2");
let text3 = document.getElementById("text3");

let delay1 = document.getElementById("delay1");
let delay2 = document.getElementById("delay2");
let delay3 = document.getElementById("delay3");

let message1 = document.getElementById("message1");
let message2 = document.getElementById("message2");
let message3 = document.getElementById("message3");

let submit = document.getElementById("submit");

let character = document.getElementById("character");
let characterImage = document.getElementById('characterImage');

let form = document.getElementById("form1");

character.addEventListener('change', function() {
    characterImage.src = `resources/${this.selectedOptions[0].value}.jpg`
  })

// your function
let submitted = function(event) {
    event.preventDefault();
    console.log("Playing Story");
    playStory();
    document.getElementById('main').style.display = 'none';
    document.getElementById('clickOut').style.display = 'block';
};

// attach event listener
form.addEventListener("submit", submitted, true);

if (text1) {
    text1.onclick = function() {
        character.value = 'Cassie';
        characterImage.src = `resources/Cassie.jpg`

        delay1.value = 10;
        message1.value = 'you packed me the wrong go gurt again';

        delay2.value = 5;
        message2.value = "you fucking idiot";

        delay3.value = 30;
        message3.value = "i hate you";

        document.getElementById('form1').style.display = 'block';
        // playStory(1);
    }
}

if (text2) {
    text2.onclick = function() {
        character.value = 'Jim';
        characterImage.src = `resources/Jim.jpg`

        delay1.value = 10;
        message1.value = "hey wanna hear a dumb joke";
        
        delay2.value = 30;
        message2.value = "what do you get when you mix human DNA with goat DNA?";
        
        delay3.value = 5;
        message3.value = "kicked out of the petting zoo LOL";
        document.getElementById('form1').style.display = 'block';
    }
}

if (text3) {
    text3.onclick = function() {
        character.value = 'Jane';
        characterImage.src = `resources/Jane.jpg`
        message1.value = '';
        message2.value = '';
        message3.value = '';
        document.getElementById('form1').style.display = 'block';
    }
}

function playStory(){
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            var activeTab = tabs[0];
            chrome.scripting.executeScript({
                target: { tabId: activeTab.id },
                files: ['notifications.js']
              }, function() {
                chrome.tabs.sendMessage(activeTab.id, {
                    "character": character.value,
                    "delay1": delay1.value*1000,
                    "delay2": delay2.value*1000,
                    "delay3": delay3.value*1000,
                    "message1": message1.value,
                    "message2": message2.value,
                    "message3": message3.value,
                });
            });
        });
}


================================================
FILE: popup.html
================================================
<!DOCTYPE html>
<html>
  <head>
    <style>

      .header {
            font-size: 20px;
            text-align:    center;
        }

        .storyContainer {
            display:flex;
            flex-direction: row;
            justify-content:space-around;
        }
        button {
          background: #3498db;
          border-radius: 18px;
          font-size: 14px;
          color: #ffffff;
        }

        button:hover {
          background: #3cb0fd;
          background-image: -webkit-linear-gradient(top, #3cb0fd, #3498db);
          background-image: -moz-linear-gradient(top, #3cb0fd, #3498db);
          background-image: -ms-linear-gradient(top, #3cb0fd, #3498db);
          background-image: -o-linear-gradient(top, #3cb0fd, #3498db);
          background-image: linear-gradient(to bottom, #3cb0fd, #3498db);
          text-decoration: none;
        }

        button:active {
          box-shadow: 0 5px #666;
          transform: translateY(4px);
        }

        #form1 {
          display: none;
        }

        #addNotification {
          display: none;
        }

        #clickOut {
          display:none;
        }

        #characterImage {
          width:20%;
          border-radius: 50%;
        }

        .characterSelector{
          display:flex;
          align-items: center;
          justify-content: space-between;
          margin: 0px 12px;
        }

        .messageForm {
          margin: 0px 12px;
        }

    </style>
  </head>
  <body>
   <div id="main">
     <h2>Pushy!</h2>
    <div class="header">Pick a story!</div>
    <br>
      <div class="storyContainer">
        <button id="story1">Cassie's Yoghurt Mixup</button>
        <button id="story2">Uncle Jims Dumb Joke</button>
        <button id="story3">Stinky!</button>
        <button id="story4">A Diaper Emergency</button>
        <button id="story5">Smize</button>
        <button id="storyDIY">Write your own!</button>
      </div>

      <br>
      <br>

      <div class="makeYourOwn">
        <form id="form1">
          <div class="characterSelector">
            <div>
                <label for="Character">Character:</label>
                <select name="Character" id="character">
                  <option value="Cassie">Cassie</option>
                  <option value="Jim" selected>Uncle Jim</option>
                  <option value="Roberto">Roberto</option>
                  <option value="Jane">Jane</option>
                </select>
            </div>
            <img src="resources/Cassie.jpg" id="characterImage">
          </div>
      <br>
      <br>

          <div class="messageForm" id="messageForm">
            
            <div id="notification1">
              <label for="delay1">Time before notification 1:</label>
              <br>
              <select name="delay1" id="delay1">
                <option value="5000">5 Seconds</option>
                <option value="10000" selected>10 Seconds</option>
                <option value="30000">30 Seconds</option>
                <option value="60000">60 Seconds</option>
              </select>
              <br>
                <label for="message1">Write your message for notification 1:</label>
                <input type="text" id="message1" name="message1" value="" style="width: 300px;" maxlength="280">
            </div>
            <br>
          </div>
          <input type="submit" value="Send Notifications">
          <button type="button" id= "addNotification" value="Add"> Add Notification</button>
        </form>
    </div>

    </div>

    <div id="clickOut">
      Nice!
      Click back to your website
    </div>

    
  </body>
</html>
<script src="popup.js"></script>

================================================
FILE: popup.js
================================================
let storyArray = document.querySelectorAll('[id*="story"]');

let story1 = storyArray[0];
let story2 = storyArray[1];
let story3 = storyArray[2];
let story4 = storyArray[3];
let story5 = storyArray[4];

let storyDIY = document.getElementById("storyDIY");

let delay1 = document.getElementById("delay1");
let delay2 = document.getElementById("delay2");
let delay3 = document.getElementById("delay3");

let message1 = document.getElementById("message1");
let message2 = document.getElementById("message2");
let message3 = document.getElementById("message3");

let notification1 = document.getElementById("notification1");
let notification2 = document.getElementById("notification2");
let notification3 = document.getElementById("notification3");

let submit = document.getElementById("submit");

let character = document.getElementById("character");
let characterImage = document.getElementById('characterImage');

let addNotification = document.getElementById('addNotification');

let form = document.getElementById("form1");
let messageForm = document.getElementById("messageForm");

let notificationData = {};

let stories;

character.addEventListener('change', function() {
    characterImage.src = `resources/${this.selectedOptions[0].value}.jpg`
  })


//   async function loadStories() {
//     const response = await fetch('/resources/stories.json');
//     stories = await response.json();
//     console.log(stories); 
//     // logs [{ name: 'Joker'}, { name: 'Batman' }]
//   }

// your function
let submitted = function(event) {
    event.preventDefault();
    let notifArray = document.querySelectorAll('[id*="notification"]');
    console.log(notifArray.length);
    notificationData['character'] = document.getElementById("character").value
    for(let i =0; i<notifArray.length; i++){
        notificationData['delay' + (i+1)] = document.getElementById(['delay' + (i+1)]).value
        notificationData['message' + (i+1)] = document.getElementById(['message' + (i+1)]).value    
    }
    playStory();
    document.getElementById('main').style.display = 'none';
    document.getElementById('clickOut').style.display = 'block';
};

// attach event listener
form.addEventListener("submit", submitted, true);

// attach event listener
addNotification.addEventListener("click", add);

if (story1) {
    story1.onclick = function() {
        notification1.style.display = 'block';
        console.log(form.length)

        if(form.length < 9){
            generate(2);
            generate(3);
            
            delay1 = document.getElementById("delay1");
            delay2 = document.getElementById("delay2");
            delay3 = document.getElementById("delay3");

            message1 = document.getElementById("message1");
            message2 = document.getElementById("message2");
            message3 = document.getElementById("message3");
        }
        
        addNotification.style.display = 'none';

        character.value = 'Cassie';
        characterImage.src = `resources/Cassie.jpg`

        delay1.value = 10000;
        message1.value = 'you packed me the wrong go gurt again';

        delay2.value = 5000;
        message2.value = "you fucking idiot";

        delay3.value = 30000;
        message3.value = "but its okay i still love you";

        document.getElementById('form1').style.display = 'block';
    }
}

if (story2) {
    story2.onclick = function() {
        notification1.style.display = 'block';
        if(form.length < 9){
            generate(2);
            generate(3);

            delay1 = document.getElementById("delay1");
            delay2 = document.getElementById("delay2");
            delay3 = document.getElementById("delay3");

            message1 = document.getElementById("message1");
            message2 = document.getElementById("message2");
            message3 = document.getElementById("message3");
        }


        addNotification.style.display = 'none';

        character.value = 'Jim';
        characterImage.src = `resources/Jim.jpg`

        delay1.value = 10000;
        message1.value = "hey wanna hear a dumb joke";
        
        delay2.value = 30000;
        message2.value = "what do you get when you mix human DNA with goat DNA?";
        
        delay3.value = 5000;
        message3.value = "kicked out of the petting zoo LOL";
        document.getElementById('form1').style.display = 'block';
    }
}

if (story3) {
    story3.onclick = function() {
        notification1.style.display = 'block';

        if(document.getElementById("notification2")){
            remove("notification2");
        }

        if(document.getElementById("notification3")){
            remove("notification3");
        }

        delay1 = document.getElementById("delay1");
        message1 = document.getElementById("message1");

        addNotification.style.display = 'none';

        character.value = 'Jane';
        characterImage.src = `resources/Jane.jpg`
        message1.value = 'You stink like the tuna!';
        document.getElementById('form1').style.display = 'block';
    }
}

if (story4) {
    story4.onclick = function() {
        notification1.style.display = 'block';
        console.log(form.length)

        if(form.length < 9){
            generate(2);
            generate(3);
            
            delay1 = document.getElementById("delay1");
            delay2 = document.getElementById("delay2");
            delay3 = document.getElementById("delay3");

            message1 = document.getElementById("message1");
            message2 = document.getElementById("message2");
            message3 = document.getElementById("message3");
        }
        
        addNotification.style.display = 'none';

        character.value = 'Roberto';
        characterImage.src = `resources/Roberto.jpg`

        delay1.value = 30000;
        message1.value = 'he shat everywhere again';

        delay2.value = 5000;
        message2.value = "its so liquidy";

        delay3.value = 5000;
        message3.value = "its liek if someone blended up a kitkat";

        document.getElementById('form1').style.display = 'block';
    }
}

if (storyDIY) {
    storyDIY.onclick = function() {
        console.log("click on diy")
        notification1.style.display = 'block';
        addNotification.style.display = 'block';

        character.value = 'Jane';
        characterImage.src = `resources/Jane.jpg`
        message1.value = '';
        document.getElementById('form1').style.display = 'block';
    }
}

let reqs_id = 1;

function remove(stringID) {
let elem = document.getElementById(stringID);
  elem.remove()
}

function add() {
  reqs_id++; // increment reqs_id to get a unique ID for the new element

  generate(reqs_id);
}

function generate(num){
    console.log("generating")
    var dom = document.createElement('div');
    let final_string = `<div id="notification${num}">
    <label for="delay${num}">Time before notification ${num}:</label>
    <br>
    <select name="delay${num}" id="delay${num}">
      <option value="5000">5 Seconds</option>
      <option value="10000" selected>10 Seconds</option>
      <option value="30000">30 Seconds</option>
      <option value="60000">60 Seconds</option>
    </select>
    <br>
      <label for="message${num}">Write your message for notification ${num}:</label>
      <input type="text" id="message${num}" name="message${num}" value="" style="width: 300px;" maxlength="280"><br><br>
  </div>`
    dom.innerHTML = final_string;
	messageForm.appendChild(dom);
}

function playStory(){
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            var activeTab = tabs[0];
            chrome.scripting.executeScript({
                target: { tabId: activeTab.id },
                files: ['notifications.js']
              }, function() {
                  console.log(notificationData)
                chrome.tabs.sendMessage(activeTab.id, notificationData);
            });
        });
}


================================================
FILE: resources/stories.json
================================================
{
    "story1":
    {
        "character":"Cassie",
        "delay1":10000,
        "message1": "you packed me the wrong go gurt again",
        "delay2":5000,
        "message2": "you fucking idiot",
        "delay3":30000,
        "message3": "its okay i still love you"
    },

    "story2":
    {
        "character":"Jim",
        "delay1":10000,
        "message1": "hey wanna hear a joke",
        "delay2":30000,
        "message2": "what do you get when you mix human dna with goat dna",
        "delay3":5000,
        "message3": "kicked out of the petting zoo LOL"
    },

    "story3":
    {
        "character":"Jane",
        "delay1":10000,
        "message1": "You stink like the tuna!"
    }
}

================================================
FILE: resources/style.css
================================================
@font-face {
  font-family: "SF-Light";
  src: url("SF-Light.otf");
  font-weight: normal;
  font-style: normal;
}

@font-face {
  font-family: "SF-Medium";
  src: url("SF-Medium.otf");
  font-weight: normal;
  font-style: normal;
}

.notificationBox {
    width: 350pt;
    border-radius: 16pt;
    position:fixed;
    top:10pt;
    right:10%;
    background-color: rgba(255, 255, 255, 0.6);
    backdrop-filter: blur(15px);
    box-shadow: 0px 0px 6px 0px #ccc;
    border: 0px solid #ACACAC;
    z-index: 99999999999999999;
    color:black;
}


.imageContainer {
  position: absolute;
  top:0;
  left:0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  width:80pt;
}

.profilePic {
    width:50%;
    position: absolute;
    left:14pt;
    border-radius: 50%;
    margin:0 !important;
}

.messageIcon {
    width:25%;
    position: relative;
    top: 14pt;
    left:12%;

}

.messageContainer{
    margin-top: 15pt;
    margin-bottom: 15pt;
    align-content: space-between;
    width:70%;
}

.title {
    position: relative;
    top:0pt;
    left:70pt;
    font-size:14pt;
    font-family: "SF-Medium";
}

.message {
    position: relative;
    margin-top: 0pt;
    left:70pt;
    font-size:13pt;
    display: -webkit-box;
    line-height: 1.2;
    font-family: "SF-Light";
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;  
  overflow: hidden;
}

.slideIn {
    -webkit-animation-name: cssAnimation;
    -webkit-animation-duration: 0.3s;
    -webkit-animation-timing-function: ease;
    -webkit-animation-fill-mode: forwards;
  }

  .slideOut {
    -webkit-animation-name: cssAnimation2;
    -webkit-animation-duration: 0.3s;
    -webkit-animation-timing-function: ease;
    -webkit-animation-fill-mode: forwards;
  }
  
  @-webkit-keyframes cssAnimation {
    from {
      -webkit-transform: translate(100%);
    }
    to {
      -webkit-transform: translate(30%);
    }
  }

  @-webkit-keyframes cssAnimation2 {
    from {
      -webkit-transform: translate(30%);
    }
    to {
      -webkit-transform: translate(150%);
    }
  }
  
Download .txt
gitextract_tpmgppc8/

├── .gitignore
├── LICENSE
├── README.md
├── background.js
├── manifest.json
├── notifications.js
├── popup working.js
├── popup.html
├── popup.js
└── resources/
    ├── SF-Light.otf
    ├── SF-Medium.otf
    ├── stories.json
    └── style.css
Download .txt
SYMBOL INDEX (9 symbols across 3 files)

FILE: notifications.js
  function sendNotification (line 1) | function sendNotification(person, messageString) {
  function sleep (line 51) | function sleep(ms) {
  function playMessage (line 55) | async function playMessage(person, message, duration) {
  function playStory (line 66) | async function playStory(request) {

FILE: popup working.js
  function playStory (line 83) | function playStory(){

FILE: popup.js
  function remove (line 215) | function remove(stringID) {
  function add (line 220) | function add() {
  function generate (line 226) | function generate(num){
  function playStory (line 246) | function playStory(){
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (24K chars).
[
  {
    "path": ".gitignore",
    "chars": 9,
    "preview": ".DS_Store"
  },
  {
    "path": "LICENSE",
    "chars": 1066,
    "preview": "MIT License\n\nCopyright (c) 2022 Adnan Aga\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.md",
    "chars": 878,
    "preview": "# Pushy!\n\nCreate awkward situations with my chrome extension that send you messages that look like an iMessage popup\n\nTh"
  },
  {
    "path": "background.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "manifest.json",
    "chars": 367,
    "preview": "{\n    \"name\": \"Pushy!\",\n    \"manifest_version\": 3,\n    \"version\": \"0.1\",\n    \"description\": \"Get a notification on comma"
  },
  {
    "path": "notifications.js",
    "chars": 2633,
    "preview": "function sendNotification(person, messageString) {\n    \n    let notificationBox = document.createElement(\"div\");\n    not"
  },
  {
    "path": "popup working.js",
    "chars": 3136,
    "preview": "let text1 = document.getElementById(\"text1\");\nlet text2 = document.getElementById(\"text2\");\nlet text3 = document.getElem"
  },
  {
    "path": "popup.html",
    "chars": 3706,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <style>\n\n      .header {\n            font-size: 20px;\n            text-align:    cen"
  },
  {
    "path": "popup.js",
    "chars": 7982,
    "preview": "let storyArray = document.querySelectorAll('[id*=\"story\"]');\n\nlet story1 = storyArray[0];\nlet story2 = storyArray[1];\nle"
  },
  {
    "path": "resources/stories.json",
    "chars": 710,
    "preview": "{\n    \"story1\":\n    {\n        \"character\":\"Cassie\",\n        \"delay1\":10000,\n        \"message1\": \"you packed me the wrong"
  },
  {
    "path": "resources/style.css",
    "chars": 2084,
    "preview": "@font-face {\n  font-family: \"SF-Light\";\n  src: url(\"SF-Light.otf\");\n  font-weight: normal;\n  font-style: normal;\n}\n\n@fon"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the adnanaga/pushy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (22.0 KB), approximately 5.7k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!