fika-editor 3.1.2 → 3.2.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.
@@ -0,0 +1,15 @@
1
+ /** Transform only source spans classified as prose by the CommonMark/math parser.
2
+ * Code (including unmatched fences), formulas, link destinations and HTML remain
3
+ * byte-for-byte source. No Markdown serialization or delimiter guessing is used.
4
+ */
5
+ export declare function mapMarkdownProse(source: string, transform: (text: string) => string): string;
6
+ export declare function normalizeGeneratedMarkdown(source: string): string;
7
+ export declare function normalizeGeneratedNewlines(source: string): string;
8
+ export interface MarkdownMathSegment {
9
+ type: 'text' | 'math';
10
+ raw: string;
11
+ value: string;
12
+ display: boolean;
13
+ }
14
+ /** Source-preserving math segments derived from the same syntax tree. */
15
+ export declare function markdownMathSegments(source: string): MarkdownMathSegment[];
@@ -0,0 +1,206 @@
1
+ import { fromMarkdown } from 'mdast-util-from-markdown';
2
+ import { mathFromMarkdown } from 'mdast-util-math';
3
+ import { math } from 'micromark-extension-llm-math';
4
+ import { decodeHTML } from 'entities';
5
+ import MarkdownIt from 'markdown-it';
6
+ import texmath from 'markdown-it-texmath';
7
+ const parseOptions = { extensions: [math()], mdastExtensions: [mathFromMarkdown()] };
8
+ // Preserve the existing Fika support for bare TeX environments. This parser is
9
+ // only used for that uncommon syntax; CommonMark code fences still take priority.
10
+ const environmentParser = new MarkdownIt({ html: false }).use(texmath, {
11
+ delimiters: ['beg_end'], engine: { renderToString: (value) => value },
12
+ });
13
+ /** micromark math follows code-span dollar rules. TeX permits escaped dollars;
14
+ * mask only those lexical escapes in the parser input, keeping every offset.
15
+ * All returned content is sliced from the original source, never this mask.
16
+ */
17
+ function parserInput(source) {
18
+ if (!source.includes('\\$'))
19
+ return source;
20
+ // Work in UTF-16 offsets, like micromark and source.position.offset.
21
+ const out = source.split('');
22
+ for (let i = 0; i < source.length; i++) {
23
+ if (source[i] !== '\\')
24
+ continue;
25
+ if (source[i + 1] === '$')
26
+ out[i + 1] = '\uE000';
27
+ i++;
28
+ }
29
+ return out.join('');
30
+ }
31
+ const parseGeneratedMarkdown = (source) => fromMarkdown(parserInput(source), parseOptions);
32
+ function environmentSpans(source) {
33
+ if (!source.includes('\\begin{'))
34
+ return [];
35
+ const lines = [0];
36
+ for (let i = 0; i < source.length; i++)
37
+ if (source[i] === '\n')
38
+ lines.push(i + 1);
39
+ const spans = [];
40
+ for (const token of environmentParser.parse(source, {})) {
41
+ if (token.type !== 'math_block' || !token.map)
42
+ continue;
43
+ const start = source.indexOf(token.content, lines[token.map[0]]);
44
+ if (start >= 0)
45
+ spans.push({ start, end: start + token.content.length, value: token.content, display: true });
46
+ }
47
+ return spans;
48
+ }
49
+ function walk(node, visit) {
50
+ if (node.type !== 'root' && visit(node) === false)
51
+ return;
52
+ if ('children' in node)
53
+ for (const child of node.children)
54
+ walk(child, visit);
55
+ }
56
+ /** Real EOL conversion is independent of JSON-style escape recovery. */
57
+ function normalizeEols(source) {
58
+ if (!source.includes('\r'))
59
+ return source;
60
+ const out = [];
61
+ for (let i = 0; i < source.length; i++) {
62
+ if (source[i] === '\r') {
63
+ out.push('\n');
64
+ if (source[i + 1] === '\n')
65
+ i++;
66
+ }
67
+ else
68
+ out.push(source[i]);
69
+ }
70
+ return out.join('');
71
+ }
72
+ /** A linear escape decoder. It never decides what is code or math. */
73
+ function decodeProseNewlines(source) {
74
+ if (!source.includes('\\') && !source.includes('\r'))
75
+ return source;
76
+ const out = [];
77
+ for (let i = 0; i < source.length;) {
78
+ if (source[i] === '\r') {
79
+ out.push('\n');
80
+ i += source[i + 1] === '\n' ? 2 : 1;
81
+ continue;
82
+ }
83
+ if (source[i] !== '\\') {
84
+ out.push(source[i++]);
85
+ continue;
86
+ }
87
+ const start = i;
88
+ while (source[i] === '\\')
89
+ i++;
90
+ const escape = source[i];
91
+ if (escape !== 'n' && escape !== 'r') {
92
+ out.push(source.slice(start, i));
93
+ continue;
94
+ }
95
+ i++;
96
+ if (escape === 'r' && source[i] === '\\') {
97
+ let next = i;
98
+ while (source[next] === '\\')
99
+ next++;
100
+ if (source[next] === 'n')
101
+ i = next + 1;
102
+ }
103
+ out.push('\n');
104
+ }
105
+ return out.join('');
106
+ }
107
+ const opaqueNodes = new Set(['code', 'inlineCode', 'math', 'inlineMath', 'html']);
108
+ /** Transform only source spans classified as prose by the CommonMark/math parser.
109
+ * Code (including unmatched fences), formulas, link destinations and HTML remain
110
+ * byte-for-byte source. No Markdown serialization or delimiter guessing is used.
111
+ */
112
+ export function mapMarkdownProse(source, transform) {
113
+ if (!source)
114
+ return source;
115
+ const env = environmentSpans(source);
116
+ const spans = env.map(span => ({ ...span, prose: false }));
117
+ walk(parseGeneratedMarkdown(source), node => {
118
+ const start = node.position?.start.offset;
119
+ const end = node.position?.end.offset;
120
+ if (start === undefined || end === undefined)
121
+ return;
122
+ if (opaqueNodes.has(node.type)) {
123
+ spans.push({ start, end, prose: false });
124
+ return false;
125
+ }
126
+ if (node.type === 'text') {
127
+ // An environment may sit inside a CommonMark text node. Split at the
128
+ // library's source boundaries, preserving text around it as prose.
129
+ let cursor = start;
130
+ for (const block of env) {
131
+ if (block.end <= cursor || block.start >= end)
132
+ continue;
133
+ if (cursor < block.start)
134
+ spans.push({ start: cursor, end: block.start, prose: true });
135
+ cursor = Math.min(end, block.end);
136
+ }
137
+ if (cursor < end)
138
+ spans.push({ start: cursor, end, prose: true });
139
+ }
140
+ });
141
+ spans.sort((a, b) => a.start - b.start || b.end - a.end);
142
+ const out = [];
143
+ let cursor = 0;
144
+ for (const span of spans) {
145
+ if (span.start < cursor)
146
+ continue;
147
+ out.push(normalizeEols(source.slice(cursor, span.start)));
148
+ const raw = source.slice(span.start, span.end);
149
+ out.push(span.prose ? transform(raw) : raw);
150
+ cursor = span.end;
151
+ }
152
+ out.push(normalizeEols(source.slice(cursor)));
153
+ return out.join('');
154
+ }
155
+ export function normalizeGeneratedMarkdown(source) {
156
+ if (!source || (!source.includes('\\') && !source.includes('\r') && !source.includes('&')))
157
+ return source;
158
+ return mapMarkdownProse(source, text => decodeProseNewlines(decodeHTML(text)));
159
+ }
160
+ export function normalizeGeneratedNewlines(source) {
161
+ if (!source || (!source.includes('\\') && !source.includes('\r')))
162
+ return source;
163
+ return mapMarkdownProse(source, decodeProseNewlines);
164
+ }
165
+ /** Source-preserving math segments derived from the same syntax tree. */
166
+ export function markdownMathSegments(source) {
167
+ const spans = environmentSpans(source);
168
+ walk(parseGeneratedMarkdown(source), node => {
169
+ if (node.type !== 'math' && node.type !== 'inlineMath')
170
+ return;
171
+ const start = node.position?.start.offset;
172
+ const end = node.position?.end.offset;
173
+ if (start !== undefined && end !== undefined) {
174
+ const raw = source.slice(start, end);
175
+ const display = node.type === 'math' || raw.startsWith('$$') || raw.startsWith('\\[');
176
+ if (!display && (raw.includes('\n') || raw.includes('\r')))
177
+ return false;
178
+ // Inline token values fold whitespace; TeX needs its original source.
179
+ let delimiter = raw.startsWith('\\') ? 2 : 0;
180
+ if (!delimiter)
181
+ while (raw[delimiter] === '$')
182
+ delimiter++;
183
+ const value = raw.slice(delimiter, -delimiter);
184
+ spans.push({ start, end, value, display });
185
+ }
186
+ return false;
187
+ });
188
+ spans.sort((a, b) => a.start - b.start);
189
+ const out = [];
190
+ let cursor = 0;
191
+ for (const span of spans) {
192
+ if (span.start < cursor)
193
+ continue;
194
+ if (span.start > cursor) {
195
+ const raw = source.slice(cursor, span.start);
196
+ out.push({ type: 'text', raw, value: raw, display: false });
197
+ }
198
+ out.push({ type: 'math', raw: source.slice(span.start, span.end), value: span.value, display: span.display });
199
+ cursor = span.end;
200
+ }
201
+ if (cursor < source.length) {
202
+ const raw = source.slice(cursor);
203
+ out.push({ type: 'text', raw, value: raw, display: false });
204
+ }
205
+ return out;
206
+ }
@@ -8,9 +8,19 @@
8
8
  * — the engine never silently drops a mark the host asked for.
9
9
  */
10
10
  export type FikaExportWatermarkPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
11
+ export type FikaWatermarkSurface = 'light' | 'dark';
11
12
  export interface FikaExportWatermark {
12
- /** PNG or JPEG as a `data:` URL or a URL the browser can fetch. */
13
+ /**
14
+ * Mark for light slide fills (dark artwork). PNG or JPEG as a `data:` URL
15
+ * or a URL the browser can fetch.
16
+ */
13
17
  image: string;
18
+ /**
19
+ * Mark for dark / photo slides (light artwork). When omitted every slide
20
+ * uses `image`. Polarities come from the same `preferredInk` query as editor
21
+ * chrome and text contrast.
22
+ */
23
+ imageOnDark?: string;
14
24
  /** Corner of the slide. @default 'bottom-right' */
15
25
  position?: FikaExportWatermarkPosition;
16
26
  /** Mark width as a fraction of the slide width (0–1]. @default 0.12 */
@@ -3,7 +3,7 @@ export type { FikaController, FikaDocument, FikaHeaderMenuItem, FikaMountOptions
3
3
  export type { FikaDeckAtlasOptions, FikaDeckAtlasSheet, FikaDeckAtlasTile, FikaRenderSlideOptions, } from './render';
4
4
  export type { ExportTabId, FikaExportTabsConfig } from '../configs/exportTabs';
5
5
  export type { FikaExportMediaResolver } from '../configs/exportMediaResolver';
6
- export type { FikaExportWatermark, FikaExportWatermarkPosition, FikaExportWatermarkResolver, } from '../configs/exportWatermark';
6
+ export type { FikaExportWatermark, FikaExportWatermarkPosition, FikaExportWatermarkResolver, FikaWatermarkSurface, } from '../configs/exportWatermark';
7
7
  export { createFikaMediaUploader, } from '../configs/mediaUpload';
8
8
  export type { FikaMediaConfig, FikaMediaConstraints, FikaMediaKind, FikaMediaSizeLimit, FikaMediaUploadProgress, FikaMediaUploadRequest, FikaMediaUploadResult, FikaMediaUploader, FikaXhrMediaUploaderOptions, } from '../configs/mediaUpload';
9
9
  export type * from './agentic/types';
@@ -1,13 +1,13 @@
1
1
  import type { FikaExportTabsConfig } from '../configs/exportTabs';
2
2
  import type { FikaExportMediaResolver } from '../configs/exportMediaResolver';
3
- import type { FikaExportWatermark, FikaExportWatermarkPosition, FikaExportWatermarkResolver } from '../configs/exportWatermark';
3
+ import type { FikaExportWatermark, FikaExportWatermarkPosition, FikaExportWatermarkResolver, FikaWatermarkSurface } from '../configs/exportWatermark';
4
4
  import type { FikaMediaConfig } from '../configs/mediaUpload';
5
5
  import type { Locales } from '../i18n/locale';
6
6
  import type { Slide, SlideTheme, SlideTemplate } from '../types/slides';
7
7
  import type { FikaAgentApi, FikaDeckViewport, FikaSlideReference } from './agentic/types';
8
8
  import type { FikaRevealOptions } from './reveal';
9
9
  export type { FikaExportMediaResolver };
10
- export type { FikaExportWatermark, FikaExportWatermarkPosition, FikaExportWatermarkResolver };
10
+ export type { FikaExportWatermark, FikaExportWatermarkPosition, FikaExportWatermarkResolver, FikaWatermarkSurface };
11
11
  export type { FikaRevealOptions };
12
12
  export type { FikaMediaConfig, FikaMediaConstraints, FikaMediaKind, FikaMediaSizeLimit, FikaMediaUploadProgress, FikaMediaUploadRequest, FikaMediaUploadResult, FikaMediaUploader, FikaXhrMediaUploaderOptions } from '../configs/mediaUpload';
13
13
  export interface FikaTemplatePayload {
@@ -0,0 +1,15 @@
1
+ /** Transform only source spans classified as prose by the CommonMark/math parser.
2
+ * Code (including unmatched fences), formulas, link destinations and HTML remain
3
+ * byte-for-byte source. No Markdown serialization or delimiter guessing is used.
4
+ */
5
+ export declare function mapMarkdownProse(source: string, transform: (text: string) => string): string;
6
+ export declare function normalizeGeneratedMarkdown(source: string): string;
7
+ export declare function normalizeGeneratedNewlines(source: string): string;
8
+ export interface MarkdownMathSegment {
9
+ type: 'text' | 'math';
10
+ raw: string;
11
+ value: string;
12
+ display: boolean;
13
+ }
14
+ /** Source-preserving math segments derived from the same syntax tree. */
15
+ export declare function markdownMathSegments(source: string): MarkdownMathSegment[];
@@ -1,3 +1,4 @@
1
+ import { normalizeGeneratedNewlines } from '../text/index';
1
2
  /**
2
3
  * Tiny helpers for agent-authored plain text before it becomes slide HTML.
3
4
  *
@@ -5,21 +6,13 @@
5
6
  * - literal escape sequences (`\n`, `\r\n`, `\r`) instead of real newlines
6
7
  * - HTML entities (`&amp;`, `&nbsp;`, `&#8222;`, …) instead of characters
7
8
  *
8
- * These helpers are intentionally dumb — no math parsing here. Callers that mix
9
- * TeX must run newline unescaping only on non-math text via `normalizeAgentText`
10
- * in `markdown.ts` (which uses the real `tokenizeMath` scanner).
11
- */
12
- /**
13
- * Decode HTML entities in a plain-text / markdown string.
14
- * Prefer a DOM textarea when available; fall back to a pure regex path for Node tests.
9
+ * Newline recovery uses the shared CommonMark/math parser to preserve literal
10
+ * code and TeX. Markdown rendering and editable run styling live in markdown.ts.
15
11
  */
12
+ /** Decode complete HTML entities, including invalid numeric references safely. */
16
13
  export declare function decodeHtmlEntities(text: string): string;
17
- /**
18
- * Turn literal `\r\n` / `\n` / `\r` escape sequences into real newlines.
19
- * Inline code spans are left untouched so `` `$\\nu$` `` stays intact; this is
20
- * not a math parser — TeX protection belongs to {@link tokenizeMath}.
21
- */
22
- export declare function unescapeAgentNewlines(text: string): string;
14
+ /** Shared parser-backed recovery; code and math source are never decoded. */
15
+ export declare const unescapeAgentNewlines: typeof normalizeGeneratedNewlines;
23
16
  /**
24
17
  * Convert real newlines (`\r\n` / `\r` / `\n`) to `<br/>` for HTML `content` paths.
25
18
  * Literal escape sequences are unescaped first. Entity decoding runs only when the
@@ -1,3 +1,4 @@
1
+ import { markdownMathSegments, normalizeGeneratedMarkdown } from '../text/index';
1
2
  /** True when a string carries math delimiters the math parser should handle. */
2
3
  export declare function containsMath(source: string): boolean;
3
4
  /** Delimited math or any TeX control sequence in the source. */
@@ -16,17 +17,9 @@ export interface ContentSegment {
16
17
  /** Math only: display (block) vs inline. */
17
18
  display: boolean;
18
19
  }
19
- /**
20
- * Split `source` into ordered text and math segments. This is the robust core
21
- * behind both line-splitting and inline rendering: it scans once, picking the
22
- * earliest opener at each position (longest on ties, so `$$` beats `$`), honors
23
- * backslash escapes (`\$`, `\\`), skips inline code spans (so `` `$x` `` is not
24
- * read as math) and keeps unterminated openers as literal text (graceful for
25
- * partial/streamed content). Inline math may not span newlines; display math
26
- * (`$$`, `\[`, `\begin{}`) may.
27
- */
28
- export declare function tokenizeMath(source: string): ContentSegment[];
29
- export declare function normalizeAgentText(text: string): string;
20
+ /** Parser-backed code/math boundaries shared with the server compiler. */
21
+ export declare const tokenizeMath: typeof markdownMathSegments;
22
+ export declare const normalizeAgentText: typeof normalizeGeneratedMarkdown;
30
23
  export declare function splitLinesPreservingMath(value: string): string[];
31
24
  export declare function markdownToHtml(markdown: string): Promise<string>;
32
25
  /**
@@ -1,4 +1,4 @@
1
- import type { FikaExportWatermark } from '../configs/exportWatermark';
1
+ import type { FikaExportWatermark, FikaWatermarkSurface } from '../configs/exportWatermark';
2
2
  export declare const EXPORT_WATERMARK_MARKER = "fika:export-watermark";
3
3
  export interface WatermarkImage {
4
4
  bytes: Uint8Array;
@@ -6,17 +6,32 @@ export interface WatermarkImage {
6
6
  width: number;
7
7
  height: number;
8
8
  }
9
+ export interface ApplyPptxExportWatermarkImages {
10
+ onLight: WatermarkImage;
11
+ onDark?: WatermarkImage;
12
+ /** Per-slide polarity from `preferredInk`. Gaps are inferred from the slide XML. */
13
+ slideSurfaces?: Array<FikaWatermarkSurface | undefined>;
14
+ masterSurface?: FikaWatermarkSurface;
15
+ }
9
16
  /** Reads dimensions from the PNG/JPEG header — no decoder or DOM needed. */
10
17
  export declare function readWatermarkImage(bytes: Uint8Array): WatermarkImage;
11
18
  /** Resolves `image` (data: URL or fetchable URL) to raw bytes plus dimensions. */
12
19
  export declare function loadWatermarkImage(image: string): Promise<WatermarkImage>;
20
+ /**
21
+ * Same cliff as `perceivedDark` in textContrast: AERT brightness 140 or WCAG
22
+ * luminance 0.25. Dark surface → light mark.
23
+ */
24
+ export declare function watermarkSurfaceFromRgb(r: number, g: number, b: number): FikaWatermarkSurface;
25
+ export declare function watermarkSurfaceFromHex(hex: string): FikaWatermarkSurface;
26
+ /** Photo fills use the invert mark; solids/gradients follow the sampled color. */
27
+ export declare function inferWatermarkSurfaceFromSlideXml(xml: string): FikaWatermarkSurface;
13
28
  /** True when the package already carries the export watermark on any slide. */
14
29
  export declare function hasPptxExportWatermark(bytes: ArrayBuffer | Uint8Array): Promise<boolean>;
15
30
  /**
16
31
  * Returns a new package with the watermark on every slide and slide master.
17
32
  * Throws when the input is not a presentation package.
18
33
  */
19
- export declare function applyPptxExportWatermark(bytes: ArrayBuffer | Uint8Array, watermark: FikaExportWatermark, image: WatermarkImage): Promise<Uint8Array>;
34
+ export declare function applyPptxExportWatermark(bytes: ArrayBuffer | Uint8Array, watermark: FikaExportWatermark, images: WatermarkImage | ApplyPptxExportWatermarkImages): Promise<Uint8Array>;
20
35
  /**
21
36
  * Browser download through a real `HTMLAnchorElement.click()` — the same seam
22
37
  * pptxgenjs uses, which embedding hosts rely on to capture export bytes.
@@ -2,6 +2,7 @@ export declare const schemaNodes: {
2
2
  doc: import("prosemirror-model").NodeSpec;
3
3
  paragraph: import("prosemirror-model").NodeSpec;
4
4
  blockquote: import("prosemirror-model").NodeSpec;
5
+ hard_break: import("prosemirror-model").NodeSpec;
5
6
  text: import("prosemirror-model").NodeSpec;
6
7
  math: import("prosemirror-model").NodeSpec;
7
8
  ordered_list: import("prosemirror-model").NodeSpec;
@@ -3,6 +3,7 @@ declare const _default: {
3
3
  doc: NodeSpec;
4
4
  paragraph: NodeSpec;
5
5
  blockquote: NodeSpec;
6
+ hard_break: NodeSpec;
6
7
  text: NodeSpec;
7
8
  math: NodeSpec;
8
9
  ordered_list: NodeSpec;
package/docs/EMBED.md CHANGED
@@ -292,7 +292,8 @@ await mountFika(host, {
292
292
  exportWatermark: () =>
293
293
  isFreeTier()
294
294
  ? {
295
- image: `${location.origin}/images/logo.png`, // PNG or JPEG; data: URLs work too
295
+ image: `${location.origin}/images/logo.png`, // light slides (dark artwork)
296
+ imageOnDark: `${location.origin}/images/logo-on-dark.png`, // optional; dark / photo slides
296
297
  position: 'bottom-right', // default
297
298
  widthRatio: 0.12, // fraction of slide width, default
298
299
  marginRatio: 0.02, // fraction of slide width, default
@@ -304,7 +305,7 @@ await mountFika(host, {
304
305
 
305
306
  The mark is written into the OOXML after generation, so it is identical for regenerated decks and retained source packages:
306
307
 
307
- - one shared media part, referenced from **every slide and every slide master**;
308
+ - one media part per polarity (`image` on light fills, `imageOnDark` on dark / photo fills). The engine picks the variant with the same `preferredInk` query as editor chrome; image backgrounds follow the invert mark;
308
309
  - the slide copy sits last in the shape tree (visible over full-bleed content) and is fully locked — `noSelect` keeps PowerPoint from selecting it by click, marquee, Ctrl+A or the selection pane, so there is no in-app way to delete it;
309
310
  - the master copy is `userDrawn` and locked too; it survives slide-level deletions in editors that ignore locks and only goes away through the slide master view (or by hiding background graphics on a slide);
310
311
  - parts already carrying the mark are skipped, so a re-imported export does not stack marks.
@@ -453,3 +454,28 @@ Persist `document.viewport` from `onChange` / `getDocument()` and pass it back o
453
454
  - `postMessage` optional bridge for cross-origin CDN hosting
454
455
  - Persist `FikaDocument` in `linked_materials` + `presentation` kind in workspace tabs
455
456
  - Split chunk / lazy `import('fika-editor/embed')` on first open of presentation tab
457
+
458
+ ### Generated Markdown outside the editor
459
+
460
+ `fika-editor/text` is a small ESM entry without React or DOM dependencies. It
461
+ uses CommonMark and math syntax trees with source positions, so servers and
462
+ browsers can recover escaped line breaks without changing code, formulas,
463
+ link destinations, or HTML source.
464
+
465
+ ```ts
466
+ import { normalizeGeneratedMarkdown, normalizeGeneratedNewlines } from 'fika-editor/text'
467
+
468
+ normalizeGeneratedMarkdown(String.raw`**Title**\r\nSecond line`)
469
+ // "**Title**\nSecond line" (a real newline)
470
+
471
+ // Preserve entities if only newline recovery is wanted:
472
+ normalizeGeneratedNewlines(String.raw`# Heading\n- **Point**`)
473
+ ```
474
+
475
+ `controller.markdownToHtml()` and Markdown layout slots use this normalization
476
+ before rendering formatted runs. `getDocument()` returns the complete target
477
+ slide during `revealSlide()`, so a host can safely snapshot it during animation.
478
+ The ProseMirror schema preserves hard line breaks and Markdown strikethrough
479
+ through storage, static rendering, and editing.
480
+ Pass `assetBaseUrl` to `mountFika()` to resolve lazy editor CSS under the same
481
+ asset directory as the embed stylesheet.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fika-editor",
3
- "version": "3.1.2",
3
+ "version": "3.2.1",
4
4
  "description": "Fika presentation editor embed bundle with a typed agentic bridge.",
5
5
  "type": "module",
6
6
  "main": "dist/embed/fika-embed.js",
@@ -11,7 +11,8 @@
11
11
  "dist/types",
12
12
  "docs/EMBED.md",
13
13
  "docs/AGENTIC_BRIDGE.md",
14
- "docs/RELEASE.md"
14
+ "docs/RELEASE.md",
15
+ "dist/text"
15
16
  ],
16
17
  "exports": {
17
18
  ".": {
@@ -29,7 +30,12 @@
29
30
  "default": "./dist/embed/fika-embed.css"
30
31
  },
31
32
  "./agentic-manifest.json": "./dist/embed/agentic-manifest.json",
32
- "./package.json": "./package.json"
33
+ "./package.json": "./package.json",
34
+ "./text": {
35
+ "types": "./dist/text/index.d.ts",
36
+ "import": "./dist/text/index.js",
37
+ "default": "./dist/text/index.js"
38
+ }
33
39
  },
34
40
  "engines": {
35
41
  "node": ">=24 <25"
@@ -49,7 +55,7 @@
49
55
  "dx:start": "1dxway start",
50
56
  "doctor": "react-doctor --no-telemetry",
51
57
  "build": "run-p type-check \"build-only {@}\" --",
52
- "build:embed": "node scripts/run-i18n.mjs --no-watch && npm run build:types && rsbuild build --config rsbuild.config.embed.ts && node scripts/rewrite-embed-base-uri.mjs && node scripts/merge-embed-css.mjs && node scripts/scope-embed-css.mjs && node scripts/extract-embed-fonts.mjs && node scripts/copy-embed-public.mjs && node scripts/generate-agentic-manifest.mjs",
58
+ "build:embed": "node scripts/run-i18n.mjs --no-watch && npm run build:text && npm run build:types && rsbuild build --config rsbuild.config.embed.ts && node scripts/rewrite-embed-base-uri.mjs && node scripts/merge-embed-css.mjs && node scripts/scope-embed-css.mjs && node scripts/extract-embed-fonts.mjs && node scripts/copy-embed-public.mjs && node scripts/generate-agentic-manifest.mjs",
53
59
  "agentic:manifest": "node scripts/generate-agentic-manifest.mjs",
54
60
  "build:types": "node -e \"require('node:fs').rmSync('dist/types',{recursive:true,force:true})\" && tsc -p tsconfig.types.json && node scripts/rewrite-embed-types.mjs",
55
61
  "prepack": "node scripts/verify-embed-pack.mjs",
@@ -62,7 +68,8 @@
62
68
  "lint:fix": "oxlint --fix",
63
69
  "test": "node scripts/test.mjs",
64
70
  "i18n": "node scripts/run-i18n.mjs",
65
- "i18n:build": "node scripts/run-i18n.mjs --no-watch"
71
+ "i18n:build": "node scripts/run-i18n.mjs --no-watch",
72
+ "build:text": "tsc -p tsconfig.text.json"
66
73
  },
67
74
  "author": "Matěj \"lofcz\" Štágl",
68
75
  "license": "MIT",
@@ -111,7 +118,10 @@
111
118
  "markdown-it-texmath": "^1.0.0",
112
119
  "mathlive": "npm:@lofcz/mathlive@^0.110.10",
113
120
  "mathml2omml-plus": "^0.6.4",
121
+ "mdast-util-from-markdown": "^2.0.3",
122
+ "mdast-util-math": "^3.0.0",
114
123
  "mermaid": "^11.17.0",
124
+ "micromark-extension-llm-math": "^3.1.1-20250610",
115
125
  "mitt": "^3.0.1",
116
126
  "nanoid": "^6.0.1",
117
127
  "number-precision": "^1.6.0",
@@ -1 +0,0 @@
1
- export const __rspack_esm_id=5992;export const __rspack_esm_ids=[5992];export const __webpack_modules__={88651(e,n,t){function a(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function r(e,n){let o=r.mergeDelimiters(n&&n.delimiters),l=n&&n.outerSpace||!1,s=n&&n.katexOptions||{};for(let a of(s.throwOnError=s.throwOnError||!1,s.macros=s.macros||n&&n.macros,r.katex||(n&&"object"==typeof n.engine?r.katex=n.engine:r.katex=t(37826)),o.inline))l&&"outerSpace"in a&&(a.outerSpace=!0),e.inline.ruler.before("escape",a.name,r.inline(a)),e.renderer.rules[a.name]=(e,n)=>a.tmpl.replace(/\$1/,r.render(e[n].content,!!a.displayMode,s));for(let n of o.block)e.block.ruler.before("fence",n.name,r.block(n)),e.renderer.rules[n.name]=(e,t)=>n.tmpl.replace(/\$2/,a(e[t].info)).replace(/\$1/,r.render(e[t].content,!0,s))}r.mergeDelimiters=function(e){let n=Array.isArray(e)?e:"string"==typeof e?[e]:["dollars"],t={inline:[],block:[]};for(let e of n)e in r.rules&&(t.inline.push(...r.rules[e].inline),t.block.push(...r.rules[e].block));return t},r.inline=e=>function(n,t){let a=n.pos,r=n.src,o=r.startsWith(e.tag,e.rex.lastIndex=a)&&(!e.pre||e.pre(r,e.outerSpace,a))&&e.rex.exec(r),l=!!o&&a<e.rex.lastIndex&&(!e.post||e.post(r,e.outerSpace,e.rex.lastIndex-1));if(l){if(!t){let t=n.push(e.name,"math",0);t.content=o[1],t.markup=e.tag}n.pos=e.rex.lastIndex}return l},r.block=e=>function(n,t,a,r){let o=n.bMarks[t]+n.tShift[t],l=n.src,s=l.startsWith(e.tag,e.rex.lastIndex=o)&&(!e.pre||e.pre(l,!1,o))&&e.rex.exec(l),i=!!s&&o<e.rex.lastIndex&&(!e.post||e.post(l,!1,e.rex.lastIndex-1));if(i&&!r){let r,o=e.rex.lastIndex-1;for(r=t;r<a&&(!(o>=n.bMarks[r]+n.tShift[r])||!(o<=n.eMarks[r]));r++);let l=n.lineMax,i=n.parentType;n.lineMax=r,n.parentType="math","blockquote"===i&&(s[1]=s[1].replace(/(\n*?^(?:\s*>)+)/gm,""));let m=n.push(e.name,"math",0);m.block=!0,m.tag=e.tag,m.markup="",m.content=s[1],m.info=s[s.length-1],m.map=[t,r+1],n.parentType=i,n.lineMax=l,n.line=r+1}return i},r.render=function(e,n,t){let o;t.displayMode=n;try{o=r.katex.renderToString(e,t)}catch(n){o=a(`${e}:${n.message}`)}return o},r.use=function(e){return r.katex=e,r},r.inlineRuleNames=["math_inline","math_inline_double"],r.blockRuleNames=["math_block","math_block_eqno"],r.$_pre=(e,n,t)=>{let a=t>0&&e[t-1].charCodeAt(0);return n?!a||32===a:!a||92!==a&&(a<48||a>57)},r.$_post=(e,n,t)=>{let a=e[t+1]&&e[t+1].charCodeAt(0);return n?!a||32===a||46===a||44===a||59===a:!a||a<48||a>57},r.rules={brackets:{inline:[{name:"math_inline",rex:/\\\((.+?)\\\)/gy,tmpl:"<eq>$1</eq>",tag:"\\("}],block:[{name:"math_block_eqno",rex:/\\\[(((?!\\\]|\\\[)[\s\S])+?)\\\]\s*?\(([^)$\r\n]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"\\["},{name:"math_block",rex:/\\\[([\s\S]+?)\\\]/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"\\["}]},doxygen:{inline:[{name:"math_inline",rex:/\\f\$(.+?)\\f\$/gy,tmpl:"<eq>$1</eq>",tag:"\\f$"}],block:[{name:"math_block_eqno",rex:/\\f\[([^]+?)\\f\]\s*?\(([^)\s]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"\\f["},{name:"math_block",rex:/\\f\[([^]+?)\\f\]/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"\\f["}]},gitlab:{inline:[{name:"math_inline",rex:/\$`(.+?)`\$/gy,tmpl:"<eq>$1</eq>",tag:"$`"}],block:[{name:"math_block_eqno",rex:/`{3}math\s*([^`]+?)\s*?`{3}\s*\(([^)\r\n]+?)\)/gm,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"```math"},{name:"math_block",rex:/`{3}math\s*([^`]*?)\s*`{3}/gm,tmpl:"<section><eqn>$1</eqn></section>",tag:"```math"}]},julia:{inline:[{name:"math_inline",rex:/`{2}([^`]+?)`{2}/gy,tmpl:"<eq>$1</eq>",tag:"``"},{name:"math_inline",rex:/\$((?:\S?)|(?:\S.*?\S))\$/gy,tmpl:"<eq>$1</eq>",tag:"$",spaceEnclosed:!1,pre:r.$_pre,post:r.$_post}],block:[{name:"math_block_eqno",rex:/`{3}math\s+?([^`]+?)\s+?`{3}\s*?\(([^)$\r\n]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"```math"},{name:"math_block",rex:/`{3}math\s+?([^`]+?)\s+?`{3}/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"```math"}]},kramdown:{inline:[{name:"math_inline",rex:/\${2}(.+?)\${2}/gy,tmpl:"<eq>$1</eq>",tag:"$$"}],block:[{name:"math_block_eqno",rex:/\${2}([^$]+?)\${2}\s*?\(([^)\s]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"$$"},{name:"math_block",rex:/\${2}([^$]+?)\${2}/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"$$"}]},beg_end:{inline:[],block:[{name:"math_block",rex:/(\\(?:begin)\{([a-z]+)\}[\s\S]+?\\(?:end)\{\2\})/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"\\"}]},dollars:{inline:[{name:"math_inline_double",rex:/\${2}([^$]*?[^\\])\${2}/gy,tmpl:"<section><eqn>$1</eqn></section>",tag:"$$",displayMode:!0,pre:r.$_pre,post:r.$_post},{name:"math_inline",rex:/\$((?:[^\s\\])|(?:\S.*?[^\s\\]))\$/gy,tmpl:"<eq>$1</eq>",tag:"$",outerSpace:!1,pre:r.$_pre,post:r.$_post}],block:[{name:"math_block_eqno",rex:/\${2}([^$]*?[^\\])\${2}\s*?\(([^)\s]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"$$"},{name:"math_block",rex:/\${2}([^$]*?[^\\])\${2}/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"$$"}]}},e.exports&&(e.exports=r)},37826(e,n,t){function a(){return""}t.r(n),n.default={renderToString:a},t.d(n,{renderToString:()=>a})}};
@@ -1 +0,0 @@
1
- export const __rspack_esm_id=8735;export const __rspack_esm_ids=[8735];export const __webpack_modules__={37826(_,e,r){function t(){return""}r.r(e),e.default={renderToString:t},r.d(e,{renderToString:()=>t})}};