13 Commits

Author SHA1 Message Date
Jack Case
5ce34bec91 theme_icons works, it's just the opposite of what I thought. 2025-11-23 14:21:16 +00:00
Jack Case
1f04a6227f Merge pull request #8 from GandalfDG/feat_bulma_ui
Convert Browser Action Popup to use Bulma CSS
2025-11-23 09:17:50 -05:00
Jack Case
39d0b7ea23 Merge branch 'main' into feat_bulma_ui 2025-11-23 09:17:12 -05:00
Jack Case
0308aa85f4 remove default_icon key in the hope that theme_icons will work 2025-11-23 14:13:34 +00:00
Jack Case
d1a9cd04b3 add package.zip to .gitignore 2025-11-23 14:05:05 +00:00
Jack Case
d3531afa05 add a logout button and improve logged in ui state handling 2025-11-23 14:04:51 +00:00
Jack Case
f03a7ac85e Merge pull request #7 from GandalfDG/package-script
created script to zip the extension for uploading
2025-11-23 08:34:48 -05:00
Jack Case
8527a9252b created script to zip the extension for uploading
package-extension.sh
2025-11-23 13:32:05 +00:00
Jack Case
a29d43f226 bump minor version and remove a csp permission in manifest.json 2025-11-23 13:29:33 +00:00
Jack Case
b5aeee47ea created script to zip the extension for uploading
package-extension.sh
2025-11-23 13:27:42 +00:00
Jack Case
7faef08713 update manifest.json 2025-11-21 22:52:07 +00:00
Jack Case
e025f3995c signup/login tabs are reactive 2025-11-21 14:07:11 +00:00
Jack Case
39fb3320c9 working on popup styling with bulma 2025-11-19 23:07:12 +00:00
8 changed files with 175 additions and 51 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
scripts/ scripts/
node_modules/ node_modules/
package/ package/
package.zip

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "Slop Farmer", "name": "Slop Farmer",
"version": "0.4.1", "version": "0.5.1",
"author": "Jack Case", "author": "Jack Case",
"description": "Crowd-source AI slop pages and domains", "description": "Crowd-source AI slop pages and domains",
@@ -13,22 +13,21 @@
"*://*.duckduckgo.com/*" "*://*.duckduckgo.com/*"
], ],
"content_security_policy": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; worker-src 'self' blob:", "content_security_policy": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; worker-src 'self'",
"browser_action": { "browser_action": {
"default_icon": "icons/virus_black_32.png",
"default_title": "Slop Farmer", "default_title": "Slop Farmer",
"default_popup": "pages/action_popup.html", "default_popup": "pages/action_popup.html",
"default_area": "navbar", "default_area": "navbar",
"theme_icons": [ "theme_icons": [
{ {
"dark": "icons/virus_white_16.png", "dark": "icons/virus_black_16.png",
"light": "icons/virus_black_16.png", "light": "icons/virus_white_16.png",
"size": 16 "size": 16
}, },
{ {
"dark": "icons/virus_white_32.png", "dark": "icons/virus_black_32.png",
"light": "icons/virus_black_32.png", "light": "icons/virus_white_32.png",
"size": 32 "size": 32
} }
] ]
@@ -44,5 +43,16 @@
"matches": ["*://*.duckduckgo.com/*q=*"], "matches": ["*://*.duckduckgo.com/*q=*"],
"js": ["scripts/content-script.js"] "js": ["scripts/content-script.js"]
} }
] ],
"browser_specific_settings": {
"gecko": {
"data_collection_permissions": {
"required": [
"authenticationInfo",
"personallyIdentifyingInfo"
]
}
}
}
} }

4
package-extension.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/bash
tsc;
zip -r package.zip icons/ pages/ scripts/ styles/ manifest.json;

View File

@@ -1,51 +1,86 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> --> <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
<!-- <script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha/dist/altcha.min.js" type="module"></script> --> <!-- <script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha/dist/altcha.min.js" type="module"></script> -->
<script async src="/scripts/browser-action.js" type="module"></script> <script async src="/scripts/browser-action.js" type="module"></script>
<script async defer src="/scripts/altcha/dist/altcha.js" type="module"></script> <script async defer src="/scripts/altcha/dist/altcha.js" type="module"></script>
<link rel="stylesheet" href="/styles/bulma/bulma.min.css">
<title>Slop Farmer</title> <title>Slop Farmer</title>
</head> </head>
<body> <body>
<nav> <div id="onboarding" style="visibility: visible;" class="hero is-primary not-logged-in">
<button id="signup-select" type="button">sign up</button> <div class="hero-body">
<button id="login-select" type="button">log in</button> <h1 class="title">Welcome, Slop Farmer!</h1>
</nav> <p>tired of ai-generated slop articles in your search results? Sign up or log in to start reporting slop
articles and have reported slop flagged in your searches!</p>
<div id="onboarding" style="visibility: visible;"> </div>
<h1>Welcome, Slop Farmer!</h1>
<p>tired of ai-generated slop articles in your search results? Sign up or log in to start reporting slop articles
and have reported slop flagged in your searches!
</p>
</div> </div>
<div id="signup" style="visibility: collapse;"> <button id="logout-button" class="button is-small logged-in">Logout</button>
<form id="signup-form">
<label for="email">email</label>
<input type="email" name="email" required />
<label for="password">password</label>
<input type="password" name="password" required />
<altcha-widget challengeurl="https://api.slopfarmer.jack-case.pro/altcha-challenge"></altcha-widget>
<button id="signup-button">sign up</button>
</form>
<h2></h2>
</div>
<div id="login" style="visibility: collapse;"> <div class="section">
<h1>Log in to enable slop checking and reporting</h1> <nav class="tabs is-centered not-logged-in">
<form id="login-form"> <ul>
<label for="email" id="username">username</label> <li id="signup-tab"><a>sign up</a></li>
<input type="text" name="username" required /> <li id="login-tab"><a>log in</a></li>
<label for="password" id="password">password</label> </ul>
<input type="password" name="password" required /> </nav>
<button id="login-button">login</button>
</form>
<h2 style="visibility: collapse;" id="login-status">You're logged in.</h2>
</div>
<div id="report" style="visibility: collapse;">
<button id="report-button">Report this page</button>
<h2></h2> <div id="signup" style="display:block" class="box">
<h1 class="title">Sign Up</h1>
<form id="signup-form">
<div class="field">
<label for="email" class="label">E-Mail</label>
<div class="control">
<input type="email" name="email" required class="input" />
</div>
</div>
<div class="field">
<label for="password" class="label">Password</label>
<div class="control">
<input type="password" name="password" required class="input" />
</div>
</div>
<div class="field">
<altcha-widget challengeurl="https://api.slopfarmer.jack-case.pro/altcha-challenge"></altcha-widget>
</div>
<div class="field">
<button id="signup-button" class="button is-primary">sign up</button>
</div>
</form>
<h2></h2>
</div>
<div id="login" style="display:none" class="box">
<h1 class="title">Log In</h1>
<form id="login-form">
<div class="field">
<label for="email" id="username" class="label">E-Mail</label>
<div class="control">
<input type="text" name="username" required class="input" />
</div>
</div>
<div class="field">
<label for="password" id="password" class="label">Password</label>
<div class="control">
<input type="password" name="password" required class="input" />
</div>
</div>
<div class="field">
<button id="login-button" class="button is-primary">log in</button>
</div>
</form>
<h2 style="visibility: collapse;" id="login-status">You're logged in.</h2>
</div>
<div id="report" style="display: none" class="block">
<button id="report-button" class="button is-primary">Report this page</button>
<h2></h2>
</div>
</div> </div>
</body> </body>

View File

@@ -1,3 +1,4 @@
import { getTextOfJSDocComment } from "../node_modules/typescript/lib/typescript.js"
import { API_URL, send_message_to_background } from "./common.js" import { API_URL, send_message_to_background } from "./common.js"
let popup_state: PopupState = null let popup_state: PopupState = null
@@ -10,19 +11,45 @@ class PopupState {
page_elements: Map<string, HTMLElement> page_elements: Map<string, HTMLElement>
constructor(logged_in: boolean, page_sections: Map<string, HTMLElement>, visible_section: string, page_elements: Map<string, HTMLElement>) { visible_logged_in: Array<HTMLElement>
visible_logged_out: Array<HTMLElement>
constructor(logged_in: boolean, page_sections: Map<string, HTMLElement>, visible_section: string, page_elements: Map<string, HTMLElement>, visible_logged_in: Array<HTMLElement>, visible_logged_out: Array<HTMLElement>) {
this.logged_in = logged_in this.logged_in = logged_in
this.page_sections = page_sections this.page_sections = page_sections
this.visible_section = visible_section this.visible_section = visible_section
this.set_visible_section(logged_in ? "report" : "signup")
this.page_elements = page_elements this.page_elements = page_elements
this.visible_logged_in = visible_logged_in
this.visible_logged_out = visible_logged_out
}
update_login_visibility() {
this.visible_logged_in.forEach((element) => {
element.style.display = this.logged_in ? "block" : "none"
})
this.visible_logged_out.forEach((element) => {
element.style.display = this.logged_in ? "none" : "block"
})
} }
set_visible_section(section_id: string) { set_visible_section(section_id: string) {
this.visible_section = section_id this.visible_section = section_id
switch (section_id) {
case "signup":
this.page_elements.get("signup_button").setAttribute("class", "is-active")
this.page_elements.get("login_button").setAttribute("class", "")
break
case "login":
this.page_elements.get("login_button").setAttribute("class", "is-active")
this.page_elements.get("signup_button").setAttribute("class", "")
break
}
this.page_sections.forEach((element, id) => { this.page_sections.forEach((element, id) => {
element.style.visibility = id === section_id ? "visible" : "collapse" element.style.display = id === section_id ? "block" : "none"
}) })
this.update_login_visibility()
} }
} }
@@ -47,6 +74,8 @@ async function submit_login_form() {
const status_el = document.getElementById("login-status") const status_el = document.getElementById("login-status")
status_el.setAttribute("style", "visibility: visible;") status_el.setAttribute("style", "visibility: visible;")
popup_state.logged_in = true
popup_state.set_visible_section("report") popup_state.set_visible_section("report")
} }
else { else {
@@ -70,6 +99,12 @@ async function submit_signup_form() {
} }
} }
async function logout() {
const response = await send_message_to_background({type: "logout"})
popup_state.logged_in = false
popup_state.set_visible_section("login")
}
async function check_login(): Promise<boolean> { async function check_login(): Promise<boolean> {
const response = await send_message_to_background({type: "islogged"}) const response = await send_message_to_background({type: "islogged"})
return response.logged_in return response.logged_in
@@ -85,11 +120,12 @@ async function initialize_popup() {
const login_section = document.getElementById("login") const login_section = document.getElementById("login")
const report_section = document.getElementById("report") const report_section = document.getElementById("report")
const signup_button = document.getElementById("signup-select") as HTMLButtonElement const signup_button = document.getElementById("signup-tab")
const signup_status = signup_section.querySelector("h2") const signup_status = signup_section.querySelector("h2")
const login_button = document.getElementById("login-select") as HTMLButtonElement const login_button = document.getElementById("login-tab")
const report_button = document.getElementById("report-button") as HTMLButtonElement const report_button = document.getElementById("report-button") as HTMLButtonElement
const report_status = report_section.querySelector("h2") const report_status = report_section.querySelector("h2")
const logout_button = document.getElementById("logout-button")
const page_sections = new Map() const page_sections = new Map()
page_sections.set("signup", signup_section) page_sections.set("signup", signup_section)
@@ -103,11 +139,16 @@ async function initialize_popup() {
page_elements.set("signup_status", signup_status) page_elements.set("signup_status", signup_status)
page_elements.set("report_button", report_button) page_elements.set("report_button", report_button)
page_elements.set("report_status", report_status) page_elements.set("report_status", report_status)
page_elements.set("signup_button", signup_button)
page_elements.set("login_button", login_button)
const logged_in_items = Array.from(document.querySelectorAll(".logged-in")) as Array<HTMLElement>
const logged_out_items = Array.from(document.querySelectorAll(".not-logged-in")) as Array<HTMLElement>
const logged_in = await check_login() const logged_in = await check_login()
popup_state = new PopupState(logged_in, page_sections, "signup", page_elements) popup_state = new PopupState(logged_in, page_sections, "signup", page_elements, logged_in_items, logged_out_items)
popup_state.set_visible_section(logged_in ? "report" : "signup")
login_form.addEventListener("submit", (event) => { event.preventDefault(); submit_login_form() }) login_form.addEventListener("submit", (event) => { event.preventDefault(); submit_login_form() })
signup_form.addEventListener("submit", (event) => { event.preventDefault(); submit_signup_form() }) signup_form.addEventListener("submit", (event) => { event.preventDefault(); submit_signup_form() })
@@ -119,6 +160,9 @@ async function initialize_popup() {
popup_state.page_elements.get("report_status").textContent = "report accepted" popup_state.page_elements.get("report_status").textContent = "report accepted"
setTimeout(() => { window.close() }, 1000) setTimeout(() => { window.close() }, 1000)
}) })
logout_button.addEventListener("click", async (event) => {
logout()
})
} }
addEventListener("DOMContentLoaded", (event) => { addEventListener("DOMContentLoaded", (event) => {

View File

@@ -225,6 +225,11 @@ function message_listener(message: any, sender: any, send_response: Function): P
return new Promise((resolve, reject) => { resolve(response) }) return new Promise((resolve, reject) => { resolve(response) })
break break
case "logout":
localStorage.removeItem("accessToken")
return new Promise((resolve, reject) => { resolve(true) })
break
case "report": case "report":
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {

21
styles/bulma/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2023 Jeremy Thomas
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.

3
styles/bulma/bulma.min.css vendored Normal file

File diff suppressed because one or more lines are too long