Add Rails backend: accounts via magic link, server documents, sharing
The app becomes a Rails 8 application; the Vue frontend moves to app/frontend and is served through vite_rails. - Without an account documents stay in the browser (IndexedDB). Sign-in is by magic link or the 6-digit code from the same mail; afterwards local documents can be uploaded into the account. - Server documents (/d/<id>) with versioned saves and a single-editor lock per browser tab; others see them read-only and follow by polling. - Sharing via link (works without an account) and via e-mail invitation, roles viewer/editor; the owner manages links and people. - Store gains a read-only mode that blocks all content changes. - Solid Queue, Cache and Cable share the one PostgreSQL database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,21 +4,34 @@ Eine browserbasierte Tabellenkalkulation nach dem Vorbild von **Apple Numbers**.
|
||||
|
||||
1. **Handwerk** — eine von Grund auf selbst geschriebene, schlanke Formel-Engine
|
||||
(Tokenizer, Parser, AST, Abhängigkeitsgraph, Neuberechnung, Zirkelbezug-
|
||||
Erkennung). Dokumentiert in [`src/engine/README.md`](src/engine/README.md).
|
||||
Erkennung). Dokumentiert in [`app/frontend/engine/README.md`](app/frontend/engine/README.md).
|
||||
2. **Produkt** — eine polierte, Numbers-nahe Oberfläche: jedes Blatt ist eine
|
||||
**freie Fläche (Canvas)**, auf die man beliebig viele **Tabellen- und
|
||||
Diagramm-Objekte** setzt, verschiebt, skaliert und befüllt.
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Vite-Devserver
|
||||
npm run test # Vitest-Suite (Engine + Store + IO), 570 Tests
|
||||
npm run build # Typecheck + Produktionsbuild
|
||||
bin/setup # Gems, npm-Pakete, Datenbank (PostgreSQL) anlegen
|
||||
bin/dev # Rails (Port 3000) + Vite-Devserver mit HMR (Port 3036)
|
||||
npm run test # Vitest-Suite (Engine + Store + IO)
|
||||
bin/rails test # Server-Tests (Models, API, Anmeldung, Teilen)
|
||||
npm run build # Typecheck + Produktionsbuild nach public/vite
|
||||
npm run lint # ESLint (flat config)
|
||||
```
|
||||
|
||||
Alles läuft **rein clientseitig** — kein Server, keine Netzwerk-Requests. Datei-
|
||||
I/O über File API und Blob-URLs.
|
||||
Die App ist eine **Rails-8-Anwendung** mit dem Vue-Frontend unter
|
||||
[`app/frontend/`](app/frontend/) (eingebunden über `vite_rails`). Ohne Konto
|
||||
bleibt alles **im Browser** (IndexedDB). Mit Konto (Anmeldung per Magic Link,
|
||||
kein Passwort) liegen Dokumente auf dem Server und lassen sich teilen – per
|
||||
Link oder per Einladung, mit den Rollen „ansehen“ und „bearbeiten“. Solange
|
||||
es keine Live-Zusammenarbeit gibt, bearbeitet immer nur ein Browser-Tab ein
|
||||
Server-Dokument; alle anderen sehen es schreibgeschützt und folgen den
|
||||
Änderungen. Details: [`docs/konzept-teilen-und-zusammenarbeit.md`](docs/konzept-teilen-und-zusammenarbeit.md).
|
||||
|
||||
Mails landen in der Entwicklung nicht im Postfach, sondern unter
|
||||
<http://localhost:3000/letter_opener>. Produktion braucht `SMTP_ADDRESS`,
|
||||
`SMTP_USER_NAME`, `SMTP_PASSWORD`, `NUMOLUS_MAIL_FROM`, `NUMOLUS_HOST` und
|
||||
`NUMOLUS_DATABASE_PASSWORD`; Solid Queue, Cache und Cable liegen in derselben
|
||||
Datenbank wie die Daten.
|
||||
|
||||
---
|
||||
|
||||
@@ -268,7 +281,7 @@ eingebauter Funktionsname: `=MARGE(Umsatz!B2; Umsatz!C2)`.
|
||||
sich automatisch umschreiben ließe, nur ihren Aufruf-Namen.
|
||||
- Ein Aufruf wird **beim Parsen der aufrufenden Zelle in ihre AST eingesetzt**
|
||||
(Inlining, keine Laufzeit-Closures) — siehe
|
||||
[`src/engine/userFunctions.ts`](src/engine/userFunctions.ts) und die
|
||||
[`app/frontend/engine/userFunctions.ts`](app/frontend/engine/userFunctions.ts) und die
|
||||
Architektur-Tabelle unten. Dadurch braucht Evaluator/Graph keine Änderung,
|
||||
und auch Zellen, die *nur* im Funktionskörper referenziert werden (nicht im
|
||||
Aufruf), lösen bei Änderung korrekt eine Neuberechnung aus.
|
||||
@@ -336,7 +349,7 @@ berechenbar ist. Eine `FOR`-Schleife über insgesamt mehr als 10.000 Zellen
|
||||
Funktion, deren Ausführung endet, ohne dass `RETURN` je gefeuert hat,
|
||||
ebenso — das Formular warnt davor zusätzlich schon **beim Speichern**, wenn
|
||||
nicht erkennbar jeder Codepfad ein `RETURN` erreicht. Details:
|
||||
[`src/engine/README.md`](src/engine/README.md)s „Schleifen"-Abschnitt.
|
||||
[`app/frontend/engine/README.md`](app/frontend/engine/README.md)s „Schleifen"-Abschnitt.
|
||||
|
||||
#### SUMIF/COUNTIF/AVERAGEIF nachbauen
|
||||
|
||||
@@ -644,7 +657,7 @@ unabhängig von der Locale), **Zell-Textfarbe**,
|
||||
### Einstellungen (⚙, links neben dem Titel)
|
||||
|
||||
Ein Zahnrad neben der Überschrift öffnet eine linke Inspector-Schublade
|
||||
([`SettingsPanel.vue`](src/components/SettingsPanel.vue)), die die Fläche zur
|
||||
([`SettingsPanel.vue`](app/frontend/components/SettingsPanel.vue)), die die Fläche zur
|
||||
Seite schiebt (auf schmalen Viewports überlagert sie). Sie bündelt alle
|
||||
wichtigen Punkte:
|
||||
|
||||
@@ -1034,7 +1047,7 @@ Fehlercode nötig).
|
||||
|
||||
## Architektur der Engine-Module
|
||||
|
||||
Framework-agnostisches ES-Modul unter [`src/engine/`](src/engine/) — **keine**
|
||||
Framework-agnostisches ES-Modul unter [`app/frontend/engine/`](app/frontend/engine/) — **keine**
|
||||
Formel-/Spreadsheet-Library. Kennt kein Vue, kein DOM. Die adressierbare Einheit
|
||||
ist eine **Tabelle**; der Abhängigkeitsgraph läuft dokumentweit über alle
|
||||
Tabellen aller Blätter.
|
||||
@@ -1054,7 +1067,7 @@ Tabellen aller Blätter.
|
||||
`graph.topoOrder()` → Zyklus-Zellen `#CIRC`, Rest topologisch auswerten, nicht
|
||||
betroffene Zellen behalten ihren Wert.
|
||||
|
||||
**Anbindung an Vue.** [`src/store/document.ts`](src/store/document.ts) ist das
|
||||
**Anbindung an Vue.** [`app/frontend/store/document.ts`](app/frontend/store/document.ts) ist das
|
||||
„eigene `reactive()`-Modul": es hält eine `Workbook`-Instanz und einen
|
||||
`recalcTick`-Ref. Die UI liest `store.cellValue(...)`, das `recalcTick` berührt —
|
||||
nach jeder Neuberechnung wird der Tick erhöht, jede Formelzelle re-rendert also
|
||||
@@ -1123,7 +1136,7 @@ Blatt **„Analyse"** mit drei Tabellen-Objekten (**Umsatz**, **Kosten**,
|
||||
**Übersicht**) und einem **Diagramm** (Umsatz je Region), plus ein leeres Blatt
|
||||
**„Blatt 2"**. „Übersicht" demonstriert tabellenübergreifende Bezüge und die vier
|
||||
Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
|
||||
`E2 = =D2/B2*100`. Siehe [`src/data/seed.ts`](src/data/seed.ts).
|
||||
`E2 = =D2/B2*100`. Siehe [`app/frontend/data/seed.ts`](app/frontend/data/seed.ts).
|
||||
|
||||
> **Hinweis zur Aufgabenstellung:** Die Akzeptanzliste nennt „Übersicht!B2 =
|
||||
> 6070". Die explizit angegebene Nord-Zeile ergibt jedoch
|
||||
@@ -1498,7 +1511,7 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
src/
|
||||
app/frontend/
|
||||
engine/ # Formel-Engine (Eigenbau) + Unit-Tests
|
||||
store/ # reaktiver Dokument-Store (v2 Canvas-Modell), Formatierung
|
||||
io/ # csv / json (Eigenbau, inkl. v1→v2), num (ZIP via fflate, lazy), xlsx (SheetJS, lazy)
|
||||
|
||||
Reference in New Issue
Block a user