diff options
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/markdown/header.vue')
-rw-r--r-- | app/assets/javascripts/vue_shared/components/markdown/header.vue | 163 |
1 files changed, 98 insertions, 65 deletions
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index af0b34f1389..0907e064e01 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -13,13 +13,13 @@ import { import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { getModifierKey } from '~/constants'; import { getSelectedFragment } from '~/lib/utils/common_utils'; -import { s__, __ } from '~/locale'; +import { truncateSha } from '~/lib/utils/text_utility'; +import { s__, __, sprintf } from '~/locale'; import { CopyAsGFM } from '~/behaviors/markdown/copy_as_gfm'; import { updateText } from '~/lib/utils/text_markdown'; import ToolbarButton from './toolbar_button.vue'; import DrawioToolbarButton from './drawio_toolbar_button.vue'; import CommentTemplatesDropdown from './comment_templates_dropdown.vue'; -import EditorModeSwitcher from './editor_mode_switcher.vue'; export default { components: { @@ -29,7 +29,6 @@ export default { DrawioToolbarButton, CommentTemplatesDropdown, AiActionsDropdown: () => import('ee_component/ai/components/ai_actions_dropdown.vue'), - EditorModeSwitcher, }, directives: { GlTooltip: GlTooltipDirective, @@ -40,6 +39,7 @@ export default { default: null, }, editorAiActions: { default: () => [] }, + mrGeneratedContent: { default: null }, }, props: { previewMarkdown: { @@ -91,17 +91,19 @@ export default { required: false, default: false, }, - showContentEditorSwitcher: { + supportsQuickActions: { type: Boolean, required: false, default: false, }, }, data() { + const modifierKey = getModifierKey(); return { tag: '> ', suggestPopoverVisible: false, - modifierKey: getModifierKey(), + modifierKey, + shiftKey: modifierKey === '⌘' ? '⇧' : 'Shift+', }; }, computed: { @@ -126,9 +128,6 @@ export default { const expandText = s__('MarkdownEditor|Click to expand'); return [`<details><summary>${expandText}</summary>`, `{text}`, '</details>'].join('\n'); }, - showEditorModeSwitcher() { - return this.showContentEditorSwitcher && !this.previewMarkdown; - }, }, watch: { showSuggestPopover() { @@ -199,17 +198,25 @@ export default { insertIntoTextarea(text) { const textArea = this.$el.closest('.md-area')?.querySelector('textarea'); if (textArea) { - const generatedByText = `${text}\n\n---\n\n_${__('This comment was generated by AI')}_`; updateText({ textArea, - tag: generatedByText, + tag: text, cursorOffset: 0, wrap: false, }); } }, - handleEditorModeChanged() { - this.$emit('enableContentEditor'); + replaceTextarea(text) { + const { description, descriptionForSha } = this.$options.i18n; + const headSha = document.getElementById('merge_request_diff_head_sha').value; + const addendum = headSha + ? sprintf(descriptionForSha, { revision: truncateSha(headSha) }) + : description; + + if (this.mrGeneratedContent) { + this.mrGeneratedContent.setGeneratedContent(`${text}\n\n---\n\n_${addendum}_`); + this.mrGeneratedContent.showWarning(); + } }, switchPreview() { if (this.previewMarkdown) { @@ -218,6 +225,12 @@ export default { this.showMarkdownPreview(); } }, + insertAIAction(text) { + this.insertIntoTextarea(`${text}\n\n---\n\n_${__('This comment was generated by AI')}_`); + }, + insertSavedReply(savedReply) { + this.insertIntoTextarea(savedReply); + }, }, shortcuts: { bold: keysFor(BOLD_TEXT), @@ -228,27 +241,36 @@ export default { outdent: keysFor(OUTDENT_LINE), }, i18n: { - preview: __('Preview'), + comment: __('This comment was generated by AI'), + description: s__('MergeRequest|This description was generated using AI'), + descriptionForSha: s__( + 'MergeRequest|This description was generated for revision %{revision} using AI', + ), hidePreview: __('Continue editing'), + preview: __('Preview'), }, }; </script> <template> - <div class="md-header gl-bg-gray-50 gl-px-2 gl-rounded-base gl-mx-2 gl-mt-2"> - <div - class="gl-display-flex gl-align-items-center gl-flex-wrap" - :class="{ - 'gl-justify-content-end': previewMarkdown, - 'gl-justify-content-space-between': !previewMarkdown, - }" - > + <div class="md-header gl-border-b gl-border-gray-100 gl-px-3"> + <div class="gl-display-flex gl-align-items-center gl-flex-wrap"> <div data-testid="md-header-toolbar" - class="md-header-toolbar gl-display-flex gl-py-2 gl-flex-wrap" - :class="{ 'gl-display-none!': previewMarkdown }" + class="md-header-toolbar gl-display-flex gl-py-3 gl-flex-wrap gl-row-gap-3" > - <template v-if="canSuggest"> + <gl-button + v-if="enablePreview" + data-testid="preview-toggle" + value="preview" + :label="$options.i18n.previewTabTitle" + class="js-md-preview-button gl-flex-direction-row-reverse gl-align-items-center gl-font-weight-normal! gl-mr-2" + size="small" + category="tertiary" + @click="switchPreview" + >{{ previewMarkdown ? $options.i18n.hidePreview : $options.i18n.preview }}</gl-button + > + <template v-if="!previewMarkdown && canSuggest"> <toolbar-button ref="suggestButton" :tag="mdSuggestion" @@ -289,11 +311,13 @@ export default { </gl-popover> </template> <ai-actions-dropdown - v-if="editorAiActions.length" + v-if="!previewMarkdown && editorAiActions.length" :actions="editorAiActions" - @input="insertIntoTextarea" + @input="insertAIAction" + @replace="replaceTextarea" /> <toolbar-button + v-show="!previewMarkdown" tag="**" :button-title=" /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ @@ -305,6 +329,7 @@ export default { icon="bold" /> <toolbar-button + v-show="!previewMarkdown" tag="_" :button-title=" /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ @@ -317,11 +342,13 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('strikethrough')" + v-show="!previewMarkdown" tag="~~" :button-title=" /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ - sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}⇧X)'), { - modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */, + sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}%{shiftKey}X)'), { + modifierKey, + shiftKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */, }) " :shortcuts="$options.shortcuts.strikethrough" @@ -329,14 +356,22 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('quote')" + v-show="!previewMarkdown" :prepend="true" :tag="tag" :button-title="__('Insert a quote')" icon="quote" @click="handleQuote" /> - <toolbar-button tag="`" tag-block="```" :button-title="__('Insert code')" icon="code" /> <toolbar-button + v-show="!previewMarkdown" + tag="`" + tag-block="```" + :button-title="__('Insert code')" + icon="code" + /> + <toolbar-button + v-show="!previewMarkdown" tag="[{text}](url)" tag-select="url" :button-title=" @@ -350,6 +385,7 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('bullet-list')" + v-show="!previewMarkdown" :prepend="true" tag="- " :button-title="__('Add a bullet list')" @@ -357,6 +393,7 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('numbered-list')" + v-show="!previewMarkdown" :prepend="true" tag="1. " :button-title="__('Add a numbered list')" @@ -364,6 +401,7 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('task-list')" + v-show="!previewMarkdown" :prepend="true" tag="- [ ] " :button-title="__('Add a checklist')" @@ -371,6 +409,7 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('indent')" + v-show="!previewMarkdown" class="gl-display-none" :button-title=" /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ @@ -384,6 +423,7 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('outdent')" + v-show="!previewMarkdown" class="gl-display-none" :button-title=" /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ @@ -397,6 +437,7 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('collapsible-section')" + v-show="!previewMarkdown" :tag="mdCollapsibleSection" :prepend="true" tag-select="Click to expand" @@ -405,17 +446,18 @@ export default { /> <toolbar-button v-if="!restrictedToolBarItems.includes('table')" + v-show="!previewMarkdown" :tag="mdTable" :prepend="true" :button-title="__('Add a table')" icon="table" /> <gl-button - v-if="!restrictedToolBarItems.includes('attach-file')" + v-if="!previewMarkdown && !restrictedToolBarItems.includes('attach-file')" v-gl-tooltip :aria-label="__('Attach a file or image')" :title="__('Attach a file or image')" - class="gl-mr-2" + class="gl-mr-3" data-testid="button-attach-file" category="tertiary" icon="paperclip" @@ -423,46 +465,37 @@ export default { @click="handleAttachFile" /> <drawio-toolbar-button - v-if="drawioEnabled" + v-if="!previewMarkdown && drawioEnabled" :uploads-path="uploadsPath" :markdown-preview-path="markdownPreviewPath" /> + <!-- TODO Add icon and trigger functionality from here --> + <toolbar-button + v-if="supportsQuickActions" + v-show="!previewMarkdown" + :prepend="true" + tag="/" + :button-title="__('Add a quick action')" + icon="quick-actions" + /> <comment-templates-dropdown - v-if="newCommentTemplatePath && glFeatures.savedReplies" + v-if="!previewMarkdown && newCommentTemplatePath && glFeatures.savedReplies" :new-comment-template-path="newCommentTemplatePath" + @select="insertSavedReply" /> - </div> - <div class="switch-preview gl-py-2 gl-display-flex gl-align-items-center gl-ml-auto"> - <editor-mode-switcher - v-if="showEditorModeSwitcher" - size="small" - class="gl-mr-2" - value="markdown" - @input="handleEditorModeChanged" - /> - <gl-button - v-if="enablePreview" - data-testid="preview-toggle" - value="preview" - :label="$options.i18n.previewTabTitle" - class="js-md-preview-button gl-flex-direction-row-reverse gl-align-items-center gl-font-weight-normal!" - size="small" - category="tertiary" - @click="switchPreview" - >{{ previewMarkdown ? $options.i18n.hidePreview : $options.i18n.preview }}</gl-button - > - <gl-button - v-if="!restrictedToolBarItems.includes('full-screen')" - v-gl-tooltip - :class="{ 'gl-display-none!': previewMarkdown }" - class="js-zen-enter gl-ml-2" - category="tertiary" - icon="maximize" - size="small" - :title="__('Go full screen')" - :prepend="true" - :aria-label="__('Go full screen')" - /> + <div v-if="!previewMarkdown" class="full-screen"> + <gl-button + v-if="!restrictedToolBarItems.includes('full-screen')" + v-gl-tooltip + class="js-zen-enter" + category="tertiary" + icon="maximize" + size="small" + :title="__('Go full screen')" + :prepend="true" + :aria-label="__('Go full screen')" + /> + </div> </div> </div> </div> |