# 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 List

The list shows every saved snippet and is always visible. A persistent **creation surface** sits at the top of the list, above all snippets, so the user can always start a new chart regardless of scroll position. It offers the two ways in with a clear hierarchy:

- **Build Chart** — the primary action; opens the **Chart Builder** (see _Chart Builder_), the guided no-JSON path. The builder opens on the most recently modified dataset (or its no-datasets state when the library has none) — it is not gated on first selecting a dataset.
- **New JSON snippet** — a ghost/tertiary action beside it; creates and selects a new snippet directly in the editor (see _Snippet Operations_ → Create New), the expert path — always visible, one click, never hidden behind the guided one.

When the library pane is dragged narrow, the buttons shed their labels (the long ghost label first) and keep their icons and accessible names.

- The list shows all snippets, ordered newest-modified first by default (see _Sort_).
- 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", with a hint to try a different term). This is the list's only empty state: a genuinely empty library never shows the list at all (see next).
- When the library is empty (first run, or after the last snippet is deleted), the workspace presents a full-width **onboarding canvas** in place of the panes — including the library list — rather than seeding placeholder content (see _First-Run & Empty Workspace_).

## First-Run & Empty Workspace

When the library is empty — on first run, or after the user deletes their last snippet — the app does **not** seed placeholder content. Instead the **onboarding canvas takes the full workspace**, replacing the pane chrome (the pane toggle strip, the library list, the editor, and the preview): with no snippets, the library's create/search/sort/storage controls and the pane toggles have nothing to act on, so the welcome gets the whole width. The user starts from a deliberate choice rather than dropped into the middle of an unfamiliar spec.

- The canvas briefly identifies what Astrolabe is, then offers the ways to begin.
- **Create your first snippet** — the primary action; starts a new snippet from the sample bar-chart template and opens it in the editor (identical to _Create New_ under _Snippet Operations_).
- **Build a chart from your data** — the data-first door beside the primary; opens the **Chart Builder** over the canvas. With no datasets yet, the builder's no-datasets state explains itself and leads to "Add a dataset" (see _Chart Builder → Opening_) — the path never dead-ends. Opening the builder also lays the workspace out at the default split below, since creating from the builder leaves the canvas directly.
- An **example gallery** of a few simple snippets showcasing distinct Vega-Lite capabilities (e.g. a bar chart, a time-series line, a scatter plot, a stacked area, a donut, a binned histogram). Each example shows a **live preview** of the chart and a one-line description.
  - **Add** on an example creates it as an ordinary snippet and makes it active (opening it in the editor).
  - **Add all** creates the whole set at once and makes one of them active.
- Added examples are **ordinary snippets**: meaningfully named (not auto-generated timestamps), and thereafter editable, duplicable, and deletable like any other — they are the user's, not a special class (_own your data_).
- Leaving the canvas by creating the first snippet(s) lays the workspace out at a sensible **default split** (library · editor · preview ≈ 25 · 25 · 50) with all three panes shown, so the first chart opens with a generous preview rather than the generic remembered widths.
- The onboarding canvas is shown **only while the library is empty**; as soon as any snippet exists, the normal panes return. Re-emptying the library brings it back.

## List Item

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 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.
- The active snippet is visually highlighted.

## Search

A live search box lets the user narrow the list as they type. It exists so users with many snippets can find one by name, by note, or by something inside the specification itself.

- The search box filters the list immediately on each keystroke.
- Matching is case-insensitive and spans the snippet **name**, the snippet **comment**, and the **specification content** (the current working/draft spec text), so a search for a field name, mark type, or dataset name in the spec will surface matching snippets.
- The search has a clear control that empties the box and returns focus to it, restoring the full list.
- Search affects only which snippets are shown; it does not change the active snippet or any data.

## Sort

The user chooses how the list is ordered. The choice persists across sessions so the library always opens the way the user left it.

- Sort fields: **Modified**, **Created**, **Name**, **Size**.
- An ascending/descending toggle controls direction; the current field and direction are indicated (e.g. a directional arrow on the active field).
- 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 selected sort field and direction persist across sessions.

## Selected-Snippet Metadata Panel

When a snippet is active, a metadata panel (within the left pane) exposes its editable properties and key facts. It exists so the user can rename, annotate, and inspect a snippet without leaving the library.

- 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_).

## Snippet Operations

The library provides the lifecycle operations for snippets. An operation whose outcome the user can't already see confirms it with a toast; an operation whose result is immediately visible needs none (see _Application Shell & Navigation_ → Toasts).

- **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. Opening in the editor _is_ the confirmation, so no toast is raised.
- **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 the newest remaining snippet becomes active (so the editor and detail panel stay populated); if none remain, no snippet is active. A toast confirms the deletion.
- These operations never affect other snippets.

## Naming & Tags

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).
- Names follow a **provenance hierarchy**: an **explicitly chosen** name (set via rename in the metadata panel) is frozen — the app never rewrites it. Every **app-picked** name (the timestamp default, a Chart Builder-generated name, or a previously derived one) is a "next best pick" that keeps tracking the spec: on each publish it is re-derived as the spec's `title` when present, else a mark + encodings description (the same dialect the Chart Builder names its output in), else the existing name stands. So the library reads by chart rather than by creation time, until the user takes over a name — at which point their word is final (see _Spec Editor → Publish_, _Data Model → `nameSource`_).
- 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 what the app's local storage is **made of** — a compact breakdown of how much space is taken by **snippets**, by **datasets**, and by the **app itself** (its offline-cached code and assets). It is informational: it helps the user see where space is going, not a fuel gauge counting down to a limit.

- The indicator stays **hidden until the user's own data — snippets + datasets — reaches a meaningful size** (about 10 MB). Below that there is nothing worth managing, so it adds no clutter; the app's own cached footprint does not count toward this threshold.
- When shown, the breakdown is a single proportional bar plus a labelled legend giving each category's size; meaning never rests on colour alone.
- There is **no "X of Y free" figure**. Browsers report only an unreliable, padded storage _quota_, so a precise "free space" number would mislead; the app shows real measured sizes instead.
- Snippet and dataset sizes are always shown — the app measures them directly. The **app** portion is shown when the browser exposes an overall usage figure; when it does not, the breakdown simply omits it.
- The genuine "out of room" moment is handled where it happens: if a save fails because storage is full, the system warns that the snippet could not be saved rather than silently losing data, so the user can delete snippets or datasets to free space (see _Import & Export_ error handling).
