Initial commit
This commit is contained in:
1 parent
1ba45f88aa
commit
9e6804c1ab
77 files changed
+3888
No files matched your search
@@ -0,0 +1,153 @@
|
||||
---
|
||||
// Ctrl/⌘ K on the website, like in the app: jump to any page or section.
|
||||
import { config } from '../config';
|
||||
import { href } from '../lib/url';
|
||||
|
||||
interface Item { group: string; label: string; url: string; keys?: string }
|
||||
const items: Item[] = [
|
||||
{ group: 'Page', label: 'Home', url: href('/') },
|
||||
{ group: 'Page', label: 'Features', url: href('/features/'), keys: 'tour' },
|
||||
{ group: 'Page', label: 'Download', url: href('/download/'), keys: 'install get' },
|
||||
{ group: 'Page', label: 'Changelog', url: href('/changelog/'), keys: 'release news versions' },
|
||||
{ group: 'Page', label: 'Docs & FAQ', url: href('/docs/'), keys: 'help questions' },
|
||||
{ group: 'Action', label: `Download Ink ${config.version} for Linux`, url: config.platforms.linux.url, keys: 'appimage linux get' },
|
||||
{ group: 'Action', label: 'Verify the download (SHA-256)', url: href('/download/#verify'), keys: 'checksum sha256 hash' },
|
||||
{ group: 'Action', label: 'Report a bug or request a feature', url: config.issues, keys: 'issue github' },
|
||||
{ group: 'Features', label: 'Writing: block editor', url: href('/features/#writing'), keys: 'slash menu markdown code math katex mermaid callout table' },
|
||||
{ group: 'Features', label: 'Linking: wiki links, embeds, backlinks', url: href('/features/#linking'), keys: '[[ ]] link rename' },
|
||||
{ group: 'Features', label: 'Canvas', url: href('/features/#canvas'), keys: 'json canvas arrows groups export png svg pdf' },
|
||||
{ group: 'Features', label: 'Kanban boards', url: href('/features/#boards'), keys: 'board columns cards' },
|
||||
{ group: 'Features', label: 'Tags, search, graph, daily notes', url: href('/features/#organising'), keys: 'tag search palette graph daily templates favorites' },
|
||||
{ group: 'Features', label: 'Version history, trash, export', url: href('/features/#safety'), keys: 'history restore trash export' },
|
||||
{ group: 'Features', label: 'Themes, focus mode, spellcheck', url: href('/features/#comfort'), keys: 'dark light theme focus outline text size' },
|
||||
{ group: 'Docs', label: 'Where are my notes stored?', url: href('/docs/#storage'), keys: 'folder files md .notes' },
|
||||
{ group: 'Docs', label: 'Obsidian compatibility', url: href('/docs/#obsidian'), keys: 'vault obsidian canvas kanban' },
|
||||
{ group: 'Docs', label: 'Keyboard shortcuts', url: href('/docs/#shortcuts'), keys: 'keys hotkeys ctrl' },
|
||||
{ group: 'Docs', label: 'Privacy', url: href('/docs/#privacy'), keys: 'tracking analytics cookies data' },
|
||||
];
|
||||
---
|
||||
<dialog class="palette" data-palette aria-label="Jump to a page or section">
|
||||
<div class="p-head">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6.5" /><path d="M16 16l4 4" /></svg>
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
aria-expanded="true"
|
||||
aria-controls="palette-list"
|
||||
aria-autocomplete="list"
|
||||
aria-label="Search pages and sections"
|
||||
placeholder="Jump to…"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
data-palette-input
|
||||
/>
|
||||
<kbd>Esc</kbd>
|
||||
</div>
|
||||
<ul id="palette-list" role="listbox" aria-label="Results" data-palette-list>
|
||||
{items.map((it, i) => (
|
||||
<li role="option" id={`palette-opt-${i}`} aria-selected="false" data-url={it.url} data-text={`${it.group} ${it.label} ${it.keys ?? ''}`.toLowerCase()}>
|
||||
<span class="g">{it.group}</span>
|
||||
<span class="l">{it.label}</span>
|
||||
<span class="enter" aria-hidden="true">↵</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p class="empty" hidden data-palette-empty>Nothing matches. Try “canvas”, “shortcuts” or “download”.</p>
|
||||
<div class="p-foot" aria-hidden="true">
|
||||
<span><kbd>↑</kbd><kbd>↓</kbd> move</span><span><kbd>↵</kbd> open</span><span><kbd>Esc</kbd> close</span>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<style>
|
||||
.palette {
|
||||
width: min(100% - 2rem, 600px); max-height: min(70vh, 560px); padding: 0; margin: 12vh auto auto;
|
||||
border: 1px solid var(--line-2); border-radius: 16px; background: var(--surface); color: var(--ink);
|
||||
box-shadow: var(--shadow); overflow: hidden; flex-direction: column;
|
||||
}
|
||||
.palette[open] { display: flex; animation: pop 0.22s var(--ease); }
|
||||
.palette::backdrop { background: rgba(0, 0, 0, 0.45); backdrop-filter: blur(3px); animation: fade 0.22s var(--ease); }
|
||||
@keyframes pop { from { opacity: 0; transform: translateY(-8px) scale(0.98); } }
|
||||
@keyframes fade { from { opacity: 0; } }
|
||||
.p-head { display: flex; align-items: center; gap: 0.7rem; padding: 0.95rem 1.1rem; border-bottom: 1px solid var(--line); }
|
||||
.p-head svg { width: 18px; height: 18px; fill: none; stroke: var(--muted); stroke-width: 1.8; stroke-linecap: round; flex: none; }
|
||||
input { flex: 1; min-width: 0; font: 400 1.05rem/1.4 var(--f-ui); color: var(--ink); background: none; border: 0; outline: none; }
|
||||
input::placeholder { color: var(--muted); }
|
||||
ul { list-style: none; margin: 0; padding: 0.4rem; overflow-y: auto; }
|
||||
li {
|
||||
display: flex; align-items: center; gap: 0.75rem; padding: 0.62rem 0.75rem; border-radius: 10px;
|
||||
cursor: pointer; color: var(--ink-2);
|
||||
}
|
||||
li[hidden] { display: none; }
|
||||
li[aria-selected='true'] { background: var(--blush-soft); color: var(--ink); }
|
||||
.g { font: 500 0.7rem/1 var(--f-mono); text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); width: 4.8rem; flex: none; }
|
||||
.l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.enter { color: var(--accent-text); opacity: 0; font-size: 0.9rem; }
|
||||
li[aria-selected='true'] .enter { opacity: 1; }
|
||||
.empty { padding: 1.5rem 1.1rem; color: var(--muted); }
|
||||
.p-foot { display: flex; gap: 1.1rem; padding: 0.6rem 1.1rem; border-top: 1px solid var(--line); font-size: 0.78rem; color: var(--muted); }
|
||||
.p-foot kbd { font-size: 0.7rem; padding: 0.2rem 0.35rem; margin-right: 0.2rem; }
|
||||
@media (max-width: 520px) {
|
||||
.palette { margin-top: 1rem; }
|
||||
.g { display: none; }
|
||||
.p-foot { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const dlg = document.querySelector<HTMLDialogElement>('[data-palette]')!;
|
||||
const input = dlg.querySelector<HTMLInputElement>('[data-palette-input]')!;
|
||||
const opts = [...dlg.querySelectorAll<HTMLLIElement>('[role="option"]')];
|
||||
const empty = dlg.querySelector<HTMLElement>('[data-palette-empty]')!;
|
||||
let active = 0;
|
||||
|
||||
const visible = () => opts.filter((o) => !o.hidden);
|
||||
function select(i: number) {
|
||||
const list = visible();
|
||||
if (!list.length) { input.removeAttribute('aria-activedescendant'); return; }
|
||||
active = (i + list.length) % list.length;
|
||||
opts.forEach((o) => o.setAttribute('aria-selected', 'false'));
|
||||
const el = list[active];
|
||||
el.setAttribute('aria-selected', 'true');
|
||||
input.setAttribute('aria-activedescendant', el.id);
|
||||
el.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
function filter() {
|
||||
const words = input.value.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
for (const o of opts) o.hidden = !words.every((w) => o.dataset.text!.includes(w));
|
||||
empty.hidden = visible().length > 0;
|
||||
select(0);
|
||||
}
|
||||
function go(o: HTMLElement | undefined) {
|
||||
if (!o) return;
|
||||
dlg.close();
|
||||
location.href = o.dataset.url!;
|
||||
}
|
||||
function open() {
|
||||
if (dlg.open) return;
|
||||
input.value = '';
|
||||
filter();
|
||||
dlg.showModal();
|
||||
input.focus();
|
||||
}
|
||||
|
||||
input.addEventListener('input', filter);
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowDown') { e.preventDefault(); select(active + 1); }
|
||||
else if (e.key === 'ArrowUp') { e.preventDefault(); select(active - 1); }
|
||||
else if (e.key === 'Enter') { e.preventDefault(); go(visible()[active]); }
|
||||
});
|
||||
opts.forEach((o) => {
|
||||
o.addEventListener('click', () => go(o));
|
||||
o.addEventListener('pointermove', () => { const i = visible().indexOf(o); if (i !== active) select(i); });
|
||||
});
|
||||
// Click on the backdrop closes.
|
||||
dlg.addEventListener('click', (e) => { if (e.target === dlg) dlg.close(); });
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
|
||||
e.preventDefault();
|
||||
dlg.open ? dlg.close() : open();
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('[data-palette-open]').forEach((b) => b.addEventListener('click', open));
|
||||
</script>
|
||||
Reference in new issue
Block a user