mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 10:12:34 +00:00
79 lines
3.0 KiB
TypeScript
79 lines
3.0 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), and privacy posture
|
|
* (SOUL.md — local-only, no accounts, no telemetry).
|
|
*/
|
|
|
|
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' },
|
|
];
|
|
|
|
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, 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.
|
|
</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 entirely in your browser. Your snippets, datasets, and settings are stored
|
|
locally and never leave your machine.
|
|
</p>
|
|
<ul className={styles.list}>
|
|
<li>No account, no sign-in, no server-side storage.</li>
|
|
<li>No telemetry, no analytics, no tracking of any kind.</li>
|
|
<li>
|
|
The only outbound network requests are ones you create: URL-sourced datasets you add
|
|
yourself.
|
|
</li>
|
|
<li>After the first load, the app works fully offline.</li>
|
|
<li>Use Import / Export to move your library between devices.</li>
|
|
</ul>
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|