@bendyline/squisq 1.1.0 → 1.2.0
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/ContentContainer-DNx460y8.d.ts +88 -0
- package/dist/{Doc-1hmT1LZl.d.ts → Doc-Dwn5aHFC.d.ts} +115 -6
- package/dist/__tests__/dataTable.test.d.ts +2 -0
- package/dist/__tests__/dataTable.test.d.ts.map +1 -0
- package/dist/__tests__/dataTable.test.js +110 -0
- package/dist/__tests__/dataTable.test.js.map +1 -0
- package/dist/__tests__/markdownToDoc.test.js +75 -0
- package/dist/__tests__/markdownToDoc.test.js.map +1 -1
- package/dist/{chunk-6YJPI6VI.js → chunk-4X3JQXNM.js} +1 -1
- package/dist/chunk-DNJKBAI7.js +1 -0
- package/dist/chunk-DNJKBAI7.js.map +1 -0
- package/dist/{chunk-S4F2RY37.js → chunk-KEFWM2VC.js} +1 -1
- package/dist/chunk-KEFWM2VC.js.map +1 -0
- package/dist/{chunk-VP5SZTZ2.js → chunk-MIBRIRGJ.js} +2 -139
- package/dist/chunk-MIBRIRGJ.js.map +1 -0
- package/dist/{chunk-URU6QMLY.js → chunk-NJLL5XYC.js} +15 -8
- package/dist/chunk-NJLL5XYC.js.map +1 -0
- package/dist/chunk-QM5PKNPW.js +165 -0
- package/dist/chunk-QM5PKNPW.js.map +1 -0
- package/dist/{chunk-PJ7AODIM.js → chunk-T6WBC3ND.js} +459 -53
- package/dist/chunk-T6WBC3ND.js.map +1 -0
- package/dist/chunk-TSG2OSE6.js +145 -0
- package/dist/chunk-TSG2OSE6.js.map +1 -0
- package/dist/{chunk-O5HCIUAD.js → chunk-TVPKD76V.js} +81 -187
- package/dist/chunk-TVPKD76V.js.map +1 -0
- package/dist/contentExtractor-BNfVJV2U.d.ts +113 -0
- package/dist/doc/audioMapping.d.ts +36 -0
- package/dist/doc/audioMapping.d.ts.map +1 -0
- package/dist/doc/audioMapping.js +279 -0
- package/dist/doc/audioMapping.js.map +1 -0
- package/dist/doc/index.d.ts +1 -0
- package/dist/doc/index.d.ts.map +1 -1
- package/dist/doc/index.js +1 -0
- package/dist/doc/index.js.map +1 -1
- package/dist/doc/markdownToDoc.d.ts.map +1 -1
- package/dist/doc/markdownToDoc.js +126 -1
- package/dist/doc/markdownToDoc.js.map +1 -1
- package/dist/doc/templates/comparisonBar.d.ts.map +1 -1
- package/dist/doc/templates/comparisonBar.js +23 -5
- package/dist/doc/templates/comparisonBar.js.map +1 -1
- package/dist/doc/templates/coverBlock.d.ts.map +1 -1
- package/dist/doc/templates/coverBlock.js +3 -0
- package/dist/doc/templates/coverBlock.js.map +1 -1
- package/dist/doc/templates/dataTable.d.ts +14 -0
- package/dist/doc/templates/dataTable.d.ts.map +1 -0
- package/dist/doc/templates/dataTable.js +73 -0
- package/dist/doc/templates/dataTable.js.map +1 -0
- package/dist/doc/templates/dateEvent.d.ts.map +1 -1
- package/dist/doc/templates/dateEvent.js +4 -0
- package/dist/doc/templates/dateEvent.js.map +1 -1
- package/dist/doc/templates/definitionCard.d.ts.map +1 -1
- package/dist/doc/templates/definitionCard.js +4 -1
- package/dist/doc/templates/definitionCard.js.map +1 -1
- package/dist/doc/templates/factCard.d.ts.map +1 -1
- package/dist/doc/templates/factCard.js +4 -0
- package/dist/doc/templates/factCard.js.map +1 -1
- package/dist/doc/templates/fullBleedQuote.d.ts.map +1 -1
- package/dist/doc/templates/fullBleedQuote.js +2 -1
- package/dist/doc/templates/fullBleedQuote.js.map +1 -1
- package/dist/doc/templates/imageWithCaption.d.ts.map +1 -1
- package/dist/doc/templates/imageWithCaption.js +4 -0
- package/dist/doc/templates/imageWithCaption.js.map +1 -1
- package/dist/doc/templates/index.d.ts +1 -0
- package/dist/doc/templates/index.d.ts.map +1 -1
- package/dist/doc/templates/index.js +3 -0
- package/dist/doc/templates/index.js.map +1 -1
- package/dist/doc/templates/listBlock.d.ts.map +1 -1
- package/dist/doc/templates/listBlock.js +3 -0
- package/dist/doc/templates/listBlock.js.map +1 -1
- package/dist/doc/templates/mapBlock.d.ts.map +1 -1
- package/dist/doc/templates/mapBlock.js +3 -0
- package/dist/doc/templates/mapBlock.js.map +1 -1
- package/dist/doc/templates/photoGrid.d.ts.map +1 -1
- package/dist/doc/templates/photoGrid.js +3 -0
- package/dist/doc/templates/photoGrid.js.map +1 -1
- package/dist/doc/templates/pullQuote.d.ts.map +1 -1
- package/dist/doc/templates/pullQuote.js +4 -0
- package/dist/doc/templates/pullQuote.js.map +1 -1
- package/dist/doc/templates/quoteBlock.d.ts.map +1 -1
- package/dist/doc/templates/quoteBlock.js +4 -0
- package/dist/doc/templates/quoteBlock.js.map +1 -1
- package/dist/doc/templates/sectionHeader.d.ts.map +1 -1
- package/dist/doc/templates/sectionHeader.js +3 -2
- package/dist/doc/templates/sectionHeader.js.map +1 -1
- package/dist/doc/templates/statHighlight.d.ts.map +1 -1
- package/dist/doc/templates/statHighlight.js +4 -1
- package/dist/doc/templates/statHighlight.js.map +1 -1
- package/dist/doc/templates/titleBlock.d.ts.map +1 -1
- package/dist/doc/templates/titleBlock.js +3 -0
- package/dist/doc/templates/titleBlock.js.map +1 -1
- package/dist/doc/templates/twoColumn.d.ts.map +1 -1
- package/dist/doc/templates/twoColumn.js +7 -1
- package/dist/doc/templates/twoColumn.js.map +1 -1
- package/dist/doc/templates/videoPullQuote.d.ts.map +1 -1
- package/dist/doc/templates/videoPullQuote.js +4 -0
- package/dist/doc/templates/videoPullQuote.js.map +1 -1
- package/dist/doc/templates/videoWithCaption.d.ts.map +1 -1
- package/dist/doc/templates/videoWithCaption.js +3 -0
- package/dist/doc/templates/videoWithCaption.js.map +1 -1
- package/dist/doc/utils/themeUtils.d.ts +9 -0
- package/dist/doc/utils/themeUtils.d.ts.map +1 -1
- package/dist/doc/utils/themeUtils.js +26 -0
- package/dist/doc/utils/themeUtils.js.map +1 -1
- package/dist/markdown/convert.d.ts.map +1 -1
- package/dist/markdown/convert.js +16 -7
- package/dist/markdown/convert.js.map +1 -1
- package/dist/markdown/types.d.ts +4 -2
- package/dist/markdown/types.d.ts.map +1 -1
- package/dist/schemas/BlockTemplates.d.ts +20 -3
- package/dist/schemas/BlockTemplates.d.ts.map +1 -1
- package/dist/schemas/BlockTemplates.js +2 -2
- package/dist/schemas/BlockTemplates.js.map +1 -1
- package/dist/schemas/Doc.d.ts +93 -1
- package/dist/schemas/Doc.d.ts.map +1 -1
- package/dist/schemas/Doc.js.map +1 -1
- package/dist/schemas/themeLibrary.d.ts +2 -2
- package/dist/schemas/themeLibrary.d.ts.map +1 -1
- package/dist/schemas/themeLibrary.js +91 -26
- package/dist/schemas/themeLibrary.js.map +1 -1
- package/dist/story/index.d.ts +63 -5
- package/dist/story/index.js +17 -5
- package/dist/{themeLibrary-DR1VRo-8.d.ts → themeLibrary-DLJtGh8-.d.ts} +3 -3
- package/dist/transform/applyTransform.d.ts +22 -0
- package/dist/transform/applyTransform.d.ts.map +1 -0
- package/dist/transform/applyTransform.js +68 -0
- package/dist/transform/applyTransform.js.map +1 -0
- package/dist/transform/blockAnalyzer.d.ts +48 -0
- package/dist/transform/blockAnalyzer.d.ts.map +1 -0
- package/dist/transform/blockAnalyzer.js +135 -0
- package/dist/transform/blockAnalyzer.js.map +1 -0
- package/dist/transform/index.d.ts +6 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/index.js +4 -0
- package/dist/transform/index.js.map +1 -0
- package/dist/transform/registry.d.ts +19 -0
- package/dist/transform/registry.d.ts.map +1 -0
- package/dist/transform/registry.js +41 -0
- package/dist/transform/registry.js.map +1 -0
- package/dist/transform/styles/dataDriven.d.ts +4 -0
- package/dist/transform/styles/dataDriven.d.ts.map +1 -0
- package/dist/transform/styles/dataDriven.js +15 -0
- package/dist/transform/styles/dataDriven.js.map +1 -0
- package/dist/transform/styles/documentary.d.ts +4 -0
- package/dist/transform/styles/documentary.d.ts.map +1 -0
- package/dist/transform/styles/documentary.js +15 -0
- package/dist/transform/styles/documentary.js.map +1 -0
- package/dist/transform/styles/index.d.ts +6 -0
- package/dist/transform/styles/index.d.ts.map +1 -0
- package/dist/transform/styles/index.js +6 -0
- package/dist/transform/styles/index.js.map +1 -0
- package/dist/transform/styles/magazine.d.ts +4 -0
- package/dist/transform/styles/magazine.d.ts.map +1 -0
- package/dist/transform/styles/magazine.js +15 -0
- package/dist/transform/styles/magazine.js.map +1 -0
- package/dist/transform/styles/minimal.d.ts +4 -0
- package/dist/transform/styles/minimal.d.ts.map +1 -0
- package/dist/transform/styles/minimal.js +15 -0
- package/dist/transform/styles/minimal.js.map +1 -0
- package/dist/transform/styles/narrative.d.ts +4 -0
- package/dist/transform/styles/narrative.d.ts.map +1 -0
- package/dist/transform/styles/narrative.js +15 -0
- package/dist/transform/styles/narrative.js.map +1 -0
- package/dist/transform/templateSelector.d.ts +30 -0
- package/dist/transform/templateSelector.d.ts.map +1 -0
- package/dist/transform/templateSelector.js +218 -0
- package/dist/transform/templateSelector.js.map +1 -0
- package/dist/transform/timingAllocator.d.ts +21 -0
- package/dist/transform/timingAllocator.d.ts.map +1 -0
- package/dist/transform/timingAllocator.js +111 -0
- package/dist/transform/timingAllocator.js.map +1 -0
- package/dist/transform/types.d.ts +86 -0
- package/dist/transform/types.d.ts.map +1 -0
- package/dist/transform/types.js +9 -0
- package/dist/transform/types.js.map +1 -0
- package/dist/{types-DHiv_Pnm.d.ts → types-DYAzisMG.d.ts} +4 -2
- package/package.json +6 -1
- package/src/__tests__/dataTable.test.ts +135 -0
- package/src/__tests__/markdownToDoc.test.ts +96 -0
- package/src/doc/audioMapping.ts +346 -0
- package/src/doc/index.ts +1 -0
- package/src/doc/markdownToDoc.ts +136 -2
- package/src/doc/templates/comparisonBar.ts +23 -5
- package/src/doc/templates/coverBlock.ts +3 -0
- package/src/doc/templates/dataTable.ts +84 -0
- package/src/doc/templates/dateEvent.ts +4 -0
- package/src/doc/templates/definitionCard.ts +4 -1
- package/src/doc/templates/factCard.ts +4 -0
- package/src/doc/templates/fullBleedQuote.ts +2 -1
- package/src/doc/templates/imageWithCaption.ts +4 -0
- package/src/doc/templates/index.ts +3 -0
- package/src/doc/templates/listBlock.ts +3 -0
- package/src/doc/templates/mapBlock.ts +3 -0
- package/src/doc/templates/photoGrid.ts +3 -0
- package/src/doc/templates/pullQuote.ts +4 -0
- package/src/doc/templates/quoteBlock.ts +4 -0
- package/src/doc/templates/sectionHeader.ts +3 -2
- package/src/doc/templates/statHighlight.ts +4 -1
- package/src/doc/templates/titleBlock.ts +3 -0
- package/src/doc/templates/twoColumn.ts +7 -1
- package/src/doc/templates/videoPullQuote.ts +4 -0
- package/src/doc/templates/videoWithCaption.ts +3 -0
- package/src/doc/utils/themeUtils.ts +29 -0
- package/src/markdown/convert.ts +17 -7
- package/src/markdown/types.ts +4 -2
- package/src/schemas/BlockTemplates.ts +22 -3
- package/src/schemas/Doc.ts +103 -1
- package/src/schemas/themeLibrary.ts +95 -26
- package/src/transform/applyTransform.ts +83 -0
- package/src/transform/blockAnalyzer.ts +181 -0
- package/src/transform/index.ts +20 -0
- package/src/transform/registry.ts +47 -0
- package/src/transform/styles/dataDriven.ts +16 -0
- package/src/transform/styles/documentary.ts +16 -0
- package/src/transform/styles/index.ts +5 -0
- package/src/transform/styles/magazine.ts +16 -0
- package/src/transform/styles/minimal.ts +16 -0
- package/src/transform/styles/narrative.ts +16 -0
- package/src/transform/templateSelector.ts +294 -0
- package/src/transform/timingAllocator.ts +126 -0
- package/src/transform/types.ts +105 -0
- package/dist/chunk-O5HCIUAD.js.map +0 -1
- package/dist/chunk-PJ7AODIM.js.map +0 -1
- package/dist/chunk-S4F2RY37.js.map +0 -1
- package/dist/chunk-URU6QMLY.js.map +0 -1
- package/dist/chunk-VP5SZTZ2.js.map +0 -1
- /package/dist/{chunk-6YJPI6VI.js.map → chunk-4X3JQXNM.js.map} +0 -0
package/src/markdown/convert.ts
CHANGED
|
@@ -181,17 +181,25 @@ function extractTemplateAnnotation(
|
|
|
181
181
|
*/
|
|
182
182
|
function parseAnnotationTokens(inner: string): HeadingTemplateAnnotation {
|
|
183
183
|
const tokens = inner.split(/\s+/);
|
|
184
|
-
const template = tokens[0];
|
|
185
184
|
const params: Record<string, string> = {};
|
|
186
185
|
|
|
187
|
-
|
|
186
|
+
// If the first token contains '=', there's no template name —
|
|
187
|
+
// the annotation is purely key-value (e.g., `{[audio=intro.mp3]}`).
|
|
188
|
+
const firstIsParam = tokens[0].indexOf('=') > 0;
|
|
189
|
+
const template = firstIsParam ? undefined : tokens[0];
|
|
190
|
+
const startIdx = firstIsParam ? 0 : 1;
|
|
191
|
+
|
|
192
|
+
for (let i = startIdx; i < tokens.length; i++) {
|
|
188
193
|
const eqIdx = tokens[i].indexOf('=');
|
|
189
194
|
if (eqIdx > 0) {
|
|
190
195
|
params[tokens[i].slice(0, eqIdx)] = tokens[i].slice(eqIdx + 1);
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
198
|
|
|
194
|
-
const result: HeadingTemplateAnnotation = {
|
|
199
|
+
const result: HeadingTemplateAnnotation = {};
|
|
200
|
+
if (template) {
|
|
201
|
+
result.template = template;
|
|
202
|
+
}
|
|
195
203
|
if (Object.keys(params).length > 0) {
|
|
196
204
|
result.params = params;
|
|
197
205
|
}
|
|
@@ -202,14 +210,16 @@ function parseAnnotationTokens(inner: string): HeadingTemplateAnnotation {
|
|
|
202
210
|
* Serialize a HeadingTemplateAnnotation back to `{[templateName key=value …]}` text.
|
|
203
211
|
*/
|
|
204
212
|
function serializeTemplateAnnotation(annotation: HeadingTemplateAnnotation): string {
|
|
205
|
-
|
|
213
|
+
const parts: string[] = [];
|
|
214
|
+
if (annotation.template) {
|
|
215
|
+
parts.push(annotation.template);
|
|
216
|
+
}
|
|
206
217
|
if (annotation.params) {
|
|
207
218
|
for (const [key, value] of Object.entries(annotation.params)) {
|
|
208
|
-
|
|
219
|
+
parts.push(`${key}=${value}`);
|
|
209
220
|
}
|
|
210
221
|
}
|
|
211
|
-
|
|
212
|
-
return result;
|
|
222
|
+
return `{[${parts.join(' ')}]}`;
|
|
213
223
|
}
|
|
214
224
|
|
|
215
225
|
/**
|
package/src/markdown/types.ts
CHANGED
|
@@ -118,8 +118,10 @@ interface MarkdownNodeBase {
|
|
|
118
118
|
* → `{ template: 'chart', params: { colorScheme: 'blue' } }`
|
|
119
119
|
*/
|
|
120
120
|
export interface HeadingTemplateAnnotation {
|
|
121
|
-
/** Template name (first token inside `{[…]}`)
|
|
122
|
-
|
|
121
|
+
/** Template name (first token inside `{[…]}`).
|
|
122
|
+
* Optional when the annotation contains only key=value pairs
|
|
123
|
+
* (e.g., `{[audio=intro.mp3]}`). */
|
|
124
|
+
template?: string;
|
|
123
125
|
/** Optional key-value parameters (remaining `key=value` tokens) */
|
|
124
126
|
params?: Record<string, string>;
|
|
125
127
|
}
|
|
@@ -39,12 +39,12 @@ export type ColorScheme = string;
|
|
|
39
39
|
* PT Serif provides a classic, readable appearance for documentary-style content.
|
|
40
40
|
* Used as a fallback in TextLayer when no font is specified.
|
|
41
41
|
*/
|
|
42
|
-
export const DEFAULT_DOC_FONT = '
|
|
42
|
+
export const DEFAULT_DOC_FONT = 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif';
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Default font family for titles.
|
|
46
46
|
*/
|
|
47
|
-
export const DEFAULT_TITLE_FONT = '
|
|
47
|
+
export const DEFAULT_TITLE_FONT = 'Georgia, "Times New Roman", serif';
|
|
48
48
|
|
|
49
49
|
// ============================================
|
|
50
50
|
// Accent Image System
|
|
@@ -429,6 +429,24 @@ export interface VideoPullQuoteInput extends BaseTemplateBlock {
|
|
|
429
429
|
};
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Data table - renders a themed table with headers and rows.
|
|
434
|
+
* Ideal for structured data, comparisons, and reference information.
|
|
435
|
+
*/
|
|
436
|
+
export interface DataTableInput extends BaseTemplateBlock {
|
|
437
|
+
template: 'dataTable';
|
|
438
|
+
/** Optional title displayed above the table */
|
|
439
|
+
title?: string;
|
|
440
|
+
/** Header cell values */
|
|
441
|
+
headers: string[];
|
|
442
|
+
/** Data rows (array of cell value arrays) */
|
|
443
|
+
rows: string[][];
|
|
444
|
+
/** Per-column alignment */
|
|
445
|
+
align?: (('left' | 'right' | 'center') | null)[];
|
|
446
|
+
/** Color scheme for the table header */
|
|
447
|
+
colorScheme?: ColorScheme;
|
|
448
|
+
}
|
|
449
|
+
|
|
432
450
|
/**
|
|
433
451
|
* Union of all template block types.
|
|
434
452
|
*/
|
|
@@ -449,7 +467,8 @@ export type TemplateBlock =
|
|
|
449
467
|
| ComparisonBarInput
|
|
450
468
|
| PullQuoteInput
|
|
451
469
|
| VideoWithCaptionInput
|
|
452
|
-
| VideoPullQuoteInput
|
|
470
|
+
| VideoPullQuoteInput
|
|
471
|
+
| DataTableInput;
|
|
453
472
|
|
|
454
473
|
/**
|
|
455
474
|
* A block can be either a raw Block or a TemplateBlock.
|
package/src/schemas/Doc.ts
CHANGED
|
@@ -127,6 +127,13 @@ export interface Block {
|
|
|
127
127
|
/** Template name that generated this block (for debugging) */
|
|
128
128
|
template?: string;
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Display title for template rendering.
|
|
132
|
+
* Extracted from the sourceHeading when the block is created by markdownToDoc().
|
|
133
|
+
* Templates like sectionHeader and titleBlock read this for their title layer.
|
|
134
|
+
*/
|
|
135
|
+
title?: string;
|
|
136
|
+
|
|
130
137
|
// ── Markdown-driven hierarchy (optional) ──
|
|
131
138
|
|
|
132
139
|
/**
|
|
@@ -170,7 +177,7 @@ export interface Block {
|
|
|
170
177
|
* A visual element within a block.
|
|
171
178
|
* Layers are composited back-to-front (first layer is background).
|
|
172
179
|
*/
|
|
173
|
-
export type Layer = ImageLayer | TextLayer | ShapeLayer | MapLayer | VideoLayer;
|
|
180
|
+
export type Layer = ImageLayer | TextLayer | ShapeLayer | MapLayer | VideoLayer | TableLayer;
|
|
174
181
|
|
|
175
182
|
interface BaseLayer {
|
|
176
183
|
/** Unique identifier for this layer */
|
|
@@ -293,6 +300,48 @@ export interface VideoLayer extends BaseLayer {
|
|
|
293
300
|
};
|
|
294
301
|
}
|
|
295
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Table layer - renders a data table with themed styling.
|
|
305
|
+
* Uses foreignObject inside SVG for HTML-based table layout.
|
|
306
|
+
*/
|
|
307
|
+
export interface TableLayer extends BaseLayer {
|
|
308
|
+
type: 'table';
|
|
309
|
+
content: {
|
|
310
|
+
/** Header cell values */
|
|
311
|
+
headers: string[];
|
|
312
|
+
/** Data rows (array of cell value arrays) */
|
|
313
|
+
rows: string[][];
|
|
314
|
+
/** Per-column alignment */
|
|
315
|
+
align?: (('left' | 'right' | 'center') | null)[];
|
|
316
|
+
/** Visual styling */
|
|
317
|
+
style: TableLayerStyle;
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Styling options for a TableLayer.
|
|
323
|
+
*/
|
|
324
|
+
export interface TableLayerStyle {
|
|
325
|
+
/** Header row background color */
|
|
326
|
+
headerBackground: string;
|
|
327
|
+
/** Header row text color */
|
|
328
|
+
headerColor: string;
|
|
329
|
+
/** Body cell background color */
|
|
330
|
+
cellBackground: string;
|
|
331
|
+
/** Body cell text color */
|
|
332
|
+
cellColor: string;
|
|
333
|
+
/** Border/divider color */
|
|
334
|
+
borderColor: string;
|
|
335
|
+
/** Font size in pixels */
|
|
336
|
+
fontSize: number;
|
|
337
|
+
/** Font family */
|
|
338
|
+
fontFamily?: string;
|
|
339
|
+
/** Header font family (falls back to fontFamily) */
|
|
340
|
+
headerFontFamily?: string;
|
|
341
|
+
/** Corner radius for the table container */
|
|
342
|
+
borderRadius?: number;
|
|
343
|
+
}
|
|
344
|
+
|
|
296
345
|
/**
|
|
297
346
|
* Available map tile styles from free/open-source providers.
|
|
298
347
|
*/
|
|
@@ -436,6 +485,19 @@ export interface AudioTrack {
|
|
|
436
485
|
// Captions
|
|
437
486
|
// ============================================
|
|
438
487
|
|
|
488
|
+
/**
|
|
489
|
+
* A single word with precise timing, used for word-level highlighting
|
|
490
|
+
* in social-style captions. Populated from TTS timing data when available.
|
|
491
|
+
*/
|
|
492
|
+
export interface CaptionWord {
|
|
493
|
+
/** The word text */
|
|
494
|
+
text: string;
|
|
495
|
+
/** Start time in seconds (relative to doc start) */
|
|
496
|
+
startTime: number;
|
|
497
|
+
/** End time in seconds */
|
|
498
|
+
endTime: number;
|
|
499
|
+
}
|
|
500
|
+
|
|
439
501
|
/**
|
|
440
502
|
* A single caption phrase to display during playback.
|
|
441
503
|
*/
|
|
@@ -448,6 +510,13 @@ export interface CaptionPhrase {
|
|
|
448
510
|
endTime: number;
|
|
449
511
|
/** Which audio segment this caption belongs to (0-indexed) */
|
|
450
512
|
audioSegment: number;
|
|
513
|
+
/**
|
|
514
|
+
* Optional per-word timing from TTS timing data.
|
|
515
|
+
* When present, enables precise word-level highlighting in social
|
|
516
|
+
* caption style. When absent, word timing is interpolated evenly
|
|
517
|
+
* across the phrase duration.
|
|
518
|
+
*/
|
|
519
|
+
words?: CaptionWord[];
|
|
451
520
|
}
|
|
452
521
|
|
|
453
522
|
/**
|
|
@@ -476,6 +545,39 @@ export interface AudioSegment {
|
|
|
476
545
|
startTime: number;
|
|
477
546
|
}
|
|
478
547
|
|
|
548
|
+
// ============================================
|
|
549
|
+
// Audio Timing Data (from TTS)
|
|
550
|
+
// ============================================
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* A single word-level timing bookmark from TTS synthesis.
|
|
554
|
+
* Used for precise audio-to-content mapping and word-level caption sync.
|
|
555
|
+
*/
|
|
556
|
+
export interface AudioBookmark {
|
|
557
|
+
/** Unique identifier (e.g., "word-0", "word-1") */
|
|
558
|
+
id: string;
|
|
559
|
+
/** Timestamp in seconds from audio start */
|
|
560
|
+
time: number;
|
|
561
|
+
/** Character offset in the source text */
|
|
562
|
+
charOffset: number;
|
|
563
|
+
/** The word text at this position */
|
|
564
|
+
textFragment?: string;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Timing data for an audio segment, typically from a `.timing.json` file
|
|
569
|
+
* generated alongside TTS audio. Contains the source text and per-word
|
|
570
|
+
* timing bookmarks for precise content matching and caption sync.
|
|
571
|
+
*/
|
|
572
|
+
export interface AudioTimingData {
|
|
573
|
+
/** The normalized plain text that was synthesized */
|
|
574
|
+
sourceText: string;
|
|
575
|
+
/** Word-level timing bookmarks */
|
|
576
|
+
bookmarks: AudioBookmark[];
|
|
577
|
+
/** Total audio duration in seconds */
|
|
578
|
+
duration: number;
|
|
579
|
+
}
|
|
580
|
+
|
|
479
581
|
// ============================================
|
|
480
582
|
// Helper Functions
|
|
481
583
|
// ============================================
|
|
@@ -19,11 +19,32 @@ import type { Theme, ThemeColorScheme } from './Theme.js';
|
|
|
19
19
|
// Shared constants
|
|
20
20
|
// ============================================
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
// ── Font stacks ──────────────────────────────────────────────────
|
|
23
|
+
// Each stack lists the recommended Google Font first (works when the
|
|
24
|
+
// wrapping site loads it), then degrades to safe system fonts.
|
|
25
|
+
// Squisq itself does NOT load Google Fonts.
|
|
26
|
+
|
|
27
|
+
/** System fonts only — no external dependencies. */
|
|
28
|
+
const SYSTEM_SANS = 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif';
|
|
29
|
+
const SYSTEM_SERIF = 'Georgia, "Times New Roman", serif';
|
|
30
|
+
const SYSTEM_MONO = 'Consolas, "Courier New", monospace';
|
|
31
|
+
|
|
32
|
+
/** Theme-specific stacks with Google Font leads. */
|
|
33
|
+
const PLAYFAIR = '"Playfair Display", Georgia, serif';
|
|
34
|
+
const SOURCE_SERIF = '"Source Serif 4", "PT Serif", Georgia, serif';
|
|
35
|
+
const INTER = '"Inter", "Segoe UI", Roboto, sans-serif';
|
|
36
|
+
const OSWALD = '"Oswald", Impact, "Arial Black", sans-serif';
|
|
37
|
+
const ROBOTO = '"Roboto", "Segoe UI", Arial, sans-serif';
|
|
38
|
+
const MERRIWEATHER = '"Merriweather", Georgia, serif';
|
|
39
|
+
const LORA = '"Lora", "Merriweather", Georgia, serif';
|
|
40
|
+
const JETBRAINS_MONO = '"JetBrains Mono", "Fira Code", Consolas, monospace';
|
|
41
|
+
const IBM_PLEX_SANS = '"IBM Plex Sans", "Segoe UI", Roboto, sans-serif';
|
|
42
|
+
const DM_SERIF = '"DM Serif Display", Georgia, serif';
|
|
43
|
+
const DM_SANS = '"DM Sans", "Segoe UI", Roboto, sans-serif';
|
|
44
|
+
const CORMORANT = '"Cormorant Garamond", Garamond, Georgia, serif';
|
|
45
|
+
const CRIMSON = '"Crimson Text", Georgia, serif';
|
|
46
|
+
const PT_SERIF = '"PT Serif", Georgia, serif';
|
|
47
|
+
const HANKEN_GROTESK = '"Hanken Grotesk", system-ui, sans-serif';
|
|
27
48
|
|
|
28
49
|
/** Standard 6-scheme set used by the documentary theme (migrated from old COLOR_SCHEMES). */
|
|
29
50
|
const CLASSIC_COLOR_SCHEMES: Record<string, ThemeColorScheme> = {
|
|
@@ -36,13 +57,60 @@ const CLASSIC_COLOR_SCHEMES: Record<string, ThemeColorScheme> = {
|
|
|
36
57
|
};
|
|
37
58
|
|
|
38
59
|
// ============================================
|
|
39
|
-
// 1.
|
|
60
|
+
// 1. Standard (default — safe system fonts, no external dependencies)
|
|
61
|
+
// ============================================
|
|
62
|
+
|
|
63
|
+
const standard: Theme = {
|
|
64
|
+
id: 'standard',
|
|
65
|
+
name: 'Standard',
|
|
66
|
+
description: 'Clean and safe. System fonts only — no external font dependencies.',
|
|
67
|
+
colors: {
|
|
68
|
+
primary: '#3182ce',
|
|
69
|
+
secondary: '#4a5568',
|
|
70
|
+
background: '#1a202c',
|
|
71
|
+
backgroundLight: '#2d3748',
|
|
72
|
+
text: '#f7fafc',
|
|
73
|
+
textMuted: '#a0aec0',
|
|
74
|
+
highlight: '#4299e1',
|
|
75
|
+
warning: '#fc8181',
|
|
76
|
+
},
|
|
77
|
+
typography: {
|
|
78
|
+
bodyFontFamily: SYSTEM_SANS,
|
|
79
|
+
titleFontFamily: SYSTEM_SERIF,
|
|
80
|
+
monoFontFamily: SYSTEM_MONO,
|
|
81
|
+
titleWeight: 'bold',
|
|
82
|
+
},
|
|
83
|
+
style: {
|
|
84
|
+
textShadow: true,
|
|
85
|
+
overlayOpacity: 0.45,
|
|
86
|
+
animationSpeed: 1.0,
|
|
87
|
+
},
|
|
88
|
+
renderStyle: {
|
|
89
|
+
name: 'standard',
|
|
90
|
+
defaultTextAnimation: 'fadeIn',
|
|
91
|
+
defaultImageAnimation: 'slowZoom',
|
|
92
|
+
ambientMotion: true,
|
|
93
|
+
defaultTransition: { type: 'fade', duration: 0.7 },
|
|
94
|
+
},
|
|
95
|
+
colorSchemes: CLASSIC_COLOR_SCHEMES,
|
|
96
|
+
persistentLayers: {
|
|
97
|
+
bottomLayers: [
|
|
98
|
+
{
|
|
99
|
+
template: 'gradientBackground',
|
|
100
|
+
config: { type: 'gradientBackground', preset: 'dark-vignette' },
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// ============================================
|
|
107
|
+
// 2. Documentary
|
|
40
108
|
// ============================================
|
|
41
109
|
|
|
42
110
|
const documentary: Theme = {
|
|
43
111
|
id: 'documentary',
|
|
44
112
|
name: 'Documentary',
|
|
45
|
-
description: 'Classic dark cinematic look with serif typography.
|
|
113
|
+
description: 'Classic dark cinematic look with elegant serif typography.',
|
|
46
114
|
colors: {
|
|
47
115
|
primary: '#3d5a80',
|
|
48
116
|
secondary: '#63b3ed',
|
|
@@ -54,8 +122,8 @@ const documentary: Theme = {
|
|
|
54
122
|
warning: '#fc8181',
|
|
55
123
|
},
|
|
56
124
|
typography: {
|
|
57
|
-
bodyFontFamily:
|
|
58
|
-
titleFontFamily:
|
|
125
|
+
bodyFontFamily: SOURCE_SERIF,
|
|
126
|
+
titleFontFamily: PLAYFAIR,
|
|
59
127
|
titleWeight: 'bold',
|
|
60
128
|
},
|
|
61
129
|
style: {
|
|
@@ -100,8 +168,8 @@ const minimalist: Theme = {
|
|
|
100
168
|
warning: '#e53e3e',
|
|
101
169
|
},
|
|
102
170
|
typography: {
|
|
103
|
-
bodyFontFamily:
|
|
104
|
-
titleFontFamily:
|
|
171
|
+
bodyFontFamily: INTER,
|
|
172
|
+
titleFontFamily: INTER,
|
|
105
173
|
titleWeight: 'bold',
|
|
106
174
|
lineHeight: 1.6,
|
|
107
175
|
},
|
|
@@ -149,8 +217,8 @@ const bold: Theme = {
|
|
|
149
217
|
warning: '#fc8181',
|
|
150
218
|
},
|
|
151
219
|
typography: {
|
|
152
|
-
bodyFontFamily:
|
|
153
|
-
titleFontFamily:
|
|
220
|
+
bodyFontFamily: ROBOTO,
|
|
221
|
+
titleFontFamily: OSWALD,
|
|
154
222
|
titleWeight: 'bold',
|
|
155
223
|
titleScale: 1.15,
|
|
156
224
|
bodyScale: 1.0,
|
|
@@ -201,7 +269,7 @@ const morningLight: Theme = {
|
|
|
201
269
|
warning: '#c53030',
|
|
202
270
|
},
|
|
203
271
|
typography: {
|
|
204
|
-
bodyFontFamily:
|
|
272
|
+
bodyFontFamily: LORA,
|
|
205
273
|
titleFontFamily: MERRIWEATHER,
|
|
206
274
|
titleWeight: 'bold',
|
|
207
275
|
lineHeight: 1.7,
|
|
@@ -247,9 +315,9 @@ const techDark: Theme = {
|
|
|
247
315
|
warning: '#ff5252',
|
|
248
316
|
},
|
|
249
317
|
typography: {
|
|
250
|
-
bodyFontFamily:
|
|
251
|
-
titleFontFamily:
|
|
252
|
-
monoFontFamily:
|
|
318
|
+
bodyFontFamily: IBM_PLEX_SANS,
|
|
319
|
+
titleFontFamily: JETBRAINS_MONO,
|
|
320
|
+
monoFontFamily: JETBRAINS_MONO,
|
|
253
321
|
titleWeight: 'bold',
|
|
254
322
|
titleScale: 0.9,
|
|
255
323
|
lineHeight: 1.5,
|
|
@@ -299,8 +367,8 @@ const magazine: Theme = {
|
|
|
299
367
|
warning: '#fc8181',
|
|
300
368
|
},
|
|
301
369
|
typography: {
|
|
302
|
-
bodyFontFamily:
|
|
303
|
-
titleFontFamily:
|
|
370
|
+
bodyFontFamily: DM_SANS,
|
|
371
|
+
titleFontFamily: DM_SERIF,
|
|
304
372
|
titleWeight: 'bold',
|
|
305
373
|
titleScale: 1.05,
|
|
306
374
|
lineHeight: 1.5,
|
|
@@ -347,8 +415,8 @@ const cinematic: Theme = {
|
|
|
347
415
|
warning: '#fc8181',
|
|
348
416
|
},
|
|
349
417
|
typography: {
|
|
350
|
-
bodyFontFamily:
|
|
351
|
-
titleFontFamily:
|
|
418
|
+
bodyFontFamily: CRIMSON,
|
|
419
|
+
titleFontFamily: CORMORANT,
|
|
352
420
|
titleWeight: 'bold',
|
|
353
421
|
lineHeight: 1.5,
|
|
354
422
|
titleLineHeight: 1.2,
|
|
@@ -405,8 +473,8 @@ const warmEarth: Theme = {
|
|
|
405
473
|
warning: '#e53e3e',
|
|
406
474
|
},
|
|
407
475
|
typography: {
|
|
408
|
-
bodyFontFamily:
|
|
409
|
-
titleFontFamily:
|
|
476
|
+
bodyFontFamily: HANKEN_GROTESK,
|
|
477
|
+
titleFontFamily: PT_SERIF,
|
|
410
478
|
titleWeight: 'bold',
|
|
411
479
|
lineHeight: 1.6,
|
|
412
480
|
},
|
|
@@ -448,6 +516,7 @@ const warmEarth: Theme = {
|
|
|
448
516
|
* All built-in themes, keyed by id.
|
|
449
517
|
*/
|
|
450
518
|
export const THEMES: Record<string, Theme> = {
|
|
519
|
+
standard,
|
|
451
520
|
documentary,
|
|
452
521
|
minimalist,
|
|
453
522
|
bold,
|
|
@@ -459,10 +528,10 @@ export const THEMES: Record<string, Theme> = {
|
|
|
459
528
|
};
|
|
460
529
|
|
|
461
530
|
/** The default theme id. */
|
|
462
|
-
export const DEFAULT_THEME_ID = '
|
|
531
|
+
export const DEFAULT_THEME_ID = 'standard';
|
|
463
532
|
|
|
464
|
-
/** The default theme (
|
|
465
|
-
export const DEFAULT_THEME: Theme =
|
|
533
|
+
/** The default theme (standard — safe system fonts, no external dependencies). */
|
|
534
|
+
export const DEFAULT_THEME: Theme = standard;
|
|
466
535
|
|
|
467
536
|
/**
|
|
468
537
|
* Resolve a theme by id. Returns the default theme when `id` is
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply Transform
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for the transform module. Takes a Doc and a style,
|
|
5
|
+
* analyzes block content, selects extractions to promote, and returns
|
|
6
|
+
* a new Doc with blocks optimized for visual presentation.
|
|
7
|
+
*
|
|
8
|
+
* The input Doc is never mutated — a fresh Doc is always returned.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Doc } from '../schemas/Doc.js';
|
|
12
|
+
import type { TransformStyleId, TransformOptions, TransformResult } from './types.js';
|
|
13
|
+
import { resolveTransformStyle } from './registry.js';
|
|
14
|
+
import { analyzeBlocks, extractDocImages } from './blockAnalyzer.js';
|
|
15
|
+
import { selectAndBuild } from './templateSelector.js';
|
|
16
|
+
import { allocateTiming } from './timingAllocator.js';
|
|
17
|
+
import { hashString } from '../random/SeededRandom.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Apply a transform style to a Doc, producing a new Doc with blocks
|
|
21
|
+
* optimized for visual presentation.
|
|
22
|
+
*
|
|
23
|
+
* @param doc - The source Doc (not mutated).
|
|
24
|
+
* @param styleId - Registered transform style id (e.g. 'documentary', 'magazine').
|
|
25
|
+
* @param options - Optional seed, images, theme override.
|
|
26
|
+
* @returns TransformResult with the new Doc and stats.
|
|
27
|
+
*/
|
|
28
|
+
export function applyTransform(
|
|
29
|
+
doc: Doc,
|
|
30
|
+
styleId: TransformStyleId,
|
|
31
|
+
options?: TransformOptions,
|
|
32
|
+
): TransformResult {
|
|
33
|
+
const baseConfig = resolveTransformStyle(styleId);
|
|
34
|
+
const config = options?.overrides
|
|
35
|
+
? { ...baseConfig, ...options.overrides, id: baseConfig.id }
|
|
36
|
+
: baseConfig;
|
|
37
|
+
|
|
38
|
+
const seed = options?.seed ?? hashString(doc.articleId || 'transform');
|
|
39
|
+
|
|
40
|
+
// Use provided images or auto-extract from the doc's markdown content
|
|
41
|
+
const images = options?.images ?? extractDocImages(doc.blocks);
|
|
42
|
+
|
|
43
|
+
// 1. Analyze blocks
|
|
44
|
+
const analyzed = analyzeBlocks(doc.blocks, {
|
|
45
|
+
minConfidence: config.minConfidence,
|
|
46
|
+
types: config.preferredTypes,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// If no blocks have meaningful content, return the doc unchanged
|
|
50
|
+
const hasContent = analyzed.some((ab) => ab.bodyWordCount >= 5);
|
|
51
|
+
if (!hasContent) {
|
|
52
|
+
return {
|
|
53
|
+
doc: { ...doc },
|
|
54
|
+
stats: {
|
|
55
|
+
totalInputBlocks: doc.blocks.length,
|
|
56
|
+
transformedBlocks: 0,
|
|
57
|
+
insertedBlocks: 0,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Select extractions and build template blocks
|
|
63
|
+
const selection = selectAndBuild(analyzed, config, images, seed);
|
|
64
|
+
|
|
65
|
+
// 3. Allocate timing across the new block sequence
|
|
66
|
+
const timedBlocks = allocateTiming(selection.blocks, doc.duration);
|
|
67
|
+
|
|
68
|
+
// 4. Assemble the output Doc
|
|
69
|
+
const transformedDoc: Doc = {
|
|
70
|
+
...doc,
|
|
71
|
+
blocks: timedBlocks,
|
|
72
|
+
themeId: options?.themeId ?? doc.themeId,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
doc: transformedDoc,
|
|
77
|
+
stats: {
|
|
78
|
+
totalInputBlocks: doc.blocks.length,
|
|
79
|
+
transformedBlocks: selection.transformedCount,
|
|
80
|
+
insertedBlocks: selection.insertedCount,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|