// 1. Upgraded to "v23" - Added Real-Time Results Counter
const stateKey = "dv_search_state_v23_" + dv.current().file.name;
const defaultState = {
term: "",
cat: true, chan: true, searchUsp: true, searchTags: true,
rUnder3: false, r3: false, r35: false, r4: false, r45: false, r5: true, rNone: false,
requireUsp: false, sourceType: "youtube",
cCategory: true, cThumb: true, cIcon: true, cUsp: true, cPlaylists: true, cSubs: true, cRating: true,
sFire: true, sRocket: true, sCompass: true, sBrain: true, sSexy: true, sGem: true, sKing: true,
sOfficial: true, sLegendary: true, sWizard: true, sUni: true, sHorror: true, sRelax: true, sWow: true,
sNone: true,
activeMin: 0, activeMax: 5,
quickTag: "",
advancedOpen: false
};
let state;
try {
const saved = localStorage.getItem(stateKey);
state = saved ? { ...defaultState, ...JSON.parse(saved) } : defaultState;
} catch (e) {
state = defaultState;
}
function saveState() {
localStorage.setItem(stateKey, JSON.stringify(state));
}
function renderTable() {
dv.container.innerHTML = "";
const uiContainer = dv.el("div", "", { attr: { style: "margin-bottom: 10px; display: flex; flex-direction: column; gap: 6px;" } });
// --- ROW 1: SEARCH, SOURCE SELECT & COUNTER ---
const topRow = document.createElement("div");
topRow.style.display = "flex";
topRow.style.gap = "10px";
topRow.style.alignItems = "center";
const searchWrapper = document.createElement("div");
searchWrapper.style.display = "flex";
searchWrapper.style.width = "40%";
searchWrapper.style.gap = "4px";
const clearBtn = document.createElement("button");
clearBtn.innerText = "✖";
clearBtn.addEventListener("click", () => { state.term = ""; saveState(); renderTable(); });
const searchBox = document.createElement("input");
searchBox.type = "text";
searchBox.placeholder = "Search...";
searchBox.value = state.term;
searchBox.style.width = "100%";
searchBox.style.padding = "4px 8px";
searchWrapper.appendChild(clearBtn);
searchWrapper.appendChild(searchBox);
topRow.appendChild(searchWrapper);
const allSources = new Set(dv.pages('""').where(p => p.source_type).map(p => p.source_type));
const uniqueSources = ["All", ...Array.from(allSources).flat().filter(Boolean)].sort();
const sourceSelect = document.createElement("select");
uniqueSources.forEach(type => {
const opt = document.createElement("option");
opt.value = type; opt.innerText = type.charAt(0).toUpperCase() + type.slice(1);
if (state.sourceType.toLowerCase() === type.toLowerCase()) opt.selected = true;
sourceSelect.appendChild(opt);
});
sourceSelect.addEventListener("change", (e) => { state.sourceType = e.target.value; saveState(); renderTable(); });
topRow.appendChild(sourceSelect);
// NEW: Placeholder for the results counter (pushed to the right)
const countDisplay = document.createElement("div");
countDisplay.style.marginLeft = "auto";
countDisplay.style.fontSize = "0.95em";
countDisplay.style.color = "var(--text-muted)";
topRow.appendChild(countDisplay);
uiContainer.appendChild(topRow);
// --- ROW 1.5: QUICK TAGS ---
const tagsRow = document.createElement("div");
tagsRow.style.display = "flex";
tagsRow.style.gap = "6px";
tagsRow.style.alignItems = "center";
tagsRow.style.flexWrap = "wrap";
tagsRow.style.fontSize = "0.85em";
const tagsLabel = document.createElement("strong");
tagsLabel.innerText = "Quick Tags:";
tagsRow.appendChild(tagsLabel);
const myQuickTags = ["ai", "python", "gaming", "history", "music", "dev", "jam_tracks"];
myQuickTags.forEach(tag => {
const btn = document.createElement("button");
btn.innerText = "#" + tag;
btn.style.padding = "2px 8px";
btn.style.borderRadius = "12px";
btn.style.border = "1px solid var(--background-modifier-border)";
btn.style.cursor = "pointer";
if (state.quickTag === tag) {
btn.style.backgroundColor = "var(--text-accent)";
btn.style.color = "var(--background-primary)";
btn.style.borderColor = "var(--text-accent)";
} else {
btn.style.backgroundColor = "var(--background-secondary)";
btn.style.color = "var(--text-normal)";
}
btn.addEventListener("click", () => {
state.quickTag = (state.quickTag === tag) ? "" : tag;
saveState(); renderTable();
});
tagsRow.appendChild(btn);
});
uiContainer.appendChild(tagsRow);
// --- ROW 2: FILTERS & RATINGS ---
const filterRow = document.createElement("div");
filterRow.style.display = "flex";
filterRow.style.flexWrap = "wrap";
filterRow.style.gap = "12px";
filterRow.style.fontSize = "0.85em";
filterRow.style.alignItems = "center";
function createCheckbox(labelText, stateProp) {
const label = document.createElement("label");
label.style.display = "inline-flex";
label.style.alignItems = "center";
label.style.gap = "3px";
label.style.cursor = "pointer";
const cb = document.createElement("input");
cb.type = "checkbox";
cb.checked = state[stateProp];
cb.addEventListener("change", (e) => { state[stateProp] = e.target.checked; saveState(); renderTable(); });
label.appendChild(cb);
label.appendChild(document.createTextNode(labelText));
return label;
}
// Ratings + Toggle
const ratWrap = document.createElement("div");
ratWrap.style.display = "flex";
ratWrap.style.gap = "6px";
ratWrap.style.alignItems = "center";
ratWrap.innerHTML = "<strong>⭐</strong>";
const tglRat = document.createElement("button");
tglRat.innerText = "±";
tglRat.style.padding = "0 4px";
tglRat.addEventListener("click", () => {
const keys = ["rUnder3", "r3", "r35", "r4", "r45", "r5", "rNone"];
const anyFalse = keys.some(k => !state[k]);
keys.forEach(k => state[k] = anyFalse);
saveState(); renderTable();
});
ratWrap.appendChild(tglRat);
["rUnder3", "r3", "r35", "r4", "r45", "r5", "rNone"].forEach((k, i) => {
const labels = ["<3", "3", "3.5", "4", "4.5", "5", "?"];
ratWrap.appendChild(createCheckbox(labels[i], k));
});
filterRow.appendChild(ratWrap);
// Activity Number Inputs
const actWrap = document.createElement("div");
actWrap.style.display = "flex";
actWrap.style.gap = "6px";
actWrap.style.alignItems = "center";
actWrap.innerHTML = "<strong>Act:</strong>";
const createNumberInput = (prop, label) => {
const wrap = document.createElement("div");
wrap.style.display = "flex";
wrap.style.alignItems = "center";
wrap.style.gap = "3px";
const sp = document.createElement("span");
sp.innerText = label;
const s = document.createElement("input");
s.type = "number"; s.min = "0"; s.max = "5"; s.step = "0.5";
s.value = state[prop];
s.style.width = "45px";
s.style.padding = "2px 4px";
s.addEventListener("change", (e) => {
let val = parseFloat(e.target.value);
if (isNaN(val)) val = prop === "activeMin" ? 0 : 5;
val = Math.max(0, Math.min(5, val));
state[prop] = val;
if (prop === "activeMin" && state.activeMin > state.activeMax) state.activeMax = state.activeMin;
if (prop === "activeMax" && state.activeMax < state.activeMin) state.activeMin = state.activeMax;
saveState(); renderTable();
});
wrap.appendChild(sp);
wrap.appendChild(s);
return wrap;
};
actWrap.appendChild(createNumberInput("activeMin", "Min:"));
actWrap.appendChild(createNumberInput("activeMax", "Max:"));
filterRow.appendChild(actWrap);
uiContainer.appendChild(filterRow);
// --- ROW 3: COLLAPSIBLE EXTRA SETTINGS ---
const details = document.createElement("details");
details.style.fontSize = "0.8em";
details.style.color = "var(--text-muted)";
details.open = state.advancedOpen;
details.addEventListener("toggle", () => {
state.advancedOpen = details.open;
saveState();
});
const summary = document.createElement("summary");
summary.innerText = "Advanced Filters & Columns";
summary.style.cursor = "pointer";
details.appendChild(summary);
const detailContent = document.createElement("div");
detailContent.style.display = "flex";
detailContent.style.flexDirection = "column";
detailContent.style.gap = "8px";
detailContent.style.padding = "8px 0";
// Specials Row & Toggle
const specRow = document.createElement("div");
specRow.style.display = "flex";
specRow.style.flexWrap = "wrap";
specRow.style.gap = "8px";
specRow.style.alignItems = "center";
const tglSpec = document.createElement("button");
tglSpec.innerText = "± Icons";
tglSpec.style.padding = "0 4px";
tglSpec.addEventListener("click", () => {
const specKeys = ["sKing", "sHorror", "sFire", "sGem", "sBrain", "sCompass", "sRocket", "sSexy", "sOfficial", "sLegendary", "sWizard", "sUni", "sRelax", "sWow", "sNone"];
const anyFalse = specKeys.some(k => !state[k]);
specKeys.forEach(k => state[k] = anyFalse);
saveState(); renderTable();
});
specRow.appendChild(tglSpec);
const emojis = { sKing:"👑", sHorror:"💀", sFire:"🔥", sGem:"💎", sBrain:"🧠", sCompass:"🧭", sRocket:"🚀", sSexy:"🌶️", sOfficial:"🏛️", sLegendary:"🗿", sWizard:"🧙♂️", sUni:"🎓", sRelax:"🪶", sWow:"💫", sNone:"∅" };
Object.keys(emojis).forEach(k => specRow.appendChild(createCheckbox(emojis[k], k)));
detailContent.appendChild(specRow);
// Search Fields
const fieldsMisc = document.createElement("div");
fieldsMisc.style.display = "flex";
fieldsMisc.style.gap = "15px";
fieldsMisc.appendChild(createCheckbox("Must USP", "requireUsp"));
const searchMap = [ {l: "Cat", p: "cat"}, {l: "Chan", p: "chan"}, {l: "Search USP", p: "searchUsp"}, {l: "Tags", p: "searchTags"} ];
searchMap.forEach(c => fieldsMisc.appendChild(createCheckbox(c.l, c.p)));
detailContent.appendChild(fieldsMisc);
// Columns & Toggle
const colsMisc = document.createElement("div");
colsMisc.style.display = "flex";
colsMisc.style.gap = "12px";
colsMisc.style.alignItems = "center";
const tglCols = document.createElement("button");
tglCols.innerText = "± Cols";
tglCols.style.padding = "0 4px";
tglCols.addEventListener("click", () => {
const colKeys = ["cCategory", "cThumb", "cIcon", "cUsp", "cPlaylists", "cSubs", "cRating"];
const anyFalse = colKeys.some(k => !state[k]);
colKeys.forEach(k => state[k] = anyFalse);
saveState(); renderTable();
});
colsMisc.appendChild(tglCols);
const colMap = [
{l: "Category", p: "cCategory"}, {l: "Img", p: "cThumb"}, {l: "📄", p: "cIcon"},
{l: "USP", p: "cUsp"}, {l: "PL", p: "cPlaylists"}, {l: "Subs", p: "cSubs"}, {l: "Rating", p: "cRating"}
];
colMap.forEach(c => colsMisc.appendChild(createCheckbox(c.l, c.p)));
detailContent.appendChild(colsMisc);
details.appendChild(detailContent);
uiContainer.appendChild(details);
dv.container.appendChild(uiContainer);
// --- LOGIC ---
searchBox.addEventListener("input", (e) => {
state.term = e.target.value;
saveState();
renderTable();
const ni = dv.container.querySelector("input[type='text']");
if (ni) { ni.focus(); ni.setSelectionRange(state.term.length, state.term.length); }
});
let pages = dv.pages('""').where(p => (p.file.name !== "_" && (!p.file.folder || (!p.file.folder.startsWith("_") && !p.file.folder.includes("/_")))));
pages = pages.where(p => {
const v = p.active != null ? Number(p.active) : null;
if (v === null) return (state.activeMin === 0 && state.activeMax === 5);
return v >= state.activeMin && v <= state.activeMax;
});
if (state.sourceType !== "All") {
pages = pages.where(p => [p.source_type].flat().some(t => t?.toLowerCase() === state.sourceType.toLowerCase()));
}
if (state.quickTag) {
const qt = state.quickTag.toLowerCase();
pages = pages.where(p => {
let tags = p.file.etags || [];
// Dataview sometimes returns a single string instead of an array, so let's handle both
if (typeof tags === 'string') tags = [tags];
return tags.some(tag => tag.toLowerCase().includes(qt));
});
}
pages = pages.where(p => {
const r = Number(p.rating);
const isUnrated = !p.rating || isNaN(r);
let ratingMatch = false;
if (state.rNone && isUnrated) ratingMatch = true;
if (state.rUnder3 && r > 0 && r < 3) ratingMatch = true;
if (state.r3 && r === 3) ratingMatch = true;
if (state.r35 && r === 3.5) ratingMatch = true;
if (state.r4 && r === 4) ratingMatch = true;
if (state.r45 && r === 4.5) ratingMatch = true;
if (state.r5 && r === 5) ratingMatch = true;
if (!ratingMatch) return false;
const specMap = { sKing:"king", sHorror:"horror", sFire:"fire", sGem:"gem", sBrain:"brain", sCompass:"compass", sRocket:"rocket", sSexy:"sexy", sOfficial:"official", sLegendary:"legendary", sWizard:"wizard", sUni:"uni", sRelax:"relax", sWow:"wow" };
let specialMatch = false;
let hasAnyIcon = false;
for (let key in specMap) {
if (p[specMap[key]] === true || p[specMap[key]] === "true") {
hasAnyIcon = true;
if (state[key]) {
specialMatch = true;
break;
}
}
}
if (!hasAnyIcon && state.sNone) {
specialMatch = true;
}
return specialMatch;
});
if (state.requireUsp) pages = pages.where(p => p.usp?.trim());
if (state.term) {
const t = state.term.toLowerCase();
pages = pages.where(p => (state.cat && p.file.folder?.toLowerCase().includes(t)) || (state.chan && p.file.name.toLowerCase().includes(t)) || (state.searchUsp && p.usp?.toLowerCase().includes(t)) || (state.searchTags && String(p.file.etags).toLowerCase().includes(t)));
}
// UPDATE COUNTER DISPLAY NOW THAT FILTERING IS DONE
countDisplay.innerHTML = `<strong>${pages.length}</strong> results`;
// --- TABLE RENDER ---
const formatThumb = (t) => t ? (typeof t === 'string' && t.startsWith("http") ? `<img src="${t}" style="width:40px;height:40px;object-fit:cover;border-radius:4px;">` : `![[${t.path || t.replace(/\[|\]/g, '')}|40]]`) : `<div style="width:40px;height:40px;background:var(--background-modifier-border);border-radius:4px;"></div>`;
let grouped = pages.groupBy(p => p.file.folder?.split("/").pop() || "Root").sort(g => g.key, "asc");
let headers = [];
if (state.cCategory) headers.push("Category");
if (state.cThumb) headers.push("Img");
if (state.cIcon) headers.push("📄");
headers.push("Channel");
if (state.cUsp) headers.push("USP");
if (state.cPlaylists) headers.push("PL");
if (state.cSubs) headers.push("Subs");
if (state.cRating) headers.push("Rating");
let finalRows = [];
grouped.forEach(g => {
let headerRow = [];
if (headers.length > 0) {
headerRow.push(`<span style="font-weight:bold;color:var(--text-accent);">${g.key}</span>`);
for(let i = 1; i < headers.length; i++) {
headerRow.push("<hr style='opacity:0.2;margin:0;'>");
}
finalRows.push(headerRow);
}
g.rows.forEach(r => {
let row = [];
if (state.cCategory) row.push("");
if (state.cThumb) row.push(formatThumb(r.thumbnail));
if (state.cIcon) row.push(`[[${r.file.path}|📄]]`);
row.push(r.url ? `<a href="${r.url}" style="font-weight:bold;text-decoration:none;">${r.file.name}</a>` : r.file.name);
if (state.cUsp) row.push(r.usp || "");
if (state.cPlaylists) {
let pl = [r.playlists].flat().filter(x => x?.url).map(x => `<a href="${x.url}" target="_blank" style="font-size:0.8em;">▶️ ${x.name}</a>`);
row.push(`<div style="display:flex;flex-direction:column;">${pl.join("")}</div>`);
}
if (state.cSubs) row.push(r.subs ? (isNaN(r.subs) ? r.subs : Number(r.subs).toLocaleString()) : "");
if (state.cRating) {
const icons = { fire:"🔥", rocket:"🚀", compass:"🧭", brain:"🧠", sexy:"🌶️", gem:"💎", king:"👑", horror:"💀", official:"🏛️", legendary:"🗿", wizard:"🧙♂️", uni:"🎓", relax:"🪶", wow:"💫" };
let out = "";
for (let k in icons) {
if (r[k] === true || r[k] === "true") {
out = `<div style="font-size:1.5em; text-align:center;">${icons[k]}</div>`;
break;
}
}
if (!out && r.rating) {
const stars = { 5: ["#FFD700", "★★★★★"], 4.5: ["#00E5FF", "★★★★½"], 4: ["#00E5FF", "★★★★"], 3.5: ["#FF9800", "★★★½"], 3: ["#FF9800", "★★★"] };
if (stars[r.rating]) {
out = `<div style="text-align:center; color:${stars[r.rating][0]}">${stars[r.rating][1]}</div>`;
} else {
out = `<div style="text-align:center;">${r.rating}</div>`;
}
}
row.push(out);
}
finalRows.push(row);
});
});
dv.table(headers, finalRows);
}
renderTable();
Last active
March 12, 2026 00:45
-
-
Save rickd-uk/1ff356c35daf81a8a881573a70b82d4d to your computer and use it in GitHub Desktop.
CR8R Obsidian Script.md
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment