article-content-renderer-vue2 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +354 -10
- package/dist/article-content-renderer-vue2.css +1 -1
- package/dist/index.cjs +8 -1
- package/dist/index.js +6629 -630
- package/dist/types/components/ArticleContentRenderer.vue.d.ts +7 -1
- package/dist/types/core/resource-question.d.ts +45 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/protocols/types.d.ts +4 -1
- package/dist/types/protocols/v1/metadata.d.ts +3 -2
- package/dist/types/protocols/v1/schema.d.ts +2 -0
- package/dist/types/types.d.ts +86 -7
- package/package.json +4 -1
- package/protocol/article-content-protocol-v1.json +460 -22
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Vue from 'vue';
|
|
2
|
-
import type { CustomSlot, ResolveArticleButtonLink } from '../types.js';
|
|
2
|
+
import type { CustomSlot, ResolveArticleButtonLink, ResolveCustomLink, OnAnchorNavigate } from '../types.js';
|
|
3
3
|
interface ArticleContentRendererProps {
|
|
4
4
|
document: unknown;
|
|
5
5
|
protocolVersion: number;
|
|
@@ -7,6 +7,12 @@ interface ArticleContentRendererProps {
|
|
|
7
7
|
customSlots: CustomSlot[];
|
|
8
8
|
imageBaseUrl: string;
|
|
9
9
|
resolveArticleButtonLink?: ResolveArticleButtonLink;
|
|
10
|
+
resolveCustomLink?: ResolveCustomLink;
|
|
11
|
+
revealedKeys: string[];
|
|
12
|
+
articleKey?: string | number;
|
|
13
|
+
scrollContainer?: HTMLElement | (() => HTMLElement | null);
|
|
14
|
+
scrollOffset: number;
|
|
15
|
+
onAnchorNavigate?: OnAnchorNavigate;
|
|
10
16
|
}
|
|
11
17
|
declare const _default: import("vue/types/vue").ExtendedVue<Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue>, {}, {}, {}, ArticleContentRendererProps, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin>;
|
|
12
18
|
export default _default;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type VNode } from 'vue';
|
|
2
|
+
import type { ArticleDocument, ArticleRendererRuntime, OnAnchorNavigate, RenderIssue, ResourceQuestionSelectEvent, TopLevelNode } from '../types.js';
|
|
3
|
+
type RecordValue = Record<string, unknown>;
|
|
4
|
+
export declare function isRecord(value: unknown): value is RecordValue;
|
|
5
|
+
export declare function getVisibleContent(document: ArticleDocument, revealedKeys?: readonly string[]): TopLevelNode[];
|
|
6
|
+
export interface NavigationSessionOptions {
|
|
7
|
+
document: unknown;
|
|
8
|
+
articleKey?: string | number;
|
|
9
|
+
revealedKeys: readonly string[];
|
|
10
|
+
scrollContainer?: HTMLElement | (() => HTMLElement | null);
|
|
11
|
+
scrollOffset: number;
|
|
12
|
+
onAnchorNavigate?: OnAnchorNavigate;
|
|
13
|
+
reportNavigationIssue(issue: RenderIssue): void;
|
|
14
|
+
emitSelect(event: ResourceQuestionSelectEvent): void;
|
|
15
|
+
}
|
|
16
|
+
/** A fresh render's registry; only its owning lifecycle component can activate it. */
|
|
17
|
+
export declare class NavigationSession {
|
|
18
|
+
readonly options: NavigationSessionOptions;
|
|
19
|
+
readonly anchors: Map<string, HTMLElement>;
|
|
20
|
+
readonly allAnchors: Set<string>;
|
|
21
|
+
readonly selections: Map<string, Map<string, ResourceQuestionSelectEvent>>;
|
|
22
|
+
select: (event: ResourceQuestionSelectEvent) => void;
|
|
23
|
+
constructor(options: NavigationSessionOptions);
|
|
24
|
+
anchorHooks(id: string): {
|
|
25
|
+
insert: (vnode: VNode) => void;
|
|
26
|
+
postpatch: (_old: VNode, vnode: VNode) => void;
|
|
27
|
+
destroy: (vnode: VNode) => void;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Persistent state owned by the renderer's existing, DOM-free lifecycle component. */
|
|
31
|
+
export declare class ResourceNavigation {
|
|
32
|
+
private session?;
|
|
33
|
+
private pending?;
|
|
34
|
+
private generation;
|
|
35
|
+
private frame?;
|
|
36
|
+
private disposed;
|
|
37
|
+
readonly api: ArticleRendererRuntime;
|
|
38
|
+
constructor();
|
|
39
|
+
update(session: NavigationSession): void;
|
|
40
|
+
cancelPendingNavigation(): void;
|
|
41
|
+
schedule(): void;
|
|
42
|
+
private navigate;
|
|
43
|
+
dispose(): void;
|
|
44
|
+
}
|
|
45
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import type { PluginObject } from 'vue';
|
|
|
2
2
|
import ArticleContentRenderer from './components/ArticleContentRenderer.vue';
|
|
3
3
|
export { ArticleContentRenderer };
|
|
4
4
|
export default ArticleContentRenderer;
|
|
5
|
+
export { getVisibleContent } from './core/resource-question.js';
|
|
5
6
|
export declare const ArticleContentRendererPlugin: PluginObject<never>;
|
|
6
7
|
export { CURRENT_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, validateArticleDocument, } from './protocols/registry.js';
|
|
7
8
|
export { ARTICLE_CONTENT_PROTOCOL_V1 } from './protocols/v1/metadata.js';
|
|
8
|
-
export type { ArticleButtonAttrs, ArticleButtonActionAttrs, ArticleButtonActionNode, ArticleButtonClickPayload, ArticleButtonLink, ArticleButtonLinkAttrs, ArticleButtonLinkDescriptor, ArticleButtonLinkNode, ArticleButtonNode, ArticleButtonStyle, ArticleContentNode, ArticleDocument, ArticleMark, BlockNode, BlockquoteNode, BoldMark, BulletListNode, CodeBlockNode, CodeBlockTextNode, CodeMark, CustomSlot, HeadingNode, HorizontalRuleNode, ImageAlign, ImageNode, ItalicMark, LinkMark, LinkTarget, ListItemNode, OrderedListNode, ParagraphNode, ProtocolVersion, RenderIssue, RenderIssueCode, ResolveArticleButtonLink, StrikeMark, TableCellNode, TableNode, TableRowNode, TextAlign, TextNode, UnderlineMark, ValidationResult, } from './types.js';
|
|
9
|
+
export type { ArticleButtonAttrs, ArticleButtonActionAttrs, ArticleButtonActionNode, ArticleButtonClickPayload, ArticleButtonLink, ArticleButtonLinkAttrs, ArticleButtonLinkDescriptor, ArticleButtonLinkNode, ArticleButtonNode, ArticleButtonStyle, ArticleContentNode, ArticleDocument, ArticleRendererRuntime, ArticleMark, BlockNode, BlockquoteNode, BoldMark, BulletListNode, CodeBlockNode, CodeBlockTextNode, CodeMark, CustomLink, CustomLinkDescriptor, CustomLinkMark, CustomLinkMarkAttrs, CustomSlot, HeadingNode, HighlightMark, HorizontalRuleNode, ImageAlign, ImageLayout, ImageNode, ItalicMark, HrefLinkMark, HrefLinkMarkAttrs, LinkMark, LinkTarget, ListItemNode, OrderedListNode, ParagraphNode, ProtocolVersion, RenderIssue, RenderIssueCode, ResolveArticleButtonLink, ResolveCustomLink, ResourceQuestionAttrs, ResourceQuestionNode, ResourceQuestionOption, ResourceQuestionSelectEvent, ResourceQuestionNavigationRequest, OnAnchorNavigate, StrikeMark, TableCellNode, TableNode, TableRowNode, TextAlign, TextNode, TextStyleMark, TopLevelNode, UnderlineMark, ValidationResult, } from './types.js';
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { CreateElement, VNode } from 'vue';
|
|
2
|
-
import type {
|
|
2
|
+
import type { NavigationSession } from '../core/resource-question.js';
|
|
3
|
+
import type { ArticleButtonClickPayload, RenderIssue, ResolveArticleButtonLink, ResolveCustomLink, ValidationResult } from '../types.js';
|
|
3
4
|
export interface RenderContext {
|
|
4
5
|
createElement: CreateElement;
|
|
5
6
|
customSlots: readonly ResolvedCustomSlot[];
|
|
6
7
|
imageBaseUrl: string;
|
|
7
8
|
resolveArticleButtonLink?: ResolveArticleButtonLink;
|
|
9
|
+
resolveCustomLink?: ResolveCustomLink;
|
|
8
10
|
emitArticleButtonClick: (payload: ArticleButtonClickPayload) => void;
|
|
9
11
|
reportIssue: (issue: RenderIssue) => void;
|
|
12
|
+
navigation: NavigationSession;
|
|
10
13
|
}
|
|
11
14
|
export interface ResolvedCustomSlot {
|
|
12
15
|
id: string;
|
|
@@ -2,11 +2,12 @@ export declare const ARTICLE_CONTENT_PROTOCOL_V1: Readonly<{
|
|
|
2
2
|
fileFormat: "article-content-protocol";
|
|
3
3
|
fileFormatVersion: 1;
|
|
4
4
|
mediaType: "application/vnd.article-content-protocol+json";
|
|
5
|
-
id: "article-content-v1";
|
|
5
|
+
id: "article-content-v1-with-extensions";
|
|
6
6
|
version: 1;
|
|
7
|
-
name: "Article Content Protocol v1";
|
|
7
|
+
name: "Article Content Protocol v1 + Extensions";
|
|
8
8
|
status: "draft";
|
|
9
9
|
rootNode: "doc";
|
|
10
10
|
nodeTypes: readonly string[];
|
|
11
11
|
markTypes: readonly string[];
|
|
12
|
+
extensions: readonly string[];
|
|
12
13
|
}>;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type ProtocolVersion = 1;
|
|
2
2
|
export type TextAlign = 'left' | 'center' | 'right' | 'justify';
|
|
3
3
|
export type ImageAlign = 'left' | 'center' | 'right';
|
|
4
|
+
export type ImageLayout = 'two-column';
|
|
4
5
|
export type ArticleButtonStyle = 'text' | 'button' | 'link';
|
|
5
6
|
export type LinkTarget = '_blank' | '_self';
|
|
6
7
|
export interface CustomSlot {
|
|
@@ -22,14 +23,42 @@ export interface UnderlineMark {
|
|
|
22
23
|
export interface CodeMark {
|
|
23
24
|
type: 'code';
|
|
24
25
|
}
|
|
25
|
-
export interface
|
|
26
|
+
export interface HrefLinkMarkAttrs {
|
|
27
|
+
type?: 'href';
|
|
28
|
+
href: string;
|
|
29
|
+
id?: never;
|
|
30
|
+
title?: never;
|
|
31
|
+
target?: LinkTarget;
|
|
32
|
+
}
|
|
33
|
+
export interface CustomLinkMarkAttrs {
|
|
34
|
+
type: 'custom';
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
href?: never;
|
|
38
|
+
target?: LinkTarget;
|
|
39
|
+
}
|
|
40
|
+
export interface HrefLinkMark {
|
|
41
|
+
type: 'link';
|
|
42
|
+
attrs: HrefLinkMarkAttrs;
|
|
43
|
+
}
|
|
44
|
+
export interface CustomLinkMark {
|
|
26
45
|
type: 'link';
|
|
46
|
+
attrs: CustomLinkMarkAttrs;
|
|
47
|
+
}
|
|
48
|
+
export type LinkMark = HrefLinkMark | CustomLinkMark;
|
|
49
|
+
export interface TextStyleMark {
|
|
50
|
+
type: 'textStyle';
|
|
51
|
+
attrs: {
|
|
52
|
+
color: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export interface HighlightMark {
|
|
56
|
+
type: 'highlight';
|
|
27
57
|
attrs: {
|
|
28
|
-
|
|
29
|
-
target?: LinkTarget;
|
|
58
|
+
color: string;
|
|
30
59
|
};
|
|
31
60
|
}
|
|
32
|
-
export type ArticleMark = BoldMark | ItalicMark | StrikeMark | UnderlineMark | CodeMark | LinkMark;
|
|
61
|
+
export type ArticleMark = BoldMark | ItalicMark | StrikeMark | UnderlineMark | CodeMark | LinkMark | TextStyleMark | HighlightMark;
|
|
33
62
|
export interface TextNode {
|
|
34
63
|
type: 'text';
|
|
35
64
|
text: string;
|
|
@@ -39,6 +68,8 @@ export interface ParagraphNode {
|
|
|
39
68
|
type: 'paragraph';
|
|
40
69
|
attrs?: {
|
|
41
70
|
textAlign?: TextAlign;
|
|
71
|
+
anchorId?: string;
|
|
72
|
+
fontSize?: number;
|
|
42
73
|
};
|
|
43
74
|
content?: TextNode[];
|
|
44
75
|
}
|
|
@@ -47,6 +78,7 @@ export interface HeadingNode {
|
|
|
47
78
|
attrs: {
|
|
48
79
|
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
49
80
|
textAlign?: TextAlign;
|
|
81
|
+
anchorId?: string;
|
|
50
82
|
};
|
|
51
83
|
content?: TextNode[];
|
|
52
84
|
}
|
|
@@ -92,6 +124,7 @@ export interface ImageNode {
|
|
|
92
124
|
width?: number;
|
|
93
125
|
height?: number;
|
|
94
126
|
imageAlign?: ImageAlign;
|
|
127
|
+
imageLayout?: ImageLayout;
|
|
95
128
|
};
|
|
96
129
|
}
|
|
97
130
|
interface ArticleButtonBaseAttrs {
|
|
@@ -118,6 +151,43 @@ export interface ArticleButtonLinkNode {
|
|
|
118
151
|
attrs: ArticleButtonLinkAttrs;
|
|
119
152
|
}
|
|
120
153
|
export type ArticleButtonNode = ArticleButtonActionNode | ArticleButtonLinkNode;
|
|
154
|
+
export interface ResourceQuestionOption {
|
|
155
|
+
id: string;
|
|
156
|
+
label: string;
|
|
157
|
+
targetAnchorId?: string;
|
|
158
|
+
}
|
|
159
|
+
export interface ResourceQuestionAttrs {
|
|
160
|
+
id: string;
|
|
161
|
+
resourceId: string;
|
|
162
|
+
title: string;
|
|
163
|
+
description: string;
|
|
164
|
+
options: ResourceQuestionOption[];
|
|
165
|
+
hideFollowing: boolean;
|
|
166
|
+
revealKey: string;
|
|
167
|
+
}
|
|
168
|
+
export interface ResourceQuestionNode {
|
|
169
|
+
type: 'resourceQuestion';
|
|
170
|
+
attrs: ResourceQuestionAttrs;
|
|
171
|
+
}
|
|
172
|
+
export interface ResourceQuestionSelectEvent {
|
|
173
|
+
questionId: string;
|
|
174
|
+
resourceId: string;
|
|
175
|
+
optionId: string;
|
|
176
|
+
revealKey: string;
|
|
177
|
+
targetAnchorId?: string;
|
|
178
|
+
}
|
|
179
|
+
/** One option click's navigation request. Methods become no-ops after cancellation or replacement. */
|
|
180
|
+
export interface ResourceQuestionNavigationRequest extends ResourceQuestionSelectEvent {
|
|
181
|
+
targetAnchorId: string;
|
|
182
|
+
scrollToAnchor(): void;
|
|
183
|
+
cancel(): void;
|
|
184
|
+
}
|
|
185
|
+
/** Suppresses automatic navigation; call request.scrollToAnchor() when the host is ready. */
|
|
186
|
+
export type OnAnchorNavigate = (request: Readonly<ResourceQuestionNavigationRequest>) => void | Promise<void>;
|
|
187
|
+
/** Delivered through renderer-ready; keep one handle per renderer instance. */
|
|
188
|
+
export interface ArticleRendererRuntime {
|
|
189
|
+
cancelPendingNavigation(): void;
|
|
190
|
+
}
|
|
121
191
|
export interface TableNode {
|
|
122
192
|
type: 'table';
|
|
123
193
|
content: TableRowNode[];
|
|
@@ -133,9 +203,10 @@ export interface TableCellNode {
|
|
|
133
203
|
export type BlockNode = ParagraphNode | HeadingNode | BlockquoteNode | BulletListNode | OrderedListNode | CodeBlockNode | HorizontalRuleNode | ImageNode | ArticleButtonNode | TableNode;
|
|
134
204
|
export interface ArticleDocument {
|
|
135
205
|
type: 'doc';
|
|
136
|
-
content:
|
|
206
|
+
content: TopLevelNode[];
|
|
137
207
|
}
|
|
138
|
-
export type
|
|
208
|
+
export type TopLevelNode = BlockNode | ResourceQuestionNode;
|
|
209
|
+
export type ArticleContentNode = ArticleDocument | BlockNode | ResourceQuestionNode | ListItemNode | TableRowNode | TableCellNode | TextNode;
|
|
139
210
|
export interface ArticleButtonLinkDescriptor {
|
|
140
211
|
href: string;
|
|
141
212
|
target?: LinkTarget;
|
|
@@ -143,12 +214,20 @@ export interface ArticleButtonLinkDescriptor {
|
|
|
143
214
|
}
|
|
144
215
|
export type ArticleButtonLink = string | ArticleButtonLinkDescriptor | null;
|
|
145
216
|
export type ResolveArticleButtonLink = (attrs: Readonly<ArticleButtonActionAttrs>, node: Readonly<ArticleButtonActionNode>) => ArticleButtonLink;
|
|
146
|
-
export
|
|
217
|
+
export interface CustomLinkDescriptor {
|
|
218
|
+
href: string;
|
|
219
|
+
target?: LinkTarget;
|
|
220
|
+
rel?: string;
|
|
221
|
+
}
|
|
222
|
+
export type CustomLink = string | CustomLinkDescriptor | null;
|
|
223
|
+
export type ResolveCustomLink = (attrs: Readonly<CustomLinkMarkAttrs>, mark: Readonly<CustomLinkMark>) => CustomLink;
|
|
224
|
+
export type RenderIssueCode = 'INVALID_ROOT' | 'INVALID_TYPE' | 'INVALID_CONTENT' | 'INVALID_VALUE' | 'MISSING_PROPERTY' | 'UNKNOWN_PROPERTY' | 'UNKNOWN_NODE' | 'UNKNOWN_MARK' | 'TABLE_COLUMN_MISMATCH' | 'UNSAFE_URL' | 'LINK_RESOLUTION_FAILED' | 'UNSUPPORTED_PROTOCOL' | 'DUPLICATE_IDENTITY' | 'MISSING_ANCHOR' | 'NAVIGATION_CALLBACK_FAILED';
|
|
147
225
|
export interface RenderIssue {
|
|
148
226
|
code: RenderIssueCode;
|
|
149
227
|
path: string;
|
|
150
228
|
message: string;
|
|
151
229
|
nodeType?: string;
|
|
230
|
+
severity?: 'error' | 'warning';
|
|
152
231
|
}
|
|
153
232
|
export interface ValidationResult {
|
|
154
233
|
valid: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "article-content-renderer-vue2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A safe Vue 2.7 renderer for the Article Content Protocol ProseMirror JSON format.",
|
|
5
5
|
"author": "NovaVoyager",
|
|
6
6
|
"license": "MIT",
|
|
@@ -73,5 +73,8 @@
|
|
|
73
73
|
},
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=18.0.0"
|
|
76
|
+
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"ajv": "^8.20.0"
|
|
76
79
|
}
|
|
77
80
|
}
|