@notatki/preview 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aliaksandr Radzivanovich
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,13 @@
1
+ import { type Model, type ModelCard, type Note } from "@notatki/core";
2
+ export declare class CardData {
3
+ #private;
4
+ constructor(model: Model, card: ModelCard, note: Note);
5
+ get model(): Model;
6
+ get card(): ModelCard;
7
+ get note(): Note;
8
+ hasValue(name: string): boolean;
9
+ getValue(name: string): string;
10
+ clearValue(name: string): void;
11
+ setValue(name: string, value: string): void;
12
+ }
13
+ //# sourceMappingURL=card-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-data.d.ts","sourceRoot":"","sources":["../src/card-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAMtE,qBAAa,QAAQ;;gBAMP,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI;IAkBrD,IAAI,KAAK,IAAI,KAAK,CAEjB;IAED,IAAI,IAAI,IAAI,SAAS,CAEpB;IAED,IAAI,IAAI,IAAI,IAAI,CAEf;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAQ9B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;CAG5C"}
@@ -0,0 +1,53 @@
1
+ import {} from "@notatki/core";
2
+ import { formatField, renderHtml } from "@notatki/format";
3
+ import { escapeHtml } from "./html.js";
4
+ const html = renderHtml({ output: "html", throwOnError: false });
5
+ export class CardData {
6
+ #model;
7
+ #card;
8
+ #note;
9
+ #data;
10
+ constructor(model, card, note) {
11
+ this.#model = model;
12
+ this.#card = card;
13
+ this.#note = note;
14
+ this.#data = new Map();
15
+ // Build-in fields.
16
+ this.setValue("Type", escapeHtml(note.type.name));
17
+ this.setValue("Card", escapeHtml(card.name));
18
+ this.setValue("Deck", escapeHtml(note.deck));
19
+ this.setValue("Subdeck", escapeHtml(note.deck.split("::").pop() ?? note.deck));
20
+ this.setValue("Tags", escapeHtml(note.tags));
21
+ this.setValue("Flags", escapeHtml("Flags"));
22
+ // User-defined fields.
23
+ for (const { name, value } of note) {
24
+ this.setValue(name, formatField(value, html));
25
+ }
26
+ }
27
+ get model() {
28
+ return this.#model;
29
+ }
30
+ get card() {
31
+ return this.#card;
32
+ }
33
+ get note() {
34
+ return this.#note;
35
+ }
36
+ hasValue(name) {
37
+ return this.#data.get(name) != null;
38
+ }
39
+ getValue(name) {
40
+ const value = this.#data.get(name);
41
+ if (value == null) {
42
+ throw new Error(`Unknown field: ${this.#note.type.name}::${name}`);
43
+ }
44
+ return value;
45
+ }
46
+ clearValue(name) {
47
+ this.#data.delete(name);
48
+ }
49
+ setValue(name, value) {
50
+ this.#data.set(name, value);
51
+ }
52
+ }
53
+ //# sourceMappingURL=card-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-data.js","sourceRoot":"","sources":["../src/card-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyC,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;AAEjE,MAAM,OAAO,QAAQ;IACV,MAAM,CAAQ;IACd,KAAK,CAAY;IACjB,KAAK,CAAO;IACZ,KAAK,CAAsB;IAEpC,YAAY,KAAY,EAAE,IAAe,EAAE,IAAU;QACnD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,mBAAmB;QACnB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5C,uBAAuB;QACvB,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IACtC,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,QAAQ,CAAC,IAAY,EAAE,KAAa;QAClC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import { type ModelMap } from "@notatki/core";
2
+ import { type CardData } from "./card-data.js";
3
+ export declare class CardTemplates {
4
+ #private;
5
+ constructor(models: ModelMap);
6
+ render(data: CardData, side: "front" | "back"): string;
7
+ }
8
+ //# sourceMappingURL=card-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-templates.d.ts","sourceRoot":"","sources":["../src/card-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE1E,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qBAAa,aAAa;;gBAGZ,MAAM,EAAE,QAAQ;IAQ5B,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM;CAQvD"}
@@ -0,0 +1,128 @@
1
+ import {} from "@notatki/core";
2
+ import { parseTemplate } from "@notatki/parser";
3
+ import {} from "./card-data.js";
4
+ export class CardTemplates {
5
+ #map = new Map();
6
+ constructor(models) {
7
+ for (const model of models) {
8
+ for (const card of model.cards) {
9
+ this.#map.set(cardKey(model, card), new CardTemplate(card));
10
+ }
11
+ }
12
+ }
13
+ render(data, side) {
14
+ const key = cardKey(data.note.type, data.card);
15
+ const card = this.#map.get(key);
16
+ if (card == null) {
17
+ throw new Error(`Unknown card: ${key}`);
18
+ }
19
+ return card.render(data, side);
20
+ }
21
+ }
22
+ function cardKey(model, card) {
23
+ return `${model.name}::${card.name}`;
24
+ }
25
+ class CardTemplate {
26
+ #front;
27
+ #back;
28
+ constructor(card) {
29
+ this.#front = new Template(card.front);
30
+ this.#back = new Template(card.back);
31
+ }
32
+ render(data, side) {
33
+ switch (side) {
34
+ case "front":
35
+ return this.renderFront(data);
36
+ case "back":
37
+ return this.renderBack(data);
38
+ }
39
+ }
40
+ renderFront(data) {
41
+ data.clearValue("FrontSide");
42
+ return this.#front.render(this, data);
43
+ }
44
+ renderBack(data) {
45
+ data.setValue("FrontSide", this.renderFront(data));
46
+ return this.#back.render(this, data);
47
+ }
48
+ getValue(data, field) {
49
+ switch (field) {
50
+ case "cloze:Text": {
51
+ return data.getValue("Text");
52
+ }
53
+ }
54
+ return data.getValue(field);
55
+ }
56
+ }
57
+ class Template {
58
+ #parts;
59
+ constructor(template) {
60
+ this.#parts = mapNodeList(parseTemplate(template));
61
+ }
62
+ render(values, data) {
63
+ let out = "";
64
+ function visit(parts) {
65
+ for (const part of parts) {
66
+ if (typeof part === "string") {
67
+ out += part;
68
+ }
69
+ else {
70
+ switch (part.type) {
71
+ case "field": {
72
+ out += values.getValue(data, part.field);
73
+ break;
74
+ }
75
+ case "if": {
76
+ if (values.getValue(data, part.field)) {
77
+ visit(part.parts);
78
+ }
79
+ break;
80
+ }
81
+ case "if-not": {
82
+ if (!values.getValue(data, part.field)) {
83
+ visit(part.parts);
84
+ }
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ visit(this.#parts);
92
+ return out;
93
+ }
94
+ }
95
+ function mapNodeList(nodes) {
96
+ return nodes.map((node) => mapNode(node));
97
+ }
98
+ function mapNode(node) {
99
+ switch (node.type) {
100
+ case "text": {
101
+ return node.text;
102
+ }
103
+ case "field": {
104
+ return {
105
+ type: "field",
106
+ field: node.field.text,
107
+ };
108
+ }
109
+ case "branch": {
110
+ const { cond } = node;
111
+ if (cond.not) {
112
+ return {
113
+ type: "if-not",
114
+ field: cond.field.text,
115
+ parts: mapNodeList(node.items),
116
+ };
117
+ }
118
+ else {
119
+ return {
120
+ type: "if",
121
+ field: cond.field.text,
122
+ parts: mapNodeList(node.items),
123
+ };
124
+ }
125
+ }
126
+ }
127
+ }
128
+ //# sourceMappingURL=card-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-templates.js","sourceRoot":"","sources":["../src/card-templates.ts"],"names":[],"mappings":"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"}
package/dist/html.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function escapeHtml(text: string): string;
2
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C"}
package/dist/html.js ADDED
@@ -0,0 +1,9 @@
1
+ export function escapeHtml(text) {
2
+ return String(text)
3
+ .replace(/</g, "&lt;")
4
+ .replace(/>/g, "&gt;")
5
+ .replace(/&/g, "&amp;")
6
+ .replace(/"/g, "&quot;")
7
+ .replace(/'/g, "&apos;");
8
+ }
9
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC;SAChB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from "./card-data.js";
2
+ export * from "./card-templates.js";
3
+ export * from "./html.js";
4
+ export * from "./preview.js";
5
+ export * from "./preview-options.js";
6
+ export * from "./preview-renderer.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./card-data.js";
2
+ export * from "./card-templates.js";
3
+ export * from "./html.js";
4
+ export * from "./preview.js";
5
+ export * from "./preview-options.js";
6
+ export * from "./preview-renderer.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type PreviewOptions = {
2
+ title: string;
3
+ showDetails: boolean;
4
+ showFront: boolean;
5
+ showBack: boolean;
6
+ };
7
+ export declare function expandPreviewOptions(options: Partial<Readonly<PreviewOptions>>): PreviewOptions;
8
+ //# sourceMappingURL=preview-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-options.d.ts","sourceRoot":"","sources":["../src/preview-options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,GAAG,cAAc,CAa/F"}
@@ -0,0 +1,11 @@
1
+ export function expandPreviewOptions(options) {
2
+ const { title = "Cards Preview", //
3
+ showDetails = true, showFront = false, showBack = true, } = options;
4
+ return {
5
+ title, //
6
+ showDetails,
7
+ showFront,
8
+ showBack,
9
+ };
10
+ }
11
+ //# sourceMappingURL=preview-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-options.js","sourceRoot":"","sources":["../src/preview-options.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,oBAAoB,CAAC,OAA0C;IAC7E,MAAM,EACJ,KAAK,GAAG,eAAe,EAAE,EAAE;IAC3B,WAAW,GAAG,IAAI,EAClB,SAAS,GAAG,KAAK,EACjB,QAAQ,GAAG,IAAI,GAChB,GAAG,OAAO,CAAC;IACZ,OAAO;QACL,KAAK,EAAE,EAAE;QACT,WAAW;QACX,SAAS;QACT,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type Note, type NoteList, type Output } from "@notatki/core";
2
+ import { CardData } from "./card-data.js";
3
+ import { type CardTemplates } from "./card-templates.js";
4
+ import { type PreviewOptions } from "./preview-options.js";
5
+ export type RendererContext = Readonly<{
6
+ options: Readonly<PreviewOptions>;
7
+ templates: CardTemplates;
8
+ notes: NoteList;
9
+ out: Output;
10
+ }>;
11
+ export declare class PreviewRenderer<Context extends RendererContext = RendererContext> {
12
+ #private;
13
+ static readonly defaultStylesheets: readonly string[];
14
+ static readonly defaultStyles: readonly string[];
15
+ get stylesheets(): string[];
16
+ set stylesheets(value: string[]);
17
+ get styles(): string[];
18
+ set styles(value: string[]);
19
+ render(ctx: Context): void;
20
+ renderHead(ctx: Context): void;
21
+ renderStylesheets(ctx: Context): void;
22
+ renderCommonStyles(ctx: Context): void;
23
+ renderModelStyles(ctx: Context): void;
24
+ renderBody(ctx: Context): void;
25
+ renderCardList(ctx: Context): void;
26
+ renderNoteCardList(ctx: Context, note: Note): void;
27
+ renderFrontCard(ctx: Context, data: CardData): void;
28
+ renderBackCard(ctx: Context, data: CardData): void;
29
+ renderCard(ctx: Context, data: CardData, side: "front" | "back"): void;
30
+ renderCardContents(ctx: Context, data: CardData, side: "front" | "back"): string;
31
+ }
32
+ //# sourceMappingURL=preview-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-renderer.d.ts","sourceRoot":"","sources":["../src/preview-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAEH,qBAAa,eAAe,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe;;IAC5E,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAEnD;IAEF,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAW9C;IAKF,IAAI,WAAW,IAAI,MAAM,EAAE,CAE1B;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAE9B;IAED,IAAI,MAAM,IAAI,MAAM,EAAE,CAErB;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAEzB;IAED,MAAM,CAAC,GAAG,EAAE,OAAO;IAQnB,UAAU,CAAC,GAAG,EAAE,OAAO;IAUvB,iBAAiB,CAAC,GAAG,EAAE,OAAO;IAM9B,kBAAkB,CAAC,GAAG,EAAE,OAAO;IAQ/B,iBAAiB,CAAC,GAAG,EAAE,OAAO;IAY9B,UAAU,CAAC,GAAG,EAAE,OAAO;IAMvB,cAAc,CAAC,GAAG,EAAE,OAAO;IAQ3B,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI;IAY3C,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ;IAI5C,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ;IAI3C,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM;IAe/D,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM;CAGjF"}
@@ -0,0 +1,136 @@
1
+ import {} from "@notatki/core";
2
+ import { CardData } from "./card-data.js";
3
+ import {} from "./card-templates.js";
4
+ import { escapeHtml } from "./html.js";
5
+ import {} from "./preview-options.js";
6
+ export class PreviewRenderer {
7
+ static defaultStylesheets = [
8
+ `https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css`, //
9
+ ];
10
+ static defaultStyles = [
11
+ `:root { color-scheme: light dark; }`,
12
+ `.card-list { display: flex; flex-flow: row wrap; justify-content: center; align-items: center; gap: 1rem; }`,
13
+ `.card-list-item { flex: 0 1 auto; min-width: 20rem; padding: 0 1rem; border: 1px dotted #666; }`,
14
+ `.prop { font-size: 0.75em; color: #666; }`,
15
+ `.prop-name { font-weight: bold; font-style: normal; }`,
16
+ `.prop-value { font-weight: normal; font-style: normal; }`,
17
+ `img { max-width: 100%; }`,
18
+ `li { text-align: start; }`,
19
+ `pre { text-align: left; }`,
20
+ `hr { height: 1px; margin: 1em 0; border: none; background-color: #666; }`,
21
+ ];
22
+ #stylesheets = [...PreviewRenderer.defaultStylesheets];
23
+ #styles = [...PreviewRenderer.defaultStyles];
24
+ get stylesheets() {
25
+ return [...this.#stylesheets];
26
+ }
27
+ set stylesheets(value) {
28
+ this.#stylesheets = [...value];
29
+ }
30
+ get styles() {
31
+ return [...this.#styles];
32
+ }
33
+ set styles(value) {
34
+ this.#styles = [...value];
35
+ }
36
+ render(ctx) {
37
+ ctx.out.print(`<!doctype html>`);
38
+ ctx.out.print(`<html>`);
39
+ this.renderHead(ctx);
40
+ this.renderBody(ctx);
41
+ ctx.out.print(`</html>`);
42
+ }
43
+ renderHead(ctx) {
44
+ ctx.out.print(`<head>`);
45
+ ctx.out.print(`<meta charset="UTF-8">`);
46
+ ctx.out.print(`<title>${escapeHtml(ctx.options.title)}</title>`);
47
+ this.renderStylesheets(ctx);
48
+ this.renderCommonStyles(ctx);
49
+ this.renderModelStyles(ctx);
50
+ ctx.out.print(`</head>`);
51
+ }
52
+ renderStylesheets(ctx) {
53
+ for (const stylesheet of this.#stylesheets) {
54
+ ctx.out.print(`<link rel="stylesheet" href="${escapeHtml(stylesheet)}" crossOrigin="anonymous">`);
55
+ }
56
+ }
57
+ renderCommonStyles(ctx) {
58
+ ctx.out.print(`<style>`);
59
+ for (const style of this.#styles) {
60
+ ctx.out.print(style);
61
+ }
62
+ ctx.out.print(`</style>`);
63
+ }
64
+ renderModelStyles(ctx) {
65
+ for (const type of ctx.notes.types) {
66
+ if (type.styling) {
67
+ ctx.out.print(`<style>`);
68
+ ctx.out.print(`[data-type="${type.name}"] {`);
69
+ ctx.out.print(type.styling);
70
+ ctx.out.print(`}`);
71
+ ctx.out.print(`</style>`);
72
+ }
73
+ }
74
+ }
75
+ renderBody(ctx) {
76
+ ctx.out.print(`<body>`);
77
+ this.renderCardList(ctx);
78
+ ctx.out.print(`</body>`);
79
+ }
80
+ renderCardList(ctx) {
81
+ ctx.out.print(`<main class="card-list">`);
82
+ for (const note of ctx.notes) {
83
+ this.renderNoteCardList(ctx, note);
84
+ }
85
+ ctx.out.print(`</main>`);
86
+ }
87
+ renderNoteCardList(ctx, note) {
88
+ for (const card of note.type.cards) {
89
+ const data = new CardData(note.type, card, note);
90
+ if (ctx.options.showFront) {
91
+ this.renderFrontCard(ctx, data);
92
+ }
93
+ if (ctx.options.showBack) {
94
+ this.renderBackCard(ctx, data);
95
+ }
96
+ }
97
+ }
98
+ renderFrontCard(ctx, data) {
99
+ this.renderCard(ctx, data, "front");
100
+ }
101
+ renderBackCard(ctx, data) {
102
+ this.renderCard(ctx, data, "back");
103
+ }
104
+ renderCard(ctx, data, side) {
105
+ ctx.out.print(`<div class="card-list-item">`);
106
+ if (ctx.options.showDetails) {
107
+ ctx.out.print(prop("Type", `${data.note.type.name}::${data.card.name}::${sideName(side)}`));
108
+ ctx.out.print(prop("Deck", data.note.deck));
109
+ ctx.out.print(prop("Tags", data.note.tags));
110
+ }
111
+ ctx.out.print(`<div data-type="${escapeHtml(data.note.type.name)}">`);
112
+ ctx.out.print(`<div class="card">`);
113
+ ctx.out.print(this.renderCardContents(ctx, data, side));
114
+ ctx.out.print(`</div>`);
115
+ ctx.out.print(`</div>`);
116
+ ctx.out.print(`</div>`);
117
+ }
118
+ renderCardContents(ctx, data, side) {
119
+ return ctx.templates.render(data, side);
120
+ }
121
+ }
122
+ function prop(name, value) {
123
+ return (`<p class="prop">` +
124
+ `<span class="prop-name">${escapeHtml(name)}:</span> ` +
125
+ `<span class="prop-value">${escapeHtml(value)}</span>` +
126
+ `</p>`);
127
+ }
128
+ function sideName(side) {
129
+ switch (side) {
130
+ case "front":
131
+ return "Front";
132
+ default:
133
+ return "Back";
134
+ }
135
+ }
136
+ //# sourceMappingURL=preview-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-renderer.js","sourceRoot":"","sources":["../src/preview-renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type NoteList } from "@notatki/core";
2
+ import { CardTemplates } from "./card-templates.js";
3
+ import { type PreviewOptions } from "./preview-options.js";
4
+ import { PreviewRenderer } from "./preview-renderer.js";
5
+ export declare function generatePreview(notes: NoteList, options?: Partial<Readonly<PreviewOptions>>, renderer?: PreviewRenderer, templates?: CardTemplates): string;
6
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAU,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EACf,OAAO,GAAE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAM,EAC/C,QAAQ,GAAE,eAAuC,EACjD,SAAS,GAAE,aAA8C,GACxD,MAAM,CAIR"}
@@ -0,0 +1,10 @@
1
+ import { Output } from "@notatki/core";
2
+ import { CardTemplates } from "./card-templates.js";
3
+ import { expandPreviewOptions } from "./preview-options.js";
4
+ import { PreviewRenderer } from "./preview-renderer.js";
5
+ export function generatePreview(notes, options = {}, renderer = new PreviewRenderer(), templates = new CardTemplates(notes.types)) {
6
+ const out = new Output();
7
+ renderer.render({ options: expandPreviewOptions(options), templates, notes, out });
8
+ return String(out);
9
+ }
10
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAuB,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,UAAU,eAAe,CAC7B,KAAe,EACf,UAA6C,EAAE,EAC/C,WAA4B,IAAI,eAAe,EAAE,EACjD,YAA2B,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC;IAEzD,MAAM,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACnF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@notatki/preview",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "dependencies": {
7
+ "@notatki/core": "^0.0.1",
8
+ "@notatki/format": "^0.0.1",
9
+ "@notatki/parser": "^0.0.1"
10
+ },
11
+ "scripts": {
12
+ "compile": "rm -fr dist && tsc",
13
+ "pretest": "npm run compile",
14
+ "test": "node --test 'dist/**/*.test.js'"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "gitHead": "545474a85a5cc649ff9ce50f36e87ccf5c57073a"
20
+ }
@@ -0,0 +1,62 @@
1
+ import { type Model, type ModelCard, type Note } from "@notatki/core";
2
+ import { formatField, renderHtml } from "@notatki/format";
3
+ import { escapeHtml } from "./html.js";
4
+
5
+ const html = renderHtml({ output: "html", throwOnError: false });
6
+
7
+ export class CardData {
8
+ readonly #model: Model;
9
+ readonly #card: ModelCard;
10
+ readonly #note: Note;
11
+ readonly #data: Map<string, string>;
12
+
13
+ constructor(model: Model, card: ModelCard, note: Note) {
14
+ this.#model = model;
15
+ this.#card = card;
16
+ this.#note = note;
17
+ this.#data = new Map();
18
+ // Build-in fields.
19
+ this.setValue("Type", escapeHtml(note.type.name));
20
+ this.setValue("Card", escapeHtml(card.name));
21
+ this.setValue("Deck", escapeHtml(note.deck));
22
+ this.setValue("Subdeck", escapeHtml(note.deck.split("::").pop() ?? note.deck));
23
+ this.setValue("Tags", escapeHtml(note.tags));
24
+ this.setValue("Flags", escapeHtml("Flags"));
25
+ // User-defined fields.
26
+ for (const { name, value } of note) {
27
+ this.setValue(name, formatField(value, html));
28
+ }
29
+ }
30
+
31
+ get model(): Model {
32
+ return this.#model;
33
+ }
34
+
35
+ get card(): ModelCard {
36
+ return this.#card;
37
+ }
38
+
39
+ get note(): Note {
40
+ return this.#note;
41
+ }
42
+
43
+ hasValue(name: string): boolean {
44
+ return this.#data.get(name) != null;
45
+ }
46
+
47
+ getValue(name: string): string {
48
+ const value = this.#data.get(name);
49
+ if (value == null) {
50
+ throw new Error(`Unknown field: ${this.#note.type.name}::${name}`);
51
+ }
52
+ return value;
53
+ }
54
+
55
+ clearValue(name: string): void {
56
+ this.#data.delete(name);
57
+ }
58
+
59
+ setValue(name: string, value: string): void {
60
+ this.#data.set(name, value);
61
+ }
62
+ }
@@ -0,0 +1,153 @@
1
+ import { type Model, type ModelCard, type ModelMap } from "@notatki/core";
2
+ import { parseTemplate, type TemplateItemNode } from "@notatki/parser";
3
+ import { type CardData } from "./card-data.js";
4
+
5
+ export class CardTemplates {
6
+ readonly #map = new Map<string, CardTemplate>();
7
+
8
+ constructor(models: ModelMap) {
9
+ for (const model of models) {
10
+ for (const card of model.cards) {
11
+ this.#map.set(cardKey(model, card), new CardTemplate(card));
12
+ }
13
+ }
14
+ }
15
+
16
+ render(data: CardData, side: "front" | "back"): string {
17
+ const key = cardKey(data.note.type, data.card);
18
+ const card = this.#map.get(key);
19
+ if (card == null) {
20
+ throw new Error(`Unknown card: ${key}`);
21
+ }
22
+ return card.render(data, side);
23
+ }
24
+ }
25
+
26
+ function cardKey(model: Model, card: ModelCard): string {
27
+ return `${model.name}::${card.name}`;
28
+ }
29
+
30
+ type ValueProvider = {
31
+ getValue(data: CardData, field: string): string;
32
+ };
33
+
34
+ class CardTemplate implements ValueProvider {
35
+ readonly #front: Template;
36
+ readonly #back: Template;
37
+
38
+ constructor(card: ModelCard) {
39
+ this.#front = new Template(card.front);
40
+ this.#back = new Template(card.back);
41
+ }
42
+
43
+ render(data: CardData, side: "front" | "back"): string {
44
+ switch (side) {
45
+ case "front":
46
+ return this.renderFront(data);
47
+ case "back":
48
+ return this.renderBack(data);
49
+ }
50
+ }
51
+
52
+ renderFront(data: CardData): string {
53
+ data.clearValue("FrontSide");
54
+ return this.#front.render(this, data);
55
+ }
56
+
57
+ renderBack(data: CardData): string {
58
+ data.setValue("FrontSide", this.renderFront(data));
59
+ return this.#back.render(this, data);
60
+ }
61
+
62
+ getValue(data: CardData, field: string): string {
63
+ switch (field) {
64
+ case "cloze:Text": {
65
+ return data.getValue("Text");
66
+ }
67
+ }
68
+ return data.getValue(field);
69
+ }
70
+ }
71
+
72
+ type Part =
73
+ | string
74
+ | { readonly type: "field"; readonly field: string }
75
+ | { readonly type: "if"; readonly field: string; readonly parts: readonly Part[] }
76
+ | { readonly type: "if-not"; readonly field: string; readonly parts: readonly Part[] };
77
+
78
+ class Template {
79
+ readonly #parts: readonly Part[];
80
+
81
+ constructor(template: string) {
82
+ this.#parts = mapNodeList(parseTemplate(template));
83
+ }
84
+
85
+ render(values: ValueProvider, data: CardData): string {
86
+ let out = "";
87
+
88
+ function visit(parts: readonly Part[]) {
89
+ for (const part of parts) {
90
+ if (typeof part === "string") {
91
+ out += part;
92
+ } else {
93
+ switch (part.type) {
94
+ case "field": {
95
+ out += values.getValue(data, part.field);
96
+ break;
97
+ }
98
+ case "if": {
99
+ if (values.getValue(data, part.field)) {
100
+ visit(part.parts);
101
+ }
102
+ break;
103
+ }
104
+ case "if-not": {
105
+ if (!values.getValue(data, part.field)) {
106
+ visit(part.parts);
107
+ }
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ visit(this.#parts);
116
+
117
+ return out;
118
+ }
119
+ }
120
+
121
+ function mapNodeList(nodes: readonly TemplateItemNode[]): Part[] {
122
+ return nodes.map((node) => mapNode(node));
123
+ }
124
+
125
+ function mapNode(node: TemplateItemNode): Part {
126
+ switch (node.type) {
127
+ case "text": {
128
+ return node.text;
129
+ }
130
+ case "field": {
131
+ return {
132
+ type: "field",
133
+ field: node.field.text,
134
+ };
135
+ }
136
+ case "branch": {
137
+ const { cond } = node;
138
+ if (cond.not) {
139
+ return {
140
+ type: "if-not",
141
+ field: cond.field.text,
142
+ parts: mapNodeList(node.items),
143
+ };
144
+ } else {
145
+ return {
146
+ type: "if",
147
+ field: cond.field.text,
148
+ parts: mapNodeList(node.items),
149
+ };
150
+ }
151
+ }
152
+ }
153
+ }
package/src/html.ts ADDED
@@ -0,0 +1,8 @@
1
+ export function escapeHtml(text: string): string {
2
+ return String(text)
3
+ .replace(/</g, "&lt;")
4
+ .replace(/>/g, "&gt;")
5
+ .replace(/&/g, "&amp;")
6
+ .replace(/"/g, "&quot;")
7
+ .replace(/'/g, "&apos;");
8
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./card-data.js";
2
+ export * from "./card-templates.js";
3
+ export * from "./html.js";
4
+ export * from "./preview.js";
5
+ export * from "./preview-options.js";
6
+ export * from "./preview-renderer.js";
@@ -0,0 +1,21 @@
1
+ export type PreviewOptions = {
2
+ title: string;
3
+ showDetails: boolean;
4
+ showFront: boolean;
5
+ showBack: boolean;
6
+ };
7
+
8
+ export function expandPreviewOptions(options: Partial<Readonly<PreviewOptions>>): PreviewOptions {
9
+ const {
10
+ title = "Cards Preview", //
11
+ showDetails = true,
12
+ showFront = false,
13
+ showBack = true,
14
+ } = options;
15
+ return {
16
+ title, //
17
+ showDetails,
18
+ showFront,
19
+ showBack,
20
+ };
21
+ }
@@ -0,0 +1,165 @@
1
+ import { type Note, type NoteList, type Output } from "@notatki/core";
2
+ import { CardData } from "./card-data.js";
3
+ import { type CardTemplates } from "./card-templates.js";
4
+ import { escapeHtml } from "./html.js";
5
+ import { type PreviewOptions } from "./preview-options.js";
6
+
7
+ export type RendererContext = Readonly<{
8
+ options: Readonly<PreviewOptions>;
9
+ templates: CardTemplates;
10
+ notes: NoteList;
11
+ out: Output;
12
+ }>;
13
+
14
+ export class PreviewRenderer<Context extends RendererContext = RendererContext> {
15
+ static readonly defaultStylesheets: readonly string[] = [
16
+ `https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css`, //
17
+ ];
18
+
19
+ static readonly defaultStyles: readonly string[] = [
20
+ `:root { color-scheme: light dark; }`,
21
+ `.card-list { display: flex; flex-flow: row wrap; justify-content: center; align-items: center; gap: 1rem; }`,
22
+ `.card-list-item { flex: 0 1 auto; min-width: 20rem; padding: 0 1rem; border: 1px dotted #666; }`,
23
+ `.prop { font-size: 0.75em; color: #666; }`,
24
+ `.prop-name { font-weight: bold; font-style: normal; }`,
25
+ `.prop-value { font-weight: normal; font-style: normal; }`,
26
+ `img { max-width: 100%; }`,
27
+ `li { text-align: start; }`,
28
+ `pre { text-align: left; }`,
29
+ `hr { height: 1px; margin: 1em 0; border: none; background-color: #666; }`,
30
+ ];
31
+
32
+ #stylesheets: string[] = [...PreviewRenderer.defaultStylesheets];
33
+ #styles: string[] = [...PreviewRenderer.defaultStyles];
34
+
35
+ get stylesheets(): string[] {
36
+ return [...this.#stylesheets];
37
+ }
38
+
39
+ set stylesheets(value: string[]) {
40
+ this.#stylesheets = [...value];
41
+ }
42
+
43
+ get styles(): string[] {
44
+ return [...this.#styles];
45
+ }
46
+
47
+ set styles(value: string[]) {
48
+ this.#styles = [...value];
49
+ }
50
+
51
+ render(ctx: Context) {
52
+ ctx.out.print(`<!doctype html>`);
53
+ ctx.out.print(`<html>`);
54
+ this.renderHead(ctx);
55
+ this.renderBody(ctx);
56
+ ctx.out.print(`</html>`);
57
+ }
58
+
59
+ renderHead(ctx: Context) {
60
+ ctx.out.print(`<head>`);
61
+ ctx.out.print(`<meta charset="UTF-8">`);
62
+ ctx.out.print(`<title>${escapeHtml(ctx.options.title)}</title>`);
63
+ this.renderStylesheets(ctx);
64
+ this.renderCommonStyles(ctx);
65
+ this.renderModelStyles(ctx);
66
+ ctx.out.print(`</head>`);
67
+ }
68
+
69
+ renderStylesheets(ctx: Context) {
70
+ for (const stylesheet of this.#stylesheets) {
71
+ ctx.out.print(`<link rel="stylesheet" href="${escapeHtml(stylesheet)}" crossOrigin="anonymous">`);
72
+ }
73
+ }
74
+
75
+ renderCommonStyles(ctx: Context) {
76
+ ctx.out.print(`<style>`);
77
+ for (const style of this.#styles) {
78
+ ctx.out.print(style);
79
+ }
80
+ ctx.out.print(`</style>`);
81
+ }
82
+
83
+ renderModelStyles(ctx: Context) {
84
+ for (const type of ctx.notes.types) {
85
+ if (type.styling) {
86
+ ctx.out.print(`<style>`);
87
+ ctx.out.print(`[data-type="${type.name}"] {`);
88
+ ctx.out.print(type.styling);
89
+ ctx.out.print(`}`);
90
+ ctx.out.print(`</style>`);
91
+ }
92
+ }
93
+ }
94
+
95
+ renderBody(ctx: Context) {
96
+ ctx.out.print(`<body>`);
97
+ this.renderCardList(ctx);
98
+ ctx.out.print(`</body>`);
99
+ }
100
+
101
+ renderCardList(ctx: Context) {
102
+ ctx.out.print(`<main class="card-list">`);
103
+ for (const note of ctx.notes) {
104
+ this.renderNoteCardList(ctx, note);
105
+ }
106
+ ctx.out.print(`</main>`);
107
+ }
108
+
109
+ renderNoteCardList(ctx: Context, note: Note) {
110
+ for (const card of note.type.cards) {
111
+ const data = new CardData(note.type, card, note);
112
+ if (ctx.options.showFront) {
113
+ this.renderFrontCard(ctx, data);
114
+ }
115
+ if (ctx.options.showBack) {
116
+ this.renderBackCard(ctx, data);
117
+ }
118
+ }
119
+ }
120
+
121
+ renderFrontCard(ctx: Context, data: CardData) {
122
+ this.renderCard(ctx, data, "front");
123
+ }
124
+
125
+ renderBackCard(ctx: Context, data: CardData) {
126
+ this.renderCard(ctx, data, "back");
127
+ }
128
+
129
+ renderCard(ctx: Context, data: CardData, side: "front" | "back") {
130
+ ctx.out.print(`<div class="card-list-item">`);
131
+ if (ctx.options.showDetails) {
132
+ ctx.out.print(prop("Type", `${data.note.type.name}::${data.card.name}::${sideName(side)}`));
133
+ ctx.out.print(prop("Deck", data.note.deck));
134
+ ctx.out.print(prop("Tags", data.note.tags));
135
+ }
136
+ ctx.out.print(`<div data-type="${escapeHtml(data.note.type.name)}">`);
137
+ ctx.out.print(`<div class="card">`);
138
+ ctx.out.print(this.renderCardContents(ctx, data, side));
139
+ ctx.out.print(`</div>`);
140
+ ctx.out.print(`</div>`);
141
+ ctx.out.print(`</div>`);
142
+ }
143
+
144
+ renderCardContents(ctx: Context, data: CardData, side: "front" | "back"): string {
145
+ return ctx.templates.render(data, side);
146
+ }
147
+ }
148
+
149
+ function prop(name: string, value: string) {
150
+ return (
151
+ `<p class="prop">` +
152
+ `<span class="prop-name">${escapeHtml(name)}:</span> ` +
153
+ `<span class="prop-value">${escapeHtml(value)}</span>` +
154
+ `</p>`
155
+ );
156
+ }
157
+
158
+ function sideName(side: "front" | "back"): string {
159
+ switch (side) {
160
+ case "front":
161
+ return "Front";
162
+ default:
163
+ return "Back";
164
+ }
165
+ }
package/src/preview.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { type NoteList, Output } from "@notatki/core";
2
+ import { CardTemplates } from "./card-templates.js";
3
+ import { expandPreviewOptions, type PreviewOptions } from "./preview-options.js";
4
+ import { PreviewRenderer } from "./preview-renderer.js";
5
+
6
+ export function generatePreview(
7
+ notes: NoteList,
8
+ options: Partial<Readonly<PreviewOptions>> = {},
9
+ renderer: PreviewRenderer = new PreviewRenderer(),
10
+ templates: CardTemplates = new CardTemplates(notes.types),
11
+ ): string {
12
+ const out = new Output();
13
+ renderer.render({ options: expandPreviewOptions(options), templates, notes, out });
14
+ return String(out);
15
+ }