feat: more : commands, grouped help, legible vim prompt

Adds :today, :next/:prev, :first/:last, :random, :clear, :reload, :copy,
:export, :stats and :theme, plus bangs on :w, :wq and :e. Every command now
declares a group so :help can lay them out under headings.

The help sheet flowed its columns wider than the sheet and clipped the
descriptions; it is newspaper columns now, and it lists the ! variants.

CodeMirror's base theme styles panels for a light editor with selectors the
app stylesheet cannot outrank, so the vim `:` prompt rendered black on black.
Those rules move into the editor's own theme, which does win, and the theme
declares itself dark so the light defaults stop applying at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01476ZKdB4k3rPDwvVhXcnDg
This commit is contained in:
nvrlandClaude Opus 5 committed 2026-09-05 07:18:32 +02:00
1 parent bf3c1f547a
commit 888e2bf7fe
4 files changed
+278 -38

No files matched your search

+65 -27
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { commands } from './commands'
import { commands, groups, type CommandSpec, type Group } from './commands'
import { diary } from './store.svelte'
const keys: [string, string][] = [
@@ -14,20 +14,36 @@
['s', 'toggle sharing for the entry'],
['y', 'copy the share link'],
['x · dd', 'delete the entry (asks first)'],
[': ', 'command line'],
[':', 'command line'],
['Ctrl-S', 'save, from anywhere'],
['?', 'this screen'],
]
const titles: Record<Group, string> = {
entries: 'entries',
editing: 'writing',
sharing: 'sharing & export',
view: 'session',
}
const grouped = groups.map(
(group) => [group, commands.filter((spec) => spec.group === group)] as [Group, CommandSpec[]],
)
/** `:write(w)` — the name first, then whatever else reaches it. */
const signature = (spec: CommandSpec) =>
`:${spec.name}${spec.aliases?.length ? `(${spec.aliases.join(' ')})` : ''}`
</script>
<div class="sheet">
<header>
<span class="accent">:help</span>
<span class="faint">everything is a key or a `:` command</span>
<button onclick={() => (diary.overlay = 'none')}>esc</button>
</header>
<div class="cols">
<section>
<section class="keys">
<h2>keys</h2>
<dl>
{#each keys as [key, description] (key)}
@@ -37,28 +53,29 @@
</dl>
</section>
<section>
<h2>commands</h2>
<dl>
{#each commands as spec (spec.name)}
<dt>
:{spec.name}{#if spec.aliases?.length}<span class="faint"> ({spec.aliases.join(' ')})</span>{/if}
{#if spec.args}<span class="faint"> {spec.args}</span>{/if}
</dt>
<dd>{spec.help}</dd>
{#if spec.bang}
<dt>:{spec.name}!{#if spec.aliases?.length}<span class="faint"> ({spec.aliases.map((a) => `${a}!`).join(' ')})</span>{/if}</dt>
<dd>{spec.bang.help}</dd>
{/if}
{/each}
</dl>
</section>
{#each grouped as [group, specs] (group)}
<section>
<h2>{titles[group]}</h2>
<dl>
{#each specs as spec (spec.name)}
<dt>
{signature(spec)}{#if spec.args}<span class="args faint">{spec.args}</span>{/if}
</dt>
<dd>{spec.help}</dd>
{#if spec.bang}
<dt>:{spec.name}!</dt>
<dd>{spec.bang.help}</dd>
{/if}
{/each}
</dl>
</section>
{/each}
</div>
<footer class="faint">
inside the editor the full vim keymap is live — hjkl, w/b, dd, ciw, visual mode, macros, and
<span class="accent">:w</span> to write. drop or paste an image, video or audio file into the
editor to attach it.
editor to attach it. <span class="accent">:12</span> jumps straight to entry 12.
</footer>
</div>
@@ -66,25 +83,46 @@
.sheet {
display: flex;
flex-direction: column;
gap: 12px;
width: min(100%, 90ch);
gap: 14px;
width: min(100%, 110ch);
max-height: 100%;
background: var(--bg-alt);
border: 1px solid var(--accent-dim);
border-radius: var(--radius);
padding: 14px 16px;
overflow-y: auto;
overflow-x: hidden;
}
header { display: flex; justify-content: space-between; align-items: center; }
header { display: flex; align-items: baseline; gap: 12px; }
header .faint { flex: 1; font-size: 12px; }
header button { align-self: center; }
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr)); gap: 20px; }
/* Newspaper columns, so the short groups fill the gaps under the long ones. */
.cols { columns: 38ch; column-gap: 28px; }
h2 { font-size: 12px; color: var(--fg-dim); text-transform: uppercase; letter-spacing: 0.1em; margin: 0 0 6px; }
section { break-inside: avoid; min-width: 0; margin: 0 0 16px; }
dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; font-size: 12px; }
h2 {
font-size: 12px;
color: var(--fg-dim);
text-transform: uppercase;
letter-spacing: 0.1em;
margin: 0 0 6px;
border-bottom: 1px dashed var(--line);
padding-bottom: 4px;
}
dl {
margin: 0;
display: grid;
grid-template-columns: max-content minmax(0, 1fr);
gap: 3px 12px;
font-size: 12px;
}
dt { color: var(--accent); white-space: nowrap; }
dd { margin: 0; color: var(--fg-dim); }
.args { margin-left: 0.6ch; }
dd { margin: 0; color: var(--fg-dim); overflow-wrap: anywhere; }
footer { font-size: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
</style>