Files
gitea/web_src/css/modules/animations.css
silverwind b7e95cc48c feat: add copy button to action step header, improve other copy buttons (#37744)
- Adds a copy button to each action step header that copies the step's
rendered log output to clipboard.
- Extract a shared `copyToClipboard(target, content)` helper in
`clipboard.ts` that adds SVG success/failure feedback.
- `is-loading` height for the new helper is sourced from
`--loading-size`.
- Change actions log timestamp format to include seconds.

The indented-markdown code-block fix has moved to #37748.

<img width="244" height="165" alt="copystep"
src="https://github.com/user-attachments/assets/ce286b51-f77b-4d82-b161-ca0aa7ec4fdc"
/>

<img width="187" height="150" alt="copybt"
src="https://github.com/user-attachments/assets/5366b290-b776-496d-8dd4-58d5fa60be92"
/>

Fixes: https://github.com/go-gitea/gitea/issues/26116

---
This PR was written with the help of Claude Opus 4.7

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
2026-05-21 07:39:09 +00:00

120 lines
2.4 KiB
CSS

@keyframes isloadingspin {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
.is-loading {
pointer-events: none !important;
position: relative !important;
}
.is-loading > * {
opacity: 0.3;
}
.btn.is-loading > *,
.btn-octicon.is-loading > *,
.button.is-loading > * {
opacity: 0;
}
.is-loading::after {
content: "";
position: absolute;
display: block;
left: 50%;
top: 50%;
height: var(--loading-size, min(4em, 66.6%));
width: fit-content; /* compat: safari - https://bugs.webkit.org/show_bug.cgi?id=267625 */
aspect-ratio: 1;
transform: translate(-50%, -50%);
animation: isloadingspin 1000ms infinite linear;
border-width: 3px;
border-style: solid;
border-color: var(--color-secondary) var(--color-secondary) var(--color-secondary-dark-8) var(--color-secondary-dark-8);
border-radius: var(--border-radius-full);
}
.is-loading.loading-icon-2px::after {
border-width: 2px;
}
.is-loading.loading-icon-3px::after {
border-width: 3px;
}
/* for single form button, the loading state should be on the button, but not go semi-transparent, just replace the text on the button with the loader. */
form.single-button-form.is-loading > * {
opacity: 1;
}
form.single-button-form.is-loading .button {
color: transparent;
}
.markup pre.is-loading,
.editor-loading.is-loading {
height: var(--height-loading);
}
.markup .is-loading > * {
visibility: hidden;
}
.markup .is-loading {
color: transparent;
background: transparent;
}
/* TODO: not needed, use "is-loading loading-icon-2px" instead */
code.language-math.is-loading::after {
padding: 0;
border-width: 2px;
width: 1.25rem;
height: 1.25rem;
}
@keyframes fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* 1p5 means 1-point-5. it can't use "pulse" here, otherwise the animation is not right (maybe due to some conflicts */
@keyframes pulse-1p5 {
0% {
transform: scale(1);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
/* pulse animation for scale(1.5) in 200ms */
.pulse-1p5-200 {
animation: pulse-1p5 200ms linear;
}
.ui.modal,
.ui.dimmer.transition {
animation-name: fadein;
animation-duration: 100ms;
animation-timing-function: ease-in-out;
}
.rotate-clockwise {
animation: rotate-clockwise-keyframes 1s linear infinite;
}
@keyframes rotate-clockwise-keyframes {
100% {
transform: rotate(360deg);
}
}