Compare commits

..

No commits in common. "b6818bc0d09b8e918e95dd2a091ab674710833ff" and "7c08031413e54376665257402ea9c04ca414543e" have entirely different histories.

7 changed files with 188 additions and 163 deletions

View File

@ -1228,7 +1228,7 @@ function renderEasterEgg(result: any, args: any): string {
grid-template-columns: 5ch 1ch minmax(0, 1fr); grid-template-columns: 5ch 1ch minmax(0, 1fr);
column-gap: 8px; column-gap: 8px;
padding: 2px 4px; padding: 2px 4px;
margin: 0; margin: 1px 0;
align-items: start; align-items: start;
} }

View File

@ -386,14 +386,6 @@ function renderEditFile(result: any, args: any): string {
return true; return true;
}) })
.sort((a: any, b: any) => { .sort((a: any, b: any) => {
const aIsChange = a.marker === '-' || a.marker === '+';
const bIsChange = b.marker === '-' || b.marker === '+';
// 仅变更行之间优先按 marker 分组(- 在前 + 在后),再按行号
if (aIsChange && bIsChange) {
const markerDelta = (markerOrder[a.marker] ?? 9) - (markerOrder[b.marker] ?? 9);
return markerDelta || (a.lineNo ?? Number.MAX_SAFE_INTEGER) - (b.lineNo ?? Number.MAX_SAFE_INTEGER);
}
// 上下文行或跨类型:按行号优先
const lineDelta = (a.lineNo ?? Number.MAX_SAFE_INTEGER) - (b.lineNo ?? Number.MAX_SAFE_INTEGER); const lineDelta = (a.lineNo ?? Number.MAX_SAFE_INTEGER) - (b.lineNo ?? Number.MAX_SAFE_INTEGER);
return lineDelta || (markerOrder[a.marker] ?? 9) - (markerOrder[b.marker] ?? 9); return lineDelta || (markerOrder[a.marker] ?? 9) - (markerOrder[b.marker] ?? 9);
}) })

View File

@ -1143,16 +1143,20 @@ const RUNTIME_QUEUE_ANIM_EASING = 'cubic-bezier(0.25, 0.8, 0.25, 1)';
const runtimeQueueList = ref<HTMLElement | null>(null); const runtimeQueueList = ref<HTMLElement | null>(null);
const runtimeQueueItemRefs = new Map<string, HTMLElement>(); const runtimeQueueItemRefs = new Map<string, HTMLElement>();
const runtimeQueuePrevIds: string[] = []; const runtimeQueuePrevIds: string[] = [];
const runtimeQueueRenderedIds: string[] = [];
const runtimeQueuePrevRects = new Map<string, DOMRect>(); const runtimeQueuePrevRects = new Map<string, DOMRect>();
const runtimeQueuePrevElements = new Map<string, HTMLElement>(); const runtimeQueuePrevElements = new Map<string, HTMLElement>();
const runtimeQueueGhosts = new Map<string, HTMLElement>(); const runtimeQueueGhosts = new Map<string, HTMLElement>();
const runtimeQueueActiveAnimations = new WeakMap<HTMLElement, Animation>();
const runtimeQueueFallbackTimers = new WeakMap<HTMLElement, number>(); const runtimeQueueFallbackTimers = new WeakMap<HTMLElement, number>();
const runtimeQueueTransitioning = ref(false); const runtimeQueueTransitioning = ref(false);
let runtimeQueueAnimationReady = false; let runtimeQueueAnimationReady = false;
let runtimeQueueTransitionTimer: number | null = null; let runtimeQueueTransitionTimer: number | null = null;
const bindRuntimeQueueItemRef = (id: string) => (el: Element | null) => { const bindRuntimeQueueItemRef = (id: string) => (el: Element | null) => {
if (!id) return; if (!id) {
return;
}
if (el instanceof HTMLElement) { if (el instanceof HTMLElement) {
runtimeQueueItemRefs.set(id, el); runtimeQueueItemRefs.set(id, el);
} else { } else {
@ -1160,21 +1164,31 @@ const bindRuntimeQueueItemRef = (id: string) => (el: Element | null) => {
} }
}; };
const resolveRuntimeQueueOffset = (el: HTMLElement): number => { const stopRuntimeQueueAnimation = (el: HTMLElement) => {
const rectHeight = Math.round(el.getBoundingClientRect().height); const anim = runtimeQueueActiveAnimations.get(el);
if (rectHeight > 0) return rectHeight; if (anim) {
if (el.offsetHeight > 0) return el.offsetHeight; try {
return 34; anim.cancel();
} catch {
// ignore
}
runtimeQueueActiveAnimations.delete(el);
}
const timer = runtimeQueueFallbackTimers.get(el);
if (timer) {
clearTimeout(timer);
runtimeQueueFallbackTimers.delete(el);
}
el.style.removeProperty('transition');
el.style.removeProperty('transform');
el.style.removeProperty('pointer-events');
}; };
const snapshotRuntimeQueueIds = (): string[] =>
runtimeQueuedMessagesForRender.value
.map((entry) => String(entry?.id || '').trim())
.filter((id) => !!id);
const keepRuntimeQueueTransitionCollapsed = () => { const keepRuntimeQueueTransitionCollapsed = () => {
runtimeQueueTransitioning.value = true; runtimeQueueTransitioning.value = true;
if (runtimeQueueTransitionTimer !== null) window.clearTimeout(runtimeQueueTransitionTimer); if (runtimeQueueTransitionTimer !== null) {
window.clearTimeout(runtimeQueueTransitionTimer);
}
runtimeQueueTransitionTimer = window.setTimeout(() => { runtimeQueueTransitionTimer = window.setTimeout(() => {
runtimeQueueTransitioning.value = false; runtimeQueueTransitioning.value = false;
runtimeQueueTransitionTimer = null; runtimeQueueTransitionTimer = null;
@ -1182,158 +1196,197 @@ const keepRuntimeQueueTransitionCollapsed = () => {
}, RUNTIME_QUEUE_ANIM_DURATION + 40); }, RUNTIME_QUEUE_ANIM_DURATION + 40);
}; };
const cancelAnim = (el: HTMLElement) => { const playRuntimeQueueTransform = (
const timer = runtimeQueueFallbackTimers.get(el);
if (timer) { clearTimeout(timer); runtimeQueueFallbackTimers.delete(el); }
el.style.removeProperty('transition');
el.style.removeProperty('transform');
el.style.removeProperty('opacity');
};
/**
* Schedule a CSS transition on an element.
* All calls in the same synchronous batch share one RAF callback,
* so all animations start in the same frame.
*/
const scheduleTransition = (
el: HTMLElement, el: HTMLElement,
props: string[], fromY: number,
from: Record<string, string>, toY: number,
to: Record<string, string>,
done?: () => void done?: () => void
) => { ) => {
if (Math.abs(fromY - toY) < 0.5) {
stopRuntimeQueueAnimation(el);
el.style.removeProperty('transition');
el.style.removeProperty('transform');
done?.();
return;
}
keepRuntimeQueueTransitionCollapsed();
stopRuntimeQueueAnimation(el);
const from = `translateY(${fromY}px)`;
const to = `translateY(${toY}px)`;
const finish = () => { const finish = () => {
el.style.removeProperty('transition'); el.style.removeProperty('transition');
props.forEach((p) => el.style.removeProperty(p)); el.style.removeProperty('transform');
done?.(); done?.();
}; };
// Apply from state immediately (no transition)
el.style.transition = 'none'; el.style.transition = 'none';
Object.entries(from).forEach(([k, v]) => { el.style.setProperty(k, v); }); el.style.transform = from;
// Defer to next frame: enable transition + apply to state
// All scheduleTransition calls in the same microtask share this RAF if (typeof el.animate === 'function') {
//
void el.offsetHeight;
const animation = el.animate([{ transform: from }, { transform: to }], {
duration: RUNTIME_QUEUE_ANIM_DURATION,
easing: RUNTIME_QUEUE_ANIM_EASING,
fill: 'both'
});
runtimeQueueActiveAnimations.set(el, animation);
animation.addEventListener(
'finish',
() => {
if (runtimeQueueActiveAnimations.get(el) === animation) {
runtimeQueueActiveAnimations.delete(el);
}
finish();
},
{ once: true }
);
animation.addEventListener(
'cancel',
() => {
if (runtimeQueueActiveAnimations.get(el) === animation) {
runtimeQueueActiveAnimations.delete(el);
}
},
{ once: true }
);
return;
}
requestAnimationFrame(() => { requestAnimationFrame(() => {
el.style.transition = props el.style.transition = `transform ${RUNTIME_QUEUE_ANIM_DURATION}ms ${RUNTIME_QUEUE_ANIM_EASING}`;
.map((p) => `${p} ${RUNTIME_QUEUE_ANIM_DURATION}ms ${RUNTIME_QUEUE_ANIM_EASING}`) el.style.transform = to;
.join(', '); const timer = window.setTimeout(() => {
Object.entries(to).forEach(([k, v]) => { el.style.setProperty(k, v); }); runtimeQueueFallbackTimers.delete(el);
const timer = window.setTimeout(finish, RUNTIME_QUEUE_ANIM_DURATION + 40); finish();
}, RUNTIME_QUEUE_ANIM_DURATION + 24);
runtimeQueueFallbackTimers.set(el, timer); runtimeQueueFallbackTimers.set(el, timer);
}); });
}; };
/* ═══════════════════ FLIP 动画 ═══════════════════ */ const resolveRuntimeQueueOffset = (el: HTMLElement): number => {
const rectHeight = Math.round(el.getBoundingClientRect().height);
if (rectHeight > 0) {
return rectHeight;
}
if (el.offsetHeight > 0) {
return el.offsetHeight;
}
return 34;
};
// IDonBeforeUpdate const snapshotRuntimeQueueIds = (): string[] => {
let runtimeQueueLastRenderedIds: string[] = []; return runtimeQueuedMessagesForRender.value
.map((entry) => String(entry?.id || '').trim())
.filter((id) => !!id);
};
const spawnRuntimeQueueLeaveGhost = (id: string) => {
const sourceEl = runtimeQueuePrevElements.get(id);
const sourceRect = runtimeQueuePrevRects.get(id);
const listEl = runtimeQueueList.value;
if (!sourceEl || !sourceRect || !listEl || !listEl.isConnected) {
return;
}
const existingGhost = runtimeQueueGhosts.get(id);
if (existingGhost) {
stopRuntimeQueueAnimation(existingGhost);
existingGhost.remove();
runtimeQueueGhosts.delete(id);
}
const ghost = sourceEl.cloneNode(true) as HTMLElement;
const listRect = listEl.getBoundingClientRect();
const top = sourceRect.top - listRect.top;
const width = sourceRect.width > 0 ? sourceRect.width : listRect.width;
ghost.style.position = 'absolute';
ghost.style.left = '0';
ghost.style.top = `${Math.round(top)}px`;
ghost.style.width = `${Math.round(width)}px`;
ghost.style.pointerEvents = 'none';
ghost.style.zIndex = '0';
ghost.style.margin = '0';
ghost.querySelectorAll('button').forEach((button) => {
if (button instanceof HTMLButtonElement) {
button.disabled = true;
}
});
listEl.appendChild(ghost);
runtimeQueueGhosts.set(id, ghost);
const offset = resolveRuntimeQueueOffset(ghost);
playRuntimeQueueTransform(ghost, 0, offset, () => {
runtimeQueueGhosts.delete(id);
ghost.remove();
});
};
const animateRuntimeQueueEnter = (el: HTMLElement) => {
const offset = resolveRuntimeQueueOffset(el);
playRuntimeQueueTransform(el, offset, 0);
};
onBeforeUpdate(() => { onBeforeUpdate(() => {
// ID snapshotRuntimeQueueIds() runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length, ...runtimeQueueRenderedIds);
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length, ...runtimeQueueLastRenderedIds);
runtimeQueuePrevRects.clear(); runtimeQueuePrevRects.clear();
runtimeQueuePrevElements.clear(); runtimeQueuePrevElements.clear();
runtimeQueueLastRenderedIds.forEach((id) => {
runtimeQueueRenderedIds.forEach((id) => {
const el = runtimeQueueItemRefs.get(id); const el = runtimeQueueItemRefs.get(id);
if (!el?.isConnected) return; if (!el?.isConnected) {
return;
}
runtimeQueuePrevRects.set(id, el.getBoundingClientRect()); runtimeQueuePrevRects.set(id, el.getBoundingClientRect());
runtimeQueuePrevElements.set(id, el); runtimeQueuePrevElements.set(id, el);
}); });
}); });
onUpdated(() => { onUpdated(() => {
const list = runtimeQueueList.value;
const currentIds = snapshotRuntimeQueueIds(); const currentIds = snapshotRuntimeQueueIds();
// First render: just bootstrap tracking state
if (!runtimeQueueAnimationReady) { if (!runtimeQueueAnimationReady) {
runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length, ...currentIds);
runtimeQueueAnimationReady = true; runtimeQueueAnimationReady = true;
runtimeQueueLastRenderedIds = [...currentIds];
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length); runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
runtimeQueuePrevRects.clear(); runtimeQueuePrevRects.clear();
runtimeQueuePrevElements.clear(); runtimeQueuePrevElements.clear();
return; return;
} }
keepRuntimeQueueTransitionCollapsed();
const previousIdSet = new Set(runtimeQueuePrevIds); const previousIdSet = new Set(runtimeQueuePrevIds);
const currentIdSet = new Set(currentIds); const currentIdSet = new Set(currentIds);
const enteringIds = currentIds.filter((id) => !previousIdSet.has(id)); const enteringIdSet = new Set(currentIds.filter((id) => !previousIdSet.has(id)));
const leavingIds = runtimeQueuePrevIds.filter((id) => !currentIdSet.has(id)); const leavingIds = runtimeQueuePrevIds.filter((id) => !currentIdSet.has(id));
if (!enteringIds.length && !leavingIds.length) return;
// Leave: create ghost clones at old positions
leavingIds.forEach((id) => { leavingIds.forEach((id) => {
const src = runtimeQueuePrevElements.get(id); spawnRuntimeQueueLeaveGhost(id);
const rect = runtimeQueuePrevRects.get(id);
if (!src || !rect || !list?.isConnected) return;
cancelAnim(src);
// Clean up any stale ghost with same id
const existing = runtimeQueueGhosts.get(id);
if (existing) { cancelAnim(existing); existing.remove(); runtimeQueueGhosts.delete(id); }
const ghost = src.cloneNode(true) as HTMLElement;
const lr = list.getBoundingClientRect();
ghost.style.position = 'absolute';
ghost.style.left = '0';
ghost.style.top = `${Math.round(rect.top - lr.top)}px`;
ghost.style.width = `${Math.round(rect.width > 0 ? rect.width : lr.width)}px`;
ghost.style.pointerEvents = 'none';
ghost.style.zIndex = '0';
ghost.style.margin = '0';
ghost.style.opacity = '1';
ghost.querySelectorAll('button').forEach((b) => {
if (b instanceof HTMLButtonElement) b.disabled = true;
});
list.appendChild(ghost);
runtimeQueueGhosts.set(id, ghost);
// Schedule ghost sink shares RAF with item animations below
const offset = resolveRuntimeQueueOffset(ghost);
scheduleTransition(ghost, ['transform'],
{ transform: 'translateY(0)' },
{ transform: `translateY(${offset}px)` },
() => { runtimeQueueGhosts.delete(id); ghost.remove(); }
);
runtimeQueueItemRefs.delete(id); runtimeQueueItemRefs.delete(id);
}); });
// Remaining items: apply FLIP transforms
currentIds.forEach((id) => { currentIds.forEach((id) => {
const el = runtimeQueueItemRefs.get(id); const el = runtimeQueueItemRefs.get(id);
if (!el?.isConnected) return; if (!el?.isConnected) {
cancelAnim(el);
if (enteringIds.includes(id)) {
// New item: rise from below
const offset = resolveRuntimeQueueOffset(el);
scheduleTransition(el, ['transform'],
{ transform: `translateY(${offset}px)` },
{ transform: 'translateY(0)' }
);
return; return;
} }
const prevRect = runtimeQueuePrevRects.get(id);
// Existing item: invert the position delta if (!prevRect || enteringIdSet.has(id)) {
const prev = runtimeQueuePrevRects.get(id); animateRuntimeQueueEnter(el);
if (!prev) return; return;
const next = el.getBoundingClientRect(); }
const deltaY = prev.top - next.top; const nextRect = el.getBoundingClientRect();
if (Math.abs(deltaY) < 0.5) return; const deltaY = prevRect.top - nextRect.top;
scheduleTransition(el, ['transform'], if (Math.abs(deltaY) < 0.5) {
{ transform: `translateY(${deltaY}px)` }, return;
{ transform: 'translateY(0)' } }
); playRuntimeQueueTransform(el, deltaY, 0);
}); });
runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length, ...currentIds);
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length); runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
runtimeQueuePrevRects.clear(); runtimeQueuePrevRects.clear();
runtimeQueuePrevElements.clear(); runtimeQueuePrevElements.clear();
// ID onBeforeUpdate 使
runtimeQueueLastRenderedIds = [...currentIds];
}); });
const hasComposerContent = computed(() => { const hasComposerContent = computed(() => {
@ -1786,20 +1839,20 @@ onBeforeUnmount(() => {
editor.value?.destroy(); editor.value?.destroy();
runtimeQueueItemRefs.forEach((el) => { runtimeQueueItemRefs.forEach((el) => {
if (el) { if (el) {
cancelAnim(el); stopRuntimeQueueAnimation(el);
} }
}); });
runtimeQueueGhosts.forEach((ghost) => { runtimeQueueGhosts.forEach((ghost) => {
cancelAnim(ghost); stopRuntimeQueueAnimation(ghost);
ghost.remove(); ghost.remove();
}); });
runtimeQueueGhosts.clear(); runtimeQueueGhosts.clear();
runtimeQueueItemRefs.clear(); runtimeQueueItemRefs.clear();
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length); runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length);
runtimeQueuePrevRects.clear(); runtimeQueuePrevRects.clear();
runtimeQueuePrevElements.clear(); runtimeQueuePrevElements.clear();
runtimeQueueAnimationReady = false; runtimeQueueAnimationReady = false;
runtimeQueueLastRenderedIds = [];
if (runtimeQueueTransitionTimer !== null) { if (runtimeQueueTransitionTimer !== null) {
window.clearTimeout(runtimeQueueTransitionTimer); window.clearTimeout(runtimeQueueTransitionTimer);
runtimeQueueTransitionTimer = null; runtimeQueueTransitionTimer = null;

View File

@ -79,14 +79,12 @@ const props = withDefaults(
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'close'): void; (e: 'close'): void;
(e: 'confirm', list: string[]): void; (e: 'confirm', list: string[]): void;
(e: 'local-files', files: File[] | null): void; (e: 'local-files', files: FileList | null): void;
}>(); }>();
const selectedSet = ref<Set<string>>(new Set(props.initialSelected || [])); const selectedSet = ref<Set<string>>(new Set(props.initialSelected || []));
const localInput = ref<HTMLInputElement | null>(null); const localInput = ref<HTMLInputElement | null>(null);
let ignoreBackdropClickUntil = 0;
watch( watch(
() => props.initialSelected, () => props.initialSelected,
(val) => { (val) => {
@ -108,35 +106,25 @@ const toggle = (path: string) => {
selectedSet.value = set; selectedSet.value = set;
}; };
const close = () => { const close = () => emit('close');
if (Date.now() < ignoreBackdropClickUntil) return;
emit('close');
};
const confirm = () => emit('confirm', Array.from(selectedSet.value)); const confirm = () => emit('confirm', Array.from(selectedSet.value));
const triggerLocal = () => { const triggerLocal = () => {
if (props.uploading) return; if (props.uploading) return;
// 500ms backdrop click touch 穿 overlay
ignoreBackdropClickUntil = Date.now() + 500;
localInput.value?.click(); localInput.value?.click();
}; };
const onLocalChange = (event: Event) => { const onLocalChange = (event: Event) => {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
const rawFiles = target?.files; const files = target?.files || null;
// WebView input value FileList
const files = rawFiles && rawFiles.length ? Array.from(rawFiles) : null;
if (!files || files.length === 0) { if (!files || files.length === 0) {
console.warn('[ImagePicker] change fired but files empty — likely WebView compat issue'); console.warn('[ImagePicker] change fired but files empty — likely WebView compat issue');
emit('local-files', null);
} else {
emit('local-files', files);
} }
// WebView files files emit('local-files', files);
setTimeout(() => { if (target) {
if (target) target.value = ''; target.value = '';
}, 200); }
}; };
const previewUrl = (path: string) => `/api/gui/files/download?path=${encodeURIComponent(path)}`; const previewUrl = (path: string) => `/api/gui/files/download?path=${encodeURIComponent(path)}`;

View File

@ -764,7 +764,7 @@ const formatTime = (value: string) => {
.diff-line { .diff-line {
padding: 2px 4px; padding: 2px 4px;
margin: 0; margin: 1px 0;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
} }

View File

@ -81,14 +81,12 @@ const props = withDefaults(
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'close'): void; (e: 'close'): void;
(e: 'confirm', list: string[]): void; (e: 'confirm', list: string[]): void;
(e: 'local-files', files: File[] | null): void; (e: 'local-files', files: FileList | null): void;
}>(); }>();
const selectedSet = ref<Set<string>>(new Set(props.initialSelected || [])); const selectedSet = ref<Set<string>>(new Set(props.initialSelected || []));
const localInput = ref<HTMLInputElement | null>(null); const localInput = ref<HTMLInputElement | null>(null);
let ignoreBackdropClickUntil = 0;
watch( watch(
() => props.initialSelected, () => props.initialSelected,
(val) => { (val) => {
@ -110,25 +108,18 @@ const toggle = (path: string) => {
selectedSet.value = set; selectedSet.value = set;
}; };
const close = () => { const close = () => emit('close');
if (Date.now() < ignoreBackdropClickUntil) return;
emit('close');
};
const confirm = () => emit('confirm', Array.from(selectedSet.value)); const confirm = () => emit('confirm', Array.from(selectedSet.value));
const triggerLocal = () => { const triggerLocal = () => {
if (props.uploading) return; if (props.uploading) return;
// 500ms backdrop click touch 穿 overlay
ignoreBackdropClickUntil = Date.now() + 500;
localInput.value?.click(); localInput.value?.click();
}; };
const onLocalChange = (event: Event) => { const onLocalChange = (event: Event) => {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
const rawFiles = target?.files; const files = target?.files || null;
// WebView input value FileList
const files = rawFiles && rawFiles.length ? Array.from(rawFiles) : null;
if (!files || files.length === 0) { if (!files || files.length === 0) {
console.warn('[VideoPicker] change fired but files empty — likely WebView compat issue'); console.warn('[VideoPicker] change fired but files empty — likely WebView compat issue');
} }

View File

@ -39,9 +39,13 @@
max-width: 90%; max-width: 90%;
margin: 0; margin: 0;
overflow: visible; overflow: visible;
border-top: 1px solid var(--runtime-queue-border);
border-right: 1px solid var(--runtime-queue-border);
border-bottom: none;
border-left: 1px solid var(--runtime-queue-border);
border-top-left-radius: 12px;
border-top-right-radius: 12px;
background: transparent; background: transparent;
border: none;
border-radius: 0;
box-shadow: none; box-shadow: none;
pointer-events: auto; pointer-events: auto;
} }
@ -66,10 +70,7 @@
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 6px 10px 6px 12px; padding: 6px 10px 6px 12px;
border-left: 1px solid var(--runtime-queue-border); border: none;
border-right: 1px solid var(--runtime-queue-border);
border-top: 1px solid var(--runtime-queue-border);
border-bottom: none;
border-radius: 0; border-radius: 0;
background: var(--runtime-queue-bg); background: var(--runtime-queue-bg);
overflow: hidden; overflow: hidden;