mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Format entire codebase with Prettier (mechanical, no behavior change)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 00 · Product Overview
|
||||
|
||||
This document set is a UX/behavioral specification for **Astrolabe**, a browser-based snippet manager for [Vega-Lite](https://vega.github.io/vega-lite/) visualizations. It describes *what the app does* from the user's perspective — its capabilities, workflows, and structural layout — so the app can be recreated on any web/HTML/TS stack. It deliberately avoids prescribing *how* anything is built: no frameworks, libraries, storage technologies, code structure, or concrete visual styling are mandated. Implementers are free to choose those.
|
||||
This document set is a UX/behavioral specification for **Astrolabe**, a browser-based snippet manager for [Vega-Lite](https://vega.github.io/vega-lite/) visualizations. It describes _what the app does_ from the user's perspective — its capabilities, workflows, and structural layout — so the app can be recreated on any web/HTML/TS stack. It deliberately avoids prescribing _how_ anything is built: no frameworks, libraries, storage technologies, code structure, or concrete visual styling are mandated. Implementers are free to choose those.
|
||||
|
||||
## What Astrolabe Is
|
||||
|
||||
@@ -8,7 +8,7 @@ Astrolabe is a local-first tool for authoring, organizing, and previewing Vega-L
|
||||
|
||||
## Who It Is For
|
||||
|
||||
People who work with Vega-Lite directly and want a fast, private workspace to draft, iterate on, and keep many visualizations: data practitioners, analysts, educators, and chart authors. Familiarity with Vega-Lite's JSON spec format is assumed; the app does not abstract Vega-Lite away (though the *Chart Builder* offers a no-JSON starting point).
|
||||
People who work with Vega-Lite directly and want a fast, private workspace to draft, iterate on, and keep many visualizations: data practitioners, analysts, educators, and chart authors. Familiarity with Vega-Lite's JSON spec format is assumed; the app does not abstract Vega-Lite away (though the _Chart Builder_ offers a no-JSON starting point).
|
||||
|
||||
## Core Value
|
||||
|
||||
@@ -22,39 +22,39 @@ People who work with Vega-Lite directly and want a fast, private workspace to dr
|
||||
|
||||
- **Local-first** — all data is stored in the browser and survives reload; the app works fully offline and is installable as a standalone app.
|
||||
- **Single-screen workspace** — a three-pane layout (library · editor · preview) plus modals for cross-cutting tools (datasets, chart builder, settings, help).
|
||||
- **Vega-Lite native** — snippets *are* Vega-Lite specs; the app validates, renders, and reasons about them as such.
|
||||
- **Vega-Lite native** — snippets _are_ Vega-Lite specs; the app validates, renders, and reasons about them as such.
|
||||
- **Keyboard-friendly and shareable** — common actions have shortcuts, and the current location is reflected in a shareable URL.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- No user accounts, authentication, or cross-device sync (use *Import & Export* to move data).
|
||||
- No user accounts, authentication, or cross-device sync (use _Import & Export_ to move data).
|
||||
- No server-side storage, rendering, or processing.
|
||||
- No collaboration or multi-user features.
|
||||
- No general BI/dashboarding — a snippet is a single Vega-Lite visualization, not a composed report.
|
||||
|
||||
## Key Concepts (Glossary)
|
||||
|
||||
- **Snippet** — a saved Vega-Lite specification plus metadata (name, comment, timestamps, tags, dataset references). The primary user-authored entity. See *Snippet Library* and *Data Model & Persistence*.
|
||||
- **Snippet** — a saved Vega-Lite specification plus metadata (name, comment, timestamps, tags, dataset references). The primary user-authored entity. See _Snippet Library_ and _Data Model & Persistence_.
|
||||
- **Spec** — the Vega-Lite JSON specification that defines one visualization.
|
||||
- **Draft vs Published** — each snippet holds a stable **published** spec and an editable **draft**; edits affect only the draft until the user publishes. See *Spec Editor & Draft/Published Workflow*.
|
||||
- **Dataset** — a named, reusable data source (JSON/CSV/TSV/TopoJSON; inline or URL) that snippets reference by name. See *Datasets*.
|
||||
- **Draft vs Published** — each snippet holds a stable **published** spec and an editable **draft**; edits affect only the draft until the user publishes. See _Spec Editor & Draft/Published Workflow_.
|
||||
- **Dataset** — a named, reusable data source (JSON/CSV/TSV/TopoJSON; inline or URL) that snippets reference by name. See _Datasets_.
|
||||
- **Dataset reference** — a Vega-Lite named-data reference, e.g. `{ "data": { "name": "MyDataset" } }`, linking a spec to a stored dataset.
|
||||
- **Live preview** — the rendered chart, updated automatically as the spec changes. See *Live Preview*.
|
||||
- **Live preview** — the rendered chart, updated automatically as the spec changes. See _Live Preview_.
|
||||
|
||||
## How This Specification Is Organized
|
||||
|
||||
| # | Section | Covers |
|
||||
|---|---------|--------|
|
||||
| 00 | Product Overview | This document — purpose, scope, glossary. |
|
||||
| 01 | Application Shell & Navigation | Layout, panes, header, modals, keyboard shortcuts, URL state, toasts, offline/installable. |
|
||||
| 02 | Snippet Library | Browsing, search, sort, metadata, create/duplicate/delete, storage monitor. |
|
||||
| 03 | Spec Editor & Draft/Published Workflow | Editing, auto-save, auto-render, draft/publish/revert, extract-to-dataset. |
|
||||
| 04 | Live Preview | Rendering, reference resolution, fit modes, error display. |
|
||||
| 05 | Datasets | Dataset manager, formats, sources, profiling, references, linking. |
|
||||
| 06 | Chart Builder | Visual no-JSON chart composition from a dataset. |
|
||||
| 07 | Settings | Appearance, editor, performance, and formatting preferences. |
|
||||
| 08 | Import & Export | Backup/transfer file format, import normalization and merging. |
|
||||
| 09 | Data Model & Persistence | Entity field definitions, storage tiers, relationships. |
|
||||
| 10 | Non-Functional Requirements | Platform, performance, accessibility, reliability, privacy. |
|
||||
| # | Section | Covers |
|
||||
| --- | -------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| 00 | Product Overview | This document — purpose, scope, glossary. |
|
||||
| 01 | Application Shell & Navigation | Layout, panes, header, modals, keyboard shortcuts, URL state, toasts, offline/installable. |
|
||||
| 02 | Snippet Library | Browsing, search, sort, metadata, create/duplicate/delete, storage monitor. |
|
||||
| 03 | Spec Editor & Draft/Published Workflow | Editing, auto-save, auto-render, draft/publish/revert, extract-to-dataset. |
|
||||
| 04 | Live Preview | Rendering, reference resolution, fit modes, error display. |
|
||||
| 05 | Datasets | Dataset manager, formats, sources, profiling, references, linking. |
|
||||
| 06 | Chart Builder | Visual no-JSON chart composition from a dataset. |
|
||||
| 07 | Settings | Appearance, editor, performance, and formatting preferences. |
|
||||
| 08 | Import & Export | Backup/transfer file format, import normalization and merging. |
|
||||
| 09 | Data Model & Persistence | Entity field definitions, storage tiers, relationships. |
|
||||
| 10 | Non-Functional Requirements | Platform, performance, accessibility, reliability, privacy. |
|
||||
|
||||
Read 00 first for orientation, then any section independently. Sections cross-reference one another by title where behavior spans more than one area.
|
||||
|
||||
@@ -6,9 +6,9 @@ This section describes the overall workspace structure, the header toolbar, the
|
||||
|
||||
Astrolabe is a single-screen workspace. Below a fixed top header sits a three-pane working area, each pane dedicated to one part of the snippet-editing workflow:
|
||||
|
||||
- **Snippet library** (left) — browse, search, select, and manage saved snippets (see *Snippet Library*).
|
||||
- **Spec editor** (center) — edit the Vega-Lite spec of the selected snippet (see *Spec Editor & Draft/Published Workflow*).
|
||||
- **Live preview** (right) — render the current spec (see *Live Preview*).
|
||||
- **Snippet library** (left) — browse, search, select, and manage saved snippets (see _Snippet Library_).
|
||||
- **Spec editor** (center) — edit the Vega-Lite spec of the selected snippet (see _Spec Editor & Draft/Published Workflow_).
|
||||
- **Live preview** (right) — render the current spec (see _Live Preview_).
|
||||
|
||||
Behavior:
|
||||
|
||||
@@ -27,19 +27,19 @@ A fixed header spans the top of the app.
|
||||
- **Left side**: the app icon, the app title ("Astrolabe"), and a version badge showing the current app version.
|
||||
- **Right side**: a row of text entry points. Each opens a destination:
|
||||
|
||||
| Entry point | Opens |
|
||||
|---|---|
|
||||
| Import | A file-picker dialog to choose a previously exported file; the chosen file is imported (see *Import & Export*). |
|
||||
| Export | Immediately produces a downloaded file containing all snippets and datasets (see *Import & Export*). |
|
||||
| Datasets | The Datasets manager modal (see *Datasets*). |
|
||||
| Settings | The Settings modal (see *Settings*). |
|
||||
| About & Privacy | The About & Help modal (keyboard shortcuts, about, and privacy information). |
|
||||
| Donate | The Donate modal. |
|
||||
| Entry point | Opens |
|
||||
| --------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| Import | A file-picker dialog to choose a previously exported file; the chosen file is imported (see _Import & Export_). |
|
||||
| Export | Immediately produces a downloaded file containing all snippets and datasets (see _Import & Export_). |
|
||||
| Datasets | The Datasets manager modal (see _Datasets_). |
|
||||
| Settings | The Settings modal (see _Settings_). |
|
||||
| About & Privacy | The About & Help modal (keyboard shortcuts, about, and privacy information). |
|
||||
| Donate | The Donate modal. |
|
||||
|
||||
Notes:
|
||||
|
||||
- Import and Export act directly (file dialog / file download); they do not open in-app modals.
|
||||
- The Datasets, Settings, About & Privacy, and Donate entry points each open a modal (see *Modal System*).
|
||||
- The Datasets, Settings, About & Privacy, and Donate entry points each open a modal (see _Modal System_).
|
||||
|
||||
## C. Modal System
|
||||
|
||||
@@ -48,20 +48,20 @@ The app shows at most one modal at a time. The modal set is: Datasets, Settings,
|
||||
- Opening any modal closes whichever modal was previously open; the two never overlap.
|
||||
- Every modal can be dismissed by: clicking its close button, pressing **Escape**, or clicking the backdrop outside the modal body.
|
||||
- Clicking inside the modal body does not dismiss it.
|
||||
- The Chart Builder and Extract-to-Dataset modals are opened from within the Datasets / snippet workflows (see *Chart Builder* and *Datasets*), not from the header.
|
||||
- The Chart Builder and Extract-to-Dataset modals are opened from within the Datasets / snippet workflows (see _Chart Builder_ and _Datasets_), not from the header.
|
||||
- Dismissing a modal returns the user to the underlying workspace unchanged.
|
||||
|
||||
## D. Keyboard Shortcuts
|
||||
|
||||
Shortcuts are platform-aware: the modifier is **Cmd** on Mac and **Ctrl** on other platforms (shown below as Cmd/Ctrl).
|
||||
|
||||
| Shortcut | Action |
|
||||
|---|---|
|
||||
| Cmd/Ctrl + Shift + N | Create a new snippet (see *Snippet Library*) |
|
||||
| Cmd/Ctrl + K | Toggle the Datasets manager open/closed |
|
||||
| Cmd/Ctrl + S | Publish the current snippet's draft (see *Spec Editor & Draft/Published Workflow*) |
|
||||
| Cmd/Ctrl + , | Open the Settings modal |
|
||||
| Escape | Close the active modal |
|
||||
| Shortcut | Action |
|
||||
| -------------------- | ---------------------------------------------------------------------------------- |
|
||||
| Cmd/Ctrl + Shift + N | Create a new snippet (see _Snippet Library_) |
|
||||
| Cmd/Ctrl + K | Toggle the Datasets manager open/closed |
|
||||
| Cmd/Ctrl + S | Publish the current snippet's draft (see _Spec Editor & Draft/Published Workflow_) |
|
||||
| Cmd/Ctrl + , | Open the Settings modal |
|
||||
| Escape | Close the active modal |
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -75,12 +75,12 @@ The app reflects its current location in the URL hash so that reloading restores
|
||||
|
||||
States and their hash forms:
|
||||
|
||||
| State | Hash |
|
||||
|---|---|
|
||||
| A selected snippet | `#snippet-<id>` |
|
||||
| Datasets manager (list) | `#datasets` |
|
||||
| A specific dataset | `#datasets/dataset-<id>` |
|
||||
| New-dataset form | `#datasets/new` |
|
||||
| State | Hash |
|
||||
| --------------------------- | ------------------------------ |
|
||||
| A selected snippet | `#snippet-<id>` |
|
||||
| Datasets manager (list) | `#datasets` |
|
||||
| A specific dataset | `#datasets/dataset-<id>` |
|
||||
| New-dataset form | `#datasets/new` |
|
||||
| Chart Builder for a dataset | `#datasets/dataset-<id>/build` |
|
||||
|
||||
Behavior:
|
||||
@@ -111,5 +111,5 @@ Events that raise toasts include:
|
||||
Astrolabe is local-first and usable without a network connection.
|
||||
|
||||
- After the first successful load, the app works fully offline; the interface and previously loaded content remain available with no connection.
|
||||
- All snippets, datasets, and settings are stored locally and remain accessible offline (see *Data Model*).
|
||||
- All snippets, datasets, and settings are stored locally and remain accessible offline (see _Data Model_).
|
||||
- The app is installable as a standalone application from a supporting browser and, once installed, launches in its own window.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# 02 · Snippet Library
|
||||
|
||||
The Snippet Library is the left pane and the primary entry point to the app. A **snippet** is a saved Vega-Lite specification together with metadata (name, comment, timestamps, tags, references to external datasets). The library lets the user browse, search, sort, select, and manage their snippets. Editing the specification, the draft-vs-published workflow, the live preview, and dataset management are covered elsewhere (see *Spec Editor & Draft/Published Workflow*, *Live Preview*, *Datasets*); this section covers only the library and management surface.
|
||||
The Snippet Library is the left pane and the primary entry point to the app. A **snippet** is a saved Vega-Lite specification together with metadata (name, comment, timestamps, tags, references to external datasets). The library lets the user browse, search, sort, select, and manage their snippets. Editing the specification, the draft-vs-published workflow, the live preview, and dataset management are covered elsewhere (see _Spec Editor & Draft/Published Workflow_, _Live Preview_, _Datasets_); this section covers only the library and management surface.
|
||||
|
||||
## The List
|
||||
|
||||
The list shows every saved snippet and is always visible. A persistent "Create New Snippet" affordance sits at the top of the list, above all snippets, so the user can always start a new snippet regardless of scroll position.
|
||||
|
||||
- The list shows all snippets, ordered newest-modified first by default (see *Sort*).
|
||||
- A "Create New Snippet" item is pinned at the top of the list; activating it creates and selects a new snippet (see *Snippet Operations*).
|
||||
- Selecting a snippet makes it the **active snippet**: it loads into the editor and preview, becomes highlighted in the list, and the URL updates to reflect the selected snippet so the state is shareable and survives a page reload (see *Application Shell & Navigation*).
|
||||
- The list shows all snippets, ordered newest-modified first by default (see _Sort_).
|
||||
- A "Create New Snippet" item is pinned at the top of the list; activating it creates and selects a new snippet (see _Snippet Operations_).
|
||||
- Selecting a snippet makes it the **active snippet**: it loads into the editor and preview, becomes highlighted in the list, and the URL updates to reflect the selected snippet so the state is shareable and survives a page reload (see _Application Shell & Navigation_).
|
||||
- Exactly one snippet is active at a time.
|
||||
- When no snippets match the current search, the list shows an empty-state message ("No snippets match your search"); when there are genuinely no snippets, it shows "No snippets found".
|
||||
- On first run, when no snippets exist, the app seeds one sample bar-chart snippet so the user starts with a working example.
|
||||
@@ -18,10 +18,10 @@ The list shows every saved snippet and is always visible. A persistent "Create N
|
||||
Each list item is a compact row summarizing one snippet, designed for fast scanning.
|
||||
|
||||
- Shows the snippet **name**.
|
||||
- Shows a **last-modified date**, rendered relatively for recent items ("Today", "Yesterday", "Nd ago" within the past week) and as a full date beyond that, formatted per the user's date-format setting (see *Settings*). When sorting by Created, the item shows the created date instead of the modified date.
|
||||
- Shows a **last-modified date**, rendered relatively for recent items ("Today", "Yesterday", "Nd ago" within the past week) and as a full date beyond that, formatted per the user's date-format setting (see _Settings_). When sorting by Created, the item shows the created date instead of the modified date.
|
||||
- Shows the snippet **size** (in KB), but only once the snippet reaches at least about 1 KB; smaller snippets omit the size to reduce clutter.
|
||||
- Shows a **status indicator** distinguishing a snippet that has unpublished draft changes from one that is fully published (the indicator communicates "draft" vs "published"). The publish and revert actions themselves live in *Spec Editor & Draft/Published Workflow*.
|
||||
- Shows a small **dataset icon** when the snippet references one or more external datasets (see *Datasets*); the icon is omitted otherwise.
|
||||
- Shows a **status indicator** distinguishing a snippet that has unpublished draft changes from one that is fully published (the indicator communicates "draft" vs "published"). The publish and revert actions themselves live in _Spec Editor & Draft/Published Workflow_.
|
||||
- Shows a small **dataset icon** when the snippet references one or more external datasets (see _Datasets_); the icon is omitted otherwise.
|
||||
- The active snippet is visually highlighted.
|
||||
|
||||
## Search
|
||||
@@ -42,7 +42,7 @@ The user chooses how the list is ordered. The choice persists across sessions so
|
||||
- Selecting the already-active sort field flips the direction; selecting a different field switches to it and resets to descending.
|
||||
- Default ordering is **Modified, descending** (newest changes first).
|
||||
- Name sorts alphabetically; Size sorts by stored snippet size; Created and Modified sort chronologically.
|
||||
- The **Modified** time advances on every save — including silent draft auto-saves (see *Spec Editor & Draft/Published Workflow*) and inline name/comment edits — so under the default Modified-descending sort the active snippet continually rises to the top while it is being edited.
|
||||
- The **Modified** time advances on every save — including silent draft auto-saves (see _Spec Editor & Draft/Published Workflow_) and inline name/comment edits — so under the default Modified-descending sort the active snippet continually rises to the top while it is being edited.
|
||||
- The selected sort field and direction persist across sessions.
|
||||
|
||||
## Selected-Snippet Metadata Panel
|
||||
@@ -51,15 +51,15 @@ When a snippet is active, a metadata panel (within the left pane) exposes its ed
|
||||
|
||||
- Shows and lets the user edit the **Name** inline; edits save automatically.
|
||||
- Shows and lets the user edit a multiline **Comment** (free-form notes); edits save automatically.
|
||||
- Shows read-only **Created** and **Modified** timestamps, formatted per the user's date-format setting (see *Settings*).
|
||||
- When the snippet references external datasets, shows a **Linked Datasets** list of the referenced dataset names, each with a dataset icon (see *Datasets*). The list is omitted when there are no references.
|
||||
- The panel also exposes the Duplicate and Delete operations for the active snippet (see *Snippet Operations*).
|
||||
- Shows read-only **Created** and **Modified** timestamps, formatted per the user's date-format setting (see _Settings_).
|
||||
- When the snippet references external datasets, shows a **Linked Datasets** list of the referenced dataset names, each with a dataset icon (see _Datasets_). The list is omitted when there are no references.
|
||||
- The panel also exposes the Duplicate and Delete operations for the active snippet (see _Snippet Operations_).
|
||||
|
||||
## Snippet Operations
|
||||
|
||||
The library provides the lifecycle operations for snippets. Each operation gives clear feedback via a toast notification (see *Application Shell & Navigation*).
|
||||
The library provides the lifecycle operations for snippets. Each operation gives clear feedback via a toast notification (see _Application Shell & Navigation_).
|
||||
|
||||
- **Create New**: starts a new snippet from a small sample Vega-Lite bar-chart template (a few inline category/value rows), assigns it an auto-generated default name (see *Naming & Tags*), saves it, and makes it the active snippet.
|
||||
- **Create New**: starts a new snippet from a small sample Vega-Lite bar-chart template (a few inline category/value rows), assigns it an auto-generated default name (see _Naming & Tags_), saves it, and makes it the active snippet.
|
||||
- **Duplicate**: creates an independent copy of the active snippet with a name suffixed "(copy)". The copy carries over the specification, comment, tags, and dataset references, gets fresh created/modified timestamps and a new identity, and becomes the active snippet. A success toast confirms the duplication.
|
||||
- **Delete**: permanently removes the active snippet after the user confirms a warning that the action cannot be undone. After deletion no snippet is active. A toast confirms the deletion.
|
||||
- These operations never affect other snippets.
|
||||
@@ -69,12 +69,12 @@ The library provides the lifecycle operations for snippets. Each operation gives
|
||||
New snippets get a sensible default name, and a tag field exists on each snippet for categorization, though tags are not a primary user surface.
|
||||
|
||||
- A new snippet receives an auto-generated default name based on the current date and time, so it is uniquely identifiable until the user renames it (renaming happens in the metadata panel).
|
||||
- Each snippet stores a list of **tags**. Tags are persisted and carried through duplication; for example, snippets brought in via import are tagged "imported" (see *Import & Export*).
|
||||
- Each snippet stores a list of **tags**. Tags are persisted and carried through duplication; for example, snippets brought in via import are tagged "imported" (see _Import & Export_).
|
||||
- There is no dedicated tag-management UI; tags are stored on the data model but are not surfaced as a primary browsing or editing control.
|
||||
|
||||
## Storage Monitor
|
||||
|
||||
A small indicator at the bottom of the library shows how much of the snippet storage budget is in use, warning the user before they run out of room. This concerns snippet storage specifically; datasets are stored separately with far greater capacity (see *Datasets* / *Data Model*).
|
||||
A small indicator at the bottom of the library shows how much of the snippet storage budget is in use, warning the user before they run out of room. This concerns snippet storage specifically; datasets are stored separately with far greater capacity (see _Datasets_ / _Data Model_).
|
||||
|
||||
- Displays current usage against the total budget (used vs. total), where the practical snippet budget is about 5 MB.
|
||||
- A fill indicator reflects the percentage used.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 03 · Spec Editor & Draft/Published Workflow
|
||||
|
||||
The center pane is where the user reads and edits the active snippet's Vega-Lite specification. It is a code editor for JSON paired with a Draft/Published workflow: edits are made against a working draft that auto-saves silently, drive the *Live Preview* automatically, and become the snippet's stable version only when explicitly published. The pane is empty when no snippet is selected; it loads the active snippet's spec when one is chosen in the *Snippet Library*.
|
||||
The center pane is where the user reads and edits the active snippet's Vega-Lite specification. It is a code editor for JSON paired with a Draft/Published workflow: edits are made against a working draft that auto-saves silently, drive the _Live Preview_ automatically, and become the snippet's stable version only when explicitly published. The pane is empty when no snippet is selected; it loads the active snippet's spec when one is chosen in the _Snippet Library_.
|
||||
|
||||
## A. The Spec Editor
|
||||
|
||||
@@ -10,8 +10,8 @@ The editor presents the active snippet's spec as formatted JSON with full code-e
|
||||
- As the user types, the spec is validated against the Vega-Lite schema; problems are surfaced as inline indicators at the offending locations (e.g. squiggles/markers), without blocking continued editing.
|
||||
- The editor offers schema-driven autocomplete/suggestions while typing (property names and allowed values from the Vega-Lite schema).
|
||||
- Pasting content and typing trigger automatic reformatting so the JSON stays consistently indented.
|
||||
- The editor's appearance and behavior — font size, editor theme, minimap visibility, word wrap, line numbers, and tab size — are configurable and read from *Settings*; this section does not redefine their defaults.
|
||||
- The editor always edits a single active snippet. Selecting a different snippet in the *Snippet Library*, or toggling the Draft/Published view, replaces the editor content with the corresponding spec.
|
||||
- The editor's appearance and behavior — font size, editor theme, minimap visibility, word wrap, line numbers, and tab size — are configurable and read from _Settings_; this section does not redefine their defaults.
|
||||
- The editor always edits a single active snippet. Selecting a different snippet in the _Snippet Library_, or toggling the Draft/Published view, replaces the editor content with the corresponding spec.
|
||||
|
||||
## B. Auto-Save of the Draft
|
||||
|
||||
@@ -19,17 +19,17 @@ Edits persist automatically so the user never loses work and never needs an expl
|
||||
|
||||
- A short moment after the user stops typing, the current editor content is parsed and stored as the snippet's working draft, silently and with no notification.
|
||||
- Auto-save only commits when the editor content is valid JSON; if the content is momentarily unparseable, the save is skipped and retried after the next pause in typing, so a half-typed spec never overwrites the stored draft.
|
||||
- Auto-save writes the **draft** only. It never alters the published version (see *D. Draft vs Published*).
|
||||
- Auto-save writes the **draft** only. It never alters the published version (see _D. Draft vs Published_).
|
||||
- Auto-save is distinct from Publish: auto-save preserves in-progress work; Publish promotes that work to stable.
|
||||
|
||||
## C. Auto-Render to Preview
|
||||
|
||||
Edits flow to the *Live Preview* automatically, so the user sees results without invoking a render.
|
||||
Edits flow to the _Live Preview_ automatically, so the user sees results without invoking a render.
|
||||
|
||||
- A brief moment after the user stops typing, the current spec is sent to the *Live Preview* for rendering.
|
||||
- The delay before rendering is a configurable debounce (see *Settings* / *Live Preview*), letting the user trade responsiveness against churn while typing heavy specs.
|
||||
- A brief moment after the user stops typing, the current spec is sent to the _Live Preview_ for rendering.
|
||||
- The delay before rendering is a configurable debounce (see _Settings_ / _Live Preview_), letting the user trade responsiveness against churn while typing heavy specs.
|
||||
- Rendering also occurs immediately when a snippet is first loaded into the editor or the Draft/Published view is switched.
|
||||
- Rendering specifics (dataset reference resolution, fit modes) belong to *Live Preview*; the editor's role is to supply the current spec text on each settle.
|
||||
- Rendering specifics (dataset reference resolution, fit modes) belong to _Live Preview_; the editor's role is to supply the current spec text on each settle.
|
||||
|
||||
## D. Draft vs Published Workflow
|
||||
|
||||
@@ -39,13 +39,13 @@ Every snippet carries two versions of its spec: a **published** (stable) version
|
||||
- **Draft view** shows the working draft and is the editable surface — all typing, auto-save, and auto-render act on the draft.
|
||||
- **Published view** shows the last published version, for reference; the published version is never modified by ordinary editing.
|
||||
- Editing the draft never touches the published version until the user publishes.
|
||||
- The *Snippet Library* status indicator reflects whether a snippet currently has unpublished draft changes (draft differs from published); this section only produces that difference, it does not render the indicator.
|
||||
- The _Snippet Library_ status indicator reflects whether a snippet currently has unpublished draft changes (draft differs from published); this section only produces that difference, it does not render the indicator.
|
||||
|
||||
### Publish
|
||||
|
||||
- A **Publish** action promotes the current draft to become the published version (the two are made identical).
|
||||
- Publish is also triggered by the keyboard shortcut Cmd/Ctrl+S.
|
||||
- On publish, the snippet's dataset references are recomputed from the now-published spec (see *Datasets* for reference linking).
|
||||
- On publish, the snippet's dataset references are recomputed from the now-published spec (see _Datasets_ for reference linking).
|
||||
- A success toast confirms the snippet was published.
|
||||
- Publish is unavailable when no snippet is active.
|
||||
|
||||
@@ -63,11 +63,11 @@ When the spec cannot be parsed or cannot be rendered, the editor pane shows the
|
||||
- When the spec is invalid JSON, or is valid JSON but fails to render as Vega-Lite (including an unresolved dataset reference), a clear, readable error message appears in the editor pane, near the editor area.
|
||||
- The error message is plainly legible (monospaced, distinct from normal content) and conveys what went wrong.
|
||||
- The editor remains fully usable while an error is shown, so the user can edit to fix it; the error clears automatically once a subsequent edit renders successfully.
|
||||
- This is the editor-side error affordance only; how a valid spec is drawn lives in *Live Preview*.
|
||||
- This is the editor-side error affordance only; how a valid spec is drawn lives in _Live Preview_.
|
||||
|
||||
## F. Extract Inline Data to a Dataset
|
||||
|
||||
When a snippet's spec embeds its data inline, the user can lift that data out into a reusable, named dataset and have the spec reference it instead. This keeps specs lean and lets the same data serve multiple snippets (see *Datasets*).
|
||||
When a snippet's spec embeds its data inline, the user can lift that data out into a reusable, named dataset and have the spec reference it instead. This keeps specs lean and lets the same data serve multiple snippets (see _Datasets_).
|
||||
|
||||
- When the active snippet's draft spec contains inline data, an **Extract to Dataset** action is available in the pane header; it is hidden when the spec has no inline data.
|
||||
- Choosing it opens a modal that shows a read-only preview of the inline data and asks the user for a dataset name (required).
|
||||
@@ -75,4 +75,4 @@ When a snippet's spec embeds its data inline, the user can lift that data out in
|
||||
- On success, the system: saves the inline data as a new dataset (preserving its detected format), rewrites the snippet's draft spec so the inline data is replaced by a reference to the dataset by name, links the dataset to the snippet, and reloads the editor to show the rewritten spec.
|
||||
- A toast confirms the dataset was created, and the modal closes.
|
||||
- The user can cancel the modal at any time, leaving the spec unchanged.
|
||||
- Dataset-side specifics (formats, storage, the bidirectional snippet↔dataset link) are described in *Datasets*.
|
||||
- Dataset-side specifics (formats, storage, the bidirectional snippet↔dataset link) are described in _Datasets_.
|
||||
|
||||
@@ -5,19 +5,19 @@ The right pane renders the active snippet's current specification as a live Vega
|
||||
## Purpose & Live Updating
|
||||
|
||||
- Renders the active snippet's current spec as a Vega-Lite visualization.
|
||||
- Always reflects the version currently shown in the editor: while the user edits the draft, the preview renders the draft; once published/viewing the published version, it renders that (see *Spec Editor & Draft/Published Workflow*).
|
||||
- Updates automatically as the user edits, after a brief render debounce so rapid keystrokes do not trigger constant re-rendering. The debounce delay is user-configurable (see *Settings*).
|
||||
- Always reflects the version currently shown in the editor: while the user edits the draft, the preview renders the draft; once published/viewing the published version, it renders that (see _Spec Editor & Draft/Published Workflow_).
|
||||
- Updates automatically as the user edits, after a brief render debounce so rapid keystrokes do not trigger constant re-rendering. The debounce delay is user-configurable (see _Settings_).
|
||||
- A subtle busy indication may appear over the preview while a render is in progress; it clears when rendering completes.
|
||||
- When no snippet is active, or the editor content is empty/blank, the preview renders nothing (a clean, empty pane) rather than showing an error.
|
||||
|
||||
## Dataset Reference Resolution
|
||||
|
||||
When a spec uses inline data, the preview renders it directly. When a spec instead references a named dataset from the library, the preview resolves that reference and renders using the stored dataset's contents (see *Datasets*).
|
||||
When a spec uses inline data, the preview renders it directly. When a spec instead references a named dataset from the library, the preview resolves that reference and renders using the stored dataset's contents (see _Datasets_).
|
||||
|
||||
- A spec may point at a dataset from the library by name instead of embedding the data inline.
|
||||
- Before rendering, the preview substitutes the referenced dataset's stored contents into the spec.
|
||||
- URL-sourced datasets are fetched as needed at render time.
|
||||
- If a referenced dataset cannot be found or fetched, the preview shows a readable error (see *Error Display*) rather than a broken chart.
|
||||
- If a referenced dataset cannot be found or fetched, the preview shows a readable error (see _Error Display_) rather than a broken chart.
|
||||
|
||||
## Fit / Sizing Modes
|
||||
|
||||
@@ -28,38 +28,38 @@ The preview pane header has a "Fit" control offering exactly four modes that det
|
||||
- **Height** — fits the chart's height to the pane; the width is left to the chart's own natural sizing.
|
||||
- **Full** — fits the chart to the pane in both dimensions, so it occupies the full available width and height.
|
||||
|
||||
The exact spec transform each mode performs is defined in *Rendering Contract* below.
|
||||
The exact spec transform each mode performs is defined in _Rendering Contract_ below.
|
||||
|
||||
Behavior of the selected mode:
|
||||
|
||||
- The control shows the four modes with the active one visibly indicated.
|
||||
- The selected mode persists across sessions, stored in *Settings* as `previewFitMode`.
|
||||
- The selected mode persists across sessions, stored in _Settings_ as `previewFitMode`.
|
||||
- The default is the natural Original mode.
|
||||
|
||||
## Rendering Contract
|
||||
|
||||
Before the chart is drawn, the spec shown in the editor is transformed into the spec actually rendered. Two deterministic transforms are applied in order. They are specified here because reproducing them faithfully is what makes references and fit modes behave correctly; the result is observable as the rendered chart.
|
||||
|
||||
**1. Dataset reference resolution.** Any named-data reference (`data` with a `name`) is replaced in-place with the referenced dataset's actual contents, shaped by the dataset's source and format (see *Datasets*):
|
||||
**1. Dataset reference resolution.** Any named-data reference (`data` with a `name`) is replaced in-place with the referenced dataset's actual contents, shaped by the dataset's source and format (see _Datasets_):
|
||||
|
||||
| Dataset source / format | The reference's `data` becomes |
|
||||
|---|---|
|
||||
| URL (any format) | a URL reference to the dataset's address, tagged with its format |
|
||||
| Inline JSON | the parsed values, inlined |
|
||||
| Inline CSV / TSV | the raw text, inlined, tagged with its format (CSV or TSV) |
|
||||
| Inline TopoJSON | the value, inlined, tagged as TopoJSON |
|
||||
| Dataset source / format | The reference's `data` becomes |
|
||||
| ----------------------- | ---------------------------------------------------------------- |
|
||||
| URL (any format) | a URL reference to the dataset's address, tagged with its format |
|
||||
| Inline JSON | the parsed values, inlined |
|
||||
| Inline CSV / TSV | the raw text, inlined, tagged with its format (CSV or TSV) |
|
||||
| Inline TopoJSON | the value, inlined, tagged as TopoJSON |
|
||||
|
||||
- Resolution recurses into nested sub-specs (layered and concatenated specs, and a parent spec's child `spec`), so references anywhere in the spec are resolved.
|
||||
- If a referenced dataset does not exist, rendering fails with a "dataset not found" error (see *Error Display*).
|
||||
- If a referenced dataset does not exist, rendering fails with a "dataset not found" error (see _Error Display_).
|
||||
|
||||
**2. Fit-mode sizing.** The selected fit mode rewrites the spec's sizing using Vega-Lite's responsive `"container"` sizing keyword, recursing into the same nested sub-specs:
|
||||
|
||||
| Mode | Transform |
|
||||
|---|---|
|
||||
| Mode | Transform |
|
||||
| -------- | ------------------------------------------------------------------------------------------ |
|
||||
| Original | spec sizing left untouched (the spec's own `width`/`height`, or Vega-Lite defaults, apply) |
|
||||
| Width | set `width` to `"container"`; remove any explicit `height` |
|
||||
| Height | set `height` to `"container"`; remove any explicit `width` |
|
||||
| Full | set both `width` and `height` to `"container"` |
|
||||
| Width | set `width` to `"container"`; remove any explicit `height` |
|
||||
| Height | set `height` to `"container"`; remove any explicit `width` |
|
||||
| Full | set both `width` and `height` to `"container"` |
|
||||
|
||||
- For the responsive (non-Original) modes the chart's container-relative dimension follows the pane size, while the unconstrained dimension is recomputed naturally — this is why Width/Height do not preserve the original aspect ratio.
|
||||
- The transform operates on a copy; the user's stored spec is never modified by rendering.
|
||||
@@ -77,5 +77,5 @@ When a spec cannot be rendered, the preview replaces the chart area with a clear
|
||||
|
||||
## Responsiveness
|
||||
|
||||
- The preview re-fits when the pane is resized, re-applying the current fit mode so the chart continues to honor the chosen sizing (see panes in *Application Shell & Navigation*).
|
||||
- The preview re-fits when the pane is resized, re-applying the current fit mode so the chart continues to honor the chosen sizing (see panes in _Application Shell & Navigation_).
|
||||
- Resizing does not require a manual refresh; the displayed chart adapts to the new pane dimensions.
|
||||
|
||||
@@ -7,13 +7,13 @@ The **Dataset Manager** is a modal for creating and managing named, reusable dat
|
||||
Datasets are named blobs of data stored in the user's local library, independent of any single snippet. A snippet references a dataset by name rather than embedding the data inline, so the same data can power many snippets and be edited in one place.
|
||||
|
||||
- Datasets persist locally across sessions in a high-capacity local store, far larger than the budget available to snippets — large datasets belong here, not inline in specs.
|
||||
- A snippet references a dataset using a Vega-Lite named-data reference, e.g. `{ "data": { "name": "MyDataset" } }`. When the *Live Preview* renders a spec, it resolves any such named reference against the dataset library (see *Live Preview*).
|
||||
- See *Data Model* for the stored shape of a dataset.
|
||||
- A snippet references a dataset using a Vega-Lite named-data reference, e.g. `{ "data": { "name": "MyDataset" } }`. When the _Live Preview_ renders a spec, it resolves any such named reference against the dataset library (see _Live Preview_).
|
||||
- See _Data Model_ for the stored shape of a dataset.
|
||||
|
||||
## Opening & Navigation
|
||||
|
||||
- Opened from a header control or via the keyboard shortcut Cmd/Ctrl+K.
|
||||
- The current view and the selected dataset are reflected in the URL, so a selected dataset produces a shareable/back-navigable location (see *Application Shell & Navigation*).
|
||||
- The current view and the selected dataset are reflected in the URL, so a selected dataset produces a shareable/back-navigable location (see _Application Shell & Navigation_).
|
||||
- Closing the modal clears the current selection and any open create form.
|
||||
|
||||
## Layout
|
||||
@@ -38,7 +38,7 @@ Clicking an item selects it and shows its detail. Per-item actions (delete, plus
|
||||
A dataset has one of two source types, chosen when creating it:
|
||||
|
||||
- **Inline** — the data itself is pasted in and stored directly in the library.
|
||||
- **URL** — the dataset stores a remote URL (http/https). The data is not copied locally; it is fetched on demand when a referencing spec is rendered (see *Live Preview*).
|
||||
- **URL** — the dataset stores a remote URL (http/https). The data is not copied locally; it is fetched on demand when a referencing spec is rendered (see _Live Preview_).
|
||||
|
||||
For inline datasets the library holds the full data and can profile it. For URL datasets the library holds only the link, so row/column/size figures are typically not computed up front and show as "N/A".
|
||||
|
||||
@@ -80,7 +80,7 @@ The detail pane for a selected dataset shows:
|
||||
- **Comment** (optional free-text notes), when present.
|
||||
- **Overview**: statistics (rows, columns, size), the **column list** with each column's name and inferred type shown with a simple type indicator, and created/modified timestamps.
|
||||
- **Preview**: a truncated rendering of the data (raw text for CSV/TSV/URL, pretty-printed for JSON/TopoJSON).
|
||||
- **Linked Snippets**: the list of snippets that reference this dataset by name. This is the dataset side of bidirectional dataset↔snippet linking (see *Snippet Library*).
|
||||
- **Linked Snippets**: the list of snippets that reference this dataset by name. This is the dataset side of bidirectional dataset↔snippet linking (see _Snippet Library_).
|
||||
|
||||
## Actions
|
||||
|
||||
@@ -94,14 +94,14 @@ Each action raises a confirming toast (or an error toast on failure).
|
||||
|
||||
## Build Chart From Dataset
|
||||
|
||||
From a selected dataset the user can launch the visual *Chart Builder* (see *Chart Builder*) pre-targeted at that dataset, producing a new snippet whose spec references the dataset by name.
|
||||
From a selected dataset the user can launch the visual _Chart Builder_ (see _Chart Builder_) pre-targeted at that dataset, producing a new snippet whose spec references the dataset by name.
|
||||
|
||||
## Extract Inline Data → Dataset
|
||||
|
||||
The reverse flow starts in the editor: a user can extract inline `data.values` out of a spec into a new named dataset (see *Spec Editor & Draft/Published Workflow*). The result appears here as a new dataset, and the originating snippet's spec is rewritten to reference it by name.
|
||||
The reverse flow starts in the editor: a user can extract inline `data.values` out of a spec into a new named dataset (see _Spec Editor & Draft/Published Workflow_). The result appears here as a new dataset, and the originating snippet's spec is rewritten to reference it by name.
|
||||
|
||||
## Naming & Uniqueness
|
||||
|
||||
- Dataset names must be **unique**. Attempting to create a dataset with a name already in use is rejected with an error toast.
|
||||
- During bulk operations such as import, conflicting names are automatically suffixed to remain unique rather than overwriting existing datasets (see *Import & Export*).
|
||||
- During bulk operations such as import, conflicting names are automatically suffixed to remain unique rather than overwriting existing datasets (see _Import & Export_).
|
||||
- Renaming a dataset that is referenced by snippets keeps references consistent by updating the matching named-data references in affected specs.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# 06 · Chart Builder
|
||||
|
||||
The Chart Builder is a visual, no-JSON way to compose a Vega-Lite chart from a selected dataset. The user picks a mark type and maps the dataset's columns to encoding channels; the builder produces a complete Vega-Lite spec and saves it as a new snippet that references the dataset. It is intended for users who want to start a chart quickly without hand-writing JSON in the *Spec Editor & Draft/Published Workflow*.
|
||||
The Chart Builder is a visual, no-JSON way to compose a Vega-Lite chart from a selected dataset. The user picks a mark type and maps the dataset's columns to encoding channels; the builder produces a complete Vega-Lite spec and saves it as a new snippet that references the dataset. It is intended for users who want to start a chart quickly without hand-writing JSON in the _Spec Editor & Draft/Published Workflow_.
|
||||
|
||||
## Opening
|
||||
|
||||
- Launched from a selected dataset in the *Datasets* manager via that dataset's "build chart" action.
|
||||
- Opens as a modal dialog over the application; the URL reflects the dataset's "build" action so the open builder is shareable/restorable (see *Application Shell & Navigation*).
|
||||
- Launched from a selected dataset in the _Datasets_ manager via that dataset's "build chart" action.
|
||||
- Opens as a modal dialog over the application; the URL reflects the dataset's "build" action so the open builder is shareable/restorable (see _Application Shell & Navigation_).
|
||||
- On open, the builder loads the selected dataset, displays its name, and pre-populates sensible defaults (see below). If no dataset is available, it shows a "No dataset loaded" message and offers no controls.
|
||||
|
||||
## Layout
|
||||
@@ -27,7 +27,7 @@ A two-pane modal:
|
||||
|
||||
- Exactly four channels are offered, in this order: **X, Y, Color, Size**.
|
||||
- For each channel the user:
|
||||
- Picks a dataset column from a dropdown of the dataset's detected columns (see *Datasets* for column detection). A "None" option leaves the channel unmapped. Each column option shows a small type indicator alongside the column name.
|
||||
- Picks a dataset column from a dropdown of the dataset's detected columns (see _Datasets_ for column detection). A "None" option leaves the channel unmapped. Each column option shows a small type indicator alongside the column name.
|
||||
- Optionally overrides the channel's **field type**, chosen from an exact set: **Quantitative, Nominal, Ordinal, Temporal**. The type override only appears once a column is selected for that channel.
|
||||
- When a column is chosen, its field type defaults from the dataset's inferred column type (numeric → Quantitative, date → Temporal, otherwise Nominal); the user may change it afterward.
|
||||
- Clearing a channel back to "None" leaves it out of the produced spec.
|
||||
@@ -39,11 +39,11 @@ A two-pane modal:
|
||||
### Dimensions (optional)
|
||||
|
||||
- Optional numeric **Width** and **Height** inputs in pixels.
|
||||
- When left empty, the chart uses default/responsive sizing (consistent with *Live Preview*); when provided, the values are written into the spec.
|
||||
- When left empty, the chart uses default/responsive sizing (consistent with _Live Preview_); when provided, the values are written into the spec.
|
||||
|
||||
## Live Preview
|
||||
|
||||
- The right pane renders the chart described by the current mark, encodings, and dimensions, resolving the dataset reference to its actual data (same rendering behavior as *Live Preview*).
|
||||
- The right pane renders the chart described by the current mark, encodings, and dimensions, resolving the dataset reference to its actual data (same rendering behavior as _Live Preview_).
|
||||
- Updates are debounced: changes to mark, encodings, or dimensions trigger a re-render after a short pause rather than on every keystroke.
|
||||
- While no encoding is mapped, the pane shows a placeholder instructing the user to configure at least one encoding.
|
||||
- If the spec fails to render, the pane shows an inline error message describing the problem instead of a chart.
|
||||
@@ -60,7 +60,7 @@ Selecting "Create Snippet" produces the final artifact:
|
||||
- Builds a complete Vega-Lite spec containing: the schema reference, a named data reference to the dataset, the chosen mark (with tooltips enabled), the mapped encodings (each with its field and field type), and any explicit width/height.
|
||||
- Channels left unmapped are omitted; if no encodings exist the spec omits the encoding block entirely (prevented by validation here).
|
||||
- Creates a new snippet from that spec with an auto-generated descriptive name, adds it to the snippet library, and records that it was built from the dataset.
|
||||
- Links the snippet to the dataset by recording the dataset reference, so the bidirectional snippet↔dataset relationship is established (see *Datasets*).
|
||||
- Links the snippet to the dataset by recording the dataset reference, so the bidirectional snippet↔dataset relationship is established (see _Datasets_).
|
||||
- Raises a success toast naming the created snippet.
|
||||
- Closes the builder; the newly created snippet becomes the active snippet in the library/editor.
|
||||
|
||||
|
||||
+23
-23
@@ -15,9 +15,9 @@ Astrolabe provides a **Settings** modal where users tune appearance, the spec ed
|
||||
|
||||
Controls the overall UI theme. Choosing the Dark theme switches the whole application chrome to a dark presentation.
|
||||
|
||||
| Setting | Options | Default |
|
||||
| -------- | ------------- | ------- |
|
||||
| UI theme | Light, Dark | Light |
|
||||
| Setting | Options | Default |
|
||||
| -------- | ----------- | ------- |
|
||||
| UI theme | Light, Dark | Light |
|
||||
|
||||
The UI theme is also exposed as a **header toggle** for one-click switching; it
|
||||
reads and writes the same persisted `ui.theme` value as this Appearance control,
|
||||
@@ -25,16 +25,16 @@ so the two always agree. (The toggle shipped in M1.5, ahead of this modal.)
|
||||
|
||||
### Editor
|
||||
|
||||
These settings configure the spec editor used to edit Vega-Lite specs (see *Spec Editor & Draft/Published Workflow*). They take effect in the editing surface for the snippet spec.
|
||||
These settings configure the spec editor used to edit Vega-Lite specs (see _Spec Editor & Draft/Published Workflow_). They take effect in the editing surface for the snippet spec.
|
||||
|
||||
| Setting | Options / Range | Default |
|
||||
| ------------- | ------------------------------------- | -------- |
|
||||
| Font size | 10–18 px (integer) | 12 px |
|
||||
| Editor theme | Auto + explicit overrides (provisional — see note) | Auto |
|
||||
| Minimap | On / Off | Off |
|
||||
| Word wrap | On / Off | On |
|
||||
| Line numbers | On / Off | On |
|
||||
| Tab size | Integer number of spaces | 2 |
|
||||
| Setting | Options / Range | Default |
|
||||
| ------------ | -------------------------------------------------- | ------- |
|
||||
| Font size | 10–18 px (integer) | 12 px |
|
||||
| Editor theme | Auto + explicit overrides (provisional — see note) | Auto |
|
||||
| Minimap | On / Off | Off |
|
||||
| Word wrap | On / Off | On |
|
||||
| Line numbers | On / Off | On |
|
||||
| Tab size | Integer number of spaces | 2 |
|
||||
|
||||
- Font size is chosen along a 10–18 range; the current value is shown alongside the control.
|
||||
- Editor theme controls the syntax/color presentation inside the editor. **Provisional (to be finalized as we implement the editor):** the default is **Auto**, which derives the editor theme from the app UI theme (light app theme → light editor theme, dark → dark), using custom Monaco themes that match the app chrome. The user may override Auto with an explicit editor theme; the exact override list (custom themes, and whether to include High Contrast or the stock Monaco themes) is deferred. Stored as `editor.theme` with an `'auto'` sentinel for the follow-the-app default.
|
||||
@@ -45,22 +45,22 @@ These settings configure the spec editor used to edit Vega-Lite specs (see *Spec
|
||||
|
||||
### Performance
|
||||
|
||||
| Setting | Range | Default |
|
||||
| --------------- | -------------------- | -------- |
|
||||
| Render debounce | 500–5000 ms | 1500 ms |
|
||||
| Setting | Range | Default |
|
||||
| --------------- | ----------- | ------- |
|
||||
| Render debounce | 500–5000 ms | 1500 ms |
|
||||
|
||||
- Render debounce is the delay after the user stops typing before the preview re-renders (see *Live Preview*).
|
||||
- Render debounce is the delay after the user stops typing before the preview re-renders (see _Live Preview_).
|
||||
- Tradeoff: a lower value makes the preview feel snappier and more immediate but re-renders more often and uses more CPU; a higher value keeps the app calmer and lighter but makes the preview feel laggier behind the spec.
|
||||
- The current value is shown alongside the control.
|
||||
|
||||
### Formatting
|
||||
|
||||
Governs how dates are rendered throughout the app, for example the timestamps shown in the *Snippet Library* list.
|
||||
Governs how dates are rendered throughout the app, for example the timestamps shown in the _Snippet Library_ list.
|
||||
|
||||
| Setting | Options | Default |
|
||||
| ------------------- | ----------------------------------------- | ------- |
|
||||
| Date format | Smart, ISO 8601, Custom | Smart |
|
||||
| Custom date format | Free-text format string | (empty) |
|
||||
| Setting | Options | Default |
|
||||
| ------------------ | ----------------------- | ------- |
|
||||
| Date format | Smart, ISO 8601, Custom | Smart |
|
||||
| Custom date format | Free-text format string | (empty) |
|
||||
|
||||
- **Smart**: relative, human-friendly rendering (e.g. "Today", "Yesterday", "3d ago", falling back to a full date for older items).
|
||||
- **ISO 8601**: a full ISO 8601 timestamp.
|
||||
@@ -71,8 +71,8 @@ Governs how dates are rendered throughout the app, for example the timestamps sh
|
||||
|
||||
The following preferences also persist locally across sessions but are managed outside this modal and are documented in their own sections:
|
||||
|
||||
- **Preview fit mode** — how the preview is sized/fit; see *Live Preview*.
|
||||
- **Snippet sort preference** — the snippet list's sort field and direction; see *Snippet Library*.
|
||||
- **Preview fit mode** — how the preview is sized/fit; see _Live Preview_.
|
||||
- **Snippet sort preference** — the snippet list's sort field and direction; see _Snippet Library_.
|
||||
|
||||
## Behaviors
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Astrolabe lets a user back up or transfer their entire workspace as a single JSO
|
||||
|
||||
## 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_) and every dataset (see _Datasets_), 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.
|
||||
@@ -21,15 +21,19 @@ The downloaded file is a single JSON object: an envelope with a format `version`
|
||||
"version": "1.0",
|
||||
"exportedAt": "2026-06-03T12:00:00.000Z",
|
||||
"exportedBy": "Astrolabe",
|
||||
"snippets": [ /* full snippet objects (see Data Model) */ ],
|
||||
"datasets": [ /* full dataset objects (see Data Model) */ ]
|
||||
"snippets": [
|
||||
/* full snippet objects (see Data Model) */
|
||||
],
|
||||
"datasets": [
|
||||
/* full dataset objects (see Data Model) */
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `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` — 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.)
|
||||
|
||||
## Import
|
||||
|
||||
@@ -47,7 +51,7 @@ The importer recognizes several shapes so that both Astrolabe exports and looser
|
||||
- **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.
|
||||
- Missing timestamps are generated at import time (creation and modification set to now, or derived from the source timestamp when present).
|
||||
- Missing identifiers, names, comments, tags, dataset references, metadata, and record `version` are filled with defaults (a missing `version` is treated as the earliest shape and migrated up on read — see *Data Model*).
|
||||
- Missing identifiers, names, comments, tags, dataset references, metadata, and record `version` are filled with defaults (a missing `version` is treated as the earliest shape and migrated up on read — see _Data Model_).
|
||||
- Such normalized imports are tagged `"imported"` so the user can find them.
|
||||
|
||||
A snippet is treated as already in current Astrolabe format when it carries an ISO-style creation timestamp; in that case its existing fields (id, name, timestamps, spec, draft spec, comment, tags, dataset references, metadata) are preserved as-is, with sensible fallbacks for any missing field.
|
||||
@@ -60,7 +64,7 @@ A snippet is treated as already in current Astrolabe format when it carries an I
|
||||
|
||||
### Dataset conflicts
|
||||
|
||||
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 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.
|
||||
@@ -68,7 +72,7 @@ When an imported dataset's name already exists in the library, it is auto-rename
|
||||
|
||||
### Storage limit handling
|
||||
|
||||
Snippet storage has an approximate 5 MB budget (see *Snippet Library* storage monitor).
|
||||
Snippet storage has an approximate 5 MB budget (see _Snippet Library_ storage monitor).
|
||||
|
||||
- If the incoming snippets would push total snippet storage over the budget, the user is warned about the overage amount, but the app still attempts to save the import.
|
||||
- If the save ultimately fails because the storage quota is exceeded, the user is told to delete some snippets and try again, and no partial snippet import is committed.
|
||||
|
||||
+59
-59
@@ -2,79 +2,79 @@
|
||||
|
||||
This section defines the persistent entities of Astrolabe and how they relate. It is the authoritative data contract: an implementer recreating the app should store equivalent records with these fields and meanings. Types are given abstractly (string, number, boolean, ISO-timestamp string, string[], object, "JSON value") so they map onto any stack. "JSON value" means any valid JSON shape — object, array, string, number, boolean, or null.
|
||||
|
||||
All data lives entirely in the browser. There is no server, account, or sync. Records survive page reload and remain available offline (see *Application Shell & Navigation*). To move data between browsers or devices, use *Import & Export*.
|
||||
All data lives entirely in the browser. There is no server, account, or sync. Records survive page reload and remain available offline (see _Application Shell & Navigation_). To move data between browsers or devices, use _Import & Export_.
|
||||
|
||||
## A. Snippet
|
||||
|
||||
A **Snippet** is a saved Vega-Lite specification together with its metadata. Snippets are the primary user-authored entity, listed and managed in the *Snippet Library*.
|
||||
A **Snippet** is a saved Vega-Lite specification together with its metadata. Snippets are the primary user-authored entity, listed and managed in the _Snippet Library_.
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|-------|------|---------|
|
||||
| `id` | string | Unique, stable identifier for the snippet. |
|
||||
| `version` | number | Schema version of this record, used for read-time migration (see *Schema versioning* below). |
|
||||
| `name` | string | Human-readable title shown in the library. |
|
||||
| `created` | ISO-timestamp string | When the snippet was first created. |
|
||||
| `modified` | ISO-timestamp string | When the snippet was last saved. |
|
||||
| `spec` | JSON value | The **published** Vega-Lite spec. May be an object or a string. This is the version rendered and shared by default. |
|
||||
| `draftSpec` | JSON value | The **working draft** Vega-Lite spec being edited. May be an object or a string. |
|
||||
| `comment` | string | Free-form user note about the snippet. |
|
||||
| `tags` | string[] | User-assigned labels for filtering and organization. |
|
||||
| `datasetRefs` | string[] | Names of *Datasets* referenced by this spec (see relationships below). |
|
||||
| `meta` | object | Free-form, extensible metadata bag for app- or feature-specific data. |
|
||||
| Field | Type | Meaning |
|
||||
| ------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | string | Unique, stable identifier for the snippet. |
|
||||
| `version` | number | Schema version of this record, used for read-time migration (see _Schema versioning_ below). |
|
||||
| `name` | string | Human-readable title shown in the library. |
|
||||
| `created` | ISO-timestamp string | When the snippet was first created. |
|
||||
| `modified` | ISO-timestamp string | When the snippet was last saved. |
|
||||
| `spec` | JSON value | The **published** Vega-Lite spec. May be an object or a string. This is the version rendered and shared by default. |
|
||||
| `draftSpec` | JSON value | The **working draft** Vega-Lite spec being edited. May be an object or a string. |
|
||||
| `comment` | string | Free-form user note about the snippet. |
|
||||
| `tags` | string[] | User-assigned labels for filtering and organization. |
|
||||
| `datasetRefs` | string[] | Names of _Datasets_ referenced by this spec (see relationships below). |
|
||||
| `meta` | object | Free-form, extensible metadata bag for app- or feature-specific data. |
|
||||
|
||||
### Dual spec / draftSpec model
|
||||
|
||||
A snippet carries two specs at once. `draftSpec` is the editable working copy; `spec` is the last published copy. Editing affects only `draftSpec` until the user publishes, at which point `draftSpec` is promoted to `spec`. This separation backs the draft/published workflow described in *Spec Editor & Draft/Published Workflow* — it lets users experiment freely while keeping a known-good published version, and drives indicators for unpublished changes.
|
||||
A snippet carries two specs at once. `draftSpec` is the editable working copy; `spec` is the last published copy. Editing affects only `draftSpec` until the user publishes, at which point `draftSpec` is promoted to `spec`. This separation backs the draft/published workflow described in _Spec Editor & Draft/Published Workflow_ — it lets users experiment freely while keeping a known-good published version, and drives indicators for unpublished changes.
|
||||
|
||||
### datasetRefs
|
||||
|
||||
`datasetRefs` records the **names** of datasets the spec depends on. It is the link used to display a snippet's linked datasets and, conversely, to find which snippets use a given dataset (see *Cross-entity relationships*). It is maintained to mirror the dataset names actually referenced in the spec.
|
||||
`datasetRefs` records the **names** of datasets the spec depends on. It is the link used to display a snippet's linked datasets and, conversely, to find which snippets use a given dataset (see _Cross-entity relationships_). It is maintained to mirror the dataset names actually referenced in the spec.
|
||||
|
||||
## B. Dataset
|
||||
|
||||
A **Dataset** is a named, reusable data source that snippets can reference by name instead of inlining data. Datasets are managed in the *Datasets* manager and support multiple formats and two source kinds.
|
||||
A **Dataset** is a named, reusable data source that snippets can reference by name instead of inlining data. Datasets are managed in the _Datasets_ manager and support multiple formats and two source kinds.
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|-------|------|---------|
|
||||
| `id` | number | Unique numeric identifier. |
|
||||
| `version` | number | Schema version of this record, used for read-time migration (see *Schema versioning* below). |
|
||||
| `name` | string | Unique, human-readable name; the key snippets reference via `datasetRefs`. |
|
||||
| `data` | JSON value | The payload. For `source = url`: the URL string. For `source = inline`: the raw CSV/TSV text, or the parsed JSON/TopoJSON value. |
|
||||
| `format` | string | One of `json`, `csv`, `tsv`, `topojson`. |
|
||||
| `source` | string | One of `inline` (data embedded in the record) or `url` (data fetched from a remote address). |
|
||||
| `comment` | string | Free-form user note about the dataset. |
|
||||
| `rowCount` | number or null | Number of data rows, or null when unknown/not applicable. |
|
||||
| `columnCount` | number or null | Number of columns, or null when unknown/not applicable. |
|
||||
| `columns` | string[] | Column names, in order. |
|
||||
| `columnTypes` | array of `{ name, type }` | Per-column inferred type. `name` is the column; `type` is one of `number`, `string`, `date`, `boolean`. |
|
||||
| `size` | number | Approximate payload size in bytes. |
|
||||
| `created` | ISO-timestamp string | When the dataset was first added. |
|
||||
| `modified` | ISO-timestamp string | When the dataset was last changed. |
|
||||
| Field | Type | Meaning |
|
||||
| ------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | number | Unique numeric identifier. |
|
||||
| `version` | number | Schema version of this record, used for read-time migration (see _Schema versioning_ below). |
|
||||
| `name` | string | Unique, human-readable name; the key snippets reference via `datasetRefs`. |
|
||||
| `data` | JSON value | The payload. For `source = url`: the URL string. For `source = inline`: the raw CSV/TSV text, or the parsed JSON/TopoJSON value. |
|
||||
| `format` | string | One of `json`, `csv`, `tsv`, `topojson`. |
|
||||
| `source` | string | One of `inline` (data embedded in the record) or `url` (data fetched from a remote address). |
|
||||
| `comment` | string | Free-form user note about the dataset. |
|
||||
| `rowCount` | number or null | Number of data rows, or null when unknown/not applicable. |
|
||||
| `columnCount` | number or null | Number of columns, or null when unknown/not applicable. |
|
||||
| `columns` | string[] | Column names, in order. |
|
||||
| `columnTypes` | array of `{ name, type }` | Per-column inferred type. `name` is the column; `type` is one of `number`, `string`, `date`, `boolean`. |
|
||||
| `size` | number | Approximate payload size in bytes. |
|
||||
| `created` | ISO-timestamp string | When the dataset was first added. |
|
||||
| `modified` | ISO-timestamp string | When the dataset was last changed. |
|
||||
|
||||
The `rowCount`, `columnCount`, `columns`, `columnTypes`, and `size` fields are derived summaries computed when data is added or updated; they support previews and type display without re-parsing the full payload.
|
||||
|
||||
### Schema versioning
|
||||
|
||||
Both **Snippet** and **Dataset** records carry a numeric `version` recording the shape of that individual record. When a record is read from storage it is migrated up to the current shape before the app uses it; new writes always store the current version. A record written before versioning existed (no `version` field) is treated as version `1`. This is distinct from the storage container's own layout version, and from the *Import & Export* envelope `version` (which describes the file format, not a record). Records exported via *Import & Export* include their `version`.
|
||||
Both **Snippet** and **Dataset** records carry a numeric `version` recording the shape of that individual record. When a record is read from storage it is migrated up to the current shape before the app uses it; new writes always store the current version. A record written before versioning existed (no `version` field) is treated as version `1`. This is distinct from the storage container's own layout version, and from the _Import & Export_ envelope `version` (which describes the file format, not a record). Records exported via _Import & Export_ include their `version`.
|
||||
|
||||
## C. UserSettings
|
||||
|
||||
**UserSettings** holds persisted user preferences as a single structured record. The semantics and UX of each option are covered in *Settings*; the shape below is the storage contract.
|
||||
**UserSettings** holds persisted user preferences as a single structured record. The semantics and UX of each option are covered in _Settings_; the shape below is the storage contract.
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|-------|------|---------|
|
||||
| `version` | number | Schema version of the settings record, used for migration. |
|
||||
| `editor.fontSize` | number | Editor font size. |
|
||||
| `editor.theme` | string | Editor color theme identifier. |
|
||||
| `editor.minimap` | boolean | Whether the editor minimap is shown. |
|
||||
| `editor.wordWrap` | string | `on` or `off`. |
|
||||
| `editor.lineNumbers` | string | `on` or `off`. |
|
||||
| `editor.tabSize` | number | Spaces per indentation level. |
|
||||
| `performance.renderDebounce` | number | Delay (ms) before re-rendering the preview after edits. |
|
||||
| `ui.theme` | string | App theme: `light` or `dark`. |
|
||||
| `ui.previewFitMode` | string | Preview sizing: `default`, `width`, `height`, or `full`. |
|
||||
| `formatting.dateFormat` | string | Date display mode: `smart`, `iso`, or `custom`. |
|
||||
| `formatting.customDateFormat` | string | Pattern used when `dateFormat = custom`. |
|
||||
| Field | Type | Meaning |
|
||||
| ----------------------------- | ------- | ---------------------------------------------------------- |
|
||||
| `version` | number | Schema version of the settings record, used for migration. |
|
||||
| `editor.fontSize` | number | Editor font size. |
|
||||
| `editor.theme` | string | Editor color theme identifier. |
|
||||
| `editor.minimap` | boolean | Whether the editor minimap is shown. |
|
||||
| `editor.wordWrap` | string | `on` or `off`. |
|
||||
| `editor.lineNumbers` | string | `on` or `off`. |
|
||||
| `editor.tabSize` | number | Spaces per indentation level. |
|
||||
| `performance.renderDebounce` | number | Delay (ms) before re-rendering the preview after edits. |
|
||||
| `ui.theme` | string | App theme: `light` or `dark`. |
|
||||
| `ui.previewFitMode` | string | Preview sizing: `default`, `width`, `height`, or `full`. |
|
||||
| `formatting.dateFormat` | string | Date display mode: `smart`, `iso`, or `custom`. |
|
||||
| `formatting.customDateFormat` | string | Pattern used when `dateFormat = custom`. |
|
||||
|
||||
A reference shape:
|
||||
|
||||
@@ -82,20 +82,20 @@ UserSettings = { version, editor: { fontSize, theme, minimap, wordWrap, lineNumb
|
||||
|
||||
## D. App / UI preferences (persisted separately)
|
||||
|
||||
Some preferences persist independently of *UserSettings* so they can update frequently without rewriting the settings record. They are stored locally and restored on load.
|
||||
Some preferences persist independently of _UserSettings_ so they can update frequently without rewriting the settings record. They are stored locally and restored on load.
|
||||
|
||||
- **Snippet sort preference** — how the *Snippet Library* list is ordered. `sortBy` is one of `name`, `modified`, `created`; `sortOrder` is `asc` or `desc`. Default is `modified` / `desc` (most recently changed first).
|
||||
- **Snippet sort preference** — how the _Snippet Library_ list is ordered. `sortBy` is one of `name`, `modified`, `created`; `sortOrder` is `asc` or `desc`. Default is `modified` / `desc` (most recently changed first).
|
||||
- **Panel layout** — the resizable three-panel arrangement: per-pane widths and per-pane visibility (which panels are shown or hidden). Restored so the workspace reopens as the user left it.
|
||||
|
||||
## E. Persistence & limits
|
||||
|
||||
| Tier | What it holds | Capacity & behavior |
|
||||
|------|---------------|---------------------|
|
||||
| Snippet store | All *Snippet* records | Local, with a practical budget of about 5 MB. A storage monitor tracks usage and surfaces warnings as the budget fills (see *Snippet Library*). |
|
||||
| Dataset store | All *Dataset* records | Local, in a separate, much higher-capacity store, suited to larger payloads. |
|
||||
| Settings & preferences | *UserSettings* plus the app/UI preferences in (D) | Local, small. |
|
||||
| Tier | What it holds | Capacity & behavior |
|
||||
| ---------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Snippet store | All _Snippet_ records | Local, with a practical budget of about 5 MB. A storage monitor tracks usage and surfaces warnings as the budget fills (see _Snippet Library_). |
|
||||
| Dataset store | All _Dataset_ records | Local, in a separate, much higher-capacity store, suited to larger payloads. |
|
||||
| Settings & preferences | _UserSettings_ plus the app/UI preferences in (D) | Local, small. |
|
||||
|
||||
Everything stays in the browser — no server or account is involved. All tiers survive reload and function offline. Because capacity is finite and per-browser, *Import & Export* is the supported path for backup and for moving data between browsers or devices.
|
||||
Everything stays in the browser — no server or account is involved. All tiers survive reload and function offline. Because capacity is finite and per-browser, _Import & Export_ is the supported path for backup and for moving data between browsers or devices.
|
||||
|
||||
## F. Cross-entity relationships
|
||||
|
||||
@@ -104,4 +104,4 @@ Snippets and datasets are linked **bidirectionally by dataset name**: `snippet.d
|
||||
- From a snippet, `datasetRefs` yields its linked datasets.
|
||||
- From a dataset, scanning snippets for its `name` in `datasetRefs` yields the snippets that reference it.
|
||||
|
||||
This name-based link is what the *Snippet Library* and *Datasets* surfaces use to show linkage in both directions. The actual resolution of a referenced dataset into spec data at render time is covered in *Live Preview*.
|
||||
This name-based link is what the _Snippet Library_ and _Datasets_ surfaces use to show linkage in both directions. The actual resolution of a referenced dataset into spec data at render time is covered in _Live Preview_.
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
# 10 · Non-Functional Requirements
|
||||
|
||||
This section defines quality attributes the rebuild must satisfy — performance, accessibility, reliability, privacy, and platform posture — independent of any single feature. Feature behavior lives in the other sections; this one constrains *how well* that behavior must work.
|
||||
This section defines quality attributes the rebuild must satisfy — performance, accessibility, reliability, privacy, and platform posture — independent of any single feature. Feature behavior lives in the other sections; this one constrains _how well_ that behavior must work.
|
||||
|
||||
## Platform & Form Factor
|
||||
|
||||
- **Target**: modern evergreen desktop browsers. The app is a single-page application that loads once and then runs locally.
|
||||
- **Desktop-first**: the primary experience is the three-pane workspace (see *Application Shell & Navigation*), designed for wide viewports. Each pane has a minimum usable width and stops shrinking below it.
|
||||
- **Desktop-first**: the primary experience is the three-pane workspace (see _Application Shell & Navigation_), designed for wide viewports. Each pane has a minimum usable width and stops shrinking below it.
|
||||
- **Small screens**: the three-pane layout is not expected to reach full parity on narrow/mobile viewports. A graceful fallback (e.g. collapsing to fewer visible panes via the toggle strip, or a single-column arrangement) is acceptable; an unusable or broken layout is not.
|
||||
- **Offline & installable**: after first load the app must function fully offline, and must be installable as a standalone application that launches in its own window (see *Application Shell & Navigation*).
|
||||
- **Offline & installable**: after first load the app must function fully offline, and must be installable as a standalone application that launches in its own window (see _Application Shell & Navigation_).
|
||||
|
||||
## Embedding & Environment Assumptions
|
||||
|
||||
Astrolabe is specified as a standalone single-page app that owns its whole viewport. A team integrating these capabilities into a larger product should know which shared environment surfaces the app currently reserves, so they can decide how to reconcile each with the host. (Surfacing the assumption is the spec's job; choosing the reconciliation is the integrator's.)
|
||||
|
||||
- **Global keyboard shortcuts** — the shortcuts in *Application Shell & Navigation* are bound document-wide and override the browser default, regardless of which element has focus or which modal is open. In a host app they may collide with the host's own bindings.
|
||||
- **URL hash as view state** — the app stores its current view (selected snippet, open dataset, chart-builder target) in the URL hash and reads it on load (see *Navigation & Shareable URL State*). A host that owns routing will need to share or namespace the hash.
|
||||
- **Local browser storage** — all state persists to local browser storage across the tiers in *Data Model & Persistence*; storage keys are not namespaced against a co-resident host app.
|
||||
- **Global keyboard shortcuts** — the shortcuts in _Application Shell & Navigation_ are bound document-wide and override the browser default, regardless of which element has focus or which modal is open. In a host app they may collide with the host's own bindings.
|
||||
- **URL hash as view state** — the app stores its current view (selected snippet, open dataset, chart-builder target) in the URL hash and reads it on load (see _Navigation & Shareable URL State_). A host that owns routing will need to share or namespace the hash.
|
||||
- **Local browser storage** — all state persists to local browser storage across the tiers in _Data Model & Persistence_; storage keys are not namespaced against a co-resident host app.
|
||||
- **Full-window workspace** — the layout assumes a wide, app-owned viewport (header, three panes, and modals). Hosting it within a smaller region falls under the small-screen fallback above.
|
||||
|
||||
## Performance & Responsiveness
|
||||
|
||||
- **Live editing stays fluid**: typing in the editor must remain smooth regardless of spec size; rendering must never block input.
|
||||
- **Debounced rendering**: preview rendering is deferred until the user pauses typing, by a user-configurable delay (see *Settings* / *Live Preview*), so rapid keystrokes do not cause continuous re-rendering.
|
||||
- **Debounced rendering**: preview rendering is deferred until the user pauses typing, by a user-configurable delay (see _Settings_ / _Live Preview_), so rapid keystrokes do not cause continuous re-rendering.
|
||||
- **Non-blocking renders**: while a render is in progress the UI stays interactive; a busy indication may overlay the preview but must not freeze editing or navigation.
|
||||
- **Auto-save is cheap and silent**: persisting the working draft must not interrupt typing or cause visible stalls (see *Spec Editor & Draft/Published Workflow*).
|
||||
- **Scales with the library**: search, sort, and list rendering must stay responsive with a large number of snippets, and large datasets must be handled by the high-capacity dataset store rather than inflating snippet storage (see *Data Model & Persistence*).
|
||||
- **Auto-save is cheap and silent**: persisting the working draft must not interrupt typing or cause visible stalls (see _Spec Editor & Draft/Published Workflow_).
|
||||
- **Scales with the library**: search, sort, and list rendering must stay responsive with a large number of snippets, and large datasets must be handled by the high-capacity dataset store rather than inflating snippet storage (see _Data Model & Persistence_).
|
||||
|
||||
## Accessibility
|
||||
|
||||
- **Keyboard operable**: all primary actions are reachable from the keyboard — the global shortcuts (see *Application Shell & Navigation*) plus standard tab/focus traversal of controls, lists, and forms.
|
||||
- **Keyboard operable**: all primary actions are reachable from the keyboard — the global shortcuts (see _Application Shell & Navigation_) plus standard tab/focus traversal of controls, lists, and forms.
|
||||
- **Modal focus management**: opening a modal moves focus into it and returns focus sensibly on close; **Escape** closes the active modal; focus is contained within an open modal.
|
||||
- **Labelled controls**: form fields, toggles, and icon-only buttons carry accessible names so assistive technology can announce them.
|
||||
- **Reduced motion**: animations and transitions (toast fades, etc.) are suppressed when the user's system requests reduced motion.
|
||||
- **Contrast**: text and interactive elements meet legible contrast in every offered UI theme; a theme that cannot meet contrast in part of the UI is not considered complete (see *Settings*).
|
||||
- **Contrast**: text and interactive elements meet legible contrast in every offered UI theme; a theme that cannot meet contrast in part of the UI is not considered complete (see _Settings_).
|
||||
|
||||
## Reliability & Data Safety
|
||||
|
||||
- **No silent data loss**: edits are auto-saved as drafts; a known-good published version is always preserved separately (see *Spec Editor & Draft/Published Workflow*).
|
||||
- **No silent data loss**: edits are auto-saved as drafts; a known-good published version is always preserved separately (see _Spec Editor & Draft/Published Workflow_).
|
||||
- **Confirm destructive actions**: deleting snippets or datasets, reverting a draft, and resetting settings require explicit confirmation.
|
||||
- **Warn before storage failure**: snippet storage usage is surfaced with escalating warnings as it fills, and the user is told when a save fails rather than losing data silently (see *Snippet Library*).
|
||||
- **Non-destructive import**: importing always merges with existing data and never overwrites or removes it; on failure the existing workspace is left unchanged (see *Import & Export*).
|
||||
- **Resilient rendering**: an invalid or unrenderable spec produces a readable error and recovers automatically when fixed; it never leaves the app in a broken state (see *Live Preview*).
|
||||
- **State survives reload**: the current selection/view is restored from the URL, and all data persists across reloads and sessions (see *Application Shell & Navigation*, *Data Model & Persistence*).
|
||||
- **Warn before storage failure**: snippet storage usage is surfaced with escalating warnings as it fills, and the user is told when a save fails rather than losing data silently (see _Snippet Library_).
|
||||
- **Non-destructive import**: importing always merges with existing data and never overwrites or removes it; on failure the existing workspace is left unchanged (see _Import & Export_).
|
||||
- **Resilient rendering**: an invalid or unrenderable spec produces a readable error and recovers automatically when fixed; it never leaves the app in a broken state (see _Live Preview_).
|
||||
- **State survives reload**: the current selection/view is restored from the URL, and all data persists across reloads and sessions (see _Application Shell & Navigation_, _Data Model & Persistence_).
|
||||
|
||||
## Privacy & Security
|
||||
|
||||
- **Local-only data**: all snippets, datasets, and settings stay in the browser. No user content is transmitted to any server, and the app requires no account or login.
|
||||
- **User-initiated network only**: the only outbound requests for user content are fetches of URL-sourced datasets or remote data referenced by a spec, which the user explicitly created (see *Datasets*). The app performs no background upload of user content.
|
||||
- **User-initiated network only**: the only outbound requests for user content are fetches of URL-sourced datasets or remote data referenced by a spec, which the user explicitly created (see _Datasets_). The app performs no background upload of user content.
|
||||
- **Client-side rendering of untrusted input**: specs and data are user-authored and rendered locally; rendering must fail safely on malformed input rather than crashing the app.
|
||||
|
||||
## Internationalization
|
||||
|
||||
- **Locale-aware formatting where it exists**: date rendering follows the user's chosen format mode (see *Settings*). Full UI translation is out of scope unless explicitly added later; the spec does not require multiple UI languages.
|
||||
- **Locale-aware formatting where it exists**: date rendering follows the user's chosen format mode (see _Settings_). Full UI translation is out of scope unless explicitly added later; the spec does not require multiple UI languages.
|
||||
|
||||
+15
-15
@@ -1,6 +1,6 @@
|
||||
# Astrolabe — Product Specification
|
||||
|
||||
A UX/behavioral specification of **Astrolabe**, a browser-based snippet manager for [Vega-Lite](https://vega.github.io/vega-lite/) visualizations. It describes *what the app does* from the user's perspective so it can be recreated on any web/HTML/TS stack.
|
||||
A UX/behavioral specification of **Astrolabe**, a browser-based snippet manager for [Vega-Lite](https://vega.github.io/vega-lite/) visualizations. It describes _what the app does_ from the user's perspective so it can be recreated on any web/HTML/TS stack.
|
||||
|
||||
## How to read this spec
|
||||
|
||||
@@ -12,20 +12,20 @@ A UX/behavioral specification of **Astrolabe**, a browser-based snippet manager
|
||||
|
||||
- **Implementation.** No frameworks, libraries, languages, storage technologies, or code architecture are prescribed. Storage is described behaviorally (e.g. "persists locally across sessions", capacity tiers), not by naming a technology.
|
||||
- **Visual design.** Structural layout (panes, regions, modal vs inline, where controls live) is specified; concrete styling, colors, and the app's visual aesthetic are left to the implementer.
|
||||
- **Domain exception.** Vega-Lite and its vocabulary (specs, marks, encoding channels, field types) and data-format names (JSON, CSV, TSV, TopoJSON) *are* named — they are the product domain, not implementation choices.
|
||||
- **Domain exception.** Vega-Lite and its vocabulary (specs, marks, encoding channels, field types) and data-format names (JSON, CSV, TSV, TopoJSON) _are_ named — they are the product domain, not implementation choices.
|
||||
|
||||
## Contents
|
||||
|
||||
| # | Section |
|
||||
|---|---------|
|
||||
| 00 | [Product Overview](00-product-overview.md) |
|
||||
| 01 | [Application Shell & Navigation](01-application-shell.md) |
|
||||
| 02 | [Snippet Library](02-snippet-library.md) |
|
||||
| 03 | [Spec Editor & Draft/Published Workflow](03-editor-and-drafts.md) |
|
||||
| 04 | [Live Preview](04-live-preview.md) |
|
||||
| 05 | [Datasets](05-datasets.md) |
|
||||
| 06 | [Chart Builder](06-chart-builder.md) |
|
||||
| 07 | [Settings](07-settings.md) |
|
||||
| 08 | [Import & Export](08-import-export.md) |
|
||||
| 09 | [Data Model & Persistence](09-data-model.md) |
|
||||
| 10 | [Non-Functional Requirements](10-non-functional.md) |
|
||||
| # | Section |
|
||||
| --- | ----------------------------------------------------------------- |
|
||||
| 00 | [Product Overview](00-product-overview.md) |
|
||||
| 01 | [Application Shell & Navigation](01-application-shell.md) |
|
||||
| 02 | [Snippet Library](02-snippet-library.md) |
|
||||
| 03 | [Spec Editor & Draft/Published Workflow](03-editor-and-drafts.md) |
|
||||
| 04 | [Live Preview](04-live-preview.md) |
|
||||
| 05 | [Datasets](05-datasets.md) |
|
||||
| 06 | [Chart Builder](06-chart-builder.md) |
|
||||
| 07 | [Settings](07-settings.md) |
|
||||
| 08 | [Import & Export](08-import-export.md) |
|
||||
| 09 | [Data Model & Persistence](09-data-model.md) |
|
||||
| 10 | [Non-Functional Requirements](10-non-functional.md) |
|
||||
|
||||
Reference in New Issue
Block a user