@segnavia/format 0.3.0 → 0.5.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/book.d.ts ADDED
@@ -0,0 +1,274 @@
1
+ import { z } from "zod";
2
+ //#region src/book.d.ts
3
+ export declare const BookMode: z.ZodEnum<{
4
+ continuous: "continuous";
5
+ paginated: "paginated";
6
+ }>;
7
+ export type BookMode = z.infer<typeof BookMode>;
8
+ export declare const BookSection: z.ZodObject<{
9
+ title: z.ZodOptional<z.ZodString>;
10
+ html: z.ZodString;
11
+ contents: z.ZodDefault<z.ZodBoolean>;
12
+ }, z.core.$strip>;
13
+ export type BookSection = z.infer<typeof BookSection>;
14
+ export declare const BookContents: z.ZodObject<{
15
+ kind: z.ZodLiteral<"contents">;
16
+ title: z.ZodOptional<z.ZodString>;
17
+ }, z.core.$strip>;
18
+ export type BookContents = z.infer<typeof BookContents>;
19
+ export declare const BookFrontMatterEntry: z.ZodUnion<readonly [z.ZodObject<{
20
+ title: z.ZodOptional<z.ZodString>;
21
+ html: z.ZodString;
22
+ contents: z.ZodDefault<z.ZodBoolean>;
23
+ }, z.core.$strip>, z.ZodObject<{
24
+ kind: z.ZodLiteral<"contents">;
25
+ title: z.ZodOptional<z.ZodString>;
26
+ }, z.core.$strip>]>;
27
+ export type BookFrontMatterEntry = z.infer<typeof BookFrontMatterEntry>;
28
+ export declare function isBookContents(entry: BookFrontMatterEntry): entry is BookContents;
29
+ export declare const Chapter: z.ZodObject<{
30
+ title: z.ZodString;
31
+ html: z.ZodString;
32
+ runningHead: z.ZodOptional<z.ZodString>;
33
+ }, z.core.$strip>;
34
+ export type Chapter = z.infer<typeof Chapter>;
35
+ export declare const BookPage: z.ZodObject<{
36
+ paper: z.ZodUnion<readonly [z.ZodEnum<{
37
+ letter: "letter";
38
+ legal: "legal";
39
+ tabloid: "tabloid";
40
+ a3: "a3";
41
+ a4: "a4";
42
+ a5: "a5";
43
+ }>, z.ZodObject<{
44
+ width: z.ZodNumber;
45
+ height: z.ZodNumber;
46
+ unit: z.ZodEnum<{
47
+ mm: "mm";
48
+ in: "in";
49
+ pt: "pt";
50
+ }>;
51
+ }, z.core.$strip>]>;
52
+ orientation: z.ZodDefault<z.ZodEnum<{
53
+ portrait: "portrait";
54
+ landscape: "landscape";
55
+ }>>;
56
+ margin: z.ZodObject<{
57
+ top: z.ZodNumber;
58
+ bottom: z.ZodNumber;
59
+ inner: z.ZodNumber;
60
+ outer: z.ZodNumber;
61
+ }, z.core.$strip>;
62
+ }, z.core.$strip>;
63
+ export type BookPage = z.infer<typeof BookPage>;
64
+ export declare const Book: z.ZodPipe<z.ZodObject<{
65
+ title: z.ZodString;
66
+ mode: z.ZodEnum<{
67
+ continuous: "continuous";
68
+ paginated: "paginated";
69
+ }>;
70
+ page: z.ZodOptional<z.ZodObject<{
71
+ paper: z.ZodUnion<readonly [z.ZodEnum<{
72
+ letter: "letter";
73
+ legal: "legal";
74
+ tabloid: "tabloid";
75
+ a3: "a3";
76
+ a4: "a4";
77
+ a5: "a5";
78
+ }>, z.ZodObject<{
79
+ width: z.ZodNumber;
80
+ height: z.ZodNumber;
81
+ unit: z.ZodEnum<{
82
+ mm: "mm";
83
+ in: "in";
84
+ pt: "pt";
85
+ }>;
86
+ }, z.core.$strip>]>;
87
+ orientation: z.ZodDefault<z.ZodEnum<{
88
+ portrait: "portrait";
89
+ landscape: "landscape";
90
+ }>>;
91
+ margin: z.ZodObject<{
92
+ top: z.ZodNumber;
93
+ bottom: z.ZodNumber;
94
+ inner: z.ZodNumber;
95
+ outer: z.ZodNumber;
96
+ }, z.core.$strip>;
97
+ }, z.core.$strip>>;
98
+ scripts: z.ZodDefault<z.ZodEnum<{
99
+ refuse: "refuse";
100
+ sandboxed: "sandboxed";
101
+ }>>;
102
+ dir: z.ZodOptional<z.ZodEnum<{
103
+ ltr: "ltr";
104
+ rtl: "rtl";
105
+ auto: "auto";
106
+ }>>;
107
+ lang: z.ZodOptional<z.ZodString>;
108
+ frontMatter: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
109
+ title: z.ZodOptional<z.ZodString>;
110
+ html: z.ZodString;
111
+ contents: z.ZodDefault<z.ZodBoolean>;
112
+ }, z.core.$strip>, z.ZodObject<{
113
+ kind: z.ZodLiteral<"contents">;
114
+ title: z.ZodOptional<z.ZodString>;
115
+ }, z.core.$strip>]>>>;
116
+ chapters: z.ZodArray<z.ZodObject<{
117
+ title: z.ZodString;
118
+ html: z.ZodString;
119
+ runningHead: z.ZodOptional<z.ZodString>;
120
+ }, z.core.$strip>>;
121
+ numbering: z.ZodDefault<z.ZodObject<{
122
+ frontMatter: z.ZodOptional<z.ZodEnum<{
123
+ arabic: "arabic";
124
+ "roman-lower": "roman-lower";
125
+ "roman-upper": "roman-upper";
126
+ hebrew: "hebrew";
127
+ }>>;
128
+ body: z.ZodOptional<z.ZodEnum<{
129
+ arabic: "arabic";
130
+ "roman-lower": "roman-lower";
131
+ "roman-upper": "roman-upper";
132
+ hebrew: "hebrew";
133
+ }>>;
134
+ }, z.core.$strip>>;
135
+ footnotes: z.ZodDefault<z.ZodObject<{
136
+ marker: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
137
+ arabic: "arabic";
138
+ "roman-lower": "roman-lower";
139
+ "roman-upper": "roman-upper";
140
+ hebrew: "hebrew";
141
+ }>, z.ZodLiteral<"symbols">]>>;
142
+ restart: z.ZodDefault<z.ZodEnum<{
143
+ chapter: "chapter";
144
+ book: "book";
145
+ }>>;
146
+ }, z.core.$strip>>;
147
+ endnotes: z.ZodDefault<z.ZodObject<{
148
+ marker: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
149
+ arabic: "arabic";
150
+ "roman-lower": "roman-lower";
151
+ "roman-upper": "roman-upper";
152
+ hebrew: "hebrew";
153
+ }>, z.ZodLiteral<"symbols">]>>;
154
+ placement: z.ZodDefault<z.ZodEnum<{
155
+ chapter: "chapter";
156
+ book: "book";
157
+ }>>;
158
+ title: z.ZodOptional<z.ZodString>;
159
+ }, z.core.$strip>>;
160
+ rectoOpenings: z.ZodDefault<z.ZodBoolean>;
161
+ contents: z.ZodDefault<z.ZodObject<{
162
+ pageNumbers: z.ZodDefault<z.ZodBoolean>;
163
+ depth: z.ZodDefault<z.ZodNumber>;
164
+ }, z.core.$strip>>;
165
+ }, z.core.$strip>, z.ZodTransform<{
166
+ numbering: {
167
+ frontMatter: "arabic" | "roman-lower" | "roman-upper" | "hebrew";
168
+ body: "arabic" | "roman-lower" | "roman-upper" | "hebrew";
169
+ };
170
+ footnotes: {
171
+ marker: "arabic" | "roman-lower" | "roman-upper" | "hebrew" | "symbols";
172
+ restart: "chapter" | "book";
173
+ };
174
+ endnotes: {
175
+ marker: "arabic" | "roman-lower" | "roman-upper" | "hebrew" | "symbols";
176
+ title: string;
177
+ placement: "chapter" | "book";
178
+ };
179
+ title: string;
180
+ mode: "continuous" | "paginated";
181
+ scripts: "refuse" | "sandboxed";
182
+ frontMatter: ({
183
+ html: string;
184
+ contents: boolean;
185
+ title?: string | undefined;
186
+ } | {
187
+ kind: "contents";
188
+ title?: string | undefined;
189
+ })[];
190
+ chapters: {
191
+ title: string;
192
+ html: string;
193
+ runningHead?: string | undefined;
194
+ }[];
195
+ rectoOpenings: boolean;
196
+ contents: {
197
+ pageNumbers: boolean;
198
+ depth: number;
199
+ };
200
+ page?: {
201
+ paper: "letter" | "legal" | "tabloid" | "a3" | "a4" | "a5" | {
202
+ width: number;
203
+ height: number;
204
+ unit: "mm" | "in" | "pt";
205
+ };
206
+ orientation: "portrait" | "landscape";
207
+ margin: {
208
+ top: number;
209
+ bottom: number;
210
+ inner: number;
211
+ outer: number;
212
+ };
213
+ } | undefined;
214
+ dir?: "ltr" | "rtl" | "auto" | undefined;
215
+ lang?: string | undefined;
216
+ }, {
217
+ title: string;
218
+ mode: "continuous" | "paginated";
219
+ scripts: "refuse" | "sandboxed";
220
+ frontMatter: ({
221
+ html: string;
222
+ contents: boolean;
223
+ title?: string | undefined;
224
+ } | {
225
+ kind: "contents";
226
+ title?: string | undefined;
227
+ })[];
228
+ chapters: {
229
+ title: string;
230
+ html: string;
231
+ runningHead?: string | undefined;
232
+ }[];
233
+ numbering: {
234
+ frontMatter?: "arabic" | "roman-lower" | "roman-upper" | "hebrew" | undefined;
235
+ body?: "arabic" | "roman-lower" | "roman-upper" | "hebrew" | undefined;
236
+ };
237
+ footnotes: {
238
+ restart: "chapter" | "book";
239
+ marker?: "arabic" | "roman-lower" | "roman-upper" | "hebrew" | "symbols" | undefined;
240
+ };
241
+ endnotes: {
242
+ placement: "chapter" | "book";
243
+ marker?: "arabic" | "roman-lower" | "roman-upper" | "hebrew" | "symbols" | undefined;
244
+ title?: string | undefined;
245
+ };
246
+ rectoOpenings: boolean;
247
+ contents: {
248
+ pageNumbers: boolean;
249
+ depth: number;
250
+ };
251
+ page?: {
252
+ paper: "letter" | "legal" | "tabloid" | "a3" | "a4" | "a5" | {
253
+ width: number;
254
+ height: number;
255
+ unit: "mm" | "in" | "pt";
256
+ };
257
+ orientation: "portrait" | "landscape";
258
+ margin: {
259
+ top: number;
260
+ bottom: number;
261
+ inner: number;
262
+ outer: number;
263
+ };
264
+ } | undefined;
265
+ dir?: "ltr" | "rtl" | "auto" | undefined;
266
+ lang?: string | undefined;
267
+ }>>;
268
+ export type Book = z.infer<typeof Book>;
269
+ export type BookInput = z.input<typeof Book>;
270
+ export declare function noteMarkerIssues(book: Book, used: {
271
+ footnotes: boolean;
272
+ endnotes: boolean;
273
+ }): string[];
274
+ //#endregion
package/dist/book.js ADDED
@@ -0,0 +1,131 @@
1
+ import { Direction } from "./direction.js";
2
+ import { NoteMarker, NumberSystem } from "./numbering.js";
3
+ import { Paper, sheetMm } from "./paper.js";
4
+ import { Scripts } from "./scripts.js";
5
+ import { z } from "zod";
6
+ //#region src/book.ts
7
+ const BookMode = z.enum(["continuous", "paginated"]);
8
+ const BookSection = z.object({
9
+ title: z.string().optional(),
10
+ html: z.string(),
11
+ contents: z.boolean().default(true)
12
+ });
13
+ const BookContents = z.object({
14
+ kind: z.literal("contents"),
15
+ title: z.string().optional()
16
+ });
17
+ const BookFrontMatterEntry = z.union([BookSection, BookContents]);
18
+ function isBookContents(entry) {
19
+ return "kind" in entry && entry.kind === "contents";
20
+ }
21
+ const Chapter = z.object({
22
+ title: z.string(),
23
+ html: z.string(),
24
+ runningHead: z.string().optional()
25
+ });
26
+ const BookPage = z.object({
27
+ paper: Paper,
28
+ orientation: z.enum(["portrait", "landscape"]).default("portrait"),
29
+ margin: z.object({
30
+ top: z.number().nonnegative(),
31
+ bottom: z.number().nonnegative(),
32
+ inner: z.number().nonnegative(),
33
+ outer: z.number().nonnegative()
34
+ })
35
+ });
36
+ const BookShape = z.object({
37
+ title: z.string(),
38
+ mode: BookMode,
39
+ page: BookPage.optional(),
40
+ scripts: Scripts.default("refuse"),
41
+ dir: Direction.optional(),
42
+ lang: z.string().optional(),
43
+ frontMatter: z.array(BookFrontMatterEntry).default([]),
44
+ chapters: z.array(Chapter).min(1),
45
+ numbering: z.object({
46
+ frontMatter: NumberSystem.optional(),
47
+ body: NumberSystem.optional()
48
+ }).default({}),
49
+ footnotes: z.object({
50
+ marker: NoteMarker.optional(),
51
+ restart: z.enum(["chapter", "book"]).default("chapter")
52
+ }).default({ restart: "chapter" }),
53
+ endnotes: z.object({
54
+ marker: NoteMarker.optional(),
55
+ placement: z.enum(["book", "chapter"]).default("book"),
56
+ title: z.string().optional()
57
+ }).default({ placement: "book" }),
58
+ rectoOpenings: z.boolean().default(false),
59
+ contents: z.object({
60
+ pageNumbers: z.boolean().default(true),
61
+ depth: z.number().int().min(1).max(6).default(2)
62
+ }).default({
63
+ pageNumbers: true,
64
+ depth: 2
65
+ })
66
+ });
67
+ function isHebrew(lang) {
68
+ const primary = lang?.split("-")[0]?.toLowerCase();
69
+ return primary === "he" || primary === "iw";
70
+ }
71
+ const Book = BookShape.transform((book) => {
72
+ const hebrew = isHebrew(book.lang);
73
+ return {
74
+ ...book,
75
+ numbering: {
76
+ frontMatter: book.numbering.frontMatter ?? (hebrew ? "arabic" : "roman-lower"),
77
+ body: book.numbering.body ?? (hebrew ? "hebrew" : "arabic")
78
+ },
79
+ footnotes: {
80
+ ...book.footnotes,
81
+ marker: book.footnotes.marker ?? (hebrew ? "hebrew" : "symbols")
82
+ },
83
+ endnotes: {
84
+ ...book.endnotes,
85
+ marker: book.endnotes.marker ?? "arabic",
86
+ title: book.endnotes.title ?? (hebrew ? "הערות" : "Notes")
87
+ }
88
+ };
89
+ }).superRefine((book, ctx) => {
90
+ const contentsPositions = book.frontMatter.flatMap((entry, index) => isBookContents(entry) ? [index + 1] : []);
91
+ if (contentsPositions.length > 1) ctx.addIssue({
92
+ code: "custom",
93
+ path: ["frontMatter"],
94
+ message: `front-matter entries at positions ${contentsPositions[0]} and ${contentsPositions[1]} are both contents entries; one must go`
95
+ });
96
+ if (book.mode === "paginated" && !book.page) ctx.addIssue({
97
+ code: "custom",
98
+ path: ["page"],
99
+ message: "a paginated book needs page; set page or use continuous mode"
100
+ });
101
+ if (book.frontMatter.length > 0 && book.numbering.frontMatter === book.numbering.body) ctx.addIssue({
102
+ code: "custom",
103
+ path: ["numbering"],
104
+ message: `front-matter and body numbering are both ${book.numbering.body}; the two must differ when front matter is present`
105
+ });
106
+ if (book.endnotes.placement === "chapter" && book.mode === "continuous") ctx.addIssue({
107
+ code: "custom",
108
+ path: ["endnotes", "placement"],
109
+ message: "endnotes are placed at the end of each chapter only in paginated mode; use book placement or paginated mode"
110
+ });
111
+ if (!book.page) return;
112
+ const trim = sheetMm(book.page);
113
+ const vertical = book.page.margin.top + book.page.margin.bottom;
114
+ const horizontal = book.page.margin.inner + book.page.margin.outer;
115
+ if (vertical >= trim.height) ctx.addIssue({
116
+ code: "custom",
117
+ path: ["page", "margin"],
118
+ message: `the top and bottom margins total ${vertical}mm and the page is ${trim.height}mm high; reduce the vertical margins to leave a text area`
119
+ });
120
+ if (horizontal >= trim.width) ctx.addIssue({
121
+ code: "custom",
122
+ path: ["page", "margin"],
123
+ message: `the inner and outer margins total ${horizontal}mm and the page is ${trim.width}mm wide; reduce the horizontal margins to leave a text area`
124
+ });
125
+ });
126
+ function noteMarkerIssues(book, used) {
127
+ if (!used.footnotes || !used.endnotes || book.footnotes.marker !== book.endnotes.marker) return [];
128
+ return [`footnote and endnote markers are both ${book.footnotes.marker}; the two must differ when both kinds are used`];
129
+ }
130
+ //#endregion
131
+ export { Book, BookContents, BookFrontMatterEntry, BookMode, BookPage, BookSection, BookShape, Chapter, isBookContents, noteMarkerIssues };
@@ -0,0 +1,116 @@
1
+ import { DeckInput } from "./deck.js";
2
+ //#region src/deck-from-slides.d.ts
3
+ type SlideInput = DeckInput['slides'][number];
4
+ type SlideFields = Omit<SlideInput, 'content'>;
5
+ type ShotContent = Extract<SlideInput['content'], {
6
+ kind: 'shot';
7
+ }>;
8
+ export type SlideRow = SlideFields & ({
9
+ kind: 'html';
10
+ html: string;
11
+ } | ShotContent);
12
+ export type DeckFromSlidesOptions = Omit<DeckInput, 'slides'> & {
13
+ omitNotes?: boolean;
14
+ };
15
+ export declare function deckFromSlides(rows: SlideRow[], options: DeckFromSlidesOptions): {
16
+ title: string;
17
+ size: {
18
+ width: number;
19
+ height: number;
20
+ };
21
+ scripts: "refuse" | "sandboxed";
22
+ slides: {
23
+ id: string;
24
+ content: {
25
+ kind: "html";
26
+ html: string;
27
+ } | {
28
+ kind: "url";
29
+ src: string;
30
+ } | {
31
+ kind: "shot";
32
+ src: string;
33
+ shot: {
34
+ key: string;
35
+ title: string;
36
+ description: string | null;
37
+ tags: string[];
38
+ file: string;
39
+ annotatedFile: string | null;
40
+ size: {
41
+ width: number;
42
+ height: number;
43
+ };
44
+ scale: number;
45
+ dir: "ltr" | "rtl";
46
+ annotations: {
47
+ ring: {
48
+ x: number;
49
+ y: number;
50
+ width: number;
51
+ height: number;
52
+ } | null;
53
+ badge: {
54
+ center: {
55
+ x: number;
56
+ y: number;
57
+ };
58
+ number: number;
59
+ } | null;
60
+ arrow: {
61
+ head: {
62
+ x: number;
63
+ y: number;
64
+ };
65
+ tail: {
66
+ x: number;
67
+ y: number;
68
+ };
69
+ label: string | null;
70
+ } | null;
71
+ }[];
72
+ capturedAt: string;
73
+ meta: Record<string, unknown>;
74
+ };
75
+ } | {
76
+ kind: "shot-ref";
77
+ href: string;
78
+ } | {
79
+ kind: "image";
80
+ src: string;
81
+ alt: string;
82
+ };
83
+ notes?: string | undefined;
84
+ narration?: {
85
+ text: string;
86
+ words: {
87
+ text: string;
88
+ startMs: number;
89
+ endMs: number;
90
+ }[];
91
+ durationMs: number;
92
+ audio: string | null;
93
+ live: boolean;
94
+ cues: {
95
+ word: number;
96
+ annotation: number;
97
+ untilWord?: number | undefined;
98
+ }[];
99
+ lang?: string | undefined;
100
+ dir?: "ltr" | "rtl" | "auto" | undefined;
101
+ voice?: string | undefined;
102
+ } | undefined;
103
+ beforeMs?: number | undefined;
104
+ afterMs?: number | undefined;
105
+ holdMs?: number | undefined;
106
+ }[];
107
+ dir?: "ltr" | "rtl" | "auto" | undefined;
108
+ lang?: string | undefined;
109
+ transition?: {
110
+ kind: "none" | "fade" | "slide";
111
+ duration: number;
112
+ } | undefined;
113
+ playback?: "continuous" | "step" | undefined;
114
+ captions?: "on" | "off" | "viewer" | undefined;
115
+ };
116
+ //#endregion
@@ -0,0 +1,36 @@
1
+ import { Deck } from "./deck.js";
2
+ //#region src/deck-from-slides.ts
3
+ function deckFromSlides(rows, options) {
4
+ const { omitNotes = false, ...deckOptions } = options;
5
+ const slides = rows.map(({ kind, ...row }) => {
6
+ const { id, notes, narration, beforeMs, afterMs, holdMs } = row;
7
+ const common = {
8
+ id,
9
+ ...!omitNotes && notes !== void 0 ? { notes } : {},
10
+ ...narration !== void 0 ? { narration } : {},
11
+ ...beforeMs !== void 0 ? { beforeMs } : {},
12
+ ...afterMs !== void 0 ? { afterMs } : {},
13
+ ...holdMs !== void 0 ? { holdMs } : {}
14
+ };
15
+ return kind === "html" ? {
16
+ ...common,
17
+ content: {
18
+ kind,
19
+ html: row.html
20
+ }
21
+ } : {
22
+ ...common,
23
+ content: {
24
+ kind,
25
+ src: row.src,
26
+ shot: row.shot
27
+ }
28
+ };
29
+ });
30
+ return Deck.parse({
31
+ ...deckOptions,
32
+ slides
33
+ });
34
+ }
35
+ //#endregion
36
+ export { deckFromSlides };