nuvra 0.5.0 → 0.6.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 +74 -18
- package/dist/components/document-editor.vue.d.ts +10 -0
- package/dist/components/editor-canvas.vue.d.ts +3 -0
- package/dist/components/editor-collaborators.vue.d.ts +17 -0
- package/dist/components/editor-outline.vue.d.ts +23 -3
- package/dist/components/editor-toolbar.vue.d.ts +2 -2
- package/dist/core/collaboration.d.ts +25 -0
- package/dist/core/engine/dom.d.ts +9 -2
- package/dist/core/engine/engine.d.ts +17 -1
- package/dist/core/engine/schema.d.ts +2 -0
- package/dist/core/export.d.ts +8 -1
- package/dist/core/locales/uz.d.ts +8 -0
- package/dist/core/outline.d.ts +2 -0
- package/dist/core/pagination.d.ts +21 -2
- package/dist/core/pdf.d.ts +43 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/{export-DhHI3ZrQ.js → export-B06RXLqn.js} +16 -14
- package/dist/export-B06RXLqn.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3210 -2778
- package/dist/index.js.map +1 -1
- package/dist/pdf-mIRjnoOP.js +70 -0
- package/dist/pdf-mIRjnoOP.js.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/dist/export-DhHI3ZrQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# nuvra
|
|
2
2
|
|
|
3
3
|
Word-style document editor for Vue 3. A real page view with paper sizes and margins, a lighter web view for
|
|
4
|
-
forms, tables, images, lists, find & replace, comments, HTML source mode, printing, export to HTML and Word
|
|
5
|
-
(`.docx`) you can open again.
|
|
4
|
+
forms, tables, images, lists, find & replace, comments, HTML source mode, printing, PDF download, export to HTML and Word
|
|
5
|
+
files (`.docx`) you can open again.
|
|
6
6
|
|
|
7
7
|
**[Documentation and live demo →](https://nuvra-docs.vercel.app)**
|
|
8
8
|
|
|
9
9
|
- No editor framework underneath: its own small editing engine with a sanitizing schema.
|
|
10
10
|
- No UI framework either: native HTML controls, built-in SVG icons and plain CSS variables. Vue is the only
|
|
11
11
|
dependency.
|
|
12
|
-
- Pagination in the page view (A4, A5, Letter, …, portrait or landscape)
|
|
12
|
+
- Pagination in the page view (A4, A5, Letter, …, portrait or landscape), with section breaks for landscape pages inside a
|
|
13
|
+
portrait document.
|
|
13
14
|
- Tables with merge / split, images with resize and alignment, task lists, links, colors, fonts.
|
|
14
15
|
- Undo / redo, keyboard shortcuts that work with non-Latin keyboard layouts, Markdown-like input rules.
|
|
15
16
|
- Office tools: format painter, letter case, paragraph spacing, formatting marks, a right-click menu and
|
|
@@ -18,10 +19,12 @@ forms, tables, images, lists, find & replace, comments, HTML source mode, printi
|
|
|
18
19
|
the editor shows them, and the Word export uses Word's own header, footer and page fields.
|
|
19
20
|
- A ruler above the page for the margins and for the first line, left and right indents of a paragraph.
|
|
20
21
|
- A page watermark such as DRAFT or COPY, drawn behind the text of every page and carried into print, HTML and Word.
|
|
21
|
-
- Real `.docx` export and import, multilevel (1.1.1) numbering, a table of
|
|
22
|
+
- Real `.docx` export and import, PDF download without the print dialog, multilevel (1.1.1) numbering, a table of
|
|
23
|
+
contents and a navigation pane with headings and page thumbnails.
|
|
22
24
|
- Footnotes at the bottom of their page, carried into print and Word.
|
|
23
25
|
- Comments with replies, tracked changes that round-trip with Word, a comparison of two versions, and templates
|
|
24
26
|
filled in as a form.
|
|
27
|
+
- Hooks for editing together: other people's carets and selections, your selection as character positions.
|
|
25
28
|
- A `/` command menu and a toolbar slot for your own commands and buttons.
|
|
26
29
|
|
|
27
30
|
## Installation
|
|
@@ -103,6 +106,7 @@ const uploadImage: DocumentImageUploadHandler = async file => {
|
|
|
103
106
|
| `author` | `string` | `''` | Name written on new comments, replies and tracked changes. |
|
|
104
107
|
| `autofocus` | `boolean` | `false` | Places the caret at the end of the document once ready. |
|
|
105
108
|
| `canvasPadding` | `number \| string` | `50` | Gray space around the page or web sheet. |
|
|
109
|
+
| `collaborators` | `Collaborator[]` | `[]` | Other people editing the document; their carets and selections are drawn. |
|
|
106
110
|
| `defaultViewMode` | `'page' \| 'web'` | `'page'` | View shown first. |
|
|
107
111
|
| `disabled` | `boolean` | `false` | Read-only document, disabled controls. |
|
|
108
112
|
| `height` | `number \| string` | `760` | Height of the editor, or `'auto'` to grow between `minHeight`/`maxHeight`. |
|
|
@@ -119,18 +123,20 @@ const uploadImage: DocumentImageUploadHandler = async file => {
|
|
|
119
123
|
| `variables` | `TemplateVariable[]` | `[]` | Template variables the user can insert. |
|
|
120
124
|
|
|
121
125
|
`Editor` accepts the same props except `v-model:page`, `v-model:comments`, `v-model:trackChanges`, `author`,
|
|
122
|
-
`defaultViewMode`, `height`, `ruler`, `slashCommands` and `title`.
|
|
126
|
+
`collaborators`, `defaultViewMode`, `height`, `ruler`, `slashCommands` and `title`.
|
|
123
127
|
|
|
124
128
|
Numbers are pixels; strings are used as CSS lengths (`'100%'`, `'50vh'`).
|
|
125
129
|
|
|
126
130
|
### Events
|
|
127
131
|
|
|
128
|
-
| Event
|
|
129
|
-
|
|
|
130
|
-
| `focus`
|
|
131
|
-
| `blur`
|
|
132
|
-
| `uploadError`
|
|
133
|
-
| `importError`
|
|
132
|
+
| Event | Payload | Description |
|
|
133
|
+
| ----------------- | ----------------------------------- | -------------------------------------------------------------- |
|
|
134
|
+
| `focus` | — | The document received focus. |
|
|
135
|
+
| `blur` | — | The document lost focus; the model is up to date. |
|
|
136
|
+
| `uploadError` | `error: unknown` | An image was rejected or its upload failed. |
|
|
137
|
+
| `importError` | `error: unknown` | A Word file could not be read. |
|
|
138
|
+
| `exportError` | `error: unknown` | The PDF could not be drawn; no file is downloaded. |
|
|
139
|
+
| `selectionChange` | `selection: SelectionOffsets \| null` | The caret or selection moved; `null` when it left the document. |
|
|
134
140
|
|
|
135
141
|
### Exposed methods (`DocumentEditor` ref)
|
|
136
142
|
|
|
@@ -144,6 +150,7 @@ Numbers are pixels; strings are used as CSS lengths (`'100%'`, `'50vh'`).
|
|
|
144
150
|
| `print()` | Opens the browser print dialog. |
|
|
145
151
|
| `exportHtml()` | Downloads the document as an HTML page. |
|
|
146
152
|
| `exportWord()` | Downloads the document as a Word file (`.docx`). |
|
|
153
|
+
| `exportPdf()` | Downloads the document as a PDF drawn from its pages. |
|
|
147
154
|
| `engine` | The editing engine (`DocumentEngine`), for advanced integrations. |
|
|
148
155
|
|
|
149
156
|
### Keyboard shortcuts
|
|
@@ -210,8 +217,25 @@ const submit = () => {
|
|
|
210
217
|
footers with page fields, the watermark, lists, tables with merged cells, images, footnotes and tracked changes as
|
|
211
218
|
Word revisions. "Open Word file (.docx)" (or `importWord(file)`) loads one into the editor, footnotes and revisions
|
|
212
219
|
included; a file that cannot be read emits `importError`. `buildDocx` and `readDocx` do the same in your own code. A
|
|
213
|
-
document with several sections
|
|
214
|
-
header is not imported.
|
|
220
|
+
document with several sections takes the page setup of its first section, and every further section starts with a
|
|
221
|
+
section break; the content of a different first page header is not imported.
|
|
222
|
+
|
|
223
|
+
### PDF download
|
|
224
|
+
|
|
225
|
+
"Download as PDF" in the "More" menu, or `exportPdf()`, saves a PDF without the print dialog. Every sheet is drawn into a
|
|
226
|
+
picture (SVG `foreignObject` → canvas → JPEG) and written into the PDF, so it looks like the printout, but its text
|
|
227
|
+
cannot be selected or searched. Images from servers without CORS are left out, and in the web view the editor switches to
|
|
228
|
+
the page view for the moment of the export. A PDF that cannot be drawn emits `exportError`; printing with "Save as PDF"
|
|
229
|
+
still gives a PDF with real text.
|
|
230
|
+
|
|
231
|
+
### Pages in different orientations
|
|
232
|
+
|
|
233
|
+
"Section break: landscape pages" and "Section break: portrait pages" in the insert menu and the `/` menu (or
|
|
234
|
+
`engine.insertSectionBreak('landscape')`) turn the pages after the break; paper size and margins stay, and the break
|
|
235
|
+
starts a new page. It is saved as `<div data-type="section-break" data-orientation="landscape"></div>`. The page view
|
|
236
|
+
draws sheets of different sizes and gives the blocks of a turned section their sheet's text width; printing uses a named
|
|
237
|
+
`@page` for turned sheets, the PDF has turned pages, and the Word export writes every section as a Word section with its
|
|
238
|
+
own orientation.
|
|
215
239
|
|
|
216
240
|
### Footnotes
|
|
217
241
|
|
|
@@ -225,7 +249,8 @@ From code: `engine.insertFootnote(text)`, `setFootnoteText(element, text)`, `rem
|
|
|
225
249
|
For long documents the toolbar offers multilevel numbering (1., 1.1., 1.1.1., saved as `<ol data-numbering="legal">`),
|
|
226
250
|
a table of contents of the headings with page numbers (saved as `<table data-type="toc">`, refreshed with
|
|
227
251
|
`updateTableOfContents()`), and a navigation pane. `PageSettings` has `differentFirstPage` to hide the header, footer
|
|
228
|
-
and page number on the first page, and `firstPageNumber` for the number printed on it.
|
|
252
|
+
and page number on the first page, and `firstPageNumber` for the number printed on it. The navigation pane lists the
|
|
253
|
+
headings or shows page thumbnails; clicking one scrolls to it.
|
|
229
254
|
|
|
230
255
|
## Comments, tracked changes and comparison
|
|
231
256
|
|
|
@@ -253,12 +278,13 @@ const save = () =>
|
|
|
253
278
|
</template>
|
|
254
279
|
```
|
|
255
280
|
|
|
256
|
-
|
|
257
|
-
deletes them, and flags comments whose text was
|
|
281
|
+
The comment and change tools are in the **Review** menu of the toolbar. Users select text and choose "Add comment"
|
|
282
|
+
(`Ctrl/⌘+Alt+M`); the "Comments" panel replies, resolves, reopens and deletes them, and flags comments whose text was
|
|
283
|
+
deleted.
|
|
258
284
|
|
|
259
285
|
### Tracked changes
|
|
260
286
|
|
|
261
|
-
Bind `v-model:trackChanges` (or
|
|
287
|
+
Bind `v-model:trackChanges` (or choose "Track changes" in the Review menu) to record typing, deleting, cut and paste as tracked changes
|
|
262
288
|
by `author`. They are part of the HTML, `<ins data-change="id" data-author="…" data-time="…">` and `<del …>`, shown
|
|
263
289
|
and printed green-underlined and red-struck. The "Changes" panel accepts or rejects them one by one or all at once;
|
|
264
290
|
from code use `engine.getChanges()`, `engine.resolveChanges(accept, id?)` and `engine.selectChange(id)`. Formatting,
|
|
@@ -277,9 +303,39 @@ shown whole. `compareDocuments(before, after)` returns the same HTML and counts
|
|
|
277
303
|
<DocumentCompare :before="previousVersion" :after="html" :height="600" />
|
|
278
304
|
```
|
|
279
305
|
|
|
306
|
+
## Editing together
|
|
307
|
+
|
|
308
|
+
The editor has hooks for several people on one document; the transport (WebSocket, WebRTC, …) is up to your app.
|
|
309
|
+
`selectionChange` reports your caret or selection as character positions, `collaborators` draws other people's carets
|
|
310
|
+
(with names) and selections, and a new `v-model` value from outside keeps your caret at the same character position.
|
|
311
|
+
The engine also offers `getSelectionOffsets()`, `getOffsetRects(offsets)` and `setContent(html, { keepSelection })`;
|
|
312
|
+
`collaboratorColor` gives the colour a collaborator is drawn in.
|
|
313
|
+
|
|
314
|
+
```vue
|
|
315
|
+
<script setup lang="ts">
|
|
316
|
+
import { ref } from 'vue';
|
|
317
|
+
import { type Collaborator, DocumentEditor, type SelectionOffsets } from 'nuvra';
|
|
318
|
+
|
|
319
|
+
const html = ref('');
|
|
320
|
+
const collaborators = ref<Collaborator[]>([]); // filled from your socket messages
|
|
321
|
+
|
|
322
|
+
const sendSelection = (selection: SelectionOffsets | null) =>
|
|
323
|
+
socket.send(JSON.stringify({ type: 'selection', id: me.id, name: me.name, selection }));
|
|
324
|
+
</script>
|
|
325
|
+
|
|
326
|
+
<template>
|
|
327
|
+
<DocumentEditor v-model="html" :collaborators="collaborators" @selection-change="sendSelection" />
|
|
328
|
+
</template>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
This is not a CRDT: the document travels as a whole, so when two people type at the same time the document sent last
|
|
332
|
+
wins, and because positions are character offsets, a remote edit before your caret shifts it. A library such as Yjs
|
|
333
|
+
can carry the document and the selections (awareness), but edits are still not merged character by character. See the
|
|
334
|
+
[guide](https://nuvra-docs.vercel.app/docs/collaboration).
|
|
335
|
+
|
|
280
336
|
## Commands and toolbar buttons
|
|
281
337
|
|
|
282
|
-
Typing `/` at the start of a line or after a space opens a command menu: headings, lists, table, page
|
|
338
|
+
Typing `/` at the start of a line or after a space opens a command menu: headings, lists, table, page and section breaks, footnote,
|
|
283
339
|
table of contents, dates, signature blocks and your variables. `slashCommands` adds your own commands at the top, and the
|
|
284
340
|
`toolbar` slot adds your own buttons:
|
|
285
341
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Collaborator, SelectionOffsets } from '../core/collaboration';
|
|
1
2
|
import { type DocumentComment } from '../core/comments';
|
|
2
3
|
import { DocumentEngine } from '../core/engine/engine';
|
|
3
4
|
import { type EditorLocaleInput } from '../core/labels';
|
|
@@ -15,6 +16,8 @@ interface Props {
|
|
|
15
16
|
autofocus?: boolean;
|
|
16
17
|
/** Gray space around the page or web sheet that separates the document from the editor frame. */
|
|
17
18
|
canvasPadding?: CssSize;
|
|
19
|
+
/** Other people editing the document; their carets and selections are drawn over it. */
|
|
20
|
+
collaborators?: readonly Collaborator[];
|
|
18
21
|
/** Name written as the author of new comments, replies and tracked changes. */
|
|
19
22
|
author?: string;
|
|
20
23
|
/** View shown first; form fields use the lighter web view. */
|
|
@@ -76,6 +79,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
76
79
|
exportHtml: () => Promise<void>;
|
|
77
80
|
/** Downloads the document as a Word-compatible file. */
|
|
78
81
|
exportWord: () => Promise<void>;
|
|
82
|
+
/** Downloads the document as a PDF drawn from its pages. */
|
|
83
|
+
exportPdf: () => Promise<void>;
|
|
79
84
|
/** Moves keyboard focus into the document. */
|
|
80
85
|
focus: () => void | undefined;
|
|
81
86
|
/** Returns the document HTML, including edits not yet written to the model. */
|
|
@@ -94,6 +99,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
94
99
|
"update:page": (value: PageSettings) => any;
|
|
95
100
|
uploadError: (error: unknown) => any;
|
|
96
101
|
importError: (error: unknown) => any;
|
|
102
|
+
exportError: (error: unknown) => any;
|
|
103
|
+
selectionChange: (selection: SelectionOffsets | null) => any;
|
|
97
104
|
"update:modelValue": (value: string) => any;
|
|
98
105
|
"update:trackChanges": (value: boolean) => any;
|
|
99
106
|
"update:comments": (value: DocumentComment[] | undefined) => any;
|
|
@@ -103,6 +110,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
103
110
|
"onUpdate:page"?: ((value: PageSettings) => any) | undefined;
|
|
104
111
|
onUploadError?: ((error: unknown) => any) | undefined;
|
|
105
112
|
onImportError?: ((error: unknown) => any) | undefined;
|
|
113
|
+
onExportError?: ((error: unknown) => any) | undefined;
|
|
114
|
+
onSelectionChange?: ((selection: SelectionOffsets | null) => any) | undefined;
|
|
106
115
|
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
107
116
|
"onUpdate:trackChanges"?: ((value: boolean) => any) | undefined;
|
|
108
117
|
"onUpdate:comments"?: ((value: DocumentComment[] | undefined) => any) | undefined;
|
|
@@ -114,6 +123,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
114
123
|
placeholder: string;
|
|
115
124
|
disabled: boolean;
|
|
116
125
|
minHeight: CssSize;
|
|
126
|
+
collaborators: readonly Collaborator[];
|
|
117
127
|
maxHeight: CssSize;
|
|
118
128
|
maxLength: number;
|
|
119
129
|
variables: readonly TemplateVariable[];
|
|
@@ -2,6 +2,7 @@ import type { ParagraphIndents } from '../core/engine/blocks';
|
|
|
2
2
|
import type { DocumentEngine } from '../core/engine/engine';
|
|
3
3
|
import { type SheetFootnote } from '../core/footnotes';
|
|
4
4
|
import { type DocumentViewMode, type PageMargins, type PageMetrics, type PageSettings } from '../core/page';
|
|
5
|
+
import { type SheetGeometry } from '../core/pagination';
|
|
5
6
|
interface Props {
|
|
6
7
|
/** Grow with the content between the editor's min and max height instead of filling a fixed height. */
|
|
7
8
|
autoHeight: boolean;
|
|
@@ -21,6 +22,8 @@ interface Props {
|
|
|
21
22
|
metrics: PageMetrics;
|
|
22
23
|
/** Number of sheets drawn behind the content in page view. */
|
|
23
24
|
pageCount: number;
|
|
25
|
+
/** Position, size and orientation of every sheet, as laid out by pagination; empty until the first layout. */
|
|
26
|
+
sheets: readonly SheetGeometry[];
|
|
24
27
|
/** Whether the ruler is drawn above the sheet; it is only ever shown in the page view. */
|
|
25
28
|
rulerVisible: boolean;
|
|
26
29
|
/** Paginated sheets or a single web sheet. */
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Collaborator } from '../core/collaboration';
|
|
2
|
+
import type { DocumentEngine } from '../core/engine/engine';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Engine that measures positions in the document. */
|
|
5
|
+
engine: DocumentEngine;
|
|
6
|
+
/** People whose carets are drawn. */
|
|
7
|
+
collaborators: readonly Collaborator[];
|
|
8
|
+
/** Element the marks are positioned in; it must be positioned itself. */
|
|
9
|
+
container: HTMLElement;
|
|
10
|
+
/** Scrolling canvas; marks outside it are hidden. */
|
|
11
|
+
scrollTarget: HTMLElement;
|
|
12
|
+
/** Changes whenever the layout may move text (zoom, view mode, page setup), so the marks are measured again. */
|
|
13
|
+
layoutKey: string;
|
|
14
|
+
}
|
|
15
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
@@ -1,16 +1,36 @@
|
|
|
1
|
-
import type { OutlineHeading } from '../core/outline';
|
|
1
|
+
import type { OutlineHeading, OutlineView } from '../core/outline';
|
|
2
|
+
import type { PageMetrics } from '../core/page';
|
|
3
|
+
import type { SheetGeometry } from '../core/pagination';
|
|
2
4
|
interface Props {
|
|
3
5
|
/** Headings of the document with the page each starts on, `null` outside the page view. */
|
|
4
6
|
headings: ReadonlyArray<OutlineHeading & {
|
|
5
7
|
page: number | null;
|
|
6
8
|
}>;
|
|
9
|
+
/** Clean HTML of every sheet, shown as page thumbnails; empty outside the page view. */
|
|
10
|
+
pages: readonly string[];
|
|
11
|
+
/** Page geometry the thumbnails are scaled from. */
|
|
12
|
+
metrics: PageMetrics;
|
|
13
|
+
/** Size of every sheet; a sheet turned by a section break gets a turned thumbnail. */
|
|
14
|
+
sheets: readonly SheetGeometry[];
|
|
15
|
+
/** Number printed on the first sheet. */
|
|
16
|
+
firstPageNumber: number;
|
|
7
17
|
}
|
|
8
|
-
|
|
18
|
+
type __VLS_Props = Props;
|
|
19
|
+
type __VLS_ModelProps = {
|
|
20
|
+
/** Which list is shown; the editor reads it to lay out thumbnails only while they are visible. */
|
|
21
|
+
'view'?: OutlineView;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
24
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
25
|
select: (heading: HTMLElement) => any;
|
|
10
26
|
close: () => any;
|
|
11
|
-
|
|
27
|
+
selectPage: (index: number) => any;
|
|
28
|
+
"update:view": (value: OutlineView) => any;
|
|
29
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
12
30
|
onSelect?: ((heading: HTMLElement) => any) | undefined;
|
|
13
31
|
onClose?: (() => any) | undefined;
|
|
32
|
+
onSelectPage?: ((index: number) => any) | undefined;
|
|
33
|
+
"onUpdate:view"?: ((value: OutlineView) => any) | undefined;
|
|
14
34
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
35
|
declare const _default: typeof __VLS_export;
|
|
16
36
|
export default _default;
|
|
@@ -37,9 +37,9 @@ interface Props {
|
|
|
37
37
|
/** Template variables offered by the variable menu; the menu is hidden without any. */
|
|
38
38
|
variables: ReadonlyArray<TemplateVariable>;
|
|
39
39
|
}
|
|
40
|
-
declare var
|
|
40
|
+
declare var __VLS_626: {};
|
|
41
41
|
type __VLS_Slots = {} & {
|
|
42
|
-
default?: (props: typeof
|
|
42
|
+
default?: (props: typeof __VLS_626) => any;
|
|
43
43
|
};
|
|
44
44
|
declare const __VLS_base: import("vue").DefineComponent<Props, {
|
|
45
45
|
/** Opens the link popover. */
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks for editing a document together. The editor reports where the caret is and draws the carets and selections
|
|
3
|
+
* of other people; sending the document and the selections between them (WebSocket, WebRTC, a CRDT such as Yjs) is
|
|
4
|
+
* left to the application.
|
|
5
|
+
*/
|
|
6
|
+
/** A selection as character positions through the document; equal positions are a caret. */
|
|
7
|
+
export interface SelectionOffsets {
|
|
8
|
+
/** Where selecting started. */
|
|
9
|
+
anchor: number;
|
|
10
|
+
/** Where the caret is. */
|
|
11
|
+
focus: number;
|
|
12
|
+
}
|
|
13
|
+
/** Someone else editing the same document. */
|
|
14
|
+
export interface Collaborator {
|
|
15
|
+
/** Stable id of the person or connection. */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Name shown next to the caret. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** CSS colour of the caret and the selection; one is picked from the id when it is left out. */
|
|
20
|
+
color?: string;
|
|
21
|
+
/** Where the person's caret or selection is, or `null` while they are not in the document. */
|
|
22
|
+
selection: SelectionOffsets | null;
|
|
23
|
+
}
|
|
24
|
+
/** The colour of a collaborator: their own, or a stable one derived from the id. */
|
|
25
|
+
export declare const collaboratorColor: (collaborator: Pick<Collaborator, "id" | "color">) => string;
|
|
@@ -13,8 +13,15 @@ export declare const FOOTNOTE_ATTRIBUTE = "data-footnote";
|
|
|
13
13
|
export declare const FOOTNOTE_SELECTOR = "sup[data-footnote]";
|
|
14
14
|
/** Selector for every inline atom: elements inside a line that behave as one character and hold no editable text. */
|
|
15
15
|
export declare const INLINE_ATOM_SELECTOR = "span[data-variable], sup[data-footnote]";
|
|
16
|
-
/** Selector for blocks without editable text inside: rules, images and
|
|
17
|
-
export declare const ATOM_SELECTOR = "hr, img, div[data-type=\"page-break\"]";
|
|
16
|
+
/** Selector for blocks without editable text inside: rules, images, page breaks and section breaks. */
|
|
17
|
+
export declare const ATOM_SELECTOR = "hr, img, div[data-type=\"page-break\"], div[data-type=\"section-break\"]";
|
|
18
|
+
/** Whether a `<div>` is one of the break blocks the document model keeps: a page break or a section break. */
|
|
19
|
+
export declare const isBreakBlock: (node: Node | null | undefined) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a section break: the sheets after it are turned to `orientation`. It also starts a new sheet, so it is drawn
|
|
22
|
+
* and printed like a page break.
|
|
23
|
+
*/
|
|
24
|
+
export declare const createSectionBreak: (orientation: "portrait" | "landscape") => HTMLDivElement;
|
|
18
25
|
/** Selector for blocks that hold editable inline text. */
|
|
19
26
|
export declare const TEXT_BLOCK_SELECTOR = "p, h1, h2, h3, h4, h5, h6, pre";
|
|
20
27
|
/** Marks the extra `<br>` that keeps an empty or break-terminated line visible; it is never serialised. */
|
|
@@ -6,6 +6,7 @@ import { EditorHistory } from './history';
|
|
|
6
6
|
import { type ListKind } from './lists';
|
|
7
7
|
import { type MarkName, type StyleName, type TextCase } from './marks';
|
|
8
8
|
import { SearchController, type SearchState } from './search';
|
|
9
|
+
import { type TextBookmark } from './selection';
|
|
9
10
|
import { type TableCell } from './tables';
|
|
10
11
|
/** A tracked insertion or deletion; the parts of one edit are joined. */
|
|
11
12
|
export interface TrackedChange {
|
|
@@ -162,10 +163,23 @@ export declare class DocumentEngine {
|
|
|
162
163
|
* @param options.addToHistory Record the replacement as an undo step instead of clearing the history.
|
|
163
164
|
* @param options.emitUpdate Emit `update` so the host saves the new content.
|
|
164
165
|
*/
|
|
165
|
-
setContent(html: string, { addToHistory, emitUpdate }?: {
|
|
166
|
+
setContent(html: string, { addToHistory, emitUpdate, keepSelection }?: {
|
|
166
167
|
addToHistory?: boolean | undefined;
|
|
167
168
|
emitUpdate?: boolean | undefined;
|
|
169
|
+
keepSelection?: boolean | undefined;
|
|
168
170
|
}): void;
|
|
171
|
+
/**
|
|
172
|
+
* The selection as character positions through the document: `anchor` where selecting started, `focus` where the
|
|
173
|
+
* caret is. Positions survive changes to the markup, so they can be sent to other people editing the document.
|
|
174
|
+
*/
|
|
175
|
+
getSelectionOffsets(): TextBookmark | null;
|
|
176
|
+
/**
|
|
177
|
+
* Viewport rectangles of the text between two character positions, for drawing someone else's selection; a
|
|
178
|
+
* collapsed position gives the rectangle of its caret line with no width.
|
|
179
|
+
*/
|
|
180
|
+
getOffsetRects(offsets: TextBookmark): DOMRect[];
|
|
181
|
+
/** Number of character positions in the document, the largest offset a selection can have. */
|
|
182
|
+
private characterPositions;
|
|
169
183
|
/** Switches between editing and read-only mode. */
|
|
170
184
|
setEditable(editable: boolean): void;
|
|
171
185
|
/** Word and character counts of the document text. */
|
|
@@ -395,6 +409,8 @@ export declare class DocumentEngine {
|
|
|
395
409
|
private insertBlock;
|
|
396
410
|
/** Inserts a horizontal rule. */
|
|
397
411
|
insertHorizontalRule(): void;
|
|
412
|
+
/** Inserts a section break: the sheets after it are turned to `orientation`, for example for a wide table. */
|
|
413
|
+
insertSectionBreak(orientation: 'portrait' | 'landscape'): void;
|
|
398
414
|
/** Inserts a manual page break. */
|
|
399
415
|
insertPageBreak(): void;
|
|
400
416
|
/**
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export declare const VARIABLE_NAME: RegExp;
|
|
3
3
|
/** Attribute holding the label a variable chip shows in the editor; editor-only. */
|
|
4
4
|
export declare const VARIABLE_LABEL_ATTRIBUTE = "data-label";
|
|
5
|
+
/** Attribute pagination writes on blocks of a section turned against the document's orientation; editor-only. */
|
|
6
|
+
export declare const ROTATED_ATTRIBUTE = "data-doc-rotated";
|
|
5
7
|
/** Attribute pagination writes on blocks it moved to the next sheet; editor-only. */
|
|
6
8
|
export declare const GAP_ATTRIBUTE = "data-doc-gap";
|
|
7
9
|
/** Class marking table cells in a drag selection; editor-only. */
|
package/dist/core/export.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface DocumentSnapshot {
|
|
|
9
9
|
* with real page numbers and breaks the pages exactly where the editor shows them.
|
|
10
10
|
*/
|
|
11
11
|
pages?: string[];
|
|
12
|
+
/** For every sheet of `pages`, whether a section break turned it against the document's orientation. */
|
|
13
|
+
rotated?: boolean[];
|
|
12
14
|
/** Footnotes of every sheet of `pages`; without pages, the notes of all of them are printed after the document. */
|
|
13
15
|
footnotes?: SheetFootnote[][];
|
|
14
16
|
/** Document title, used for the print title and file names. */
|
|
@@ -16,6 +18,11 @@ interface DocumentSnapshot {
|
|
|
16
18
|
/** Page size, orientation, margins and the running texts. */
|
|
17
19
|
page: PageSettings;
|
|
18
20
|
}
|
|
21
|
+
/** Paper width and height in millimetres, in the chosen orientation. */
|
|
22
|
+
export declare const paperSize: ({ size, orientation }: PageSettings) => {
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
};
|
|
19
26
|
/** Turns a document title into a safe file name without extension. */
|
|
20
27
|
export declare const toFileName: (title: string) => string;
|
|
21
28
|
/**
|
|
@@ -24,7 +31,7 @@ export declare const toFileName: (title: string) => string;
|
|
|
24
31
|
* the pages break where the editor shows them; without them the document flows and the browser repeats one fixed
|
|
25
32
|
* header and footer on every page.
|
|
26
33
|
*/
|
|
27
|
-
export declare const buildPrintableHtml: ({ html, pages, footnotes, title, page }: DocumentSnapshot) => string;
|
|
34
|
+
export declare const buildPrintableHtml: ({ html, pages, rotated, footnotes, title, page }: DocumentSnapshot) => string;
|
|
28
35
|
/** Offers text or binary content as a file download. */
|
|
29
36
|
export declare const downloadFile: (content: string | Blob, fileName: string, type: string) => void;
|
|
30
37
|
/** Prints through a detached iframe so the application chrome never ends up on paper. Waits for images first. */
|
|
@@ -59,6 +59,7 @@ export declare const uz: {
|
|
|
59
59
|
readonly 'editor.enterFullscreen': "To‘liq ekran";
|
|
60
60
|
readonly 'editor.exitFullscreen': "To‘liq ekrandan chiqish";
|
|
61
61
|
readonly 'editor.exportHtml': "HTML sifatida yuklab olish";
|
|
62
|
+
readonly 'editor.exportPdf': "PDF sifatida yuklab olish";
|
|
62
63
|
readonly 'editor.exportWord': "Word (.docx) sifatida yuklab olish";
|
|
63
64
|
readonly 'editor.fitWidth': "Kenglikka moslash";
|
|
64
65
|
readonly 'editor.fontFamily': "Shrift";
|
|
@@ -112,6 +113,10 @@ export declare const uz: {
|
|
|
112
113
|
readonly 'editor.numbering.legal': "Ko‘p darajali raqamlash (1.1, 1.2)";
|
|
113
114
|
readonly 'editor.outline': "Navigatsiya paneli";
|
|
114
115
|
readonly 'editor.outline.empty': "Hujjatda sarlavhalar yo‘q. Sarlavha uslubini qo‘llang, ular shu yerda ko‘rinadi.";
|
|
116
|
+
readonly 'editor.outline.headings': "Sarlavhalar";
|
|
117
|
+
readonly 'editor.outline.noPages': "Sahifalar faqat sahifa ko‘rinishida chiqadi.";
|
|
118
|
+
readonly 'editor.outline.page': "{page}-sahifa";
|
|
119
|
+
readonly 'editor.outline.pages': "Sahifalar";
|
|
115
120
|
readonly 'editor.openLink': "Havolani ochish";
|
|
116
121
|
readonly 'editor.or': "yoki havola orqali";
|
|
117
122
|
readonly 'editor.orderedList': "Raqamli ro‘yxat";
|
|
@@ -147,6 +152,7 @@ export declare const uz: {
|
|
|
147
152
|
readonly 'editor.replace.one': "Almashtirish";
|
|
148
153
|
readonly 'editor.replace.placeholder': "Almashtirish";
|
|
149
154
|
readonly 'editor.replace.toggle': "Almashtirishni ko‘rsatish";
|
|
155
|
+
readonly 'editor.review': "Taqriz: izohlar va o‘zgarishlar";
|
|
150
156
|
readonly 'editor.ruler': "Chizg‘ich";
|
|
151
157
|
readonly 'editor.ruler.firstLine': "Birinchi qator chekinishi";
|
|
152
158
|
readonly 'editor.ruler.indentLeft': "Chap chekinish";
|
|
@@ -160,6 +166,8 @@ export declare const uz: {
|
|
|
160
166
|
readonly 'editor.search.title': "Qidirish va almashtirish";
|
|
161
167
|
readonly 'editor.search.wholeWord': "Butun so‘z";
|
|
162
168
|
readonly 'editor.selectAll': "Barchasini tanlash";
|
|
169
|
+
readonly 'editor.sectionBreak.landscape': "Bo‘lim uzilishi: albom sahifalar";
|
|
170
|
+
readonly 'editor.sectionBreak.portrait': "Bo‘lim uzilishi: kitob sahifalar";
|
|
163
171
|
readonly 'editor.signature': "Imzo bloki";
|
|
164
172
|
readonly 'editor.signature.agreed': "KELISHILDI grifi";
|
|
165
173
|
readonly 'editor.signature.approval': "TASDIQLAYMAN grifi";
|
package/dist/core/outline.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface OutlineHeading {
|
|
|
12
12
|
/** The heading element in the editable document. */
|
|
13
13
|
element: HTMLElement;
|
|
14
14
|
}
|
|
15
|
+
/** What the navigation panel lists: the headings, or thumbnails of the pages. */
|
|
16
|
+
export type OutlineView = 'headings' | 'pages';
|
|
15
17
|
/** A line of the table of contents. */
|
|
16
18
|
export interface TableOfContentsEntry {
|
|
17
19
|
/** Heading level, 1 to 6; deeper levels are indented. */
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { type SheetFootnote } from './footnotes';
|
|
2
|
-
import type { PageMetrics } from './page';
|
|
2
|
+
import type { PageMetrics, PageOrientation } from './page';
|
|
3
|
+
/** Where a sheet is drawn and which way it is turned. */
|
|
4
|
+
export interface SheetGeometry {
|
|
5
|
+
/** Distance of the sheet from the top of the first sheet, in pixels. */
|
|
6
|
+
top: number;
|
|
7
|
+
/** Sheet width in pixels. */
|
|
8
|
+
width: number;
|
|
9
|
+
/** Sheet height in pixels. */
|
|
10
|
+
height: number;
|
|
11
|
+
/** Orientation of the sheet. */
|
|
12
|
+
orientation: PageOrientation;
|
|
13
|
+
/** Whether the sheet is turned against the document's own orientation, by a section break. */
|
|
14
|
+
rotated: boolean;
|
|
15
|
+
}
|
|
3
16
|
/** Handle returned by {@link createPagination}. */
|
|
4
17
|
export interface PaginationController {
|
|
5
18
|
/** Sets the page geometry and re-runs the layout; pass `null` to switch pagination off (web view). */
|
|
@@ -15,15 +28,21 @@ interface PaginationOptions {
|
|
|
15
28
|
onPageCount: (count: number) => void;
|
|
16
29
|
/** Receives the footnotes of every sheet whenever they change; the web view is one sheet holding all of them. */
|
|
17
30
|
onFootnotes?: (sheets: SheetFootnote[][]) => void;
|
|
31
|
+
/** Receives the position, size and orientation of every sheet whenever they change; empty in the web view. */
|
|
32
|
+
onSheets?: (sheets: SheetGeometry[]) => void;
|
|
18
33
|
/** Whether an IME composition is in progress, during which blocks must not move. */
|
|
19
34
|
isComposing: () => boolean;
|
|
20
35
|
}
|
|
36
|
+
/** Sheets of a document without section breaks, all alike. */
|
|
37
|
+
export declare const uniformSheets: (metrics: PageMetrics, count: number) => SheetGeometry[];
|
|
38
|
+
/** Index of the sheet a vertical position falls on, counted from the top of the first sheet. */
|
|
39
|
+
export declare const sheetIndexAt: (sheets: readonly SheetGeometry[], offset: number) => number;
|
|
21
40
|
/**
|
|
22
41
|
* Splits the laid-out document into the clean HTML of every sheet, for printing and export. Pagination has already
|
|
23
42
|
* moved every block onto its page, so a block belongs to the page its top falls on; blocks taller than a page stay
|
|
24
43
|
* whole, exactly as they are shown on screen. Footnote references keep their document-wide numbers.
|
|
25
44
|
*/
|
|
26
|
-
export declare const splitIntoPages: (root: HTMLElement, metrics: PageMetrics, pageCount: number) => string[];
|
|
45
|
+
export declare const splitIntoPages: (root: HTMLElement, metrics: PageMetrics, pageCount: number, geometry?: readonly SheetGeometry[]) => string[];
|
|
27
46
|
/**
|
|
28
47
|
* Keeps the document laid out on sheets. Layout passes are batched to one per animation frame and re-run when the
|
|
29
48
|
* document resizes, images or fonts load, or the host schedules one after an edit.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF download without the print dialog and without dependencies. Every sheet of the printable document is drawn into
|
|
3
|
+
* a canvas through an SVG `foreignObject`, and the pictures are written into a PDF, one per page. The PDF looks like
|
|
4
|
+
* the printout, but its text cannot be selected or searched.
|
|
5
|
+
*/
|
|
6
|
+
/** A rendered page: a JPEG picture of the sheet and the paper size it is printed on. */
|
|
7
|
+
export interface PdfPage {
|
|
8
|
+
/** JPEG data of the page picture. */
|
|
9
|
+
jpeg: Uint8Array;
|
|
10
|
+
/** Picture width in pixels. */
|
|
11
|
+
width: number;
|
|
12
|
+
/** Picture height in pixels. */
|
|
13
|
+
height: number;
|
|
14
|
+
/** Paper width in PDF points (1/72 inch). */
|
|
15
|
+
widthPt: number;
|
|
16
|
+
/** Paper height in PDF points. */
|
|
17
|
+
heightPt: number;
|
|
18
|
+
}
|
|
19
|
+
/** Options of {@link renderPdf}. */
|
|
20
|
+
export interface PdfOptions {
|
|
21
|
+
/** Pixels per CSS pixel of the page pictures; higher is sharper and larger. Defaults to 2. */
|
|
22
|
+
scale?: number;
|
|
23
|
+
/** JPEG quality between 0 and 1. Defaults to 0.92. */
|
|
24
|
+
quality?: number;
|
|
25
|
+
}
|
|
26
|
+
/** MIME type of PDF files. */
|
|
27
|
+
export declare const PDF_MIME = "application/pdf";
|
|
28
|
+
/**
|
|
29
|
+
* Writes JPEG pictures into a PDF document, one picture filling each page.
|
|
30
|
+
*
|
|
31
|
+
* @returns the PDF file content.
|
|
32
|
+
*/
|
|
33
|
+
export declare const buildPdf: (pages: readonly PdfPage[]) => Uint8Array;
|
|
34
|
+
/**
|
|
35
|
+
* Turns the printable HTML of a paginated document (one `section.doc-sheet` per page) into a PDF.
|
|
36
|
+
*
|
|
37
|
+
* @param printableHtml Standalone HTML as built for printing from the page view.
|
|
38
|
+
* @param paper Paper width and height in millimetres, in the chosen orientation.
|
|
39
|
+
*/
|
|
40
|
+
export declare const renderPdf: (printableHtml: string, paper: {
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}, options?: PdfOptions) => Promise<Blob>;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** Uploads an image and resolves with its public URL. Without a handler, images are embedded as data URLs. */
|
|
2
2
|
export type DocumentImageUploadHandler = (file: File) => Promise<string>;
|
|
3
|
-
export type DocumentMenuAction = 'source' | 'print' | 'exportHtml' | 'exportWord' | 'importWord' | 'formattingMarks' | 'ruler' | 'outline' | 'tableOfContents' | 'comments' | 'addComment' | 'footnote' | 'trackChanges' | 'changes' | 'fullscreen';
|
|
3
|
+
export type DocumentMenuAction = 'source' | 'print' | 'exportHtml' | 'exportWord' | 'exportPdf' | 'importWord' | 'formattingMarks' | 'ruler' | 'outline' | 'tableOfContents' | 'comments' | 'addComment' | 'footnote' | 'trackChanges' | 'changes' | 'fullscreen';
|