@ng-native/components 0.13.0 → 0.15.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/keyboard-avoiding-view.js +3 -2
- package/dist/keyboard-avoiding-view.js.map +1 -1
- package/dist/markdown-blocks.d.ts +18 -1
- package/dist/markdown-blocks.js +112 -4
- package/dist/markdown-blocks.js.map +1 -1
- package/dist/markdown-columns.d.ts +24 -0
- package/dist/markdown-columns.js +58 -0
- package/dist/markdown-columns.js.map +1 -0
- package/dist/markdown-stream.d.ts +31 -0
- package/dist/markdown-stream.js +282 -0
- package/dist/markdown-stream.js.map +1 -0
- package/dist/markdown.d.ts +30 -3
- package/dist/markdown.js +131 -41
- package/dist/markdown.js.map +1 -1
- package/dist/modal.d.ts +8 -2
- package/dist/modal.js +8 -5
- package/dist/modal.js.map +1 -1
- package/dist/pressable.js +6 -6
- package/dist/pressable.js.map +1 -1
- package/dist/section-list.js +4 -4
- package/dist/section-list.js.map +1 -1
- package/dist/swipe-refresh-layout.js +1 -1
- package/dist/swipe-refresh-layout.js.map +1 -1
- package/dist/text-input.d.ts +6 -1
- package/dist/text-input.js +8 -2
- package/dist/text-input.js.map +1 -1
- package/dist/touchable-opacity.js +3 -2
- package/dist/touchable-opacity.js.map +1 -1
- package/dist/transforms.d.ts +6 -0
- package/dist/transforms.js +8 -0
- package/dist/transforms.js.map +1 -1
- package/dist/view-base.d.ts +1 -1
- package/dist/view-base.js +6 -4
- package/dist/view-base.js.map +1 -1
- package/dist/virtual-list.js +6 -5
- package/dist/virtual-list.js.map +1 -1
- package/dist/written-props.js +1 -0
- package/dist/written-props.js.map +1 -1
- package/dist/zero-size-warning.js +2 -2
- package/dist/zero-size-warning.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Component,
|
|
1
|
+
import { Component, computed, effect, inject, input, signal, booleanAttribute, } from '@angular/core';
|
|
2
2
|
import { Keyboard, LayoutAnimation } from '@ng-native/device';
|
|
3
3
|
import { ContentContainer } from "./content-container.js";
|
|
4
4
|
import { View } from "./view.js";
|
|
5
5
|
import { ViewBase } from "./view-base.js";
|
|
6
|
+
import { numberOr } from "./transforms.js";
|
|
6
7
|
import * as i0 from "@angular/core";
|
|
7
8
|
/**
|
|
8
9
|
* The names `LayoutEasing` knows. `'keyboard'` is among them: iOS reports its keyboard's own
|
|
@@ -64,7 +65,7 @@ export class KeyboardAvoidingView extends ViewBase {
|
|
|
64
65
|
* Extra room to leave above the keyboard, in points. RN's name, and in RN the place to put a
|
|
65
66
|
* header's height; here the view is measured in the window, so a navigation bar needs none.
|
|
66
67
|
*/
|
|
67
|
-
keyboardVerticalOffset = input(0, { ...(ngDevMode ? { debugName: "keyboardVerticalOffset" } : /* istanbul ignore next */ {}), transform:
|
|
68
|
+
keyboardVerticalOffset = input(0, { ...(ngDevMode ? { debugName: "keyboardVerticalOffset" } : /* istanbul ignore next */ {}), transform: numberOr(0) });
|
|
68
69
|
keyboard = inject(Keyboard).metrics;
|
|
69
70
|
layoutAnimation = inject(LayoutAnimation);
|
|
70
71
|
frame = signal(null, /* @ts-ignore */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-avoiding-view.js","sourceRoot":"","sources":["../src/keyboard-avoiding-view.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n numberAttribute,\n signal,\n} from '@angular/core';\nimport { Keyboard, LayoutAnimation, type LayoutEasing } from '@ng-native/device';\nimport type { NativeSyntheticEvent } from '@ng-native/fabric';\nimport type { Rect } from './events.ts';\nimport { ContentContainer } from './content-container.ts';\nimport { View } from './view.ts';\nimport { ViewBase } from './view-base.ts';\n\nexport type KeyboardAvoidingBehavior = 'padding' | 'height' | 'position';\n\n/**\n * The names `LayoutEasing` knows. `'keyboard'` is among them: iOS reports its keyboard's own\n * curve by that name, and RN's KeyboardAvoidingView animates on it.\n */\nconst LAYOUT_EASINGS: ReadonlySet<string> = new Set([\n 'linear',\n 'easeInEaseOut',\n 'easeIn',\n 'easeOut',\n 'spring',\n 'keyboard',\n]);\n\n/** A keyboard event's `easing`, narrowed to what `LayoutAnimation` accepts, or left to its default. */\nfunction layoutEasingOf(easing: string | undefined): LayoutEasing | undefined {\n return easing !== undefined && LAYOUT_EASINGS.has(easing) ? (easing as LayoutEasing) : undefined;\n}\n\n/**\n * Moves its content clear of the keyboard.\n *\n * RN's KeyboardAvoidingView is a JS composite: there is no native component, only a view whose\n * padding, height or position tracks the keyboard. Off a device the `Keyboard` service reports\n * nothing, so this renders as a plain view and does nothing, which is also the right behaviour on\n * a platform with no soft keyboard.\n *\n * The overlap is the view's own frame against the keyboard's top edge, so a view that does not\n * reach the bottom of the screen moves only as far as it must. Unlike RN, the frame's top is\n * measured in the window, which is what the keyboard's `screenY` is in: `(layout)` reports it in\n * the parent's coordinates, and on a screen under a navigation bar those start a bar's height\n * down. RN leaves that to `keyboardVerticalOffset`, and a view that was not told came up short by\n * exactly that much, its bottom edge still behind the keyboard. The overlap never exceeds the\n * view's own height either: padding a view by more than it has pushes its content out of it.\n * When the keyboard source reports no `screenY`, the keyboard's height stands in.\n *\n * The adjustment itself animates with the keyboard's own motion, the way RN's own\n * KeyboardAvoidingView does: a keyboard event's `duration` and `easing` configure the next\n * layout animation before the style change that moves this view, so the two run together rather\n * than the padding, height or position jumping to its resting place in one step.\n */\n@Component({\n selector: 'keyboard-avoiding-view',\n imports: [ContentContainer, View],\n template: `\n @if (behavior() === 'position') {\n <view\n [contentContainerOf]=\"node\"\n [contentContainerClass]=\"contentContainerClass()\"\n [style]=\"positioned()\"\n ><ng-content\n /></view>\n } @else {\n <ng-content />\n }\n `,\n host: {\n '[styleOverride]': 'avoidance()',\n '(layout)': 'onLayout($event)',\n },\n})\nexport class KeyboardAvoidingView extends ViewBase {\n /**\n * How to make room: pad the bottom, shrink the height, or shift the content up. RN leaves this\n * unset and does nothing; `padding` is the default here because that is what a form wants.\n */\n readonly behavior = input<KeyboardAvoidingBehavior>('padding');\n /** Styles for the inner view that `position` moves. */\n readonly contentContainerStyle = input<Record<string, unknown>>();\n /**\n * Classes for the inner view that `position` moves, matched as if it were written in the\n * template this view is. See `ContentContainer`.\n */\n readonly contentContainerClass = input<string>();\n /** Set to false to stop avoiding without removing the view. */\n readonly enabled = input(true, { transform: booleanAttribute });\n /**\n * Extra room to leave above the keyboard, in points. RN's name, and in RN the place to put a\n * header's height; here the view is measured in the window, so a navigation bar needs none.\n */\n readonly keyboardVerticalOffset = input(0, { transform: numberAttribute });\n\n private readonly keyboard = inject(Keyboard).metrics;\n private readonly layoutAnimation = inject(LayoutAnimation);\n private readonly frame = signal<Rect | null>(null);\n /** The height before any adjustment, which `height` mode shrinks from. */\n private initialHeight: number | null = null;\n\n /** How far the keyboard intrudes into this view, in points. */\n protected readonly overlap = computed(() => {\n if (!this.enabled()) return 0;\n const { height, screenY } = this.keyboard();\n if (height === 0) return 0;\n const frame = this.frame();\n const offset = this.keyboardVerticalOffset();\n if (!frame) return Math.max(0, height - offset);\n // In `height` mode the frame is the one this view already shrank, so measure the height it\n // had before, as RN adds its current adjustment back; otherwise the room it made reads as no\n // overlap and the height springs back under the keyboard.\n const extent =\n this.behavior() === 'height' ? (this.initialHeight ?? frame.height) : frame.height;\n const overlap = screenY === undefined ? height - offset : frame.y + extent - (screenY - offset);\n return Math.min(extent, Math.max(0, overlap));\n });\n\n /**\n * The adjustment, or nothing to adjust. Bound as the engine's style override rather than as\n * host style, because Angular ranks a host style binding under the template's: a caller's\n * `[style]=\"{flex: 1}\"` would otherwise keep its flex in height mode and the view would not\n * shrink at all. RN composes the adjustment after the caller's style, so it wins there too.\n */\n protected readonly avoidance = computed(() => {\n const overlap = this.overlap();\n const behavior = this.behavior();\n if (overlap === 0 || behavior === 'position') return undefined;\n if (behavior === 'height') {\n const base = this.initialHeight ?? this.frame()?.height;\n return base === undefined ? undefined : { height: base - overlap, flex: 0 };\n }\n return { paddingBottom: overlap };\n });\n\n protected readonly positioned = computed(() => ({\n ...this.contentContainerStyle(),\n bottom: this.overlap(),\n }));\n\n constructor() {\n super();\n // A duration of zero is Android's own keyboard event, which animates itself; there is\n // nothing to configure alongside it. `appear`/`leave` are turned off because this is an\n // update to a view already on screen, not one appearing or leaving - RN's own bare\n // `LayoutAnimation.configureNext({duration, update})` animates nothing else either.\n effect(() => {\n const { duration, easing } = this.keyboard();\n if (!this.enabled() || !duration) return;\n void this.layoutAnimation.animate(() => {}, {\n duration,\n easing: layoutEasingOf(easing),\n appear: 'none',\n leave: 'none',\n });\n });\n }\n\n protected onLayout(event: NativeSyntheticEvent<{ layout?: Rect }>): void {\n const layout = event.nativeEvent?.layout;\n if (!layout) return;\n this.frame.set({ ...layout, y: this.windowTop(layout.y) });\n // Only a layout that the keyboard did not cause counts as the natural height.\n if (this.overlap() === 0) this.initialHeight = layout.height;\n }\n\n /**\n * Where this view's top edge is in the window, or `fallback` where the platform cannot say.\n * Synchronous on the new architecture, so it answers for the layout being reported.\n */\n private windowTop(fallback: number): number {\n let top = fallback;\n this.engine.measure(this.node, (frame) => (top = frame.y));\n return top;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"keyboard-avoiding-view.js","sourceRoot":"","sources":["../src/keyboard-avoiding-view.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n computed,\n effect,\n inject,\n input,\n signal,\n booleanAttribute,\n} from '@angular/core';\nimport { Keyboard, LayoutAnimation, type LayoutEasing } from '@ng-native/device';\nimport type { NativeSyntheticEvent } from '@ng-native/fabric';\nimport type { Rect } from './events.ts';\nimport { ContentContainer } from './content-container.ts';\nimport { View } from './view.ts';\nimport { ViewBase } from './view-base.ts';\nimport { numberOr } from './transforms.ts';\n\nexport type KeyboardAvoidingBehavior = 'padding' | 'height' | 'position';\n\n/**\n * The names `LayoutEasing` knows. `'keyboard'` is among them: iOS reports its keyboard's own\n * curve by that name, and RN's KeyboardAvoidingView animates on it.\n */\nconst LAYOUT_EASINGS: ReadonlySet<string> = new Set([\n 'linear',\n 'easeInEaseOut',\n 'easeIn',\n 'easeOut',\n 'spring',\n 'keyboard',\n]);\n\n/** A keyboard event's `easing`, narrowed to what `LayoutAnimation` accepts, or left to its default. */\nfunction layoutEasingOf(easing: string | undefined): LayoutEasing | undefined {\n return easing !== undefined && LAYOUT_EASINGS.has(easing) ? (easing as LayoutEasing) : undefined;\n}\n\n/**\n * Moves its content clear of the keyboard.\n *\n * RN's KeyboardAvoidingView is a JS composite: there is no native component, only a view whose\n * padding, height or position tracks the keyboard. Off a device the `Keyboard` service reports\n * nothing, so this renders as a plain view and does nothing, which is also the right behaviour on\n * a platform with no soft keyboard.\n *\n * The overlap is the view's own frame against the keyboard's top edge, so a view that does not\n * reach the bottom of the screen moves only as far as it must. Unlike RN, the frame's top is\n * measured in the window, which is what the keyboard's `screenY` is in: `(layout)` reports it in\n * the parent's coordinates, and on a screen under a navigation bar those start a bar's height\n * down. RN leaves that to `keyboardVerticalOffset`, and a view that was not told came up short by\n * exactly that much, its bottom edge still behind the keyboard. The overlap never exceeds the\n * view's own height either: padding a view by more than it has pushes its content out of it.\n * When the keyboard source reports no `screenY`, the keyboard's height stands in.\n *\n * The adjustment itself animates with the keyboard's own motion, the way RN's own\n * KeyboardAvoidingView does: a keyboard event's `duration` and `easing` configure the next\n * layout animation before the style change that moves this view, so the two run together rather\n * than the padding, height or position jumping to its resting place in one step.\n */\n@Component({\n selector: 'keyboard-avoiding-view',\n imports: [ContentContainer, View],\n template: `\n @if (behavior() === 'position') {\n <view\n [contentContainerOf]=\"node\"\n [contentContainerClass]=\"contentContainerClass()\"\n [style]=\"positioned()\"\n ><ng-content\n /></view>\n } @else {\n <ng-content />\n }\n `,\n host: {\n '[styleOverride]': 'avoidance()',\n '(layout)': 'onLayout($event)',\n },\n})\nexport class KeyboardAvoidingView extends ViewBase {\n /**\n * How to make room: pad the bottom, shrink the height, or shift the content up. RN leaves this\n * unset and does nothing; `padding` is the default here because that is what a form wants.\n */\n readonly behavior = input<KeyboardAvoidingBehavior>('padding');\n /** Styles for the inner view that `position` moves. */\n readonly contentContainerStyle = input<Record<string, unknown>>();\n /**\n * Classes for the inner view that `position` moves, matched as if it were written in the\n * template this view is. See `ContentContainer`.\n */\n readonly contentContainerClass = input<string>();\n /** Set to false to stop avoiding without removing the view. */\n readonly enabled = input(true, { transform: booleanAttribute });\n /**\n * Extra room to leave above the keyboard, in points. RN's name, and in RN the place to put a\n * header's height; here the view is measured in the window, so a navigation bar needs none.\n */\n readonly keyboardVerticalOffset = input(0, { transform: numberOr(0) });\n\n private readonly keyboard = inject(Keyboard).metrics;\n private readonly layoutAnimation = inject(LayoutAnimation);\n private readonly frame = signal<Rect | null>(null);\n /** The height before any adjustment, which `height` mode shrinks from. */\n private initialHeight: number | null = null;\n\n /** How far the keyboard intrudes into this view, in points. */\n protected readonly overlap = computed(() => {\n if (!this.enabled()) return 0;\n const { height, screenY } = this.keyboard();\n if (height === 0) return 0;\n const frame = this.frame();\n const offset = this.keyboardVerticalOffset();\n if (!frame) return Math.max(0, height - offset);\n // In `height` mode the frame is the one this view already shrank, so measure the height it\n // had before, as RN adds its current adjustment back; otherwise the room it made reads as no\n // overlap and the height springs back under the keyboard.\n const extent =\n this.behavior() === 'height' ? (this.initialHeight ?? frame.height) : frame.height;\n const overlap = screenY === undefined ? height - offset : frame.y + extent - (screenY - offset);\n return Math.min(extent, Math.max(0, overlap));\n });\n\n /**\n * The adjustment, or nothing to adjust. Bound as the engine's style override rather than as\n * host style, because Angular ranks a host style binding under the template's: a caller's\n * `[style]=\"{flex: 1}\"` would otherwise keep its flex in height mode and the view would not\n * shrink at all. RN composes the adjustment after the caller's style, so it wins there too.\n */\n protected readonly avoidance = computed(() => {\n const overlap = this.overlap();\n const behavior = this.behavior();\n if (overlap === 0 || behavior === 'position') return undefined;\n if (behavior === 'height') {\n const base = this.initialHeight ?? this.frame()?.height;\n return base === undefined ? undefined : { height: base - overlap, flex: 0 };\n }\n return { paddingBottom: overlap };\n });\n\n protected readonly positioned = computed(() => ({\n ...this.contentContainerStyle(),\n bottom: this.overlap(),\n }));\n\n constructor() {\n super();\n // A duration of zero is Android's own keyboard event, which animates itself; there is\n // nothing to configure alongside it. `appear`/`leave` are turned off because this is an\n // update to a view already on screen, not one appearing or leaving - RN's own bare\n // `LayoutAnimation.configureNext({duration, update})` animates nothing else either.\n effect(() => {\n const { duration, easing } = this.keyboard();\n if (!this.enabled() || !duration) return;\n void this.layoutAnimation.animate(() => {}, {\n duration,\n easing: layoutEasingOf(easing),\n appear: 'none',\n leave: 'none',\n });\n });\n }\n\n protected onLayout(event: NativeSyntheticEvent<{ layout?: Rect }>): void {\n const layout = event.nativeEvent?.layout;\n if (!layout) return;\n this.frame.set({ ...layout, y: this.windowTop(layout.y) });\n // Only a layout that the keyboard did not cause counts as the natural height.\n if (this.overlap() === 0) this.initialHeight = layout.height;\n }\n\n /**\n * Where this view's top edge is in the window, or `fallback` where the platform cannot say.\n * Synchronous on the new architecture, so it answers for the layout being reported.\n */\n private windowTop(fallback: number): number {\n let top = fallback;\n this.engine.measure(this.node, (frame) => (top = frame.y));\n return top;\n }\n}\n"]}
|
|
@@ -73,7 +73,24 @@ export declare function schemeOf(url: string): string | null;
|
|
|
73
73
|
export declare function linkTarget(href: string): string | null;
|
|
74
74
|
/** Whether `href` is absolute in a scheme a link may be followed to, and so is opened on press. */
|
|
75
75
|
export declare function opensOnPress(href: string): boolean;
|
|
76
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* An image's address if it may be loaded: `http`, `https`, or a picture written into the document
|
|
78
|
+
* as a `data:` url of a raster type. A pasted screenshot arrives that way from a web editor, and
|
|
79
|
+
* a native image only decodes such a picture: nothing in it runs. Any other `data:` url, an SVG
|
|
80
|
+
* or HTML among them, is refused, as is every other scheme.
|
|
81
|
+
*/
|
|
77
82
|
export declare function imageSource(href: string): string | null;
|
|
78
83
|
/** The blocks a document of `tokens` is drawn as. */
|
|
79
84
|
export declare function markdownBlocks(tokens: readonly Token[]): MarkdownBlock[];
|
|
85
|
+
/**
|
|
86
|
+
* A key for `block` that stays the same while it is drawn: a block kept from the document before
|
|
87
|
+
* keeps its own, and one {@link shareBlocks} made in place of an edited one takes that one's.
|
|
88
|
+
*/
|
|
89
|
+
export declare function blockKey(block: MarkdownBlock): number;
|
|
90
|
+
/**
|
|
91
|
+
* `next`, with every part of it that equals the same part of `previous` replaced by that part, so
|
|
92
|
+
* a block that did not change is the object drawn already and is not drawn again. The two are
|
|
93
|
+
* matched from each end, so a block added or removed in the middle leaves those either side of it
|
|
94
|
+
* kept; what is left between is matched by position.
|
|
95
|
+
*/
|
|
96
|
+
export declare function shareBlocks(previous: readonly MarkdownBlock[] | undefined, next: readonly MarkdownBlock[]): readonly MarkdownBlock[];
|
package/dist/markdown-blocks.js
CHANGED
|
@@ -55,12 +55,27 @@ export function opensOnPress(href) {
|
|
|
55
55
|
const scheme = schemeOf(href.trim());
|
|
56
56
|
return scheme !== null && LINK_SCHEMES.has(scheme);
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* An image's address if it may be loaded: `http`, `https`, or a picture written into the document
|
|
60
|
+
* as a `data:` url of a raster type. A pasted screenshot arrives that way from a web editor, and
|
|
61
|
+
* a native image only decodes such a picture: nothing in it runs. Any other `data:` url, an SVG
|
|
62
|
+
* or HTML among them, is refused, as is every other scheme.
|
|
63
|
+
*/
|
|
59
64
|
export function imageSource(href) {
|
|
60
65
|
const source = href.trim();
|
|
61
66
|
const scheme = schemeOf(source);
|
|
62
|
-
|
|
67
|
+
if (scheme === null)
|
|
68
|
+
return null;
|
|
69
|
+
if (IMAGE_SCHEMES.has(scheme))
|
|
70
|
+
return source;
|
|
71
|
+
if (scheme !== 'data')
|
|
72
|
+
return null;
|
|
73
|
+
// A native loader may not decode a url with whitespace in it, so a wrapped payload is joined.
|
|
74
|
+
const joined = source.replace(/\s+/g, '');
|
|
75
|
+
return DATA_IMAGE.test(joined) ? joined : null;
|
|
63
76
|
}
|
|
77
|
+
/** A base64 `data:` url of a raster picture: PNG, JPEG, GIF or WebP. */
|
|
78
|
+
const DATA_IMAGE = /^data:image\/(?:png|jpe?g|gif|webp);base64,[a-z0-9+/=]+$/i;
|
|
64
79
|
/** A link's or an image's target with its entities decoded, or null when it is not a string. */
|
|
65
80
|
function hrefOf(token) {
|
|
66
81
|
const href = token.href;
|
|
@@ -76,7 +91,8 @@ const BLOCKS = {
|
|
|
76
91
|
heading: (token) => {
|
|
77
92
|
const heading = token;
|
|
78
93
|
const depth = Math.min(Math.max(heading.depth, 1), 6);
|
|
79
|
-
|
|
94
|
+
const children = trimmedEnd(inlines(heading.tokens));
|
|
95
|
+
return [{ kind: 'heading', element: HEADINGS[depth - 1], children }];
|
|
80
96
|
},
|
|
81
97
|
paragraph: (token) => paragraphs(token.tokens),
|
|
82
98
|
text: (token) => {
|
|
@@ -143,7 +159,7 @@ function paragraphs(tokens) {
|
|
|
143
159
|
let run = [];
|
|
144
160
|
const flush = () => {
|
|
145
161
|
if (run.some((one) => one.kind !== 'text' || one.text.trim())) {
|
|
146
|
-
out.push({ kind: 'paragraph', children: run });
|
|
162
|
+
out.push({ kind: 'paragraph', children: trimmedEnd(run) });
|
|
147
163
|
}
|
|
148
164
|
run = [];
|
|
149
165
|
};
|
|
@@ -160,6 +176,21 @@ function paragraphs(tokens) {
|
|
|
160
176
|
flush();
|
|
161
177
|
return out;
|
|
162
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Runs without the whitespace at their end, which a paragraph does not draw. The engine works out
|
|
181
|
+
* that trim when a run changes, not when a run arrives after it, so a run that ended a streaming
|
|
182
|
+
* paragraph and stops ending it would otherwise keep its trimmed space: `Run ` and then a code
|
|
183
|
+
* span would read as one word.
|
|
184
|
+
*/
|
|
185
|
+
function trimmedEnd(runs) {
|
|
186
|
+
const last = runs.at(-1);
|
|
187
|
+
if (last?.kind !== 'text')
|
|
188
|
+
return runs;
|
|
189
|
+
const text = last.text.replace(/[ \t\n\r\f]+$/, '');
|
|
190
|
+
if (text === last.text)
|
|
191
|
+
return runs;
|
|
192
|
+
return text ? [...runs.slice(0, -1), { kind: 'text', text }] : runs.slice(0, -1);
|
|
193
|
+
}
|
|
163
194
|
function inlines(tokens) {
|
|
164
195
|
return (tokens ?? []).flatMap(inline);
|
|
165
196
|
}
|
|
@@ -209,4 +240,81 @@ function inline(token) {
|
|
|
209
240
|
return draw(token);
|
|
210
241
|
return token.raw ? [{ kind: 'text', text: token.raw }] : [];
|
|
211
242
|
}
|
|
243
|
+
const keys = new WeakMap();
|
|
244
|
+
let lastKey = 0;
|
|
245
|
+
/**
|
|
246
|
+
* A key for `block` that stays the same while it is drawn: a block kept from the document before
|
|
247
|
+
* keeps its own, and one {@link shareBlocks} made in place of an edited one takes that one's.
|
|
248
|
+
*/
|
|
249
|
+
export function blockKey(block) {
|
|
250
|
+
let key = keys.get(block);
|
|
251
|
+
if (key === undefined)
|
|
252
|
+
keys.set(block, (key = ++lastKey));
|
|
253
|
+
return key;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* `next`, with every part of it that equals the same part of `previous` replaced by that part, so
|
|
257
|
+
* a block that did not change is the object drawn already and is not drawn again. The two are
|
|
258
|
+
* matched from each end, so a block added or removed in the middle leaves those either side of it
|
|
259
|
+
* kept; what is left between is matched by position.
|
|
260
|
+
*/
|
|
261
|
+
export function shareBlocks(previous, next) {
|
|
262
|
+
return previous ? share(previous, next) : next;
|
|
263
|
+
}
|
|
264
|
+
function share(previous, next) {
|
|
265
|
+
if (previous === next || typeof previous !== 'object' || typeof next !== 'object')
|
|
266
|
+
return next;
|
|
267
|
+
if (previous === null || next === null)
|
|
268
|
+
return next;
|
|
269
|
+
if (Array.isArray(previous) !== Array.isArray(next))
|
|
270
|
+
return next;
|
|
271
|
+
if (Array.isArray(previous))
|
|
272
|
+
return shareArray(previous, next);
|
|
273
|
+
return shareObject(previous, next);
|
|
274
|
+
}
|
|
275
|
+
function shareArray(previous, next) {
|
|
276
|
+
const out = next.slice();
|
|
277
|
+
let start = 0;
|
|
278
|
+
while (start < out.length && start < previous.length) {
|
|
279
|
+
const kept = share(previous[start], out[start]);
|
|
280
|
+
if (kept !== previous[start])
|
|
281
|
+
break;
|
|
282
|
+
out[start++] = kept;
|
|
283
|
+
}
|
|
284
|
+
let end = 0;
|
|
285
|
+
while (end < out.length - start && end < previous.length - start) {
|
|
286
|
+
const at = out.length - 1 - end;
|
|
287
|
+
const was = previous.length - 1 - end;
|
|
288
|
+
const kept = share(previous[was], out[at]);
|
|
289
|
+
if (kept !== previous[was])
|
|
290
|
+
break;
|
|
291
|
+
out[at] = kept;
|
|
292
|
+
end++;
|
|
293
|
+
}
|
|
294
|
+
for (let i = start; i < out.length - end && i < previous.length - end; i++) {
|
|
295
|
+
out[i] = share(previous[i], out[i]);
|
|
296
|
+
}
|
|
297
|
+
const same = out.length === previous.length && out.every((one, i) => one === previous[i]);
|
|
298
|
+
return same ? previous : out;
|
|
299
|
+
}
|
|
300
|
+
function shareObject(previous, next) {
|
|
301
|
+
if (previous['kind'] !== next['kind'])
|
|
302
|
+
return next;
|
|
303
|
+
const names = Object.keys(next);
|
|
304
|
+
let same = names.length === Object.keys(previous).length;
|
|
305
|
+
const out = {};
|
|
306
|
+
for (const name of names) {
|
|
307
|
+
const was = previous[name];
|
|
308
|
+
const kept = Object.hasOwn(previous, name) ? share(was, next[name]) : next[name];
|
|
309
|
+
if (kept !== was)
|
|
310
|
+
same = false;
|
|
311
|
+
out[name] = kept;
|
|
312
|
+
}
|
|
313
|
+
if (same)
|
|
314
|
+
return previous;
|
|
315
|
+
const key = keys.get(previous);
|
|
316
|
+
if (key !== undefined)
|
|
317
|
+
keys.set(out, key);
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
212
320
|
//# sourceMappingURL=markdown-blocks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-blocks.js","sourceRoot":"","sources":["../src/markdown-blocks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Marked's tokens, turned into the blocks `<markdown>` draws: the entities decoded, the links and\n * images held to the schemes they may use, raw HTML made plain text, and an image lifted out of\n * its paragraph, since a `<text>` cannot hold one.\n *\n * Plain functions with no Angular in them, so the policy is the same wherever the tokens came from:\n * `marked.lexer` on the device, or a build step that lexed them ahead of time.\n */\nimport type { Token, Tokens } from 'marked';\nimport { HTML_ENTITIES } from './html-entities.ts';\n\n/** The elements a document is drawn with, by the HTML names the Markdown would have produced. */\nexport type MarkdownElement =\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'p'\n | 'blockquote'\n | 'ul'\n | 'ol'\n | 'li'\n | 'marker'\n | 'pre'\n | 'code'\n | 'strong'\n | 'em'\n | 'del'\n | 'a'\n | 'hr'\n | 'img'\n | 'table'\n | 'tr'\n | 'th'\n | 'td';\n\nexport type MarkdownInline =\n | { readonly kind: 'text'; readonly text: string }\n | { readonly kind: 'strong' | 'em' | 'del'; readonly children: readonly MarkdownInline[] }\n | { readonly kind: 'code'; readonly text: string }\n | {\n readonly kind: 'link';\n readonly href: string;\n readonly title: string | null;\n readonly children: readonly MarkdownInline[];\n };\n\nexport interface MarkdownListItem {\n readonly marker: string;\n readonly children: readonly MarkdownBlock[];\n}\n\nexport type MarkdownBlock =\n | {\n readonly kind: 'heading';\n readonly element: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n readonly children: readonly MarkdownInline[];\n }\n | { readonly kind: 'paragraph'; readonly children: readonly MarkdownInline[] }\n | { readonly kind: 'image'; readonly src: string; readonly alt: string }\n | { readonly kind: 'blockquote'; readonly children: readonly MarkdownBlock[] }\n | {\n readonly kind: 'list';\n readonly element: 'ul' | 'ol';\n readonly items: readonly MarkdownListItem[];\n }\n | { readonly kind: 'code'; readonly text: string }\n | { readonly kind: 'hr' }\n | {\n readonly kind: 'table';\n readonly header: readonly (readonly MarkdownInline[])[];\n readonly rows: readonly (readonly (readonly MarkdownInline[])[])[];\n };\n\nlet namedEntities: Map<string, string> | undefined;\n\n/** The character HTML names `name`, from its full table, built on the first name looked up. */\nfunction namedEntity(name: string): string | undefined {\n if (!namedEntities) {\n namedEntities = new Map();\n const pairs = HTML_ENTITIES.split(' ');\n for (let i = 0; i + 1 < pairs.length; i += 2) namedEntities.set(pairs[i]!, pairs[i + 1]!);\n }\n return namedEntities.get(name);\n}\n\nconst ENTITY = /&(?:#(\\d{1,7})|#[xX]([0-9a-fA-F]{1,6})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;\n\n/**\n * The text an HTML reader would show for `text`, in one pass so `&lt;` is `<` and not `<`.\n * A reference to no character, or to one outside Unicode, is the replacement character, as HTML\n * makes it; a name HTML does not have stays as written.\n */\nexport function decodeEntities(text: string): string {\n if (!text.includes('&')) return text;\n return text.replace(ENTITY, (whole, decimal?: string, hex?: string, name?: string) => {\n if (name !== undefined) return namedEntity(name) ?? whole;\n const code = decimal !== undefined ? Number(decimal) : parseInt(hex!, 16);\n const valid = code > 0 && code <= 0x10ffff && (code < 0xd800 || code > 0xdfff);\n return valid ? String.fromCodePoint(code) : '�';\n });\n}\n\n/**\n * What a URL parser ignores while it reads a scheme: the C0 controls and the space, and the C1\n * controls and DEL beside them. `java\\tscript:` is `javascript:` to the thing that opens it.\n */\nconst IGNORED_IN_SCHEME = /[\\u0000- \\u007f-\\u009f]/g;\nconst SCHEME = /^([a-z][a-z0-9+.-]*):/;\n\n/** The scheme `url` would be opened with, lowercased, or null for a relative one. */\nexport function schemeOf(url: string): string | null {\n return SCHEME.exec(url.replace(IGNORED_IN_SCHEME, '').toLowerCase())?.[1] ?? null;\n}\n\nconst LINK_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);\nconst IMAGE_SCHEMES = new Set(['http', 'https']);\n\n/**\n * A link's target if it may be followed: `http`, `https`, `mailto`, `tel`, or relative, which is\n * the app's to resolve. Anything else, `javascript:`, `data:`, `vbscript:` and `file:` among\n * them, is null, and the link is drawn as its text. The scheme is read after the trim, so\n * whitespace the trim strips cannot hide one.\n */\nexport function linkTarget(href: string): string | null {\n const target = href.trim();\n const scheme = schemeOf(target);\n return scheme === null || LINK_SCHEMES.has(scheme) ? target : null;\n}\n\n/** Whether `href` is absolute in a scheme a link may be followed to, and so is opened on press. */\nexport function opensOnPress(href: string): boolean {\n const scheme = schemeOf(href.trim());\n return scheme !== null && LINK_SCHEMES.has(scheme);\n}\n\n/** An image's address if it may be loaded: `http` or `https` only. */\nexport function imageSource(href: string): string | null {\n const source = href.trim();\n const scheme = schemeOf(source);\n return scheme !== null && IMAGE_SCHEMES.has(scheme) ? source : null;\n}\n\n/** A link's or an image's target with its entities decoded, or null when it is not a string. */\nfunction hrefOf(token: Token): string | null {\n const href: unknown = (token as Tokens.Link).href;\n return typeof href === 'string' ? decodeEntities(href) : null;\n}\n\nconst HEADINGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;\n\n/** The blocks a document of `tokens` is drawn as. */\nexport function markdownBlocks(tokens: readonly Token[]): MarkdownBlock[] {\n return tokens.flatMap(block);\n}\n\n/** What each kind of block token is drawn as. A kind not here is drawn as its source text. */\nconst BLOCKS: Readonly<Record<string, (token: Token) => MarkdownBlock[]>> = {\n heading: (token) => {\n const heading = token as Tokens.Heading;\n const depth = Math.min(Math.max(heading.depth, 1), 6);\n return [{ kind: 'heading', element: HEADINGS[depth - 1]!, children: inlines(heading.tokens) }];\n },\n paragraph: (token) => paragraphs((token as Tokens.Paragraph).tokens),\n text: (token) => {\n const text = token as Tokens.Text;\n return text.tokens\n ? paragraphs(text.tokens)\n : [{ kind: 'paragraph', children: [plain(softBreaks(text.text))] }];\n },\n blockquote: (token) => [\n { kind: 'blockquote', children: markdownBlocks((token as Tokens.Blockquote).tokens) },\n ],\n list: (token) => [list(token as Tokens.List)],\n code: (token) => [{ kind: 'code', text: indented((token as Tokens.Code).text) }],\n hr: () => [{ kind: 'hr' }],\n table: (token) => {\n const table = token as Tokens.Table;\n return [\n {\n kind: 'table',\n header: table.header.map((cell) => inlines(cell.tokens)),\n rows: table.rows.map((row) => row.map((cell) => inlines(cell.tokens))),\n },\n ];\n },\n html: (token) => literal((token as Tokens.HTML).text),\n space: () => [],\n def: () => [],\n checkbox: () => [],\n};\n\nfunction block(token: Token): MarkdownBlock[] {\n const draw = Object.hasOwn(BLOCKS, token.type) ? BLOCKS[token.type]! : null;\n return draw ? draw(token) : literal(token.raw ?? '');\n}\n\n/**\n * A code block's text with its first line's indent made of no-break spaces: a paragraph loses the\n * spaces at its start, and a block's indent is part of the code.\n */\nfunction indented(text: string): string {\n return text.replace(/^ +/, (spaces) => '\\u00a0'.repeat(spaces.length));\n}\n\n/** Text drawn as written, which is what raw HTML and a token nothing here knows become. */\nfunction literal(text: string): MarkdownBlock[] {\n const trimmed = text.replace(/\\n+$/, '');\n return trimmed.trim() ? [{ kind: 'paragraph', children: [{ kind: 'text', text: trimmed }] }] : [];\n}\n\nfunction list(token: Tokens.List): MarkdownBlock {\n const start = typeof token.start === 'number' ? token.start : 1;\n return {\n kind: 'list',\n element: token.ordered ? 'ol' : 'ul',\n items: token.items.map((item, index) => ({\n marker: item.task ? (item.checked ? '☑' : '☐') : token.ordered ? `${start + index}.` : '•',\n children: markdownBlocks(item.tokens),\n })),\n };\n}\n\n/**\n * A paragraph's runs, split around the images at its top level: an image is a block of its own,\n * and the text either side of it a paragraph each. An image whose address is refused, and one\n * inside a link or an emphasis, is its alt text instead.\n */\nfunction paragraphs(tokens: readonly Token[]): MarkdownBlock[] {\n const out: MarkdownBlock[] = [];\n let run: MarkdownInline[] = [];\n const flush = () => {\n if (run.some((one) => one.kind !== 'text' || one.text.trim())) {\n out.push({ kind: 'paragraph', children: run });\n }\n run = [];\n };\n for (const token of tokens) {\n const href = token.type === 'image' ? hrefOf(token) : null;\n const src = href === null ? null : imageSource(href);\n if (src === null) {\n run.push(...inline(token));\n continue;\n }\n flush();\n out.push({ kind: 'image', src, alt: decodeEntities((token as Tokens.Image).text) });\n }\n flush();\n return out;\n}\n\nfunction inlines(tokens: readonly Token[] | undefined): MarkdownInline[] {\n return (tokens ?? []).flatMap(inline);\n}\n\n/**\n * Text with each line break the source wrapped it at made a space, as CommonMark reads a soft\n * break, with the spaces around it. A hard break is a `br` token of its own, and stays one.\n */\nfunction softBreaks(text: string): string {\n return text.replace(/ *\\n */g, ' ');\n}\n\nfunction plain(text: string): MarkdownInline {\n return { kind: 'text', text: decodeEntities(text) };\n}\n\nfunction emphasis(token: Token): MarkdownInline[] {\n const kind = token.type as 'strong' | 'em' | 'del';\n return [{ kind, children: inlines((token as Tokens.Strong).tokens) }];\n}\n\n/** What each kind of inline token is drawn as. A kind not here is drawn as its source text. */\nconst INLINES: Readonly<Record<string, (token: Token) => MarkdownInline[]>> = {\n text: (token) => {\n const text = token as Tokens.Text;\n return text.tokens ? inlines(text.tokens) : [plain(softBreaks(text.text))];\n },\n escape: (token) => [plain((token as Tokens.Escape).text)],\n strong: emphasis,\n em: emphasis,\n del: emphasis,\n codespan: (token) => [{ kind: 'code', text: (token as Tokens.Codespan).text }],\n br: () => [{ kind: 'text', text: '\\n' }],\n link: (token) => {\n const link = token as Tokens.Link;\n const children = inlines(link.tokens);\n const decoded = hrefOf(link);\n const href = decoded === null ? null : linkTarget(decoded);\n if (href === null) return children;\n const title = link.title ? decodeEntities(link.title) : null;\n return [{ kind: 'link', href, title, children }];\n },\n image: (token) => [plain((token as Tokens.Image).text)],\n html: (token) => [{ kind: 'text', text: (token as Tokens.HTML).text }],\n checkbox: () => [],\n};\n\nfunction inline(token: Token): MarkdownInline[] {\n const draw = Object.hasOwn(INLINES, token.type) ? INLINES[token.type]! : null;\n if (draw) return draw(token);\n return token.raw ? [{ kind: 'text', text: token.raw }] : [];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"markdown-blocks.js","sourceRoot":"","sources":["../src/markdown-blocks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Marked's tokens, turned into the blocks `<markdown>` draws: the entities decoded, the links and\n * images held to the schemes they may use, raw HTML made plain text, and an image lifted out of\n * its paragraph, since a `<text>` cannot hold one.\n *\n * Plain functions with no Angular in them, so the policy is the same wherever the tokens came from:\n * `marked.lexer` on the device, or a build step that lexed them ahead of time.\n */\nimport type { Token, Tokens } from 'marked';\nimport { HTML_ENTITIES } from './html-entities.ts';\n\n/** The elements a document is drawn with, by the HTML names the Markdown would have produced. */\nexport type MarkdownElement =\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'p'\n | 'blockquote'\n | 'ul'\n | 'ol'\n | 'li'\n | 'marker'\n | 'pre'\n | 'code'\n | 'strong'\n | 'em'\n | 'del'\n | 'a'\n | 'hr'\n | 'img'\n | 'table'\n | 'tr'\n | 'th'\n | 'td';\n\nexport type MarkdownInline =\n | { readonly kind: 'text'; readonly text: string }\n | { readonly kind: 'strong' | 'em' | 'del'; readonly children: readonly MarkdownInline[] }\n | { readonly kind: 'code'; readonly text: string }\n | {\n readonly kind: 'link';\n readonly href: string;\n readonly title: string | null;\n readonly children: readonly MarkdownInline[];\n };\n\nexport interface MarkdownListItem {\n readonly marker: string;\n readonly children: readonly MarkdownBlock[];\n}\n\nexport type MarkdownBlock =\n | {\n readonly kind: 'heading';\n readonly element: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n readonly children: readonly MarkdownInline[];\n }\n | { readonly kind: 'paragraph'; readonly children: readonly MarkdownInline[] }\n | { readonly kind: 'image'; readonly src: string; readonly alt: string }\n | { readonly kind: 'blockquote'; readonly children: readonly MarkdownBlock[] }\n | {\n readonly kind: 'list';\n readonly element: 'ul' | 'ol';\n readonly items: readonly MarkdownListItem[];\n }\n | { readonly kind: 'code'; readonly text: string }\n | { readonly kind: 'hr' }\n | {\n readonly kind: 'table';\n readonly header: readonly (readonly MarkdownInline[])[];\n readonly rows: readonly (readonly (readonly MarkdownInline[])[])[];\n };\n\nlet namedEntities: Map<string, string> | undefined;\n\n/** The character HTML names `name`, from its full table, built on the first name looked up. */\nfunction namedEntity(name: string): string | undefined {\n if (!namedEntities) {\n namedEntities = new Map();\n const pairs = HTML_ENTITIES.split(' ');\n for (let i = 0; i + 1 < pairs.length; i += 2) namedEntities.set(pairs[i]!, pairs[i + 1]!);\n }\n return namedEntities.get(name);\n}\n\nconst ENTITY = /&(?:#(\\d{1,7})|#[xX]([0-9a-fA-F]{1,6})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;\n\n/**\n * The text an HTML reader would show for `text`, in one pass so `&lt;` is `<` and not `<`.\n * A reference to no character, or to one outside Unicode, is the replacement character, as HTML\n * makes it; a name HTML does not have stays as written.\n */\nexport function decodeEntities(text: string): string {\n if (!text.includes('&')) return text;\n return text.replace(ENTITY, (whole, decimal?: string, hex?: string, name?: string) => {\n if (name !== undefined) return namedEntity(name) ?? whole;\n const code = decimal !== undefined ? Number(decimal) : parseInt(hex!, 16);\n const valid = code > 0 && code <= 0x10ffff && (code < 0xd800 || code > 0xdfff);\n return valid ? String.fromCodePoint(code) : '�';\n });\n}\n\n/**\n * What a URL parser ignores while it reads a scheme: the C0 controls and the space, and the C1\n * controls and DEL beside them. `java\\tscript:` is `javascript:` to the thing that opens it.\n */\nconst IGNORED_IN_SCHEME = /[\\u0000- \\u007f-\\u009f]/g;\nconst SCHEME = /^([a-z][a-z0-9+.-]*):/;\n\n/** The scheme `url` would be opened with, lowercased, or null for a relative one. */\nexport function schemeOf(url: string): string | null {\n return SCHEME.exec(url.replace(IGNORED_IN_SCHEME, '').toLowerCase())?.[1] ?? null;\n}\n\nconst LINK_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);\nconst IMAGE_SCHEMES = new Set(['http', 'https']);\n\n/**\n * A link's target if it may be followed: `http`, `https`, `mailto`, `tel`, or relative, which is\n * the app's to resolve. Anything else, `javascript:`, `data:`, `vbscript:` and `file:` among\n * them, is null, and the link is drawn as its text. The scheme is read after the trim, so\n * whitespace the trim strips cannot hide one.\n */\nexport function linkTarget(href: string): string | null {\n const target = href.trim();\n const scheme = schemeOf(target);\n return scheme === null || LINK_SCHEMES.has(scheme) ? target : null;\n}\n\n/** Whether `href` is absolute in a scheme a link may be followed to, and so is opened on press. */\nexport function opensOnPress(href: string): boolean {\n const scheme = schemeOf(href.trim());\n return scheme !== null && LINK_SCHEMES.has(scheme);\n}\n\n/**\n * An image's address if it may be loaded: `http`, `https`, or a picture written into the document\n * as a `data:` url of a raster type. A pasted screenshot arrives that way from a web editor, and\n * a native image only decodes such a picture: nothing in it runs. Any other `data:` url, an SVG\n * or HTML among them, is refused, as is every other scheme.\n */\nexport function imageSource(href: string): string | null {\n const source = href.trim();\n const scheme = schemeOf(source);\n if (scheme === null) return null;\n if (IMAGE_SCHEMES.has(scheme)) return source;\n if (scheme !== 'data') return null;\n // A native loader may not decode a url with whitespace in it, so a wrapped payload is joined.\n const joined = source.replace(/\\s+/g, '');\n return DATA_IMAGE.test(joined) ? joined : null;\n}\n\n/** A base64 `data:` url of a raster picture: PNG, JPEG, GIF or WebP. */\nconst DATA_IMAGE = /^data:image\\/(?:png|jpe?g|gif|webp);base64,[a-z0-9+/=]+$/i;\n\n/** A link's or an image's target with its entities decoded, or null when it is not a string. */\nfunction hrefOf(token: Token): string | null {\n const href: unknown = (token as Tokens.Link).href;\n return typeof href === 'string' ? decodeEntities(href) : null;\n}\n\nconst HEADINGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;\n\n/** The blocks a document of `tokens` is drawn as. */\nexport function markdownBlocks(tokens: readonly Token[]): MarkdownBlock[] {\n return tokens.flatMap(block);\n}\n\n/** What each kind of block token is drawn as. A kind not here is drawn as its source text. */\nconst BLOCKS: Readonly<Record<string, (token: Token) => MarkdownBlock[]>> = {\n heading: (token) => {\n const heading = token as Tokens.Heading;\n const depth = Math.min(Math.max(heading.depth, 1), 6);\n const children = trimmedEnd(inlines(heading.tokens));\n return [{ kind: 'heading', element: HEADINGS[depth - 1]!, children }];\n },\n paragraph: (token) => paragraphs((token as Tokens.Paragraph).tokens),\n text: (token) => {\n const text = token as Tokens.Text;\n return text.tokens\n ? paragraphs(text.tokens)\n : [{ kind: 'paragraph', children: [plain(softBreaks(text.text))] }];\n },\n blockquote: (token) => [\n { kind: 'blockquote', children: markdownBlocks((token as Tokens.Blockquote).tokens) },\n ],\n list: (token) => [list(token as Tokens.List)],\n code: (token) => [{ kind: 'code', text: indented((token as Tokens.Code).text) }],\n hr: () => [{ kind: 'hr' }],\n table: (token) => {\n const table = token as Tokens.Table;\n return [\n {\n kind: 'table',\n header: table.header.map((cell) => inlines(cell.tokens)),\n rows: table.rows.map((row) => row.map((cell) => inlines(cell.tokens))),\n },\n ];\n },\n html: (token) => literal((token as Tokens.HTML).text),\n space: () => [],\n def: () => [],\n checkbox: () => [],\n};\n\nfunction block(token: Token): MarkdownBlock[] {\n const draw = Object.hasOwn(BLOCKS, token.type) ? BLOCKS[token.type]! : null;\n return draw ? draw(token) : literal(token.raw ?? '');\n}\n\n/**\n * A code block's text with its first line's indent made of no-break spaces: a paragraph loses the\n * spaces at its start, and a block's indent is part of the code.\n */\nfunction indented(text: string): string {\n return text.replace(/^ +/, (spaces) => '\\u00a0'.repeat(spaces.length));\n}\n\n/** Text drawn as written, which is what raw HTML and a token nothing here knows become. */\nfunction literal(text: string): MarkdownBlock[] {\n const trimmed = text.replace(/\\n+$/, '');\n return trimmed.trim() ? [{ kind: 'paragraph', children: [{ kind: 'text', text: trimmed }] }] : [];\n}\n\nfunction list(token: Tokens.List): MarkdownBlock {\n const start = typeof token.start === 'number' ? token.start : 1;\n return {\n kind: 'list',\n element: token.ordered ? 'ol' : 'ul',\n items: token.items.map((item, index) => ({\n marker: item.task ? (item.checked ? '☑' : '☐') : token.ordered ? `${start + index}.` : '•',\n children: markdownBlocks(item.tokens),\n })),\n };\n}\n\n/**\n * A paragraph's runs, split around the images at its top level: an image is a block of its own,\n * and the text either side of it a paragraph each. An image whose address is refused, and one\n * inside a link or an emphasis, is its alt text instead.\n */\nfunction paragraphs(tokens: readonly Token[]): MarkdownBlock[] {\n const out: MarkdownBlock[] = [];\n let run: MarkdownInline[] = [];\n const flush = () => {\n if (run.some((one) => one.kind !== 'text' || one.text.trim())) {\n out.push({ kind: 'paragraph', children: trimmedEnd(run) });\n }\n run = [];\n };\n for (const token of tokens) {\n const href = token.type === 'image' ? hrefOf(token) : null;\n const src = href === null ? null : imageSource(href);\n if (src === null) {\n run.push(...inline(token));\n continue;\n }\n flush();\n out.push({ kind: 'image', src, alt: decodeEntities((token as Tokens.Image).text) });\n }\n flush();\n return out;\n}\n\n/**\n * Runs without the whitespace at their end, which a paragraph does not draw. The engine works out\n * that trim when a run changes, not when a run arrives after it, so a run that ended a streaming\n * paragraph and stops ending it would otherwise keep its trimmed space: `Run ` and then a code\n * span would read as one word.\n */\nfunction trimmedEnd(runs: MarkdownInline[]): MarkdownInline[] {\n const last = runs.at(-1);\n if (last?.kind !== 'text') return runs;\n const text = last.text.replace(/[ \\t\\n\\r\\f]+$/, '');\n if (text === last.text) return runs;\n return text ? [...runs.slice(0, -1), { kind: 'text', text }] : runs.slice(0, -1);\n}\n\nfunction inlines(tokens: readonly Token[] | undefined): MarkdownInline[] {\n return (tokens ?? []).flatMap(inline);\n}\n\n/**\n * Text with each line break the source wrapped it at made a space, as CommonMark reads a soft\n * break, with the spaces around it. A hard break is a `br` token of its own, and stays one.\n */\nfunction softBreaks(text: string): string {\n return text.replace(/ *\\n */g, ' ');\n}\n\nfunction plain(text: string): MarkdownInline {\n return { kind: 'text', text: decodeEntities(text) };\n}\n\nfunction emphasis(token: Token): MarkdownInline[] {\n const kind = token.type as 'strong' | 'em' | 'del';\n return [{ kind, children: inlines((token as Tokens.Strong).tokens) }];\n}\n\n/** What each kind of inline token is drawn as. A kind not here is drawn as its source text. */\nconst INLINES: Readonly<Record<string, (token: Token) => MarkdownInline[]>> = {\n text: (token) => {\n const text = token as Tokens.Text;\n return text.tokens ? inlines(text.tokens) : [plain(softBreaks(text.text))];\n },\n escape: (token) => [plain((token as Tokens.Escape).text)],\n strong: emphasis,\n em: emphasis,\n del: emphasis,\n codespan: (token) => [{ kind: 'code', text: (token as Tokens.Codespan).text }],\n br: () => [{ kind: 'text', text: '\\n' }],\n link: (token) => {\n const link = token as Tokens.Link;\n const children = inlines(link.tokens);\n const decoded = hrefOf(link);\n const href = decoded === null ? null : linkTarget(decoded);\n if (href === null) return children;\n const title = link.title ? decodeEntities(link.title) : null;\n return [{ kind: 'link', href, title, children }];\n },\n image: (token) => [plain((token as Tokens.Image).text)],\n html: (token) => [{ kind: 'text', text: (token as Tokens.HTML).text }],\n checkbox: () => [],\n};\n\nfunction inline(token: Token): MarkdownInline[] {\n const draw = Object.hasOwn(INLINES, token.type) ? INLINES[token.type]! : null;\n if (draw) return draw(token);\n return token.raw ? [{ kind: 'text', text: token.raw }] : [];\n}\n\nconst keys = new WeakMap<object, number>();\nlet lastKey = 0;\n\n/**\n * A key for `block` that stays the same while it is drawn: a block kept from the document before\n * keeps its own, and one {@link shareBlocks} made in place of an edited one takes that one's.\n */\nexport function blockKey(block: MarkdownBlock): number {\n let key = keys.get(block);\n if (key === undefined) keys.set(block, (key = ++lastKey));\n return key;\n}\n\n/**\n * `next`, with every part of it that equals the same part of `previous` replaced by that part, so\n * a block that did not change is the object drawn already and is not drawn again. The two are\n * matched from each end, so a block added or removed in the middle leaves those either side of it\n * kept; what is left between is matched by position.\n */\nexport function shareBlocks(\n previous: readonly MarkdownBlock[] | undefined,\n next: readonly MarkdownBlock[],\n): readonly MarkdownBlock[] {\n return previous ? (share(previous, next) as readonly MarkdownBlock[]) : next;\n}\n\nfunction share(previous: unknown, next: unknown): unknown {\n if (previous === next || typeof previous !== 'object' || typeof next !== 'object') return next;\n if (previous === null || next === null) return next;\n if (Array.isArray(previous) !== Array.isArray(next)) return next;\n if (Array.isArray(previous)) return shareArray(previous, next as unknown[]);\n return shareObject(previous as Record<string, unknown>, next as Record<string, unknown>);\n}\n\nfunction shareArray(previous: readonly unknown[], next: readonly unknown[]): readonly unknown[] {\n const out = next.slice();\n let start = 0;\n while (start < out.length && start < previous.length) {\n const kept = share(previous[start], out[start]);\n if (kept !== previous[start]) break;\n out[start++] = kept;\n }\n let end = 0;\n while (end < out.length - start && end < previous.length - start) {\n const at = out.length - 1 - end;\n const was = previous.length - 1 - end;\n const kept = share(previous[was], out[at]);\n if (kept !== previous[was]) break;\n out[at] = kept;\n end++;\n }\n for (let i = start; i < out.length - end && i < previous.length - end; i++) {\n out[i] = share(previous[i], out[i]);\n }\n const same = out.length === previous.length && out.every((one, i) => one === previous[i]);\n return same ? previous : out;\n}\n\nfunction shareObject(\n previous: Record<string, unknown>,\n next: Record<string, unknown>,\n): Record<string, unknown> {\n if (previous['kind'] !== next['kind']) return next;\n const names = Object.keys(next);\n let same = names.length === Object.keys(previous).length;\n const out: Record<string, unknown> = {};\n for (const name of names) {\n const was = previous[name];\n const kept = Object.hasOwn(previous, name) ? share(was, next[name]) : next[name];\n if (kept !== was) same = false;\n out[name] = kept;\n }\n if (same) return previous;\n const key = keys.get(previous);\n if (key !== undefined) keys.set(out, key);\n return out;\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LayoutEvent } from './events.ts';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class MarkdownColumns {
|
|
4
|
+
/** The table whose columns are lined up. */
|
|
5
|
+
readonly table: import("@angular/core").InputSignal<{
|
|
6
|
+
readonly kind: "table";
|
|
7
|
+
readonly header: readonly (readonly import("./markdown-blocks.ts").MarkdownInline[])[];
|
|
8
|
+
readonly rows: readonly (readonly (readonly import("./markdown-blocks.ts").MarkdownInline[])[])[];
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* The measured cells and each column's widest. A new table keeps only the cells of the rows
|
|
12
|
+
* and columns it still has, so one that goes and comes back starts unmeasured.
|
|
13
|
+
*/
|
|
14
|
+
private readonly columns;
|
|
15
|
+
/** The least width of a cell in `column`, or null until one of its cells is measured. */
|
|
16
|
+
width(column: number): number | null;
|
|
17
|
+
/**
|
|
18
|
+
* Records the width the text of the cell at `row` (0 for the header) and `column` was laid out
|
|
19
|
+
* at. Only a column whose widest cell narrows is gone over again.
|
|
20
|
+
*/
|
|
21
|
+
measure(row: number, column: number, event: Partial<LayoutEvent>): void;
|
|
22
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MarkdownColumns, never>;
|
|
23
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<MarkdownColumns, "[markdownColumns]", ["markdownColumns"], { "table": { "alias": "markdownColumns"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The columns of a Markdown table, each as wide as its widest cell.
|
|
3
|
+
*
|
|
4
|
+
* Yoga has no table layout, so a table is rows of cells, and a cell is as wide as its own text.
|
|
5
|
+
* Each cell's text reports its width with `(layout)`; the widest in a column becomes the least
|
|
6
|
+
* width of every cell in it, which lines the column up. The text is measured inside that least
|
|
7
|
+
* width rather than at it, so a cell that narrows, or a row or column that goes, narrows its
|
|
8
|
+
* column again.
|
|
9
|
+
*/
|
|
10
|
+
import { Directive, input, linkedSignal } from '@angular/core';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
const widest = (column) => column.reduce((most, width) => Math.max(most, width), 0);
|
|
13
|
+
export class MarkdownColumns {
|
|
14
|
+
/** The table whose columns are lined up. */
|
|
15
|
+
table = input.required({ ...(ngDevMode ? { debugName: "table" } : /* istanbul ignore next */ {}), alias: 'markdownColumns' });
|
|
16
|
+
/**
|
|
17
|
+
* The measured cells and each column's widest. A new table keeps only the cells of the rows
|
|
18
|
+
* and columns it still has, so one that goes and comes back starts unmeasured.
|
|
19
|
+
*/
|
|
20
|
+
columns = linkedSignal({ ...(ngDevMode ? { debugName: "columns" } : /* istanbul ignore next */ {}), source: this.table,
|
|
21
|
+
computation: ({ header, rows }, previous) => {
|
|
22
|
+
const cells = header.map((_, column) => (previous?.value.cells[column] ?? []).slice(0, rows.length + 1));
|
|
23
|
+
return { cells, widths: cells.map(widest) };
|
|
24
|
+
} });
|
|
25
|
+
/** The least width of a cell in `column`, or null until one of its cells is measured. */
|
|
26
|
+
width(column) {
|
|
27
|
+
return this.columns().widths[column] || null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Records the width the text of the cell at `row` (0 for the header) and `column` was laid out
|
|
31
|
+
* at. Only a column whose widest cell narrows is gone over again.
|
|
32
|
+
*/
|
|
33
|
+
measure(row, column, event) {
|
|
34
|
+
const width = event.nativeEvent?.layout.width;
|
|
35
|
+
if (width === undefined || row > this.table().rows.length)
|
|
36
|
+
return;
|
|
37
|
+
const { cells, widths } = this.columns();
|
|
38
|
+
const measured = cells[column];
|
|
39
|
+
if (!measured || measured[row] === width)
|
|
40
|
+
return;
|
|
41
|
+
const was = measured[row];
|
|
42
|
+
measured[row] = width;
|
|
43
|
+
const most = widths[column];
|
|
44
|
+
const next = width >= most ? width : was === most ? widest(measured) : most;
|
|
45
|
+
if (next === most)
|
|
46
|
+
return;
|
|
47
|
+
const changed = [...widths];
|
|
48
|
+
changed[column] = next;
|
|
49
|
+
this.columns.set({ cells, widths: changed });
|
|
50
|
+
}
|
|
51
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: MarkdownColumns, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
52
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.2.0", type: MarkdownColumns, isStandalone: true, selector: "[markdownColumns]", inputs: { table: { classPropertyName: "table", publicName: "markdownColumns", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["markdownColumns"], ngImport: i0 });
|
|
53
|
+
}
|
|
54
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: MarkdownColumns, decorators: [{
|
|
55
|
+
type: Directive,
|
|
56
|
+
args: [{ selector: '[markdownColumns]', exportAs: 'markdownColumns' }]
|
|
57
|
+
}], propDecorators: { table: [{ type: i0.Input, args: [{ isSignal: true, alias: "markdownColumns", required: true }] }] } });
|
|
58
|
+
//# sourceMappingURL=markdown-columns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-columns.js","sourceRoot":"","sources":["../src/markdown-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;;AAa/D,MAAM,MAAM,GAAG,CAAC,MAAyB,EAAU,EAAE,CACnD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AAG3D,MAAM,OAAO,eAAe;IAC1B,4CAA4C;IACnC,KAAK,GAAG,KAAK,CAAC,QAAQ,4EAAkB,KAAK,EAAE,iBAAiB,GAAG,CAAC;IAE7E;;;OAGG;IACc,OAAO,GAAG,YAAY,8EACrC,MAAM,EAAE,IAAI,CAAC,KAAK;QAClB,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE;YAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CACrC,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAChE,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9C,CAAC,GACD,CAAC;IAEH,yFAAyF;IACzF,KAAK,CAAC,MAAc;QAClB,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IAC/C,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,GAAW,EAAE,MAAc,EAAE,KAA2B;QAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC;QAC9C,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QAClE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,KAAK;YAAE,OAAO;QACjD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC1B,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5E,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO;QAC1B,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/C,CAAC;uGAzCU,eAAe;2FAAf,eAAe;;2FAAf,eAAe;kBAD3B,SAAS;mBAAC,EAAE,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,EAAE,iBAAiB,EAAE","sourcesContent":["/**\n * The columns of a Markdown table, each as wide as its widest cell.\n *\n * Yoga has no table layout, so a table is rows of cells, and a cell is as wide as its own text.\n * Each cell's text reports its width with `(layout)`; the widest in a column becomes the least\n * width of every cell in it, which lines the column up. The text is measured inside that least\n * width rather than at it, so a cell that narrows, or a row or column that goes, narrows its\n * column again.\n */\nimport { Directive, input, linkedSignal } from '@angular/core';\nimport type { LayoutEvent } from './events.ts';\nimport type { MarkdownBlock } from './markdown-blocks.ts';\n\ntype MarkdownTable = Extract<MarkdownBlock, { kind: 'table' }>;\n\ninterface Columns {\n /** Each cell's text width, by column and then row, with the header as row 0. */\n readonly cells: number[][];\n /** Each column's widest cell. */\n readonly widths: readonly number[];\n}\n\nconst widest = (column: readonly number[]): number =>\n column.reduce((most, width) => Math.max(most, width), 0);\n\n@Directive({ selector: '[markdownColumns]', exportAs: 'markdownColumns' })\nexport class MarkdownColumns {\n /** The table whose columns are lined up. */\n readonly table = input.required<MarkdownTable>({ alias: 'markdownColumns' });\n\n /**\n * The measured cells and each column's widest. A new table keeps only the cells of the rows\n * and columns it still has, so one that goes and comes back starts unmeasured.\n */\n private readonly columns = linkedSignal<MarkdownTable, Columns>({\n source: this.table,\n computation: ({ header, rows }, previous) => {\n const cells = header.map((_, column) =>\n (previous?.value.cells[column] ?? []).slice(0, rows.length + 1),\n );\n return { cells, widths: cells.map(widest) };\n },\n });\n\n /** The least width of a cell in `column`, or null until one of its cells is measured. */\n width(column: number): number | null {\n return this.columns().widths[column] || null;\n }\n\n /**\n * Records the width the text of the cell at `row` (0 for the header) and `column` was laid out\n * at. Only a column whose widest cell narrows is gone over again.\n */\n measure(row: number, column: number, event: Partial<LayoutEvent>): void {\n const width = event.nativeEvent?.layout.width;\n if (width === undefined || row > this.table().rows.length) return;\n const { cells, widths } = this.columns();\n const measured = cells[column];\n if (!measured || measured[row] === width) return;\n const was = measured[row];\n measured[row] = width;\n const most = widths[column]!;\n const next = width >= most ? width : was === most ? widest(measured) : most;\n if (next === most) return;\n const changed = [...widths];\n changed[column] = next;\n this.columns.set({ cells, widths: changed });\n }\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Markdown source that grows as it streams in, lexed a chunk at a time: the blocks before the
|
|
3
|
+
* last one are kept, only the last is lexed again, and syntax still open at its end is held back
|
|
4
|
+
* until it closes, so a half-written code span, emphasis, link, image, table row or code fence
|
|
5
|
+
* line never shows as the characters it is written with.
|
|
6
|
+
*
|
|
7
|
+
* Plain functions and a class with no Angular in them, beside `markdown-blocks.ts`.
|
|
8
|
+
*/
|
|
9
|
+
import { type Token } from 'marked';
|
|
10
|
+
/**
|
|
11
|
+
* Lexes a source that grows, keeping the blocks before the last one from the lex before. A source
|
|
12
|
+
* that does not start with the one before is lexed whole, as is one whose new tail defines a link
|
|
13
|
+
* reference or changes one, since that can change a block already kept.
|
|
14
|
+
*/
|
|
15
|
+
export declare class MarkdownStream {
|
|
16
|
+
private source;
|
|
17
|
+
/** The blocks before the last one, whose raw text is the start of the source exactly. */
|
|
18
|
+
private settled;
|
|
19
|
+
/** How much of the source the settled blocks are. */
|
|
20
|
+
private settledLength;
|
|
21
|
+
/** The link references the settled blocks define. */
|
|
22
|
+
private settledLinks;
|
|
23
|
+
/** The link references the whole source defines. */
|
|
24
|
+
private links;
|
|
25
|
+
lex(text: string): Token[];
|
|
26
|
+
/**
|
|
27
|
+
* Keeps the tail's blocks before its last one as settled, as far as their raw text is the
|
|
28
|
+
* source's: marked trims some blocks' raw text, and the lex after starts again from there.
|
|
29
|
+
*/
|
|
30
|
+
private settle;
|
|
31
|
+
}
|