Start with an empty document; offer Datei › Neu and Datei › Muster

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>
This commit is contained in:
wkh
2026-09-28 02:11:51 +02:00
co-authored by Claude Opus 5.5
parent 86d03346e8
commit 5a79ddee79
5 changed files with 62 additions and 11 deletions
+13 -4
View File
@@ -196,10 +196,17 @@ async function doImport(action: 'replace' | 'append' | 'confirm') {
}
}
function onReset() {
if (confirm('Vorbefülltes Dokument wiederherstellen? Alle Änderungen gehen verloren.')) {
function onNew() {
if (confirm('Neues, leeres Dokument anlegen? Alle Änderungen gehen verloren.')) {
store.newDocument();
toasts.info('Neues Dokument angelegt.');
}
}
function onSample() {
if (confirm('Musterdokument laden? Alle Änderungen gehen verloren.')) {
store.resetToSeed();
toasts.info('Dokument zurückgesetzt.');
toasts.info('Musterdokument geladen.');
}
}
</script>
@@ -291,6 +298,9 @@ function onReset() {
<div class="menu-wrap">
<button class="btn" @click="fileMenuOpen = !fileMenuOpen">Datei ▾</button>
<ul v-if="fileMenuOpen" class="menu" @click="fileMenuOpen = false">
<li><button @click="onNew">Neu</button></li>
<li><button @click="onSample">Muster · Umsatzanalyse</button></li>
<li class="sep" />
<li><button @click="pickFile">Importieren … (.json .csv .xlsx)</button></li>
<li class="sep" />
<li><button @click="runJsonExport">Export · Dokument (.json)</button></li>
@@ -310,7 +320,6 @@ function onReset() {
>
<ToolbarIcon :name="themeIcon" />
</button>
<button class="btn" @click="onReset">Zurücksetzen</button>
<input ref="fileInput" type="file" accept=".json,.csv,.xlsx" hidden @change="onFileChosen" />
<span v-if="busy" class="busy">Verarbeite …</span>
+21
View File
@@ -1,4 +1,7 @@
/**
* Document templates: `createBlankDocument()` (Datei › Neu, and what a first
* start opens) and `createSeedDocument()` (Datei › Muster).
*
* The pre-filled document ("roter Faden" of the study), in the v2 canvas model:
* one sheet ("Analyse") carrying three table objects and a chart object, plus a
* second empty sheet to try things out.
@@ -144,3 +147,21 @@ export function createSeedDocument(): DocumentState {
macros: [],
};
}
/** An empty document: one sheet with one empty table, ready to type into. */
export function createBlankDocument(): DocumentState {
const table = makeTable('Tabelle 1', 40, 40, 12, 6, []);
const sheet = { id: nextId('sheet'), name: 'Blatt 1', objects: [table] };
return {
version: 2,
sheets: [sheet],
activeSheetId: sheet.id,
activeObjectId: table.id,
selection: { tableId: table.id, anchor: { col: 0, row: 0 }, focus: { col: 0, row: 0 } },
locale: 'de-DE',
csvDelimiter: ',',
customFunctions: [],
namedRanges: [],
macros: [],
};
}
+13
View File
@@ -120,6 +120,19 @@ describe('seed document acceptance (canvas model)', () => {
expect(val(s, 'Übersicht', 'B2')).toBe(5970);
});
it('newDocument replaces everything with one empty sheet + table and a fresh history', () => {
const s = fresh();
setCell(s, 'Umsatz', 'B2', '99999');
s.newDocument();
expect(s.doc.sheets).toHaveLength(1);
expect(s.doc.sheets[0].name).toBe('Blatt 1');
const [table] = s.doc.sheets[0].objects;
expect(table.kind === 'table' && table.name).toBe('Tabelle 1');
expect(table.kind === 'table' && Object.keys(table.cells)).toEqual([]);
expect(s.doc.namedRanges).toEqual([]);
expect(s.canUndo.value).toBe(false);
});
it('inserting a new table and referencing it across sheets works', () => {
const s = fresh();
const sheet2 = s.doc.sheets[1].id;
+8 -2
View File
@@ -77,7 +77,7 @@ import type {
TextObject,
} from './types';
import { defaultTableView, DEFAULT_ROW_H, MIN_GRID_SIZE, MAX_GRID_SIZE } from './types';
import { createSeedDocument } from '../data/seed';
import { createBlankDocument, createSeedDocument } from '../data/seed';
import { validateDocument } from '../io/json';
const STORAGE_KEY = 'numolus:doc:v2';
@@ -535,7 +535,7 @@ function loadInitial(): DocumentState {
} catch {
/* missing / corrupt / stale-schema -> clean fallback */
}
return createSeedDocument();
return createBlankDocument();
}
// ---- store singleton --------------------------------------------------
@@ -2848,6 +2848,11 @@ function createStore() {
applyDocumentState(next);
resetHistory();
}
/** Datei › Neu: an empty document. */
function newDocument() {
replaceDocument(createBlankDocument());
}
/** Datei › Muster: the pre-filled sample document. */
function resetToSeed() {
replaceDocument(createSeedDocument());
}
@@ -3040,6 +3045,7 @@ function createStore() {
pointDrag,
// document
replaceDocument,
newDocument,
resetToSeed,
appendSheets,
addTableFromCells,