mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-03 16:41:27 +01:00
help: the whole sheet, walked by key
The help was a box narrower than the screen that had to be scrolled with the mouse. It is now as big as the porthole, in newspaper columns that run on to the right: h/l and j/k move a column, Ctrl-D/U and Space a screen, g/G to either end. A row is never split between columns, and says what it says before its first dash; / filters the rows by key or words, and shows the ones left whole. The prose footer became an 'in the editor' group.
This commit is contained in:
1 parent
38d15afda2
commit
304c67151c
4 files changed
+261
-88
No files matched your search
@@ -60,7 +60,7 @@ arguments and `↑` / `↓` recall earlier lines.
|
|||||||
| `gb` | the documents that link to the open one — \[\[title]] links by title, \[\[ completes one while writing |
|
| `gb` | the documents that link to the open one — \[\[title]] links by title, \[\[ completes one while writing |
|
||||||
| `:` | command line — Tab / Shift-Tab cycle the matches, then the argument; ↑ ↓ recall what ran before |
|
| `:` | command line — Tab / Shift-Tab cycle the matches, then the argument; ↑ ↓ recall what ran before |
|
||||||
| `Ctrl-S` | save, from anywhere |
|
| `Ctrl-S` | save, from anywhere |
|
||||||
| `?` | this screen — in the editor too |
|
| `?` | this screen, from the editor too — h/l a column, / filters it down |
|
||||||
|
|
||||||
### On a board
|
### On a board
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ The rail of buttons on the right does the same as the keys, for a mouse.
|
|||||||
inbox of this space's board. Press `b` to see the board, and `b` again to go
|
inbox of this space's board. Press `b` to see the board, and `b` again to go
|
||||||
back.
|
back.
|
||||||
7. Press `T`. That is today's diary entry, started for you.
|
7. Press `T`. That is today's diary entry, started for you.
|
||||||
8. Press `?` for the list of every key and command, and `Esc` to close it.
|
8. Press `?` for the list of every key and command. `h` / `l` walk its columns, `/` narrows it down, and `Esc` closes it.
|
||||||
|
|
||||||
On a phone the same things are buttons: the dock at the bottom has a new
|
On a phone the same things are buttons: the dock at the bottom has a new
|
||||||
document, a new folder, the board, today's entry, an upload and a search, and
|
document, a new folder, the board, today's entry, an upload and a search, and
|
||||||
|
|||||||
+47
-1
@@ -9,7 +9,7 @@
|
|||||||
import DocumentPane from './lib/DocumentPane.svelte'
|
import DocumentPane from './lib/DocumentPane.svelte'
|
||||||
import FilePane from './lib/FilePane.svelte'
|
import FilePane from './lib/FilePane.svelte'
|
||||||
import Headings from './lib/Headings.svelte'
|
import Headings from './lib/Headings.svelte'
|
||||||
import Help from './lib/Help.svelte'
|
import Help, { focusHelpFilter, scrollHelp } from './lib/Help.svelte'
|
||||||
import Hints from './lib/Hints.svelte'
|
import Hints from './lib/Hints.svelte'
|
||||||
import Verify from './lib/Verify.svelte'
|
import Verify from './lib/Verify.svelte'
|
||||||
import Login from './lib/Login.svelte'
|
import Login from './lib/Login.svelte'
|
||||||
@@ -378,6 +378,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The help's keys: its columns run on to the right, so h/l and j/k move a
|
||||||
|
* column, Ctrl-D/U a screen, g/G to either end, and `/` narrows it down.
|
||||||
|
*/
|
||||||
|
function helpKey(event: KeyboardEvent): boolean {
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
if (event.key !== 'd' && event.key !== 'u') return false
|
||||||
|
scrollHelp(event.key === 'd' ? 'pagedown' : 'pageup')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch (event.key) {
|
||||||
|
case 'l':
|
||||||
|
case 'j':
|
||||||
|
case 'ArrowRight':
|
||||||
|
case 'ArrowDown':
|
||||||
|
scrollHelp(1)
|
||||||
|
return true
|
||||||
|
case 'h':
|
||||||
|
case 'k':
|
||||||
|
case 'ArrowLeft':
|
||||||
|
case 'ArrowUp':
|
||||||
|
scrollHelp(-1)
|
||||||
|
return true
|
||||||
|
case ' ':
|
||||||
|
scrollHelp(event.shiftKey ? 'pageup' : 'pagedown')
|
||||||
|
return true
|
||||||
|
case 'g':
|
||||||
|
scrollHelp('start')
|
||||||
|
return true
|
||||||
|
case 'G':
|
||||||
|
scrollHelp('end')
|
||||||
|
return true
|
||||||
|
case '/':
|
||||||
|
focusHelpFilter()
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** `gO`'s list: j/k down the headings, Enter to the one picked. */
|
/** `gO`'s list: j/k down the headings, Enter to the one picked. */
|
||||||
function outlineKey(event: KeyboardEvent): boolean {
|
function outlineKey(event: KeyboardEvent): boolean {
|
||||||
const last = Math.max(workspace.outline.length - 1, 0)
|
const last = Math.max(workspace.outline.length - 1, 0)
|
||||||
@@ -682,6 +722,12 @@
|
|||||||
|
|
||||||
// The command line over an overlay — `D` in the agenda — keeps its keys.
|
// The command line over an overlay — `D` in the agenda — keeps its keys.
|
||||||
const listening = cmdline === null && !isTypingTarget(event.target)
|
const listening = cmdline === null && !isTypingTarget(event.target)
|
||||||
|
if (workspace.overlay === 'help' && listening && !event.metaKey && !event.altKey) {
|
||||||
|
if (helpKey(event)) {
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if (workspace.overlay === 'overview' && listening && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
if (workspace.overlay === 'overview' && listening && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
||||||
if (overviewKey(event)) {
|
if (overviewKey(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|||||||
+212
-85
@@ -1,6 +1,47 @@
|
|||||||
|
<script lang="ts" module>
|
||||||
|
/*
|
||||||
|
* The help is a sheet the size of the porthole, laid out in newspaper
|
||||||
|
* columns that run on to the right, so all of it is in view at once on a
|
||||||
|
* wide screen and a key away on a narrow one. Its keys live in App.svelte
|
||||||
|
* with the others; these are what they move.
|
||||||
|
*/
|
||||||
|
let pages: HTMLElement | undefined = $state()
|
||||||
|
let field: HTMLInputElement | undefined = $state()
|
||||||
|
/** `/` in the help: only the rows whose key or words hold this. */
|
||||||
|
export const helpFilter = $state({ text: '' })
|
||||||
|
|
||||||
|
/** A column on or back, a screen on or back, or to either end. */
|
||||||
|
export function scrollHelp(by: 1 | -1 | 'pagedown' | 'pageup' | 'start' | 'end') {
|
||||||
|
if (!pages) return
|
||||||
|
// Columns that run off the right edge scroll sideways; on a phone they
|
||||||
|
// stack, and the sheet around them scrolls down instead.
|
||||||
|
const sideways = pages.scrollWidth > pages.clientWidth
|
||||||
|
const scroller = sideways ? pages : pages.parentElement
|
||||||
|
if (!scroller) return
|
||||||
|
const axis = sideways ? 'left' : 'top'
|
||||||
|
if (by === 'start' || by === 'end') {
|
||||||
|
scroller.scrollTo({ [axis]: by === 'start' ? 0 : 1e9, behavior: 'smooth' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const page = sideways ? scroller.clientWidth : scroller.clientHeight
|
||||||
|
// A row is never broken over columns, so it is as wide as one of them.
|
||||||
|
const column = sideways ? (pages.querySelector<HTMLElement>('.row')?.offsetWidth ?? 0) + COLUMN_GAP : page / 2
|
||||||
|
const distance = typeof by === 'number' ? by * column : (by === 'pagedown' ? 1 : -1) * page
|
||||||
|
scroller.scrollBy({ [axis]: distance, behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function focusHelpFilter() {
|
||||||
|
field?.focus()
|
||||||
|
field?.select()
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLUMN_GAP = 28
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { commands, groups, type CommandSpec, type Group } from './commands'
|
import { commands, groups, type CommandSpec, type Group } from './commands'
|
||||||
import { workspace } from './store.svelte'
|
import { workspace } from './store.svelte'
|
||||||
|
import { isTouchDevice } from './util'
|
||||||
|
|
||||||
const keys: [string, string][] = [
|
const keys: [string, string][] = [
|
||||||
['1 … 9 · 0', 'switch to that space, like a workspace'],
|
['1 … 9 · 0', 'switch to that space, like a workspace'],
|
||||||
@@ -50,7 +91,7 @@
|
|||||||
['gb', 'the documents that link to the open one — [[title]] links by title, [[ completes one while writing'],
|
['gb', 'the documents that link to the open one — [[title]] links by title, [[ completes one while writing'],
|
||||||
[':', 'command line — Tab / Shift-Tab cycle the matches, then the argument; ↑ ↓ recall what ran before'],
|
[':', 'command line — Tab / Shift-Tab cycle the matches, then the argument; ↑ ↓ recall what ran before'],
|
||||||
['Ctrl-S', 'save, from anywhere'],
|
['Ctrl-S', 'save, from anywhere'],
|
||||||
['?', 'this screen — in the editor too'],
|
['?', 'this screen, from the editor too — h/l a column, / filters it down'],
|
||||||
]
|
]
|
||||||
|
|
||||||
/* The board rebinds most keys, so it gets its own column rather than
|
/* The board rebinds most keys, so it gets its own column rather than
|
||||||
@@ -96,139 +137,225 @@
|
|||||||
view: 'session',
|
view: 'session',
|
||||||
}
|
}
|
||||||
|
|
||||||
const grouped = groups.map(
|
type Row = { key: string; args?: string; description: string }
|
||||||
(group) => [group, commands.filter((spec) => spec.group === group)] as [Group, CommandSpec[]],
|
|
||||||
)
|
const fromPairs = (pairs: [string, string][]): Row[] =>
|
||||||
|
pairs.map(([key, description]) => ({ key, description }))
|
||||||
|
|
||||||
/** `:write(w)` — the name first, then whatever else reaches it. */
|
/** `:write(w)` — the name first, then whatever else reaches it. */
|
||||||
const signature = (spec: CommandSpec) =>
|
const signature = (spec: CommandSpec) =>
|
||||||
`:${spec.name}${spec.aliases?.length ? `(${spec.aliases.join(' ')})` : ''}`
|
`:${spec.name}${spec.aliases?.length ? `(${spec.aliases.join(' ')})` : ''}`
|
||||||
|
|
||||||
|
const fromSpecs = (specs: CommandSpec[]): Row[] =>
|
||||||
|
specs.flatMap((spec) => [
|
||||||
|
{ key: signature(spec), args: spec.args, description: spec.help },
|
||||||
|
...(spec.bang ? [{ key: `:${spec.name}!`, description: spec.bang.help }] : []),
|
||||||
|
])
|
||||||
|
|
||||||
|
const editorKeys: [string, string][] = [
|
||||||
|
['vim', 'the full keymap — hjkl, w/b, dd, ciw, visual mode, macros; :w writes'],
|
||||||
|
['drop · paste', 'an image, video or audio file attaches it'],
|
||||||
|
['$x^2$ · $$…$$', 'set as math'],
|
||||||
|
[':12', 'jumps straight to entry 12'],
|
||||||
|
['y · d', 'copied to the system clipboard — :set noclipboard keeps them in the registers'],
|
||||||
|
['files', 'open in place — pdf, text and code, markdown, csv, certificates and keystores'],
|
||||||
|
]
|
||||||
|
|
||||||
|
const sections: [string, Row[]][] = [
|
||||||
|
['keys', fromPairs(keys)],
|
||||||
|
['board keys', fromPairs(boardKeys)],
|
||||||
|
['agenda keys', fromPairs(agendaKeys)],
|
||||||
|
['in the editor', fromPairs(editorKeys)],
|
||||||
|
...groups.map((group: Group) => [titles[group], fromSpecs(commands.filter((spec) => spec.group === group))] as [string, Row[]]),
|
||||||
|
]
|
||||||
|
|
||||||
|
const shown = $derived.by(() => {
|
||||||
|
const words = helpFilter.text.trim().toLowerCase().split(/\s+/).filter(Boolean)
|
||||||
|
if (words.length === 0) return sections
|
||||||
|
const hit = (row: Row) => {
|
||||||
|
const text = `${row.key} ${row.args ?? ''} ${row.description}`.toLowerCase()
|
||||||
|
return words.every((word) => text.includes(word))
|
||||||
|
}
|
||||||
|
return sections.map(([title, rows]) => [title, rows.filter(hit)] as [string, Row[]]).filter(([, rows]) => rows.length > 0)
|
||||||
|
})
|
||||||
|
const count = $derived(shown.reduce((sum, [, rows]) => sum + rows.length, 0))
|
||||||
|
const filtering = $derived(helpFilter.text.trim() !== '')
|
||||||
|
|
||||||
|
/* What a row says before its first dash is enough to find it by; the rest
|
||||||
|
comes back once a filter has narrowed the sheet down to a few. */
|
||||||
|
const brief = (description: string) => description.split(' — ')[0]
|
||||||
|
/** The key column of a section, as wide as its longest key, within reason. */
|
||||||
|
const keyWidth = (rows: Row[]) => Math.min(Math.max(...rows.map((row) => Math.max(row.key.length, row.args?.length ?? 0))), 14)
|
||||||
|
|
||||||
|
// A filter left over from last time would hide rows without saying so.
|
||||||
|
helpFilter.text = ''
|
||||||
|
|
||||||
|
/* The field takes its own Enter and Esc, so they settle the filter
|
||||||
|
instead of closing the help underneath it. */
|
||||||
|
function filterKey(event: KeyboardEvent) {
|
||||||
|
if (event.key !== 'Escape' && event.key !== 'Enter') return
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
if (event.key === 'Escape') helpFilter.text = ''
|
||||||
|
field?.blur()
|
||||||
|
field?.closest<HTMLElement>('.overlay')?.focus()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="sheet">
|
<div class="sheet">
|
||||||
<header>
|
<header>
|
||||||
<span class="label rule"><span class="marker"></span><span class="title">help</span> everything is a key or a `:` command</span>
|
<span class="label rule"><span class="marker"></span><span class="title">help</span> {count} keys and commands</span>
|
||||||
|
<label class="filter" class:on={helpFilter.text !== ''}>
|
||||||
|
<span class="slash">/</span>
|
||||||
|
<input
|
||||||
|
bind:this={field}
|
||||||
|
bind:value={helpFilter.text}
|
||||||
|
onkeydown={filterKey}
|
||||||
|
placeholder="filter"
|
||||||
|
spellcheck="false"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
<button onclick={() => (workspace.overlay = 'none')}>esc</button>
|
<button onclick={() => (workspace.overlay = 'none')}>esc</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="cols">
|
<div class="cols" bind:this={pages}>
|
||||||
<section class="keys">
|
{#each shown as [title, rows] (title)}
|
||||||
<h2>keys</h2>
|
<section style="--key: {keyWidth(rows)}ch">
|
||||||
<dl>
|
<h2>{title}</h2>
|
||||||
{#each keys as [key, description] (key)}
|
|
||||||
<dt>{key}</dt>
|
|
||||||
<dd>{description}</dd>
|
|
||||||
{/each}
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="keys">
|
|
||||||
<h2>board keys</h2>
|
|
||||||
<dl>
|
|
||||||
{#each boardKeys as [key, description] (key)}
|
|
||||||
<dt>{key}</dt>
|
|
||||||
<dd>{description}</dd>
|
|
||||||
{/each}
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="keys">
|
|
||||||
<h2>agenda keys</h2>
|
|
||||||
<dl>
|
|
||||||
{#each agendaKeys as [key, description] (key)}
|
|
||||||
<dt>{key}</dt>
|
|
||||||
<dd>{description}</dd>
|
|
||||||
{/each}
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{#each grouped as [group, specs] (group)}
|
|
||||||
<section>
|
|
||||||
<h2>{titles[group]}</h2>
|
|
||||||
<dl>
|
<dl>
|
||||||
{#each specs as spec (spec.name)}
|
{#each rows as row (row.key)}
|
||||||
<dt>
|
{@const short = filtering ? row.description : brief(row.description)}
|
||||||
{signature(spec)}{#if spec.args}<span class="args muted">{spec.args}</span>{/if}
|
<div class="row">
|
||||||
</dt>
|
<dt>{row.key}{#if row.args}<span class="args">{row.args}</span>{/if}</dt>
|
||||||
<dd>{spec.help}</dd>
|
<dd>{short}{#if short !== row.description && !short.endsWith('…')}<span class="more" title={row.description}> …</span>{/if}</dd>
|
||||||
{#if spec.bang}
|
</div>
|
||||||
<dt>:{spec.name}!</dt>
|
|
||||||
<dd>{spec.bang.help}</dd>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
</dl>
|
</dl>
|
||||||
</section>
|
</section>
|
||||||
|
{:else}
|
||||||
|
<p class="none">nothing matches "{helpFilter.text}"</p>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
{#if !isTouchDevice}
|
||||||
inside the editor the full vim keymap is live — hjkl, w/b, dd, ciw, visual mode, macros, and
|
<footer class="label">
|
||||||
<span class="key">:w</span> to write. drop or paste an image, video or audio file into the
|
<span class="kbd">h</span><span class="kbd">l</span> column
|
||||||
editor to attach it. <span class="key">$x^2$</span> and <span class="key">$$…$$</span> are set as math. a file opens in place — pdf, text and code, markdown, csv, and
|
<span class="gap"></span>
|
||||||
certificates and keystores, which ask for their password. <span class="key">:12</span> jumps straight to entry 12. yanks and
|
<span class="kbd">⌃d</span><span class="kbd">⌃u</span> page
|
||||||
deletes are copied to the system clipboard —
|
<span class="gap"></span>
|
||||||
<span class="key">:set noclipboard</span> keeps them in vim's registers.
|
<span class="kbd">g</span><span class="kbd">G</span> start · end
|
||||||
</footer>
|
<span class="gap"></span>
|
||||||
|
<span class="kbd">/</span> filter, and read the lines whole
|
||||||
|
<span class="gap"></span>
|
||||||
|
<span class="kbd">?</span><span class="kbd">esc</span> close
|
||||||
|
</footer>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* A free-standing panel, as the desktop draws one: hull, one rim, the frame's radius. */
|
/* A free-standing panel, as the desktop draws one: hull, one rim, the
|
||||||
|
frame's radius — as big as the porthole, so nothing hides below it. */
|
||||||
.sheet {
|
.sheet {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 18px;
|
gap: 14px;
|
||||||
max-height: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
background: color-mix(in oklab, var(--hull) 97%, transparent);
|
background: color-mix(in oklab, var(--hull) 97%, transparent);
|
||||||
border: 1px solid var(--surface-alt);
|
border: 1px solid var(--surface-alt);
|
||||||
border-radius: var(--frame-radius);
|
border-radius: var(--frame-radius);
|
||||||
padding: 20px 24px;
|
padding: 18px 22px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
header { display: flex; align-items: center; gap: 12px; }
|
header { display: flex; align-items: center; gap: 12px; }
|
||||||
header .label { flex: 1; }
|
header .label { flex: 1; }
|
||||||
header .marker { background: var(--light); }
|
header .marker { background: var(--light); }
|
||||||
header .title { color: var(--light); }
|
header .title { color: var(--light); }
|
||||||
.sheet { width: min(100%, 112ch); overflow-y: auto; overflow-x: hidden; }
|
|
||||||
|
|
||||||
/* Newspaper columns, so the short groups fill the gaps under the long ones. */
|
.filter {
|
||||||
.cols { columns: 44ch; column-gap: 36px; }
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
width: 24ch;
|
||||||
|
padding: 0 8px;
|
||||||
|
border: 1px solid var(--rim);
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
.filter:focus-within, .filter.on { border-color: var(--outline); color: var(--light); }
|
||||||
|
.filter input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 4px 0;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
color: var(--light);
|
||||||
|
font: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
section { break-inside: avoid; min-width: 0; margin: 0 0 20px; }
|
/*
|
||||||
|
* Newspaper columns of a fixed height, so they fill across before they
|
||||||
|
* run off the right edge, where h/l reach them. A group may break between
|
||||||
|
* columns, but never straight after its heading.
|
||||||
|
*/
|
||||||
|
.cols {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
columns: 40ch;
|
||||||
|
column-gap: 28px;
|
||||||
|
column-fill: auto;
|
||||||
|
column-rule: 1px solid var(--surface-alt);
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
section { margin: 0 0 12px; }
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
margin: 0 0 8px;
|
margin: 0 0 6px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.22em;
|
letter-spacing: 0.22em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
|
break-after: avoid;
|
||||||
}
|
}
|
||||||
h2::after { content: ''; height: 1px; flex: 1; background: var(--overlay); }
|
h2::after { content: ''; height: 1px; flex: 1; background: var(--overlay); }
|
||||||
|
|
||||||
/* The key column is capped, so one long signature cannot squeeze every
|
/* One row is never split between columns. The key column is as wide as
|
||||||
description in its group down to a letter a line; arguments wrap
|
the section's longest key up to a cap, so one long signature cannot
|
||||||
underneath the name instead. */
|
squeeze every description down; arguments wrap under the name. */
|
||||||
dl {
|
dl { margin: 0; font-size: 12px; line-height: 1.35; }
|
||||||
margin: 0;
|
.row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: fit-content(20ch) minmax(0, 1fr);
|
grid-template-columns: var(--key) minmax(0, 1fr);
|
||||||
gap: 7px 16px;
|
gap: 12px;
|
||||||
font-size: 13px;
|
padding: 1px 0;
|
||||||
line-height: 1.45;
|
break-inside: avoid;
|
||||||
}
|
}
|
||||||
dt { color: var(--light); white-space: nowrap; }
|
dt { color: var(--light); overflow-wrap: break-word; }
|
||||||
.args { display: block; white-space: normal; font-size: 11px; }
|
.args { display: block; font-size: 10px; color: var(--muted); }
|
||||||
dd { margin: 0; color: var(--fg); overflow-wrap: break-word; }
|
dd { margin: 0; color: var(--subtle); overflow-wrap: break-word; }
|
||||||
|
.more { color: var(--outline); }
|
||||||
|
|
||||||
footer {
|
.none { margin: 0; color: var(--subtle); font-size: 12px; }
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted);
|
footer { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; font-size: 10px; }
|
||||||
border-top: 1px solid var(--overlay);
|
footer .kbd { margin-right: 2px; }
|
||||||
padding-top: 12px;
|
footer .gap { width: 14px; }
|
||||||
line-height: 1.7;
|
|
||||||
|
/* A phone stacks the columns and scrolls the sheet down. */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.sheet { overflow-y: auto; }
|
||||||
|
.cols { flex: none; columns: auto; overflow: visible; }
|
||||||
|
.filter { width: 14ch; }
|
||||||
}
|
}
|
||||||
.key { color: var(--light); }
|
|
||||||
</style>
|
</style>
|
||||||
Reference in new issue
Block a user