mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Copy: pare product claims to what we can certify, cut rule-of-three cadence, record arch 10 §10
This commit is contained in:
@@ -155,7 +155,11 @@ role`) or the rule it demonstrates. - **Positional sub-section cross-refs.** Cit
|
||||
|
||||
14. **User-facing copy**: keep user-visible strings centralized and written for users (sentence
|
||||
case, active voice, no "please", no exclamation marks in errors). If/when an i18n layer
|
||||
exists, route strings through it instead of hardcoding.
|
||||
exists, route strings through it instead of hardcoding. **Product claims** (landing,
|
||||
About, onboarding, value props) follow `arch 10 §10`: claim only what we can certify, no
|
||||
absolutes (never/always/fully/everything), no durability the platform doesn't back ("saved",
|
||||
not "permanent"), and state a posture once per surface — reduce uncertain promises, keep the
|
||||
real ones.
|
||||
|
||||
15. **Chart-builder guidance reasons over role, not raw type** (`src/core/chart-builder.ts`):
|
||||
a `builderWarnings` rule (or any measure/dimension decision) must ask the post-transform
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A browser-based **snippet manager for [Vega-Lite](https://vega.github.io/vega-lite/)
|
||||
visualizations**. Author chart specs as JSON, watch them render live, and keep a personal,
|
||||
searchable library — fully local, offline-capable, no account.
|
||||
searchable library — local, offline-capable, no account.
|
||||
|
||||
> Astrolabe is a **spec-driven rebuild** on an architecture adapted from its sibling
|
||||
> project Syto. The authoritative behavioral contract lives in [`docs/spec/`](docs/spec/).
|
||||
@@ -23,10 +23,28 @@ npm run typecheck
|
||||
npm test # Vitest
|
||||
```
|
||||
|
||||
## What it does
|
||||
|
||||
- **Editor + live preview** — write Vega-Lite specs as JSON in Monaco with schema-aware
|
||||
validation and autocomplete; the chart re-renders as you type.
|
||||
- **Snippet library** — save, search, tag, and organize specs. Each snippet carries a stable
|
||||
published version plus a separate editable draft, so you can tinker without losing a
|
||||
known-good copy.
|
||||
- **Reusable datasets** — store data once (inline, or fetched once from a URL and snapshotted)
|
||||
and reference it by name from many snippets.
|
||||
- **Chart Builder** — a no-JSON on-ramp that generates a spec from field, mark, and encoding
|
||||
choices. The JSON stays the source of truth and is always editable.
|
||||
- **Theming & fonts** — custom chart themes with a visual Theme Builder, a curated font roster,
|
||||
and your own uploaded font faces.
|
||||
- **Local-first** — your library lives in the browser (IndexedDB); offline-capable and installable
|
||||
(PWA), with import/export for backup and transfer. No account, no server.
|
||||
|
||||
## Status
|
||||
|
||||
**M0 — Skeleton.** Toolchain green (typecheck, tests, build, PWA). The three-pane shell
|
||||
renders; features land milestone by milestone per the implementation plan (MVP at end of M1).
|
||||
Active development, **pre-1.0** and not yet publicly released — well past the initial milestones
|
||||
and usable day to day; the first public release will be `1.0.0`. Behavior is specified in
|
||||
[`docs/spec/`](docs/spec/) and the architecture patterns in
|
||||
[`docs/architecture/`](docs/architecture/00-overview.md).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -683,6 +683,39 @@ choice isn't re-litigated per surface:
|
||||
_(Consulted via /council → WAI-ARIA APG tabs/accordion/disclosure, IBM Carbon
|
||||
accordion usage, NN/g #6/#8.)_
|
||||
|
||||
## 10. Product claims & promise copy
|
||||
|
||||
Declarative copy — the landing, the About modal, onboarding, empty-state value props —
|
||||
makes **claims** about the product, not just feedback about an action. The care the §3
|
||||
triad gives error wording applies here too: **say only what we can certify, and say it
|
||||
once.** An overstated claim reads as insecurity, and the first time a user catches one
|
||||
being false it costs more trust than the claim ever bought (NN/g credibility; GOV.UK
|
||||
"don't oversell"). The test is not modesty for its own sake — it is that every sentence
|
||||
survives a skeptical reading.
|
||||
|
||||
- **Claim what we can certify — not the future, not what we don't control.** "No account,
|
||||
no server" is structural and always true (there is no backend). "Never leave your machine"
|
||||
is a vow over every future build and every edge case; state the posture instead — "stored
|
||||
locally on your device; there's no server to send them to."
|
||||
- **No absolutes.** _never · always · fully · entirely · everything._ One edge case or one
|
||||
future feature falsifies them, and the reader feels the overreach even when it happens to
|
||||
hold. Prefer the scoped form: "works offline" over "fully offline"; "your library lives in
|
||||
the browser" over "everything lives in the browser."
|
||||
- **Don't promise durability the platform doesn't back.** Browser storage (IndexedDB, no
|
||||
`persist()`) is best-effort and the browser may evict it. A chart is **saved**, not kept
|
||||
forever — route the permanence claim through **export**, which is the real backup.
|
||||
- **State a posture once per surface.** Repeating "local / no account / no server / offline"
|
||||
across the hero, the lede, and a feature grid is three chances to sound unsure of it. Give
|
||||
the posture one home and let the other surfaces describe the product.
|
||||
- **Match the register, and don't under-sell.** Astrolabe is a free, spare-time tool: the
|
||||
voice is plain and matter-of-fact, not manifesto. But concrete, true capabilities —
|
||||
portable Vega-Lite JSON, two authoring modes, custom themes — are claims worth making
|
||||
plainly. Reducing promises means cutting the _uncertain_ ones, never the real ones.
|
||||
|
||||
SOUL.md §"Local-Only by Default" is the internal **intent** and may be absolute; this
|
||||
section governs how that intent is **phrased to users**, where the promise should be only as
|
||||
strong as we can keep.
|
||||
|
||||
---
|
||||
|
||||
## Do / Don't
|
||||
@@ -694,6 +727,8 @@ accordion usage, NN/g #6/#8.)_
|
||||
- Adopt the APG keyboard pattern for new widgets; route all global keys through the one
|
||||
router.
|
||||
- Mark **optional** fields, not required ones (GOV.UK) — e.g. "Comment (optional)".
|
||||
- In product claims, say only what we can certify, once per surface; route durability
|
||||
through export.
|
||||
- Consult `/council` when this contract is silent — then record the answer back here.
|
||||
|
||||
**Don't**
|
||||
@@ -709,3 +744,5 @@ accordion usage, NN/g #6/#8.)_
|
||||
- Don't ship a **dead disabled control** as a placeholder for an unbuilt feature — a
|
||||
disabled button explains nothing and is skipped by assistive tech (GOV.UK, NN/g). Omit the
|
||||
action until it works, then show it enabled (e.g. "Build Chart" appears with M4).
|
||||
- Don't use absolutes in product claims (never/always/fully/everything) or promise what the
|
||||
platform can't keep — say "saved," not "permanent."
|
||||
|
||||
@@ -47,9 +47,8 @@ export function AboutModal() {
|
||||
v<span className={styles.version}>{__APP_VERSION__}</span>
|
||||
</p>
|
||||
<p className={styles.body}>
|
||||
A local-first workspace for authoring, organizing, and previewing Vega-Lite charts. Edit
|
||||
JSON, see the chart update live, and keep a personal library of snippets — with no
|
||||
account, no server, and full offline support.
|
||||
A local-first workspace for authoring and organizing Vega-Lite charts. Edit the JSON,
|
||||
watch it render live, and keep a personal library of snippets.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -74,17 +73,18 @@ export function AboutModal() {
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.heading}>Privacy</h3>
|
||||
<p className={styles.body}>
|
||||
Astrolabe runs entirely in your browser. Your snippets, datasets, and settings are stored
|
||||
locally and never leave your machine.
|
||||
Astrolabe runs in your browser. Your snippets, datasets, and settings are stored locally
|
||||
on your device — there’s no server to send them to.
|
||||
</p>
|
||||
<ul className={styles.list}>
|
||||
<li>No account, no sign-in, no server-side storage.</li>
|
||||
<li>The app runs no analytics or tracking — no cookies, no telemetry, no profiling.</li>
|
||||
<li>No account or sign-in.</li>
|
||||
<li>
|
||||
The only outbound network requests are ones you create: URL-sourced datasets you add
|
||||
yourself.
|
||||
The app itself runs no analytics or tracking. Its host (Cloudflare) records standard,
|
||||
aggregate traffic like any web server — not your library or the charts you build, which
|
||||
stay in your browser.
|
||||
</li>
|
||||
<li>After the first load, the app works fully offline.</li>
|
||||
<li>The only outbound requests are ones you make: datasets you load from a URL.</li>
|
||||
<li>After the first load, the app works offline.</li>
|
||||
<li>
|
||||
Use the header’s import / export buttons to move your library between devices.
|
||||
</li>
|
||||
|
||||
@@ -137,8 +137,8 @@ export function Onboarding() {
|
||||
<div className={styles.inner}>
|
||||
<h2 className={styles.title}>Welcome to Astrolabe</h2>
|
||||
<p className={styles.tagline}>
|
||||
A local library for your Vega-Lite charts — authored as JSON, rendered live, and kept on
|
||||
your device.
|
||||
A local library for your Vega-Lite charts — write the spec as JSON, watch it render live,
|
||||
and keep it all on your device.
|
||||
</p>
|
||||
|
||||
<div className={styles.ctaRow}>
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Project feedback channel.
|
||||
*
|
||||
* There is no server and no tracker (see the About modal — no telemetry of any
|
||||
* kind); feedback is a plain email the user composes and sends from their own
|
||||
* client. The address is a Cloudflare Email Routing alias that forwards to the
|
||||
* author, so it can be retired without exposing or churning a personal inbox.
|
||||
* The app sends no telemetry of its own (see the About modal); feedback is a
|
||||
* plain email the user composes and sends from their own client. The address is
|
||||
* a Cloudflare Email Routing alias that forwards to the author, so it can be
|
||||
* retired without exposing or churning a personal inbox.
|
||||
*
|
||||
* Shared by the Support modal and the About modal — a contact address is worth
|
||||
* a single source of truth so the two surfaces can't drift.
|
||||
|
||||
+13
-15
@@ -369,8 +369,7 @@ export function Landing(): ReactNode {
|
||||
</h1>
|
||||
<p>
|
||||
Astrolabe is a local studio for Vega-Lite. Write a spec by hand or build one by
|
||||
clicking, give it a theme, and keep all your charts in one library — in the browser,
|
||||
with no account and no server.
|
||||
clicking, give it a theme, and keep all your charts in one searchable library.
|
||||
</p>
|
||||
<div className={styles.heroCta}>
|
||||
<a className={`${styles.btn} ${styles.btnPrimary}`} href="/app/">
|
||||
@@ -399,8 +398,8 @@ export function Landing(): ReactNode {
|
||||
</h2>
|
||||
<p className={styles.capP}>
|
||||
The editor is Monaco with the Vega-Lite schema loaded, so you get validation,
|
||||
autocompletion, and inline docs without going online. Each chart keeps a draft and a
|
||||
published version: edit the draft, publish when it's ready, revert when it isn't.
|
||||
autocompletion, and inline docs without going online. Each chart keeps an editable
|
||||
draft alongside a published version you can revert to.
|
||||
</p>
|
||||
<p className={styles.capP}>
|
||||
If you'd rather not start from JSON, the builder works from the kind of chart you
|
||||
@@ -426,7 +425,7 @@ export function Landing(): ReactNode {
|
||||
<div className={styles.capText}>
|
||||
<div className={styles.capEyebrow}>One library</div>
|
||||
<h2 className={styles.capH}>
|
||||
Every chart you make <b>stays</b>, with the data behind it.
|
||||
Every chart you make is <b>saved</b>, with the data behind it.
|
||||
</h2>
|
||||
<p className={styles.capP}>
|
||||
Store a dataset once and reference it by name from as many charts as you want. Rename
|
||||
@@ -434,8 +433,8 @@ export function Landing(): ReactNode {
|
||||
</p>
|
||||
<p className={styles.capP}>
|
||||
Load data by pasting CSV or JSON, or by fetching a URL. You can also lift inline data
|
||||
out of a spec into a shared dataset. Search, sort, and duplicate as the collection
|
||||
grows.
|
||||
out of a spec into a shared dataset. Search and sort as the collection grows, and
|
||||
duplicate a snippet to start a variant.
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.shot}>
|
||||
@@ -599,17 +598,16 @@ export function Landing(): ReactNode {
|
||||
Your charts are <b>yours.</b>
|
||||
</h2>
|
||||
<p className={styles.creedLede}>
|
||||
Astrolabe runs in your browser and stores everything locally — no account, no server.
|
||||
What it produces is ordinary Vega-Lite JSON, so you can read it, edit it in other tools,
|
||||
or keep it long after you've stopped using Astrolabe.
|
||||
Astrolabe runs in your browser, and what it makes is ordinary Vega-Lite JSON — read it,
|
||||
edit it in other tools, or take it elsewhere whenever you want.
|
||||
</p>
|
||||
<div className={styles.creedGrid}>
|
||||
<div>
|
||||
<span className={styles.creedKey}>private</span>
|
||||
<h3>Stays on your machine</h3>
|
||||
<h3>Local to your browser</h3>
|
||||
<p>
|
||||
Charts, data, and themes are saved in your browser. Install it and it works with no
|
||||
connection.
|
||||
Charts, data, and themes are saved in this browser, and it keeps working offline
|
||||
once installed.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@@ -624,8 +622,8 @@ export function Landing(): ReactNode {
|
||||
<span className={styles.creedKey}>yours</span>
|
||||
<h3>Set up your way</h3>
|
||||
<p>
|
||||
Two ways to author, your own fonts and themes, your own library. Arrange it to match
|
||||
how you work.
|
||||
Author by hand or by clicking, with your own fonts and themes. Arrange the library
|
||||
to match how you work.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user