From 1debb7d9da8dde8b3b799b0ca0c869731bbe04d1 Mon Sep 17 00:00:00 2001 From: mhoennig Date: Mon, 24 Aug 2026 17:13:18 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20ID-Vorschl=C3=A4ge=20beim=20Tippen=20vo?= =?UTF-8?q?n=20Abh=C3=A4ngigkeiten=20(:#)=20=E2=80=94=20D63?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wer :# tippt, bekommt die vergebenen IDs als Liste an der Schreibmarke: Präfix- vor Teilstring-Treffern, Knotentitel als Kontext, schon gelistete und die eigene ID ausgenommen. ↑/↓ wählt, Enter/Tab übernimmt (undo-fähig), Esc schließt; Live-Region für Screenreader. Eingabehilfe wie die Kurzform (D55) — der Parser sieht nie etwas davon. Regeln headless in autocomplete.js (20 Tests), app.js verdrahtet Popup, Tasten und Einfügen. Co-Authored-By: Claude Fable 5 --- docs/CHANGELOG.md | 1 + docs/DECISIONS.md | 81 ++++++++++++++ docs/SPEC.md | 24 +++++ docs/examples/werkbaum.werkbaum | 6 ++ frontend/CLAUDE.md | 9 ++ frontend/src/app.js | 160 +++++++++++++++++++++++++++- frontend/src/autocomplete.js | 87 +++++++++++++++ frontend/src/style.css | 29 ++++- frontend/tests/autocomplete.test.js | 114 ++++++++++++++++++++ 9 files changed, 507 insertions(+), 4 deletions(-) create mode 100644 frontend/src/autocomplete.js create mode 100644 frontend/tests/autocomplete.test.js diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 72ee12f..76bd8aa 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -19,6 +19,7 @@ reverse. ## 2026-08-24 +- Typing `:#` suggests the document's ids — arrows choose, Enter inserts - When the caret's node is folded away, the nearest visible ancestor takes the highlight — also on Alt+click from the text - A size that cannot hold its sub-packages turns its badge amber and gets a warning — sizes are read as ranges, so only sure conflicts are reported - Fix: the exported SVG drew the assumed-M badge filled like a real size instead of inverted diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 5b1ad13..a1d5261 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -4688,3 +4688,84 @@ disjunktive Gruppen summiert statt Minimum: genau zwei. Im Browser: Badge den Grund; im exportierten SVG das Konflikt-Badge bernstein und das implizite M weiß mit Petrol-Rand. Kanonisches Beispiel §10 und der mitgelieferte Plan: 0 Warnungen. + +## D63 — ID-Vorschläge beim Tippen von Abhängigkeiten (`:#`) +Wer eine Abhängigkeit tippt, muss die Ziel-ID auswendig wissen oder im Text +suchen — bei 180 IDs im mitgelieferten Plan keine Kleinigkeit. Jetzt öffnet +`:#` eine Vorschlagsliste an der Schreibmarke: die vergebenen IDs, gefiltert +nach dem getippten Fragment, mit dem Knotentitel als Kontext. + +**Dieselbe Kategorie wie die ID-Kurzform (D55): Eingabehilfe, keine +Notation.** Der Parser sieht nie etwas davon, SPEC-Syntax und `llms.md` +bleiben unberührt; übernommen wird undo-fähig über das vorhandene `writeAt()` +(D53). Die drei schwierigen Zutaten lagen schon im Haus: Die **Kandidaten** +kommen aus dem Parse-Baum, der bei jedem Tastendruck ohnehin frisch ist; die +**Pixel-Position der Schreibmarke** misst der Spiegel-`div` aus D25/D33 (ein +Marker-Span, `offsetTop`/`offsetLeft` — seit `wrap="off"` (D49) trivial); das +**Einfügen ohne Undo-Verlust** ist die D53-Lehre. Kein CodeMirror/Monaco — +das wäre eine Laufzeit-Abhängigkeit (D11/D19) für etwas, das drei vorhandene +Mechanismen zusammensetzen. + +**Ausgelöst nur im Abhängigkeits-Kontext, nicht bei jedem `#`.** Ein +alleinstehendes `#` *definiert* meist eine neue ID — dort wäre die Liste im +Weg. Erkannt wird dieselbe Form, die der Parser liest: `(^|\s):#…` (§1) und +die Kopf-Form `#auth:#…` (D36); `(:#a` bleibt Zitat, `Regel: #x` bleibt +Label, im Kommentar und hinter `---` gibt es keinen Kontext. Die Erkennung +(`depFragment`) und die Kandidaten-Auswahl (`collectIds`/`matchIds`) stehen +headless in **`autocomplete.js`** — die Hausregel aus D54-Nachtrag 3; app.js +verdrahtet nur Popup, Tasten und Einfügen. + +**Angeboten wird alles, ausgenommen das Sinnlose.** Auch IDs verworfener und +eingeklappter Knoten stehen in der Liste — eine Abhängigkeit darf dorthin +zeigen (§1), und die Faltung ist nur Ansicht (D38). Ausgenommen sind die im +Token **schon gelisteten** IDs und die **eigene ID der Zeile** (die +Selbst-Abhängigkeit ist zulässig, aber nie das, was man tippen will). +Sortierung: Präfix-Treffer vor Teilstring-Treffern, je in +Dokumentreihenfolge, Groß-/Kleinschreibung egal — die IDs selbst bleiben, wie +sie geschrieben sind. Ersetzt wird bis ans **Ende der ID-Zeichen** hinter der +Schreibmarke, sonst ergäbe eine Übernahme mitten im Wort `#authth`. + +**Die Tasten-Arbitrierung ist der heikle Teil, nicht das Popup.** ↑/↓, Enter, +Tab und Esc gehören sonst dem Textfeld — abgefangen werden sie **nur bei +offener Liste**, über einen Capture-Handler auf `document`: Die +Textfeld-Handler (Tab rückt ein, Esc löst die Tab-Falle — beide D53) sind +früher registriert und kämen sonst zuerst; `stopPropagation` hält sie heraus. +Zwei Folgen, beide gemessen: Tab übernimmt bei offener Liste, **ohne** +zusätzlich einzurücken, und rückt bei geschlossener unverändert ein; Esc +schließt die Liste, ohne die Tab-Falle zu lösen. **Nach Esc und nach einer +Übernahme bleibt derselbe Kontext zu** (`acSuppress`) — sonst öffnete ihn das +nächste keyup sofort wieder; Weitertippen ändert das Fragment und hebt die +Sperre. + +**Ein Echo wird nicht angeboten:** Steht die ID schon vollständig da (der +eine exakte Treffer, Schreibmarke am Token-Ende), bleibt die Liste zu — so +schließt sie nach der Übernahme von selbst und meldet sich nicht bei jedem +Cursor-Besuch einer fertigen Zeile. + +**Barrierefreiheit als benannte Grenze, nicht als Behauptung.** Das saubere +ARIA-Combobox-Muster passt nicht auf ein `