fix(desktop): improve ad marquee and hide windows consoles

This commit is contained in:
leokun
2026-09-05 00:36:11 +08:00
parent 20fdea8fb3
commit 7484bb9e07
3 changed files with 65 additions and 15 deletions
+30 -13
View File
@@ -21,24 +21,41 @@ type OverflowTextProps = {
}; };
function OverflowText({ children, className }: OverflowTextProps) { function OverflowText({ children, className }: OverflowTextProps) {
const container = useRef<HTMLDivElement>(null);
const text = useRef<HTMLSpanElement>(null); const text = useRef<HTMLSpanElement>(null);
const [overflowing, setOverflowing] = useState(false); const [overflowing, setOverflowing] = useState(false);
useLayoutEffect(() => { useLayoutEffect(() => {
const element = text.current; const viewport = container.current;
if (!element) return; if (!viewport) return;
const update = () => setOverflowing(element.scrollWidth > element.clientWidth + 1);
const update = () => {
const element = text.current;
if (!element) return;
setOverflowing(element.scrollWidth > viewport.clientWidth + 1);
};
update(); update();
const observer = new ResizeObserver(update); const observer = new ResizeObserver(update);
observer.observe(element); observer.observe(viewport);
if (element.parentElement) observer.observe(element.parentElement); if (text.current) observer.observe(text.current);
return () => observer.disconnect(); let disposed = false;
}, [children]); void document.fonts?.ready.then(() => {
if (!disposed) update();
});
return () => {
disposed = true;
observer.disconnect();
};
}, [children, overflowing]);
if (!overflowing) return <span ref={text} className={className}>{children}</span>; return <div ref={container} className={styles.menuOverflow}>
return <Marquee className={styles.menuMarquee} repeat loop={0} speed={4} gap={24}> {overflowing
<span ref={text} className={className}>{children}</span> ? <Marquee className={styles.menuMarquee} repeat loop={0} speed={4} gap={24}>
</Marquee>; <span ref={text} className={className}>{children}</span>
</Marquee>
: <span ref={text} className={className}>{children}</span>}
</div>;
} }
export function AdMenu({ ads, activeAdId, dismissingAdId, readAdIds, triggerRefs, onOpen, onDismiss }: AdMenuProps) { export function AdMenu({ ads, activeAdId, dismissingAdId, readAdIds, triggerRefs, onOpen, onDismiss }: AdMenuProps) {
@@ -60,10 +77,10 @@ export function AdMenu({ ads, activeAdId, dismissingAdId, readAdIds, triggerRefs
> >
{!readAdIds.has(ad.id) && <span className={styles.unreadDot} aria-hidden="true" />} {!readAdIds.has(ad.id) && <span className={styles.unreadDot} aria-hidden="true" />}
<img className={styles.menuImage} src={ad.target.imageUrl} alt="" /> <img className={styles.menuImage} src={ad.target.imageUrl} alt="" />
<span className={styles.menuCopy}> <div className={styles.menuCopy}>
<OverflowText className={styles.menuTitle}>{ad.target.title}</OverflowText> <OverflowText className={styles.menuTitle}>{ad.target.title}</OverflowText>
<OverflowText className={styles.menuSubtitle}>{ad.target.description}</OverflowText> <OverflowText className={styles.menuSubtitle}>{ad.target.description}</OverflowText>
</span> </div>
</button> </button>
<button <button
type="button" type="button"
@@ -84,7 +84,9 @@
} }
.menuCopy { .menuCopy {
flex: 1 1 0;
min-width: 0; min-width: 0;
overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1px; gap: 1px;
@@ -98,18 +100,37 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.menuOverflow {
flex: 0 0 auto;
width: 100%;
min-width: 0;
overflow: hidden;
}
.menuMarquee { .menuMarquee {
display: block;
flex: 0 0 auto;
width: 100%;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
} }
.menuMarquee .menuTitle, .menuMarquee .menuTitle,
.menuMarquee .menuSubtitle { .menuMarquee .menuSubtitle {
flex: 0 0 auto;
width: max-content; width: max-content;
min-width: max-content;
max-width: none;
overflow: visible; overflow: visible;
text-overflow: clip; text-overflow: clip;
} }
@media (prefers-reduced-motion: reduce) {
.menuMarquee [data-rcm-t] {
animation-play-state: running !important;
}
}
.menuTitle { .menuTitle {
font-size: type.$font-size-xs; font-size: type.$font-size-xs;
font-weight: 500; font-weight: 500;
+14 -2
View File
@@ -4,6 +4,14 @@ use tokio::process::Command;
use crate::{Error, Result}; use crate::{Error, Result};
#[cfg(windows)]
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
#[cfg(windows)]
fn hide_console(command: &mut Command) {
command.creation_flags(CREATE_NO_WINDOW);
}
pub async fn terminate_cursor() -> Result<()> { pub async fn terminate_cursor() -> Result<()> {
terminate_platform_cursor().await terminate_platform_cursor().await
} }
@@ -42,7 +50,9 @@ async fn terminate_unix_process(name: &str) -> Result<()> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
async fn terminate_platform_cursor() -> Result<()> { async fn terminate_platform_cursor() -> Result<()> {
let processes = Command::new("tasklist") let mut list = Command::new("tasklist");
hide_console(&mut list);
let processes = list
.args(["/FI", "IMAGENAME eq Cursor.exe", "/NH", "/FO", "CSV"]) .args(["/FI", "IMAGENAME eq Cursor.exe", "/NH", "/FO", "CSV"])
.output() .output()
.await?; .await?;
@@ -57,7 +67,9 @@ async fn terminate_platform_cursor() -> Result<()> {
{ {
return Ok(()); return Ok(());
} }
let terminated = Command::new("taskkill") let mut kill = Command::new("taskkill");
hide_console(&mut kill);
let terminated = kill
.args(["/F", "/T", "/IM", "Cursor.exe"]) .args(["/F", "/T", "/IM", "Cursor.exe"])
.status() .status()
.await?; .await?;