feat/community-themes #28
+83
-11
@@ -17,6 +17,9 @@
|
|||||||
const PAGE_SIZE = 50;
|
const PAGE_SIZE = 50;
|
||||||
|
|
||||||
const INTENT_HINTS = { grab: 'scan + copy', read: 'expand + study', fill: 'templates only' };
|
const INTENT_HINTS = { grab: 'scan + copy', read: 'expand + study', fill: 'templates only' };
|
||||||
|
const READ_TYPES = ['note', 'link', 'decision'];
|
||||||
|
const FILL_TYPES = ['template', 'checklist'];
|
||||||
|
const GRAB_TYPES = ['snippet'];
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
view: 'stream',
|
view: 'stream',
|
||||||
@@ -363,7 +366,7 @@
|
|||||||
html += '<div class="rail-lbl">intent</div>';
|
html += '<div class="rail-lbl">intent</div>';
|
||||||
for (const k of ['grab', 'read', 'fill']) {
|
for (const k of ['grab', 'read', 'fill']) {
|
||||||
const on = state.intent === k ? ' on' : '';
|
const on = state.intent === k ? ' on' : '';
|
||||||
const count = k === 'grab' ? state.entities.length : k === 'read' ? state.entities.filter(e => e.card_data).length : state.entities.filter(e => e.body && /\$\{.+\}/.test(e.body)).length;
|
const count = k === 'grab' ? state.entities.filter(e => !e.card_type || GRAB_TYPES.includes(e.card_type)).length : k === 'read' ? state.entities.filter(e => READ_TYPES.includes(e.card_type)).length : state.entities.filter(e => FILL_TYPES.includes(e.card_type)).length;
|
||||||
html += `<button class="rail-item${on}" data-intent="${k}">`;
|
html += `<button class="rail-item${on}" data-intent="${k}">`;
|
||||||
html += `<span class="rail-arrow">${state.intent === k ? '▸' : ''}</span>`;
|
html += `<span class="rail-arrow">${state.intent === k ? '▸' : ''}</span>`;
|
||||||
html += '<span class="rail-dot"></span>';
|
html += '<span class="rail-dot"></span>';
|
||||||
@@ -1842,9 +1845,10 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function filterByIntent(entities) {
|
function filterByIntent(entities) {
|
||||||
if (state.view !== 'cards' || state.intent === 'grab') return entities;
|
if (state.view !== 'cards') return entities;
|
||||||
if (state.intent === 'read') return entities.filter(e => e.card_data);
|
if (state.intent === 'grab') return entities.filter(e => !e.card_type || GRAB_TYPES.includes(e.card_type));
|
||||||
if (state.intent === 'fill') return entities.filter(e => e.body && /\$\{.+\}/.test(e.body));
|
if (state.intent === 'read') return entities.filter(e => READ_TYPES.includes(e.card_type));
|
||||||
|
if (state.intent === 'fill') return entities.filter(e => FILL_TYPES.includes(e.card_type));
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1888,20 +1892,88 @@
|
|||||||
|
|
||||||
// ========== Theme ==========
|
// ========== Theme ==========
|
||||||
|
|
||||||
const THEMES = ['dark', 'paper', 'tinycard'];
|
const THEMES_DARK = [
|
||||||
const THEME_ICONS = { dark: '◑', paper: '◐', tinycard: '◈' };
|
{ id: 'dark', label: 'Noir', swatch: '#c8942a' },
|
||||||
|
{ id: 'tinycard', label: 'Tinycard', swatch: '#ad8ee6' },
|
||||||
|
{ id: 'catppuccin', label: 'Catppuccin', swatch: '#cba6f7' },
|
||||||
|
{ id: 'nord', label: 'Nord', swatch: '#88c0d0' },
|
||||||
|
{ id: 'dracula', label: 'Dracula', swatch: '#bd93f9' },
|
||||||
|
{ id: 'gruvbox', label: 'Gruvbox', swatch: '#fabd2f' },
|
||||||
|
{ id: 'rosepine', label: 'Rosé Pine', swatch: '#c4a7e7' },
|
||||||
|
{ id: 'tokyonight', label: 'Tokyo Night', swatch: '#7aa2f7' },
|
||||||
|
{ id: 'solarized', label: 'Solarized', swatch: '#268bd2' },
|
||||||
|
];
|
||||||
|
const THEMES_LIGHT = [
|
||||||
|
{ id: 'paper', label: 'Paper', swatch: '#8a6018' },
|
||||||
|
{ id: 'catppuccin-latte', label: 'Catppuccin Latte', swatch: '#8839ef' },
|
||||||
|
{ id: 'rosepine-dawn', label: 'Rosé Pine Dawn', swatch: '#907aa9' },
|
||||||
|
{ id: 'solarized-light', label: 'Solarized Light', swatch: '#268bd2' },
|
||||||
|
];
|
||||||
|
const ALL_THEME_IDS = [...THEMES_DARK, ...THEMES_LIGHT].map(t => t.id);
|
||||||
|
|
||||||
const themeToggle = $('#theme-toggle');
|
const themeToggle = $('#theme-toggle');
|
||||||
let nibTheme = localStorage.getItem('nib:theme') || 'dark';
|
let nibTheme = localStorage.getItem('nib:theme') || 'dark';
|
||||||
if (!THEMES.includes(nibTheme)) nibTheme = 'dark';
|
if (!ALL_THEME_IDS.includes(nibTheme)) nibTheme = 'dark';
|
||||||
document.documentElement.setAttribute('data-theme', nibTheme);
|
document.documentElement.setAttribute('data-theme', nibTheme);
|
||||||
themeToggle.textContent = THEME_ICONS[nibTheme];
|
themeToggle.textContent = '◑';
|
||||||
|
|
||||||
themeToggle.addEventListener('click', () => {
|
const popover = document.createElement('div');
|
||||||
nibTheme = THEMES[(THEMES.indexOf(nibTheme) + 1) % THEMES.length];
|
popover.className = 'theme-popover';
|
||||||
|
function buildPopover() {
|
||||||
|
popover.innerHTML = '';
|
||||||
|
const addSection = (label, themes) => {
|
||||||
|
const lbl = document.createElement('div');
|
||||||
|
lbl.className = 'theme-popover-label';
|
||||||
|
lbl.textContent = label;
|
||||||
|
popover.appendChild(lbl);
|
||||||
|
themes.forEach(t => {
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'theme-popover-item' + (t.id === nibTheme ? ' active' : '');
|
||||||
|
item.dataset.theme = t.id;
|
||||||
|
item.innerHTML = `<span class="theme-popover-swatch" style="background:${t.swatch}"></span>${t.label}`;
|
||||||
|
popover.appendChild(item);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
addSection('Dark', THEMES_DARK);
|
||||||
|
addSection('Light', THEMES_LIGHT);
|
||||||
|
}
|
||||||
|
buildPopover();
|
||||||
|
themeToggle.appendChild(popover);
|
||||||
|
|
||||||
|
let previewTheme = null;
|
||||||
|
themeToggle.addEventListener('click', (e) => {
|
||||||
|
if (e.target.closest('.theme-popover-item')) return;
|
||||||
|
popover.classList.toggle('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
popover.addEventListener('mouseover', (e) => {
|
||||||
|
const item = e.target.closest('.theme-popover-item');
|
||||||
|
if (!item) return;
|
||||||
|
previewTheme = item.dataset.theme;
|
||||||
|
document.documentElement.setAttribute('data-theme', previewTheme);
|
||||||
|
});
|
||||||
|
|
||||||
|
popover.addEventListener('mouseleave', () => {
|
||||||
|
previewTheme = null;
|
||||||
|
document.documentElement.setAttribute('data-theme', nibTheme);
|
||||||
|
});
|
||||||
|
|
||||||
|
popover.addEventListener('click', (e) => {
|
||||||
|
const item = e.target.closest('.theme-popover-item');
|
||||||
|
if (!item) return;
|
||||||
|
nibTheme = item.dataset.theme;
|
||||||
|
previewTheme = null;
|
||||||
document.documentElement.setAttribute('data-theme', nibTheme);
|
document.documentElement.setAttribute('data-theme', nibTheme);
|
||||||
localStorage.setItem('nib:theme', nibTheme);
|
localStorage.setItem('nib:theme', nibTheme);
|
||||||
themeToggle.textContent = THEME_ICONS[nibTheme];
|
popover.classList.remove('open');
|
||||||
|
buildPopover();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!themeToggle.contains(e.target)) popover.classList.remove('open');
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') popover.classList.remove('open');
|
||||||
});
|
});
|
||||||
|
|
||||||
// ========== Init ==========
|
// ========== Init ==========
|
||||||
|
|||||||
+275
@@ -74,6 +74,226 @@
|
|||||||
--mono: 'JetBrains Mono', ui-monospace, monospace;
|
--mono: 'JetBrains Mono', ui-monospace, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-theme="catppuccin"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #1e1e2e;
|
||||||
|
--surf: #181825;
|
||||||
|
--raised: #313244;
|
||||||
|
--border: #45475a;
|
||||||
|
--soft: #252536;
|
||||||
|
--text: #cdd6f4;
|
||||||
|
--muted: #a6adc8;
|
||||||
|
--dim: #6c7086;
|
||||||
|
--accent: #cba6f7;
|
||||||
|
--a-bg: rgba(203,166,247,.09);
|
||||||
|
--a-str: rgba(203,166,247,.22);
|
||||||
|
--todo: #f9e2af;
|
||||||
|
--note: #94e2d5;
|
||||||
|
--event: #89b4fa;
|
||||||
|
--remind: #fab387;
|
||||||
|
--ok: #a6e3a1;
|
||||||
|
--danger: #f38ba8;
|
||||||
|
--lineage: #f5c2e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="nord"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #2e3440;
|
||||||
|
--surf: #3b4252;
|
||||||
|
--raised: #434c5e;
|
||||||
|
--border: #4c566a;
|
||||||
|
--soft: #363d4a;
|
||||||
|
--text: #eceff4;
|
||||||
|
--muted: #d8dee9;
|
||||||
|
--dim: #4c566a;
|
||||||
|
--accent: #88c0d0;
|
||||||
|
--a-bg: rgba(136,192,208,.09);
|
||||||
|
--a-str: rgba(136,192,208,.22);
|
||||||
|
--todo: #ebcb8b;
|
||||||
|
--note: #8fbcbb;
|
||||||
|
--event: #81a1c1;
|
||||||
|
--remind: #d08770;
|
||||||
|
--ok: #a3be8c;
|
||||||
|
--danger: #bf616a;
|
||||||
|
--lineage: #b48ead;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dracula"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #282a36;
|
||||||
|
--surf: #21222c;
|
||||||
|
--raised: #44475a;
|
||||||
|
--border: #6272a4;
|
||||||
|
--soft: #343746;
|
||||||
|
--text: #f8f8f2;
|
||||||
|
--muted: #bfbfbf;
|
||||||
|
--dim: #6272a4;
|
||||||
|
--accent: #bd93f9;
|
||||||
|
--a-bg: rgba(189,147,249,.09);
|
||||||
|
--a-str: rgba(189,147,249,.22);
|
||||||
|
--todo: #f1fa8c;
|
||||||
|
--note: #8be9fd;
|
||||||
|
--event: #8be9fd;
|
||||||
|
--remind: #ffb86c;
|
||||||
|
--ok: #50fa7b;
|
||||||
|
--danger: #ff5555;
|
||||||
|
--lineage: #ff79c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="gruvbox"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #282828;
|
||||||
|
--surf: #1d2021;
|
||||||
|
--raised: #3c3836;
|
||||||
|
--border: #504945;
|
||||||
|
--soft: #32302f;
|
||||||
|
--text: #ebdbb2;
|
||||||
|
--muted: #a89984;
|
||||||
|
--dim: #665c54;
|
||||||
|
--accent: #fabd2f;
|
||||||
|
--a-bg: rgba(250,189,47,.09);
|
||||||
|
--a-str: rgba(250,189,47,.22);
|
||||||
|
--todo: #fabd2f;
|
||||||
|
--note: #8ec07c;
|
||||||
|
--event: #83a598;
|
||||||
|
--remind: #fe8019;
|
||||||
|
--ok: #b8bb26;
|
||||||
|
--danger: #fb4934;
|
||||||
|
--lineage: #d3869b;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="rosepine"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #191724;
|
||||||
|
--surf: #1f1d2e;
|
||||||
|
--raised: #26233a;
|
||||||
|
--border: #403d52;
|
||||||
|
--soft: #211f30;
|
||||||
|
--text: #e0def4;
|
||||||
|
--muted: #908caa;
|
||||||
|
--dim: #6e6a86;
|
||||||
|
--accent: #c4a7e7;
|
||||||
|
--a-bg: rgba(196,167,231,.09);
|
||||||
|
--a-str: rgba(196,167,231,.22);
|
||||||
|
--todo: #f6c177;
|
||||||
|
--note: #9ccfd8;
|
||||||
|
--event: #31748f;
|
||||||
|
--remind: #ea9a97;
|
||||||
|
--ok: #a6da95;
|
||||||
|
--danger: #eb6f92;
|
||||||
|
--lineage: #c4a7e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="tokyonight"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #1a1b26;
|
||||||
|
--surf: #16161e;
|
||||||
|
--raised: #292e42;
|
||||||
|
--border: #3b4261;
|
||||||
|
--soft: #1f2335;
|
||||||
|
--text: #c0caf5;
|
||||||
|
--muted: #a9b1d6;
|
||||||
|
--dim: #565f89;
|
||||||
|
--accent: #7aa2f7;
|
||||||
|
--a-bg: rgba(122,162,247,.09);
|
||||||
|
--a-str: rgba(122,162,247,.22);
|
||||||
|
--todo: #e0af68;
|
||||||
|
--note: #7dcfff;
|
||||||
|
--event: #7aa2f7;
|
||||||
|
--remind: #ff9e64;
|
||||||
|
--ok: #9ece6a;
|
||||||
|
--danger: #f7768e;
|
||||||
|
--lineage: #bb9af7;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="solarized"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #002b36;
|
||||||
|
--surf: #073642;
|
||||||
|
--raised: #094552;
|
||||||
|
--border: #586e75;
|
||||||
|
--soft: #05303b;
|
||||||
|
--text: #839496;
|
||||||
|
--muted: #657b83;
|
||||||
|
--dim: #586e75;
|
||||||
|
--accent: #268bd2;
|
||||||
|
--a-bg: rgba(38,139,210,.09);
|
||||||
|
--a-str: rgba(38,139,210,.22);
|
||||||
|
--todo: #b58900;
|
||||||
|
--note: #2aa198;
|
||||||
|
--event: #268bd2;
|
||||||
|
--remind: #cb4b16;
|
||||||
|
--ok: #859900;
|
||||||
|
--danger: #dc322f;
|
||||||
|
--lineage: #6c71c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="catppuccin-latte"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg: #eff1f5;
|
||||||
|
--surf: #e6e9ef;
|
||||||
|
--raised: #dce0e8;
|
||||||
|
--border: #ccd0da;
|
||||||
|
--soft: #e4e7ed;
|
||||||
|
--text: #4c4f69;
|
||||||
|
--muted: #6c6f85;
|
||||||
|
--dim: #9ca0b0;
|
||||||
|
--accent: #8839ef;
|
||||||
|
--a-bg: rgba(136,57,239,.08);
|
||||||
|
--a-str: rgba(136,57,239,.18);
|
||||||
|
--todo: #df8e1d;
|
||||||
|
--note: #179299;
|
||||||
|
--event: #1e66f5;
|
||||||
|
--remind: #fe640b;
|
||||||
|
--ok: #40a02b;
|
||||||
|
--danger: #d20f39;
|
||||||
|
--lineage: #ea76cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="rosepine-dawn"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg: #faf4ed;
|
||||||
|
--surf: #fffaf3;
|
||||||
|
--raised: #f2e9e1;
|
||||||
|
--border: #dfdad9;
|
||||||
|
--soft: #f4ede8;
|
||||||
|
--text: #575279;
|
||||||
|
--muted: #797593;
|
||||||
|
--dim: #9893a5;
|
||||||
|
--accent: #907aa9;
|
||||||
|
--a-bg: rgba(144,122,169,.08);
|
||||||
|
--a-str: rgba(144,122,169,.18);
|
||||||
|
--todo: #ea9d34;
|
||||||
|
--note: #56949f;
|
||||||
|
--event: #286983;
|
||||||
|
--remind: #d7827e;
|
||||||
|
--ok: #56949f;
|
||||||
|
--danger: #b4637a;
|
||||||
|
--lineage: #907aa9;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="solarized-light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg: #fdf6e3;
|
||||||
|
--surf: #eee8d5;
|
||||||
|
--raised: #e4ddc8;
|
||||||
|
--border: #d3cbb7;
|
||||||
|
--soft: #f5eedb;
|
||||||
|
--text: #657b83;
|
||||||
|
--muted: #586e75;
|
||||||
|
--dim: #93a1a1;
|
||||||
|
--accent: #268bd2;
|
||||||
|
--a-bg: rgba(38,139,210,.08);
|
||||||
|
--a-str: rgba(38,139,210,.18);
|
||||||
|
--todo: #b58900;
|
||||||
|
--note: #2aa198;
|
||||||
|
--event: #268bd2;
|
||||||
|
--remind: #cb4b16;
|
||||||
|
--ok: #859900;
|
||||||
|
--danger: #dc322f;
|
||||||
|
--lineage: #6c71c4;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── RESET ──────────────────────────────────────────── */
|
/* ── RESET ──────────────────────────────────────────── */
|
||||||
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
html, body { height: 100%; overflow: hidden; }
|
html, body { height: 100%; overflow: hidden; }
|
||||||
@@ -162,6 +382,7 @@ nav { display: flex; gap: 2px; }
|
|||||||
#search-input::placeholder { color: var(--dim); }
|
#search-input::placeholder { color: var(--dim); }
|
||||||
|
|
||||||
.theme-toggle {
|
.theme-toggle {
|
||||||
|
position: relative;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--r1);
|
border-radius: var(--r1);
|
||||||
@@ -174,6 +395,60 @@ nav { display: flex; gap: 2px; }
|
|||||||
|
|
||||||
.theme-toggle:hover { color: var(--accent); border-color: var(--accent); }
|
.theme-toggle:hover { color: var(--accent); border-color: var(--accent); }
|
||||||
|
|
||||||
|
.theme-popover {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 6px);
|
||||||
|
right: 0;
|
||||||
|
z-index: 900;
|
||||||
|
background: var(--surf);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--r3);
|
||||||
|
padding: 8px 0;
|
||||||
|
min-width: 180px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0,0,0,.3);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-popover.open { display: block; }
|
||||||
|
|
||||||
|
.theme-popover-label {
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-family: var(--mono);
|
||||||
|
color: var(--dim);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-popover-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: var(--sans);
|
||||||
|
color: var(--muted);
|
||||||
|
transition: background var(--t-fast), color var(--t-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-popover-item:hover {
|
||||||
|
background: var(--a-bg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-popover-item.active {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-popover-swatch {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── MAIN LAYOUT ────────────────────────────────────── */
|
/* ── MAIN LAYOUT ────────────────────────────────────── */
|
||||||
main {
|
main {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
Reference in New Issue
Block a user