Workspace transfer: custom themes ride the export/import envelope

This commit is contained in:
2026-06-12 21:45:44 +03:00
parent 80d13c9b6f
commit dbb4522d78
11 changed files with 405 additions and 173 deletions
+20 -15
View File
@@ -6,17 +6,17 @@ Separately, a single chart can be exported on its own — its spec or its render
## Export
Export produces one downloadable JSON file containing every snippet (see _Snippet Library_) and every dataset (see _Datasets_), wrapped in an envelope carrying format metadata.
Export produces one downloadable JSON file containing every snippet (see _Snippet Library_), every dataset (see _Datasets_), and every custom chart theme (see _Live Preview → Chart theme_), wrapped in an envelope carrying format metadata.
- **Trigger**: the **Export** header control runs the export immediately (no intermediate dialog).
- **Contents**: all snippets and all datasets currently stored, plus envelope metadata.
- **Empty workspace**: if there are no snippets, the user is informed ("No snippets to export") and no file is downloaded — even if datasets exist.
- **Contents**: all snippets, datasets, and custom chart themes currently stored, plus envelope metadata.
- **Empty workspace**: if there are no snippets, the user is informed ("No snippets to export") and no file is downloaded — even if datasets or themes exist.
- **Filename**: `astrolabe-project-YYYY-MM-DD.json`, where the date is today's date (export day).
- **Feedback**: on success a toast reports the counts, e.g. "Exported 4 snippets and 2 datasets" (the dataset clause is omitted when there are no datasets; singular/plural wording adapts to the counts).
- **Feedback**: on success a toast reports the counts, e.g. "Exported 4 snippets, 2 datasets and 1 theme" (the dataset and theme clauses are omitted when their counts are zero; singular/plural wording adapts to the counts).
### Export envelope shape
The downloaded file is a single JSON object: an envelope with a format `version`, an export timestamp, an exporter tag, and the two data arrays.
The downloaded file is a single JSON object: an envelope with a format `version`, an export timestamp, an exporter tag, and the data arrays.
```json
{
@@ -28,6 +28,9 @@ The downloaded file is a single JSON object: an envelope with a format `version`
],
"datasets": [
/* full dataset objects (see Data Model) */
],
"themes": [
/* full custom chart theme objects (see Data Model) */
]
}
```
@@ -35,7 +38,7 @@ The downloaded file is a single JSON object: an envelope with a format `version`
- `version` — export format version (currently `"1.0"`).
- `exportedAt` — ISO 8601 timestamp of the export.
- `exportedBy` — fixed identifier `"Astrolabe"`.
- `snippets` / `datasets` — arrays of complete records as defined in _Data Model_, each including its record `version` field. (This is the per-record schema version, not the envelope `version` above.)
- `snippets` / `datasets` / `themes` — arrays of complete records as defined in _Data Model_, each including its record `version` field. (This is the per-record schema version, not the envelope `version` above.) `themes` is additive: exports always write it, and importers treat it as optional, so pre-theme envelopes remain valid `"1.0"` files.
## Per-chart export
@@ -70,8 +73,8 @@ Import lets the user pick a JSON file from their device; its contents are normal
The importer recognizes several shapes so that both Astrolabe exports and looser snippet files work:
- **Astrolabe export envelope** — an object with a `version` and a `snippets` array; an optional `datasets` array is imported too.
- **Bare array of snippets** — a top-level JSON array is treated as a list of snippets (no datasets).
- **Astrolabe export envelope** — an object with a `version` and a `snippets` array; optional `datasets` and `themes` arrays are imported too.
- **Bare array of snippets** — a top-level JSON array is treated as a list of snippets (no datasets or themes).
- **Single snippet object** — any other object is treated as one snippet.
- **Older / foreign snippet shapes** — snippets that do not match the current model are normalized onto it:
- Alternative field names are mapped: `content` → spec, `draft` → draft spec, `createdAt` → creation timestamp.
@@ -85,14 +88,16 @@ A snippet is treated as already in current Astrolabe format when it carries an I
- Imported snippets are **appended** to the existing library; nothing is overwritten or removed.
- **ID collisions** (an incoming snippet whose id already exists) are resolved by assigning the incoming snippet a fresh unique id; the original snippet keeps its id.
- Datasets are imported **before** snippets so that snippet dataset references can resolve.
- Datasets and custom themes are imported **before** snippets so that snippet dataset references can resolve.
- Imported custom themes always receive fresh ids from the theme library; an envelope's theme ids never displace existing records.
### Dataset conflicts
### Name conflicts (datasets and themes)
When an imported dataset's name already exists in the library, it is auto-renamed to a unique name rather than overwriting the existing one (see _Datasets_).
When an imported dataset's or custom theme's name already exists in the library, it is auto-renamed to a unique name rather than overwriting the existing one (see _Datasets_).
- A numeric suffix is appended to the original name; further suffixes are added until the name is unique.
- The renamed datasets are reported to the user via a warning toast listing each `original -> new` rename.
- The renamed records are reported to the user via a warning toast listing each `original -> new` rename.
- A dataset rename is propagated into the imported snippets that reference it; theme renames need no propagation (nothing references a theme by name).
- If a single dataset fails to import, it is skipped and the rest of the import continues.
### Storage limit handling
@@ -105,8 +110,8 @@ Snippet storage has an approximate 5 MB budget (see _Snippet Library_ storage mo
### Feedback
- **Success**: a toast reports how many snippets (and datasets, when any) were imported, e.g. "Imported 4 snippets and 2 datasets".
- **Renames**: when datasets were renamed, the success message is shown as a warning toast that also lists the renames.
- **Empty file**: if no snippets are found in the file, the user is informed ("No snippets found in file") and nothing is imported.
- **Success**: a toast reports how many snippets (and datasets and themes, when any) were imported, e.g. "Imported 4 snippets, 2 datasets and 1 theme".
- **Renames**: when datasets or themes were renamed, the success message is shown as a warning toast that also lists the renames.
- **Empty file**: if no snippets are found in the file, the user is informed ("No snippets found in file") and nothing is imported — even if the file carries datasets or themes.
- **Quota failure**: a clear error advising the user to delete snippets and retry.
- **Invalid file**: a non-JSON or unparseable file produces a clear error ("Failed to import. Please check that the file is valid JSON."); an unreadable file produces a read error. In all error cases the existing workspace is left unchanged.