nuvra 0.1.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/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/components/document-editor.vue.d.ts +82 -0
- package/dist/components/editor-bubble-menus.vue.d.ts +14 -0
- package/dist/components/editor-canvas.vue.d.ts +30 -0
- package/dist/components/editor-color-picker.vue.d.ts +15 -0
- package/dist/components/editor-find-bar.vue.d.ts +24 -0
- package/dist/components/editor-object-overlay.vue.d.ts +10 -0
- package/dist/components/editor-page-setup.vue.d.ts +12 -0
- package/dist/components/editor-status-bar.vue.d.ts +36 -0
- package/dist/components/editor-table-picker.vue.d.ts +16 -0
- package/dist/components/editor-toolbar.vue.d.ts +38 -0
- package/dist/core/engine/blocks.d.ts +37 -0
- package/dist/core/engine/clipboard.d.ts +23 -0
- package/dist/core/engine/dom.d.ts +64 -0
- package/dist/core/engine/editing.d.ts +44 -0
- package/dist/core/engine/engine.d.ts +319 -0
- package/dist/core/engine/history.d.ts +43 -0
- package/dist/core/engine/input-rules.d.ts +17 -0
- package/dist/core/engine/keymap.d.ts +7 -0
- package/dist/core/engine/lists.d.ts +29 -0
- package/dist/core/engine/marks.d.ts +50 -0
- package/dist/core/engine/schema.d.ts +37 -0
- package/dist/core/engine/search.d.ts +65 -0
- package/dist/core/engine/selection.d.ts +30 -0
- package/dist/core/engine/tables.d.ts +59 -0
- package/dist/core/export.d.ts +21 -0
- package/dist/core/labels.d.ts +150 -0
- package/dist/core/page.d.ts +109 -0
- package/dist/core/pagination.d.ts +23 -0
- package/dist/core/types.d.ts +3 -0
- package/dist/core/ui-state.d.ts +79 -0
- package/dist/editor.vue.d.ts +50 -0
- package/dist/export-cyOnlakM.js +59 -0
- package/dist/export-cyOnlakM.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +4900 -0
- package/dist/index.js.map +1 -0
- package/dist/page-CEEleKNI.js +93 -0
- package/dist/page-CEEleKNI.js.map +1 -0
- package/dist/style.css +2 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eshonqul
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# nuvra
|
|
2
|
+
|
|
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`).
|
|
5
|
+
|
|
6
|
+
- No editor framework underneath: its own small editing engine with a sanitizing schema.
|
|
7
|
+
- Pagination in the page view (A4, A5, Letter, …, portrait or landscape).
|
|
8
|
+
- Tables with merge / split, images with resize and alignment, task lists, links, colors, fonts.
|
|
9
|
+
- Undo / redo, keyboard shortcuts that work with non-Latin keyboard layouts, Markdown-like input rules.
|
|
10
|
+
- Built on [Element Plus](https://element-plus.org) controls and [Lucide](https://lucide.dev) icons.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pnpm add nuvra element-plus
|
|
16
|
+
# or
|
|
17
|
+
npm install nuvra element-plus
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`vue` (3.5+) and `element-plus` (2.9+) are peer dependencies.
|
|
21
|
+
|
|
22
|
+
Import the styles once, next to the Element Plus styles:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import 'element-plus/dist/index.css';
|
|
26
|
+
import 'nuvra/style.css';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Document editor
|
|
32
|
+
|
|
33
|
+
```vue
|
|
34
|
+
<script setup lang="ts">
|
|
35
|
+
import { ref } from 'vue';
|
|
36
|
+
import { DocumentEditor, type PageSettings, createPageSettings } from 'nuvra';
|
|
37
|
+
|
|
38
|
+
const html = ref('');
|
|
39
|
+
const page = ref<PageSettings>(createPageSettings());
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<DocumentEditor v-model="html" v-model:page="page" height="100%" title="Contract" />
|
|
44
|
+
</template>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Form field
|
|
48
|
+
|
|
49
|
+
`Editor` is the same editor in the web view, growing with its content between `minHeight` and `maxHeight`:
|
|
50
|
+
|
|
51
|
+
```vue
|
|
52
|
+
<script setup lang="ts">
|
|
53
|
+
import { ref } from 'vue';
|
|
54
|
+
import { Editor } from 'nuvra';
|
|
55
|
+
|
|
56
|
+
const description = ref('');
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<template>
|
|
60
|
+
<Editor v-model="description" placeholder="Description" :max-length="2000" />
|
|
61
|
+
</template>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Image upload
|
|
65
|
+
|
|
66
|
+
Without an upload handler images are embedded as data URLs. Pass `uploadImage` to store them on your server:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import type { DocumentImageUploadHandler } from 'nuvra';
|
|
70
|
+
|
|
71
|
+
const uploadImage: DocumentImageUploadHandler = async file => {
|
|
72
|
+
const body = new FormData();
|
|
73
|
+
body.append('file', file);
|
|
74
|
+
const response = await fetch('/api/files', { method: 'POST', body });
|
|
75
|
+
return (await response.json()).url;
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API
|
|
80
|
+
|
|
81
|
+
### `DocumentEditor` props
|
|
82
|
+
|
|
83
|
+
| Prop | Type | Default | Description |
|
|
84
|
+
| ----------------- | --------------------------------------- | -------- | ------------------------------------------------------------------------ |
|
|
85
|
+
| `v-model` | `string` | `''` | Document HTML; an empty document is an empty string. |
|
|
86
|
+
| `v-model:page` | `PageSettings` | A4 | Paper size, orientation and margins. |
|
|
87
|
+
| `autofocus` | `boolean` | `false` | Places the caret at the end of the document once ready. |
|
|
88
|
+
| `canvasPadding` | `number \| string` | `50` | Gray space around the page or web sheet. |
|
|
89
|
+
| `defaultViewMode` | `'page' \| 'web'` | `'page'` | View shown first. |
|
|
90
|
+
| `disabled` | `boolean` | `false` | Read-only document, disabled controls. |
|
|
91
|
+
| `height` | `number \| string` | `760` | Height of the editor, or `'auto'` to grow between `minHeight`/`maxHeight`. |
|
|
92
|
+
| `minHeight` | `number \| string` | `240` | Smallest height of an auto-height editor. |
|
|
93
|
+
| `maxHeight` | `number \| string` | `600` | Largest height of an auto-height editor. |
|
|
94
|
+
| `maxImageSizeMb` | `number` | `10` | Largest accepted image file. |
|
|
95
|
+
| `maxLength` | `number` | `0` | Character limit; `0` means unlimited. |
|
|
96
|
+
| `placeholder` | `string` | `''` | Text shown while the document is empty. |
|
|
97
|
+
| `title` | `string` | `''` | Print title and exported file name. |
|
|
98
|
+
| `uploadImage` | `(file: File) => Promise<string>` | — | Uploads an image and resolves with its URL. |
|
|
99
|
+
|
|
100
|
+
`Editor` accepts the same props except `v-model:page`, `defaultViewMode`, `height` and `title`.
|
|
101
|
+
|
|
102
|
+
Numbers are pixels; strings are used as CSS lengths (`'100%'`, `'50vh'`).
|
|
103
|
+
|
|
104
|
+
### Events
|
|
105
|
+
|
|
106
|
+
| Event | Payload | Description |
|
|
107
|
+
| ------------- | ---------------- | --------------------------------------------- |
|
|
108
|
+
| `focus` | — | The document received focus. |
|
|
109
|
+
| `blur` | — | The document lost focus; the model is up to date. |
|
|
110
|
+
| `uploadError` | `error: unknown` | An image was rejected or its upload failed. |
|
|
111
|
+
|
|
112
|
+
### Exposed methods (`DocumentEditor` ref)
|
|
113
|
+
|
|
114
|
+
| Method | Description |
|
|
115
|
+
| -------------- | -------------------------------------------------------------- |
|
|
116
|
+
| `focus()` | Moves keyboard focus into the document. |
|
|
117
|
+
| `getHTML()` | Returns the document HTML, including edits not yet in the model. |
|
|
118
|
+
| `print()` | Opens the browser print dialog. |
|
|
119
|
+
| `exportHtml()` | Downloads the document as an HTML page. |
|
|
120
|
+
| `exportWord()` | Downloads the document as a Word-compatible `.doc` file. |
|
|
121
|
+
| `engine` | The editing engine, for advanced integrations. |
|
|
122
|
+
|
|
123
|
+
### Keyboard shortcuts
|
|
124
|
+
|
|
125
|
+
`Ctrl/⌘+B`, `I`, `U` — bold, italic, underline · `Ctrl/⌘+Z`, `Ctrl/⌘+Shift+Z` — undo, redo ·
|
|
126
|
+
`Ctrl/⌘+F` — find · `Ctrl/⌘+H` — replace · `Ctrl/⌘+K` — link · `Ctrl/⌘+P` — print.
|
|
127
|
+
|
|
128
|
+
## Translations
|
|
129
|
+
|
|
130
|
+
Built-in labels are in Uzbek. Every label has a key such as `editor.bold`; the full list with default texts is
|
|
131
|
+
exported as `editorMessages`. Register a translator once, before the app mounts. Returning `undefined` keeps the
|
|
132
|
+
built-in text for that key.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { setEditorTranslator } from 'nuvra';
|
|
136
|
+
|
|
137
|
+
const en: Record<string, string> = {
|
|
138
|
+
'editor.bold': 'Bold',
|
|
139
|
+
'editor.status.words': '{count} words'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
setEditorTranslator((key, named) =>
|
|
143
|
+
en[key]?.replace(/\{(\w+)\}/g, (_, name) => String(named?.[name] ?? ''))
|
|
144
|
+
);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
With `vue-i18n`, labels follow the active locale:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { setEditorTranslator } from 'nuvra';
|
|
151
|
+
import { i18n } from './i18n';
|
|
152
|
+
|
|
153
|
+
setEditorTranslator((key, named) => (i18n.global.te(key) ? i18n.global.t(key, named ?? {}) : undefined));
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Theming
|
|
157
|
+
|
|
158
|
+
The editor uses Element Plus CSS variables (`--el-color-primary`, `--el-border-color`, `--el-bg-color`, …), so it
|
|
159
|
+
follows your Element Plus theme and dark mode.
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { DocumentEngine } from '../core/engine/engine';
|
|
2
|
+
import { type DocumentViewMode, type PageSettings } from '../core/page';
|
|
3
|
+
import type { DocumentImageUploadHandler } from '../core/types';
|
|
4
|
+
import '../styles/document-content.css';
|
|
5
|
+
import '../styles/editor-ui.css';
|
|
6
|
+
/** A CSS length: numbers are pixels, strings are used as written (for example, `'100%'` or `'auto'`). */
|
|
7
|
+
type CssSize = number | string;
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Places the caret at the end of the document as soon as the editor is ready. */
|
|
10
|
+
autofocus?: boolean;
|
|
11
|
+
/** Gray space around the page or web sheet that separates the document from the editor frame. */
|
|
12
|
+
canvasPadding?: CssSize;
|
|
13
|
+
/** View shown first; form fields use the lighter web view. */
|
|
14
|
+
defaultViewMode?: DocumentViewMode;
|
|
15
|
+
/** Makes the document read-only and disables every editing control. */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** Height of the whole editor, or `'auto'` to grow with the content between `minHeight` and `maxHeight`. */
|
|
18
|
+
height?: CssSize;
|
|
19
|
+
/** Largest height of an auto-height editor; longer documents scroll inside it. */
|
|
20
|
+
maxHeight?: CssSize;
|
|
21
|
+
/** Largest accepted image file, in megabytes. */
|
|
22
|
+
maxImageSizeMb?: number;
|
|
23
|
+
/** Largest number of characters the document may contain; `0` means unlimited. */
|
|
24
|
+
maxLength?: number;
|
|
25
|
+
/** Smallest height of an auto-height editor. */
|
|
26
|
+
minHeight?: CssSize;
|
|
27
|
+
/** Text shown while the document is empty; falls back to the translated default placeholder. */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** Used as the print title and the exported file name. */
|
|
30
|
+
title?: string;
|
|
31
|
+
/** Uploads an inserted image and resolves with its URL; without it images are embedded as data URLs. */
|
|
32
|
+
uploadImage?: DocumentImageUploadHandler;
|
|
33
|
+
}
|
|
34
|
+
type __VLS_Props = Props;
|
|
35
|
+
type __VLS_ModelProps = {
|
|
36
|
+
/** Document HTML. An empty document is written as an empty string; typing updates it after a short delay. */
|
|
37
|
+
modelValue?: string;
|
|
38
|
+
/** Paper size, orientation, and margins used by the page view, printing, and export. */
|
|
39
|
+
'page'?: PageSettings;
|
|
40
|
+
};
|
|
41
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
42
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
43
|
+
/** The editing engine, for advanced integrations. */
|
|
44
|
+
engine: import("vue").ShallowRef<DocumentEngine | null, DocumentEngine | null>;
|
|
45
|
+
/** Downloads the document as an HTML page. */
|
|
46
|
+
exportHtml: () => Promise<void>;
|
|
47
|
+
/** Downloads the document as a Word-compatible file. */
|
|
48
|
+
exportWord: () => Promise<void>;
|
|
49
|
+
/** Moves keyboard focus into the document. */
|
|
50
|
+
focus: () => void | undefined;
|
|
51
|
+
/** Returns the document HTML, including edits not yet written to the model. */
|
|
52
|
+
getHTML: () => string;
|
|
53
|
+
/** Opens the print dialog. */
|
|
54
|
+
print: () => Promise<void>;
|
|
55
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
56
|
+
focus: () => any;
|
|
57
|
+
blur: () => any;
|
|
58
|
+
"update:modelValue": (value: string) => any;
|
|
59
|
+
"update:page": (value: PageSettings) => any;
|
|
60
|
+
uploadError: (error: unknown) => any;
|
|
61
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
62
|
+
onFocus?: (() => any) | undefined;
|
|
63
|
+
onBlur?: (() => any) | undefined;
|
|
64
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
65
|
+
"onUpdate:page"?: ((value: PageSettings) => any) | undefined;
|
|
66
|
+
onUploadError?: ((error: unknown) => any) | undefined;
|
|
67
|
+
}>, {
|
|
68
|
+
title: string;
|
|
69
|
+
height: CssSize;
|
|
70
|
+
disabled: boolean;
|
|
71
|
+
placeholder: string;
|
|
72
|
+
autofocus: boolean;
|
|
73
|
+
minHeight: CssSize;
|
|
74
|
+
maxLength: number;
|
|
75
|
+
maxHeight: CssSize;
|
|
76
|
+
canvasPadding: CssSize;
|
|
77
|
+
defaultViewMode: DocumentViewMode;
|
|
78
|
+
maxImageSizeMb: number;
|
|
79
|
+
uploadImage: DocumentImageUploadHandler;
|
|
80
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
81
|
+
declare const _default: typeof __VLS_export;
|
|
82
|
+
export default _default;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DocumentEngine } from '../core/engine/engine';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Engine whose selection decides which menu is shown. */
|
|
4
|
+
engine: DocumentEngine;
|
|
5
|
+
/** Canvas scroll container; menus follow its scrolling and hide when their target leaves it. */
|
|
6
|
+
scrollTarget: HTMLElement;
|
|
7
|
+
}
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
editLink: () => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
11
|
+
onEditLink?: (() => any) | undefined;
|
|
12
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DocumentEngine } from '../core/engine/engine';
|
|
2
|
+
import type { DocumentViewMode, PageMetrics } from '../core/page';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Grow with the content between the editor's min and max height instead of filling a fixed height. */
|
|
5
|
+
autoHeight: boolean;
|
|
6
|
+
/** Engine working on the editable element; `null` until the parent has created it. */
|
|
7
|
+
engine: DocumentEngine | null;
|
|
8
|
+
/** Page size and margins in pixels. */
|
|
9
|
+
metrics: PageMetrics;
|
|
10
|
+
/** Number of sheets drawn behind the content in page view. */
|
|
11
|
+
pageCount: number;
|
|
12
|
+
/** Paginated sheets or a single web sheet. */
|
|
13
|
+
viewMode: DocumentViewMode;
|
|
14
|
+
/** Zoom in percent. */
|
|
15
|
+
zoom: number;
|
|
16
|
+
}
|
|
17
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {
|
|
18
|
+
/** The editable element the engine is created on. */
|
|
19
|
+
contentElement: import("vue").Ref<HTMLElement | undefined, HTMLElement | undefined>;
|
|
20
|
+
/** Zoom percentage that makes the page fill the canvas width. */
|
|
21
|
+
getFitWidthZoom: () => number;
|
|
22
|
+
/** The scroll container, followed by floating menus. */
|
|
23
|
+
scrollElement: import("vue").Ref<HTMLElement | undefined, HTMLElement | undefined>;
|
|
24
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
25
|
+
resize: (contentWidth: number) => any;
|
|
26
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
27
|
+
onResize?: ((contentWidth: number) => any) | undefined;
|
|
28
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
29
|
+
declare const _default: typeof __VLS_export;
|
|
30
|
+
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Colour applied under the caret, used to mark the selected swatch. */
|
|
3
|
+
current: string;
|
|
4
|
+
/** Disables both parts of the button. */
|
|
5
|
+
disabled: boolean;
|
|
6
|
+
/** Whether the picker sets the text colour or the highlight colour. */
|
|
7
|
+
mode: 'text' | 'highlight';
|
|
8
|
+
}
|
|
9
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
|
+
select: (color: string | null) => any;
|
|
11
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
12
|
+
onSelect?: ((color: string | null) => any) | undefined;
|
|
13
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DocumentEngine } from '../core/engine/engine';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Engine whose document is searched. */
|
|
4
|
+
engine: DocumentEngine;
|
|
5
|
+
/** Hides the replacement row when the document cannot be edited. */
|
|
6
|
+
readonly: boolean | undefined;
|
|
7
|
+
}
|
|
8
|
+
type __VLS_Props = Props;
|
|
9
|
+
type __VLS_ModelProps = {
|
|
10
|
+
/** Whether the replacement row is expanded; kept by the parent so Ctrl+H can open it directly. */
|
|
11
|
+
'replace'?: boolean;
|
|
12
|
+
};
|
|
13
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
14
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
15
|
+
focus: () => void;
|
|
16
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
17
|
+
close: () => any;
|
|
18
|
+
"update:replace": (value: boolean) => any;
|
|
19
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
20
|
+
onClose?: (() => any) | undefined;
|
|
21
|
+
"onUpdate:replace"?: ((value: boolean) => any) | undefined;
|
|
22
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const _default: typeof __VLS_export;
|
|
24
|
+
export default _default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DocumentEngine } from '../core/engine/engine';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Engine whose selected image and tables are edited. */
|
|
4
|
+
engine: DocumentEngine;
|
|
5
|
+
/** Zoom in percent, needed to convert pointer movement into document pixels. */
|
|
6
|
+
zoom: number;
|
|
7
|
+
}
|
|
8
|
+
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>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type PageSettings } from '../core/page';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Settings currently applied to the document. */
|
|
4
|
+
page: PageSettings;
|
|
5
|
+
}
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
7
|
+
change: (page: PageSettings) => any;
|
|
8
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
9
|
+
onChange?: ((page: PageSettings) => any) | undefined;
|
|
10
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
+
declare const _default: typeof __VLS_export;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type DocumentViewMode } from '../core/page';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Number of characters in the document. */
|
|
4
|
+
characters: number;
|
|
5
|
+
/** Page that contains the caret (page view only). */
|
|
6
|
+
currentPage: number;
|
|
7
|
+
/** Whether the editor currently covers the screen. */
|
|
8
|
+
fullscreen: boolean;
|
|
9
|
+
/** Character limit; 0 means unlimited and shows a plain counter. */
|
|
10
|
+
maxLength: number | undefined;
|
|
11
|
+
/** Number of pages the document occupies (page view only). */
|
|
12
|
+
pageCount: number;
|
|
13
|
+
/** Number of words in the document. */
|
|
14
|
+
words: number;
|
|
15
|
+
}
|
|
16
|
+
type __VLS_Props = Props;
|
|
17
|
+
type __VLS_ModelProps = {
|
|
18
|
+
/** Zoom in percent; every change is snapped to the zoom step and clamped to the allowed range. */
|
|
19
|
+
'zoom': number;
|
|
20
|
+
/** Page layout with sheets, or the continuous web layout. */
|
|
21
|
+
'viewMode': DocumentViewMode;
|
|
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
|
+
fitWidth: () => any;
|
|
26
|
+
toggleFullscreen: () => any;
|
|
27
|
+
"update:zoom": (value: number) => any;
|
|
28
|
+
"update:viewMode": (value: DocumentViewMode) => any;
|
|
29
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
30
|
+
onFitWidth?: (() => any) | undefined;
|
|
31
|
+
onToggleFullscreen?: (() => any) | undefined;
|
|
32
|
+
"onUpdate:zoom"?: ((value: number) => any) | undefined;
|
|
33
|
+
"onUpdate:viewMode"?: ((value: DocumentViewMode) => any) | undefined;
|
|
34
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
35
|
+
declare const _default: typeof __VLS_export;
|
|
36
|
+
export default _default;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Table requested by the user. */
|
|
2
|
+
interface TableSelection {
|
|
3
|
+
/** Number of rows, header row included. */
|
|
4
|
+
rows: number;
|
|
5
|
+
/** Number of columns. */
|
|
6
|
+
cols: number;
|
|
7
|
+
/** Whether the first row is made of header cells. */
|
|
8
|
+
withHeaderRow: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
|
+
select: (table: TableSelection) => any;
|
|
12
|
+
}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
|
|
13
|
+
onSelect?: ((table: TableSelection) => any) | undefined;
|
|
14
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DocumentEngine } from '../core/engine/engine';
|
|
2
|
+
import type { PageSettings } from '../core/page';
|
|
3
|
+
import type { DocumentMenuAction } from '../core/types';
|
|
4
|
+
import { type EditorUiState } from '../core/ui-state';
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Read-only document: every editing control is disabled. */
|
|
7
|
+
disabled: boolean | undefined;
|
|
8
|
+
/** Engine that receives the commands. */
|
|
9
|
+
engine: DocumentEngine;
|
|
10
|
+
/** Whether the find bar is open, shown as the search button state. */
|
|
11
|
+
findOpen: boolean;
|
|
12
|
+
/** Whether the editor is in fullscreen, used for the menu item label. */
|
|
13
|
+
fullscreen: boolean;
|
|
14
|
+
/** Current page settings, edited in the page setup popover. */
|
|
15
|
+
page: PageSettings;
|
|
16
|
+
/** Whether the HTML source is shown; formatting controls are disabled meanwhile. */
|
|
17
|
+
sourceMode: boolean;
|
|
18
|
+
/** Formatting at the caret, used for active states and labels. */
|
|
19
|
+
state: EditorUiState;
|
|
20
|
+
/** Whether images are being uploaded, shown on the upload button. */
|
|
21
|
+
uploading: boolean;
|
|
22
|
+
}
|
|
23
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {
|
|
24
|
+
/** Opens the link popover. */
|
|
25
|
+
openLink: () => void;
|
|
26
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
27
|
+
menu: (action: DocumentMenuAction) => any;
|
|
28
|
+
find: () => any;
|
|
29
|
+
insertImages: (files: File[]) => any;
|
|
30
|
+
"update:page": (page: PageSettings) => any;
|
|
31
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
32
|
+
onMenu?: ((action: DocumentMenuAction) => any) | undefined;
|
|
33
|
+
onFind?: (() => any) | undefined;
|
|
34
|
+
onInsertImages?: ((files: File[]) => any) | undefined;
|
|
35
|
+
"onUpdate:page"?: ((page: PageSettings) => any) | undefined;
|
|
36
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
37
|
+
declare const _default: typeof __VLS_export;
|
|
38
|
+
export default _default;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Heading element tags. */
|
|
2
|
+
export type HeadingTag = 'H1' | 'H2' | 'H3' | 'H4' | 'H5' | 'H6';
|
|
3
|
+
/** Paragraph alignment; `left` is the default and is stored as no style. */
|
|
4
|
+
export type TextAlign = 'left' | 'center' | 'right' | 'justify';
|
|
5
|
+
/** Paragraph direction; `auto` is stored as no `dir` attribute. */
|
|
6
|
+
export type TextDirection = 'auto' | 'ltr' | 'rtl';
|
|
7
|
+
/** The element whose children are blocks: the document, a quote, a list item body or a table cell. */
|
|
8
|
+
export declare const containerOf: (node: Node, root: HTMLElement) => HTMLElement;
|
|
9
|
+
/** The direct child of `container` that contains `node` (or `node` itself when it is a direct child). */
|
|
10
|
+
export declare const childOf: (container: Node, node: Node) => Node;
|
|
11
|
+
/** Turns every selected text block into a paragraph or heading, keeping its alignment and indentation. */
|
|
12
|
+
export declare const setBlockType: (root: HTMLElement, range: Range, tag: "P" | HeadingTag) => void;
|
|
13
|
+
/** Converts the selected blocks to code blocks, or back to paragraphs when all of them already are. */
|
|
14
|
+
export declare const toggleCodeBlock: (root: HTMLElement, range: Range) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Unwraps the quotes around the selection when every selected block is quoted; otherwise wraps the selected
|
|
17
|
+
* blocks, at the level of their closest common container, in a new quote.
|
|
18
|
+
*/
|
|
19
|
+
export declare const toggleBlockquote: (root: HTMLElement, range: Range) => void;
|
|
20
|
+
/** Aligns the selected paragraphs and headings; code blocks keep their alignment. */
|
|
21
|
+
export declare const setTextAlign: (root: HTMLElement, range: Range, align: TextAlign) => void;
|
|
22
|
+
/** Sets the line height of the selected paragraphs and headings, or resets it with `null`. */
|
|
23
|
+
export declare const setLineHeight: (root: HTMLElement, range: Range, lineHeight: string | null) => void;
|
|
24
|
+
/** Sets the writing direction of the selected blocks; `auto` lets each paragraph follow its own text. */
|
|
25
|
+
export declare const setTextDirection: (root: HTMLElement, range: Range, direction: TextDirection) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Moves the selected paragraphs outside lists by whole indentation steps.
|
|
28
|
+
* @returns whether any block's indentation changed.
|
|
29
|
+
*/
|
|
30
|
+
export declare const changeIndent: (root: HTMLElement, range: Range, delta: number) => boolean;
|
|
31
|
+
/** Splits a text block at a point; the new right-hand block keeps the tag and paragraph formatting. */
|
|
32
|
+
export declare const splitBlock: (block: HTMLElement, container: Node, offset: number) => HTMLElement;
|
|
33
|
+
/**
|
|
34
|
+
* Places a block element (rule, page break, image, table) at the caret and returns the text block that should
|
|
35
|
+
* receive the caret afterwards.
|
|
36
|
+
*/
|
|
37
|
+
export declare const insertBlockAtCaret: (root: HTMLElement, range: Range, element: HTMLElement) => HTMLElement;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The parts of a clipboard or drag payload the editor uses. */
|
|
2
|
+
export interface TransferContent {
|
|
3
|
+
/** Rich HTML, empty when the source offered none. */
|
|
4
|
+
html: string;
|
|
5
|
+
/** Plain text, empty when the source offered none. */
|
|
6
|
+
text: string;
|
|
7
|
+
/** Files, e.g. pasted screenshots or dropped images. */
|
|
8
|
+
files: File[];
|
|
9
|
+
}
|
|
10
|
+
/** Extracts HTML, text and files from a clipboard or drag payload. */
|
|
11
|
+
export declare const readTransfer: (data: DataTransfer | null) => TransferContent;
|
|
12
|
+
/** The image files of a payload. */
|
|
13
|
+
export declare const imageFiles: (content: TransferContent) => File[];
|
|
14
|
+
/** The URL when the plain text is a single web address (`www.` addresses get `https://`), otherwise `null`. */
|
|
15
|
+
export declare const singleUrl: (content: TransferContent) => string | null;
|
|
16
|
+
/** Converts plain text into paragraphs, one per line; a final line break does not add an empty paragraph. */
|
|
17
|
+
export declare const textToFragment: (text: string) => DocumentFragment;
|
|
18
|
+
/** Converts a payload into editor blocks, preferring sanitised HTML over plain text; `null` when it has neither. */
|
|
19
|
+
export declare const transferToFragment: (content: TransferContent) => DocumentFragment | null;
|
|
20
|
+
/** Puts clean HTML and plain text of the selection on a clipboard or drag payload for copy, cut and drag. */
|
|
21
|
+
export declare const writeTransfer: (data: DataTransfer, root: HTMLElement, range: Range) => void;
|
|
22
|
+
/** Collapsed range at the text position under the pointer, used to place dropped content and canvas clicks. */
|
|
23
|
+
export declare const rangeFromPoint: (x: number, y: number) => Range | null;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-free DOM helpers shared by every part of the editing engine: node type guards, lookups that never leave
|
|
3
|
+
* the editor root, element factories and the small structural fixes (caret placeholders, mark merging) that keep the
|
|
4
|
+
* editable markup predictable.
|
|
5
|
+
*/
|
|
6
|
+
/** Selector for blocks without editable text inside: rules, images and page breaks. */
|
|
7
|
+
export declare const ATOM_SELECTOR = "hr, img, div[data-type=\"page-break\"]";
|
|
8
|
+
/** Selector for blocks that hold editable inline text. */
|
|
9
|
+
export declare const TEXT_BLOCK_SELECTOR = "p, h1, h2, h3, h4, h5, h6, pre";
|
|
10
|
+
/** Marks the extra `<br>` that keeps an empty or break-terminated line visible; it is never serialised. */
|
|
11
|
+
export declare const TRAILING_BREAK = "data-doc-trailing";
|
|
12
|
+
/** Whether the node is an element. */
|
|
13
|
+
export declare const isElement: (node: Node | null | undefined) => node is HTMLElement;
|
|
14
|
+
/** Whether the node is a text node. */
|
|
15
|
+
export declare const isText: (node: Node | null | undefined) => node is Text;
|
|
16
|
+
/** Whether the node is a paragraph, heading or code block. */
|
|
17
|
+
export declare const isTextBlock: (node: Node | null | undefined) => node is HTMLElement;
|
|
18
|
+
/** Whether the node is an ordered or unordered list. */
|
|
19
|
+
export declare const isList: (node: Node | null | undefined) => node is HTMLElement;
|
|
20
|
+
/** Whether the node is a block without editable text (rule, image, page break). */
|
|
21
|
+
export declare const isAtom: (node: Node | null | undefined) => node is HTMLElement;
|
|
22
|
+
/** Whether the node is a `<br>`, placeholder or real. */
|
|
23
|
+
export declare const isBreak: (node: Node | null | undefined) => node is HTMLBRElement;
|
|
24
|
+
/** Whether the node is the editor's placeholder `<br>` rather than a line break typed by the user. */
|
|
25
|
+
export declare const isTrailingBreak: (node: Node | null | undefined) => boolean;
|
|
26
|
+
/** Nearest ancestor-or-self matching the predicate, never leaving `root` (which itself is never returned). */
|
|
27
|
+
export declare const closestWithin: (node: Node | null | undefined, root: HTMLElement, predicate: (element: HTMLElement) => boolean) => HTMLElement | null;
|
|
28
|
+
/** Nearest ancestor-or-self inside `root` whose tag is `tags` or one of `tags`. */
|
|
29
|
+
export declare const closestTag: (node: Node | null | undefined, root: HTMLElement, tags: string | ReadonlySet<string>) => HTMLElement | null;
|
|
30
|
+
/** The paragraph, heading or code block containing `node`. */
|
|
31
|
+
export declare const closestTextBlock: (node: Node | null | undefined, root: HTMLElement) => HTMLElement | null;
|
|
32
|
+
/** Creates an element with attributes and children in one call. */
|
|
33
|
+
export declare const createElement: <K extends keyof HTMLElementTagNameMap>(tag: K, attributes?: Record<string, string>, children?: Array<Node | string>) => HTMLElementTagNameMap[K];
|
|
34
|
+
/** Creates the placeholder `<br>` that gives empty lines their height. */
|
|
35
|
+
export declare const createTrailingBreak: () => HTMLBRElement;
|
|
36
|
+
/** Creates a paragraph with the given inline content and the placeholder break it needs. */
|
|
37
|
+
export declare const createParagraph: (children?: Node[]) => HTMLParagraphElement;
|
|
38
|
+
/** Whether the element contains text, an image or a real line break. */
|
|
39
|
+
export declare const hasVisibleContent: (element: Element) => boolean;
|
|
40
|
+
/** Whether a text block has no text and no image, holding at most one break. */
|
|
41
|
+
export declare const isEmptyTextBlock: (block: Element) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Keeps exactly one placeholder `<br>` where the browser needs it: in empty blocks and after a user line break at the
|
|
44
|
+
* end of a block. Leaves the DOM untouched when nothing has to change, so the caret is not disturbed while typing.
|
|
45
|
+
*/
|
|
46
|
+
export declare const syncTrailingBreak: (block: HTMLElement) => void;
|
|
47
|
+
/** Replaces an element with its own children. */
|
|
48
|
+
export declare const unwrap: (element: Element) => void;
|
|
49
|
+
/** Replaces the element with one of another tag, keeping attributes and children. */
|
|
50
|
+
export declare const renameElement: <T extends HTMLElement = HTMLElement>(element: HTMLElement, tag: string) => T;
|
|
51
|
+
/** Removes marks that no longer contain anything the user can see or place a caret into. */
|
|
52
|
+
export declare const removeEmptyMarks: (scope: HTMLElement) => void;
|
|
53
|
+
/** Joins neighbouring marks with identical tag and attributes, then merges adjacent text nodes. */
|
|
54
|
+
export declare const mergeAdjacentMarks: (scope: HTMLElement) => void;
|
|
55
|
+
/** All paragraphs, headings and code blocks inside `scope`, in document order. */
|
|
56
|
+
export declare const textBlocksWithin: (scope: ParentNode) => HTMLElement[];
|
|
57
|
+
/** Text blocks touched by the range, in document order. */
|
|
58
|
+
export declare const textBlocksInRange: (root: HTMLElement, range: Range) => HTMLElement[];
|
|
59
|
+
/** Whether nothing visible precedes the point inside its block. */
|
|
60
|
+
export declare const isAtBlockStart: (block: HTMLElement, container: Node, offset: number) => boolean;
|
|
61
|
+
/** Whether nothing visible follows the point inside its block. */
|
|
62
|
+
export declare const isAtBlockEnd: (block: HTMLElement, container: Node, offset: number) => boolean;
|
|
63
|
+
/** Escapes regular-expression syntax so user text can be searched literally. */
|
|
64
|
+
export declare const escapeRegExp: (value: string) => string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** A collapsed DOM position returned by edits so the engine can restore the caret. */
|
|
2
|
+
export interface Caret {
|
|
3
|
+
/** Container node of the position. */
|
|
4
|
+
node: Node;
|
|
5
|
+
/** Offset inside `node`: a character index for text, a child index for elements. */
|
|
6
|
+
offset: number;
|
|
7
|
+
}
|
|
8
|
+
/** Caret before the first character of a block (or inside it when it has no text). */
|
|
9
|
+
export declare const startCaret: (block: HTMLElement) => Caret;
|
|
10
|
+
/** Caret after the last character of a block, before its caret placeholder when it has no text. */
|
|
11
|
+
export declare const endCaret: (block: HTMLElement) => Caret;
|
|
12
|
+
/** Deletes the selected content. The caret should be restored to the range start by the caller. */
|
|
13
|
+
export declare const deleteRange: (root: HTMLElement, range: Range) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Backspace at the very start of a text block: lifts it out of a list or quote, deletes a preceding atom,
|
|
16
|
+
* moves into a preceding table, or merges it into the previous text block of the same cell.
|
|
17
|
+
* @returns the new caret, or `null` when nothing applies.
|
|
18
|
+
*/
|
|
19
|
+
export declare const joinBackward: (root: HTMLElement, block: HTMLElement) => Caret | null;
|
|
20
|
+
/**
|
|
21
|
+
* Delete at the very end of a text block: deletes a following atom or merges the next text block of the same
|
|
22
|
+
* cell into this one.
|
|
23
|
+
* @returns the new caret, or `null` when nothing applies.
|
|
24
|
+
*/
|
|
25
|
+
export declare const joinForward: (root: HTMLElement, block: HTMLElement) => Caret | null;
|
|
26
|
+
/**
|
|
27
|
+
* Enter at a collapsed range: a newline in code blocks, leaving an empty list item or quote, or splitting the
|
|
28
|
+
* block (and its list item). A new line after a heading becomes a paragraph.
|
|
29
|
+
*/
|
|
30
|
+
export declare const splitAtCaret: (root: HTMLElement, range: Range) => Caret | null;
|
|
31
|
+
/** Shift+Enter: a `<br>` line break, or a newline inside code blocks. */
|
|
32
|
+
export declare const insertLineBreak: (root: HTMLElement, range: Range) => Caret | null;
|
|
33
|
+
/** Inserts plain text at a collapsed range, creating a paragraph if the caret sits between blocks. */
|
|
34
|
+
export declare const insertTextAtCaret: (root: HTMLElement, range: Range, text: string) => Caret;
|
|
35
|
+
/**
|
|
36
|
+
* Pastes sanitised blocks at a collapsed range. A single paragraph flows into the current line; several blocks
|
|
37
|
+
* split the current block, with the first and last pasted paragraphs merging into its halves as in Word.
|
|
38
|
+
* @returns the caret after the inserted content.
|
|
39
|
+
*/
|
|
40
|
+
export declare const insertFragment: (root: HTMLElement, range: Range, fragment: DocumentFragment) => Caret | null;
|
|
41
|
+
/** Whether the range is collapsed before any visible content of the block. */
|
|
42
|
+
export declare const isCaretAtBlockStart: (block: HTMLElement, range: Range) => boolean;
|
|
43
|
+
/** Whether the range is collapsed after all visible content of the block. */
|
|
44
|
+
export declare const isCaretAtBlockEnd: (block: HTMLElement, range: Range) => boolean;
|