markdown-design-doc-viewer-embed 1.5.3 → 1.5.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdown-design-doc-viewer-embed",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "The viewer of Markdown Design Doc Viewer to embed in a web app (React or not): design documents in Markdown with their mermaid sequence diagrams, flowcharts, ER and state diagrams and wireframes linked to the steps after them.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,6 +36,10 @@
36
36
  "types": "./types/embed/react.d.ts",
37
37
  "default": "./react.js"
38
38
  },
39
+ "./export": {
40
+ "types": "./types/embed/export.d.ts",
41
+ "default": "./export.js"
42
+ },
39
43
  "./viewer.css": "./viewer.css",
40
44
  "./package.json": "./package.json"
41
45
  },
package/react.js CHANGED
@@ -2,21 +2,46 @@
2
2
 
3
3
  // embed/react.tsx
4
4
  import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
5
- import { createViewer, openLinkInNewTab } from "./index.js";
5
+ import { createDesignDocList, createViewer, openLinkInNewTab, openLinkInPage } from "./index.js";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  var DesignDocViewer = forwardRef(function DesignDocViewer2({ className, style, ...options }, ref) {
8
8
  const element = useRef(null);
9
9
  const viewer = useRef(void 0);
10
10
  const created = useRef(false);
11
11
  const latest = useRef(options);
12
- latest.current = options;
13
- const revealing = options.onRevealLine !== void 0;
14
- const viewerOptions = () => ({
15
- ...latest.current,
16
- onOpenLink: (href) => (latest.current.onOpenLink ?? openLinkInNewTab)(href),
17
- // Only with one: without, a double-click does nothing.
18
- onRevealLine: revealing ? (line) => latest.current.onRevealLine?.(line) : void 0
12
+ useEffect(() => {
13
+ latest.current = options;
19
14
  });
15
+ const revealing = options.onRevealLine !== void 0;
16
+ const closing = options.onCloseDiff !== void 0;
17
+ const viewerOptions = () => {
18
+ const o = latest.current;
19
+ return {
20
+ markdown: o.markdown,
21
+ documentId: o.documentId,
22
+ baseUrl: o.baseUrl,
23
+ readFile: o.readFile,
24
+ resolveResource: o.resolveResource,
25
+ lang: o.lang,
26
+ theme: o.theme,
27
+ diagramLook: o.diagramLook,
28
+ splitMinWidth: o.splitMinWidth,
29
+ toc: o.toc,
30
+ limitContentWidth: o.limitContentWidth,
31
+ stickyTableHeader: o.stickyTableHeader,
32
+ tableTreeHighlight: o.tableTreeHighlight,
33
+ frontMatter: o.frontMatter,
34
+ headingNumbers: o.headingNumbers,
35
+ messageWrapWidth: o.messageWrapWidth,
36
+ compareWith: o.compareWith,
37
+ compareLabel: o.compareLabel,
38
+ onOpenLink: (href) => (latest.current.onOpenLink ?? openLinkInNewTab)(href),
39
+ // Only with one: without, a double-click does nothing.
40
+ onRevealLine: o.onRevealLine !== void 0 ? (line) => latest.current.onRevealLine?.(line) : void 0,
41
+ // Only with one: without, the viewer closes the differences by itself.
42
+ onCloseDiff: o.onCloseDiff !== void 0 ? () => latest.current.onCloseDiff?.() : void 0
43
+ };
44
+ };
20
45
  useEffect(() => {
21
46
  const instance = createViewer(element.current, viewerOptions());
22
47
  viewer.current = instance;
@@ -26,20 +51,63 @@ var DesignDocViewer = forwardRef(function DesignDocViewer2({ className, style, .
26
51
  viewer.current = void 0;
27
52
  };
28
53
  }, [options.lang]);
29
- const { markdown, documentId, baseUrl, readFile, resolveResource, theme, diagramLook, splitMinWidth, toc, limitContentWidth, stickyTableHeader, tableTreeHighlight, frontMatter, headingNumbers, messageWrapWidth } = options;
54
+ const { markdown, documentId, baseUrl, readFile, resolveResource, theme, diagramLook, splitMinWidth, toc, limitContentWidth, stickyTableHeader, tableTreeHighlight, frontMatter, headingNumbers, messageWrapWidth, compareWith, compareLabel } = options;
30
55
  useEffect(() => {
31
56
  if (created.current) {
32
57
  created.current = false;
33
58
  return;
34
59
  }
35
60
  void viewer.current?.update(viewerOptions());
36
- }, [options.lang, markdown, documentId, baseUrl, readFile, resolveResource, theme, diagramLook, splitMinWidth, toc, limitContentWidth, stickyTableHeader, tableTreeHighlight, frontMatter, headingNumbers, messageWrapWidth, revealing]);
61
+ }, [options.lang, markdown, documentId, baseUrl, readFile, resolveResource, theme, diagramLook, splitMinWidth, toc, limitContentWidth, stickyTableHeader, tableTreeHighlight, frontMatter, headingNumbers, messageWrapWidth, compareWith, compareLabel, revealing, closing]);
37
62
  useImperativeHandle(ref, () => ({
38
63
  scrollToLine: (line) => viewer.current?.scrollToLine(line),
39
64
  markLine: (line) => viewer.current?.markLine(line)
40
65
  }));
41
66
  return /* @__PURE__ */ jsx("div", { ref: element, className, style });
42
67
  });
68
+ function DesignDocList({ className, style, ...options }) {
69
+ const element = useRef(null);
70
+ const list = useRef(void 0);
71
+ const created = useRef(false);
72
+ const latest = useRef(options);
73
+ useEffect(() => {
74
+ latest.current = options;
75
+ });
76
+ const listOptions = () => {
77
+ const o = latest.current;
78
+ return {
79
+ documents: o.documents,
80
+ title: o.title,
81
+ fileLabel: o.fileLabel,
82
+ filterPlaceholder: o.filterPlaceholder,
83
+ empty: o.empty,
84
+ keepOrder: o.keepOrder,
85
+ lang: o.lang,
86
+ theme: o.theme,
87
+ limitContentWidth: o.limitContentWidth,
88
+ onOpenLink: (href) => (latest.current.onOpenLink ?? openLinkInPage)(href)
89
+ };
90
+ };
91
+ useEffect(() => {
92
+ const instance = createDesignDocList(element.current, listOptions());
93
+ list.current = instance;
94
+ created.current = true;
95
+ return () => {
96
+ instance.destroy();
97
+ list.current = void 0;
98
+ };
99
+ }, [options.lang]);
100
+ const { documents, title, fileLabel, filterPlaceholder, empty, keepOrder, theme, limitContentWidth } = options;
101
+ useEffect(() => {
102
+ if (created.current) {
103
+ created.current = false;
104
+ return;
105
+ }
106
+ void list.current?.update(listOptions());
107
+ }, [options.lang, documents, title, fileLabel, filterPlaceholder, empty, keepOrder, theme, limitContentWidth]);
108
+ return /* @__PURE__ */ jsx("div", { ref: element, className, style });
109
+ }
43
110
  export {
111
+ DesignDocList,
44
112
  DesignDocViewer
45
113
  };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The HTML of a list of design documents embedded in a page (`createDesignDocList` of embed/viewer.ts): the list of
3
+ * the design documents of the extension and the desktop app (`indexBody`), of documents given by the app instead of
4
+ * the files of a folder.
5
+ */
6
+ import type { Translate } from '../src/shared/l10n.js';
7
+ import type { MarkdownIt } from 'markdown-it';
8
+ /** A document of a list (`createDesignDocList`). */
9
+ export interface DesignDocListDocument {
10
+ /** Its Markdown, or only its front matter: its title and the main entries of its front matter come from it. */
11
+ markdown: string;
12
+ /** The link of its title, given to `onOpenLink` when it is clicked (`#...` scrolls the list to that fragment instead). */
13
+ href: string;
14
+ /** The folder it is in, with `/` between the folders: a table and a heading for each. The top one (`''`) by default. */
15
+ folder?: string;
16
+ /** Its file name, shown next to its title (or what `fileLabel` names). */
17
+ file?: string;
18
+ /** Its title, instead of that of the Markdown (the `title` of its front matter, its first `#` heading, `file`, or `href`). */
19
+ title?: string;
20
+ }
21
+ /** The texts and order of a list. */
22
+ export interface DesignDocListContent {
23
+ documents: DesignDocListDocument[];
24
+ /** The heading of the list. `Design Docs` by default. */
25
+ title?: string;
26
+ /** The header of the column of `file`. `File` by default. */
27
+ fileLabel?: string;
28
+ /** The placeholder of the search box. By default, it names the title, the file name and the metadata. */
29
+ filterPlaceholder?: string;
30
+ /** The text without documents. By default, that of the extension. */
31
+ empty?: string;
32
+ /**
33
+ * Lists the folders and the documents in the order of `documents` (a folder where its first document is) instead of
34
+ * by name. false by default.
35
+ */
36
+ keepOrder?: boolean;
37
+ }
38
+ /** The HTML of the list of `content`, with the texts translated by `t`. */
39
+ export declare function designDocListHtml(md: MarkdownIt, content: DesignDocListContent, t: Translate): string;
@@ -0,0 +1,4 @@
1
+ import { type ExportOptions } from './viewer.js';
2
+ export type { DiagramLook, ExportOptions } from './viewer.js';
3
+ /** The HTML file of the design document of `options`, as a string. */
4
+ export declare function exportHtml(options: ExportOptions): Promise<string>;
@@ -2,13 +2,14 @@
2
2
  * The viewer of embed/viewer.ts as a React component. Its styles are embed/viewer.css, which the app imports.
3
3
  *
4
4
  * <DesignDocViewer markdown={text} baseUrl="/docs/" style={{ height: 600 }} />
5
+ * <DesignDocList documents={[{ markdown, href: '/docs/login', file: 'login.md' }]} onOpenLink={navigate} />
5
6
  *
6
7
  * It needs a height, which the viewer fills. `readFile` and `resolveResource` render the document again when they
7
8
  * change: keep them the same across renders (`useCallback`). The other callbacks may change freely.
8
9
  */
9
10
  import { type CSSProperties } from 'react';
10
- import { type ViewerOptions } from './viewer.js';
11
- export type { ViewerOptions } from './viewer.js';
11
+ import { type DesignDocListOptions, type ViewerOptions } from './viewer.js';
12
+ export type { DesignDocListDocument, DesignDocListOptions, ViewerOptions } from './viewer.js';
12
13
  export interface DesignDocViewerProps extends ViewerOptions {
13
14
  className?: string;
14
15
  style?: CSSProperties;
@@ -21,3 +22,13 @@ export interface DesignDocViewerHandle {
21
22
  markLine(line: number): void;
22
23
  }
23
24
  export declare const DesignDocViewer: import("react").ForwardRefExoticComponent<DesignDocViewerProps & import("react").RefAttributes<DesignDocViewerHandle>>;
25
+ export interface DesignDocListProps extends DesignDocListOptions {
26
+ className?: string;
27
+ style?: CSSProperties;
28
+ }
29
+ /**
30
+ * The list of the design documents of createDesignDocList (embed/viewer.ts), as high as the list. `onOpenLink` may
31
+ * change freely; the list is shown again when the other options change (keep `documents` the same across renders
32
+ * while they do not change: `useMemo`).
33
+ */
34
+ export declare function DesignDocList({ className, style, ...options }: DesignDocListProps): import("react").JSX.Element;
@@ -1,5 +1,7 @@
1
- import type { DiagramLook } from '../src/shared/protocol.js';
1
+ import { type DiagramLook } from '../src/shared/protocol.js';
2
+ import { type DesignDocListContent } from './designDocList.js';
2
3
  export type { DiagramLook };
4
+ export type { DesignDocListContent, DesignDocListDocument } from './designDocList.js';
3
5
  export interface ViewerOptions {
4
6
  /** The Markdown of the design document. */
5
7
  markdown: string;
@@ -42,7 +44,7 @@ export interface ViewerOptions {
42
44
  headingNumbers?: boolean;
43
45
  /**
44
46
  * Breaks the labels of the arrows of the sequence diagrams into lines at this many characters (a full-width one
45
- * counts as 2). 0 does not break them. 30 by default.
47
+ * counts as 2). 0 does not break them. 40 by default.
46
48
  */
47
49
  messageWrapWidth?: number;
48
50
  /**
@@ -52,6 +54,20 @@ export interface ViewerOptions {
52
54
  onOpenLink?: (href: string) => void;
53
55
  /** Called with the line (0-based) of the Markdown double-clicked in the viewer, e.g. to show it in an editor. */
54
56
  onRevealLine?: (line: number) => void;
57
+ /**
58
+ * The Markdown of another version of the document (an older one, for instance). With it, the viewer shows `markdown`
59
+ * with its differences from that version: the blocks added, changed (with the words changed) and deleted, the
60
+ * diagrams before the change, and a bar with their numbers and buttons to go from one change to the next. Without it,
61
+ * the document alone.
62
+ */
63
+ compareWith?: string;
64
+ /** What the document is compared with (e.g. `Compared with version 3 (2026-11-01)`), shown on the bar of the differences. */
65
+ compareLabel?: string;
66
+ /**
67
+ * Called when the bar of the differences is closed (its × button), e.g. to remove `compareWith`. Without it, the
68
+ * viewer shows the document alone until `compareWith` changes.
69
+ */
70
+ onCloseDiff?: () => void;
55
71
  }
56
72
  export interface Viewer {
57
73
  /** Resolves once the document is shown, with its diagrams (that of `createViewer`, or of an update made meanwhile). */
@@ -69,3 +85,43 @@ export interface Viewer {
69
85
  export declare function openLinkInNewTab(href: string): void;
70
86
  /** Shows the design document of `options` in `container`, which it fills. */
71
87
  export declare function createViewer(container: HTMLElement, options: ViewerOptions): Viewer;
88
+ /** The options of `createDesignDocList`. */
89
+ export interface DesignDocListOptions extends DesignDocListContent, Pick<ViewerOptions, 'theme' | 'limitContentWidth'> {
90
+ /** The language of the texts of the list (`en` by default, or that of the browser). Only read by `createDesignDocList`. */
91
+ lang?: 'en' | 'ja';
92
+ /** Opens the link of a title (`href` of its document). By default, goes to it in the page (`openLinkInPage`). */
93
+ onOpenLink?: (href: string) => void;
94
+ }
95
+ export interface DesignDocList {
96
+ /** Resolves once the list is shown. */
97
+ ready: Promise<void>;
98
+ /** Changes some options (all but `lang`) and shows the list again, keeping its search and order. */
99
+ update(options: Partial<Omit<DesignDocListOptions, 'lang'>>): Promise<void>;
100
+ /** Removes the list from its element. */
101
+ destroy(): void;
102
+ }
103
+ /**
104
+ * What `onOpenLink` of a list does by default: goes to the link in the page, as the links of a list are those of the
105
+ * app (except `javascript:`, `data:` and `vbscript:` ones).
106
+ */
107
+ export declare function openLinkInPage(href: string): void;
108
+ /**
109
+ * Shows the list of the design documents of `options` in `container`: a table for each folder, with the main entries
110
+ * of their front matter, a search box and sortable columns. Unlike a viewer, it does not scroll inside the element:
111
+ * it is as high as the list.
112
+ */
113
+ export declare function createDesignDocList(container: HTMLElement, options: DesignDocListOptions): DesignDocList;
114
+ /** The options of `exportHtml` (`markdown-design-doc-viewer-embed/export`). */
115
+ export interface ExportOptions extends Pick<ViewerOptions, 'markdown' | 'baseUrl' | 'readFile' | 'diagramLook' | 'splitMinWidth' | 'toc' | 'limitContentWidth' | 'stickyTableHeader' | 'tableTreeHighlight' | 'frontMatter' | 'headingNumbers' | 'messageWrapWidth' | 'compareWith' | 'compareLabel'> {
116
+ /** The language of the texts of the exported file: `en`, or `ja`. That of the browser by default. */
117
+ lang?: 'en' | 'ja';
118
+ /** The title of the page when the document has none (neither `title` in its front matter nor a `#` heading). */
119
+ title?: string;
120
+ /**
121
+ * What an image of the document (a path, not a URL) becomes in the exported file: a data URI to embed it, or a URL to
122
+ * link it. It is given the `src` of the image without its query and fragment (`icons.svg` of `icons.svg#save`), which
123
+ * are added back to a URL. By default, resolved from `baseUrl` (or left as it is without it). When it fails, the
124
+ * `src` is kept.
125
+ */
126
+ resolveImage?: (path: string) => string | Promise<string>;
127
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Translation of the texts shown in the preview. The extension host passes `vscode.l10n.t`;
3
+ * the modules here do not import `vscode`, so they take the function as a parameter.
4
+ */
5
+ /** Same signature as `vscode.l10n.t`: `message` is the English text, with `{0}`, `{1}`… for `args`. */
6
+ export type Translate = (message: string, ...args: (string | number)[]) => string;
7
+ /** Default `Translate` that keeps the English text and only fills in the arguments. */
8
+ export declare const formatMessage: Translate;
9
+ /** `Translate` with the translations of a bundle (l10n/bundle.l10n.<lang>.json), keeping the English text of the others. */
10
+ export declare function translatorFor(messages: Record<string, string>): Translate;
package/viewer.css CHANGED
@@ -1761,3 +1761,12 @@ del.seqnotes-diff-del {
1761
1761
  position: absolute;
1762
1762
  max-width: calc(100% - 32px);
1763
1763
  }
1764
+ .seqnotes-viewer.seqnotes-embedded.seqnotes-embedded-list {
1765
+ height: auto;
1766
+ }
1767
+ .seqnotes-embedded-list > .seqnotes-viewer-scroller {
1768
+ height: auto;
1769
+ overflow-x: auto;
1770
+ overflow-y: hidden;
1771
+ padding: 2px 0;
1772
+ }