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.
@@ -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 {};
@@ -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 { ArticleButtonClickPayload, RenderIssue, ResolveArticleButtonLink, ValidationResult } from '../types.js';
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
  }>;
@@ -0,0 +1,2 @@
1
+ import type { RenderIssue } from '../../types.js';
2
+ export declare function validateDocumentSchema(document: unknown): RenderIssue[];
@@ -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 LinkMark {
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
- href: string;
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: BlockNode[];
206
+ content: TopLevelNode[];
137
207
  }
138
- export type ArticleContentNode = ArticleDocument | BlockNode | ListItemNode | TableRowNode | TableCellNode | TextNode;
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 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';
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.1",
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
  }