Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ preview = Preview
loading = Loading…
files = Files

code_toggle_wrap = Toggle Code Wrap

error = Error
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
error503 = The server could not complete your request. Please try again later.
Expand Down
1 change: 1 addition & 0 deletions public/assets/img/svg/material-wrap-text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions templates/base/head_script.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly.
mermaidMaxSourceCharacters: {{MermaidMaxSourceCharacters}},
{{/* this global i18n object should only contain general texts. for specialized texts, it should be provided inside the related modules by: (1) API response (2) HTML data-attribute (3) PageData */}}
i18n: {
copy: {{ctx.Locale.Tr "copy"}},
copy_success: {{ctx.Locale.Tr "copy_success"}},
copy_error: {{ctx.Locale.Tr "copy_error"}},
error_occurred: {{ctx.Locale.Tr "error.occurred"}},
Expand All @@ -41,6 +42,7 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly.
modal_confirm: {{ctx.Locale.Tr "modal.confirm"}},
modal_cancel: {{ctx.Locale.Tr "modal.cancel"}},
more_items: {{ctx.Locale.Tr "more_items"}},
code_toggle_wrap: {{ctx.Locale.Tr "code_toggle_wrap"}},
},
};
{{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}}
Expand Down
2 changes: 1 addition & 1 deletion web_src/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
@import "./features/console.css";

@import "./markup/content.css";
@import "./markup/codecopy.css";
@import "./markup/codeblocks.css";
@import "./markup/codepreview.css";
@import "./markup/asciicast.css";

Expand Down
35 changes: 35 additions & 0 deletions web_src/css/markup/codeblocks.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.markup .code-block-buttons {
position: absolute;
top: 8px;
right: 6px;
display: flex;
gap: 4px;
visibility: hidden;
animation: fadeout 0.2s both;
}

/* adjustments for comment content having only 14px font size */
.repository.view.issue .comment-list .comment .markup .code-copy {
right: 5px;
padding: 8px;
}

.markup .code-block-buttons .btn {
background: var(--color-button) !important;
padding: 9px;
border: 1px solid var(--color-light-border);
}

.markup .code-block-buttons .btn:hover {
background: var(--color-hover-opaque) !important;
}

.markup .code-block-buttons .btn[data-active="false"] {
color: var(--color-text-light-3);
}

.markup .code-block-container:hover .code-block-buttons,
.markup .code-block:hover .code-block-buttons {
visibility: visible;
animation: fadein 0.2s both;
}
30 changes: 0 additions & 30 deletions web_src/css/markup/codecopy.css

This file was deleted.

2 changes: 1 addition & 1 deletion web_src/js/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ interface Element {

interface Window {
__webpack_public_path__: string;
config: import('./web_src/js/types.ts').Config;
config: import('./types.ts').Config;
$: typeof import('@types/jquery'),
jQuery: typeof import('@types/jquery'),
htmx: typeof import('htmx.org').default,
Expand Down
51 changes: 51 additions & 0 deletions web_src/js/markup/codeblocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {svg, type SvgName} from '../svg.ts';
import {queryElems, type DOMEvent} from '../utils/dom.ts';

export function makeCodeBlockButton(className: string, name: SvgName): HTMLButtonElement {
const button = document.createElement('button');
button.classList.add(className, 'btn');
button.innerHTML = svg(name, 14);
return button;
}

const getMarkupCodeWrap = () => (localStorage.getItem('wrap-markup-code') || 'false') === 'true';
const setMarkupCodeWrap = (value: boolean) => localStorage.setItem('wrap-markup-code', String(value));

function updateWrap(container: Element, wrap: boolean) {
container.classList.remove(wrap ? 'code-overflow-scroll' : 'code-overflow-wrap');
container.classList.add(wrap ? 'code-overflow-wrap' : 'code-overflow-scroll');
}

export function initMarkupCodeBlocks(elMarkup: HTMLElement): void {
// .markup .code-block code
queryElems(elMarkup, '.code-block code', (el) => {
if (!el.textContent) return;

const copyBtn = makeCodeBlockButton('code-copy', 'octicon-copy');
copyBtn.setAttribute('data-tooltip-content', window.config.i18n.copy);
// remove final trailing newline introduced during HTML rendering
copyBtn.setAttribute('data-clipboard-text', el.textContent.replace(/\r?\n$/, ''));

// we only want to use `.code-block-container` if it exists, no matter `.code-block` exists or not.
const container = el.closest('.code-block-container') ?? el.closest('.code-block');

const wrapBtn = makeCodeBlockButton('code-wrap', 'material-wrap-text');
const wrap = getMarkupCodeWrap();
wrapBtn.setAttribute('data-active', String(wrap));
updateWrap(container, wrap);
Copy link
Member Author

@silverwind silverwind Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes a flicker on page load, but I think it's okay for now. Moving the initial classes to backend will be more work and I don't know how to use the backend user setting storage.


wrapBtn.setAttribute('data-tooltip-content', window.config.i18n.code_toggle_wrap);
wrapBtn.addEventListener('click', (e) => {
const wrap = !getMarkupCodeWrap();
updateWrap(container, wrap);
(e.currentTarget as HTMLButtonElement).setAttribute('data-active', String(wrap));
setMarkupCodeWrap(wrap);
});

const btnContainer = document.createElement('div');
btnContainer.classList.add('code-block-buttons');
btnContainer.append(wrapBtn);
btnContainer.append(copyBtn);
container.append(btnContainer);
});
}
22 changes: 0 additions & 22 deletions web_src/js/markup/codecopy.ts

This file was deleted.

4 changes: 2 additions & 2 deletions web_src/js/markup/content.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {initMarkupCodeMermaid} from './mermaid.ts';
import {initMarkupCodeMath} from './math.ts';
import {initMarkupCodeCopy} from './codecopy.ts';
import {initMarkupCodeBlocks} from './codeblocks.ts';
import {initMarkupRenderAsciicast} from './asciicast.ts';
import {initMarkupTasklist} from './tasklist.ts';
import {registerGlobalSelectorFunc} from '../modules/observer.ts';

// code that runs for all markup content
export function initMarkupContent(): void {
registerGlobalSelectorFunc('.markup', (el: HTMLElement) => {
initMarkupCodeCopy(el);
initMarkupCodeBlocks(el);
initMarkupTasklist(el);
initMarkupCodeMermaid(el);
initMarkupCodeMath(el);
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/markup/mermaid.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {isDarkTheme} from '../utils.ts';
import {makeCodeCopyButton} from './codecopy.ts';
import {makeCodeBlockButton} from './codeblocks.ts';
import {displayError} from './common.ts';
import {queryElems} from '../utils/dom.ts';
import {html, htmlRaw} from '../utils/html.ts';
Expand Down Expand Up @@ -53,7 +53,7 @@ export async function initMarkupCodeMermaid(elMarkup: HTMLElement): Promise<void
mermaidBlock.classList.add('mermaid-block', 'is-loading', 'tw-hidden');
mermaidBlock.append(iframe);

const btn = makeCodeCopyButton();
const btn = makeCodeBlockButton('code-copy', 'octicon-copy');
btn.setAttribute('data-clipboard-text', source);
mermaidBlock.append(btn);

Expand Down
2 changes: 2 additions & 0 deletions web_src/js/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import giteaDoubleChevronLeft from '../../public/assets/img/svg/gitea-double-che
import giteaDoubleChevronRight from '../../public/assets/img/svg/gitea-double-chevron-right.svg';
import giteaEmptyCheckbox from '../../public/assets/img/svg/gitea-empty-checkbox.svg';
import giteaExclamation from '../../public/assets/img/svg/gitea-exclamation.svg';
import materialWrapText from '../../public/assets/img/svg/material-wrap-text.svg';
import octiconArchive from '../../public/assets/img/svg/octicon-archive.svg';
import octiconArrowSwitch from '../../public/assets/img/svg/octicon-arrow-switch.svg';
import octiconBlocked from '../../public/assets/img/svg/octicon-blocked.svg';
Expand Down Expand Up @@ -84,6 +85,7 @@ const svgs = {
'gitea-double-chevron-right': giteaDoubleChevronRight,
'gitea-empty-checkbox': giteaEmptyCheckbox,
'gitea-exclamation': giteaExclamation,
'material-wrap-text': materialWrapText,
'octicon-archive': octiconArchive,
'octicon-arrow-switch': octiconArrowSwitch,
'octicon-blocked': octiconBlocked,
Expand Down
1 change: 1 addition & 0 deletions web_src/svg/material-wrap-text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading