From cc050f075949c2018029504a3f5c4f8309a63b82 Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Thu, 22 Feb 2024 12:42:17 +0100 Subject: [PATCH 01/25] Overhaul autocomplete styling --- .../design-system/src/css/_primitives.scss | 5 ++ packages/design-system/src/css/_tokens.scss | 2 +- .../src/styles/plugins/_codemirror.scss | 72 +++++++++++++------ 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/packages/design-system/src/css/_primitives.scss b/packages/design-system/src/css/_primitives.scss index 77e3a4e30ca4f..73e5b98ec3554 100644 --- a/packages/design-system/src/css/_primitives.scss +++ b/packages/design-system/src/css/_primitives.scss @@ -350,6 +350,11 @@ var(--prim-color-alt-e-l), 0.4 ); + --prim-color-alt-e-tint-100: hsl( + var(--prim-color-alt-e-h), + var(--prim-color-alt-e-s), + calc(var(--prim-color-alt-e-l) + 10%) + ); --prim-color-alt-e-tint-250: hsl( var(--prim-color-alt-e-h), var(--prim-color-alt-e-s), diff --git a/packages/design-system/src/css/_tokens.scss b/packages/design-system/src/css/_tokens.scss index 3c20089511182..fa3c51fc9202b 100644 --- a/packages/design-system/src/css/_tokens.scss +++ b/packages/design-system/src/css/_tokens.scss @@ -136,7 +136,7 @@ --color-code-gutterBackground: var(--prim-gray-0); --color-code-gutterForeground: var(--prim-gray-320); --color-code-tags-comment: var(--prim-gray-420); - --color-autocomplete-selected-background: var(--prim-color-alt-e); + --color-autocomplete-selected-background: var(--prim-color-alt-e-tint-100); --color-autocomplete-selected-font: var(--prim-gray-0); // Variables diff --git a/packages/editor-ui/src/styles/plugins/_codemirror.scss b/packages/editor-ui/src/styles/plugins/_codemirror.scss index fd6105a228e74..270cbfcefec0f 100644 --- a/packages/editor-ui/src/styles/plugins/_codemirror.scss +++ b/packages/editor-ui/src/styles/plugins/_codemirror.scss @@ -8,35 +8,59 @@ content: 'n8n supports all JavaScript functions, including those not listed.'; } -// Custom autocomplete item type icons -// 1. Native and n8n extension functions: -.cm-completionIcon-extension-function, -.cm-completionIcon-native-function { - &::after { - content: 'ƒ'; - } -} - -.cm-tooltip-autocomplete { +.ͼ2 .cm-tooltip-autocomplete { background-color: var(--color-background-xlight) !important; + box-shadow: var(--box-shadow-light); + border: none; .cm-tooltip { overflow: hidden; text-overflow: ellipsis; } - li .cm-completionLabel { - color: var(--color-success); - } -} + > ul[role='listbox'] { + font-family: var(--font-family-monospace); + max-height: min(280px, 50vh); + width: min(320px, 50vw); + min-width: auto; + max-width: none; + border: var(--border-base); + border-radius: var(--border-radius-base); -.ͼ2 .cm-tooltip-autocomplete ul li[aria-selected] { - background-color: var(--color-autocomplete-selected-background); -} + li[role='option'] { + color: var(--color-success); + display: flex; + justify-content: space-between; + padding: var(--spacing-5xs) var(--spacing-2xs); + scroll-margin-top: 40px; + } + + li .cm-completionLabel { + line-height: var(--font-line-height-xloose); + } + + li[aria-selected] { + background-color: var(--color-autocomplete-selected-background); + color: var(--color-autocomplete-selected-font); + } -.ͼ2 .cm-tooltip-autocomplete ul li[aria-selected] .cm-completionLabel { - color: var(--color-autocomplete-selected-font) !important; - font-weight: 600; + > completion-section { + font-family: var(--font-family); + font-weight: var(--font-weight-bold); + padding: var(--spacing-4xs) var(--spacing-2xs); + color: var(--color-text-base); + opacity: 1; + background-color: var(--color-background-base); + border-bottom: var(--border-base); + line-height: var(--font-line-height-loose); + position: sticky; + top: 0; + } + + li:has(+ completion-section) { + border-bottom: var(--border-base); + } + } } .autocomplete-info-container { @@ -45,8 +69,12 @@ padding: var(--spacing-4xs) 0; } -.cm-completionInfo { - background-color: var(--color-background-xlight) !important; +.ͼ2 .cm-completionInfo { + background-color: var(--color-background-xlight); + border: var(--border-base); + margin-left: var(--spacing-5xs); + border-radius: var(--border-radius-base); + line-height: var(--font-line-height-loose); .autocomplete-info-header { color: var(--color-text-dark); From 26aab04e900aac628c066e2b218882d987060701 Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Thu, 22 Feb 2024 12:43:02 +0100 Subject: [PATCH 02/25] Reuse keymap and autocompletion options across editors --- .../CodeNodeEditor/baseExtensions.ts | 61 +++------------ .../ExpressionEditorModalInput.vue | 25 +++--- .../src/components/HtmlEditor/HtmlEditor.vue | 20 ++--- .../InlineExpressionEditorInput.vue | 36 ++++----- .../src/components/JsEditor/JsEditor.vue | 16 ++-- .../src/components/JsonEditor/JsonEditor.vue | 20 ++--- .../src/components/SqlEditor/SqlEditor.vue | 18 +++-- .../src/plugins/codemirror/keymap.ts | 77 +++++++++++++++++++ .../src/plugins/codemirror/n8nLang.ts | 3 + 9 files changed, 166 insertions(+), 110 deletions(-) create mode 100644 packages/editor-ui/src/plugins/codemirror/keymap.ts diff --git a/packages/editor-ui/src/components/CodeNodeEditor/baseExtensions.ts b/packages/editor-ui/src/components/CodeNodeEditor/baseExtensions.ts index bed55df677de9..d2543dc3f5964 100644 --- a/packages/editor-ui/src/components/CodeNodeEditor/baseExtensions.ts +++ b/packages/editor-ui/src/components/CodeNodeEditor/baseExtensions.ts @@ -6,24 +6,19 @@ import { highlightSpecialChars, keymap, lineNumbers, - type KeyBinding, } from '@codemirror/view'; import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language'; -import { acceptCompletion, selectedCompletion } from '@codemirror/autocomplete'; -import { - history, - indentLess, - indentMore, - insertNewlineAndIndent, - toggleComment, - redo, - deleteCharBackward, - undo, -} from '@codemirror/commands'; +import { history, toggleComment, redo, deleteCharBackward, undo } from '@codemirror/commands'; import { lintGutter } from '@codemirror/lint'; import { type Extension, Prec } from '@codemirror/state'; import { codeInputHandler } from '@/plugins/codemirror/inputHandlers/code.inputHandler'; +import { + autocompleteKeyMap, + enterKeyMap, + historyKeyMap, + tabKeyMap, +} from '@/plugins/codemirror/keymap'; export const readOnlyEditorExtensions: readonly Extension[] = [ lineNumbers(), @@ -31,42 +26,6 @@ export const readOnlyEditorExtensions: readonly Extension[] = [ highlightSpecialChars(), ]; -export const tabKeyMap: KeyBinding[] = [ - { - any(editor, event) { - if (event.key === 'Tab' || (event.key === 'Escape' && selectedCompletion(editor.state))) { - event.stopPropagation(); - } - - return false; - }, - }, - { - key: 'Tab', - run: (editor) => { - if (selectedCompletion(editor.state)) { - return acceptCompletion(editor); - } - - return indentMore(editor); - }, - }, - { key: 'Shift-Tab', run: indentLess }, -]; - -export const enterKeyMap: KeyBinding[] = [ - { - key: 'Enter', - run: (editor) => { - if (selectedCompletion(editor.state)) { - return acceptCompletion(editor); - } - - return insertNewlineAndIndent(editor); - }, - }, -]; - export const writableEditorExtensions: readonly Extension[] = [ history(), lintGutter(), @@ -79,11 +38,11 @@ export const writableEditorExtensions: readonly Extension[] = [ highlightActiveLineGutter(), Prec.highest( keymap.of([ - ...tabKeyMap, + ...tabKeyMap(), ...enterKeyMap, + ...autocompleteKeyMap, + ...historyKeyMap, { key: 'Mod-/', run: toggleComment }, - { key: 'Mod-z', run: undo }, - { key: 'Mod-Shift-z', run: redo }, { key: 'Backspace', run: deleteCharBackward, shift: deleteCharBackward }, ]), ), diff --git a/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue b/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue index a08d3d964a799..1bc9db1bfced1 100644 --- a/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue +++ b/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue @@ -6,18 +6,24 @@ import { defineComponent } from 'vue'; import { EditorView, keymap } from '@codemirror/view'; import { EditorState, Prec } from '@codemirror/state'; -import { history, redo, undo } from '@codemirror/commands'; +import { history } from '@codemirror/commands'; import { expressionManager } from '@/mixins/expressionManager'; import { completionManager } from '@/mixins/completionManager'; import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler'; -import { n8nLang } from '@/plugins/codemirror/n8nLang'; +import { n8nAutocompletion, n8nLang } from '@/plugins/codemirror/n8nLang'; import { highlighter } from '@/plugins/codemirror/resolvableHighlighter'; import { inputTheme } from './theme'; import { forceParse } from '@/utils/forceParse'; -import { acceptCompletion, autocompletion } from '@codemirror/autocomplete'; +import { completionStatus } from '@codemirror/autocomplete'; import type { IVariableItemSelected } from '@/Interface'; +import { + autocompleteKeyMap, + enterKeyMap, + historyKeyMap, + tabKeyMap, +} from '@/plugins/codemirror/keymap'; export default defineComponent({ name: 'ExpressionEditorModalInput', @@ -44,13 +50,15 @@ export default defineComponent({ mounted() { const extensions = [ inputTheme(), - autocompletion(), Prec.highest( keymap.of([ - { key: 'Tab', run: acceptCompletion }, + ...tabKeyMap(), + ...historyKeyMap, + ...enterKeyMap, + ...autocompleteKeyMap, { - any: (_: EditorView, event: KeyboardEvent) => { - if (event.key === 'Escape') { + any: (view, event) => { + if (event.key === 'Escape' && completionStatus(view.state) === null) { event.stopPropagation(); this.$emit('close'); } @@ -58,11 +66,10 @@ export default defineComponent({ return false; }, }, - { key: 'Mod-z', run: undo }, - { key: 'Mod-Shift-z', run: redo }, ]), ), n8nLang(), + n8nAutocompletion(), history(), expressionInputHandler(), EditorView.lineWrapping, diff --git a/packages/editor-ui/src/components/HtmlEditor/HtmlEditor.vue b/packages/editor-ui/src/components/HtmlEditor/HtmlEditor.vue index 01d4ea5a5cb90..d1a53c9114c17 100644 --- a/packages/editor-ui/src/components/HtmlEditor/HtmlEditor.vue +++ b/packages/editor-ui/src/components/HtmlEditor/HtmlEditor.vue @@ -6,8 +6,7 @@