@sveltia/ui 0.79.2 → 0.79.3
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/dist/components/text-editor/background-update.d.ts +3 -0
- package/dist/components/text-editor/background-update.js +18 -0
- package/dist/components/text-editor/code-editor.svelte +14 -0
- package/dist/components/text-editor/code-editor.svelte.d.ts +13 -1
- package/dist/components/text-editor/core.js +6 -1
- package/dist/components/text-editor/shiki/facade.js +2 -1
- package/dist/components/text-editor/shiki/highlighter.js +2 -1
- package/dist/components/text-editor/shiki/theme.js +2 -1
- package/dist/components/text-editor/shiki/version.d.ts +1 -1
- package/dist/components/text-editor/shiki/version.js +1 -1
- package/dist/components/text-editor/toolbar/code-language-switcher.svelte +17 -10
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SKIP_DOM_SELECTION_TAG } from 'lexical';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @import { LexicalEditor, UpdateTag } from 'lexical';
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the tags of an editor update the user didn’t make, such as one that highlights the code once
|
|
9
|
+
* a grammar has loaded. While the focus is elsewhere, the update leaves the DOM selection alone,
|
|
10
|
+
* which would otherwise move the focus back to the editor, taking over what the user types next.
|
|
11
|
+
* @param {LexicalEditor} editor Editor instance.
|
|
12
|
+
* @param {UpdateTag[]} [tags] Other tags of the update.
|
|
13
|
+
* @returns {UpdateTag[]} Tags.
|
|
14
|
+
*/
|
|
15
|
+
export const getBackgroundUpdateTags = (editor, tags = []) =>
|
|
16
|
+
editor.getRootElement()?.contains(document.activeElement)
|
|
17
|
+
? tags
|
|
18
|
+
: [...tags, SKIP_DOM_SELECTION_TAG];
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* @typedef {object} Props
|
|
21
21
|
* @property {string} [code] Input value.
|
|
22
22
|
* @property {string} [lang] Selected language.
|
|
23
|
+
* @property {boolean} [pending] Whether the user has changed the code, and the editor has yet to
|
|
24
|
+
* update {@link code}, which it does a moment later. Bind it to wait for the change before
|
|
25
|
+
* reading the code, for example to save it. Read-only.
|
|
23
26
|
* @property {boolean} [showLanguageSwitcher] Whether to show the language selector.
|
|
24
27
|
* @property {boolean} [flex] Make the text input container flexible.
|
|
25
28
|
* @property {string} [class] The `class` attribute on the wrapper element.
|
|
@@ -47,6 +50,7 @@
|
|
|
47
50
|
/* eslint-disable prefer-const */
|
|
48
51
|
code = $bindable(''),
|
|
49
52
|
lang = $bindable('plain'),
|
|
53
|
+
pending = $bindable(false),
|
|
50
54
|
showLanguageSwitcher = false,
|
|
51
55
|
flex = false,
|
|
52
56
|
hidden = false,
|
|
@@ -86,6 +90,16 @@
|
|
|
86
90
|
|
|
87
91
|
setContext('editorStore', editorStore);
|
|
88
92
|
|
|
93
|
+
$effect(() => {
|
|
94
|
+
pending = editorStore.pending;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// An editor removed right after a change never converts it, so don’t leave a bound `pending` set
|
|
98
|
+
// for good, which would hold up anything waiting for the change
|
|
99
|
+
$effect(() => () => {
|
|
100
|
+
pending = false;
|
|
101
|
+
});
|
|
102
|
+
|
|
89
103
|
$effect(() => {
|
|
90
104
|
// The root initializes the editor before these effects first run, and stays initialized
|
|
91
105
|
/* v8 ignore next */
|
|
@@ -13,6 +13,12 @@ declare const CodeEditor: import("svelte").Component<{
|
|
|
13
13
|
* Selected language.
|
|
14
14
|
*/
|
|
15
15
|
lang?: string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the user has changed the code, and the editor has yet to
|
|
18
|
+
* update {@link code}, which it does a moment later. Bind it to wait for the change before
|
|
19
|
+
* reading the code, for example to save it. Read-only.
|
|
20
|
+
*/
|
|
21
|
+
pending?: boolean | undefined;
|
|
16
22
|
/**
|
|
17
23
|
* Whether to show the language selector.
|
|
18
24
|
*/
|
|
@@ -67,7 +73,7 @@ declare const CodeEditor: import("svelte").Component<{
|
|
|
67
73
|
* Primary slot content.
|
|
68
74
|
*/
|
|
69
75
|
children?: Snippet<[]> | undefined;
|
|
70
|
-
} & Record<string, any>, {}, "code" | "lang">;
|
|
76
|
+
} & Record<string, any>, {}, "code" | "lang" | "pending">;
|
|
71
77
|
type Props = {
|
|
72
78
|
/**
|
|
73
79
|
* Input value.
|
|
@@ -77,6 +83,12 @@ type Props = {
|
|
|
77
83
|
* Selected language.
|
|
78
84
|
*/
|
|
79
85
|
lang?: string | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Whether the user has changed the code, and the editor has yet to
|
|
88
|
+
* update {@link code}, which it does a moment later. Bind it to wait for the change before
|
|
89
|
+
* reading the code, for example to save it. Read-only.
|
|
90
|
+
*/
|
|
91
|
+
pending?: boolean | undefined;
|
|
80
92
|
/**
|
|
81
93
|
* Whether to show the language selector.
|
|
82
94
|
*/
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
$getNearestNodeFromDOMNode as getNearestNodeFromDOMNode,
|
|
51
51
|
$getRoot as getRoot,
|
|
52
52
|
$getSelection as getSelection,
|
|
53
|
+
HISTORY_MERGE_TAG,
|
|
53
54
|
INDENT_CONTENT_COMMAND,
|
|
54
55
|
INSERT_PARAGRAPH_COMMAND,
|
|
55
56
|
KEY_ENTER_COMMAND,
|
|
@@ -1033,6 +1034,10 @@ export const convertMarkdownToLexical = async (editor, value, enabledTransformer
|
|
|
1033
1034
|
// Pad blank blockquote lines so they are not imported as literal `>` text
|
|
1034
1035
|
value = padBlankBlockquoteLines(value);
|
|
1035
1036
|
|
|
1037
|
+
// Lexical commits an empty state when the root element is attached, and the history keeps it as
|
|
1038
|
+
// the current entry. An empty state can’t be restored, so undoing to it throws an error. Merge
|
|
1039
|
+
// the first import into that entry instead of pushing it onto the undo stack
|
|
1040
|
+
const isEmpty = editor.getEditorState().isEmpty();
|
|
1036
1041
|
/** @type {unknown} */
|
|
1037
1042
|
let error;
|
|
1038
1043
|
|
|
@@ -1047,7 +1052,7 @@ export const convertMarkdownToLexical = async (editor, value, enabledTransformer
|
|
|
1047
1052
|
// Tell the import from a change made by the user, and commit it right away, so the node
|
|
1048
1053
|
// transforms, e.g. the one giving a code block its default language, have run by the time the
|
|
1049
1054
|
// content is exported below
|
|
1050
|
-
{ tag: IMPORT_UPDATE_TAG, discrete: true },
|
|
1055
|
+
{ tag: isEmpty ? [IMPORT_UPDATE_TAG, HISTORY_MERGE_TAG] : IMPORT_UPDATE_TAG, discrete: true },
|
|
1051
1056
|
);
|
|
1052
1057
|
|
|
1053
1058
|
if (error) {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
HISTORY_MERGE_TAG,
|
|
22
22
|
tokenizeRawText,
|
|
23
23
|
} from 'lexical';
|
|
24
|
+
import { getBackgroundUpdateTags } from '../background-update.js';
|
|
24
25
|
import { cachePayload, getCachedPayload } from './cache.js';
|
|
25
26
|
import { LANGUAGES, THEMES } from './generated.js';
|
|
26
27
|
import { getCodeHighlighterLoaders } from './loader.js';
|
|
@@ -123,7 +124,7 @@ const refreshCodeNode = (editor, codeNodeKey) => {
|
|
|
123
124
|
|
|
124
125
|
codeNode.markDirty();
|
|
125
126
|
},
|
|
126
|
-
{ tag: HISTORY_MERGE_TAG },
|
|
127
|
+
{ tag: getBackgroundUpdateTags(editor, [HISTORY_MERGE_TAG]) },
|
|
127
128
|
);
|
|
128
129
|
};
|
|
129
130
|
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
HISTORY_MERGE_TAG,
|
|
34
34
|
TextNode,
|
|
35
35
|
} from 'lexical';
|
|
36
|
+
import { getBackgroundUpdateTags } from '../background-update.js';
|
|
36
37
|
import {
|
|
37
38
|
getHighlightNodes,
|
|
38
39
|
isCodeLanguageLoaded,
|
|
@@ -368,7 +369,7 @@ const codeNodeTransform = (editor, tokenizer, transformState, node) => {
|
|
|
368
369
|
staleNode.markDirty();
|
|
369
370
|
}
|
|
370
371
|
},
|
|
371
|
-
{ tag: HISTORY_MERGE_TAG },
|
|
372
|
+
{ tag: getBackgroundUpdateTags(editor, [HISTORY_MERGE_TAG]) },
|
|
372
373
|
);
|
|
373
374
|
});
|
|
374
375
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { $isCodeNode as isCodeNode } from '@lexical/code-core';
|
|
2
2
|
import { $getRoot as getRoot, HISTORY_MERGE_TAG } from 'lexical';
|
|
3
|
+
import { getBackgroundUpdateTags } from '../background-update.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @import { LexicalEditor } from 'lexical';
|
|
@@ -85,6 +86,6 @@ export const observeCodeTheme = (editor) =>
|
|
|
85
86
|
}
|
|
86
87
|
});
|
|
87
88
|
},
|
|
88
|
-
{ tag: HISTORY_MERGE_TAG },
|
|
89
|
+
{ tag: getBackgroundUpdateTags(editor, [HISTORY_MERGE_TAG]) },
|
|
89
90
|
);
|
|
90
91
|
});
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { getContext } from 'svelte';
|
|
6
6
|
import Option from '../../listbox/option.svelte';
|
|
7
7
|
import Select from '../../select/select.svelte';
|
|
8
|
+
import { getBackgroundUpdateTags } from '../background-update.js';
|
|
8
9
|
import { focusEditor, loadCodeHighlighter } from '../core.js';
|
|
9
10
|
import { LANGUAGES } from '../shiki/generated.js';
|
|
10
11
|
|
|
@@ -90,18 +91,24 @@
|
|
|
90
91
|
return;
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
await loadCodeHighlighter(lang);
|
|
94
|
+
const { editor } = editorStore;
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const node = getCodeNode();
|
|
96
|
+
await focusEditor(editor);
|
|
97
|
+
await loadCodeHighlighter(lang);
|
|
99
98
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
// The user may have moved on to another field while the highlighter was loading
|
|
100
|
+
editor.update(
|
|
101
|
+
() => {
|
|
102
|
+
// https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx#L713
|
|
103
|
+
const node = getCodeNode();
|
|
104
|
+
|
|
105
|
+
if (node) {
|
|
106
|
+
node.setLanguage(lang);
|
|
107
|
+
selectedLanguage = lang;
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{ tag: getBackgroundUpdateTags(editor) },
|
|
111
|
+
);
|
|
105
112
|
}}
|
|
106
113
|
>
|
|
107
114
|
<Option label={_('_sui.text_editor.plain_text')} value="plain" dir="ltr" />
|