Merge branch 'dev'
CI/CD / deploy (push) Successful in 3m25s
Details
CI/CD / deploy (push) Successful in 3m25s
Details
This commit is contained in:
commit
fa08c00d38
|
|
@ -11,9 +11,9 @@ const routes = new Hono();
|
|||
routes.get('/', (c) => c.text('rAuctions — coming soon'));
|
||||
|
||||
export const auctionsModule: RSpaceModule = {
|
||||
id: 'auctions',
|
||||
id: 'rauctions',
|
||||
name: 'rAuctions',
|
||||
icon: '🏛',
|
||||
icon: '🎭',
|
||||
description: 'Community auctions with USDC',
|
||||
routes,
|
||||
scoping: { defaultScope: 'space', userConfigurable: true },
|
||||
|
|
|
|||
|
|
@ -3787,7 +3787,7 @@ async function serveStatic(path: string, url?: URL): Promise<Response | null> {
|
|||
}
|
||||
|
||||
// ── Module ID aliases (plural/misspelling → canonical) ──
|
||||
const MODULE_ALIASES: Record<string, string> = { rsheet: "rsheets" };
|
||||
const MODULE_ALIASES: Record<string, string> = { rsheet: "rsheets", auctions: "rauctions" };
|
||||
function resolveModuleAlias(id: string): string { return MODULE_ALIASES[id] ?? id; }
|
||||
|
||||
// ── Standalone domain → module lookup ──
|
||||
|
|
|
|||
|
|
@ -16,40 +16,45 @@ const COMPAT_POLYFILLS = `<script>(function(){if(typeof structuredClone!=="funct
|
|||
// ── Dynamic per-module favicon (inline, runs after body parse) ──
|
||||
// Badge map mirrors MODULE_BADGES from rstack-app-switcher.ts — kept in sync manually.
|
||||
const FAVICON_BADGE_MAP: Record<string, { badge: string; color: string }> = {
|
||||
rspace: { badge: "r🎨", color: "#5eead4" },
|
||||
rnotes: { badge: "r📝", color: "#fcd34d" },
|
||||
rpubs: { badge: "r📖", color: "#fda4af" },
|
||||
rswag: { badge: "r👕", color: "#fda4af" },
|
||||
rsplat: { badge: "r🔮", color: "#d8b4fe" },
|
||||
rcal: { badge: "r📅", color: "#7dd3fc" },
|
||||
rtrips: { badge: "r✈️", color: "#6ee7b7" },
|
||||
rmaps: { badge: "r🗺", color: "#86efac" },
|
||||
rchats: { badge: "r🗨", color: "#6ee7b7" },
|
||||
rinbox: { badge: "r📨", color: "#a5b4fc" },
|
||||
rmail: { badge: "r✉️", color: "#93c5fd" },
|
||||
rforum: { badge: "r💬", color: "#fcd34d" },
|
||||
rmeets: { badge: "r📹", color: "#67e8f9" },
|
||||
rspace: { badge: "r🎨", color: "#5eead4" },
|
||||
rdocs: { badge: "r📄", color: "#a5b4fc" },
|
||||
rdesign: { badge: "r🎨", color: "#c4b5fd" },
|
||||
rnotes: { badge: "r📝", color: "#fcd34d" },
|
||||
rpubs: { badge: "r📖", color: "#fda4af" },
|
||||
rsheets: { badge: "r📑", color: "#86efac" },
|
||||
rsplat: { badge: "r🔮", color: "#d8b4fe" },
|
||||
rswag: { badge: "r👕", color: "#fda4af" },
|
||||
rchats: { badge: "r🗨", color: "#6ee7b7" },
|
||||
rforum: { badge: "r💬", color: "#fcd34d" },
|
||||
rinbox: { badge: "r📨", color: "#a5b4fc" },
|
||||
rmail: { badge: "r✉️", color: "#93c5fd" },
|
||||
rmeets: { badge: "r📹", color: "#67e8f9" },
|
||||
rcal: { badge: "r📅", color: "#7dd3fc" },
|
||||
rchoices: { badge: "r☑️", color: "#f0abfc" },
|
||||
rschedule: { badge: "r⏱", color: "#a5b4fc" },
|
||||
rtasks: { badge: "r📋", color: "#cbd5e1" },
|
||||
rtime: { badge: "r⏳", color: "#a78bfa" },
|
||||
rvote: { badge: "r🗳", color: "#c4b5fd" },
|
||||
crowdsurf: { badge: "r🏄", color: "#fde68a" },
|
||||
rnetwork: { badge: "r🌐", color: "#93c5fd" },
|
||||
rsocials: { badge: "r📢", color: "#7dd3fc" },
|
||||
rexchange: { badge: "r💱", color: "#fde047" },
|
||||
rflows: { badge: "r🌊", color: "#bef264" },
|
||||
rwallet: { badge: "r💰", color: "#fde047" },
|
||||
rcart: { badge: "r🛒", color: "#fdba74" },
|
||||
rauctions: { badge: "r🏛", color: "#fca5a5" },
|
||||
rtube: { badge: "r🎬", color: "#f9a8d4" },
|
||||
rauctions: { badge: "r🎭", color: "#fca5a5" },
|
||||
rgov: { badge: "r⚖️", color: "#94a3b8" },
|
||||
rphotos: { badge: "r📸", color: "#f9a8d4" },
|
||||
rnetwork: { badge: "r🌐", color: "#93c5fd" },
|
||||
rsocials: { badge: "r📢", color: "#7dd3fc" },
|
||||
rfiles: { badge: "r📁", color: "#67e8f9" },
|
||||
rtube: { badge: "r🎬", color: "#f9a8d4" },
|
||||
rbooks: { badge: "r📚", color: "#fda4af" },
|
||||
rdata: { badge: "r📊", color: "#d8b4fe" },
|
||||
rmaps: { badge: "r🗺", color: "#86efac" },
|
||||
rtrips: { badge: "r✈️", color: "#6ee7b7" },
|
||||
rbnb: { badge: "r🏠", color: "#fbbf24" },
|
||||
rvnb: { badge: "r🚐", color: "#a5f3fc" },
|
||||
rtasks: { badge: "r📋", color: "#cbd5e1" },
|
||||
rschedule: { badge: "r⏱", color: "#a5b4fc" },
|
||||
crowdsurf: { badge: "r🏄", color: "#fde68a" },
|
||||
rdata: { badge: "r📊", color: "#d8b4fe" },
|
||||
ragents: { badge: "r🤖", color: "#6ee7b7" },
|
||||
rids: { badge: "r🪪", color: "#6ee7b7" },
|
||||
rdesign: { badge: "r🎨", color: "#7c3aed" },
|
||||
rtime: { badge: "r⏳", color: "#a78bfa" },
|
||||
rstack: { badge: "r✨", color: "#c4b5fd" },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,47 +20,54 @@ export interface AppSwitcherModule {
|
|||
|
||||
// Pastel badge abbreviations & colors for each module
|
||||
const MODULE_BADGES: Record<string, { badge: string; color: string }> = {
|
||||
// Creating
|
||||
// Create
|
||||
rspace: { badge: "r🎨", color: "#5eead4" }, // teal-300
|
||||
rdocs: { badge: "r📄", color: "#a5b4fc" }, // indigo-300
|
||||
rnotes: { badge: "r📝", color: "#fcd34d" }, // amber-300
|
||||
rpubs: { badge: "r📖", color: "#fda4af" }, // rose-300
|
||||
rdesign: { badge: "r🎨", color: "#c4b5fd" }, // violet-300
|
||||
rsheets: { badge: "r📑", color: "#86efac" }, // green-300
|
||||
rswag: { badge: "r👕", color: "#fda4af" }, // rose-300
|
||||
rsplat: { badge: "r🔮", color: "#d8b4fe" }, // purple-300
|
||||
// Planning
|
||||
rcal: { badge: "r📅", color: "#7dd3fc" }, // sky-300
|
||||
rtrips: { badge: "r✈️", color: "#6ee7b7" }, // emerald-300
|
||||
rmaps: { badge: "r🗺", color: "#86efac" }, // green-300
|
||||
// Communicating
|
||||
// Communicate
|
||||
rchats: { badge: "r🗨", color: "#6ee7b7" }, // emerald-200
|
||||
rinbox: { badge: "r📨", color: "#a5b4fc" }, // indigo-300
|
||||
rmail: { badge: "r✉️", color: "#93c5fd" }, // blue-200
|
||||
rforum: { badge: "r💬", color: "#fcd34d" }, // amber-200
|
||||
rmeets: { badge: "r📹", color: "#67e8f9" }, // cyan-300
|
||||
// Deciding
|
||||
// Coordinate
|
||||
rcal: { badge: "r📅", color: "#7dd3fc" }, // sky-300
|
||||
rchoices: { badge: "r☑️", color: "#f0abfc" }, // fuchsia-300
|
||||
rschedule: { badge: "r⏱", color: "#a5b4fc" }, // indigo-200
|
||||
rtasks: { badge: "r📋", color: "#cbd5e1" }, // slate-300
|
||||
rtime: { badge: "r⏳", color: "#a78bfa" }, // violet-400
|
||||
rvote: { badge: "r🗳", color: "#c4b5fd" }, // violet-300
|
||||
// Funding & Commerce
|
||||
crowdsurf: { badge: "r🏄", color: "#fde68a" }, // amber-200
|
||||
// Connect
|
||||
rnetwork: { badge: "r🌐", color: "#93c5fd" }, // blue-300
|
||||
rsocials: { badge: "r📢", color: "#7dd3fc" }, // sky-200
|
||||
// Commerce
|
||||
rexchange: { badge: "r💱", color: "#fde047" }, // yellow-300
|
||||
rflows: { badge: "r🌊", color: "#bef264" }, // lime-300
|
||||
rwallet: { badge: "r💰", color: "#fde047" }, // yellow-300
|
||||
rcart: { badge: "r🛒", color: "#fdba74" }, // orange-300
|
||||
rauctions: { badge: "r🏛", color: "#fca5a5" }, // red-300
|
||||
rtube: { badge: "r🎬", color: "#f9a8d4" }, // pink-300
|
||||
// Sharing
|
||||
rauctions: { badge: "r🎭", color: "#fca5a5" }, // red-300
|
||||
// Govern
|
||||
rgov: { badge: "r⚖️", color: "#94a3b8" }, // slate-400
|
||||
// Media
|
||||
rphotos: { badge: "r📸", color: "#f9a8d4" }, // pink-200
|
||||
rnetwork: { badge: "r🌐", color: "#93c5fd" }, // blue-300
|
||||
rsocials: { badge: "r📢", color: "#7dd3fc" }, // sky-200
|
||||
rfiles: { badge: "r📁", color: "#67e8f9" }, // cyan-300
|
||||
rtube: { badge: "r🎬", color: "#f9a8d4" }, // pink-300
|
||||
rbooks: { badge: "r📚", color: "#fda4af" }, // rose-300
|
||||
// Observing
|
||||
rdata: { badge: "r📊", color: "#d8b4fe" }, // purple-300
|
||||
// Travel & Stay
|
||||
rmaps: { badge: "r🗺", color: "#86efac" }, // green-300
|
||||
rtrips: { badge: "r✈️", color: "#6ee7b7" }, // emerald-300
|
||||
rbnb: { badge: "r🏠", color: "#fbbf24" }, // amber-300
|
||||
rvnb: { badge: "r🚐", color: "#a5f3fc" }, // cyan-200
|
||||
// Coordinate
|
||||
rtasks: { badge: "r📋", color: "#cbd5e1" }, // slate-300
|
||||
rschedule: { badge: "r⏱", color: "#a5b4fc" }, // indigo-200
|
||||
crowdsurf: { badge: "r🏄", color: "#fde68a" }, // amber-200
|
||||
// Identity & Infrastructure
|
||||
// Observe
|
||||
rdata: { badge: "r📊", color: "#d8b4fe" }, // purple-300
|
||||
// Platform
|
||||
ragents: { badge: "r🤖", color: "#6ee7b7" }, // emerald-300
|
||||
rids: { badge: "r🪪", color: "#6ee7b7" }, // emerald-300
|
||||
rstack: { badge: "r✨", color: "" }, // gradient (handled separately)
|
||||
};
|
||||
|
|
@ -68,45 +75,53 @@ const MODULE_BADGES: Record<string, { badge: string; color: string }> = {
|
|||
// Category definitions for the rApp dropdown (display-only grouping)
|
||||
const MODULE_CATEGORIES: Record<string, string> = {
|
||||
// Create
|
||||
rspace: "Create",
|
||||
rdocs: "Create",
|
||||
rdesign: "Create",
|
||||
rnotes: "Create",
|
||||
rpubs: "Create",
|
||||
rsheets: "Create",
|
||||
rsplat: "Create",
|
||||
rspace: "Create",
|
||||
rswag: "Create",
|
||||
// Communicate
|
||||
rchats: "Communicate",
|
||||
rforum: "Communicate",
|
||||
rinbox: "Communicate",
|
||||
rmail: "Communicate",
|
||||
rforum: "Communicate",
|
||||
rmeets: "Communicate",
|
||||
// Coordinate
|
||||
crowdsurf: "Coordinate",
|
||||
rcal: "Coordinate",
|
||||
rchoices: "Coordinate",
|
||||
rschedule: "Coordinate",
|
||||
rtasks: "Coordinate",
|
||||
rchoices: "Coordinate",
|
||||
rtime: "Coordinate",
|
||||
rvote: "Coordinate",
|
||||
crowdsurf: "Coordinate",
|
||||
// Connect
|
||||
rnetwork: "Connect",
|
||||
rsocials: "Connect",
|
||||
// Commerce
|
||||
rauctions: "Commerce",
|
||||
rcart: "Commerce",
|
||||
rexchange: "Commerce",
|
||||
rflows: "Commerce",
|
||||
rwallet: "Commerce",
|
||||
rcart: "Commerce",
|
||||
rauctions: "Commerce",
|
||||
// Govern
|
||||
rgov: "Govern",
|
||||
// Media
|
||||
rphotos: "Media",
|
||||
rfiles: "Media",
|
||||
rtube: "Media",
|
||||
rbooks: "Media",
|
||||
rfiles: "Media",
|
||||
rphotos: "Media",
|
||||
rtube: "Media",
|
||||
// Travel & Stay
|
||||
rbnb: "Travel & Stay",
|
||||
rmaps: "Travel & Stay",
|
||||
rtrips: "Travel & Stay",
|
||||
rbnb: "Travel & Stay",
|
||||
rvnb: "Travel & Stay",
|
||||
// Observe
|
||||
rdata: "Observe",
|
||||
// Platform
|
||||
ragents: "Platform",
|
||||
rids: "Platform",
|
||||
rstack: "Platform",
|
||||
};
|
||||
|
|
@ -117,6 +132,7 @@ const CATEGORY_ORDER = [
|
|||
"Coordinate",
|
||||
"Connect",
|
||||
"Commerce",
|
||||
"Govern",
|
||||
"Media",
|
||||
"Travel & Stay",
|
||||
"Observe",
|
||||
|
|
@ -132,11 +148,22 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
#isOpen = false;
|
||||
#catalogOpen = false;
|
||||
#catalogBusy = false;
|
||||
#sortMode: 'function' | 'alpha' = 'function';
|
||||
#pinnedIds: Set<string> = new Set();
|
||||
#outsideClickHandler: ((e: PointerEvent) => void) | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#shadow = this.attachShadow({ mode: "open" });
|
||||
// Restore persisted preferences
|
||||
try {
|
||||
const sort = localStorage.getItem("rspace-sort-mode");
|
||||
if (sort === "alpha" || sort === "function") this.#sortMode = sort;
|
||||
} catch {}
|
||||
try {
|
||||
const pinned = JSON.parse(localStorage.getItem("rspace-pinned-modules") || "[]");
|
||||
if (Array.isArray(pinned)) this.#pinnedIds = new Set(pinned);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
|
|
@ -193,19 +220,8 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
}
|
||||
|
||||
#renderGroupedModules(current: string): string {
|
||||
// Group modules by category
|
||||
const groups = new Map<string, AppSwitcherModule[]>();
|
||||
const uncategorized: AppSwitcherModule[] = [];
|
||||
|
||||
for (const m of this.#modules) {
|
||||
const cat = MODULE_CATEGORIES[m.id];
|
||||
if (cat) {
|
||||
if (!groups.has(cat)) groups.set(cat, []);
|
||||
groups.get(cat)!.push(m);
|
||||
} else {
|
||||
uncategorized.push(m);
|
||||
}
|
||||
}
|
||||
const alpha = (a: AppSwitcherModule, b: AppSwitcherModule) =>
|
||||
a.name.localeCompare(b.name);
|
||||
|
||||
// rStack header (clickable)
|
||||
let html = `
|
||||
|
|
@ -218,6 +234,15 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
</a>
|
||||
`;
|
||||
|
||||
// Pinned section
|
||||
const pinnedModules = this.#modules
|
||||
.filter((m) => this.#pinnedIds.has(m.id))
|
||||
.sort(alpha);
|
||||
if (pinnedModules.length > 0) {
|
||||
html += `<div class="category-header recent-header">Pinned</div>`;
|
||||
html += pinnedModules.map((m) => this.#renderItem(m, current)).join("");
|
||||
}
|
||||
|
||||
// Recently Used section
|
||||
const recentIds = RStackAppSwitcher.#getRecentModules().filter((id) => id !== current);
|
||||
const recentModules = recentIds
|
||||
|
|
@ -228,15 +253,38 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
html += recentModules.map((m) => this.#renderItem(m, current)).join("");
|
||||
}
|
||||
|
||||
for (const cat of CATEGORY_ORDER) {
|
||||
const items = groups.get(cat);
|
||||
if (!items || items.length === 0) continue;
|
||||
html += `<div class="category-header">${cat}</div>`;
|
||||
html += items.map((m) => this.#renderItem(m, current)).join("");
|
||||
}
|
||||
if (uncategorized.length > 0) {
|
||||
html += `<div class="category-header">Other</div>`;
|
||||
html += uncategorized.map((m) => this.#renderItem(m, current)).join("");
|
||||
if (this.#sortMode === 'alpha') {
|
||||
// Flat alphabetical list
|
||||
const sorted = [...this.#modules].sort(alpha);
|
||||
html += `<div class="category-header">All rApps</div>`;
|
||||
html += sorted.map((m) => this.#renderItem(m, current)).join("");
|
||||
} else {
|
||||
// Group by category, alphabetical within each group
|
||||
const groups = new Map<string, AppSwitcherModule[]>();
|
||||
const uncategorized: AppSwitcherModule[] = [];
|
||||
|
||||
for (const m of this.#modules) {
|
||||
const cat = MODULE_CATEGORIES[m.id];
|
||||
if (cat) {
|
||||
if (!groups.has(cat)) groups.set(cat, []);
|
||||
groups.get(cat)!.push(m);
|
||||
} else {
|
||||
uncategorized.push(m);
|
||||
}
|
||||
}
|
||||
|
||||
for (const cat of CATEGORY_ORDER) {
|
||||
const items = groups.get(cat);
|
||||
if (!items || items.length === 0) continue;
|
||||
items.sort(alpha);
|
||||
html += `<div class="category-header">${cat}</div>`;
|
||||
html += items.map((m) => this.#renderItem(m, current)).join("");
|
||||
}
|
||||
if (uncategorized.length > 0) {
|
||||
uncategorized.sort(alpha);
|
||||
html += `<div class="category-header">Other</div>`;
|
||||
html += uncategorized.map((m) => this.#renderItem(m, current)).join("");
|
||||
}
|
||||
}
|
||||
|
||||
// "Manage rApps" catalog section
|
||||
|
|
@ -278,8 +326,12 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
// Sort toggle + Footer
|
||||
html += `
|
||||
<div class="sort-toggle">
|
||||
<button class="sort-btn ${this.#sortMode === 'function' ? 'sort-active' : ''}" id="sort-function" title="Sort by function">By Function</button>
|
||||
<button class="sort-btn ${this.#sortMode === 'alpha' ? 'sort-active' : ''}" id="sort-alpha" title="Sort alphabetically">A–Z</button>
|
||||
</div>
|
||||
<div class="rstack-footer">
|
||||
<a href="https://rstack.online" target="_blank" rel="noopener">rstack.online — self-hosted, community-run</a>
|
||||
</div>
|
||||
|
|
@ -319,8 +371,6 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
: `<span class="item-icon">${m.icon}</span>`;
|
||||
|
||||
const space = this.#getSpaceSlug();
|
||||
// On bare domain or standalone r*.online: link to landing pages.
|
||||
// On demo.rspace.online or user subdomains: use rspaceNavUrl for in-app navigation.
|
||||
const host = window.location.host.split(":")[0];
|
||||
const isBareDomain = host === "rspace.online" || host === "www.rspace.online";
|
||||
const href =
|
||||
|
|
@ -332,6 +382,8 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
? `<span class="scope-badge scope-global" title="Global data (shared across spaces)">G</span>`
|
||||
: "";
|
||||
|
||||
const isPinned = this.#pinnedIds.has(m.id);
|
||||
|
||||
return `
|
||||
<div class="item-row ${m.id === current ? "active" : ""}">
|
||||
<a class="item"
|
||||
|
|
@ -346,6 +398,9 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
<span class="item-desc">${m.description}</span>
|
||||
</div>
|
||||
</a>
|
||||
<button class="pin-btn ${isPinned ? 'pin-active' : ''}"
|
||||
data-pin-id="${m.id}"
|
||||
title="${isPinned ? 'Unpin' : 'Pin to top'}">★</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -465,6 +520,34 @@ export class RStackAppSwitcher extends HTMLElement {
|
|||
});
|
||||
});
|
||||
|
||||
// Sort toggle
|
||||
this.#shadow.getElementById("sort-function")?.addEventListener("click", () => {
|
||||
this.#sortMode = 'function';
|
||||
try { localStorage.setItem("rspace-sort-mode", "function"); } catch {}
|
||||
this.#render();
|
||||
});
|
||||
this.#shadow.getElementById("sort-alpha")?.addEventListener("click", () => {
|
||||
this.#sortMode = 'alpha';
|
||||
try { localStorage.setItem("rspace-sort-mode", "alpha"); } catch {}
|
||||
this.#render();
|
||||
});
|
||||
|
||||
// Pin/unpin buttons
|
||||
this.#shadow.querySelectorAll<HTMLElement>("[data-pin-id]").forEach((btn) => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const id = btn.dataset.pinId!;
|
||||
if (this.#pinnedIds.has(id)) {
|
||||
this.#pinnedIds.delete(id);
|
||||
} else {
|
||||
this.#pinnedIds.add(id);
|
||||
}
|
||||
try { localStorage.setItem("rspace-pinned-modules", JSON.stringify([...this.#pinnedIds])); } catch {}
|
||||
this.#render();
|
||||
});
|
||||
});
|
||||
|
||||
// Catalog toggle
|
||||
this.#shadow.getElementById("catalog-toggle")?.addEventListener("click", () => {
|
||||
this.#catalogOpen = !this.#catalogOpen;
|
||||
|
|
@ -789,6 +872,40 @@ a.rstack-header:hover, a.rstack-header:active { background: var(--rs-bg-hover);
|
|||
}
|
||||
.catalog-btn--remove:hover { border-color: #ef4444; background: rgba(239,68,68,0.1); }
|
||||
|
||||
/* ── Pin star button ── */
|
||||
.pin-btn {
|
||||
width: 28px; height: 28px; flex-shrink: 0;
|
||||
border: none; background: none; cursor: pointer;
|
||||
font-size: 0.85rem; line-height: 1;
|
||||
color: var(--rs-text-secondary); opacity: 0;
|
||||
transition: opacity 0.15s, color 0.15s;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.item-row:hover .pin-btn,
|
||||
.pin-btn.pin-active { opacity: 1; }
|
||||
.pin-btn.pin-active { color: #fbbf24; }
|
||||
.pin-btn:hover { color: #fbbf24; background: rgba(251,191,36,0.1); }
|
||||
|
||||
/* ── Sort toggle ── */
|
||||
.sort-toggle {
|
||||
display: flex; gap: 4px; padding: 6px 14px;
|
||||
border-top: 1px solid var(--rs-border-subtle);
|
||||
}
|
||||
.sort-btn {
|
||||
flex: 1; padding: 4px 8px; border-radius: 5px;
|
||||
border: 1px solid var(--rs-border); background: none;
|
||||
font-size: 0.65rem; font-weight: 600;
|
||||
text-transform: uppercase; letter-spacing: 0.04em;
|
||||
color: var(--rs-text-secondary); cursor: pointer;
|
||||
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
||||
}
|
||||
.sort-btn:hover { background: var(--rs-bg-hover); }
|
||||
.sort-btn.sort-active {
|
||||
background: var(--rs-bg-active); color: var(--rs-text-primary);
|
||||
border-color: var(--rs-accent, #6366f1);
|
||||
}
|
||||
|
||||
/* Mobile: sidebar overlays instead of pushing */
|
||||
@media (max-width: 640px) {
|
||||
.sidebar { box-shadow: 4px 0 20px rgba(0,0,0,0.3); }
|
||||
|
|
|
|||
Loading…
Reference in New Issue