@difizen/libro-rendermime 0.0.2-alpha.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/es/components/html-render.d.ts +6 -0
  4. package/es/components/html-render.d.ts.map +1 -0
  5. package/es/components/html-render.js +39 -0
  6. package/es/components/image-render.d.ts +6 -0
  7. package/es/components/image-render.d.ts.map +1 -0
  8. package/es/components/image-render.js +36 -0
  9. package/es/components/index.d.ts +7 -0
  10. package/es/components/index.d.ts.map +1 -0
  11. package/es/components/index.js +6 -0
  12. package/es/components/latex-render.d.ts +6 -0
  13. package/es/components/latex-render.d.ts.map +1 -0
  14. package/es/components/latex-render.js +23 -0
  15. package/es/components/markdown-render.d.ts +6 -0
  16. package/es/components/markdown-render.d.ts.map +1 -0
  17. package/es/components/markdown-render.js +38 -0
  18. package/es/components/svg-render.d.ts +6 -0
  19. package/es/components/svg-render.d.ts.map +1 -0
  20. package/es/components/svg-render.js +33 -0
  21. package/es/components/text-render.d.ts +9 -0
  22. package/es/components/text-render.d.ts.map +1 -0
  23. package/es/components/text-render.js +44 -0
  24. package/es/index.d.ts +7 -0
  25. package/es/index.d.ts.map +1 -0
  26. package/es/index.js +6 -0
  27. package/es/renderers.d.ts +42 -0
  28. package/es/renderers.d.ts.map +1 -0
  29. package/es/renderers.js +327 -0
  30. package/es/rendermime-factory.d.ts +30 -0
  31. package/es/rendermime-factory.d.ts.map +1 -0
  32. package/es/rendermime-factory.js +73 -0
  33. package/es/rendermime-module.d.ts +3 -0
  34. package/es/rendermime-module.d.ts.map +1 -0
  35. package/es/rendermime-module.js +11 -0
  36. package/es/rendermime-protocol.d.ts +405 -0
  37. package/es/rendermime-protocol.d.ts.map +1 -0
  38. package/es/rendermime-protocol.js +60 -0
  39. package/es/rendermime-registry.d.ts +135 -0
  40. package/es/rendermime-registry.d.ts.map +1 -0
  41. package/es/rendermime-registry.js +392 -0
  42. package/es/rendermime-utils.d.ts +87 -0
  43. package/es/rendermime-utils.d.ts.map +1 -0
  44. package/es/rendermime-utils.js +603 -0
  45. package/package.json +61 -0
  46. package/src/components/html-render.tsx +42 -0
  47. package/src/components/image-render.tsx +46 -0
  48. package/src/components/index.ts +6 -0
  49. package/src/components/latex-render.tsx +30 -0
  50. package/src/components/markdown-render.tsx +42 -0
  51. package/src/components/svg-render.tsx +38 -0
  52. package/src/components/text-render.tsx +51 -0
  53. package/src/index.ts +6 -0
  54. package/src/renderers.ts +325 -0
  55. package/src/rendermime-factory.ts +92 -0
  56. package/src/rendermime-module.ts +19 -0
  57. package/src/rendermime-protocol.ts +516 -0
  58. package/src/rendermime-registry.ts +301 -0
  59. package/src/rendermime-utils.ts +665 -0
@@ -0,0 +1,301 @@
1
+ import { defaultSanitizer } from '@difizen/libro-common';
2
+ import type { BaseOutputView } from '@difizen/libro-core';
3
+ import { MarkdownParser } from '@difizen/libro-markdown';
4
+ import type { Contribution } from '@difizen/mana-app';
5
+ import { contrib, inject, singleton, Priority } from '@difizen/mana-app';
6
+ import { Emitter } from '@difizen/mana-app';
7
+
8
+ import {
9
+ RenderMimeContribution,
10
+ IRenderMimeRegistryOptions,
11
+ } from './rendermime-protocol.js';
12
+ import type {
13
+ FactoryMap,
14
+ ILinkHandler,
15
+ IRendererFactory,
16
+ IRenderMimeRegistry,
17
+ IResolver,
18
+ RankMap,
19
+ } from './rendermime-protocol.js';
20
+ import { sortedTypes } from './rendermime-utils.js';
21
+
22
+ /**
23
+ * An object which manages mime renderer factories.
24
+ *
25
+ * This object is used to render mime models using registered mime
26
+ * renderers, selecting the preferred mime renderer to render the
27
+ * model into a widget.
28
+ *
29
+ * #### Notes
30
+ * This class is not intended to be subclassed.
31
+ */
32
+ @singleton()
33
+ export class RenderMimeRegistry implements IRenderMimeRegistry {
34
+ renderMimeEmitter: Emitter<{ renderType: string; mimeType: string }> = new Emitter();
35
+ get onMimeRender() {
36
+ return this.renderMimeEmitter.event;
37
+ }
38
+ /**
39
+ * Construct a new rendermime.
40
+ *
41
+ * @param options - The options for initializing the instance.
42
+ */
43
+ constructor(
44
+ @inject(IRenderMimeRegistryOptions) options: IRenderMimeRegistryOptions,
45
+ @inject(MarkdownParser) markdownParser: MarkdownParser,
46
+ ) {
47
+ // Parse the options.
48
+ // this.translator = options.translator ?? nullTranslator;
49
+ this.resolver = options.resolver ?? null;
50
+ this.linkHandler = options.linkHandler ?? null;
51
+ this.markdownParser = options.markdownParser ?? markdownParser;
52
+ this.sanitizer = options.sanitizer ?? defaultSanitizer;
53
+
54
+ // Add the initial factories.
55
+ if (options.initialFactories) {
56
+ for (const factory of options.initialFactories) {
57
+ this.addFactory(factory);
58
+ }
59
+ }
60
+ }
61
+
62
+ @contrib(RenderMimeContribution)
63
+ renderMimeProvider: Contribution.Provider<RenderMimeContribution>;
64
+
65
+ /**
66
+ * The sanitizer used by the rendermime instance.
67
+ */
68
+ readonly sanitizer = defaultSanitizer;
69
+
70
+ /**
71
+ * The object used to resolve relative urls for the rendermime instance.
72
+ */
73
+ readonly resolver: IResolver | null;
74
+
75
+ /**
76
+ * The object used to handle path opening links.
77
+ */
78
+ readonly linkHandler: ILinkHandler | null;
79
+
80
+ /**
81
+ * The Markdown parser for the rendermime.
82
+ */
83
+ readonly markdownParser: MarkdownParser | null;
84
+
85
+ // /**
86
+ // * The application language translator.
87
+ // */
88
+ // readonly translator: ITranslator;
89
+
90
+ /**
91
+ * The ordered list of mimeTypes.
92
+ */
93
+ get mimeTypes(): readonly string[] {
94
+ return this._types || (this._types = sortedTypes(this._ranks));
95
+ }
96
+
97
+ protected getSortedRenderMimes(model: BaseOutputView): RenderMimeContribution[] {
98
+ const prioritized = Priority.sortSync(
99
+ this.renderMimeProvider.getContributions(),
100
+ (contribution) => contribution.canHandle(model),
101
+ );
102
+ const sortedRenderMimes = prioritized.map((c) => c.value);
103
+ return sortedRenderMimes;
104
+ }
105
+
106
+ defaultPreferredMimeType(
107
+ model: BaseOutputView,
108
+ // safe: 'ensure' | 'prefer' | 'any' = 'ensure',
109
+ ): string | undefined {
110
+ for (const mt of this.mimeTypes) {
111
+ if (mt in model.data) {
112
+ return mt;
113
+ }
114
+ }
115
+
116
+ // Otherwise, no matching mime type exists.
117
+ return undefined;
118
+ }
119
+
120
+ /**
121
+ * Find the preferred mime type for a mime bundle.
122
+ *
123
+ * @param bundle - The bundle of mime data.
124
+ *
125
+ * @param safe - How to consider safe/unsafe factories. If 'ensure',
126
+ * it will only consider safe factories. If 'any', any factory will be
127
+ * considered. If 'prefer', unsafe factories will be considered, but
128
+ * only after the safe options have been exhausted.
129
+ *
130
+ * @returns The preferred mime type from the available factories,
131
+ * or `undefined` if the mime type cannot be rendered.
132
+ */
133
+ preferredMimeType(
134
+ model: BaseOutputView,
135
+ // safe: 'ensure' | 'prefer' | 'any' = 'ensure',
136
+ ): string | undefined {
137
+ // // Try to find a safe factory first, if preferred.
138
+ // if (safe === 'ensure' || safe === 'prefer') {
139
+ // for (const mt of this.mimeTypes) {
140
+ // if (mt in bundle && this._factories[mt].safe) {
141
+ // return mt;
142
+ // }
143
+ // }
144
+ // }
145
+
146
+ // if (safe !== 'ensure') {
147
+ // // Otherwise, search for the best factory among all factories.
148
+ // for (const mt of this.mimeTypes) {
149
+ // if (mt in bundle) {
150
+ // return mt;
151
+ // }
152
+ // }
153
+ // }
154
+ const sortedRenderMimes = this.getSortedRenderMimes(model);
155
+
156
+ for (const renderMime of sortedRenderMimes) {
157
+ for (const mt of renderMime.mimeTypes) {
158
+ if (mt in model.data) {
159
+ return mt;
160
+ }
161
+ }
162
+ }
163
+
164
+ for (const mt of this.mimeTypes) {
165
+ if (mt in model.data) {
166
+ return mt;
167
+ }
168
+ }
169
+
170
+ // Otherwise, no matching mime type exists.
171
+ return undefined;
172
+ }
173
+
174
+ /**
175
+ * Create a renderer for a mime type.
176
+ *
177
+ * @param mimeType - The mime type of interest.
178
+ *
179
+ * @returns A new renderer for the given mime type.
180
+ *
181
+ * @throws An error if no factory exists for the mime type.
182
+ */
183
+ createRenderer(
184
+ mimeType: string,
185
+ model: BaseOutputView, // model: BaseOutputModel,
186
+ // host: HTMLElement,
187
+ ): React.FC<{ model: BaseOutputView }> {
188
+ const renderMimes = this.getSortedRenderMimes(model);
189
+ for (const renderMime of renderMimes) {
190
+ for (const mt of renderMime.mimeTypes) {
191
+ if (mimeType === mt) {
192
+ const OutputRender = renderMime.render;
193
+ this.renderMimeEmitter.fire({
194
+ renderType: renderMime.renderType,
195
+ mimeType,
196
+ });
197
+ return OutputRender;
198
+ }
199
+ }
200
+ }
201
+
202
+ // Throw an error if no factory exists for the mime type.
203
+ if (!(mimeType in this._factories)) {
204
+ throw new Error(`No factory for mime type: '${mimeType}'`);
205
+ }
206
+ const OutputRender = this._factories[mimeType].render;
207
+ this.renderMimeEmitter.fire({
208
+ renderType: this._factories[mimeType].renderType,
209
+ mimeType,
210
+ });
211
+ // Invoke the best factory for the given mime type.
212
+ return OutputRender;
213
+ }
214
+
215
+ /**
216
+ * Get the renderer factory registered for a mime type.
217
+ *
218
+ * @param mimeType - The mime type of interest.
219
+ *
220
+ * @returns The factory for the mime type, or `undefined`.
221
+ */
222
+ getFactory(mimeType: string): IRendererFactory | undefined {
223
+ return this._factories[mimeType];
224
+ }
225
+
226
+ /**
227
+ * Add a renderer factory to the rendermime.
228
+ *
229
+ * @param factory - The renderer factory of interest.
230
+ *
231
+ * @param _rank - The rank of the renderer. A lower rank indicates
232
+ * a higher priority for rendering. If not given, the rank will
233
+ * defer to the `defaultRank` of the factory. If no `defaultRank`
234
+ * is given, it will default to 100.
235
+ *
236
+ * #### Notes
237
+ * The renderer will replace an existing renderer for the given
238
+ * mimeType.
239
+ */
240
+ addFactory(factory: IRendererFactory, _rank?: number): void {
241
+ let rank = _rank;
242
+ if (rank === undefined) {
243
+ rank = factory.defaultRank;
244
+ if (rank === undefined) {
245
+ rank = 100;
246
+ }
247
+ }
248
+ for (const mt of factory.mimeTypes) {
249
+ this._factories[mt] = factory;
250
+ this._ranks[mt] = { rank, id: this._id++ };
251
+ }
252
+ this._types = null;
253
+ }
254
+
255
+ /**
256
+ * Remove a mime type.
257
+ *
258
+ * @param mimeType - The mime type of interest.
259
+ */
260
+ removeMimeType(mimeType: string): void {
261
+ delete this._factories[mimeType];
262
+ delete this._ranks[mimeType];
263
+ this._types = null;
264
+ }
265
+
266
+ /**
267
+ * Get the rank for a given mime type.
268
+ *
269
+ * @param mimeType - The mime type of interest.
270
+ *
271
+ * @returns The rank of the mime type or undefined.
272
+ */
273
+ getRank(mimeType: string): number | undefined {
274
+ const rank = this._ranks[mimeType];
275
+ return rank && rank.rank;
276
+ }
277
+
278
+ /**
279
+ * Set the rank of a given mime type.
280
+ *
281
+ * @param mimeType - The mime type of interest.
282
+ *
283
+ * @param rank - The new rank to assign.
284
+ *
285
+ * #### Notes
286
+ * This is a no-op if the mime type is not registered.
287
+ */
288
+ setRank(mimeType: string, rank: number): void {
289
+ if (!this._ranks[mimeType]) {
290
+ return;
291
+ }
292
+ const id = this._id++;
293
+ this._ranks[mimeType] = { rank, id };
294
+ this._types = null;
295
+ }
296
+
297
+ private _id = 0;
298
+ private _ranks: RankMap = {};
299
+ private _types: string[] | null = null;
300
+ private _factories: FactoryMap = {};
301
+ }