mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
192 lines
7.5 KiB
TypeScript
192 lines
7.5 KiB
TypeScript
/**
|
|
* About & Help modal body (spec §01B/§01C).
|
|
*
|
|
* Rendered inside ModalShell — no backdrop, close button, or focus trap here;
|
|
* the shell owns all of that (docs/architecture/03 → Layer 3). This component
|
|
* is pure content: app identity, keyboard shortcuts (§01D), privacy posture
|
|
* (SOUL.md — local-only, no accounts, no telemetry, no AI), and acknowledgements of
|
|
* the projects Astrolabe is built on and shaped by.
|
|
*/
|
|
|
|
import type { ReactNode } from 'react';
|
|
import { FEEDBACK_EMAIL, feedbackMailtoHref } from '../feedback';
|
|
import styles from './AboutModal.module.css';
|
|
|
|
/** True when the user agent is macOS / iOS — drives the Cmd vs. Ctrl label. */
|
|
const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform);
|
|
const mod = isMac ? '⌘' : 'Ctrl';
|
|
|
|
/** Keyboard shortcut rows sourced from spec §01D. */
|
|
const SHORTCUTS: readonly { keys: string; action: string }[] = [
|
|
{ keys: `${mod}+Shift+N`, action: 'Create a new snippet' },
|
|
{ keys: `${mod}+K`, action: 'Toggle the Datasets manager' },
|
|
{ keys: `${mod}+S`, action: 'Publish the current snippet draft' },
|
|
{ keys: `${mod}+,`, action: 'Open editor settings' },
|
|
{ keys: 'Esc', action: 'Close the active modal' },
|
|
];
|
|
|
|
/**
|
|
* External acknowledgement link. Opens in a new tab so following a credit never
|
|
* unloads the workspace mid-edit; `rel` isolates the opened context.
|
|
*/
|
|
function Ack({ href, children }: { href: string; children: ReactNode }) {
|
|
return (
|
|
<a className={styles.link} href={href} target="_blank" rel="noopener noreferrer">
|
|
{children}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
export function AboutModal() {
|
|
return (
|
|
<div className={styles.about}>
|
|
{/* Identity */}
|
|
<section className={styles.section}>
|
|
<h3 className={styles.heading}>Astrolabe</h3>
|
|
<p className={styles.body}>
|
|
v<span className={styles.version}>{__APP_VERSION__}</span>
|
|
</p>
|
|
<p className={styles.body}>
|
|
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>
|
|
|
|
{/* Keyboard shortcuts */}
|
|
<section className={styles.section}>
|
|
<h3 className={styles.heading}>Keyboard shortcuts</h3>
|
|
<table className={styles.shortcuts} aria-label="Keyboard shortcuts">
|
|
<tbody>
|
|
{SHORTCUTS.map(({ keys, action }) => (
|
|
<tr key={keys} className={styles.row}>
|
|
<td className={styles.keys}>
|
|
<kbd className={styles.kbd}>{keys}</kbd>
|
|
</td>
|
|
<td className={styles.action}>{action}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
{/* Privacy */}
|
|
<section className={styles.section}>
|
|
<h3 className={styles.heading}>Privacy</h3>
|
|
<p className={styles.body}>
|
|
Astrolabe runs in your browser. Your snippets, datasets, and settings are stored locally
|
|
on your device — there’s no server to send them to. Work that has to stay
|
|
confidential is safe here.
|
|
</p>
|
|
<ul className={styles.list}>
|
|
<li>No account or sign-in.</li>
|
|
<li>
|
|
No AI. No model authors, edits, or critiques your charts, and nothing is sent to one.
|
|
The chart builder’s suggestions are computed locally from your data.
|
|
</li>
|
|
<li>
|
|
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>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>
|
|
</ul>
|
|
</section>
|
|
|
|
{/* Feedback */}
|
|
<section className={styles.section}>
|
|
<h3 className={styles.heading}>Feedback</h3>
|
|
<p className={styles.body}>
|
|
Found a bug or have an idea? Email{' '}
|
|
<a className={styles.link} href={feedbackMailtoHref()}>
|
|
{FEEDBACK_EMAIL}
|
|
</a>
|
|
.
|
|
</p>
|
|
</section>
|
|
|
|
{/* Acknowledgements */}
|
|
<section className={styles.section}>
|
|
<h3 className={styles.heading}>Acknowledgements</h3>
|
|
<p className={styles.body}>Astrolabe stands on the work of many open projects.</p>
|
|
|
|
<div className={styles.ackGroup}>
|
|
<h4 className={styles.ackLabel}>Built on</h4>
|
|
<ul className={styles.list}>
|
|
<li>
|
|
<Ack href="https://vega.github.io/vega-lite/">Vega-Lite</Ack>,{' '}
|
|
<Ack href="https://vega.github.io/vega/">Vega</Ack>,{' '}
|
|
<Ack href="https://github.com/vega/vega-embed">vega-embed</Ack>
|
|
<span className={styles.ackNote}> — render every chart</span>
|
|
</li>
|
|
<li>
|
|
<Ack href="https://microsoft.github.io/monaco-editor/">Monaco</Ack>
|
|
<span className={styles.ackNote}> — the JSON editor</span>
|
|
</li>
|
|
<li>
|
|
<Ack href="https://react.dev/">React</Ack>,{' '}
|
|
<Ack href="https://github.com/pmndrs/zustand">Zustand</Ack>,{' '}
|
|
<Ack href="https://vite.dev/">Vite</Ack>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className={styles.ackGroup}>
|
|
<h4 className={styles.ackLabel}>Chart guidance shaped by</h4>
|
|
<ul className={styles.list}>
|
|
<li>
|
|
<Ack href="https://github.com/uwdata/draco">Draco</Ack>,{' '}
|
|
<Ack href="https://github.com/vega/voyager">Voyager</Ack>
|
|
<span className={styles.ackNote}> — encoding rules</span>
|
|
</li>
|
|
<li>
|
|
<Ack href="https://github.com/Financial-Times/chart-doctor">FT Visual Vocabulary</Ack>
|
|
, <Ack href="https://www.datawrapper.de/">Datawrapper</Ack>
|
|
</li>
|
|
<li>
|
|
<Ack href="https://github.com/vega/lyra">Lyra</Ack>
|
|
<span className={styles.ackNote}> — authoring interactions</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className={styles.ackGroup}>
|
|
<h4 className={styles.ackLabel}>Design informed by</h4>
|
|
<ul className={styles.list}>
|
|
<li>
|
|
<Ack href="https://carbondesignsystem.com/">IBM Carbon</Ack>,{' '}
|
|
<Ack href="https://design-system.service.gov.uk/">GOV.UK</Ack>
|
|
</li>
|
|
<li>
|
|
<Ack href="https://www.w3.org/WAI/ARIA/apg/">WAI-ARIA APG</Ack>,{' '}
|
|
<Ack href="https://www.nngroup.com/">Nielsen Norman</Ack>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className={styles.ackGroup}>
|
|
<h4 className={styles.ackLabel}>Typography</h4>
|
|
<ul className={styles.list}>
|
|
<li>
|
|
<Ack href="https://github.com/IBM/plex">IBM Plex</Ack> and other open typefaces,
|
|
self-hosted via <Ack href="https://fontsource.org/">Fontsource</Ack>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Sibling project */}
|
|
<section className={styles.section}>
|
|
<h3 className={styles.heading}>Also from the maker</h3>
|
|
<p className={styles.body}>
|
|
Astrolabe has a sibling: <Ack href="https://syto.app">Syto</Ack>, a workspace for cleaning
|
|
and reshaping data before you chart it. Go take a look.
|
|
</p>
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|