From 22ee9c7f2b2e23695bf897bb709e3a5e563152e9 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri, 18 Aug 2023 21:19:50 +0100 Subject: [PATCH] Init --- .../jenkins/model/Jenkins/_script.jelly | 2 +- .../resources/lib/hudson/scriptConsole.jelly | 5 +- war/package.json | 6 + war/src/main/js/app.js | 2 + war/src/main/js/components/textarea/index.js | 28 +++ war/src/main/js/components/textarea/theme.js | 140 +++++++++++++++ war/src/main/scss/form/codemirror.scss | 60 ------- .../main/webapp/scripts/hudson-behavior.js | 43 ----- war/yarn.lock | 159 ++++++++++++++++++ 9 files changed, 338 insertions(+), 107 deletions(-) create mode 100644 war/src/main/js/components/textarea/index.js create mode 100644 war/src/main/js/components/textarea/theme.js diff --git a/core/src/main/resources/jenkins/model/Jenkins/_script.jelly b/core/src/main/resources/jenkins/model/Jenkins/_script.jelly index 89495f20ea35..2c9acd6d982a 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/_script.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/_script.jelly @@ -30,4 +30,4 @@ THE SOFTWARE.
println(Jenkins.instance.pluginManager.plugins)
- \ No newline at end of file + diff --git a/core/src/main/resources/lib/hudson/scriptConsole.jelly b/core/src/main/resources/lib/hudson/scriptConsole.jelly index f6d21d6fcda9..9e6a9a962918 100644 --- a/core/src/main/resources/lib/hudson/scriptConsole.jelly +++ b/core/src/main/resources/lib/hudson/scriptConsole.jelly @@ -46,13 +46,12 @@ THE SOFTWARE.

- +
- - +

${%Result} diff --git a/war/package.json b/war/package.json index 363ea601b452..2d846c9018dd 100644 --- a/war/package.json +++ b/war/package.json @@ -50,6 +50,12 @@ "webpack-remove-empty-scripts": "1.0.3" }, "dependencies": { + "@codemirror/language": "^6.9.0", + "@codemirror/legacy-modes": "^6.3.3", + "@codemirror/state": "^6.2.1", + "@codemirror/view": "^6.16.0", + "@lezer/highlight": "^1.1.6", + "codemirror": "^6.0.1", "handlebars": "4.7.8", "hotkeys-js": "3.12.0", "jquery": "3.7.0", diff --git a/war/src/main/js/app.js b/war/src/main/js/app.js index 63a35226d7d4..46b38a769b39 100644 --- a/war/src/main/js/app.js +++ b/war/src/main/js/app.js @@ -5,6 +5,7 @@ import Tooltips from "@/components/tooltips"; import StopButtonLink from "@/components/stop-button-link"; import ConfirmationLink from "@/components/confirmation-link"; import Dialogs from "@/components/dialogs"; +import Textarea from "@/components/textarea"; Dropdowns.init(); Notifications.init(); @@ -13,3 +14,4 @@ Tooltips.init(); StopButtonLink.init(); ConfirmationLink.init(); Dialogs.init(); +Textarea.init(); diff --git a/war/src/main/js/components/textarea/index.js b/war/src/main/js/components/textarea/index.js new file mode 100644 index 000000000000..78053901b03b --- /dev/null +++ b/war/src/main/js/components/textarea/index.js @@ -0,0 +1,28 @@ +import {EditorView, basicSetup } from "codemirror" +import {EditorState} from "@codemirror/state" +import {groovy} from "@codemirror/legacy-modes/mode/groovy" +import {StreamLanguage} from "@codemirror/language" +import { oneDark } from "@/components/textarea/theme"; + +function editorFromTextArea(textarea, extensions) { + let view = new EditorView({ + state: EditorState.create({doc: textarea.value, extensions: [basicSetup, StreamLanguage.define(groovy), oneDark]}), + }); + textarea.parentNode.insertBefore(view.dom, textarea) + textarea.style.display = "none" + if (textarea.form) { + textarea.form.addEventListener("submit", () => { + textarea.value = view.state.doc.toString() + }) + } + return view + } + +function init() { + const textareas = document.querySelectorAll(".script"); + textareas.forEach((textarea) => { + const view = editorFromTextArea(textarea) + }); +} + +export default { init }; diff --git a/war/src/main/js/components/textarea/theme.js b/war/src/main/js/components/textarea/theme.js new file mode 100644 index 000000000000..6587b26419e6 --- /dev/null +++ b/war/src/main/js/components/textarea/theme.js @@ -0,0 +1,140 @@ +import {EditorView} from "@codemirror/view" +import {HighlightStyle, syntaxHighlighting} from "@codemirror/language" +import {tags as t} from "@lezer/highlight" + +const chalky = "#e5c07b", + coral = "var(--red)", + cyan = "var(--cyan)", + invalid = "#ffffff", + ivory = "#abb2bf", + stone = "var(--text-color-secondary)", + malibu = "var(--blue)", + sage = "var(--green)", + whiskey = "var(--orange)", + violet = "var(--purple)", + darkBackground = "#21252b", + highlightBackground = "#2c313a", + background = "var(--input-color)", + tooltipBackground = "#353a42", + selection = "var(--selection-color)", + cursor = "var(--text-color)" + +/// The editor theme styles for One Dark. +export const oneDarkTheme = EditorView.theme({ + "&": { + color: ivory, + backgroundColor: background, + borderRadius: "10px", + border: "2px solid var(--input-border)", + padding: "6px", + boxShadow: 'var(--form-input-glow)', + transition: 'var(--standard-transition)', + }, + "&:hover": { + borderColor: 'var(--input-border-hover)', + }, + "&:focus-within": { + borderColor: 'var(--focus-input-border)', + boxShadow: 'var(--form-input-glow--focus)' + }, + ".cm-content": { + caretColor: cursor + }, + ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor }, + "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": { backgroundColor: selection }, + ".cm-panels": { backgroundColor: darkBackground, color: ivory }, + ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, + ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, + ".cm-searchMatch": { + backgroundColor: "#72a1ff59", + outline: "1px solid #457dff" + }, + ".cm-searchMatch.cm-searchMatch-selected": { + backgroundColor: "#6199ff2f" + }, + ".cm-scroller": { + fontFamily: 'var(--font-family-mono)' + }, + ".cm-activeLine": { backgroundColor: "#6699ff0b" }, + ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, + "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { + backgroundColor: "#bad0f847" + }, + "&.cm-focused": { + outline: "none", + }, + ".cm-gutters": { + backgroundColor: "transparent", + color: stone, + border: "none" + }, + ".cm-activeLineGutter": { + backgroundColor: "transparent" + }, + ".cm-line": { + backgroundColor: "transparent", + }, + ".cm-foldPlaceholder": { + backgroundColor: "transparent", + border: "none", + color: "#ddd" + }, + ".cm-tooltip": { + border: "none", + backgroundColor: tooltipBackground + }, + ".cm-tooltip .cm-tooltip-arrow:before": { + borderTopColor: "transparent", + borderBottomColor: "transparent" + }, + ".cm-tooltip .cm-tooltip-arrow:after": { + borderTopColor: tooltipBackground, + borderBottomColor: tooltipBackground + }, + ".cm-tooltip-autocomplete": { + "& > ul > li[aria-selected]": { + backgroundColor: highlightBackground, + color: ivory + } + } +}) + +/// The highlighting style for code in the One Dark theme. +export const oneDarkHighlightStyle = HighlightStyle.define([ + {tag: t.keyword, + color: violet}, + {tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], + color: coral}, + {tag: [t.function(t.variableName), t.labelName], + color: malibu}, + {tag: [t.color, t.constant(t.name), t.standard(t.name)], + color: whiskey}, + {tag: [t.definition(t.name), t.separator], + color: ivory}, + {tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], + color: chalky}, + {tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], + color: cyan}, + {tag: [t.meta, t.comment], + color: stone}, + {tag: t.strong, + fontWeight: "bold"}, + {tag: t.emphasis, + fontStyle: "italic"}, + {tag: t.strikethrough, + textDecoration: "line-through"}, + {tag: t.link, + color: stone, + textDecoration: "underline"}, + {tag: t.heading, + fontWeight: "bold", + color: coral}, + {tag: [t.atom, t.bool, t.special(t.variableName)], + color: whiskey }, + {tag: [t.processingInstruction, t.string, t.inserted], + color: sage}, + {tag: t.invalid, + color: invalid}, +]) + +export const oneDark = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)] diff --git a/war/src/main/scss/form/codemirror.scss b/war/src/main/scss/form/codemirror.scss index 67520192c0e5..e69de29bb2d1 100644 --- a/war/src/main/scss/form/codemirror.scss +++ b/war/src/main/scss/form/codemirror.scss @@ -1,60 +0,0 @@ -.CodeMirror { - display: block; - background: var(--input-color); - border: 2px solid var(--input-border); - border-radius: var(--form-input-border-radius); - width: 100%; - box-shadow: var(--form-input-glow); - transition: - var(--standard-transition), - height 0s; - cursor: text; - margin-bottom: var(--section-padding); - - // Override the defaults of codemirror.css - pre { - font-family: var(--font-family-mono) !important; - font-weight: 500 !important; - line-height: 1.66 !important; - } - - &:hover { - border-color: var(--input-border-hover); - } - - &:active, - &:focus-within { - outline: none; - border-color: var(--focus-input-border); - box-shadow: var(--form-input-glow--focus); - } - - textarea { - background: transparent; - border: none; - outline: none; - } - - .cm-variable { - color: var(--text-color) !important; - } - - .CodeMirror-selected { - background-color: var(--selection-color) !important; - } -} - -.jenkins-codemirror-resizer { - position: relative; - width: 10px; - height: 10px; - margin: calc(-2rem - 11px) 3px 2rem auto; - z-index: 10; - cursor: ns-resize; - background: currentColor; - mask-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='20px' height='20px' viewBox='0 0 20 20' version='1.1' xmlns='http://www.w3.org/2000/svg'%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cline x1='19' y1='12' x2='12' y2='19' id='Path' stroke='%23979797' stroke-width='2'%3E%3C/line%3E%3Cline x1='1' y1='18' x2='17.9705627' y2='1.02943725' id='Path-2' stroke='%23979797' stroke-width='2'%3E%3C/line%3E%3C/g%3E%3C/svg%3E"); - mask-size: 7px 7px; - mask-position: 3px 3px; - mask-repeat: no-repeat; - opacity: 0.75; -} diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index 4197fa903207..89f40c48090c 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -1390,49 +1390,6 @@ function rowvgStartEachRow(recursive, f) { e.tabIndex = 9999; // make help link unnavigable from keyboard }); - // Script Console : settings and shortcut key - Behaviour.specify("TEXTAREA.script", "textarea-script", ++p, function (e) { - (function () { - var cmdKeyDown = false; - var mode = e.getAttribute("script-mode") || "text/x-groovy"; - - // eslint-disable-next-line no-unused-vars - var w = CodeMirror.fromTextArea(e, { - mode: mode, - lineNumbers: true, - matchBrackets: true, - onKeyEvent: function (editor, event) { - function saveAndSubmit() { - editor.save(); - getParentForm(e).submit(); - event.stop(); - } - - // Mac (Command + Enter) - if (navigator.userAgent.indexOf("Mac") > -1) { - if (event.type == "keydown" && isCommandKey(event)) { - cmdKeyDown = true; - } - if (event.type == "keyup" && isCommandKey(event)) { - cmdKeyDown = false; - } - if (cmdKeyDown && isReturnKeyDown()) { - saveAndSubmit(); - return true; - } - - // Windows, Linux (Ctrl + Enter) - } else { - if (event.ctrlKey && isReturnKeyDown()) { - saveAndSubmit(); - return true; - } - } - }, - }).getWrapperElement(); - })(); - }); - // deferred client-side clickable map. // this is useful where the generation of element is time consuming Behaviour.specify("IMG[lazymap]", "img-lazymap-", ++p, function (e) { diff --git a/war/yarn.lock b/war/yarn.lock index 57535318721c..dfd9be37606f 100644 --- a/war/yarn.lock +++ b/war/yarn.lock @@ -1342,6 +1342,98 @@ __metadata: languageName: node linkType: hard +"@codemirror/autocomplete@npm:^6.0.0": + version: 6.9.0 + resolution: "@codemirror/autocomplete@npm:6.9.0" + dependencies: + "@codemirror/language": ^6.0.0 + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.6.0 + "@lezer/common": ^1.0.0 + peerDependencies: + "@codemirror/language": ^6.0.0 + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + "@lezer/common": ^1.0.0 + checksum: a5f661944c75f40b02c90a193c9a459c0fd7e335c0ac5973420c19157dfb46010f573c2b70731591fe477e7a2ad10121ff3ae394a72d450946d7b886c28b0368 + languageName: node + linkType: hard + +"@codemirror/commands@npm:^6.0.0": + version: 6.2.4 + resolution: "@codemirror/commands@npm:6.2.4" + dependencies: + "@codemirror/language": ^6.0.0 + "@codemirror/state": ^6.2.0 + "@codemirror/view": ^6.0.0 + "@lezer/common": ^1.0.0 + checksum: 468895fa19ff0554181b698c81f850820de5c0289cab92c44392fb127286f09ca72b921d6ea4353b70b616a4fd0c3667d86b6f917202a3ad2e196eb7b581f7b6 + languageName: node + linkType: hard + +"@codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.9.0": + version: 6.9.0 + resolution: "@codemirror/language@npm:6.9.0" + dependencies: + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + "@lezer/common": ^1.0.0 + "@lezer/highlight": ^1.0.0 + "@lezer/lr": ^1.0.0 + style-mod: ^4.0.0 + checksum: 9a897fb0f569159eeafb7dce83061b425af7244bbeae2649e0e677488548b2a02eaf0c13c0c5b4d59da55e8866e6f4dc7abe3dfaa09c13749a2fa2c0dbc0c565 + languageName: node + linkType: hard + +"@codemirror/legacy-modes@npm:^6.3.3": + version: 6.3.3 + resolution: "@codemirror/legacy-modes@npm:6.3.3" + dependencies: + "@codemirror/language": ^6.0.0 + checksum: 3cd32b0f011b0a193e0948e5901b625f38aa6d9a8b24344531d6e142eb6fbb3e6cb5969429102044f3d04fbe53c4deaebd9f659c05067a0b18d17766290c9e05 + languageName: node + linkType: hard + +"@codemirror/lint@npm:^6.0.0": + version: 6.4.0 + resolution: "@codemirror/lint@npm:6.4.0" + dependencies: + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + crelt: ^1.0.5 + checksum: ba15f7dd87afbceafaa0b68f94b0d53727e4aacca7a81a4ed3278706df5787fdf18cd3f0d807a136f902b2fc2296bf3490462fd543d1d4ced17a0d8c171820fd + languageName: node + linkType: hard + +"@codemirror/search@npm:^6.0.0": + version: 6.5.1 + resolution: "@codemirror/search@npm:6.5.1" + dependencies: + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + crelt: ^1.0.5 + checksum: 672515c20238f69ff5cd8b662128699178ba7e020fc44a8ed2b0dcc25d8d5f5579418865616dd8809317a408fb08b6001a442f0fb706a772250b4284d7437045 + languageName: node + linkType: hard + +"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.4, @codemirror/state@npm:^6.2.0, @codemirror/state@npm:^6.2.1": + version: 6.2.1 + resolution: "@codemirror/state@npm:6.2.1" + checksum: d12a321d0471b264b9d3259042bff913a8b939e8d28d408ff452004538a71ca9d5329df3f8a1d8a9183f5b42a7ef5b200737bcab1065714f5ae8e0a5ba9d59d3 + languageName: node + linkType: hard + +"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.16.0, @codemirror/view@npm:^6.6.0": + version: 6.16.0 + resolution: "@codemirror/view@npm:6.16.0" + dependencies: + "@codemirror/state": ^6.1.4 + style-mod: ^4.0.0 + w3c-keyname: ^2.2.4 + checksum: 54d412b5159716c8a1a9c46fa04ff083e68a663cb887e6e2a4ca86fe9c3930d5255200fe84c65620e0a442f62dc2c13df277bcd1d4eef2e11e3c4e124fcf9d38 + languageName: node + linkType: hard + "@csstools/cascade-layer-name-parser@npm:^1.0.3, @csstools/cascade-layer-name-parser@npm:^1.0.4": version: 1.0.4 resolution: "@csstools/cascade-layer-name-parser@npm:1.0.4" @@ -1875,6 +1967,31 @@ __metadata: languageName: node linkType: hard +"@lezer/common@npm:^1.0.0": + version: 1.0.4 + resolution: "@lezer/common@npm:1.0.4" + checksum: 0bea82da76e0b89afad4e5159d3add460022916352c47906ec67b26d6fe5ec9cb8e23df0e2bf0adef765ae78bed1706fc573a11506d01a80112a5b6dd317730c + languageName: node + linkType: hard + +"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.6": + version: 1.1.6 + resolution: "@lezer/highlight@npm:1.1.6" + dependencies: + "@lezer/common": ^1.0.0 + checksum: 411a702394c4c996b7d7f145a38f3a85a8cc698b3918acc7121c629255bb76d4ab383753f69009e011dc415210c6acbbb5b27bde613259ab67e600b29397b03b + languageName: node + linkType: hard + +"@lezer/lr@npm:^1.0.0": + version: 1.3.10 + resolution: "@lezer/lr@npm:1.3.10" + dependencies: + "@lezer/common": ^1.0.0 + checksum: 9d3c22bf692561cf7fe2f3d14e821913f87116ff9d73b8b550e7998b6135baae9f504563846a4257e1bb4eae97ae1b60c06c6066450ddeef5e03e8783526b2ae + languageName: node + linkType: hard + "@nicolo-ribaudo/chokidar-2@npm:2.1.8-no-fsevents.3": version: 2.1.8-no-fsevents.3 resolution: "@nicolo-ribaudo/chokidar-2@npm:2.1.8-no-fsevents.3" @@ -2858,6 +2975,21 @@ __metadata: languageName: node linkType: hard +"codemirror@npm:^6.0.1": + version: 6.0.1 + resolution: "codemirror@npm:6.0.1" + dependencies: + "@codemirror/autocomplete": ^6.0.0 + "@codemirror/commands": ^6.0.0 + "@codemirror/language": ^6.0.0 + "@codemirror/lint": ^6.0.0 + "@codemirror/search": ^6.0.0 + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + checksum: 1a78f7077ac5801bdbff162aa0c61bf2b974603c7e9a477198c3ce50c789af674a061d7c293c58b73807eda345c2b5228c38ad2aabb9319d552d5486f785cbef + languageName: node + linkType: hard + "color-convert@npm:^1.9.0": version: 1.9.3 resolution: "color-convert@npm:1.9.3" @@ -2992,6 +3124,13 @@ __metadata: languageName: node linkType: hard +"crelt@npm:^1.0.5": + version: 1.0.6 + resolution: "crelt@npm:1.0.6" + checksum: dad842093371ad702afbc0531bfca2b0a8dd920b23a42f26e66dabbed9aad9acd5b9030496359545ef3937c3aced0fd4ac39f7a2d280a23ddf9eb7fdcb94a69f + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -4352,8 +4491,14 @@ __metadata: "@babel/cli": 7.22.10 "@babel/core": 7.22.10 "@babel/preset-env": 7.22.10 + "@codemirror/language": ^6.9.0 + "@codemirror/legacy-modes": ^6.3.3 + "@codemirror/state": ^6.2.1 + "@codemirror/view": ^6.16.0 + "@lezer/highlight": ^1.1.6 babel-loader: 9.1.3 clean-webpack-plugin: 4.0.0 + codemirror: ^6.0.1 css-loader: 6.8.1 css-minimizer-webpack-plugin: 5.0.1 eslint: 8.47.0 @@ -6720,6 +6865,13 @@ __metadata: languageName: node linkType: hard +"style-mod@npm:^4.0.0": + version: 4.0.3 + resolution: "style-mod@npm:4.0.3" + checksum: 934556e720bd29026ff8fef43a1a35b58957813025b91f996d886e9405acf934ddb1934def4400b174bd7784c9263eb9c71f07ae83925af9271b7d921d546854 + languageName: node + linkType: hard + "style-search@npm:^0.1.0": version: 0.1.0 resolution: "style-search@npm:0.1.0" @@ -7115,6 +7267,13 @@ __metadata: languageName: node linkType: hard +"w3c-keyname@npm:^2.2.4": + version: 2.2.8 + resolution: "w3c-keyname@npm:2.2.8" + checksum: 95bafa4c04fa2f685a86ca1000069c1ec43ace1f8776c10f226a73296caeddd83f893db885c2c220ebeb6c52d424e3b54d7c0c1e963bbf204038ff1a944fbb07 + languageName: node + linkType: hard + "watchpack@npm:^2.4.0": version: 2.4.0 resolution: "watchpack@npm:2.4.0"