diff --git a/src/app/chat/components/__tests__/tailwind-components.test.tsx b/src/app/chat/components/__tests__/tailwind-components.test.tsx index decd1378..98867487 100644 --- a/src/app/chat/components/__tests__/tailwind-components.test.tsx +++ b/src/app/chat/components/__tests__/tailwind-components.test.tsx @@ -1,6 +1,7 @@ import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it, vi } from "vitest"; +import { BrowserHintOverlay } from "../browser-hint-overlay"; import { ChatHeader } from "../chat-header"; import { ChatInsufficientCreditsBanner } from "../chat-insufficient-credits-banner"; import { ChatInputTextField } from "../chat-input-text-field"; @@ -8,6 +9,7 @@ import { ChatSendButton } from "../chat-send-button"; import { ImageBubble } from "../image-bubble"; import { MessageAvatar } from "../message-avatar"; import { PrivateMessageCard } from "../private-message-card"; +import { PwaInstallDialog } from "../pwa-install-dialog"; import { TextBubble } from "../text-bubble"; vi.mock("@/router/use-app-navigator", () => ({ @@ -181,4 +183,42 @@ describe("chat Tailwind components", () => { expect(html).toContain("Message Elio"); expect(html).toContain("Hello"); }); + + it("renders BrowserHintOverlay visibility and expanded styles", () => { + expect(renderToStaticMarkup()).toBe(""); + + const html = renderToStaticMarkup( + , + ); + + expect(html).toContain('aria-expanded="true"'); + expect(html).toContain('aria-label="Open elsewhere. Better experience"'); + expect(html).toContain("grid-cols-[auto_minmax(0,1fr)]"); + expect(html).toContain("backdrop-blur-[12px]"); + expect(html).toContain("hover:opacity-[0.94]"); + expect(html).toContain("Open elsewhere"); + expect(html).toContain("Better experience"); + }); + + it("renders PwaInstallDialog with Tailwind dialog layout", () => { + const html = renderToStaticMarkup( + undefined} onInstall={() => undefined} />, + ); + + expect(html).toContain('role="dialog"'); + expect(html).toContain('aria-modal="true"'); + expect(html).toContain('aria-labelledby="pwa-dialog-title"'); + expect(html).toContain("fixed inset-0"); + expect(html).toContain("max-w-[var(--pwa-install-dialog-max-width,360px)]"); + expect(html).toContain("size-[var(--icon-size-86,86px)]"); + expect(html).toContain("%2Fimages%2Ficons%2Ficon-add-to-home.png"); + expect(html).toContain("Add to Home Screen"); + expect(html).toContain("Cancel"); + expect(html).toContain("OK"); + }); }); diff --git a/src/app/chat/components/browser-hint-overlay.module.css b/src/app/chat/components/browser-hint-overlay.module.css deleted file mode 100644 index 28c0c20b..00000000 --- a/src/app/chat/components/browser-hint-overlay.module.css +++ /dev/null @@ -1,122 +0,0 @@ -/* BrowserHintOverlay 浏览器提示样式 */ - -.overlay { - position: absolute; - top: var(--spacing-2, 8px); - right: var(--spacing-2, 8px); - display: grid; - grid-template-columns: auto minmax(0, 1fr); - align-items: center; - gap: 8px; - z-index: 20; - width: fit-content; - max-width: min(74vw, 260px); - min-height: 48px; - padding: 8px 10px; - border: 1px solid rgba(255, 255, 255, 0.18); - border-radius: 18px; - background: - linear-gradient(135deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.04)), - var(--color-blur-background, rgba(13, 11, 20, 0.86)); - box-shadow: - 0 12px 28px rgba(0, 0, 0, 0.24), - inset 0 1px 0 rgba(255, 255, 255, 0.16); - color: #fff; - cursor: pointer; - pointer-events: auto; - backdrop-filter: blur(12px); - transition: border-color 0.16s ease, box-shadow 0.16s ease, - opacity 0.16s ease, padding 0.18s ease, transform 0.16s ease; - -webkit-tap-highlight-color: transparent; -} - -.expanded { - width: fit-content; -} - -.collapsed { - display: inline-flex; - width: 42px; - height: 42px; - min-width: 42px; - min-height: 42px; - align-items: center; - justify-content: center; - padding: 5px; - border-radius: 999px; -} - -.iconWrap { - display: inline-flex; - width: 30px; - height: 30px; - align-items: center; - justify-content: center; - border-radius: 999px; - background: rgba(255, 255, 255, 0.15); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16); - color: #fff; - transition: background 0.18s ease, transform 0.18s ease; -} - -.collapsed .iconWrap { - width: 30px; - height: 30px; -} - -.copy { - display: flex; - min-width: 0; - max-width: 190px; - flex-direction: column; - gap: 2px; - text-align: left; - opacity: 1; - overflow: hidden; - transform: translateX(0); - transition: max-width 0.2s ease, opacity 0.14s ease, - transform 0.18s ease; - white-space: normal; -} - -.collapsed .copy { - display: none; - opacity: 0; - pointer-events: none; - transform: translateX(6px); -} - -.title { - color: #fff; - font-size: var(--font-size-sm, 12px); - font-weight: 800; - line-height: 1.25; - overflow-wrap: break-word; -} - -.description { - color: rgba(255, 255, 255, 0.72); - font-size: 11px; - font-weight: 650; - line-height: 1.2; - overflow-wrap: break-word; -} - -.overlay:hover { - border-color: rgba(255, 255, 255, 0.28); - box-shadow: - 0 14px 32px rgba(0, 0, 0, 0.28), - inset 0 1px 0 rgba(255, 255, 255, 0.18); - opacity: 0.94; - transform: translateY(-1px); -} - -.overlay:active { - opacity: 0.88; - transform: translateY(1px) scale(0.99); -} - -.overlay:focus-visible { - outline: 2px solid rgba(255, 255, 255, 0.88); - outline-offset: 4px; -} diff --git a/src/app/chat/components/browser-hint-overlay.tsx b/src/app/chat/components/browser-hint-overlay.tsx index 4e4c3392..154f4397 100644 --- a/src/app/chat/components/browser-hint-overlay.tsx +++ b/src/app/chat/components/browser-hint-overlay.tsx @@ -15,8 +15,6 @@ import { ExternalLink } from "lucide-react"; import { openChatInExternalBrowser } from "@/lib/chat/chat_external_browser"; -import styles from "./browser-hint-overlay.module.css"; - export interface BrowserHintOverlayProps { /** 自定义按钮标题 */ title?: string; @@ -31,6 +29,12 @@ export interface BrowserHintOverlayProps { const DEFAULT_TITLE = "Open in external browser"; const DEFAULT_DESCRIPTION = "Find me more easily"; const DEFAULT_AUTO_COLLAPSE_DELAY_MS = 3500; +const OVERLAY_BASE_CLASS_NAME = + "pointer-events-auto absolute right-[var(--spacing-2,8px)] top-[var(--spacing-2,8px)] z-20 box-border min-h-12 max-w-[min(74vw,260px)] cursor-pointer border border-[rgba(255,255,255,0.18)] bg-[linear-gradient(135deg,rgba(255,255,255,0.14),rgba(255,255,255,0.04)),var(--color-blur-background,rgba(13,11,20,0.86))] text-white shadow-[0_12px_28px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.16)] backdrop-blur-[12px] backdrop-saturate-[1.2] transition-[border-color,box-shadow,opacity,padding,transform] duration-[180ms] [-webkit-tap-highlight-color:transparent] hover:-translate-y-px hover:border-[rgba(255,255,255,0.28)] hover:opacity-[0.94] hover:shadow-[0_14px_32px_rgba(0,0,0,0.28),inset_0_1px_0_rgba(255,255,255,0.18)] active:translate-y-px active:scale-[0.99] active:opacity-[0.88] focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[rgba(255,255,255,0.88)]"; +const OVERLAY_EXPANDED_CLASS_NAME = + "grid w-fit grid-cols-[auto_minmax(0,1fr)] items-center gap-2 rounded-[18px] px-2.5 py-2"; +const OVERLAY_COLLAPSED_CLASS_NAME = + "inline-flex size-[42px] min-h-[42px] min-w-[42px] items-center justify-center rounded-full p-[5px]"; export function BrowserHintOverlay({ title = DEFAULT_TITLE, @@ -77,19 +81,34 @@ export function BrowserHintOverlay({ return ( ); diff --git a/src/app/chat/components/pwa-install-dialog.module.css b/src/app/chat/components/pwa-install-dialog.module.css deleted file mode 100644 index 127023b1..00000000 --- a/src/app/chat/components/pwa-install-dialog.module.css +++ /dev/null @@ -1,91 +0,0 @@ -/* PwaInstallDialog PWA 安装弹窗样式 - * - * 视觉规格:白底 + 大圆角 + 渐变按钮 + 86×86 图标 - */ - -.overlay { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 60; - padding: - calc(var(--dialog-safe-margin, 16px) + var(--app-safe-top, 0px)) - calc(var(--dialog-safe-margin, 16px) + var(--app-safe-right, 0px)) - calc(var(--dialog-safe-margin, 16px) + var(--app-safe-bottom, 0px)) - calc(var(--dialog-safe-margin, 16px) + var(--app-safe-left, 0px)); -} - -.dialog { - width: 100%; - max-width: var(--pwa-install-dialog-max-width, 360px); - background: var(--color-page-background, #ffffff); - border-radius: var(--responsive-card-radius, 40px); - padding: - var(--responsive-card-padding-lg, 20px) - var(--responsive-card-padding, 16px) - var(--responsive-card-padding, 16px); - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); - display: flex; - flex-direction: column; - align-items: center; - text-align: center; -} - -.icon { - width: var(--icon-size-86, 86px); - height: var(--icon-size-86, 86px); - margin-bottom: var(--spacing-lg, 16px); - object-fit: contain; -} - -.title { - font-size: var(--responsive-page-title, var(--font-size-22, 22px)); - font-weight: 700; - margin: 0 0 var(--spacing-sm, 8px); - color: var(--color-text-foreground, #171717); - line-height: 1.2; -} - -.content { - font-size: var(--responsive-body, var(--font-size-lg, 16px)); - line-height: 1.5; - color: #393939; - margin: - 0 - var(--responsive-card-padding-lg, 36px) - var(--page-section-gap-lg, 24px); -} - -.actions { - display: flex; - gap: var(--spacing-md, 12px); - width: 100%; -} - -.btn { - flex: 1 1 auto; - min-height: var(--pwa-button-height, 44px); - border: 0; - border-radius: var(--radius-bottom-sheet, 28px); - font-size: var(--responsive-body, var(--font-size-lg, 16px)); - font-weight: 600; - cursor: pointer; - color: var(--color-page-background, #ffffff); - display: flex; - align-items: center; - justify-content: center; -} - -.btnSecondary { - background: var(--color-text-secondary, #9e9e9e); -} - -.btnPrimary { - background: linear-gradient( - var(--color-button-gradient-start, #ff67e0), - var(--color-button-gradient-end, #ff52a2) - ); -} diff --git a/src/app/chat/components/pwa-install-dialog.tsx b/src/app/chat/components/pwa-install-dialog.tsx index f5d7c510..fded6fcc 100644 --- a/src/app/chat/components/pwa-install-dialog.tsx +++ b/src/app/chat/components/pwa-install-dialog.tsx @@ -9,8 +9,6 @@ */ import Image from "next/image"; -import styles from "./pwa-install-dialog.module.css"; - export interface PwaInstallDialogProps { onClose: () => void; onInstall?: () => void | Promise; @@ -31,37 +29,40 @@ export function PwaInstallDialog({ onClose, onInstall }: PwaInstallDialogProps) return (
-
+
-

+

Add to Home Screen

-

+

Add CozSweet to Home Screen{"\n"}for the best experience

-
+