Add files via upload

This commit is contained in:
Kenneth Hendricks 2026-01-24 20:32:26 -05:00 committed by GitHub
parent d45dae4388
commit 51a049b4b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 918 additions and 204 deletions

View file

@ -34,6 +34,8 @@ const safeListURLs = [
];
const fmhyFilterListURL =
"https://raw.githubusercontent.com/fmhy/FMHY-SafeGuard/refs/heads/main/fmhy-filterlist.txt";
const notesBaseURL =
"https://raw.githubusercontent.com/fmhy/edit/main/docs/.vitepress/notes/";
// State Variables
let unsafeSitesRegex = null;
@ -42,6 +44,7 @@ let fmhySitesRegex = null;
let safeSites = [];
let starredSites = [];
const approvedUrls = new Map(); // Map to store approved URLs per tab
const notesCache = new Map(); // Cache for fetched notes
// List of search engines to check against
const searchEngines = [
@ -73,7 +76,185 @@ const searchEngines = [
"seznam.cz",
];
// Helper Functions
// Notes Mapping - Maps domains to their corresponding FMHY note slugs
const notesMapping = {
// Torrent sites
"1337x.to": "1337x-ranks", "1337x.st": "1337x-ranks", "1337x.is": "1337x-ranks",
"1337x.gd": "1337x-ranks", "1337x.so": "1337x-ranks", "1337x.tw": "1337x-ranks",
// Audiobookbay
"audiobookbay.is": "audiobookbay-warning", "audiobookbay.se": "audiobookbay-warning",
"audiobookbay.fi": "audiobookbay-warning", "audiobookbay.nl": "audiobookbay-warning",
// Aurora Store
"auroraoss.com": "aurora-note",
// APKMirror
"apkmirror.com": "apkmirror-extensions",
// BuzzHeavier
"buzzheavier.com": "buzzheavier-warning",
// ChatGPT
"chat.openai.com": "chatgpt-limits", "chatgpt.com": "chatgpt-limits",
// Crystal Disk Info
"crystalmark.info": "crystaldiskinfo",
// CS.RIN.RU
"cs.rin.ru": "csrin-search",
// DODI Repacks
"dodi-repacks.site": "dodi-warning",
// FileBin
"filebin.net": "filebin-warning",
// FileLu
"filelu.com": "filelu-warning",
// FileZilla
"filezilla-project.org": "filezilla",
// Fluxy Repacks
"fluxyrepacks.site": "fluxy-repacks",
// Foxit Reader
"foxit.com": "foxit-warning",
// FreeGOGPCGames
"freegogpcgames.com": "freegogpcgames-note",
// Glitchwave
"glitchwave.com": "glitchwave-note",
// Google Translate
"translate.google.com": "google-translate-note",
// HDO Box
"hdo.app": "hdo-box-note",
// Hugging Face
"huggingface.co": "hugging-face-warning",
// InstaEclipse
"instaeclipse.com": "instaeclipse-note",
// IRC Highway
"irchighway.net": "irc-highway-note",
// JDownloader
"jdownloader.org": "jdownloader",
// LiteAPK / ModYolo
"liteapks.com": "liteapk-modyolo-note", "modyolo.com": "liteapk-modyolo-note",
// Mobilism
"mobilism.me": "mobilism-ranks", "mobilism.org": "mobilism-ranks",
// ModelScope
"modelscope.cn": "modelscope",
// Mori
"mori.space": "mori-note",
// movie-web / pstream
"movie-web.app": "movie-web", "pstream.org": "movie-web", "pstream.mov": "movie-web",
// MovieParadise
"movieparadise.org": "movieparadise-code",
// MVSEP
"mvsep.com": "mvsep-note",
// OpenAsar
"openasar.dev": "openasar",
// OpenRGB
"openrgb.org": "openrgb-beta",
// Pollinations AI
"pollinations.ai": "pollinations-limits",
// Proton VPN
"protonvpn.com": "proton-torrenting",
// REAPER DAW
"reaper.fm": "reaper-note",
// SaNET / SoftArchive
"sanet.st": "sanet-warning", "sanet.lc": "sanet-warning", "sanet.cd": "sanet-warning",
"softarchive.is": "softarchive-mirrors",
// Soft98
"soft98.ir": "soft98-note",
// Sora
"soraapp.tv": "sora",
// Spicetify
"spicetify.app": "spicetify-note",
// Sport7
"sport7.live": "sport7",
// Steam
"store.steampowered.com": "steam-controller-support",
// Tautulli
"tautulli.com": "tautulli-note",
// TeamSpeak
"teamspeak.com": "teamspeak-warning",
// Thunderbird
"thunderbird.net": "thunderbird",
// TinyURL
"tinyurl.com": "tinyurl-note",
// Video DownloadHelper
"downloadhelper.net": "video-downloadhelper",
// VuenXX
"vuenxx.com": "vuenxx-note",
// WeLib
"welib.org": "welib-note",
// WinRAR
"rarlab.com": "winrar", "win-rar.com": "winrar",
// YTS / Yify
"yts.mx": "yts-yify-note", "yts.rs": "yts-yify-note", "yts.lt": "yts-yify-note",
"yts.am": "yts-yify-note", "yts.ag": "yts-yify-note", "yts.pm": "yts-yify-note",
// 4PDA
"4pda.to": "captcha-4pda",
// Eruda
"eruda.liriliri.io": "eruda",
// Twitch alternate player
"twitch.tv": "alt-twitch-player-extensions",
// WARP alternatives
"1.1.1.1": "alt-warp-clients",
// Eaglercraft
"eaglercraft.com": "eaglercraft-note", "eagler.xyz": "eaglercraft-note",
// Bookmarkeddit
"bookmarkeddit.com": "bookmarkeddit",
// RGShows
"rgshows.to": "rgshows-autoplay", "rgshows.me": "rgshows-autoplay",
// OneClick
"oneclick.download": "oneclick-note",
// Forest
"forestapp.cc": "forest-extensions",
// Flicker proxy
"flicker.city": "flicker-proxy",
// Dolby
"dolby.com": "dolby-access-atmos-note",
// Bypass Freedlink
"freedlink.org": "bypass-freedlink", "freedl.ink": "bypass-freedlink",
// Limit bypass
"12ft.io": "limit-bypass-note", "archive.is": "limit-bypass-note",
// Malware removal forums
"malwaretips.com": "malware-removal-forums", "bleepingcomputer.com": "malware-removal-forums",
// Advanced calculators
"desmos.com": "advanced-logic-calculators", "wolframalpha.com": "advanced-logic-calculators",
"symbolab.com": "advanced-logic-calculators",
};
// Pattern-based matching for dynamic domains
const notesPatterns = [
{ pattern: /^1337x\./i, noteSlug: "1337x-ranks" },
{ pattern: /^yts\./i, noteSlug: "yts-yify-note" },
{ pattern: /^audiobookbay\./i, noteSlug: "audiobookbay-warning" },
{ pattern: /^sanet\./i, noteSlug: "sanet-warning" },
{ pattern: /^softarchive\./i, noteSlug: "softarchive-mirrors" },
{ pattern: /^mobilism\./i, noteSlug: "mobilism-ranks" },
{ pattern: /^rgshows\./i, noteSlug: "rgshows-autoplay" },
];
// Get note slug for a domain
function getNoteSlugForDomain(hostname) {
const domain = hostname.replace(/^www\./, "").toLowerCase();
if (notesMapping[domain]) return notesMapping[domain];
for (const { pattern, noteSlug } of notesPatterns) {
if (pattern.test(domain)) return noteSlug;
}
return null;
}
// Fetch note content from GitHub
async function fetchNoteContent(noteSlug) {
// Check cache first
if (notesCache.has(noteSlug)) {
return notesCache.get(noteSlug);
}
try {
const response = await fetch(`${notesBaseURL}${noteSlug}.md`);
if (!response.ok) {
console.log(`Note not found: ${noteSlug}`);
return null;
}
const content = await response.text();
notesCache.set(noteSlug, content);
return content;
} catch (error) {
console.error(`Error fetching note ${noteSlug}:`, error);
return null;
}
}
function extractUrlsFromMarkdown(markdown) {
const urlRegex = /https?:\/\/[^\s)]+/g;
return markdown.match(urlRegex) || [];
@ -628,6 +809,44 @@ browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
}
return true; // Keep the message channel open for async response
}
// Handle note requests for websites
if (message.action === "getNoteForSite") {
const url = message.url;
if (!url) {
sendResponse({ note: null });
return true;
}
try {
const urlObj = new URL(url);
const noteSlug = getNoteSlugForDomain(urlObj.hostname);
console.log(`getNoteForSite: domain=${urlObj.hostname}, slug=${noteSlug}`);
if (!noteSlug) {
sendResponse({ note: null });
return true;
}
// Fetch note content asynchronously
fetchNoteContent(noteSlug).then(noteContent => {
console.log(`getNoteForSite: fetched content for ${noteSlug}, length=${noteContent?.length || 0}`);
sendResponse({ note: noteContent, slug: noteSlug });
}).catch(error => {
console.error("Error fetching note content:", error);
sendResponse({ note: null, error: error.message });
});
return true; // Keep channel open for async response
} catch (error) {
console.error("Error in getNoteForSite handler:", error);
sendResponse({ note: null, error: error.message });
return true;
}
}
// Return false for unhandled message types
return false;
});
function getStatusFromLists(url) {
@ -733,6 +952,7 @@ browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse({ status: "Settings updated successfully" });
return true; // Indicates asynchronous response handling
}
// Don't return anything for messages we don't handle - let other listeners process them
});
// Listen for tab updates
@ -870,35 +1090,33 @@ async function initializeExtension() {
}
// Extension message handling
browserAPI.runtime.onMessage.addListener(
async (message, sender, sendResponse) => {
if (message.action === "updateAlarm") {
await setupUpdateSchedule();
return true;
}
browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "updateAlarm") {
setupUpdateSchedule().then(() => {
sendResponse({ status: "updated" });
});
return true;
}
if (message.action === "refreshAllTabs") {
// Get all tabs
const tabs = await browserAPI.tabs.query({});
// Send refresh message to all tabs
if (message.action === "refreshAllTabs") {
// Get all tabs and refresh
browserAPI.tabs.query({}).then(async (tabs) => {
for (const tab of tabs) {
try {
await browserAPI.tabs.sendMessage(tab.id, {
action: "refreshSettings",
});
} catch (error) {
// Content script might not be loaded in some tabs, ignore errors
console.log(`Could not refresh tab ${tab.id}: ${error.message}`);
}
}
return true;
}
return false;
sendResponse({ status: "refreshed" });
});
return true;
}
);
// Don't return anything for unhandled messages
});
// Add listener for approval from the warning page
browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {

329
src/js/notes-mapping.js Normal file
View file

@ -0,0 +1,329 @@
// Notes Mapping - Maps domains to their corresponding FMHY note slugs
// The slug is used to fetch: https://raw.githubusercontent.com/fmhy/edit/main/docs/.vitepress/notes/{slug}.md
const notesMapping = {
// 1337x torrent site
"1337x.to": "1337x-ranks",
"1337x.st": "1337x-ranks",
"1337x.is": "1337x-ranks",
"1337x.gd": "1337x-ranks",
"1337x.so": "1337x-ranks",
"1337x.tw": "1337x-ranks",
// Audiobookbay
"audiobookbay.is": "audiobookbay-warning",
"audiobookbay.se": "audiobookbay-warning",
"audiobookbay.fi": "audiobookbay-warning",
"audiobookbay.ws": "audiobookbay-warning",
"audiobookbay.nl": "audiobookbay-warning",
// Aurora Store (F-Droid alternative)
"auroraoss.com": "aurora-note",
// APKMirror
"apkmirror.com": "apkmirror-extensions",
// BuzzHeavier
"buzzheavier.com": "buzzheavier-warning",
// ChatGPT
"chat.openai.com": "chatgpt-limits",
"chatgpt.com": "chatgpt-limits",
// Crystal Disk Info
"crystalmark.info": "crystaldiskinfo",
// CS.RIN.RU
"cs.rin.ru": "csrin-search",
// DODI Repacks
"dodi-repacks.site": "dodi-warning",
// FileBin
"filebin.net": "filebin-warning",
// FileLu
"filelu.com": "filelu-warning",
// FileZilla
"filezilla-project.org": "filezilla",
// Fluxy Repacks
"fluxyrepacks.site": "fluxy-repacks",
// Foxit Reader
"foxit.com": "foxit-warning",
// FreeGOGPCGames
"freegogpcgames.com": "freegogpcgames-note",
// Glitchwave (game database)
"glitchwave.com": "glitchwave-note",
// Google Translate
"translate.google.com": "google-translate-note",
// HDO Box
"hdo.app": "hdo-box-note",
// Hugging Face
"huggingface.co": "hugging-face-warning",
// InstaEclipse (modded Instagram)
"instaeclipse.com": "instaeclipse-note",
// IRC Highway (ebooks)
"irchighway.net": "irc-highway-note",
// JDownloader
"jdownloader.org": "jdownloader",
// LiteAPK / ModYolo
"liteapks.com": "liteapk-modyolo-note",
"modyolo.com": "liteapk-modyolo-note",
// MegaBasterd
"github.com/tonikelope/megabasterd": "megabasterd-note",
// Mobilism
"mobilism.me": "mobilism-ranks",
"mobilism.org": "mobilism-ranks",
// ModelScope
"modelscope.cn": "modelscope",
// Mori (iOS tool)
"mori.space": "mori-note",
// movie-web / pstream
"movie-web.app": "movie-web",
"pstream.org": "movie-web",
"pstream.mov": "movie-web",
// MovieParadise
"movieparadise.org": "movieparadise-code",
// MVSEP (audio separation)
"mvsep.com": "mvsep-note",
// OpenAsar (Discord optimization)
"openasar.dev": "openasar",
// OpenRGB
"openrgb.org": "openrgb-beta",
"gitlab.com/CalcProgrammer1/OpenRGB": "openrgb-beta",
// Pollinations AI
"pollinations.ai": "pollinations-limits",
// Proton VPN (torrenting)
"protonvpn.com": "proton-torrenting",
// REAPER DAW
"reaper.fm": "reaper-note",
// SaNET
"sanet.st": "sanet-warning",
"sanet.lc": "sanet-warning",
"sanet.cd": "sanet-warning",
// Soft98 (Persian software)
"soft98.ir": "soft98-note",
// SoftArchive
"softarchive.is": "softarchive-mirrors",
"sanet.st": "softarchive-mirrors",
// Sora (video streaming)
"soraapp.tv": "sora",
// Spicetify
"spicetify.app": "spicetify-note",
// Sport7 streams
"sport7.live": "sport7",
// Steam (controller support & currency)
"store.steampowered.com": "steam-controller-support",
"steampowered.com": "steam-currency-converter-note",
// Tautulli (Plex monitoring)
"tautulli.com": "tautulli-note",
// TeamSpeak
"teamspeak.com": "teamspeak-warning",
// Thunderbird
"thunderbird.net": "thunderbird",
// TinyURL
"tinyurl.com": "tinyurl-note",
// Video DownloadHelper
"downloadhelper.net": "video-downloadhelper",
// VuenXX (video app)
"vuenxx.com": "vuenxx-note",
// WeLib (library thing)
"welib.org": "welib-note",
// WinRAR
"rarlab.com": "winrar",
"win-rar.com": "winrar",
// YTS / Yify (multiple domains)
"yts.mx": "yts-yify-note",
"yts.rs": "yts-yify-note",
"yts.lt": "yts-yify-note",
"yts.am": "yts-yify-note",
"yts.ag": "yts-yify-note",
"yts.pm": "yts-yify-note",
"yify-torrent.org": "yts-yify-note",
"yifysubtitles.org": "yts-yify-note",
// 4PDA (Russian mobile forum)
"4pda.to": "captcha-4pda",
// Buster captcha solver
"github.com/nickyout/buster-client": "buster-note",
// Eruda (mobile console)
"eruda.liriliri.io": "eruda",
// Twitch alternate player extensions
"twitch.tv": "alt-twitch-player-extensions",
// Cloudflare WARP alternatives
"1.1.1.1": "alt-warp-clients",
// Spotify Android mods
"github.com/xManager-App/xManager": "android-spotify-note",
// Cofi (coffee timer)
"github.com/rozPierog/Cofi": "cofi-note",
// Eaglercraft (web Minecraft)
"eaglercraft.com": "eaglercraft-note",
"eagler.xyz": "eaglercraft-note",
// Bookmarkeddit
"bookmarkeddit.com": "bookmarkeddit",
// Reddit Filter
"redditfilter.com": "redditfilter-note",
// RGShows autoplay
"rgshows.to": "rgshows-autoplay",
"rgshows.me": "rgshows-autoplay",
// SD Maid
"github.com/d4rken-org/sdmaid-se": "sd-maid",
// OneClick (debrid)
"oneclick.download": "oneclick-note",
// Forest focus timer
"forestapp.cc": "forest-extensions",
// Flicker proxy
"flicker.city": "flicker-proxy",
// Dolby Access/Atmos
"dolby.com": "dolby-access-atmos-note",
// Better reasoning (AI)
"better-reasoning.com": "better-reasoning",
// Bypass Freedlink
"freedlink.org": "bypass-freedlink",
"freedl.ink": "bypass-freedlink",
// App Lock apps
"github.com/AkaneTan/Privacy-App-Lock": "app-lock",
// Clipboard to file extensions
"addons.mozilla.org/firefox/addon/clipboard2file": "clipboard2file-addons",
// Limit bypass services
"12ft.io": "limit-bypass-note",
"archive.is": "limit-bypass-note",
// PrintEdit WE
"nickyout.github.io/PrintEdit-WE": "printeditwe-addons",
// SavePageWE
"nickyout.github.io/SavePageWE": "savepagewe",
// Scroll Anywhere
"addons.mozilla.org/firefox/addon/scrollanywhere": "scrollanywhere-addons",
// Site Favicon Download
"realfavicongenerator.net": "site-favicon-dl",
// TabIverse
"tabiverse.com": "tabiverse-extensions",
// SH.ST shortener
"sh.st": "sh-note",
"sht.st": "sh-note",
// General driver sites
"driverpack.io": "driver-note",
"drivereasy.com": "driver-note",
// Google song identification
"google.com/search?q=song+identification": "google-song-identification",
// Malware removal forums
"malwaretips.com": "malware-removal-forums",
"bleepingcomputer.com": "malware-removal-forums",
// Yet Another Call Blocker
"gitlab.com/nickyout/YetAnotherCallBlocker": "yet-another-call-blocker-note",
// YouTube Tweaks
"addons.mozilla.org/firefox/addon/youtube-tweaks": "youtube-tweaks",
"chrome.google.com/webstore/detail/youtube-tweaks": "youtube-tweaks",
// Advanced Logic Calculators
"desmos.com": "advanced-logic-calculators",
"wolframalpha.com": "advanced-logic-calculators",
"symbolab.com": "advanced-logic-calculators",
};
// Pattern-based matching for dynamic domains
const notesPatterns = [
{ pattern: /^1337x\./i, noteSlug: "1337x-ranks" },
{ pattern: /^yts\./i, noteSlug: "yts-yify-note" },
{ pattern: /^audiobookbay\./i, noteSlug: "audiobookbay-warning" },
{ pattern: /^sanet\./i, noteSlug: "sanet-warning" },
{ pattern: /^softarchive\./i, noteSlug: "softarchive-mirrors" },
{ pattern: /^mobilism\./i, noteSlug: "mobilism-ranks" },
{ pattern: /^rgshows\./i, noteSlug: "rgshows-autoplay" },
];
// Function to get note slug for a domain
function getNoteSlugForDomain(hostname) {
// Remove www. prefix
const domain = hostname.replace(/^www\./, "").toLowerCase();
// Check exact match first
if (notesMapping[domain]) {
return notesMapping[domain];
}
// Check pattern matches
for (const { pattern, noteSlug } of notesPatterns) {
if (pattern.test(domain)) {
return noteSlug;
}
}
return null;
}
// Export for use in background.js
if (typeof module !== "undefined" && module.exports) {
module.exports = { notesMapping, notesPatterns, getNoteSlugForDomain };
}

View file

@ -1,214 +1,284 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FMHY SafeGuard</title>
<style>
@font-face {
font-family: "Inter";
src: url("../res/fonts/inter.woff2") format("woff2");
}
:root {
--background-color: rgb(26, 26, 26);
--text-color: #848a94;
--text-color-light: #e8e8e8;
--accent-color: #c4b5fd;
--link-color: #78b3e2;
--hover-bg: rgba(255, 255, 255, 0.1);
}
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FMHY SafeGuard</title>
<style>
@font-face {
font-family: "Inter";
src: url("../res/fonts/inter.woff2") format("woff2");
}
[data-theme="light"] {
--background-color: #f5f5f5;
--text-color: #4b5563;
--text-color-light: #1a1a1a;
--accent-color: #6c63ff;
--link-color: #0366d6;
--hover-bg: rgba(0, 0, 0, 0.1);
}
:root {
--background-color: rgb(26, 26, 26);
--text-color: #848a94;
--text-color-light: #e8e8e8;
--accent-color: #c4b5fd;
--link-color: #78b3e2;
--hover-bg: rgba(255, 255, 255, 0.1);
}
body {
text-align: center;
background-color: var(--background-color);
font-family: "Inter", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
color: var(--text-color);
margin: 0;
padding: 10px;
min-width: 300px;
max-width: 320px; /* Limit the width to make it more compact */
position: relative; /* For absolute positioning of settings button */
}
[data-theme="light"] {
--background-color: #f5f5f5;
--text-color: #4b5563;
--text-color-light: #1a1a1a;
--accent-color: #6c63ff;
--link-color: #0366d6;
--hover-bg: rgba(0, 0, 0, 0.1);
}
.header {
position: relative;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
padding-right: 25px;
}
body {
text-align: center;
background-color: var(--background-color);
font-family: "Inter", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
color: var(--text-color);
margin: 0;
padding: 10px;
min-width: 300px;
max-width: 320px;
/* Limit the width to make it more compact */
position: relative;
/* For absolute positioning of settings button */
}
.title h1 {
font-size: 24px;
background: -webkit-linear-gradient(
120deg,
.header {
position: relative;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
padding-right: 25px;
}
.title h1 {
font-size: 24px;
background: -webkit-linear-gradient(120deg,
var(--accent-color) 30%,
var(--link-color)
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 0;
padding: 10px;
}
var(--link-color));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
margin: 0;
padding: 10px;
}
/* Settings Button */
.settings-button {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
padding: 8px;
border-radius: 8px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
/* Settings Button */
.settings-button {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
padding: 8px;
border-radius: 8px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.settings-button:hover {
background: var(--hover-bg);
}
.settings-button:hover {
background: var(--hover-bg);
}
.settings-button svg {
width: 20px;
height: 20px;
transition: transform 0.3s ease;
}
.settings-button svg {
width: 20px;
height: 20px;
transition: transform 0.3s ease;
}
.settings-button:hover svg {
transform: rotate(45deg);
}
.settings-button:hover svg {
transform: rotate(45deg);
}
.settings-button path {
fill: var(--text-color);
transition: fill 0.3s ease;
}
.settings-button path {
fill: var(--text-color);
transition: fill 0.3s ease;
}
.settings-button:hover path {
fill: var(--accent-color);
}
.settings-button:hover path {
fill: var(--accent-color);
}
#status-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 10px;
}
#status-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 10px;
}
.status-image {
width: 60px; /* Reduced icon size */
height: 60px;
transition: transform 0.3s ease;
}
.status-image {
width: 60px;
/* Reduced icon size */
height: 60px;
transition: transform 0.3s ease;
}
#status-message {
font-size: 14px; /* Smaller text for compact design */
margin-top: 10px;
color: var(--text-color-light);
line-height: 1.3;
padding: 0 10px;
}
#status-message {
font-size: 14px;
/* Smaller text for compact design */
margin-top: 10px;
color: var(--text-color-light);
line-height: 1.3;
padding: 0 10px;
}
#status-message strong {
color: var(--accent-color);
}
#status-message strong {
color: var(--accent-color);
}
#error-message {
color: red;
margin-top: 10px;
}
#error-message {
color: red;
margin-top: 10px;
}
#footer {
font-size: 11px; /* Smaller footer text */
margin-top: 10px;
color: var(--text-color);
}
#footer {
font-size: 11px;
/* Smaller footer text */
margin-top: 10px;
color: var(--text-color);
}
#footer a {
color: var(--link-color);
text-decoration: none;
}
#footer a {
color: var(--link-color);
text-decoration: none;
}
#footer a:hover {
text-decoration: underline;
}
#footer a:hover {
text-decoration: underline;
}
#feedback {
font-size: 12px;
margin-top: 10px;
color: var(--text-color);
}
#feedback {
font-size: 12px;
margin-top: 10px;
color: var(--text-color);
}
#feedback a {
color: var(--link-color);
text-decoration: none;
}
#feedback a {
color: var(--link-color);
text-decoration: none;
}
#feedback a:hover {
text-decoration: underline;
}
#feedback a:hover {
text-decoration: underline;
}
/* Animation for status change */
.status-image.active {
transform: scale(1.1);
}
</style>
</head>
<body>
<div class="header">
<div class="title">
<h1>FMHY SafeGuard</h1>
</div>
<button class="settings-button" id="settingsButton" title="Open Settings">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.65.07-.97 0-.32-.03-.65-.07-.97l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65c-.04-.24-.25-.42-.5-.42h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 12c-.04.32-.07.65-.07.97 0 .32.03.65.07.97l-2.11 1.63c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.31.61.22l2.49-1c.52.39 1.06.73 1.69.98l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.25 1.17-.59 1.69-.98l2.49 1c.22-.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.63Z"
/>
</svg>
</button>
/* Animation for status change */
.status-image.active {
transform: scale(1.1);
}
/* Note Section Styles */
#note-container {
margin-top: 15px;
padding: 10px;
background: var(--hover-bg);
border-radius: 8px;
text-align: left;
display: none;
}
#note-container.visible {
display: block;
}
#note-title {
font-size: 13px;
font-weight: 600;
color: var(--accent-color);
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 6px;
}
#note-content {
font-size: 12px;
line-height: 1.5;
color: var(--text-color-light);
}
#note-content h4 {
font-size: 13px;
margin: 0 0 8px 0;
color: var(--accent-color);
}
#note-content ul {
margin: 5px 0;
padding-left: 20px;
}
#note-content li {
margin: 3px 0;
}
#note-content a {
color: var(--link-color);
text-decoration: none;
}
#note-content a:hover {
text-decoration: underline;
}
#note-content code {
background: rgba(0, 0, 0, 0.2);
padding: 1px 4px;
border-radius: 3px;
font-size: 11px;
}
</style>
</head>
<body>
<div class="header">
<div class="title">
<h1>FMHY SafeGuard</h1>
</div>
<div id="status-container">
<img
id="status-icon"
class="status-image"
src="safe.png"
alt="Status Icon"
/>
<p id="status-message">Checking site status...</p>
</div>
<p id="error-message"></p>
<div id="feedback">
<p>
Think this is a mistake?
<a href="https://github.com/fmhy/FMHY-SafeGuard/issues" target="_blank"
>Let us know</a
>.
</p>
</div>
<div id="footer">
<p>
Powered by
<a href="https://github.com/fmhy/FMHYFilterlist" target="_blank"
>FMHY Filterlist</a
>
</p>
<button class="settings-button" id="settingsButton" title="Open Settings">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.65.07-.97 0-.32-.03-.65-.07-.97l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65c-.04-.24-.25-.42-.5-.42h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 12c-.04.32-.07.65-.07.97 0 .32.03.65.07.97l-2.11 1.63c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.31.61.22l2.49-1c.52.39 1.06.73 1.69.98l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.25 1.17-.59 1.69-.98l2.49 1c.22-.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.63Z" />
</svg>
</button>
</div>
<div id="status-container">
<img id="status-icon" class="status-image" src="safe.png" alt="Status Icon" />
<p id="status-message">Checking site status...</p>
</div>
<p id="error-message"></p>
<div id="note-container">
<div id="note-title">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
</svg>
FMHY Note
</div>
<div id="note-content"></div>
</div>
<div id="feedback">
<p>
Think this is a mistake?
<a href="https://github.com/fmhy/FMHY-SafeGuard/issues" target="_blank">Let us know</a>.
</p>
</div>
<div id="footer">
<p>
Powered by
<a href="https://github.com/fmhy/FMHYFilterlist" target="_blank">FMHY Filterlist</a>
</p>
</div>
<script src="index.js"></script>
</body>
</html>
<script src="index.js"></script>
</body>
</html>

View file

@ -4,6 +4,8 @@ document.addEventListener("DOMContentLoaded", async () => {
const statusIcon = document.getElementById("status-icon");
const statusMessage = document.getElementById("status-message");
const errorMessage = document.getElementById("error-message");
const noteContainer = document.getElementById("note-container");
const noteContent = document.getElementById("note-content");
const browserAPI = typeof browser !== "undefined" ? browser : chrome;
@ -17,6 +19,9 @@ document.addEventListener("DOMContentLoaded", async () => {
// Check site status immediately
await checkSiteStatus();
// Check for notes for this site
await fetchNoteForSite();
// Add settings button listener
document.getElementById("settingsButton").addEventListener("click", () => {
browserAPI.runtime.openOptionsPage();
@ -37,6 +42,98 @@ document.addEventListener("DOMContentLoaded", async () => {
}
}
// Simple markdown to HTML converter
function parseMarkdown(md) {
if (!md) return "";
let result = md
// Remove the main header (#### Title) since we show "FMHY Note" already
.replace(/^#{1,4}\s+.*$/gm, '')
// Trim leading/trailing whitespace
.trim()
// Bold (must come before italic)
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
// Italic
.replace(/\*(.*?)\*/g, '<em>$1</em>')
// Links
.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank">$1</a>')
// Code
.replace(/`(.*?)`/g, '<code>$1</code>')
// List items - convert to proper list
.replace(/^[\*\-]\s+(.*)$/gm, '<li>$1</li>');
// Wrap consecutive li tags in ul
result = result.replace(/(<li>.*?<\/li>\s*)+/gs, '<ul>$&</ul>');
// Convert double newlines to paragraph breaks
result = result.replace(/\n\n+/g, '</p><p>');
// Convert single newlines to line breaks
result = result.replace(/\n/g, '<br>');
// Wrap in paragraph if content exists
if (result.trim()) {
result = '<p>' + result + '</p>';
// Clean up empty paragraphs
result = result.replace(/<p>\s*<\/p>/g, '');
}
return result;
}
// Fetch and display note for current site
async function fetchNoteForSite() {
console.log("fetchNoteForSite: Starting note fetch...");
try {
const [activeTab] = await browserAPI.tabs.query({
active: true,
currentWindow: true,
});
console.log("fetchNoteForSite: Active tab:", activeTab?.url);
if (!activeTab || !activeTab.url) {
console.log("fetchNoteForSite: No active tab or URL");
return;
}
// Skip extension pages
if (activeTab.url.startsWith(browserAPI.runtime.getURL(""))) {
console.log("fetchNoteForSite: Skipping extension page");
return;
}
console.log("fetchNoteForSite: Sending message to background...");
// Use callback style for better cross-browser compatibility
browserAPI.runtime.sendMessage(
{ action: "getNoteForSite", url: activeTab.url },
(response) => {
console.log("fetchNoteForSite: Got response in callback:", response);
if (browserAPI.runtime.lastError) {
console.error("fetchNoteForSite: Runtime error:", browserAPI.runtime.lastError);
return;
}
if (response && response.note) {
const htmlContent = parseMarkdown(response.note);
console.log("fetchNoteForSite: Parsed HTML:", htmlContent.substring(0, 100));
noteContent.innerHTML = htmlContent;
noteContainer.classList.add("visible");
console.log(`fetchNoteForSite: Displayed note for: ${response.slug}`);
} else {
console.log("fetchNoteForSite: No note found or null response");
noteContainer.classList.remove("visible");
}
}
);
} catch (error) {
console.error("fetchNoteForSite: Error:", error);
noteContainer.classList.remove("visible");
}
}
async function checkSiteStatus() {
console.log("Checking site status from popup");
try {