scribe-cms 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.cjs +114 -121
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +114 -121
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +69 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +69 -70
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +23 -18
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +23 -18
- package/dist/runtime.js.map +1 -1
- package/dist/src/history/record-snapshot.d.ts +11 -0
- package/dist/src/history/record-snapshot.d.ts.map +1 -0
- package/dist/src/storage/sqlite.d.ts.map +1 -1
- package/dist/src/storage/translations.d.ts +14 -21
- package/dist/src/storage/translations.d.ts.map +1 -1
- package/dist/src/translate/page-translator.d.ts.map +1 -1
- package/dist/studio/server.cjs +51 -64
- package/dist/studio/server.cjs.map +1 -1
- package/dist/studio/server.d.ts.map +1 -1
- package/dist/studio/server.js +51 -64
- package/dist/studio/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/history/record-revision.d.ts +0 -14
- package/dist/src/history/record-revision.d.ts.map +0 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import type { ScribeConfig } from "../core/types.js";
|
|
3
|
+
/** Record (or reuse) a deduplicated EN-source snapshot for a translation run. */
|
|
4
|
+
export declare function recordEnSnapshot(config: ScribeConfig, input: {
|
|
5
|
+
contentType: string;
|
|
6
|
+
enSlug: string;
|
|
7
|
+
enHash: string;
|
|
8
|
+
frontmatter: Record<string, unknown>;
|
|
9
|
+
body: string;
|
|
10
|
+
}, db?: Database.Database): number;
|
|
11
|
+
//# sourceMappingURL=record-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-snapshot.d.ts","sourceRoot":"","sources":["../../../src/history/record-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,iFAAiF;AACjF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE;IACL,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,EACD,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,GACrB,MAAM,CAYR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../../src/storage/sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../../src/storage/sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAsDrD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;AAElD,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE7D;AAED,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,GAAE,UAAwB,GAAG,QAAQ,CAAC,QAAQ,CAUjG;AAwCD,wBAAgB,UAAU,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAEtD"}
|
|
@@ -9,6 +9,7 @@ export interface TranslationRow {
|
|
|
9
9
|
en_hash: string;
|
|
10
10
|
translated_at: string;
|
|
11
11
|
model: string;
|
|
12
|
+
snapshot_id: number | null;
|
|
12
13
|
}
|
|
13
14
|
export interface TranslationInput {
|
|
14
15
|
contentType: string;
|
|
@@ -20,45 +21,37 @@ export interface TranslationInput {
|
|
|
20
21
|
enHash: string;
|
|
21
22
|
translatedAt: string;
|
|
22
23
|
model: string;
|
|
24
|
+
snapshotId: number;
|
|
23
25
|
}
|
|
24
|
-
export interface
|
|
26
|
+
export interface EnSnapshotInput {
|
|
25
27
|
contentType: string;
|
|
26
28
|
enSlug: string;
|
|
27
|
-
locale: string | null;
|
|
28
|
-
revisionKind: "translation" | "en_edit_detected" | "snapshot";
|
|
29
29
|
enHash: string;
|
|
30
|
-
|
|
30
|
+
frontmatter: Record<string, unknown>;
|
|
31
|
+
body: string;
|
|
31
32
|
createdAt: string;
|
|
32
|
-
model?: string;
|
|
33
|
-
bodyPreview?: string;
|
|
34
|
-
/** Full translated frontmatter snapshot (JSON) at revision time. */
|
|
35
|
-
frontmatterJson?: string | null;
|
|
36
|
-
/** Full translated body snapshot at revision time. */
|
|
37
|
-
body?: string | null;
|
|
38
33
|
}
|
|
39
|
-
export interface
|
|
34
|
+
export interface EnSnapshotRow {
|
|
40
35
|
id: number;
|
|
41
36
|
content_type: string;
|
|
42
37
|
en_slug: string;
|
|
43
|
-
locale: string | null;
|
|
44
|
-
revision_kind: string;
|
|
45
38
|
en_hash: string;
|
|
46
|
-
|
|
39
|
+
frontmatter_json: string;
|
|
40
|
+
body: string;
|
|
47
41
|
created_at: string;
|
|
48
|
-
model: string | null;
|
|
49
|
-
body_preview: string | null;
|
|
50
|
-
frontmatter_json: string | null;
|
|
51
|
-
body: string | null;
|
|
52
42
|
}
|
|
43
|
+
export interface EnSnapshotWithLocales extends EnSnapshotRow {
|
|
44
|
+
locales: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function getOrCreateEnSnapshot(db: Database.Database, input: EnSnapshotInput): number;
|
|
47
|
+
export declare function getEnSnapshot(db: Database.Database, snapshotId: number): EnSnapshotRow | undefined;
|
|
48
|
+
export declare function listEnSnapshotsForEnSlug(db: Database.Database, contentType: string, enSlug: string): EnSnapshotWithLocales[];
|
|
53
49
|
export declare function upsertTranslation(db: Database.Database, input: TranslationInput): void;
|
|
54
50
|
export declare function getTranslation(db: Database.Database, contentType: string, enSlug: string, locale: string): TranslationRow | undefined;
|
|
55
51
|
export declare function listTranslationsForType(db: Database.Database, contentType: string): TranslationRow[];
|
|
56
52
|
export declare function listTranslationsForEnSlug(db: Database.Database, contentType: string, enSlug: string): TranslationRow[];
|
|
57
53
|
export declare function listTranslationsForLocale(db: Database.Database, contentType: string, locale: string): TranslationRow[];
|
|
58
54
|
export declare function bulkLoadTranslations(db: Database.Database): TranslationRow[];
|
|
59
|
-
export declare function appendRevision(db: Database.Database, input: RevisionInput): number;
|
|
60
|
-
/** List revision history for an EN slug, optionally filtered by locale. */
|
|
61
|
-
export declare function listRevisions(db: Database.Database, contentType: string, enSlug: string, locale?: string): RevisionRow[];
|
|
62
55
|
export declare function countTranslations(db: Database.Database): number;
|
|
63
56
|
export declare function countStaleTranslations(db: Database.Database, contentType: string, enSlug: string, currentEnHash: string): number;
|
|
64
57
|
//# sourceMappingURL=translations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/storage/translations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/storage/translations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-translator.d.ts","sourceRoot":"","sources":["../../../src/translate/page-translator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,kBAAkB,CAAC;AAarE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACtF;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAkEtF,mEAAmE;AACnE,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,mBAAmB,EACzB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAClE,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"page-translator.d.ts","sourceRoot":"","sources":["../../../src/translate/page-translator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,kBAAkB,CAAC;AAarE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACtF;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAkEtF,mEAAmE;AACnE,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,mBAAmB,EACzB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAClE,OAAO,CAAC,mBAAmB,CAAC,CAsH9B;AAMD,oEAAoE;AACpE,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,mBAAmB,EAAE,EAC5B,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACjD,GACL,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA6BhC"}
|
package/dist/studio/server.cjs
CHANGED
|
@@ -342,7 +342,7 @@ function mergeBuiltinsIntoFrontmatter(frontmatter, doc, type, defaultLocale) {
|
|
|
342
342
|
out.canonicalPath = resolveCanonicalPathname(type, doc, defaultLocale);
|
|
343
343
|
return out;
|
|
344
344
|
}
|
|
345
|
-
var SCHEMA_VERSION =
|
|
345
|
+
var SCHEMA_VERSION = 4;
|
|
346
346
|
var MIGRATIONS = [
|
|
347
347
|
`CREATE TABLE IF NOT EXISTS meta (
|
|
348
348
|
key TEXT PRIMARY KEY,
|
|
@@ -362,20 +362,6 @@ var MIGRATIONS = [
|
|
|
362
362
|
)`,
|
|
363
363
|
`CREATE INDEX IF NOT EXISTS idx_translations_type_locale
|
|
364
364
|
ON translations(content_type, locale)`,
|
|
365
|
-
`CREATE TABLE IF NOT EXISTS revisions (
|
|
366
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
367
|
-
content_type TEXT NOT NULL,
|
|
368
|
-
en_slug TEXT NOT NULL,
|
|
369
|
-
locale TEXT,
|
|
370
|
-
revision_kind TEXT NOT NULL,
|
|
371
|
-
en_hash TEXT NOT NULL,
|
|
372
|
-
body_hash TEXT NOT NULL,
|
|
373
|
-
created_at TEXT NOT NULL,
|
|
374
|
-
model TEXT,
|
|
375
|
-
body_preview TEXT
|
|
376
|
-
)`,
|
|
377
|
-
`CREATE INDEX IF NOT EXISTS idx_revisions_lookup
|
|
378
|
-
ON revisions(content_type, en_slug, locale, created_at DESC)`,
|
|
379
365
|
`CREATE TABLE IF NOT EXISTS slug_aliases (
|
|
380
366
|
content_type TEXT NOT NULL,
|
|
381
367
|
canonical_en_slug TEXT NOT NULL,
|
|
@@ -392,7 +378,19 @@ var MIGRATIONS = [
|
|
|
392
378
|
locale_slug TEXT NOT NULL,
|
|
393
379
|
captured_at TEXT NOT NULL,
|
|
394
380
|
PRIMARY KEY (content_type, alias_en_slug, locale)
|
|
395
|
-
)
|
|
381
|
+
)`,
|
|
382
|
+
`CREATE TABLE IF NOT EXISTS en_snapshots (
|
|
383
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
384
|
+
content_type TEXT NOT NULL,
|
|
385
|
+
en_slug TEXT NOT NULL,
|
|
386
|
+
en_hash TEXT NOT NULL,
|
|
387
|
+
frontmatter_json TEXT NOT NULL,
|
|
388
|
+
body TEXT NOT NULL,
|
|
389
|
+
created_at TEXT NOT NULL,
|
|
390
|
+
UNIQUE (content_type, en_slug, en_hash)
|
|
391
|
+
)`,
|
|
392
|
+
`CREATE INDEX IF NOT EXISTS idx_en_snapshots_lookup
|
|
393
|
+
ON en_snapshots(content_type, en_slug, created_at DESC)`
|
|
396
394
|
];
|
|
397
395
|
function resolveStorePath(config) {
|
|
398
396
|
return config.storePath;
|
|
@@ -414,12 +412,19 @@ function addColumnIfMissing(db, table, column, ddlType) {
|
|
|
414
412
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${ddlType}`);
|
|
415
413
|
}
|
|
416
414
|
}
|
|
415
|
+
function readSchemaVersion(db) {
|
|
416
|
+
const row = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
|
|
417
|
+
return row ? Number.parseInt(row.value, 10) || 0 : 0;
|
|
418
|
+
}
|
|
417
419
|
function migrate(db) {
|
|
420
|
+
const previousVersion = readSchemaVersion(db);
|
|
418
421
|
for (const sql of MIGRATIONS) {
|
|
419
422
|
db.exec(sql);
|
|
420
423
|
}
|
|
421
|
-
|
|
422
|
-
|
|
424
|
+
if (previousVersion < 4) {
|
|
425
|
+
db.exec(`DROP TABLE IF EXISTS revisions`);
|
|
426
|
+
}
|
|
427
|
+
addColumnIfMissing(db, "translations", "snapshot_id", "INTEGER");
|
|
423
428
|
db.prepare(
|
|
424
429
|
`INSERT INTO meta(key, value) VALUES('schema_version', ?)
|
|
425
430
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
@@ -427,6 +432,9 @@ function migrate(db) {
|
|
|
427
432
|
}
|
|
428
433
|
|
|
429
434
|
// src/storage/translations.ts
|
|
435
|
+
function getEnSnapshot(db, snapshotId) {
|
|
436
|
+
return db.prepare(`SELECT * FROM en_snapshots WHERE id = ?`).get(snapshotId);
|
|
437
|
+
}
|
|
430
438
|
function getTranslation(db, contentType, enSlug, locale) {
|
|
431
439
|
return db.prepare(
|
|
432
440
|
`SELECT * FROM translations WHERE content_type = ? AND en_slug = ? AND locale = ?`
|
|
@@ -435,20 +443,6 @@ function getTranslation(db, contentType, enSlug, locale) {
|
|
|
435
443
|
function listTranslationsForLocale(db, contentType, locale) {
|
|
436
444
|
return db.prepare(`SELECT * FROM translations WHERE content_type = ? AND locale = ? ORDER BY en_slug`).all(contentType, locale);
|
|
437
445
|
}
|
|
438
|
-
function listRevisions(db, contentType, enSlug, locale) {
|
|
439
|
-
if (locale) {
|
|
440
|
-
return db.prepare(
|
|
441
|
-
`SELECT * FROM revisions
|
|
442
|
-
WHERE content_type = ? AND en_slug = ? AND locale = ?
|
|
443
|
-
ORDER BY created_at DESC, id DESC`
|
|
444
|
-
).all(contentType, enSlug, locale);
|
|
445
|
-
}
|
|
446
|
-
return db.prepare(
|
|
447
|
-
`SELECT * FROM revisions
|
|
448
|
-
WHERE content_type = ? AND en_slug = ?
|
|
449
|
-
ORDER BY created_at DESC, id DESC`
|
|
450
|
-
).all(contentType, enSlug);
|
|
451
|
-
}
|
|
452
446
|
|
|
453
447
|
// src/loader/normalize-en.ts
|
|
454
448
|
function normalizeEnFrontmatter(data) {
|
|
@@ -681,38 +675,31 @@ function renderFrontmatterTable(frontmatter, schema) {
|
|
|
681
675
|
<tbody>${rows || `<tr><td colspan="2" class="dim">\u2014</td></tr>`}</tbody>
|
|
682
676
|
</table>`;
|
|
683
677
|
}
|
|
684
|
-
function
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
frontmatter = {};
|
|
691
|
-
}
|
|
692
|
-
const fmRows = flattenFrontmatter(frontmatter).map(
|
|
693
|
-
(row) => `<tr><td class="k">${escapeHtml(row.key)}</td><td class="v">${escapeHtml(row.value)}</td></tr>`
|
|
694
|
-
).join("");
|
|
695
|
-
return `<table class="kv"><tbody>${fmRows}</tbody></table>
|
|
696
|
-
<pre class="code">${escapeHtml(revision.body)}</pre>`;
|
|
678
|
+
function renderEnSnapshotPreview(snapshot) {
|
|
679
|
+
let frontmatter = {};
|
|
680
|
+
try {
|
|
681
|
+
frontmatter = JSON.parse(snapshot.frontmatter_json);
|
|
682
|
+
} catch {
|
|
683
|
+
frontmatter = {};
|
|
697
684
|
}
|
|
698
|
-
|
|
685
|
+
const fmRows = flattenFrontmatter(frontmatter).map(
|
|
686
|
+
(row) => `<tr><td class="k">${escapeHtml(row.key)}</td><td class="v">${escapeHtml(row.value)}</td></tr>`
|
|
687
|
+
).join("");
|
|
688
|
+
return `<table class="kv"><tbody>${fmRows}</tbody></table>
|
|
689
|
+
<pre class="code">${escapeHtml(snapshot.body)}</pre>`;
|
|
699
690
|
}
|
|
700
|
-
function
|
|
701
|
-
if (
|
|
702
|
-
return `<p class="dim">No
|
|
691
|
+
function renderTranslationSnapshotPanel(snapshot, currentEnHash) {
|
|
692
|
+
if (!snapshot) {
|
|
693
|
+
return `<p class="dim">No EN snapshot linked to this translation.</p>`;
|
|
703
694
|
}
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
).join("");
|
|
713
|
-
return `<table class="data"><thead><tr>
|
|
714
|
-
<th>When</th><th>Kind</th><th>Model</th><th>Hash</th><th></th>
|
|
715
|
-
</tr></thead><tbody>${items}</tbody></table>`;
|
|
695
|
+
const staleNote = currentEnHash && currentEnHash !== snapshot.en_hash ? `<p class="dim">Current EN hash differs from snapshot (${escapeHtml(currentEnHash.slice(0, 12))} vs ${escapeHtml(snapshot.en_hash.slice(0, 12))}).</p>` : "";
|
|
696
|
+
return `<dl class="meta">
|
|
697
|
+
<dt>snapshot</dt><dd>#${snapshot.id}</dd>
|
|
698
|
+
<dt>captured</dt><dd>${escapeHtml(snapshot.created_at.slice(0, 19))}</dd>
|
|
699
|
+
<dt>en_hash</dt><dd class="mono">${escapeHtml(snapshot.en_hash.slice(0, 12))}</dd>
|
|
700
|
+
</dl>
|
|
701
|
+
${staleNote}
|
|
702
|
+
<details><summary>EN source at translation time</summary>${renderEnSnapshotPreview(snapshot)}</details>`;
|
|
716
703
|
}
|
|
717
704
|
function renderLayout(title, body, project, options = {}) {
|
|
718
705
|
const typeLinks = project.listTypes().map((type) => {
|
|
@@ -1023,10 +1010,10 @@ async function startStudio(project, options = {}) {
|
|
|
1023
1010
|
<dt>slug</dt><dd>${escapeHtml(translation.slug)}</dd>
|
|
1024
1011
|
<dt>en_hash</dt><dd>${escapeHtml(currentEnHash?.slice(0, 12) ?? "\u2014")} / ${escapeHtml(storedEnHash?.slice(0, 12) ?? "\u2014")}</dd>
|
|
1025
1012
|
</dl>`;
|
|
1026
|
-
const
|
|
1013
|
+
const snapshot = translation.snapshot_id != null ? getEnSnapshot(db, translation.snapshot_id) : void 0;
|
|
1027
1014
|
historyPanel = `<div class="section">
|
|
1028
|
-
<div class="section-head">
|
|
1029
|
-
<div class="section-body">${
|
|
1015
|
+
<div class="section-head">EN snapshot</div>
|
|
1016
|
+
<div class="section-body">${renderTranslationSnapshotPanel(snapshot, currentEnHash)}</div>
|
|
1030
1017
|
</div>`;
|
|
1031
1018
|
} else {
|
|
1032
1019
|
contentPanel = `<p class="dim" style="padding:12px">No translation for ${escapeHtml(locale)}.</p>`;
|