qword 1.1.11 → 1.1.13

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 (73) hide show
  1. package/ChangeLog +10 -0
  2. package/bower.json +25 -0
  3. package/client/_clipboard.js +50 -0
  4. package/client/_init-socket.js +76 -0
  5. package/client/_on-save.js +38 -0
  6. package/client/content.d.ts +8 -0
  7. package/client/content.js +14 -0
  8. package/client/create.d.ts +33 -0
  9. package/client/create.js +101 -0
  10. package/client/decorations.d.ts +38 -0
  11. package/client/decorations.js +110 -0
  12. package/client/dom.d.ts +8 -0
  13. package/client/dom.js +17 -0
  14. package/client/editor.d.ts +46 -0
  15. package/client/editor.js +178 -0
  16. package/client/events.d.ts +8 -0
  17. package/client/events.js +8 -0
  18. package/client/highlight.d.ts +3 -0
  19. package/client/highlight.js +82 -0
  20. package/client/history.d.ts +5 -0
  21. package/client/history.js +14 -0
  22. package/client/index.d.ts +60 -0
  23. package/client/index.js +38 -0
  24. package/client/loadremote.js +102 -0
  25. package/client/options.d.ts +31 -0
  26. package/client/options.js +70 -0
  27. package/client/position.d.ts +19 -0
  28. package/client/position.js +33 -0
  29. package/client/qword.js +196 -0
  30. package/client/save.js +53 -0
  31. package/client/scroll.d.ts +11 -0
  32. package/client/scroll.js +9 -0
  33. package/client/show-message.js +30 -0
  34. package/client/story.js +55 -0
  35. package/client/types.d.ts +28 -0
  36. package/dist/qword.js +1 -1
  37. package/dist/qword.js.map +1 -1
  38. package/dist-dev/qword.js +1 -1
  39. package/package.json +1 -1
  40. package/modules/cm-searchbox/ChangeLog +0 -60
  41. package/modules/cm-searchbox/LICENSE +0 -21
  42. package/modules/cm-searchbox/README.md +0 -27
  43. package/modules/cm-searchbox/img/searchbox.png +0 -0
  44. package/modules/cm-searchbox/lib/searchbox.js +0 -679
  45. package/modules/cm-searchbox/package.json +0 -27
  46. package/modules/cm-show-invisibles/ChangeLog +0 -108
  47. package/modules/cm-show-invisibles/LICENSE +0 -21
  48. package/modules/cm-show-invisibles/README.md +0 -25
  49. package/modules/cm-show-invisibles/lib/show-invisibles.js +0 -131
  50. package/modules/cm-show-invisibles/package.json +0 -34
  51. package/modules/codemirror-emmet/README.md +0 -56
  52. package/modules/codemirror-emmet/dist/emmet.js +0 -48661
  53. package/modules/codemirror-emmet/dist/emmet.min.js +0 -8
  54. package/modules/codemirror-emmet/dist/emmetPlugin.js +0 -840
  55. package/modules/codemirror-emmet/dist/emmetPlugin.min.js +0 -1
  56. package/modules/codemirror-emmet/dist/index.html +0 -46
  57. package/modules/codemirror-emmet/editor.js +0 -204
  58. package/modules/codemirror-emmet/gulpfile.js +0 -54
  59. package/modules/codemirror-emmet/index.html +0 -68
  60. package/modules/codemirror-emmet/package.json +0 -36
  61. package/modules/codemirror-emmet/plugin.js +0 -201
  62. package/modules/codemirror-emmet/shim.js +0 -19
  63. package/modules/codemirror-emmet/vendor/almond.js +0 -410
  64. package/modules/codemirror-emmet/vendor/codemirror/v4/codemirror.css +0 -270
  65. package/modules/codemirror-emmet/vendor/codemirror/v4/codemirror.js +0 -7339
  66. package/modules/codemirror-emmet/vendor/codemirror/v4/css.js +0 -701
  67. package/modules/codemirror-emmet/vendor/codemirror/v4/htmlmixed.js +0 -117
  68. package/modules/codemirror-emmet/vendor/codemirror/v4/xml.js +0 -349
  69. package/modules/loadremote/ChangeLog +0 -23
  70. package/modules/loadremote/LICENSE +0 -21
  71. package/modules/loadremote/README.md +0 -53
  72. package/modules/loadremote/lib/loadremote.js +0 -147
  73. package/modules/loadremote/package.json +0 -26
package/ChangeLog CHANGED
@@ -1,3 +1,13 @@
1
+ 2026.09.07, v1.1.13
2
+
3
+ fix:
4
+ - d53dfd5 client: options: keepIndent on double enter
5
+
6
+ 2026.09.07, v1.1.12
7
+
8
+ fix:
9
+ - ee42e43 npmignore
10
+
1
11
  2026.09.07, v1.1.11
2
12
 
3
13
  fix:
package/bower.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "dword",
3
+ "homepage": "https://github.com/cloudcmd/dword",
4
+ "authors": [
5
+ "coderaiser <mnemonic.enemy@gmail.com>"
6
+ ],
7
+ "description": "web editor",
8
+ "main": "bin/dword.js",
9
+ "keywords": [
10
+ "file",
11
+ "edit"
12
+ ],
13
+ "license": "MIT",
14
+ "private": true,
15
+ "ignore": [
16
+ "node_modules",
17
+ "modules"
18
+ ],
19
+ "dependencies": {
20
+ "codemirror-emmet": "~1.1.0",
21
+ "cm-searchbox": "1.0.8",
22
+ "cm-show-invisibles": "^3.1.0",
23
+ "jshint": "^2.13.5"
24
+ }
25
+ }
@@ -0,0 +1,50 @@
1
+ import clipboard from '@cloudcmd/clipboard';
2
+ import once from 'once';
3
+ import showMessage from './show-message.js';
4
+
5
+ const showMessageOnce = once(showMessage);
6
+
7
+ export default function clipboardCommand(command) {
8
+ const {_view, _story} = this;
9
+ const NAME = 'editor-clipboard';
10
+
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
+ };
27
+
28
+ if (command === 'copy') {
29
+ const value = getSelected();
30
+ _story.setData(NAME, value);
31
+
32
+ return clipboard.writeText(value);
33
+ }
34
+
35
+ if (command === 'cut') {
36
+ const value = getSelected();
37
+ _story.setData(NAME, value);
38
+ insert('');
39
+
40
+ return clipboard.writeText(value);
41
+ }
42
+
43
+ return clipboard
44
+ .readText()
45
+ .then(insert)
46
+ .catch(() => {
47
+ showMessageOnce.call(this, 'Could not paste from clipboard. Inner buffer used.');
48
+ insert(_story.getData(NAME));
49
+ });
50
+ }
@@ -0,0 +1,76 @@
1
+ import {applyPatch} from 'daffy';
2
+ import {alert} from 'smalltalk';
3
+
4
+ const getHost = () => {
5
+ const l = location;
6
+
7
+ return l.origin || l.protocol + '//' + l.host;
8
+ };
9
+
10
+ export default function() {
11
+ const qword = this;
12
+ const href = getHost();
13
+ const FIVE_SECONDS = 5000;
14
+
15
+ const patch = (name, data) => {
16
+ socket.emit('patch', name, data);
17
+ };
18
+
19
+ const {_prefixSocket} = qword;
20
+
21
+ const socket = globalThis.io.connect(href + _prefixSocket, {
22
+ 'max reconnection attempts': 2 ** 32,
23
+ 'reconnection limit': FIVE_SECONDS,
24
+ 'path': this._socketPath + '/socket.io',
25
+ });
26
+
27
+ this._socket = socket;
28
+
29
+ socket.on('reject', () => {
30
+ this.emit('reject');
31
+ });
32
+
33
+ socket.on('connect', () => {
34
+ qword._patch = patch;
35
+ });
36
+
37
+ socket.on('message', (msg) => {
38
+ this._onSave(null, msg);
39
+ });
40
+
41
+ socket.on('file', (name, data) => {
42
+ qword
43
+ .setModeForPath(name)
44
+ .setValueFirst(name, data)
45
+ .moveCursorTo(0, 0);
46
+ });
47
+
48
+ socket.on('patch', (name, data, hash) => {
49
+ if (name !== this._FileName)
50
+ return;
51
+
52
+ if (hash !== this._story.getHash(name))
53
+ return;
54
+
55
+ const cursor = qword.getCursor();
56
+ const value = qword.getValue();
57
+ const result = applyPatch(value, data);
58
+
59
+ this.setValue(result);
60
+
61
+ this
62
+ ._story
63
+ .setData(name, value)
64
+ .setHash(name, this.sha());
65
+
66
+ qword.moveCursorTo(cursor.row, cursor.column);
67
+ });
68
+
69
+ socket.on('disconnect', () => {
70
+ qword.save.patch = globalThis._patchHttp;
71
+ });
72
+
73
+ socket.on('err', (error) => {
74
+ alert(this._TITLE, error);
75
+ });
76
+ }
@@ -0,0 +1,38 @@
1
+ import {confirm} from 'smalltalk';
2
+ import {write} from 'restafary/client';
3
+
4
+ function empty() {}
5
+
6
+ export default function onSave(error, text) {
7
+ const Value = this._Value;
8
+ const FileName = this._FileName;
9
+ let msg = 'Try again?';
10
+
11
+ if (error) {
12
+ if (error.message)
13
+ msg = error.message + '\n' + msg;
14
+ else
15
+ msg = `Can't save.` + msg;
16
+
17
+ const onSave = this._onSave.bind(this);
18
+
19
+ return confirm(this._TITLE, msg)
20
+ .then(() => {
21
+ write(this._FileName, this._Value, onSave);
22
+ })
23
+ .catch(empty)
24
+ .then(() => {
25
+ this.focus();
26
+ });
27
+ }
28
+
29
+ this.showMessage(text);
30
+
31
+ const hash = this.sha();
32
+ this
33
+ ._story
34
+ .setData(FileName, Value)
35
+ .setHash(FileName, hash);
36
+
37
+ this._Emitter.emit('save', Value.length);
38
+ }
@@ -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,33 @@
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;
33
+
@@ -0,0 +1,101 @@
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
+ indentOnInput,
14
+ indentUnit,
15
+ bracketMatching,
16
+ } from '@codemirror/language';
17
+ import {highlightStyle} from './highlight.js';
18
+ import {
19
+ keymapExtension,
20
+ themeExtension,
21
+ languageExtension,
22
+ } from './options.js';
23
+ import {markField, lineField} from './decorations.js';
24
+
25
+ export function createEditor(element, options = {}) {
26
+ const {
27
+ value = '',
28
+ mode = 'javascript',
29
+ keyMap = 'default',
30
+ theme = 'default',
31
+ lineNumbers = true,
32
+ readOnly = false,
33
+ foldGutter: fold = false,
34
+ updateListener,
35
+ } = options;
36
+
37
+ const themeCompartment = new Compartment();
38
+ const keymapCompartment = new Compartment();
39
+ const langCompartment = new Compartment();
40
+ const historyCompartment = new Compartment();
41
+
42
+ const hideCursorOnBlur = EditorView.theme({
43
+ '&:not(.cm-focused) .cm-fat-cursor': {
44
+ display: 'none',
45
+ },
46
+ });
47
+
48
+ const extensions = [
49
+ historyCompartment.of([
50
+ history(),
51
+ keymap.of(historyKeymap),
52
+ ]),
53
+ markField,
54
+ lineField,
55
+ themeCompartment.of(themeExtension(theme)),
56
+ keymapCompartment.of(keymapExtension(keyMap)),
57
+ bracketMatching(),
58
+ langCompartment.of(languageExtension(mode)),
59
+ syntaxHighlighting(highlightStyle),
60
+ indentOnInput(),
61
+ indentUnit.of(' '),
62
+ ...lineNumbers
63
+ ? [
64
+ lineNumbersExtension(),
65
+ ]
66
+ : [],
67
+ ...fold
68
+ ? [
69
+ foldGutter(),
70
+ codeFolding(),
71
+ ]
72
+ : [],
73
+ ...readOnly
74
+ ? [
75
+ EditorState.readOnly.of(true),
76
+ ]
77
+ : [],
78
+ ...updateListener
79
+ ? [
80
+ EditorView.updateListener.of(updateListener),
81
+ ]
82
+ : [],
83
+ drawSelection(),
84
+ hideCursorOnBlur,
85
+ ];
86
+
87
+ const view = new EditorView({
88
+ state: EditorState.create({
89
+ doc: value,
90
+ extensions,
91
+ }),
92
+ parent: element,
93
+ });
94
+
95
+ view._themeCompartment = themeCompartment;
96
+ view._keymapCompartment = keymapCompartment;
97
+ view._langCompartment = langCompartment;
98
+ view._historyCompartment = historyCompartment;
99
+
100
+ return view;
101
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ type Range,
3
+ StateEffect,
4
+ StateField,
5
+ } from '@codemirror/state';
6
+ import {
7
+ Decoration,
8
+ EditorView,
9
+ type DecorationSet,
10
+ } from '@codemirror/view';
11
+ import type {SourcePosition} from './types.js';
12
+
13
+ export const setMarkEffect: StateEffect<Range<Decoration>>;
14
+ export const clearMarkEffect: StateEffect<null>;
15
+ export const setLineEffect: StateEffect<{
16
+ line: number;
17
+ cls: string;
18
+ }>;
19
+ export const clearLineEffect: StateEffect<{
20
+ line: number;
21
+ cls: string;
22
+ }>;
23
+
24
+ export const markField: StateField<DecorationSet>;
25
+ export const lineField: StateField<DecorationSet>;
26
+
27
+ export type MarkHandle = {
28
+ clear: () => void;
29
+ };
30
+
31
+ export function markText(view: EditorView, from: SourcePosition, to: SourcePosition, {className}: {
32
+ className: string;
33
+ }): MarkHandle;
34
+
35
+ export function addLineClass(view: EditorView, line: number, _where: string, className: string): void;
36
+
37
+ export function removeLineClass(view: EditorView, line: number, _where: string, className: string): void;
38
+
@@ -0,0 +1,110 @@
1
+ import {StateEffect, StateField} from '@codemirror/state';
2
+ import {EditorView, Decoration} from '@codemirror/view';
3
+ import {positionToOffset} from './position.js';
4
+
5
+ const noop = () => {};
6
+
7
+ export const setMarkEffect = StateEffect.define();
8
+ export const clearMarkEffect = StateEffect.define();
9
+ export const setLineEffect = StateEffect.define();
10
+ export const clearLineEffect = StateEffect.define();
11
+
12
+ export const markField = StateField.define({
13
+ create: () => Decoration.none,
14
+ update(decorations, transaction) {
15
+ decorations = decorations.map(transaction.changes);
16
+
17
+ for (const effect of transaction.effects) {
18
+ if (effect.is(setMarkEffect))
19
+ decorations = decorations.update({
20
+ add: [effect.value],
21
+ sort: true,
22
+ });
23
+
24
+ if (effect.is(clearMarkEffect))
25
+ decorations = Decoration.none;
26
+ }
27
+
28
+ return decorations;
29
+ },
30
+ provide: (field) => EditorView.decorations.from(field),
31
+ });
32
+
33
+ export const lineField = StateField.define({
34
+ create: () => Decoration.none,
35
+ update(decorations, transaction) {
36
+ decorations = decorations.map(transaction.changes);
37
+
38
+ for (const effect of transaction.effects) {
39
+ if (effect.is(setLineEffect)) {
40
+ const {line, cls} = effect.value;
41
+ const {from} = transaction.state.doc.line(Math.min(line + 1, transaction.state.doc.lines));
42
+
43
+ decorations = decorations.update({
44
+ add: [
45
+ Decoration
46
+ .line({
47
+ class: cls,
48
+ })
49
+ .range(from),
50
+ ],
51
+ sort: true,
52
+ });
53
+ }
54
+
55
+ if (effect.is(clearLineEffect)) {
56
+ const {line, cls} = effect.value;
57
+
58
+ decorations = decorations.update({
59
+ filter: (from, _, decoration) => !(decoration.spec.class === cls && transaction.state.doc.lineAt(from).number === line + 1),
60
+ });
61
+ }
62
+ }
63
+
64
+ return decorations;
65
+ },
66
+ provide: (field) => EditorView.decorations.from(field),
67
+ });
68
+
69
+ export function markText(view, from, to, {className}) {
70
+ const fromOffset = positionToOffset(view.state.doc, from);
71
+ const toOffset = positionToOffset(view.state.doc, to);
72
+
73
+ if (fromOffset === toOffset)
74
+ return {
75
+ clear: noop,
76
+ };
77
+
78
+ const decoration = Decoration
79
+ .mark({
80
+ class: className,
81
+ })
82
+ .range(fromOffset, toOffset);
83
+
84
+ view.dispatch({
85
+ effects: setMarkEffect.of(decoration),
86
+ });
87
+ return {
88
+ clear: () => view.dispatch({
89
+ effects: clearMarkEffect.of(null),
90
+ }),
91
+ };
92
+ }
93
+
94
+ export function addLineClass(view, line, _where, className) {
95
+ view.dispatch({
96
+ effects: setLineEffect.of({
97
+ line,
98
+ cls: className,
99
+ }),
100
+ });
101
+ }
102
+
103
+ export function removeLineClass(view, line, _where, className) {
104
+ view.dispatch({
105
+ effects: clearLineEffect.of({
106
+ line,
107
+ cls: className,
108
+ }),
109
+ });
110
+ }
@@ -0,0 +1,8 @@
1
+ import {EditorView} from '@codemirror/view';
2
+
3
+ export function getView(container: HTMLElement): EditorView | null;
4
+
5
+ export function refresh(view: EditorView): void;
6
+
7
+ export function observeResize(view: EditorView, container: Element): () => void;
8
+
package/client/dom.js ADDED
@@ -0,0 +1,17 @@
1
+ import {EditorView} from '@codemirror/view';
2
+
3
+ export function getView(container) {
4
+ const element = container.querySelector('.cm-editor');
5
+ return element ? EditorView.findFromDOM(element) : null;
6
+ }
7
+
8
+ export function refresh(view) {
9
+ view.requestMeasure();
10
+ }
11
+
12
+ export function observeResize(view, container) {
13
+ const observer = new ResizeObserver(() => view.requestMeasure());
14
+ observer.observe(container);
15
+
16
+ return () => observer.disconnect();
17
+ }
@@ -0,0 +1,46 @@
1
+ import {
2
+ type QwordEditorView,
3
+ type CreateEditorOptions,
4
+ } from './create.js';
5
+ import type {MarkHandle} from './decorations.js';
6
+ import type {OptionKey, OptionValue} from './options.js';
7
+ import type {
8
+ SourceCode,
9
+ SourcePosition,
10
+ CharOffset,
11
+ KeyMap,
12
+ EditorMode,
13
+ EditorTheme,
14
+ } from './types.js';
15
+
16
+ export type EditorOptions = CreateEditorOptions;
17
+
18
+ export default class Editor {
19
+ constructor(element: Element, options?: EditorOptions);
20
+ getValue(): SourceCode;
21
+ setValue(value: SourceCode): this;
22
+ getCursorIndex(): CharOffset;
23
+ getCursor(): {
24
+ row: number;
25
+ column: number;
26
+ };
27
+ posFromIndex(index: CharOffset): SourcePosition;
28
+ focus(): this;
29
+ moveCursorTo(row: number, column?: number): this;
30
+ markRange(from: SourcePosition, to: SourcePosition, className: string): MarkHandle;
31
+ addLineClass(line: number, className: string): this;
32
+ removeLineClass(line: number, className: string): this;
33
+ setOption(name: OptionKey, value: OptionValue): this;
34
+ setMode(mode: EditorMode): this;
35
+ setTheme(theme: EditorTheme): this;
36
+ setKeyMap(name: KeyMap): this;
37
+ setModeForPath(path: string): this;
38
+ isChanged(): boolean;
39
+ clearHistory(): this;
40
+ on(event: string, handler: (...args: unknown[]) => void): this;
41
+ addListener(event: string, handler: (...args: unknown[]) => void): this;
42
+ once(event: string, handler: (...args: unknown[]) => void): this;
43
+ removeListener(event: string, handler: (...args: unknown[]) => void): this;
44
+ emit(...args: unknown[]): this;
45
+ _view(): QwordEditorView;
46
+ }