A first start (nothing in localStorage) now opens a blank document -- one
sheet "Blatt 1" with an empty "Tabelle 1" -- instead of the pre-filled
sample. The file menu gets "Neu" (blank document) and "Muster ·
Umsatzanalyse" (the former seed), both behind a confirm and both starting
a fresh undo history. The toolbar's "Zurücksetzen" button is gone; its job
is now Datei › Muster.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Erster von zwei Commits (UI folgt separat): neue, explizit ausgelöste
Makro-Sprache — dieselbe block-mode-Grammatik wie bei "Mit Schleife"-
Funktionen (FOR/IF/RETURN), plus ein neues Schreib-Vokabular (SETVALUE,
INSERTROW/DELETEROW/INSERTCOL/DELETECOL, ADDTABLE/RENAMETABLE/DELETETABLE,
ADDSHEET/RENAMESHEET/DELETESHEET), adressiert mit der bestehenden
Blatt!Tabelle::Zelle-Syntax.
Bewusst außerhalb der normalen Neuberechnung: ein Makro läuft nie aus einer
Zellformel heraus, sondern nur explizit getriggert (store.runMacro), und
mutiert ausschließlich über die bereits vorhandenen Store-Primitiven
(setCellInput, insertRow, addTable, addSheet, …) — Workbook/EngineHost/
DependencyGraph bleiben unverändert rein lesend. Ein ganzer synchroner
Makro-Lauf wird automatisch zu einem einzigen Undo-Schritt.
50 neue Tests (Engine + Store), inkl. eines gezielten Regressionstests für
die zentrale Korrektheits-Fallgrube: eine strukturelle Aktion mitten im
Lauf ersetzt die Workbook-Instanz komplett, jeder Host-Zugriff muss daher
frisch aufgelöst werden statt eine Referenz zu cachen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Vergleichsoperatoren (= <> > < >= <=) unterscheiden jetzt Text- von
Zahlenvergleich statt jede Seite zwangsweise durch toNumber() zu
schicken (=A1="Nord" gab vorher #VALUE! statt 1); ermöglicht
Text-Kriterien in selbstgebauten COUNTIF/SUMIF/AVERAGEIF-Funktionen
(neue README-Sektion "SUMIF/COUNTIF/AVERAGEIF nachbauen").
- Neuer &-Operator (Excel-Präzedenz zwischen Vergleich und +/-) plus
CONCAT/CONCATENATE als Funktions-Äquivalent.
- Benannte Bereiche: neuer Toolbar-Button "🏷" verwaltet dokumentweite
Namen für eine feste Zelle/einen festen Bereich (z. B. Umsatz_Q1statt
Umsatz!B2:B6), unqualifiziert in jeder Top-Level-Formel nutzbar.
Live gegen die Tabellen-ID aufgelöst (übersteht Tabellen-Umbenennung),
bewusst nicht innerhalb einer benutzerdefinierten Funktion selbst
nutzbar (Aufrufer können sie aber als Argument übergeben).
- Nebenbei: COUNT2s Autovervollständigungs-Hint korrigiert (zeigte
"COUNT(...)" statt "COUNT2(...)").
374 Tests, vue-tsc/eslint/build sauber, Playwright-live-verifiziert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Document-wide, named formula functions (e.g. MARGE(revenue; cost) =
(revenue - cost) / revenue) callable from any cell like a built-in
(=MARGE(Umsatz!B2; Umsatz!C2)). Created, edited and deleted only through
a new "𝑓 Funktionen" toolbar button -> modal (list + shared edit form),
never via JSON handedit.
Engine: a call is inlined into the caller's AST at parse time
(src/engine/userFunctions.ts's expandUserFunctions), so evaluator.ts and
graph.ts need no changes -- dependencies on cells referenced only inside
a function body (not in the call) are picked up for free. parser.ts
gains an optional paramNames argument to parseFormula() producing a new
ParamNode leaf, used only when parsing a function body.
Store: addCustomFunction/updateCustomFunction/deleteCustomFunction with
inline (not toast) validation -- name/parameter shape, no collision with
a built-in or another function, the body parses, and a definition-time
cycle check across the whole call graph. Renaming/deleting a function
mirrors table rename/delete: existing calls are not rewritten, they
become #NAME? on the next rebuild. Fully undo/redo-able and part of the
JSON document (customFunctions: [], migrated for v1 and older v2 docs).
178 tests passing (32 new), vue-tsc and eslint clean. Manually verified
end-to-end in the running app via Playwright: define/call/recalculate,
rename -> #NAME?, a closing cycle rejected inline at save, undo restores
the function library.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>