nuvra 0.4.1 → 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.
Files changed (62) hide show
  1. package/README.md +241 -19
  2. package/dist/compare.vue.d.ts +19 -0
  3. package/dist/components/document-editor.vue.d.ts +59 -3
  4. package/dist/components/editor-canvas.vue.d.ts +9 -7
  5. package/dist/components/editor-changes.vue.d.ts +18 -0
  6. package/dist/components/editor-collaborators.vue.d.ts +17 -0
  7. package/dist/components/editor-comments.vue.d.ts +32 -0
  8. package/dist/components/editor-footnote-form.vue.d.ts +19 -0
  9. package/dist/components/editor-outline.vue.d.ts +36 -0
  10. package/dist/components/editor-slash-menu.vue.d.ts +13 -0
  11. package/dist/components/editor-toolbar.vue.d.ts +24 -1
  12. package/dist/core/collaboration.d.ts +25 -0
  13. package/dist/core/comments.d.ts +26 -0
  14. package/dist/core/dates.d.ts +8 -0
  15. package/dist/core/diff.d.ts +11 -0
  16. package/dist/core/document-templates.d.ts +22 -0
  17. package/dist/core/docx/export.d.ts +19 -0
  18. package/dist/core/docx/import.d.ts +19 -0
  19. package/dist/core/docx/zip.d.ts +23 -0
  20. package/dist/core/engine/dom.d.ts +40 -3
  21. package/dist/core/engine/editing.d.ts +11 -0
  22. package/dist/core/engine/engine.d.ts +161 -3
  23. package/dist/core/engine/input-rules.d.ts +3 -1
  24. package/dist/core/engine/marks.d.ts +50 -1
  25. package/dist/core/engine/schema.d.ts +9 -2
  26. package/dist/core/export.d.ts +13 -5
  27. package/dist/core/footnotes.d.ts +15 -0
  28. package/dist/core/icons.d.ts +1 -1
  29. package/dist/core/labels.d.ts +6 -2
  30. package/dist/core/locales/uz-cyrl.d.ts +3 -0
  31. package/dist/core/locales/uz.d.ts +72 -1
  32. package/dist/core/numbers.d.ts +19 -0
  33. package/dist/core/outline.d.ts +44 -0
  34. package/dist/core/page.d.ts +12 -2
  35. package/dist/core/pagination.d.ts +25 -3
  36. package/dist/core/pdf.d.ts +43 -0
  37. package/dist/core/signature.d.ts +15 -0
  38. package/dist/core/slash-commands.d.ts +19 -0
  39. package/dist/core/templates.d.ts +32 -0
  40. package/dist/core/transliterate.d.ts +13 -0
  41. package/dist/core/types.d.ts +1 -1
  42. package/dist/core/ui-state.d.ts +8 -0
  43. package/dist/editor.vue.d.ts +6 -2
  44. package/dist/export-B06RXLqn.js +96 -0
  45. package/dist/export-B06RXLqn.js.map +1 -0
  46. package/dist/footnotes-DBShImw7.js +11 -0
  47. package/dist/footnotes-DBShImw7.js.map +1 -0
  48. package/dist/form.vue.d.ts +39 -0
  49. package/dist/index.d.ts +28 -1
  50. package/dist/index.js +8391 -4198
  51. package/dist/index.js.map +1 -1
  52. package/dist/{page-DL7Oj2o8.js → page-DY5fdgcT.js} +10 -10
  53. package/dist/page-DY5fdgcT.js.map +1 -0
  54. package/dist/pdf-mIRjnoOP.js +70 -0
  55. package/dist/pdf-mIRjnoOP.js.map +1 -0
  56. package/dist/style.css +1 -1
  57. package/dist/zip-CpbwZtot.js +46 -0
  58. package/dist/zip-CpbwZtot.js.map +1 -0
  59. package/package.json +5 -2
  60. package/dist/export-VeTfgCEp.js +0 -117
  61. package/dist/export-VeTfgCEp.js.map +0 -1
  62. package/dist/page-DL7Oj2o8.js.map +0 -1
package/README.md CHANGED
@@ -1,14 +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, HTML source mode, printing and export to HTML or Word (`.doc`).
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.
5
6
 
6
7
  **[Documentation and live demo →](https://nuvra-docs.vercel.app)**
7
8
 
8
9
  - No editor framework underneath: its own small editing engine with a sanitizing schema.
9
10
  - No UI framework either: native HTML controls, built-in SVG icons and plain CSS variables. Vue is the only
10
11
  dependency.
11
- - 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.
12
14
  - Tables with merge / split, images with resize and alignment, task lists, links, colors, fonts.
13
15
  - Undo / redo, keyboard shortcuts that work with non-Latin keyboard layouts, Markdown-like input rules.
14
16
  - Office tools: format painter, letter case, paragraph spacing, formatting marks, a right-click menu and
@@ -17,6 +19,13 @@ forms, tables, images, lists, find & replace, HTML source mode, printing and exp
17
19
  the editor shows them, and the Word export uses Word's own header, footer and page fields.
18
20
  - A ruler above the page for the margins and for the first line, left and right indents of a paragraph.
19
21
  - A page watermark such as DRAFT or COPY, drawn behind the text of every page and carried into print, HTML and Word.
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.
24
+ - Footnotes at the bottom of their page, carried into print and Word.
25
+ - Comments with replies, tracked changes that round-trip with Word, a comparison of two versions, and templates
26
+ filled in as a form.
27
+ - Hooks for editing together: other people's carets and selections, your selection as character positions.
28
+ - A `/` command menu and a toolbar slot for your own commands and buttons.
20
29
 
21
30
  ## Installation
22
31
 
@@ -91,9 +100,13 @@ const uploadImage: DocumentImageUploadHandler = async file => {
91
100
  | Prop | Type | Default | Description |
92
101
  | ----------------- | --------------------------------- | -------- | -------------------------------------------------------------------------- |
93
102
  | `v-model` | `string` | `''` | Document HTML; an empty document is an empty string. |
94
- | `v-model:page` | `PageSettings` | A4 | Paper size, orientation and margins. |
103
+ | `v-model:page` | `PageSettings` | A4 | Paper size, orientation, margins, headers, footers, watermark, numbering. |
104
+ | `v-model:comments`| `DocumentComment[]` | — | Comments; binding it turns the comment tools on. |
105
+ | `v-model:trackChanges` | `boolean` | `false` | Records edits as tracked changes. |
106
+ | `author` | `string` | `''` | Name written on new comments, replies and tracked changes. |
95
107
  | `autofocus` | `boolean` | `false` | Places the caret at the end of the document once ready. |
96
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. |
97
110
  | `defaultViewMode` | `'page' \| 'web'` | `'page'` | View shown first. |
98
111
  | `disabled` | `boolean` | `false` | Read-only document, disabled controls. |
99
112
  | `height` | `number \| string` | `760` | Height of the editor, or `'auto'` to grow between `minHeight`/`maxHeight`. |
@@ -103,43 +116,252 @@ const uploadImage: DocumentImageUploadHandler = async file => {
103
116
  | `maxImageSizeMb` | `number` | `10` | Largest accepted image file. |
104
117
  | `maxLength` | `number` | `0` | Character limit; `0` means unlimited. |
105
118
  | `placeholder` | `string` | `''` | Text shown while the document is empty. |
119
+ | `ruler` | `boolean` | `true` | Shows the ruler in the page view. |
120
+ | `slashCommands` | `SlashCommand[]` | `[]` | Your own commands, listed first in the `/` menu. |
106
121
  | `title` | `string` | `''` | Print title and exported file name. |
107
122
  | `uploadImage` | `(file: File) => Promise<string>` | — | Uploads an image and resolves with its URL. |
123
+ | `variables` | `TemplateVariable[]` | `[]` | Template variables the user can insert. |
108
124
 
109
- `Editor` accepts the same props except `v-model:page`, `defaultViewMode`, `height` and `title`.
125
+ `Editor` accepts the same props except `v-model:page`, `v-model:comments`, `v-model:trackChanges`, `author`,
126
+ `collaborators`, `defaultViewMode`, `height`, `ruler`, `slashCommands` and `title`.
110
127
 
111
128
  Numbers are pixels; strings are used as CSS lengths (`'100%'`, `'50vh'`).
112
129
 
113
130
  ### Events
114
131
 
115
- | Event | Payload | Description |
116
- | ------------- | ---------------- | ------------------------------------------------- |
117
- | `focus` | — | The document received focus. |
118
- | `blur` | — | The document lost focus; the model is up to date. |
119
- | `uploadError` | `error: unknown` | An image was rejected or its upload failed. |
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. |
120
140
 
121
141
  ### Exposed methods (`DocumentEditor` ref)
122
142
 
123
- | Method | Description |
124
- | -------------- | ---------------------------------------------------------------- |
125
- | `focus()` | Moves keyboard focus into the document. |
126
- | `getHTML()` | Returns the document HTML, including edits not yet in the model. |
127
- | `print()` | Opens the browser print dialog. |
128
- | `exportHtml()` | Downloads the document as an HTML page. |
129
- | `exportWord()` | Downloads the document as a Word-compatible `.doc` file. |
130
- | `engine` | The editing engine, for advanced integrations. |
143
+ | Method | Description |
144
+ | ------------------------- | ---------------------------------------------------------------- |
145
+ | `focus()` | Moves keyboard focus into the document. |
146
+ | `getHTML()` | Returns the document HTML, including edits not yet in the model. |
147
+ | `insertVariable(name)` | Inserts a template variable at the selection. |
148
+ | `updateTableOfContents()` | Inserts or refreshes the table of contents. |
149
+ | `importWord(file)` | Replaces the document with the content of a `.docx` file. |
150
+ | `print()` | Opens the browser print dialog. |
151
+ | `exportHtml()` | Downloads the document as an HTML page. |
152
+ | `exportWord()` | Downloads the document as a Word file (`.docx`). |
153
+ | `exportPdf()` | Downloads the document as a PDF drawn from its pages. |
154
+ | `engine` | The editing engine (`DocumentEngine`), for advanced integrations. |
131
155
 
132
156
  ### Keyboard shortcuts
133
157
 
134
158
  `Ctrl/⌘+B`, `I`, `U` — bold, italic, underline · `Ctrl/⌘+Shift+H` — highlight · `Ctrl/⌘+Z`, `Ctrl/⌘+Shift+Z` — undo,
135
- redo · `Ctrl/⌘+F` — find · `Ctrl/⌘+H` — replace · `Ctrl/⌘+K` — link · `Ctrl/⌘+P` — print.
159
+ redo · `Ctrl/⌘+F` — find · `Ctrl/⌘+H` — replace · `Ctrl/⌘+K` — link · `Ctrl/⌘+Alt+M` — comment · `Ctrl/⌘+P` — print.
136
160
 
137
161
  The full list, with Markdown-like input rules, is in the
138
162
  [keyboard shortcuts guide](https://nuvra-docs.vercel.app/docs/keyboard-shortcuts).
139
163
 
164
+ ## Templates and signatures
165
+
166
+ Pass `variables` to let users insert template variables with the toolbar's **{ }** menu or by typing `{{name}}`.
167
+ They are saved as `<span data-variable="name">{{name}}</span>`; `fillTemplate` replaces them with escaped values,
168
+ in the browser or on a Node server:
169
+
170
+ ```ts
171
+ import { type TemplateVariable, fillTemplate } from 'nuvra';
172
+
173
+ const variables: TemplateVariable[] = [
174
+ { name: 'full_name', label: 'Full name' },
175
+ { name: 'letter_date', label: 'Letter date' }
176
+ ];
177
+
178
+ const letter = fillTemplate(template, { full_name: 'Aziz Karimov', letter_date: '14.09.2026' });
179
+ ```
180
+
181
+ The pen button inserts a signature block in the editor's language: a signature line, an “Approved” or “Agreed”
182
+ block, or the signatures of both parties of a contract. It is a borderless table, so it prints and exports to Word
183
+ without lines.
184
+
185
+ The document button inserts ready-made templates (official letter, order, application, certificate, act), and the
186
+ toolbar writes amounts in words (`15 000 000 (o‘n besh million)`), inserts long dates (`2026-yil 14-sentabr`) and
187
+ converts Uzbek text between the Latin and Cyrillic alphabets. The same helpers are exported: `getDocumentTemplate`,
188
+ `numberToWords`, `formatAmountInWords`, `parseAmount`, `formatLongDate` and `transliterate`.
189
+
190
+ ### Filling a template as a form
191
+
192
+ `DocumentForm` shows a saved template as it will be printed, with an input in place of every variable. Fields of the
193
+ same variable share one value:
194
+
195
+ ```vue
196
+ <script setup lang="ts">
197
+ import { ref } from 'vue';
198
+ import { DocumentForm } from 'nuvra';
199
+
200
+ const values = ref<Record<string, string>>({});
201
+ const form = ref<InstanceType<typeof DocumentForm>>();
202
+
203
+ const submit = () => {
204
+ if (form.value?.validate().length) return; // empty fields are marked and focused
205
+ const html = form.value?.getHTML(); // filled with fillTemplate
206
+ };
207
+ </script>
208
+
209
+ <template>
210
+ <DocumentForm ref="form" v-model="values" :template="template" :variables="variables" />
211
+ </template>
212
+ ```
213
+
214
+ ## Word files and long documents
215
+
216
+ "Download as Word (.docx)" in the "More" menu writes a real Office Open XML file with the page setup, headers and
217
+ footers with page fields, the watermark, lists, tables with merged cells, images, footnotes and tracked changes as
218
+ Word revisions. "Open Word file (.docx)" (or `importWord(file)`) loads one into the editor, footnotes and revisions
219
+ included; a file that cannot be read emits `importError`. `buildDocx` and `readDocx` do the same in your own code. A
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.
239
+
240
+ ### Footnotes
241
+
242
+ "Footnote" in the insert menu or the `/` menu adds a numbered reference and opens a small form for the note; clicking
243
+ a reference edits or deletes it. A footnote is saved in the reference, `<sup data-footnote="note text">1</sup>`, and
244
+ renumbered in document order. The page view draws the notes at the bottom of the sheet the reference is on, the web
245
+ view after the document; printing follows the sheets, and the Word export writes Word footnotes. Notes are plain text.
246
+ From code: `engine.insertFootnote(text)`, `setFootnoteText(element, text)`, `removeFootnote(element)`,
247
+ `getFootnotes()`.
248
+
249
+ For long documents the toolbar offers multilevel numbering (1., 1.1., 1.1.1., saved as `<ol data-numbering="legal">`),
250
+ a table of contents of the headings with page numbers (saved as `<table data-type="toc">`, refreshed with
251
+ `updateTableOfContents()`), and a navigation pane. `PageSettings` has `differentFirstPage` to hide the header, footer
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.
254
+
255
+ ## Comments, tracked changes and comparison
256
+
257
+ Bind `v-model:comments` to turn comments on. The HTML keeps only the anchors, `<span data-comment="id">`; the
258
+ comments are plain data you store next to the document:
259
+
260
+ ```vue
261
+ <script setup lang="ts">
262
+ import { ref } from 'vue';
263
+ import { DocumentEditor, type DocumentComment } from 'nuvra';
264
+
265
+ const html = ref('');
266
+ const comments = ref<DocumentComment[]>([]);
267
+
268
+ const save = () =>
269
+ fetch('/api/documents/42', {
270
+ method: 'PUT',
271
+ headers: { 'Content-Type': 'application/json' },
272
+ body: JSON.stringify({ html: html.value, comments: comments.value })
273
+ });
274
+ </script>
275
+
276
+ <template>
277
+ <DocumentEditor v-model="html" v-model:comments="comments" author="Aziz Karimov" @blur="save" />
278
+ </template>
279
+ ```
280
+
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.
284
+
285
+ ### Tracked changes
286
+
287
+ Bind `v-model:trackChanges` (or choose "Track changes" in the Review menu) to record typing, deleting, cut and paste as tracked changes
288
+ by `author`. They are part of the HTML, `<ins data-change="id" data-author="…" data-time="…">` and `<del …>`, shown
289
+ and printed green-underlined and red-struck. The "Changes" panel accepts or rejects them one by one or all at once;
290
+ from code use `engine.getChanges()`, `engine.resolveChanges(accept, id?)` and `engine.selectChange(id)`. Formatting,
291
+ block changes (headings, lists, tables), Enter and joining paragraphs are not tracked. The Word export writes them as
292
+ revisions and the import reads Word revisions back.
293
+
294
+ ```vue
295
+ <DocumentEditor v-model="html" v-model:track-changes="tracking" author="Aziz Karimov" />
296
+ ```
297
+
298
+ `DocumentCompare` shows what changed between two versions: inserted words in green, deleted words struck through in
299
+ red. Unchanged blocks stay as they are, changed paragraphs are compared word by word, and added or removed blocks are
300
+ shown whole. `compareDocuments(before, after)` returns the same HTML and counts for your own view.
301
+
302
+ ```vue
303
+ <DocumentCompare :before="previousVersion" :after="html" :height="600" />
304
+ ```
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
+
336
+ ## Commands and toolbar buttons
337
+
338
+ Typing `/` at the start of a line or after a space opens a command menu: headings, lists, table, page and section breaks, footnote,
339
+ table of contents, dates, signature blocks and your variables. `slashCommands` adds your own commands at the top, and the
340
+ `toolbar` slot adds your own buttons:
341
+
342
+ ```vue
343
+ <script setup lang="ts">
344
+ import { DocumentEditor, type SlashCommand } from 'nuvra';
345
+
346
+ const slashCommands: SlashCommand[] = [
347
+ { id: 'director', label: 'Director’s name', icon: 'pencil', run: engine => engine.insertText('A. Karimov') }
348
+ ];
349
+ </script>
350
+
351
+ <template>
352
+ <DocumentEditor v-model="html" :slash-commands="slashCommands">
353
+ <template #toolbar="{ engine, disabled }">
354
+ <button type="button" class="doc-tb-button" :disabled="disabled" @mousedown.prevent @click="engine.insertText('✓')">
355
+
356
+ </button>
357
+ </template>
358
+ </DocumentEditor>
359
+ </template>
360
+ ```
361
+
140
362
  ## Languages
141
363
 
142
- The interface ships in Uzbek (`uz`, the default), English (`en`) and Russian (`ru`). The translations are part of
364
+ The interface ships in Uzbek (`uz`, the default), Uzbek Cyrillic (`uzCyrl`), English (`en`) and Russian (`ru`). The translations are part of
143
365
  the package and cannot be changed from outside; an app only picks the language.
144
366
 
145
367
  For one editor, pass the locale (or just its code) to the `locale` prop:
@@ -0,0 +1,19 @@
1
+ import { type EditorLocaleInput } from './core/labels';
2
+ import './styles/document-content.css';
3
+ import './styles/editor-ui.css';
4
+ interface Props {
5
+ /** HTML of the earlier version. */
6
+ before: string;
7
+ /** HTML of the later version. */
8
+ after: string;
9
+ /** Interface language: a built-in locale or its code; defaults to the app-wide language. */
10
+ locale?: EditorLocaleInput;
11
+ /** Height of the component, in pixels or any CSS length; `'auto'` grows with the document. */
12
+ height?: number | string;
13
+ }
14
+ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
15
+ height: number | string;
16
+ locale: EditorLocaleInput;
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -1,7 +1,12 @@
1
+ import type { Collaborator, SelectionOffsets } from '../core/collaboration';
2
+ import { type DocumentComment } from '../core/comments';
1
3
  import { DocumentEngine } from '../core/engine/engine';
2
4
  import { type EditorLocaleInput } from '../core/labels';
3
5
  import { type DocumentViewMode, type PageSettings } from '../core/page';
6
+ import type { SlashCommand } from '../core/slash-commands';
7
+ import type { TemplateVariable } from '../core/templates';
4
8
  import type { DocumentImageUploadHandler } from '../core/types';
9
+ import { type EditorUiState } from '../core/ui-state';
5
10
  import '../styles/document-content.css';
6
11
  import '../styles/editor-ui.css';
7
12
  /** A CSS length: numbers are pixels, strings are used as written (for example, `'100%'` or `'auto'`). */
@@ -11,6 +16,10 @@ interface Props {
11
16
  autofocus?: boolean;
12
17
  /** Gray space around the page or web sheet that separates the document from the editor frame. */
13
18
  canvasPadding?: CssSize;
19
+ /** Other people editing the document; their carets and selections are drawn over it. */
20
+ collaborators?: readonly Collaborator[];
21
+ /** Name written as the author of new comments, replies and tracked changes. */
22
+ author?: string;
14
23
  /** View shown first; form fields use the lighter web view. */
15
24
  defaultViewMode?: DocumentViewMode;
16
25
  /** Makes the document read-only and disables every editing control. */
@@ -29,32 +38,59 @@ interface Props {
29
38
  minHeight?: CssSize;
30
39
  /** Text shown while the document is empty; falls back to the translated default placeholder. */
31
40
  placeholder?: string;
41
+ /** Commands of the host application, listed first in the `/` menu. */
42
+ slashCommands?: readonly SlashCommand[];
32
43
  /** Shows the ruler above the sheet in the page view; users can also toggle it in the document menu. */
33
44
  ruler?: boolean;
34
45
  /** Used as the print title and the exported file name. */
35
46
  title?: string;
36
47
  /** Uploads an inserted image and resolves with its URL; without it images are embedded as data URLs. */
37
48
  uploadImage?: DocumentImageUploadHandler;
49
+ /** Template variables the user can insert; typing `{{name}}` of one of them inserts it as well. */
50
+ variables?: readonly TemplateVariable[];
38
51
  }
39
52
  type __VLS_Props = Props;
53
+ type __VLS_Slots = {
54
+ /**
55
+ * Buttons of the host application, placed at the start of the toolbar's right-hand group. `engine` runs editing
56
+ * commands, `state` describes the formatting at the caret and `disabled` tells whether editing is possible.
57
+ */
58
+ toolbar?: (props: {
59
+ engine: DocumentEngine;
60
+ state: EditorUiState;
61
+ disabled: boolean;
62
+ }) => unknown;
63
+ };
40
64
  type __VLS_ModelProps = {
41
65
  /** Document HTML. An empty document is written as an empty string; typing updates it after a short delay. */
42
66
  modelValue?: string;
43
67
  /** Paper size, orientation, and margins used by the page view, printing, and export. */
44
68
  'page'?: PageSettings;
69
+ /** Whether edits are recorded as tracked changes; the toolbar can switch it. */
70
+ 'trackChanges'?: boolean;
71
+ /** Comments on the document; binding it turns the comment tools on. The HTML keeps only their anchors. */
72
+ 'comments'?: DocumentComment[] | undefined;
45
73
  };
46
74
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
47
- declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
75
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
48
76
  /** The editing engine, for advanced integrations. */
49
77
  engine: import("vue").ShallowRef<DocumentEngine | null, DocumentEngine | null>;
50
78
  /** Downloads the document as an HTML page. */
51
79
  exportHtml: () => Promise<void>;
52
80
  /** Downloads the document as a Word-compatible file. */
53
81
  exportWord: () => Promise<void>;
82
+ /** Downloads the document as a PDF drawn from its pages. */
83
+ exportPdf: () => Promise<void>;
54
84
  /** Moves keyboard focus into the document. */
55
85
  focus: () => void | undefined;
56
86
  /** Returns the document HTML, including edits not yet written to the model. */
57
87
  getHTML: () => string;
88
+ /** Replaces the document with the content and page setup of a `.docx` file. */
89
+ importWord: (file: File) => Promise<void>;
90
+ /** Inserts the chip of a template variable at the selection. */
91
+ insertVariable: (name: string) => void | undefined;
92
+ /** Inserts the table of contents at the selection, or refreshes the existing one. */
93
+ updateTableOfContents: () => Promise<void>;
58
94
  /** Opens the print dialog. */
59
95
  print: () => Promise<void>;
60
96
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
@@ -62,28 +98,48 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
62
98
  blur: () => any;
63
99
  "update:page": (value: PageSettings) => any;
64
100
  uploadError: (error: unknown) => any;
101
+ importError: (error: unknown) => any;
102
+ exportError: (error: unknown) => any;
103
+ selectionChange: (selection: SelectionOffsets | null) => any;
65
104
  "update:modelValue": (value: string) => any;
105
+ "update:trackChanges": (value: boolean) => any;
106
+ "update:comments": (value: DocumentComment[] | undefined) => any;
66
107
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
67
108
  onFocus?: (() => any) | undefined;
68
109
  onBlur?: (() => any) | undefined;
69
110
  "onUpdate:page"?: ((value: PageSettings) => any) | undefined;
70
111
  onUploadError?: ((error: unknown) => any) | undefined;
112
+ onImportError?: ((error: unknown) => any) | undefined;
113
+ onExportError?: ((error: unknown) => any) | undefined;
114
+ onSelectionChange?: ((selection: SelectionOffsets | null) => any) | undefined;
71
115
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
116
+ "onUpdate:trackChanges"?: ((value: boolean) => any) | undefined;
117
+ "onUpdate:comments"?: ((value: DocumentComment[] | undefined) => any) | undefined;
72
118
  }>, {
73
119
  title: string;
74
120
  height: CssSize;
121
+ locale: EditorLocaleInput;
75
122
  ruler: boolean;
76
123
  placeholder: string;
77
124
  disabled: boolean;
78
125
  minHeight: CssSize;
79
- maxLength: number;
126
+ collaborators: readonly Collaborator[];
80
127
  maxHeight: CssSize;
128
+ maxLength: number;
129
+ variables: readonly TemplateVariable[];
130
+ author: string;
81
131
  autofocus: boolean;
82
132
  canvasPadding: CssSize;
83
133
  defaultViewMode: DocumentViewMode;
84
- locale: EditorLocaleInput;
85
134
  maxImageSizeMb: number;
135
+ slashCommands: readonly SlashCommand[];
86
136
  uploadImage: DocumentImageUploadHandler;
87
137
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
138
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
88
139
  declare const _default: typeof __VLS_export;
89
140
  export default _default;
141
+ type __VLS_WithSlots<T, S> = T & {
142
+ new (): {
143
+ $slots: S;
144
+ };
145
+ };
@@ -1,6 +1,8 @@
1
1
  import type { ParagraphIndents } from '../core/engine/blocks';
2
2
  import type { DocumentEngine } from '../core/engine/engine';
3
- import { type DocumentViewMode, type PageHeaderFooter, type PageMargins, type PageMetrics, type PageWatermark } from '../core/page';
3
+ import { type SheetFootnote } from '../core/footnotes';
4
+ import { type DocumentViewMode, type PageMargins, type PageMetrics, type PageSettings } from '../core/page';
5
+ import { type SheetGeometry } from '../core/pagination';
4
6
  interface Props {
5
7
  /** Grow with the content between the editor's min and max height instead of filling a fixed height. */
6
8
  autoHeight: boolean;
@@ -10,22 +12,22 @@ interface Props {
10
12
  documentTitle: string;
11
13
  /** Engine working on the editable element; `null` until the parent has created it. */
12
14
  engine: DocumentEngine | null;
13
- /** Text repeated in the bottom margin of every sheet. */
14
- footer?: PageHeaderFooter;
15
- /** Text repeated in the top margin of every sheet. */
16
- header?: PageHeaderFooter;
15
+ /** Footnotes of every sheet, drawn at its bottom; the web view shows all of them after the document. */
16
+ footnotes: SheetFootnote[][];
17
+ /** Page setup: running texts, watermark and page numbering of every sheet. */
18
+ page: PageSettings;
17
19
  /** Indents of the paragraph at the caret, shown by the ruler. */
18
20
  indents: ParagraphIndents;
19
21
  /** Page size and margins in pixels. */
20
22
  metrics: PageMetrics;
21
23
  /** Number of sheets drawn behind the content in page view. */
22
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[];
23
27
  /** Whether the ruler is drawn above the sheet; it is only ever shown in the page view. */
24
28
  rulerVisible: boolean;
25
29
  /** Paginated sheets or a single web sheet. */
26
30
  viewMode: DocumentViewMode;
27
- /** Watermark drawn behind the text of every sheet. */
28
- watermark?: PageWatermark;
29
31
  /** Zoom in percent. */
30
32
  zoom: number;
31
33
  }
@@ -0,0 +1,18 @@
1
+ import type { TrackedChange } from '../core/engine/engine';
2
+ interface Props {
3
+ /** Tracked changes in document order. */
4
+ changes: readonly TrackedChange[];
5
+ /** Read-only document: changes can be reviewed but not accepted or rejected. */
6
+ readonly: boolean | undefined;
7
+ }
8
+ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ select: (id: string) => any;
10
+ close: () => any;
11
+ resolve: (accept: boolean, id?: string | undefined) => any;
12
+ }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
13
+ onSelect?: ((id: string) => any) | undefined;
14
+ onClose?: (() => any) | undefined;
15
+ onResolve?: ((accept: boolean, id?: string | undefined) => any) | undefined;
16
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
+ declare const _default: typeof __VLS_export;
18
+ export default _default;
@@ -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;
@@ -0,0 +1,32 @@
1
+ import type { DocumentComment } from '../core/comments';
2
+ interface Props {
3
+ /** Comments in document order. */
4
+ comments: ReadonlyArray<DocumentComment>;
5
+ /** Ids of the comments whose text is still in the document. */
6
+ anchored: ReadonlySet<string>;
7
+ /** Comment at the caret, highlighted in the list. */
8
+ activeId: string | null;
9
+ /** Whether the form of a new comment is shown. */
10
+ drafting: boolean;
11
+ /** Read-only document: comments can be read but not changed. */
12
+ readonly: boolean | undefined;
13
+ }
14
+ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
15
+ select: (id: string) => any;
16
+ reply: (id: string, text: string) => any;
17
+ close: () => any;
18
+ remove: (id: string) => any;
19
+ resolve: (id: string, resolved: boolean) => any;
20
+ add: (text: string) => any;
21
+ cancelDraft: () => any;
22
+ }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
23
+ onSelect?: ((id: string) => any) | undefined;
24
+ onReply?: ((id: string, text: string) => any) | undefined;
25
+ onClose?: (() => any) | undefined;
26
+ onRemove?: ((id: string) => any) | undefined;
27
+ onResolve?: ((id: string, resolved: boolean) => any) | undefined;
28
+ onAdd?: ((text: string) => any) | undefined;
29
+ onCancelDraft?: (() => any) | undefined;
30
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ declare const _default: typeof __VLS_export;
32
+ export default _default;
@@ -0,0 +1,19 @@
1
+ interface Props {
2
+ /** The footnote reference being edited. */
3
+ reference: HTMLElement;
4
+ /** Element the form is positioned in; it must be positioned itself. */
5
+ container: HTMLElement;
6
+ /** Read-only document: the note is shown but cannot be changed. */
7
+ readonly: boolean | undefined;
8
+ }
9
+ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ cancel: () => any;
11
+ remove: () => any;
12
+ save: (text: string) => any;
13
+ }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
14
+ onCancel?: (() => any) | undefined;
15
+ onRemove?: (() => any) | undefined;
16
+ onSave?: ((text: string) => any) | undefined;
17
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import type { OutlineHeading, OutlineView } from '../core/outline';
2
+ import type { PageMetrics } from '../core/page';
3
+ import type { SheetGeometry } from '../core/pagination';
4
+ interface Props {
5
+ /** Headings of the document with the page each starts on, `null` outside the page view. */
6
+ headings: ReadonlyArray<OutlineHeading & {
7
+ page: number | null;
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;
17
+ }
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, {
25
+ select: (heading: HTMLElement) => any;
26
+ close: () => any;
27
+ selectPage: (index: number) => any;
28
+ "update:view": (value: OutlineView) => any;
29
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
30
+ onSelect?: ((heading: HTMLElement) => any) | undefined;
31
+ onClose?: (() => any) | undefined;
32
+ onSelectPage?: ((index: number) => any) | undefined;
33
+ "onUpdate:view"?: ((value: OutlineView) => any) | undefined;
34
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
+ declare const _default: typeof __VLS_export;
36
+ export default _default;