feat(capture): skriv hvor som helst, og se hvad feltet forstår
Release / Build and publish image (push) Failing after 10m4s
Release / Build and publish image (push) Failing after 10m4s
To ting fra Doda. Begynder man at skrive et sted i appen, åbner hurtig tilføjelse med bogstavet allerede i feltet — ingen knap at ramme først, og tanken skal ikke overleve en rejse hen for at finde et sted at ligge. Og under feltet står nu, hvad parseren kan læse: #projekt, /sektion, @etiket, p1, en dato. Det stod i pladsholderen før, som er det ene sted en hjælp ikke kan stå: den forsvinder ved første anslag, altså præcis når den begynder at være nyttig. Prisen er de enkelte bogstaver som genveje. Ét tastatur kan ikke både stave "tal med Anders" og springe til I dag på samme t, og det, man gør halvtreds gange om dagen, er at fange. Navigation ligger i paletten, som kan mere og koster ét anslag. Genvejssiden er rettet, så den ikke lover q, t og u. Undervejs, i den rækkefølge testene fandt dem: reglen var først "er det et tekstfelt?", hvilket stjal bogstaver fra en fokuseret knap eller liste — den er nu "har noget som helst fokus?". Hjælpelinjen lå i flowet og skubbede hele listen ned, når feltet fik fokus, så klik landede en række forkert; tolv tests fangede det. Den svæver nu, og efter at have svævet ovenpå den første opgave og slugt dens klik tager den ikke længere imod pegeredskaber. Og en fejl, der allerede lå der: q slog feltet op på aria-label="Ny opgave", så genvejen gjorde stille ingenting for enhver, der kørte fladen på engelsk. Feltet findes nu på et mærke, ikke på en oversat tekst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a38a79e9b6
commit
cbe66d75a5
@@ -21,6 +21,19 @@
|
||||
let spans = $state([]);
|
||||
let submitting = $state(false);
|
||||
let input;
|
||||
let focused = $state(false);
|
||||
|
||||
// Kept beside the parser's own vocabulary on purpose: a legend that promises
|
||||
// something internal/quickadd cannot read is worse than no legend. These four
|
||||
// are what Parse recognises as marks; dates it reads as prose, which is why the
|
||||
// last entry is an example rather than a symbol.
|
||||
let syntax = $derived([
|
||||
{ mark: '#', what: t('task.syntaxProject') },
|
||||
{ mark: '/', what: t('task.syntaxSection') },
|
||||
{ mark: '@', what: t('task.syntaxLabel') },
|
||||
{ mark: 'p1', what: t('task.syntaxPriority') },
|
||||
{ mark: t('task.syntaxDateMark'), what: t('task.syntaxDate') }
|
||||
]);
|
||||
|
||||
let previewController = null;
|
||||
let previewTimer = null;
|
||||
@@ -143,7 +156,10 @@
|
||||
bind:this={input}
|
||||
bind:value={text}
|
||||
{onkeydown}
|
||||
onfocus={() => (focused = true)}
|
||||
onblur={() => (focused = false)}
|
||||
type="text"
|
||||
data-quickadd
|
||||
placeholder={t('task.placeholder')}
|
||||
aria-label={t('task.new')}
|
||||
autocomplete="off"
|
||||
@@ -154,10 +170,52 @@
|
||||
{#if text.trim()}
|
||||
<button type="submit" class="submit" disabled={submitting}>{t('task.add')}</button>
|
||||
{/if}
|
||||
|
||||
<!-- What the parser understands, spelled out while somebody is typing into it.
|
||||
It was in the placeholder before, which is the one place a hint cannot be:
|
||||
the placeholder disappears at the first keystroke, so the help was gone
|
||||
exactly when it started being useful.
|
||||
|
||||
Floated rather than in the flow. In the flow it pushed the whole list down
|
||||
the moment the field took focus, and a click aimed at the first task landed
|
||||
on the second — twelve tests caught it, which is twelve more than the eye
|
||||
would have. -->
|
||||
{#if focused}
|
||||
<p class="syntax">
|
||||
{#each syntax as item}
|
||||
<span><kbd>{item.mark}</kbd> {item.what}</span>
|
||||
{/each}
|
||||
</p>
|
||||
{/if}
|
||||
</form>
|
||||
|
||||
<style>
|
||||
/* Sits under the field it explains, in the same rhythm as a hint anywhere else. */
|
||||
.syntax {
|
||||
/* Nothing to click, and it floats over the first task in the list — which it
|
||||
promptly swallowed the clicks of. Legends are for reading. */
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
background: var(--surface);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--s1) var(--s3);
|
||||
padding: var(--s1) 0 0 var(--s4);
|
||||
font-size: var(--text-xs);
|
||||
color: var(--ink-faint);
|
||||
}
|
||||
|
||||
.syntax kbd {
|
||||
font-family: var(--mono, ui-monospace, monospace);
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.quickadd {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s3);
|
||||
|
||||
@@ -57,6 +57,12 @@ export const da = {
|
||||
// --- a task in a list ----------------------------------------------------------
|
||||
'task.add': 'Tilføj',
|
||||
'task.new': 'Ny opgave',
|
||||
'task.syntaxProject': 'projekt',
|
||||
'task.syntaxSection': 'sektion',
|
||||
'task.syntaxLabel': 'etiket',
|
||||
'task.syntaxPriority': 'prioritet',
|
||||
'task.syntaxDateMark': 'i morgen kl 10',
|
||||
'task.syntaxDate': 'forfalder',
|
||||
'task.placeholder': 'Tilføj en opgave — prøv “betal moms i morgen kl 10 p1 #Firma”',
|
||||
'task.complete': 'Markér som færdig',
|
||||
'task.reopen': 'Genåbn opgave',
|
||||
@@ -371,6 +377,8 @@ export const da = {
|
||||
'data.templatesHint':
|
||||
'Et projekt gemt som en form, der kan bruges igen. Datoerne i skabelonen er relative, så de bliver regnet ud fra den startdato, du vælger.',
|
||||
|
||||
'keys.anyLetter': 'et bogstav',
|
||||
'keys.typeToCapture': 'begynder en opgave, med bogstavet i feltet',
|
||||
'keys.navigation': 'Navigation',
|
||||
'keys.jumpToQuickAdd': 'Hop til feltet “tilføj opgave”',
|
||||
'keys.thisPage': 'Denne side',
|
||||
|
||||
@@ -53,6 +53,12 @@ export const en = {
|
||||
// --- a task in a list ----------------------------------------------------------
|
||||
'task.add': 'Add',
|
||||
'task.new': 'New task',
|
||||
'task.syntaxProject': 'project',
|
||||
'task.syntaxSection': 'section',
|
||||
'task.syntaxLabel': 'label',
|
||||
'task.syntaxPriority': 'priority',
|
||||
'task.syntaxDateMark': 'tomorrow at 10',
|
||||
'task.syntaxDate': 'due',
|
||||
'task.placeholder': 'Add a task — try “pay VAT tomorrow at 10 p1 #Company”',
|
||||
'task.complete': 'Mark as done',
|
||||
'task.reopen': 'Reopen task',
|
||||
@@ -366,6 +372,8 @@ export const en = {
|
||||
'data.templatesHint':
|
||||
'A project saved as a shape you can use again. The dates in a template are relative, so they are worked out from the start date you pick.',
|
||||
|
||||
'keys.anyLetter': 'any letter',
|
||||
'keys.typeToCapture': 'starts a task, with the letter already in the field',
|
||||
'keys.navigation': 'Navigation',
|
||||
'keys.jumpToQuickAdd': 'Jump to the “add a task” field',
|
||||
'keys.thisPage': 'This page',
|
||||
|
||||
Reference in New Issue
Block a user