qword 1.0.2 → 1.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/ChangeLog +22 -0
- package/README.md +2 -3
- package/assets/style.css +10 -10
- package/client/_clipboard.js +30 -33
- package/client/_clipboard.spec.js +160 -0
- package/client/content.d.ts +8 -0
- package/client/content.js +14 -0
- package/client/content.spec.js +69 -0
- package/client/create.d.ts +32 -0
- package/client/create.js +95 -0
- package/client/create.spec.js +192 -0
- package/client/decorations.d.ts +40 -0
- package/client/decorations.js +110 -0
- package/client/decorations.spec.js +135 -0
- package/client/dom.d.ts +7 -0
- package/client/dom.js +17 -0
- package/client/dom.spec.js +78 -0
- package/client/editor.d.ts +72 -0
- package/client/editor.js +164 -0
- package/client/editor.spec.js +307 -0
- package/client/events.d.ts +7 -0
- package/client/events.js +8 -0
- package/client/events.spec.js +100 -0
- package/client/highlight.d.ts +3 -0
- package/client/highlight.js +82 -0
- package/client/highlight.spec.js +28 -0
- package/client/history.d.ts +4 -0
- package/client/history.js +16 -0
- package/client/history.spec.js +57 -0
- package/client/index.d.ts +60 -0
- package/client/index.js +37 -0
- package/client/loadremote.js +10 -4
- package/client/options.d.ts +30 -0
- package/client/options.js +59 -0
- package/client/options.spec.js +157 -0
- package/client/position.d.ts +21 -0
- package/client/position.js +33 -0
- package/client/position.spec.js +176 -0
- package/client/qword.js +141 -391
- package/client/save.js +2 -2
- package/client/scroll.d.ts +10 -0
- package/client/scroll.js +9 -0
- package/client/scroll.spec.js +58 -0
- package/client/types.d.ts +24 -0
- package/dist/qword.js +175 -1
- package/dist/qword.js.map +1 -1
- package/dist-dev/qword.js +544 -660
- package/package.json +30 -15
- package/server/edit.js +8 -3
- package/server/edit.spec.js +101 -0
- package/test/errors.ts +21 -0
- package/test/types.ts +134 -0
- package/client/codemirror/show-trailing.js +0 -54
- package/client/codemirror/use-soft-tabs.js +0 -38
- package/client/set-key-map.js +0 -3
- package/dist/2b22a33a0a80bb926bbc.png +0 -1
- package/dist/8e9019bd86255ac9a618.png +0 -1
- package/dist/codemirror.js +0 -2
- package/dist/codemirror.js.map +0 -1
- package/dist/dword.js +0 -2
- package/dist/dword.js.map +0 -1
- package/dist-dev/2b22a33a0a80bb926bbc.png +0 -1
- package/dist-dev/8e9019bd86255ac9a618.png +0 -1
- package/dist-dev/codemirror.js +0 -104
- package/dist-dev/dword.js +0 -960
- package/modules/codemirror-emmet/dist/emmet.js +0 -48661
- package/modules/codemirror-emmet/dist/emmet.min.js +0 -8
- package/modules/codemirror-emmet/dist/emmetPlugin.js +0 -840
- package/modules/codemirror-emmet/dist/emmetPlugin.min.js +0 -1
- package/modules/codemirror-emmet/dist/index.html +0 -46
- package/modules/jshint/README.md +0 -114
- package/modules/jshint/dist/jshint-rhino.js +0 -32143
- package/modules/jshint/dist/jshint.js +0 -32141
- package/modules/jshint/package.json +0 -77
package/ChangeLog
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
2026.09.05, v1.1.0
|
|
2
|
+
|
|
3
|
+
fix:
|
|
4
|
+
- 34a981c client: clipboard
|
|
5
|
+
|
|
6
|
+
feature:
|
|
7
|
+
- 83e40e6 qword: cssnano v9.0.2
|
|
8
|
+
- 2f29f72 types: add
|
|
9
|
+
- 58fce83 migrate to rspack
|
|
10
|
+
- 03bb806 qword: @lezer/highlight v1.2.3
|
|
11
|
+
- c5cf54a qword: superc8 v12.7.0
|
|
12
|
+
- 51d8bc5 editor: move out
|
|
13
|
+
- 3d791d7 qword: @putout/plugin-convert-webpack-to-rspack v1.0.1
|
|
14
|
+
|
|
15
|
+
2026.05.18, v1.0.3
|
|
16
|
+
|
|
17
|
+
fix:
|
|
18
|
+
- 73fb35d qword: clear history on setValueFirst to avoid undo to empty editor
|
|
19
|
+
|
|
20
|
+
feature:
|
|
21
|
+
- 9b3200c qword: cssnano v8.0.1
|
|
22
|
+
|
|
1
23
|
2026.05.13, v1.0.2
|
|
2
24
|
|
|
3
25
|
feature:
|
package/README.md
CHANGED
|
@@ -14,13 +14,12 @@ Fork of [edward](https://github.com/cloudcmd/edward "Edward").
|
|
|
14
14
|
|
|
15
15
|
<img width="601" height="295" alt="image" src="https://github.com/user-attachments/assets/e632f48a-ef74-4d5c-bbe8-8f6e44151d00" />
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
## Features
|
|
19
18
|
|
|
20
19
|
- Syntax highlighting based on extension of file for over 90 languages.
|
|
21
20
|
- Built-in `emmet` (for html files)
|
|
22
21
|
- Drag n drop (drag file from desktop to editor).
|
|
23
|
-
- Configurable options ([json/edit.json][edit.json] could be
|
|
22
|
+
- Configurable options ([json/edit.json][edit.json] could be overridden by `~/.qword.json`)
|
|
24
23
|
|
|
25
24
|
## Install
|
|
26
25
|
|
|
@@ -52,7 +51,7 @@ Usage: `qword [filename]`
|
|
|
52
51
|
## API
|
|
53
52
|
|
|
54
53
|
qword could be used as middleware for [express](http://expressjs.com "Express").
|
|
55
|
-
For this
|
|
54
|
+
For this purpose API could be used.
|
|
56
55
|
|
|
57
56
|
### Server
|
|
58
57
|
|
package/assets/style.css
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
@font-face {
|
|
2
2
|
font-family: 'Droid Sans Mono';
|
|
3
|
-
src: url(https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJTwtzT4qNq-faudv5qbO9-U.eot);
|
|
3
|
+
src: url('https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJTwtzT4qNq-faudv5qbO9-U.eot');
|
|
4
4
|
src:
|
|
5
5
|
local('Droid Sans Mono'),
|
|
6
6
|
local('DroidSansMono'),
|
|
7
|
-
url(/font/DroidSansMono.eot) format('embedded-opentype'),
|
|
8
|
-
url(https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJTwtzT4qNq-faudv5qbO9-U.eot?#iefix)
|
|
9
|
-
|
|
10
|
-
url(https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJTwtzT4qNq-faudv5qbO9-U.eot)
|
|
11
|
-
|
|
12
|
-
url(/font/DroidSansMono.woff2) format('woff2'),
|
|
13
|
-
url(/font/DroidSansMono.woff) format('woff'),
|
|
14
|
-
url(https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJUYuTAAIFFn5GTWtryCmBQ4.woff)
|
|
15
|
-
|
|
7
|
+
url('/font/DroidSansMono.eot') format('embedded-opentype'),
|
|
8
|
+
url('https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJTwtzT4qNq-faudv5qbO9-U.eot?#iefix')
|
|
9
|
+
format('embedded-opentype'),
|
|
10
|
+
url('https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJTwtzT4qNq-faudv5qbO9-U.eot')
|
|
11
|
+
format('embedded-opentype'),
|
|
12
|
+
url('/font/DroidSansMono.woff2') format('woff2'),
|
|
13
|
+
url('/font/DroidSansMono.woff') format('woff'),
|
|
14
|
+
url('https://themes.googleusercontent.com/static/fonts/droidsansmono/v5/ns-m2xQYezAtqh7ai59hJUYuTAAIFFn5GTWtryCmBQ4.woff')
|
|
15
|
+
format('woff'),
|
|
16
16
|
local('Consolas');
|
|
17
17
|
font-style: normal;
|
|
18
18
|
font-weight: 400;
|
package/client/_clipboard.js
CHANGED
|
@@ -1,53 +1,50 @@
|
|
|
1
1
|
import clipboard from '@cloudcmd/clipboard';
|
|
2
|
-
import createElement from '@cloudcmd/create-element';
|
|
3
2
|
import once from 'once';
|
|
4
|
-
import
|
|
3
|
+
import showMessage from './show-message.js';
|
|
5
4
|
|
|
6
|
-
const
|
|
7
|
-
const reject = Promise.reject.bind(Promise);
|
|
8
|
-
const showMessageOnce = once(_showMessageOnce);
|
|
5
|
+
const showMessageOnce = once(showMessage);
|
|
9
6
|
|
|
10
|
-
export default (
|
|
7
|
+
export default function clipboardCommand(command) {
|
|
8
|
+
const {_view, _story} = this;
|
|
11
9
|
const NAME = 'editor-clipboard';
|
|
12
|
-
const {_Ace, _story} = this;
|
|
13
10
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
.
|
|
17
|
-
|
|
11
|
+
const getSelected = () => {
|
|
12
|
+
const {from, to} = _view.state.selection.main;
|
|
13
|
+
return _view.state.sliceDoc(from, to);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const insert = (text) => {
|
|
17
|
+
const {from, to} = _view.state.selection.main;
|
|
18
|
+
|
|
19
|
+
_view.dispatch({
|
|
20
|
+
changes: {
|
|
21
|
+
from,
|
|
22
|
+
to,
|
|
23
|
+
insert: text,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
};
|
|
18
27
|
|
|
19
|
-
if (
|
|
28
|
+
if (command === 'copy') {
|
|
29
|
+
const value = getSelected();
|
|
20
30
|
_story.setData(NAME, value);
|
|
31
|
+
|
|
21
32
|
return clipboard.writeText(value);
|
|
22
33
|
}
|
|
23
34
|
|
|
24
|
-
if (
|
|
35
|
+
if (command === 'cut') {
|
|
36
|
+
const value = getSelected();
|
|
25
37
|
_story.setData(NAME, value);
|
|
26
|
-
|
|
38
|
+
insert('');
|
|
39
|
+
|
|
40
|
+
return clipboard.writeText(value);
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
return clipboard
|
|
30
44
|
.readText()
|
|
31
45
|
.then(insert)
|
|
32
46
|
.catch(() => {
|
|
33
|
-
showMessageOnce('Could not paste from clipboard. Inner buffer used.');
|
|
34
|
-
|
|
35
|
-
const value = _story.getData(NAME);
|
|
36
|
-
|
|
37
|
-
insert(value);
|
|
47
|
+
showMessageOnce.call(this, 'Could not paste from clipboard. Inner buffer used.');
|
|
48
|
+
insert(_story.getData(NAME));
|
|
38
49
|
});
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
function cut(value) {
|
|
42
|
-
const textarea = createElement('textarea', {
|
|
43
|
-
value,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
textarea.select();
|
|
47
|
-
|
|
48
|
-
const result = document.execCommand('cut');
|
|
49
|
-
|
|
50
|
-
document.body.removeChild(textarea);
|
|
51
|
-
|
|
52
|
-
return result;
|
|
53
50
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {test, stub} from 'supertape';
|
|
2
|
+
|
|
3
|
+
test('clipboard: copy stores value in story', (t) => {
|
|
4
|
+
const story = createStoryStub();
|
|
5
|
+
const view = createViewStub('hello world');
|
|
6
|
+
|
|
7
|
+
const clipboardFn = createClipboard({
|
|
8
|
+
story,
|
|
9
|
+
view,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
clipboardFn('copy');
|
|
13
|
+
|
|
14
|
+
t.ok(story.setData.called, 'story.setData should be called');
|
|
15
|
+
t.end();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('clipboard: copy calls clipboard.writeText', async (t) => {
|
|
19
|
+
const story = createStoryStub();
|
|
20
|
+
const view = createViewStub('selected text');
|
|
21
|
+
|
|
22
|
+
const writeTextStub = stub().returns(Promise.resolve());
|
|
23
|
+
const clipboardFn = createClipboard({
|
|
24
|
+
story,
|
|
25
|
+
view,
|
|
26
|
+
writeText: writeTextStub,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await clipboardFn('copy');
|
|
30
|
+
|
|
31
|
+
t.calledWith(writeTextStub, ['selected text'], 'writeText should be called with selected text');
|
|
32
|
+
t.end();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('clipboard: cut stores value in story', (t) => {
|
|
36
|
+
const story = createStoryStub();
|
|
37
|
+
const view = createViewStub('cut text');
|
|
38
|
+
|
|
39
|
+
const clipboardFn = createClipboard({
|
|
40
|
+
story,
|
|
41
|
+
view,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
clipboardFn('cut');
|
|
45
|
+
|
|
46
|
+
t.ok(story.setData.called, 'story.setData should be called');
|
|
47
|
+
t.end();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('clipboard: paste reads from clipboard and inserts', async (t) => {
|
|
51
|
+
const story = createStoryStub();
|
|
52
|
+
const view = createViewStub('');
|
|
53
|
+
|
|
54
|
+
const readTextStub = stub().returns(Promise.resolve('pasted content'));
|
|
55
|
+
const clipboardFn = createClipboard({
|
|
56
|
+
story,
|
|
57
|
+
view,
|
|
58
|
+
readText: readTextStub,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await clipboardFn('paste');
|
|
62
|
+
|
|
63
|
+
t.ok(view.dispatch.called, 'view.dispatch should be called');
|
|
64
|
+
t.end();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('clipboard: paste falls back to story on clipboard error', async (t) => {
|
|
68
|
+
const story = createStoryStub();
|
|
69
|
+
|
|
70
|
+
story.getData = stub().returns('fallback text');
|
|
71
|
+
|
|
72
|
+
const view = createViewStub('');
|
|
73
|
+
|
|
74
|
+
const readTextStub = stub().returns(Promise.reject(Error('denied')));
|
|
75
|
+
const clipboardFn = createClipboard({
|
|
76
|
+
story,
|
|
77
|
+
view,
|
|
78
|
+
readText: readTextStub,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await clipboardFn('paste');
|
|
82
|
+
|
|
83
|
+
t.ok(view.dispatch.called, 'view.dispatch should be called');
|
|
84
|
+
t.end();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const createStoryStub = () => ({
|
|
88
|
+
setData: stub(),
|
|
89
|
+
getData: stub().returns(''),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const createViewStub = (selectedText = '') => ({
|
|
93
|
+
state: {
|
|
94
|
+
selection: {
|
|
95
|
+
main: {
|
|
96
|
+
from: 0,
|
|
97
|
+
to: selectedText.length,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
sliceDoc: () => selectedText,
|
|
101
|
+
},
|
|
102
|
+
dispatch: stub().returns(true),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
function createClipboard({story, view, writeText, readText}) {
|
|
106
|
+
const clipboard = {
|
|
107
|
+
writeText: writeText || stub().returns(Promise.resolve()),
|
|
108
|
+
readText: readText || stub().returns(Promise.resolve('')),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const showMessageOnce = stub();
|
|
112
|
+
|
|
113
|
+
return (cmd) => {
|
|
114
|
+
const NAME = 'editor-clipboard';
|
|
115
|
+
|
|
116
|
+
const value = view.state.sliceDoc(view.state.selection.main.from, view.state.selection.main.to);
|
|
117
|
+
|
|
118
|
+
const insert = (text) => {
|
|
119
|
+
view.dispatch({
|
|
120
|
+
changes: {
|
|
121
|
+
from: view.state.selection.main.from,
|
|
122
|
+
to: view.state.selection.main.to,
|
|
123
|
+
insert: text,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (cmd === 'copy') {
|
|
129
|
+
story.setData(NAME, value);
|
|
130
|
+
return clipboard.writeText(value);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (cmd === 'cut') {
|
|
134
|
+
story.setData(NAME, value);
|
|
135
|
+
return cut(view) ? Promise.resolve() : Promise.reject();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return clipboard
|
|
139
|
+
.readText()
|
|
140
|
+
.then(insert)
|
|
141
|
+
.catch(() => {
|
|
142
|
+
showMessageOnce('Could not paste from clipboard. Inner buffer used.');
|
|
143
|
+
|
|
144
|
+
const fallbackValue = story.getData(NAME);
|
|
145
|
+
|
|
146
|
+
insert(fallbackValue);
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function cut(view) {
|
|
152
|
+
view.dispatch({
|
|
153
|
+
changes: {
|
|
154
|
+
from: view.state.selection.main.from,
|
|
155
|
+
to: view.state.selection.main.to,
|
|
156
|
+
insert: '',
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type {EditorView} from '@codemirror/view';
|
|
2
|
+
import type {SourceCode} from './types.js';
|
|
3
|
+
|
|
4
|
+
export function getValue(view: EditorView): SourceCode;
|
|
5
|
+
export const getDocValue: typeof getValue;
|
|
6
|
+
|
|
7
|
+
export function setValue(view: EditorView, value: SourceCode): void;
|
|
8
|
+
export const setDocValue: typeof setValue;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const getValue = (view) => view.state.doc.toString();
|
|
2
|
+
export const getDocValue = getValue;
|
|
3
|
+
|
|
4
|
+
export function setValue(view, value) {
|
|
5
|
+
view.dispatch({
|
|
6
|
+
changes: {
|
|
7
|
+
from: 0,
|
|
8
|
+
to: view.state.doc.length,
|
|
9
|
+
insert: value,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const setDocValue = setValue;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {test} from 'supertape';
|
|
2
|
+
import {EditorState} from '@codemirror/state';
|
|
3
|
+
import {EditorView} from '@codemirror/view';
|
|
4
|
+
import {
|
|
5
|
+
getValue,
|
|
6
|
+
setValue,
|
|
7
|
+
getDocValue,
|
|
8
|
+
setDocValue,
|
|
9
|
+
} from './content.js';
|
|
10
|
+
|
|
11
|
+
function makeView(doc = 'hello') {
|
|
12
|
+
const element = document.createElement('div');
|
|
13
|
+
document.body.appendChild(element);
|
|
14
|
+
|
|
15
|
+
return new EditorView({
|
|
16
|
+
state: EditorState.create({
|
|
17
|
+
doc,
|
|
18
|
+
}),
|
|
19
|
+
parent: element,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test('content: getValue returns document string', (t) => {
|
|
24
|
+
const view = makeView('abc');
|
|
25
|
+
const result = getValue(view);
|
|
26
|
+
const expected = 'abc';
|
|
27
|
+
|
|
28
|
+
view.destroy();
|
|
29
|
+
|
|
30
|
+
t.equal(result, expected);
|
|
31
|
+
t.end();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('content: setValue replaces content', (t) => {
|
|
35
|
+
const view = makeView('abc');
|
|
36
|
+
setValue(view, 'xyz');
|
|
37
|
+
|
|
38
|
+
const result = getValue(view);
|
|
39
|
+
const expected = 'xyz';
|
|
40
|
+
|
|
41
|
+
view.destroy();
|
|
42
|
+
|
|
43
|
+
t.equal(result, expected);
|
|
44
|
+
t.end();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('content: getDocValue aliases getValue', (t) => {
|
|
48
|
+
const view = makeView('abc');
|
|
49
|
+
const result = getDocValue(view);
|
|
50
|
+
const expected = 'abc';
|
|
51
|
+
|
|
52
|
+
view.destroy();
|
|
53
|
+
|
|
54
|
+
t.equal(result, expected);
|
|
55
|
+
t.end();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('content: setDocValue aliases setValue', (t) => {
|
|
59
|
+
const view = makeView('abc');
|
|
60
|
+
setDocValue(view, 'xyz');
|
|
61
|
+
|
|
62
|
+
const result = getValue(view);
|
|
63
|
+
const expected = 'xyz';
|
|
64
|
+
|
|
65
|
+
view.destroy();
|
|
66
|
+
|
|
67
|
+
t.equal(result, expected);
|
|
68
|
+
t.end();
|
|
69
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {Compartment} from '@codemirror/state';
|
|
2
|
+
import {type ViewUpdate, EditorView} from '@codemirror/view';
|
|
3
|
+
import type {
|
|
4
|
+
SourceCode,
|
|
5
|
+
KeyMap,
|
|
6
|
+
EditorMode,
|
|
7
|
+
EditorTheme,
|
|
8
|
+
} from './types.js';
|
|
9
|
+
|
|
10
|
+
export type CreateEditorOptions = {
|
|
11
|
+
value?: SourceCode;
|
|
12
|
+
mode?: EditorMode | {
|
|
13
|
+
name: EditorMode;
|
|
14
|
+
json?: boolean;
|
|
15
|
+
};
|
|
16
|
+
keyMap?: KeyMap;
|
|
17
|
+
theme?: EditorTheme;
|
|
18
|
+
lineNumbers?: boolean;
|
|
19
|
+
readOnly?: boolean;
|
|
20
|
+
foldGutter?: boolean;
|
|
21
|
+
updateListener?: (update: ViewUpdate) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Augmented EditorView with compartment refs for dynamic option changes.
|
|
25
|
+
export type QwordEditorView = EditorView & {
|
|
26
|
+
_themeCompartment: Compartment;
|
|
27
|
+
_keymapCompartment: Compartment;
|
|
28
|
+
_langCompartment: Compartment;
|
|
29
|
+
_historyCompartment: Compartment;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function createEditor(element: Element, options?: CreateEditorOptions): QwordEditorView;
|
package/client/create.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {EditorState, Compartment} from '@codemirror/state';
|
|
2
|
+
import {
|
|
3
|
+
EditorView,
|
|
4
|
+
lineNumbers as lineNumbersExtension,
|
|
5
|
+
drawSelection,
|
|
6
|
+
keymap,
|
|
7
|
+
} from '@codemirror/view';
|
|
8
|
+
import {history, historyKeymap} from '@codemirror/commands';
|
|
9
|
+
import {
|
|
10
|
+
foldGutter,
|
|
11
|
+
codeFolding,
|
|
12
|
+
syntaxHighlighting,
|
|
13
|
+
} from '@codemirror/language';
|
|
14
|
+
import {highlightStyle} from './highlight.js';
|
|
15
|
+
import {
|
|
16
|
+
keymapExtension,
|
|
17
|
+
themeExtension,
|
|
18
|
+
languageExtension,
|
|
19
|
+
} from './options.js';
|
|
20
|
+
import {markField, lineField} from './decorations.js';
|
|
21
|
+
|
|
22
|
+
export function createEditor(element, options = {}) {
|
|
23
|
+
const {
|
|
24
|
+
value = '',
|
|
25
|
+
mode = 'javascript',
|
|
26
|
+
keyMap = 'default',
|
|
27
|
+
theme = 'default',
|
|
28
|
+
lineNumbers = true,
|
|
29
|
+
readOnly = false,
|
|
30
|
+
foldGutter: fold = false,
|
|
31
|
+
updateListener,
|
|
32
|
+
} = options;
|
|
33
|
+
|
|
34
|
+
const themeCompartment = new Compartment();
|
|
35
|
+
const keymapCompartment = new Compartment();
|
|
36
|
+
const langCompartment = new Compartment();
|
|
37
|
+
const historyCompartment = new Compartment();
|
|
38
|
+
|
|
39
|
+
const hideCursorOnBlur = EditorView.theme({
|
|
40
|
+
'&:not(.cm-focused) .cm-fat-cursor': {
|
|
41
|
+
display: 'none',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const extensions = [
|
|
46
|
+
historyCompartment.of([
|
|
47
|
+
history(),
|
|
48
|
+
keymap.of(historyKeymap),
|
|
49
|
+
]),
|
|
50
|
+
markField,
|
|
51
|
+
lineField,
|
|
52
|
+
themeCompartment.of(themeExtension(theme)),
|
|
53
|
+
keymapCompartment.of(keymapExtension(keyMap)),
|
|
54
|
+
langCompartment.of(languageExtension(mode)),
|
|
55
|
+
syntaxHighlighting(highlightStyle),
|
|
56
|
+
...lineNumbers
|
|
57
|
+
? [
|
|
58
|
+
lineNumbersExtension(),
|
|
59
|
+
]
|
|
60
|
+
: [],
|
|
61
|
+
...fold
|
|
62
|
+
? [
|
|
63
|
+
foldGutter(),
|
|
64
|
+
codeFolding(),
|
|
65
|
+
]
|
|
66
|
+
: [],
|
|
67
|
+
...readOnly
|
|
68
|
+
? [
|
|
69
|
+
EditorState.readOnly.of(true),
|
|
70
|
+
]
|
|
71
|
+
: [],
|
|
72
|
+
...updateListener
|
|
73
|
+
? [
|
|
74
|
+
EditorView.updateListener.of(updateListener),
|
|
75
|
+
]
|
|
76
|
+
: [],
|
|
77
|
+
drawSelection(),
|
|
78
|
+
hideCursorOnBlur,
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const view = new EditorView({
|
|
82
|
+
state: EditorState.create({
|
|
83
|
+
doc: value,
|
|
84
|
+
extensions,
|
|
85
|
+
}),
|
|
86
|
+
parent: element,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
view._themeCompartment = themeCompartment;
|
|
90
|
+
view._keymapCompartment = keymapCompartment;
|
|
91
|
+
view._langCompartment = langCompartment;
|
|
92
|
+
view._historyCompartment = historyCompartment;
|
|
93
|
+
|
|
94
|
+
return view;
|
|
95
|
+
}
|