article-content-renderer-vue2 0.4.0 → 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, ResolveCustomLink } 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;
@@ -8,6 +8,11 @@ interface ArticleContentRendererProps {
8
8
  imageBaseUrl: string;
9
9
  resolveArticleButtonLink?: ResolveArticleButtonLink;
10
10
  resolveCustomLink?: ResolveCustomLink;
11
+ revealedKeys: string[];
12
+ articleKey?: string | number;
13
+ scrollContainer?: HTMLElement | (() => HTMLElement | null);
14
+ scrollOffset: number;
15
+ onAnchorNavigate?: OnAnchorNavigate;
11
16
  }
12
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>;
13
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, CustomLink, CustomLinkDescriptor, CustomLinkMark, CustomLinkMarkAttrs, CustomSlot, HeadingNode, HorizontalRuleNode, ImageAlign, ImageNode, ItalicMark, HrefLinkMark, HrefLinkMarkAttrs, LinkMark, LinkTarget, ListItemNode, OrderedListNode, ParagraphNode, ProtocolVersion, RenderIssue, RenderIssueCode, ResolveArticleButtonLink, ResolveCustomLink, 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,4 +1,5 @@
1
1
  import type { CreateElement, VNode } from 'vue';
2
+ import type { NavigationSession } from '../core/resource-question.js';
2
3
  import type { ArticleButtonClickPayload, RenderIssue, ResolveArticleButtonLink, ResolveCustomLink, ValidationResult } from '../types.js';
3
4
  export interface RenderContext {
4
5
  createElement: CreateElement;
@@ -8,6 +9,7 @@ export interface RenderContext {
8
9
  resolveCustomLink?: ResolveCustomLink;
9
10
  emitArticleButtonClick: (payload: ArticleButtonClickPayload) => void;
10
11
  reportIssue: (issue: RenderIssue) => void;
12
+ navigation: NavigationSession;
11
13
  }
12
14
  export interface ResolvedCustomSlot {
13
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 {
@@ -45,7 +46,19 @@ export interface CustomLinkMark {
45
46
  attrs: CustomLinkMarkAttrs;
46
47
  }
47
48
  export type LinkMark = HrefLinkMark | CustomLinkMark;
48
- export type ArticleMark = BoldMark | ItalicMark | StrikeMark | UnderlineMark | CodeMark | LinkMark;
49
+ export interface TextStyleMark {
50
+ type: 'textStyle';
51
+ attrs: {
52
+ color: string;
53
+ };
54
+ }
55
+ export interface HighlightMark {
56
+ type: 'highlight';
57
+ attrs: {
58
+ color: string;
59
+ };
60
+ }
61
+ export type ArticleMark = BoldMark | ItalicMark | StrikeMark | UnderlineMark | CodeMark | LinkMark | TextStyleMark | HighlightMark;
49
62
  export interface TextNode {
50
63
  type: 'text';
51
64
  text: string;
@@ -55,6 +68,8 @@ export interface ParagraphNode {
55
68
  type: 'paragraph';
56
69
  attrs?: {
57
70
  textAlign?: TextAlign;
71
+ anchorId?: string;
72
+ fontSize?: number;
58
73
  };
59
74
  content?: TextNode[];
60
75
  }
@@ -63,6 +78,7 @@ export interface HeadingNode {
63
78
  attrs: {
64
79
  level: 1 | 2 | 3 | 4 | 5 | 6;
65
80
  textAlign?: TextAlign;
81
+ anchorId?: string;
66
82
  };
67
83
  content?: TextNode[];
68
84
  }
@@ -108,6 +124,7 @@ export interface ImageNode {
108
124
  width?: number;
109
125
  height?: number;
110
126
  imageAlign?: ImageAlign;
127
+ imageLayout?: ImageLayout;
111
128
  };
112
129
  }
113
130
  interface ArticleButtonBaseAttrs {
@@ -134,6 +151,43 @@ export interface ArticleButtonLinkNode {
134
151
  attrs: ArticleButtonLinkAttrs;
135
152
  }
136
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
+ }
137
191
  export interface TableNode {
138
192
  type: 'table';
139
193
  content: TableRowNode[];
@@ -149,9 +203,10 @@ export interface TableCellNode {
149
203
  export type BlockNode = ParagraphNode | HeadingNode | BlockquoteNode | BulletListNode | OrderedListNode | CodeBlockNode | HorizontalRuleNode | ImageNode | ArticleButtonNode | TableNode;
150
204
  export interface ArticleDocument {
151
205
  type: 'doc';
152
- content: BlockNode[];
206
+ content: TopLevelNode[];
153
207
  }
154
- 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;
155
210
  export interface ArticleButtonLinkDescriptor {
156
211
  href: string;
157
212
  target?: LinkTarget;
@@ -166,12 +221,13 @@ export interface CustomLinkDescriptor {
166
221
  }
167
222
  export type CustomLink = string | CustomLinkDescriptor | null;
168
223
  export type ResolveCustomLink = (attrs: Readonly<CustomLinkMarkAttrs>, mark: Readonly<CustomLinkMark>) => CustomLink;
169
- 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';
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';
170
225
  export interface RenderIssue {
171
226
  code: RenderIssueCode;
172
227
  path: string;
173
228
  message: string;
174
229
  nodeType?: string;
230
+ severity?: 'error' | 'warning';
175
231
  }
176
232
  export interface ValidationResult {
177
233
  valid: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "article-content-renderer-vue2",
3
- "version": "0.4.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
  }