From 1d74f29c2a46960e267f5b5bee38ce6ca2e5155a Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 13:55:52 +0800 Subject: [PATCH 01/12] copy(search): shorten mobile search placeholder --- src/i18n.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n.tsx b/src/i18n.tsx index ca3b7d7..9780da4 100644 --- a/src/i18n.tsx +++ b/src/i18n.tsx @@ -21,7 +21,7 @@ const zhDict: Dict = { popular: "热门资料", search: "搜索", searchPlaceholder: "搜索资料...", - searchPanelPlaceholder: "搜索 PPT、影片、海报、公告、教程、文...", + searchPanelPlaceholder: "搜索资料...", searchNow: "立即搜索资料", searchSubmit: "搜索", cancel: "取消", @@ -148,7 +148,7 @@ const enDict: Dict = { popular: "Popular", search: "Search", searchPlaceholder: "Search resources...", - searchPanelPlaceholder: "Search PPT, videos, posters, news, guides...", + searchPanelPlaceholder: "Search assets...", searchNow: "Search now", searchSubmit: "Search", cancel: "Cancel", From cc9f0a5730bd88c33facd6f476d0c6f318a2e869 Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 15:42:13 +0800 Subject: [PATCH 02/12] fix(stream): preserve filename extension in narrow bubbles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the file bubble wrapped JS middle-ellipsis in CSS truncate, so narrow containers silently clipped the tail+extension, leaving e.g. "25cb264a-e06…." instead of "25cb264a-e06…811a.jpg". Split the displayed name into a shrinking head (with CSS truncate) and a non-shrinking tail (last 4 base chars + extension). The browser now decides how much head to clip while the suffix is always visible. --- .../messageStream/bubbles/FileDocBubble.tsx | 20 +++++++++++++------ .../messageStream/utils/filenameDisplay.ts | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/components/messageStream/bubbles/FileDocBubble.tsx b/src/components/messageStream/bubbles/FileDocBubble.tsx index 42837ca..de9366a 100644 --- a/src/components/messageStream/bubbles/FileDocBubble.tsx +++ b/src/components/messageStream/bubbles/FileDocBubble.tsx @@ -5,10 +5,7 @@ import { useI18n } from "../../../i18n"; import type { Attachment, Post } from "../../../types/post"; import { downloadAttachment } from "../utils/downloadFile"; import { fileIcon } from "../utils/fileIcon"; -import { - filenameWithExtension, - middleEllipsisFilename, -} from "../utils/filenameDisplay"; +import { filenameWithExtension, splitFilename } from "../utils/filenameDisplay"; import { formatBytes } from "../utils/formatBytes"; import { postDisplayText } from "../utils/postText"; import { CollapsibleText } from "../CollapsibleText"; @@ -60,10 +57,21 @@ function AttachmentRow({ postId, att }: { postId: string; att: Attachment }) { )}
- {middleEllipsisFilename(displayFilename)} + {(() => { + const { base, ext } = splitFilename(displayFilename); + const tailChars = Math.min(4, base.length); + const head = base.slice(0, base.length - tailChars); + const tail = base.slice(base.length - tailChars) + ext; + return ( + <> + {head} + {tail} + + ); + })()}
{isDownloading ? t("downloading") : formatBytes(att.sizeBytes)} diff --git a/src/components/messageStream/utils/filenameDisplay.ts b/src/components/messageStream/utils/filenameDisplay.ts index e9da4ff..8a58b95 100644 --- a/src/components/messageStream/utils/filenameDisplay.ts +++ b/src/components/messageStream/utils/filenameDisplay.ts @@ -51,7 +51,7 @@ export function middleEllipsisFilename( return `${base.slice(0, headLength)}${ellipsis}${base.slice(-tailLength)}${ext}`; } -function splitFilename(filename: string): { base: string; ext: string } { +export function splitFilename(filename: string): { base: string; ext: string } { const dotIndex = filename.lastIndexOf("."); if (!hasFileExtension(filename)) return { base: filename, ext: "" }; return { From 9bef178bc8c60904769da51314efe29861270027 Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 15:43:30 +0800 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20=E5=A4=A7=E5=9B=BE=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E5=99=A8=E6=94=AF=E6=8C=81=20iOS=20=E9=95=BF=E6=8C=89?= =?UTF-8?q?"=E5=AD=98=E5=82=A8=E5=88=B0=E7=85=A7=E7=89=87"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 去掉全尺寸图上的 select-none 并显式设 -webkit-touch-callout:default,使 iOS Safari 长按图片能弹出原生「存储到照片」菜单(保存的是 current.url 全图)。 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/messageStream/overlays/ImageLightbox.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/messageStream/overlays/ImageLightbox.tsx b/src/components/messageStream/overlays/ImageLightbox.tsx index fc5f32a..d14d51d 100644 --- a/src/components/messageStream/overlays/ImageLightbox.tsx +++ b/src/components/messageStream/overlays/ImageLightbox.tsx @@ -279,11 +279,12 @@ function LightboxView({ touchStartX.current = null; }} > + {/* No select-none / touch-callout:none here so iOS Safari's native + long-press menu ("Save in Photos") works on the full-size image. */} {current.filename}
From 40d64f129313f26133e427c3707101c91c95bb82 Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 15:43:30 +0800 Subject: [PATCH 04/12] =?UTF-8?q?fix:=20=E9=93=BE=E6=8E=A5=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E5=BC=BA=E8=B0=83=E8=89=B2=E6=8C=89=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=E5=85=9C=E5=BA=95,=E8=85=BE=E8=AE=AF=E4=BC=9A=E8=AE=AE?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E7=94=A8=E9=BB=91=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 部分站点(如 meeting.tencent.com)返回 themeColor=#000000,在深色气泡上看不见。 按域名覆盖为品牌金色,其余仍优先用 themeColor。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../messageStream/LinkPreviewCard.tsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/components/messageStream/LinkPreviewCard.tsx b/src/components/messageStream/LinkPreviewCard.tsx index 3858464..d10a5a1 100644 --- a/src/components/messageStream/LinkPreviewCard.tsx +++ b/src/components/messageStream/LinkPreviewCard.tsx @@ -1,5 +1,27 @@ import type { LinkPreview } from "../../types/post"; +const LINK_ACCENT = "#eeb726"; + +const DOMAIN_ACCENT_OVERRIDES: Array<[string, string]> = [ + // Tencent Meeting returns themeColor="#000000", which disappears on the + // dark bubble surface. Keep it consistent with inline link color instead. + ["meeting.tencent.com", LINK_ACCENT], +]; + +function linkPreviewAccent(preview: LinkPreview): string { + const url = preview.canonicalUrl || preview.url; + try { + const host = new URL(url).hostname.replace(/^www\./, ""); + const match = DOMAIN_ACCENT_OVERRIDES.find( + ([domain]) => host === domain || host.endsWith(`.${domain}`), + ); + if (match) return match[1]; + } catch { + // Ignore malformed preview URLs and fall back below. + } + return preview.themeColor || LINK_ACCENT; +} + /** * Telegram-style rich preview card for a single URL embedded in a post. * @@ -8,7 +30,7 @@ import type { LinkPreview } from "../../types/post"; * that opens `canonicalUrl` in a new tab. */ export function LinkPreviewCard({ preview }: { preview: LinkPreview }) { - const accent = preview.themeColor || "#EEB726"; + const accent = linkPreviewAccent(preview); const hasUsefulText = preview.title.length > 0 || preview.description.length > 0; if (!hasUsefulText && !preview.imageUrl) return null; From 07f040a5495d2827f689dbcb09864d93a897a261 Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 16:54:03 +0800 Subject: [PATCH 05/12] =?UTF-8?q?fix(video):=20=E5=85=A8=E5=B1=8F=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=90=8E=E5=86=85=E5=B5=8C=E8=BF=9B=E5=BA=A6=E6=9D=A1?= =?UTF-8?q?=E8=B7=B3=E5=8F=98=E8=80=8C=E9=9D=9E=E4=BB=8E0=E6=89=AB?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 程序化 seek 同步全屏播放进度时,新增 snapProgress 抑制进度条 的宽度过渡动画,使其直接落到观看位置,恢复播放时再启用过渡。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../messageStream/MessageInlineVideo.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/messageStream/MessageInlineVideo.tsx b/src/components/messageStream/MessageInlineVideo.tsx index c4ae03b..5418f2a 100644 --- a/src/components/messageStream/MessageInlineVideo.tsx +++ b/src/components/messageStream/MessageInlineVideo.tsx @@ -120,13 +120,22 @@ export function MessageInlineVideo({ const [currentTime, setCurrentTime] = useState(initialTime); const [duration, setDuration] = useState(attachment.durationSec ?? 0); const [isScrubbing, setIsScrubbing] = useState(false); + // When we programmatically seek (e.g. syncing the playhead back from the + // fullscreen overlay) the progress fill should jump straight to the watched + // position instead of sweeping up from its old width via the CSS transition. + // Cleared as soon as real playback resumes so live progress stays smooth. + const [snapProgress, setSnapProgress] = useState(false); const t = TOKENS[size]; useEffect(() => { const v = videoRef.current; if (!v) return; - const onPlay = () => setIsPlaying(true); + const onPlay = () => { + setIsPlaying(true); + // Real playback advances the fill smoothly again; re-enable transitions. + setSnapProgress(false); + }; const onPause = () => setIsPlaying(false); const onTime = () => { setCurrentTime(v.currentTime); @@ -232,7 +241,9 @@ export function MessageInlineVideo({ // Update React state synchronously so the progress bar paints the // new playhead in the next frame, before the