Merge branch 'main' into feat-32257-add-comments-unchanged-lines-and-show

This commit is contained in:
Rajesh Jonnalagadda
2024-12-04 19:32:43 +05:30
committed by GitHub
74 changed files with 571 additions and 621 deletions
@@ -96,6 +96,11 @@
font-size: 0.85em;
}
.combo-markdown-editor .ui.tab.markup[data-tab-panel="markdown-previewer"] {
border-bottom: 1px solid var(--color-secondary);
padding-bottom: 1rem;
}
text-expander {
display: block;
position: relative;
-6
View File
@@ -126,12 +126,6 @@
cursor: pointer;
}
.ui.list .list > .item [class*="right floated"],
.ui.list > .item [class*="right floated"] {
float: right;
margin: 0 0 0 1em;
}
.ui.menu .ui.list > .item,
.ui.menu .ui.list .list > .item {
display: list-item;
-12
View File
@@ -633,18 +633,6 @@
}
}
.ui.floated.menu {
float: left;
margin: 0 0.5rem 0 0;
}
.ui.floated.menu .item:last-child::before {
display: none;
}
.ui.right.floated.menu {
float: right;
margin: 0 0 0 0.5rem;
}
.ui.borderless.menu .item::before,
.ui.borderless.menu .item .menu .item::before,
.ui.menu .borderless.item::before {
+1 -1
View File
@@ -1005,7 +1005,7 @@ td .commit-summary {
}
.repository.view.issue .comment-list .code-comment .comment-content {
margin-left: 36px;
margin-left: 24px;
}
.repository.view.issue .comment-list .comment > .avatar {
-25
View File
@@ -102,19 +102,11 @@
cursor: pointer;
}
.comment-code-cloud .ui.active.tab {
padding: 0.5em;
}
.comment-code-cloud .ui.active.tab.markup {
padding: 1em;
min-height: 168px;
}
.comment-code-cloud .ui.tabular.menu {
margin: 0.5em;
}
.comment-code-cloud .editor-statusbar {
display: none;
}
@@ -123,23 +115,6 @@
padding: 10px 0;
}
.comment-code-cloud .footer .markup-info {
display: inline-block;
margin: 5px 0;
font-size: 12px;
color: var(--color-text-light);
}
.comment-code-cloud .footer .ui.right.floated {
padding-top: 6px;
}
.comment-code-cloud .footer::after {
clear: both;
content: "";
display: block;
}
.diff-file-body .comment-form {
margin: 0 0 0 3em;
}
@@ -86,6 +86,8 @@ const sfc = {
textCreateBranch: elRoot.getAttribute('data-text-create-branch'),
textCreateRefFrom: elRoot.getAttribute('data-text-create-ref-from'),
textNoResults: elRoot.getAttribute('data-text-no-results'),
textViewAllBranches: elRoot.getAttribute('data-text-view-all-branches'),
textViewAllTags: elRoot.getAttribute('data-text-view-all-tags'),
currentRepoDefaultBranch: elRoot.getAttribute('data-current-repo-default-branch'),
currentRepoLink: elRoot.getAttribute('data-current-repo-link'),
@@ -99,6 +101,7 @@ const sfc = {
showTabBranches: shouldShowTabBranches,
showTabTags: elRoot.getAttribute('data-show-tab-tags') === 'true',
allowCreateNewRef: elRoot.getAttribute('data-allow-create-new-ref') === 'true',
showViewAllRefsEntry: elRoot.getAttribute('data-show-view-all-refs-entry') === 'true',
enableFeed: elRoot.getAttribute('data-enable-feed') === 'true',
};
@@ -281,6 +284,11 @@ export default sfc; // activate IDE's Vue plugin
<div class="message" v-if="showNoResults">
{{ textNoResults }}
</div>
<template v-if="showViewAllRefsEntry">
<div class="divider tw-m-0"/>
<a v-if="selectedTab === 'branches'" class="item" :href="currentRepoLink + '/branches'">{{ textViewAllBranches }}</a>
<a v-if="selectedTab === 'tags'" class="item" :href="currentRepoLink + '/tags'">{{ textViewAllTags }}</a>
</template>
</div>
</div>
</template>
+2 -4
View File
@@ -134,19 +134,17 @@ function getFileBasedOptions(filename: string, lineWrapExts: string[]) {
}
function togglePreviewDisplay(previewable: boolean) {
const previewTab = document.querySelector('a[data-tab="preview"]');
const previewTab = document.querySelector<HTMLElement>('a[data-tab="preview"]');
if (!previewTab) return;
if (previewable) {
const newUrl = (previewTab.getAttribute('data-url') || '').replace(/(.*)\/.*/, `$1/markup`);
previewTab.setAttribute('data-url', newUrl);
previewTab.style.display = '';
} else {
previewTab.style.display = 'none';
// If the "preview" tab was active, user changes the filename to a non-previewable one,
// then the "preview" tab becomes inactive (hidden), so the "write" tab should become active
if (previewTab.classList.contains('active')) {
const writeTab = document.querySelector('a[data-tab="write"]');
const writeTab = document.querySelector<HTMLElement>('a[data-tab="write"]');
writeTab.click();
}
}
+7 -1
View File
@@ -1,5 +1,7 @@
import {applyAreYouSure, initAreYouSure} from '../vendor/jquery.are-you-sure.ts';
import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.ts';
import {queryElems} from '../utils/dom.ts';
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
export function initGlobalFormDirtyLeaveConfirm() {
initAreYouSure(window.jQuery);
@@ -11,7 +13,7 @@ export function initGlobalFormDirtyLeaveConfirm() {
}
export function initGlobalEnterQuickSubmit() {
document.addEventListener('keydown', (e) => {
document.addEventListener('keydown', (e: KeyboardEvent & {target: HTMLElement}) => {
if (e.key !== 'Enter') return;
const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey);
if (hasCtrlOrMeta && e.target.matches('textarea')) {
@@ -27,3 +29,7 @@ export function initGlobalEnterQuickSubmit() {
}
});
}
export function initGlobalComboMarkdownEditor() {
queryElems<HTMLElement>(document, '.combo-markdown-editor:not(.custom-init)', (el) => initComboMarkdownEditor(el));
}
+35 -23
View File
@@ -1,6 +1,5 @@
import '@github/markdown-toolbar-element';
import '@github/text-expander-element';
import $ from 'jquery';
import {attachTribute} from '../tribute.ts';
import {hideElem, showElem, autosize, isElemVisible} from '../../utils/dom.ts';
import {
@@ -23,6 +22,8 @@ import {
} from './EditorMarkdown.ts';
import {DropzoneCustomEventReloadFiles, initDropzone} from '../dropzone.ts';
import {createTippy} from '../../modules/tippy.ts';
import {fomanticQuery} from '../../modules/fomantic/base.ts';
import type EasyMDE from 'easymde';
let elementIdCounter = 0;
@@ -48,18 +49,23 @@ export function validateTextareaNonEmpty(textarea) {
return true;
}
type ComboMarkdownEditorOptions = {
editorHeights?: {minHeight?: string, height?: string, maxHeight?: string},
easyMDEOptions?: EasyMDE.Options,
};
export class ComboMarkdownEditor {
static EventEditorContentChanged = EventEditorContentChanged;
static EventUploadStateChanged = EventUploadStateChanged;
public container : HTMLElement;
// TODO: use correct types to replace these "any" types
options: any;
options: ComboMarkdownEditorOptions;
tabEditor: HTMLElement;
tabPreviewer: HTMLElement;
supportEasyMDE: boolean;
easyMDE: any;
easyMDEToolbarActions: any;
easyMDEToolbarDefault: any;
@@ -71,11 +77,12 @@ export class ComboMarkdownEditor {
dropzone: HTMLElement;
attachedDropzoneInst: any;
previewMode: string;
previewUrl: string;
previewContext: string;
previewMode: string;
constructor(container, options = {}) {
constructor(container, options:ComboMarkdownEditorOptions = {}) {
if (container._giteaComboMarkdownEditor) throw new Error('ComboMarkdownEditor already initialized');
container._giteaComboMarkdownEditor = this;
this.options = options;
this.container = container;
@@ -99,6 +106,10 @@ export class ComboMarkdownEditor {
}
setupContainer() {
this.supportEasyMDE = this.container.getAttribute('data-support-easy-mde') === 'true';
this.previewMode = this.container.getAttribute('data-content-mode');
this.previewUrl = this.container.getAttribute('data-preview-url');
this.previewContext = this.container.getAttribute('data-preview-context');
initTextExpander(this.container.querySelector('text-expander'));
}
@@ -137,12 +148,14 @@ export class ComboMarkdownEditor {
monospaceButton.setAttribute('aria-checked', String(enabled));
});
const easymdeButton = this.container.querySelector('.markdown-switch-easymde');
easymdeButton.addEventListener('click', async (e) => {
e.preventDefault();
this.userPreferredEditor = 'easymde';
await this.switchToEasyMDE();
});
if (this.supportEasyMDE) {
const easymdeButton = this.container.querySelector('.markdown-switch-easymde');
easymdeButton.addEventListener('click', async (e) => {
e.preventDefault();
this.userPreferredEditor = 'easymde';
await this.switchToEasyMDE();
});
}
this.initMarkdownButtonTableAdd();
@@ -187,6 +200,7 @@ export class ComboMarkdownEditor {
setupTab() {
const tabs = this.container.querySelectorAll<HTMLElement>('.tabular.menu > .item');
if (!tabs.length) return;
// Fomantic Tab requires the "data-tab" to be globally unique.
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
@@ -207,11 +221,8 @@ export class ComboMarkdownEditor {
});
});
$(tabs).tab();
fomanticQuery(tabs).tab();
this.previewUrl = this.tabPreviewer.getAttribute('data-preview-url');
this.previewContext = this.tabPreviewer.getAttribute('data-preview-context');
this.previewMode = this.options.previewMode ?? 'comment';
this.tabPreviewer.addEventListener('click', async () => {
const formData = new FormData();
formData.append('mode', this.previewMode);
@@ -219,7 +230,7 @@ export class ComboMarkdownEditor {
formData.append('text', this.value());
const response = await POST(this.previewUrl, {data: formData});
const data = await response.text();
renderPreviewPanelContent($(panelPreviewer), data);
renderPreviewPanelContent(panelPreviewer, data);
});
}
@@ -284,7 +295,7 @@ export class ComboMarkdownEditor {
}
async switchToUserPreference() {
if (this.userPreferredEditor === 'easymde') {
if (this.userPreferredEditor === 'easymde' && this.supportEasyMDE) {
await this.switchToEasyMDE();
} else {
this.switchToTextarea();
@@ -304,7 +315,7 @@ export class ComboMarkdownEditor {
if (this.easyMDE) return;
// EasyMDE's CSS should be loaded via webpack config, otherwise our own styles can not overwrite the default styles.
const {default: EasyMDE} = await import(/* webpackChunkName: "easymde" */'easymde');
const easyMDEOpt = {
const easyMDEOpt: EasyMDE.Options = {
autoDownloadFontAwesome: false,
element: this.textarea,
forceSync: true,
@@ -384,19 +395,20 @@ export class ComboMarkdownEditor {
}
get userPreferredEditor() {
return window.localStorage.getItem(`markdown-editor-${this.options.useScene ?? 'default'}`);
return window.localStorage.getItem(`markdown-editor-${this.previewMode ?? 'default'}`);
}
set userPreferredEditor(s) {
window.localStorage.setItem(`markdown-editor-${this.options.useScene ?? 'default'}`, s);
window.localStorage.setItem(`markdown-editor-${this.previewMode ?? 'default'}`, s);
}
}
export function getComboMarkdownEditor(el) {
if (el instanceof $) el = el[0];
return el?._giteaComboMarkdownEditor;
if (!el) return null;
if (el.length) el = el[0];
return el._giteaComboMarkdownEditor;
}
export async function initComboMarkdownEditor(container: HTMLElement, options = {}) {
export async function initComboMarkdownEditor(container: HTMLElement, options:ComboMarkdownEditorOptions = {}) {
if (!container) {
throw new Error('initComboMarkdownEditor: container is null');
}
+1 -1
View File
@@ -5,7 +5,7 @@ import {fomanticQuery} from '../../modules/fomantic/base.ts';
const {i18n} = window.config;
export function confirmModal({header = '', content = '', confirmButtonColor = 'primary'} = {}) {
export function confirmModal({header = '', content = '', confirmButtonColor = 'primary'} = {}): Promise<boolean> {
return new Promise((resolve) => {
const headerHtml = header ? `<div class="header">${htmlEscape(header)}</div>` : '';
const modal = createElementFromHTML(`
+41 -48
View File
@@ -1,4 +1,3 @@
import $ from 'jquery';
import {htmlEscape} from 'escape-goat';
import {createCodeEditor} from './codeeditor.ts';
import {hideElem, queryElems, showElem, createElementFromHTML} from '../utils/dom.ts';
@@ -6,39 +5,33 @@ import {initMarkupContent} from '../markup/content.ts';
import {attachRefIssueContextPopup} from './contextpopup.ts';
import {POST} from '../modules/fetch.ts';
import {initDropzone} from './dropzone.ts';
import {confirmModal} from './comp/ConfirmModal.ts';
import {applyAreYouSure} from '../vendor/jquery.are-you-sure.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
function initEditPreviewTab($form) {
const $tabMenu = $form.find('.repo-editor-menu');
$tabMenu.find('.item').tab();
const $previewTab = $tabMenu.find('a[data-tab="preview"]');
if ($previewTab.length) {
$previewTab.on('click', async function () {
const $this = $(this);
let context = `${$this.data('context')}/`;
const mode = $this.data('markup-mode') || 'comment';
const $treePathEl = $form.find('input#tree_path');
if ($treePathEl.length > 0) {
context += $treePathEl.val();
}
context = context.substring(0, context.lastIndexOf('/'));
function initEditPreviewTab(elForm: HTMLFormElement) {
const elTabMenu = elForm.querySelector('.repo-editor-menu');
fomanticQuery(elTabMenu.querySelectorAll('.item')).tab();
const formData = new FormData();
formData.append('mode', mode);
formData.append('context', context);
formData.append('text', $form.find('.tab[data-tab="write"] textarea').val());
formData.append('file_path', $treePathEl.val());
try {
const response = await POST($this.data('url'), {data: formData});
const data = await response.text();
const $previewPanel = $form.find('.tab[data-tab="preview"]');
if ($previewPanel.length) {
renderPreviewPanelContent($previewPanel, data);
}
} catch (error) {
console.error('Error:', error);
}
});
}
const elPreviewTab = elTabMenu.querySelector('a[data-tab="preview"]');
const elPreviewPanel = elForm.querySelector('.tab[data-tab="preview"]');
if (!elPreviewTab || !elPreviewPanel) return;
elPreviewTab.addEventListener('click', async () => {
const elTreePath = elForm.querySelector<HTMLInputElement>('input#tree_path');
const previewUrl = elPreviewTab.getAttribute('data-preview-url');
const previewContextRef = elPreviewTab.getAttribute('data-preview-context-ref');
let previewContext = `${previewContextRef}/${elTreePath.value}`;
previewContext = previewContext.substring(0, previewContext.lastIndexOf('/'));
const formData = new FormData();
formData.append('mode', 'file');
formData.append('context', previewContext);
formData.append('text', elForm.querySelector<HTMLTextAreaElement>('.tab[data-tab="write"] textarea').value);
formData.append('file_path', elTreePath.value);
const response = await POST(previewUrl, {data: formData});
const data = await response.text();
renderPreviewPanelContent(elPreviewPanel, data);
});
}
export function initRepoEditor() {
@@ -151,8 +144,8 @@ export function initRepoEditor() {
}
});
const $form = $('.repository.editor .edit.form');
initEditPreviewTab($form);
const elForm = document.querySelector<HTMLFormElement>('.repository.editor .edit.form');
initEditPreviewTab(elForm);
(async () => {
const editor = await createCodeEditor(editArea, filenameInput);
@@ -160,16 +153,16 @@ export function initRepoEditor() {
// Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
// to enable or disable the commit button
const commitButton = document.querySelector<HTMLButtonElement>('#commit-button');
const $editForm = $('.ui.edit.form');
const dirtyFileClass = 'dirty-file';
// Disabling the button at the start
if ($('input[name="page_has_posted"]').val() !== 'true') {
if (document.querySelector<HTMLInputElement>('input[name="page_has_posted"]').value !== 'true') {
commitButton.disabled = true;
}
// Registering a custom listener for the file path and the file content
$editForm.areYouSure({
// FIXME: it is not quite right here (old bug), it causes double-init, the global areYouSure "dirty" class will also be added
applyAreYouSure(elForm, {
silent: true,
dirtyClass: dirtyFileClass,
fieldSelector: ':input:not(.commit-form-wrapper :input)',
@@ -187,24 +180,24 @@ export function initRepoEditor() {
editor.setValue(value);
}
commitButton?.addEventListener('click', (e) => {
commitButton?.addEventListener('click', async (e) => {
// A modal which asks if an empty file should be committed
if (!editArea.value) {
$('#edit-empty-content-modal').modal({
onApprove() {
$('.edit.form').trigger('submit');
},
}).modal('show');
e.preventDefault();
if (await confirmModal({
header: elForm.getAttribute('data-text-empty-confirm-header'),
content: elForm.getAttribute('data-text-empty-confirm-content'),
})) {
elForm.classList.remove('dirty');
elForm.submit();
}
}
});
})();
}
export function renderPreviewPanelContent($previewPanel, data) {
$previewPanel.html(data);
export function renderPreviewPanelContent(previewPanel: Element, content: string) {
previewPanel.innerHTML = content;
initMarkupContent();
const $refIssues = $previewPanel.find('p .ref-issue');
attachRefIssueContextPopup($refIssues);
attachRefIssueContextPopup(previewPanel.querySelectorAll('p .ref-issue'));
}
+1
View File
@@ -90,6 +90,7 @@ function filterRepoFiles(filter) {
const span = document.createElement('span');
// safely escape by using textContent
span.textContent = part;
span.title = span.textContent;
// if the target file path is "abc/xyz", to search "bx", then the matchResult is ['a', 'b', 'c/', 'x', 'yz']
// the matchResult[odd] is matched and highlighted to red.
if (index % 2 === 1) span.classList.add('ui', 'text', 'red');
-5
View File
@@ -414,11 +414,6 @@ export function initRepoPullRequestReview() {
await handleReply(this);
});
const elReviewBox = document.querySelector('.review-box-panel');
if (elReviewBox) {
initComboMarkdownEditor(elReviewBox.querySelector('.combo-markdown-editor'));
}
// The following part is only for diff views
if (!$('.repository.pull.diff').length) return;
-10
View File
@@ -1,5 +1,4 @@
import {hideElem, showElem} from '../utils/dom.ts';
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
export function initRepoRelease() {
document.addEventListener('click', (e) => {
@@ -16,7 +15,6 @@ export function initRepoReleaseNew() {
if (!document.querySelector('.repository.new.release')) return;
initTagNameEditor();
initRepoReleaseEditor();
}
function initTagNameEditor() {
@@ -48,11 +46,3 @@ function initTagNameEditor() {
hideTargetInput(e.target);
});
}
function initRepoReleaseEditor() {
const editor = document.querySelector<HTMLElement>('.repository.new.release .combo-markdown-editor');
if (!editor) {
return;
}
initComboMarkdownEditor(editor);
}
+3 -4
View File
@@ -2,6 +2,7 @@ import {initMarkupContent} from '../markup/content.ts';
import {validateTextareaNonEmpty, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
import {fomanticMobileScreen} from '../modules/fomantic.ts';
import {POST} from '../modules/fetch.ts';
import type {ComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
async function initRepoWikiFormEditor() {
const editArea = document.querySelector<HTMLTextAreaElement>('.repository.wiki .combo-markdown-editor textarea');
@@ -9,7 +10,7 @@ async function initRepoWikiFormEditor() {
const form = document.querySelector('.repository.wiki.new .ui.form');
const editorContainer = form.querySelector<HTMLElement>('.combo-markdown-editor');
let editor;
let editor: ComboMarkdownEditor;
let renderRequesting = false;
let lastContent;
@@ -45,12 +46,10 @@ async function initRepoWikiFormEditor() {
renderEasyMDEPreview();
editor = await initComboMarkdownEditor(editorContainer, {
useScene: 'wiki',
// EasyMDE has some problems of height definition, it has inline style height 300px by default, so we also use inline styles to override it.
// And another benefit is that we only need to write the style once for both editors.
// TODO: Move height style to CSS after EasyMDE removal.
editorHeights: {minHeight: '300px', height: 'calc(100vh - 600px)'},
previewMode: 'wiki',
easyMDEOptions: {
previewRender: (_content, previewTarget) => previewTarget.innerHTML, // disable builtin preview render
toolbar: ['bold', 'italic', 'strikethrough', '|',
@@ -59,7 +58,7 @@ async function initRepoWikiFormEditor() {
'unordered-list', 'ordered-list', '|',
'link', 'image', 'table', 'horizontal-rule', '|',
'preview', 'fullscreen', 'side-by-side', '|', 'gitea-switch-to-textarea',
],
] as any, // to use custom toolbar buttons
},
});
+6 -1
View File
@@ -83,7 +83,11 @@ import {
initGlobalButtons,
initGlobalDeleteButton,
} from './features/common-button.ts';
import {initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
import {
initGlobalComboMarkdownEditor,
initGlobalEnterQuickSubmit,
initGlobalFormDirtyLeaveConfirm,
} from './features/common-form.ts';
initGiteaFomantic();
initDirAuto();
@@ -127,6 +131,7 @@ onDomReady(() => {
initGlobalCopyToClipboardListener,
initGlobalEnterQuickSubmit,
initGlobalFormDirtyLeaveConfirm,
initGlobalComboMarkdownEditor,
initGlobalDeleteButton,
initCommonOrganization,
+2 -2
View File
@@ -196,6 +196,6 @@ export function initAreYouSure($) {
};
}
export function applyAreYouSure(selector: string) {
$(selector).areYouSure();
export function applyAreYouSure(selectorOrEl: string|Element|$, opts = {}) {
$(selectorOrEl).areYouSure(opts);
}