1
0

Enhance popup UI and functionality: update styles, improve input handling, and add max archive settings

This commit is contained in:
Chipperfluff 2026-01-20 06:11:32 +01:00
parent cb062589fa
commit 49bbce95ea
4 changed files with 264 additions and 80 deletions

View File

@ -13,8 +13,7 @@ let generatedIdCounter = 0;
let hudDirty = false;
let lastHudSignature = "";
const STORAGE_KEY = "memberOnlyHidden";
const MAX_GLOBAL_ITEMS = 200;
const MAX_PER_CHANNEL = 20;
const DEFAULT_MAX_ARCHIVE = 500;
let persistTimer = null;
/* ---------------- CSS injection ---------------- */
@ -259,7 +258,7 @@ function process(root = document) {
const channelUrl = channelInfo.url || "";
const channelKey = channelInfo.key;
const id = getOrCreateVideoId(video, url);
const stableId = getStableId(video, url);
const stableId = getStableId(video, url, id);
video.setAttribute(`data-${DATA_PREFIX}-member-only`, "true");
if (channelKey) video.setAttribute(`data-${DATA_PREFIX}-channel`, channelKey);
@ -374,11 +373,11 @@ function getOrCreateVideoId(video, url) {
return generated;
}
function getStableId(video, url) {
function getStableId(video, url, fallbackId) {
const dataId = video.getAttribute("data-video-id");
if (dataId) return dataId;
if (url) return url;
return "";
return fallbackId || "";
}
// Read the channel key from DOM or fallback to text.
@ -490,6 +489,9 @@ function updateHudDot() {
}
function updateSharedIndex(meta, hidden) {
if (!meta.stableId) {
meta.stableId = meta.url || meta.id || "";
}
if (!meta.stableId) return;
if (hidden) {
sharedIndex.set(meta.stableId, {
@ -500,7 +502,8 @@ function updateSharedIndex(meta, hidden) {
title: meta.title,
url: meta.url,
thumb: meta.thumb,
hidden: true
hidden: true,
lastSeen: Date.now()
});
} else {
sharedIndex.delete(meta.stableId);
@ -518,20 +521,16 @@ function schedulePersistSharedIndex() {
function persistSharedIndex() {
const items = Array.from(sharedIndex.values());
items.sort((a, b) => a.id.localeCompare(b.id));
const perChannelCounts = new Map();
const pruned = [];
for (const item of items) {
const key = item.channelKey || item.channelLabel || "(unknown)";
const count = perChannelCounts.get(key) || 0;
if (count >= MAX_PER_CHANNEL) continue;
if (pruned.length >= MAX_GLOBAL_ITEMS) break;
perChannelCounts.set(key, count + 1);
pruned.push(item);
items.sort((a, b) => (b.lastSeen || 0) - (a.lastSeen || 0));
chrome.storage.local.get({ maxArchive: DEFAULT_MAX_ARCHIVE }, data => {
const maxArchive = Number(data.maxArchive);
const max = Number.isFinite(maxArchive) ? maxArchive : DEFAULT_MAX_ARCHIVE;
if (max < 0) {
chrome.storage.local.set({ [STORAGE_KEY]: items });
return;
}
chrome.storage.local.set({ [STORAGE_KEY]: pruned });
chrome.storage.local.set({ [STORAGE_KEY]: items.slice(0, max) });
});
}
function loadStoredIndex() {

182
popup.css
View File

@ -1,19 +1,58 @@
/* Base layout */
body {
font-family: "Trebuchet MS", "Verdana", sans-serif;
min-width: 260px;
font-family: "Oswald", "Impact", "Franklin Gothic Medium", sans-serif;
min-width: 460px;
margin: 0;
padding: 12px;
background: linear-gradient(180deg, #f7f3e8, #efe6d2);
color: #2b2014;
padding: 0;
background: radial-gradient(circle at 20% 10%, #2b2b2b, #151515 60%);
color: #f5efe6;
}
/* Title */
h3 {
margin: 0 0 10px;
letter-spacing: 0.4px;
.scroll {
max-height: 640px;
overflow-y: auto;
padding-bottom: 14px;
}
:root {
--orange: #e35b14;
--orange-strong: #ff7a1a;
--dark: #171717;
--dark-2: #1f1f1f;
--cream: #f5efe6;
--muted: #c7b8a4;
}
.topbar {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 14px;
background: linear-gradient(90deg, var(--orange), #c9490e);
border-bottom: 2px solid #b2410e;
}
.logo {
width: 24px;
height: 24px;
border-radius: 6px;
background: radial-gradient(circle, #fff1d6, #ffb256);
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.25);
}
.title-wrap h3 {
margin: 0;
letter-spacing: 1px;
text-transform: uppercase;
font-size: 13px;
font-size: 16px;
color: #fff8ee;
}
.subtitle {
font-size: 10px;
letter-spacing: 0.8px;
text-transform: uppercase;
color: #2a1506;
}
.subtext {
@ -32,47 +71,110 @@ h3 {
display: flex;
gap: 6px;
align-items: center;
margin-bottom: 8px;
margin: 8px 12px 0;
}
details.section {
margin: 10px 10px 0;
padding: 6px 6px 10px;
border-radius: 10px;
background: rgba(15, 15, 15, 0.55);
border: 1px solid #2a2a2a;
}
details.section > summary {
list-style: none;
cursor: pointer;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.6px;
font-size: 11px;
color: #ffd8b0;
padding: 6px 6px 2px;
}
details.section > summary::-webkit-details-marker {
display: none;
}
details.section > summary::before {
content: "▸";
margin-right: 6px;
color: #ffb26a;
}
details.section[open] > summary::before {
content: "▾";
}
/* Description */
.subtext {
margin: 10px 12px 0;
font-size: 11px;
line-height: 1.35;
color: var(--muted);
font-family: "Verdana", sans-serif;
}
.subtext.subtext-debug {
margin-top: 6px;
}
.row.settings {
justify-content: space-between;
margin-top: 10px;
}
.hint {
margin: 6px 12px 0;
font-size: 11px;
color: #ffb26a;
font-family: "Verdana", sans-serif;
min-height: 14px;
}
/* Inputs */
input {
flex: 1;
padding: 6px 8px;
border: 1px solid #cdbf9e;
border-radius: 6px;
background: #fffaf0;
padding: 8px 10px;
border: 1px solid #393939;
border-radius: 8px;
background: var(--dark-2);
font-size: 12px;
color: var(--cream);
outline: none;
}
/* Buttons */
button {
padding: 6px 10px;
border: 1px solid #b2985b;
border-radius: 6px;
background: #d9c18c;
color: #2b2014;
padding: 8px 12px;
border: 1px solid #9b3b0c;
border-radius: 8px;
background: var(--orange);
color: #1f0f06;
font-size: 12px;
cursor: pointer;
transition: transform 0.08s ease, filter 0.15s ease;
}
button.ghost {
background: transparent;
border-color: #cdbf9e;
border-color: #4a3a2a;
color: var(--cream);
}
button:hover {
filter: brightness(0.95);
filter: brightness(1.05);
}
button:active {
transform: translateY(1px);
}
/* List */
ul {
padding-left: 0;
margin: 0;
margin: 10px 12px 12px;
list-style: none;
}
@ -81,18 +183,20 @@ li {
display: flex;
justify-content: space-between;
margin: 4px 0;
padding: 6px 8px;
background: #fffaf0;
border: 1px solid #e2d7be;
border-radius: 6px;
padding: 8px 10px;
background: #232323;
border: 1px solid #3a2c22;
border-radius: 8px;
font-size: 12px;
color: var(--cream);
}
li button {
padding: 2px 8px;
border-radius: 999px;
border-color: #c95a3a;
background: #f2a285;
border-color: #9b3b0c;
background: var(--orange-strong);
color: #1f0f06;
}
/* Debug toggle row */
@ -101,4 +205,24 @@ li button {
gap: 6px;
align-items: center;
font-size: 12px;
color: var(--cream);
}
.section.explain {
margin: 10px 12px 12px;
padding: 10px 12px;
border-radius: 10px;
background: #1c1c1c;
border: 1px solid #2b2b2b;
font-family: "Verdana", sans-serif;
font-size: 11px;
color: var(--muted);
}
.explain-title {
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.6px;
color: #ffb26a;
margin-bottom: 6px;
}

View File

@ -6,14 +6,23 @@
<link rel="stylesheet" href="popup.css" />
</head>
<body>
<div class="scroll">
<!-- Title + short description -->
<h3>Channel Whitelist</h3>
<div class="topbar">
<div class="logo"></div>
<div class="title-wrap">
<h3>ANTI-BS</h3>
<div class="subtitle">Channel whitelist</div>
</div>
</div>
<p class="subtext">
Hide member-only videos by default. Whitelisted creators stay visible.
Add the channel name and its @handle or link.
</p>
<!-- Inputs -->
<details class="section" open>
<summary>Whitelist a creator</summary>
<div class="row">
<input id="channelInput" placeholder="Channel name" />
</div>
@ -21,8 +30,20 @@
<input id="channelLinkInput" placeholder="Channel link or @handle" />
<button id="addBtn">Add</button>
</div>
<div id="addError" class="hint"></div>
</details>
<details class="section">
<summary>Archive settings</summary>
<div class="row">
<input id="maxInput" type="number" placeholder="Max archived (-1 = unlimited)" />
<button id="saveMaxBtn">Save max</button>
</div>
</details>
<!-- Settings -->
<details class="section">
<summary>App settings</summary>
<div class="row settings">
<label class="toggle">
<input id="debugToggle" type="checkbox" />
@ -33,9 +54,30 @@
<p class="subtext subtext-debug">
Debug logs will appear in the console only when enabled.
</p>
</details>
<p class="subtext">
Bottom-right dot: grey means none detected. Orange pulsing means member-only
videos were found. Click it to see the list.
</p>
<!-- Whitelist list -->
<details class="section" open>
<summary>Whitelisted creators</summary>
<ul id="channelList"></ul>
</details>
<div class="section explain">
<div class="explain-title">For non-technical users</div>
<p>
This tool hides members-only videos you cant watch. Add creators you
support to the whitelist so their videos stay visible.
</p>
<p>
The orange dot shows when hidden members-only videos exist. Click it to
see a list of whats hidden.
</p>
</div>
</div>
<script src="popup.js"></script>
</body>

View File

@ -2,6 +2,9 @@
const input = document.getElementById("channelInput");
const linkInput = document.getElementById("channelLinkInput");
const addBtn = document.getElementById("addBtn");
const maxInput = document.getElementById("maxInput");
const saveMaxBtn = document.getElementById("saveMaxBtn");
const addError = document.getElementById("addError");
const list = document.getElementById("channelList");
const debugToggle = document.getElementById("debugToggle");
const resetBtn = document.getElementById("resetBtn");
@ -9,14 +12,16 @@ const resetBtn = document.getElementById("resetBtn");
// Default settings payload.
const DEFAULTS = {
whitelist: [],
debug: false
debug: false,
maxArchive: 200
};
// Render whitelist and debug state.
function loadChannels() {
chrome.storage.local.get(DEFAULTS, ({ whitelist, debug }) => {
chrome.storage.local.get(DEFAULTS, ({ whitelist, debug, maxArchive }) => {
list.innerHTML = "";
debugToggle.checked = Boolean(debug);
maxInput.value = String(maxArchive);
const entries = normalizeWhitelist(whitelist);
entries.forEach((entry, index) => {
const li = document.createElement("li");
@ -39,9 +44,16 @@ function loadChannels() {
function addChannel() {
const name = input.value.trim();
const link = linkInput.value.trim();
if (!name || !link) return;
if (!name || !link) {
addError.textContent = "Need both name + @handle/link.";
return;
}
const handle = normalizeHandle(link);
if (!handle) return;
if (!handle) {
addError.textContent = "Invalid link or @handle.";
return;
}
addError.textContent = "";
chrome.storage.local.get(DEFAULTS, ({ whitelist }) => {
const entries = normalizeWhitelist(whitelist);
@ -92,8 +104,15 @@ function normalizeHandle(value) {
return "";
}
function saveMaxArchive() {
const value = parseInt(maxInput.value, 10);
if (Number.isNaN(value)) return;
chrome.storage.local.set({ maxArchive: value }, loadChannels);
}
// Wire UI events.
addBtn.onclick = addChannel;
saveMaxBtn.onclick = saveMaxArchive;
debugToggle.onchange = e => setDebug(e.target.checked);
resetBtn.onclick = resetDefaults;
loadChannels();