mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Fonts: carry uploaded faces through export/import + embed in SVG export
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
import type { CustomTheme } from './custom-theme';
|
||||
import type { Dataset } from './dataset';
|
||||
import { serializeFontAsset, type FontAsset, type SerializedFontAsset } from './font-asset';
|
||||
import type { Snippet } from './snippet';
|
||||
|
||||
/**
|
||||
@@ -26,8 +27,8 @@ export const EXPORT_ENVELOPE_VERSION = '1.0';
|
||||
/**
|
||||
* The downloaded file's top-level shape (spec §08 → Export envelope shape): format
|
||||
* metadata plus the complete-record arrays. Each record keeps its own `version`
|
||||
* field unchanged. `themes` is additive (always written, optional on read) so
|
||||
* pre-theme envelopes and importers remain compatible without a format bump.
|
||||
* field unchanged. `themes` and `fonts` are additive (always written, optional on
|
||||
* read) so older envelopes and importers remain compatible without a format bump.
|
||||
*/
|
||||
export interface ExportEnvelope {
|
||||
/** Export format version (currently `"1.0"`). */
|
||||
@@ -42,6 +43,13 @@ export interface ExportEnvelope {
|
||||
datasets: Dataset[];
|
||||
/** All custom chart themes, as complete records (each including its record `version`). */
|
||||
themes: CustomTheme[];
|
||||
/**
|
||||
* All uploaded font faces, base64-encoded so a referenced face survives the
|
||||
* round-trip (without this the family name imports but renders as fallback —
|
||||
* spec §08, scope doc §4). The whole library travels, like datasets/themes: a
|
||||
* workspace export is a backup, not a minimal bundle of what's referenced.
|
||||
*/
|
||||
fonts: SerializedFontAsset[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +63,7 @@ export function buildExportEnvelope(
|
||||
snippets: ReadonlyArray<Snippet>,
|
||||
datasets: ReadonlyArray<Dataset>,
|
||||
themes: ReadonlyArray<CustomTheme>,
|
||||
fonts: ReadonlyArray<FontAsset>,
|
||||
opts: { now: Date },
|
||||
): ExportEnvelope {
|
||||
return {
|
||||
@@ -64,6 +73,8 @@ export function buildExportEnvelope(
|
||||
snippets: [...snippets],
|
||||
datasets: [...datasets],
|
||||
themes: [...themes],
|
||||
// Encode each face's bytes (ArrayBuffer → base64) so the array is JSON-safe.
|
||||
fonts: fonts.map(serializeFontAsset),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,19 +101,21 @@ function countClause(count: number, noun: string): string {
|
||||
/**
|
||||
* Success-toast message reporting transfer counts (spec §08 → Feedback) for both
|
||||
* directions, e.g. "Exported 4 snippets, 2 datasets and 1 theme" / "Imported 1
|
||||
* snippet". The dataset and theme clauses are omitted entirely when their counts
|
||||
* are zero; singular/plural wording adapts. One builder for export and import so
|
||||
* a new record kind or wording change lands in both messages at once.
|
||||
* snippet". The dataset, theme, and font clauses are omitted entirely when their
|
||||
* counts are zero; singular/plural wording adapts. One builder for export and
|
||||
* import so a new record kind or wording change lands in both messages at once.
|
||||
*/
|
||||
export function transferSummaryMessage(
|
||||
verb: 'Exported' | 'Imported',
|
||||
snippetCount: number,
|
||||
datasetCount: number,
|
||||
themeCount: number,
|
||||
fontCount: number,
|
||||
): string {
|
||||
const clauses = [countClause(snippetCount, 'snippet')];
|
||||
if (datasetCount > 0) clauses.push(countClause(datasetCount, 'dataset'));
|
||||
if (themeCount > 0) clauses.push(countClause(themeCount, 'theme'));
|
||||
if (fontCount > 0) clauses.push(countClause(fontCount, 'font'));
|
||||
const last = clauses.pop()!;
|
||||
return clauses.length === 0 ? `${verb} ${last}` : `${verb} ${clauses.join(', ')} and ${last}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user