Merge pull request 'fix: 修复 deploy 工作流缺少 Node.js 导致 checkout 失败的问题' #72

Merged
huanghaosheng merged 61 commits from develop into main 2026-06-14 14:37:27 +08:00
9 changed files with 1811 additions and 153 deletions
Showing only changes of commit b1e71be3d9 - Show all commits

11
frontend/.mcp.json Normal file
View File

@@ -0,0 +1,11 @@
{
"mcpServers": {
"shadcn": {
"command": "npx",
"args": [
"shadcn@latest",
"mcp"
]
}
}
}

View File

@@ -1,10 +1,11 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>frontend</title>
<meta name="description" content="CamTalk — 多模态实时 AI 视觉对话助手,通过摄像头和麦克风与 AI 自然交互" />
<title>CamTalk — AI 视觉对话助手</title>
</head>
<body>
<div id="root"></div>

View File

@@ -0,0 +1,188 @@
# Redesign Skill - Anti-Slop Frontend Framework
> Source: https://github.com/Leonxlnx/taste-skill/tree/main/skills/redesign-skill
> Purpose: Audit and upgrade existing frontend code to eliminate generic AI-generated UI patterns.
## How This Works
When applied to an existing project, follow this sequence:
1. **Scan** — Read the codebase. Identify the framework, styling method (Tailwind, vanilla CSS, styled-components, etc.), and current design patterns.
2. **Diagnose** — Run through the audit below. List every generic pattern, weak point, and missing state you find.
3. **Fix** — Apply targeted upgrades working with the existing stack. Do not rewrite from scratch. Improve what's there.
---
## Design Audit
### Typography
Check for these problems and fix them:
- **Browser default fonts or Inter everywhere.** Replace with a font that has character. Good options: `Geist`, `Outfit`, `Cabinet Grotesk`, `Satoshi`. For editorial/creative projects, pair a serif header with a sans-serif body.
- **Headlines lack presence.** Increase size for display text, tighten letter-spacing, reduce line-height. Headlines should feel heavy and intentional.
- **Body text too wide.** Limit paragraph width to roughly 65 characters. Increase line-height for readability.
- **Only Regular (400) and Bold (700) weights used.** Introduce Medium (500) and SemiBold (600) for more subtle hierarchy.
- **Numbers in proportional font.** Use a monospace font or enable tabular figures (`font-variant-numeric: tabular-nums`) for data-heavy interfaces.
- **Missing letter-spacing adjustments.** Use negative tracking for large headers, positive tracking for small caps or labels.
- **All-caps subheaders everywhere.** Try lowercase italics, sentence case, or small-caps instead.
- **Orphaned words.** Single words sitting alone on the last line. Fix with `text-wrap: balance` or `text-wrap: pretty`.
### Color and Surfaces
- **Pure `#000000` background.** Replace with off-black, dark charcoal, or tinted dark (`#0a0a0a`, `#121212`, or a dark navy).
- **Oversaturated accent colors.** Keep saturation below 80%. Desaturate accents so they blend with neutrals instead of screaming.
- **More than one accent color.** Pick one. Remove the rest. Consistency beats variety.
- **Mixing warm and cool grays.** Stick to one gray family. Tint all grays with a consistent hue (warm or cool, not both).
- **Purple/blue "AI gradient" aesthetic.** This is the most common AI design fingerprint. Replace with neutral bases and a single, considered accent.
- **Generic `box-shadow`.** Tint shadows to match the background hue. Use colored shadows (e.g., dark blue shadow on a blue background) instead of pure black at low opacity.
- **Flat design with zero texture.** Add subtle noise, grain, or micro-patterns to backgrounds. Pure flat vectors feel sterile.
- **Perfectly even gradients.** Break the uniformity with radial gradients, noise overlays, or mesh gradients instead of standard linear 45-degree fades.
- **Inconsistent lighting direction.** Audit all shadows to ensure they suggest a single, consistent light source.
- **Random dark sections in a light mode page (or vice versa).** A single dark-background section breaking an otherwise light page looks like a copy-paste accident. Either commit to a full dark mode or keep a consistent background tone throughout. If contrast is needed, use a slightly darker shade of the same palette — not a sudden jump to `#111` in the middle of a cream page.
- **Empty, flat sections with no visual depth.** Sections that are just text on a plain background feel unfinished. Add high-quality background imagery (blurred, overlaid, or masked), subtle patterns, or ambient gradients. Use reliable placeholder sources like `https://picsum.photos/seed/{name}/1920/1080` when real assets are not available. Experiment with background images behind hero sections, feature blocks, or CTAs — even a subtle full-width photo at low opacity adds presence.
### Layout
- **Everything centered and symmetrical.** Break symmetry with offset margins, mixed aspect ratios, or left-aligned headers over centered content.
- **Three equal card columns as feature row.** This is the most generic AI layout. Replace with a 2-column zig-zag, asymmetric grid, horizontal scroll, or masonry layout.
- **Using `height: 100vh` for full-screen sections.** Replace with `min-height: 100dvh` to prevent layout jumping on mobile browsers (iOS Safari viewport bug).
- **Complex flexbox percentage math.** Replace with CSS Grid for reliable multi-column structures.
- **No max-width container.** Add a container constraint (around 1200-1440px) with auto margins so content doesn't stretch edge-to-edge on wide screens.
- **Cards of equal height forced by flexbox.** Allow variable heights or use masonry when content varies in length.
- **Uniform border-radius on everything.** Vary the radius: tighter on inner elements, softer on containers.
- **No overlap or depth.** Elements sit flat next to each other. Use negative margins to create layering and visual depth.
- **Symmetrical vertical padding.** Top and bottom padding are always identical. Adjust optically — bottom padding often needs to be slightly larger.
- **Dashboard always has a left sidebar.** Try top navigation, a floating command menu, or a collapsible panel instead.
- **Missing whitespace.** Double the spacing. Let the design breathe. Dense layouts work for data dashboards, not for marketing pages.
- **Buttons not bottom-aligned in card groups.** When cards have different content lengths, CTAs end up at random heights. Pin buttons to the bottom of each card so they form a clean horizontal line regardless of content above.
- **Feature lists starting at different vertical positions.** In pricing tables or comparison cards, the list of features should start at the same Y position across all columns. Use consistent spacing above the list or fixed-height title/price blocks.
- **Inconsistent vertical rhythm in side-by-side elements.** When placing cards, columns, or panels next to each other, align shared elements (titles, descriptions, prices, buttons) across all items. Misaligned baselines make the layout look broken.
- **Mathematical alignment that looks optically wrong.** Centering by the math doesn't always look centered to the eye. Icons next to text, play buttons in circles, or text in buttons often need 1-2px optical adjustments to feel right.
### Interactivity and States
- **No hover states on buttons.** Add background shift, slight scale, or translate on hover.
- **No active/pressed feedback.** Add a subtle `scale(0.98)` or `translateY(1px)` on press to simulate a physical click.
- **Instant transitions with zero duration.** Add smooth transitions (200-300ms) to all interactive elements.
- **Missing focus ring.** Ensure visible focus indicators for keyboard navigation. This is an accessibility requirement, not optional.
- **No loading states.** Replace generic circular spinners with skeleton loaders that match the layout shape.
- **No empty states.** An empty dashboard showing nothing is a missed opportunity. Design a composed "getting started" view.
- **No error states.** Add clear, inline error messages for forms. Do not use `window.alert()`.
- **Dead links.** Buttons that link to `#`. Either link to real destinations or visually disable them.
- **No indication of current page in navigation.** Style the active nav link differently so users know where they are.
- **Scroll jumping.** Anchor clicks jump instantly. Add `scroll-behavior: smooth`.
- **Animations using `top`, `left`, `width`, `height`.** Switch to `transform` and `opacity` for GPU-accelerated, smooth animation.
### Content
- **Generic names like "John Doe" or "Jane Smith".** Use diverse, realistic-sounding names.
- **Fake round numbers like `99.99%`, `50%`, `$100.00`.** Use organic, messy data: `47.2%`, `$99.00`, `+1 (312) 847-1928`.
- **Placeholder company names like "Acme Corp", "Nexus", "SmartFlow".** Invent contextual, believable brand names.
- **AI copywriting cliches.** Never use "Elevate", "Seamless", "Unleash", "Next-Gen", "Game-changer", "Delve", "Tapestry", or "In the world of...". Write plain, specific language.
- **Exclamation marks in success messages.** Remove them. Be confident, not loud.
- **"Oops!" error messages.** Be direct: "Connection failed. Please try again."
- **Passive voice.** Use active voice: "We couldn't save your changes" instead of "Mistakes were made."
- **All blog post dates identical.** Randomize dates to appear real.
- **Same avatar image for multiple users.** Use unique assets for every distinct person.
- **Lorem Ipsum.** Never use placeholder latin text. Write real draft copy.
- **Title Case On Every Header.** Use sentence case instead.
### Component Patterns
- **Generic card look (border + shadow + white background).** Remove the border, or use only background color, or use only spacing. Cards should exist only when elevation communicates hierarchy.
- **Always one filled button + one ghost button.** Add text links or tertiary styles to reduce visual noise.
- **Pill-shaped "New" and "Beta" badges.** Try square badges, flags, or plain text labels.
- **Accordion FAQ sections.** Use a side-by-side list, searchable help, or inline progressive disclosure.
- **3-card carousel testimonials with dots.** Replace with a masonry wall, embedded social posts, or a single rotating quote.
- **Pricing table with 3 towers.** Highlight the recommended tier with color and emphasis, not just extra height.
- **Modals for everything.** Use inline editing, slide-over panels, or expandable sections instead of popups for simple actions.
- **Avatar circles exclusively.** Try squircles or rounded squares for a less generic look.
- **Light/dark toggle always a sun/moon switch.** Use a dropdown, system preference detection, or integrate it into settings.
- **Footer link farm with 4 columns.** Simplify. Focus on main navigational paths and legally required links.
### Iconography
- **Lucide or Feather icons exclusively.** These are the "default" AI icon choice. Use Phosphor, Heroicons, or a custom set for differentiation.
- **Rocketship for "Launch", shield for "Security".** Replace cliche metaphors with less obvious icons (bolt, fingerprint, spark, vault).
- **Inconsistent stroke widths across icons.** Audit all icons and standardize to one stroke weight.
- **Missing favicon.** Always include a branded favicon.
- **Stock "diverse team" photos.** Use real team photos, candid shots, or a consistent illustration style instead of uncanny stock imagery.
### Code Quality
- **Div soup.** Use semantic HTML: `<header>`, `<main>`, `<article>`, `<section>`, `<footer>`.
- **Inline styles mixed with CSS classes.** Move all styling to the project's styling system.
- **Hardcoded pixel widths.** Use relative units (`%`, `rem`, `em`, `max-width`) for flexible layouts.
- **Missing alt text on images.** Describe image content for screen readers. Never leave `alt=""` or `alt="image"` on meaningful images.
- **Arbitrary z-index values like `9999`.** Establish a clean z-index scale in the theme/variables.
- **Commented-out dead code.** Remove all debug artifacts before shipping.
- **Import hallucinations.** Check that every import actually exists in `package.json` or the project dependencies.
- **Missing meta tags.** Add proper `<title>`, `<description>`, `<og:image>`, and social sharing meta tags.
### Strategic Omissions (What AI Typically Forgets)
- **No legal links.** Add privacy policy and terms of service links in the footer.
- **No "back" navigation.** Dead ends in user flows. Every page needs a way back.
- **No custom 404 page.** Design a helpful, branded "page not found" experience.
- **No form validation.** Add client-side validation for emails, required fields, and format checks.
- **No "skip to content" link.** Essential for keyboard users. Add a hidden skip-link.
- **No cookie consent.** If required by jurisdiction, add a compliant consent banner.
---
## Upgrade Techniques
When upgrading a project, pull from these high-impact techniques to replace generic patterns:
### Typography Upgrades
- **Variable font animation.** Interpolate weight or width on scroll or hover for text that feels alive.
- **Outlined-to-fill transitions.** Text starts as a stroke outline and fills with color on scroll entry or interaction.
- **Text mask reveals.** Large typography acting as a window to video or animated imagery behind it.
### Layout Upgrades
- **Broken grid / asymmetry.** Elements that deliberately ignore column structure — overlapping, bleeding off-screen, or offset with calculated randomness.
- **Whitespace maximization.** Aggressive use of negative space to force focus on a single element.
- **Parallax card stacks.** Sections that stick and physically stack over each other during scroll.
- **Split-screen scroll.** Two halves of the screen sliding in opposite directions.
### Motion Upgrades
- **Smooth scroll with inertia.** Decouple scrolling from browser defaults for a heavier, cinematic feel.
- **Staggered entry.** Elements cascade in with slight delays, combining Y-axis translation with opacity fade. Never mount everything at once.
- **Spring physics.** Replace linear easing with spring-based motion for a natural, weighty feel on all interactive elements.
- **Scroll-driven reveals.** Content entering through expanding masks, wipes, or draw-on SVG paths tied to scroll progress.
### Surface Upgrades
- **True glassmorphism.** Go beyond `backdrop-filter: blur`. Add a 1px inner border and a subtle inner shadow to simulate edge refraction.
- **Spotlight borders.** Card borders that illuminate dynamically under the cursor.
- **Grain and noise overlays.** A fixed, pointer-events-none overlay with subtle noise to break digital flatness.
- **Colored, tinted shadows.** Shadows that carry the hue of the background rather than using generic black.
---
## Fix Priority
Apply changes in this order for maximum visual impact with minimum risk:
1. **Font swap** — biggest instant improvement, lowest risk
2. **Color palette cleanup** — remove clashing or oversaturated colors
3. **Hover and active states** — makes the interface feel alive
4. **Layout and spacing** — proper grid, max-width, consistent padding
5. **Replace generic components** — swap cliche patterns for modern alternatives
6. **Add loading, empty, and error states** — makes it feel finished
7. **Polish typography scale and spacing** — the premium final touch
---
## Rules
- Work with the existing tech stack. Do not migrate frameworks or styling libraries.
- Do not break existing functionality. Test after every change.
- Before importing any new library, check the project's dependency file first.
- If the project uses Tailwind, check the version (v3 vs v4) before modifying config.
- If the project has no framework, use vanilla CSS.
- Keep changes reviewable and focused. Small, targeted improvements over big rewrites.

View File

@@ -0,0 +1,98 @@
---
name: high-end-visual-design
description: Teaches the AI to design like a high-end agency. Defines the exact fonts, spacing, shadows, card structures, and animations that make a website feel expensive. Blocks all the common defaults that make AI designs look cheap or generic.
---
# Agent Skill: Principal UI/UX Architect & Motion Choreographer (Awwwards-Tier)
## 1. Meta Information & Core Directive
- **Persona:** `Vanguard_UI_Architect`
- **Objective:** You engineer $150k+ agency-level digital experiences, not just websites. Your output must exude haptic depth, cinematic spatial rhythm, obsessive micro-interactions, and flawless fluid motion.
- **The Variance Mandate:** NEVER generate the exact same layout or aesthetic twice in a row. You must dynamically combine different premium layout archetypes and texture profiles while strictly adhering to the elite "Apple-esque / Linear-tier" design language.
## 2. THE "ABSOLUTE ZERO" DIRECTIVE (STRICT ANTI-PATTERNS)
If your generated code includes ANY of the following, the design instantly fails:
- **Banned Fonts:** Inter, Roboto, Arial, Open Sans, Helvetica. (Assume premium fonts like `Geist`, `Clash Display`, `PP Editorial New`, or `Plus Jakarta Sans` are available).
- **Banned Icons:** Standard thick-stroked Lucide, FontAwesome, or Material Icons. Use only ultra-light, precise lines (e.g., Phosphor Light, Remix Line).
- **Banned Borders & Shadows:** Generic 1px solid gray borders. Harsh, dark drop shadows (`shadow-md`, `rgba(0,0,0,0.3)`).
- **Banned Layouts:** Edge-to-edge sticky navbars glued to the top. Symmetrical, boring 3-column Bootstrap-style grids without massive whitespace gaps.
- **Banned Motion:** Standard `linear` or `ease-in-out` transitions. Instant state changes without interpolation.
## 3. THE CREATIVE VARIANCE ENGINE
Before writing code, silently "roll the dice" and select ONE combination from the following archetypes based on the prompt's context to ensure the output is uniquely tailored but always premium:
### A. Vibe & Texture Archetypes (Pick 1)
1. **Ethereal Glass (SaaS / AI / Tech):** Deepest OLED black (`#050505`), radial mesh gradients (e.g., subtle glowing purple/emerald orbs) in the background. Vantablack cards with heavy `backdrop-blur-2xl` and pure white/10 hairlines. Wide geometric Grotesk typography.
2. **Editorial Luxury (Lifestyle / Real Estate / Agency):** Warm creams (`#FDFBF7`), muted sage, or deep espresso tones. High-contrast Variable Serif fonts for massive headings. Subtle CSS noise/film-grain overlay (`opacity-[0.03]`) for a physical paper feel.
3. **Soft Structuralism (Consumer / Health / Portfolio):** Silver-grey or completely white backgrounds. Massive bold Grotesk typography. Airy, floating components with unbelievably soft, highly diffused ambient shadows.
### B. Layout Archetypes (Pick 1)
1. **The Asymmetrical Bento:** A masonry-like CSS Grid of varying card sizes (e.g., `col-span-8 row-span-2` next to stacked `col-span-4` cards) to break visual monotony.
- **Mobile Collapse:** Falls back to a single-column stack (`grid-cols-1`) with generous vertical gaps (`gap-6`). All `col-span` overrides reset to `col-span-1`.
2. **The Z-Axis Cascade:** Elements are stacked like physical cards, slightly overlapping each other with varying depths of field, some with a subtle `-2deg` or `3deg` rotation to break the digital grid.
- **Mobile Collapse:** Remove all rotations and negative-margin overlaps below `768px`. Stack vertically with standard spacing. Overlapping elements cause touch-target conflicts on mobile.
3. **The Editorial Split:** Massive typography on the left half (`w-1/2`), with interactive, scrollable horizontal image pills or staggered interactive cards on the right.
- **Mobile Collapse:** Converts to a full-width vertical stack (`w-full`). Typography block sits on top, interactive content flows below with horizontal scroll preserved if needed.
**Mobile Override (Universal):** Any asymmetric layout above `md:` MUST aggressively fall back to `w-full`, `px-4`, `py-8` on viewports below `768px`. Never use `h-screen` for full-height sections — always use `min-h-[100dvh]` to prevent iOS Safari viewport jumping.
## 4. HAPTIC MICRO-AESTHETICS (COMPONENT MASTERY)
### A. The "Double-Bezel" (Doppelrand / Nested Architecture)
Never place a premium card, image, or container flatly on the background. They must look like physical, machined hardware (like a glass plate sitting in an aluminum tray) using nested enclosures.
- **Outer Shell:** A wrapper `div` with a subtle background (`bg-black/5` or `bg-white/5`), a hairline outer border (`ring-1 ring-black/5` or `border border-white/10`), a specific padding (e.g., `p-1.5` or `p-2`), and a large outer radius (`rounded-[2rem]`).
- **Inner Core:** The actual content container inside the shell. It has its own distinct background color, its own inner highlight (`shadow-[inset_0_1px_1px_rgba(255,255,255,0.15)]`), and a mathematically calculated smaller radius (e.g., `rounded-[calc(2rem-0.375rem)]`) for concentric curves.
### B. Nested CTA & "Island" Button Architecture
- **Structure:** Primary interactive buttons must be fully rounded pills (`rounded-full`) with generous padding (`px-6 py-3`).
- **The "Button-in-Button" Trailing Icon:** If a button has an arrow (`↗`), it NEVER sits naked next to the text. It must be nested inside its own distinct circular wrapper (e.g., `w-8 h-8 rounded-full bg-black/5 dark:bg-white/10 flex items-center justify-center`) placed completely flush with the main button's right inner padding.
### C. Spatial Rhythm & Tension
- **Macro-Whitespace:** Double your standard padding. Use `py-24` to `py-40` for sections. Allow the design to breathe heavily.
- **Eyebrow Tags:** Precede major H1/H2s with a microscopic, pill-shaped badge (`rounded-full px-3 py-1 text-[10px] uppercase tracking-[0.2em] font-medium`).
## 5. MOTION CHOREOGRAPHY (FLUID DYNAMICS)
Never use default transitions. All motion must simulate real-world mass and spring physics. Use custom cubic-beziers (e.g., `transition-all duration-700 ease-[cubic-bezier(0.32,0.72,0,1)]`).
### A. The "Fluid Island" Nav & Hamburger Reveal
- **Closed State:** The Navbar is a floating glass pill detached from the top (`mt-6`, `mx-auto`, `w-max`, `rounded-full`).
- **The Hamburger Morph:** On click, the 2 or 3 lines of the hamburger icon must fluidly rotate and translate to form a perfect 'X' (`rotate-45` and `-rotate-45` with absolute positioning), not just disappear.
- **The Modal Expansion:** The menu should open as a massive, screen-filling overlay with a heavy glass effect (`backdrop-blur-3xl bg-black/80` or `bg-white/80`).
- **Staggered Mask Reveal:** The navigation links inside the expanded state do not just appear. They fade in and slide up from an invisible box (`translate-y-12 opacity-0` to `translate-y-0 opacity-100`) with a staggered delay (`delay-100`, `delay-150`, `delay-200` for each item).
### B. Magnetic Button Hover Physics
- Use the `group` utility. On hover, do not just change the background color.
- Scale the entire button down slightly (`active:scale-[0.98]`) to simulate physical pressing.
- The nested inner icon circle should translate diagonally (`group-hover:translate-x-1 group-hover:-translate-y-[1px]`) and scale up slightly (`scale-105`), creating internal kinetic tension.
### C. Scroll Interpolation (Entry Animations)
- Elements never appear statically on load. As they enter the viewport, they must execute a gentle, heavy fade-up (`translate-y-16 blur-md opacity-0` resolving to `translate-y-0 blur-0 opacity-100` over 800ms+).
- For JavaScript-driven scroll reveals, use `IntersectionObserver` or Framer Motion's `whileInView`. Never use `window.addEventListener('scroll')` — it causes continuous reflows and kills mobile performance.
## 6. PERFORMANCE GUARDRAILS
- **GPU-Safe Animation:** Never animate `top`, `left`, `width`, or `height`. Animate exclusively via `transform` and `opacity`. Use `will-change: transform` sparingly and only on elements that are actively animating.
- **Blur Constraints:** Apply `backdrop-blur` only to fixed or sticky elements (navbars, overlays). Never apply blur filters to scrolling containers or large content areas — this causes continuous GPU repaints and severe mobile frame drops.
- **Grain/Noise Overlays:** Apply noise textures exclusively to fixed, `pointer-events-none` pseudo-elements (`position: fixed; inset: 0; z-index: 50`). Never attach them to scrolling containers.
- **Z-Index Discipline:** Do not use arbitrary `z-50` or `z-[9999]`. Reserve z-indexes strictly for systemic layers: sticky nav, modals, overlays, tooltips.
## 7. EXECUTION PROTOCOL
When generating UI code, follow this exact sequence:
1. **[SILENT THOUGHT]** Roll the Variance Engine (Section 3). Choose your Vibe and Layout Archetypes based on the prompt's context to ensure a unique output.
2. **[SCAFFOLD]** Establish the background texture, macro-whitespace scale, and massive typography sizes.
3. **[ARCHITECT]** Build the DOM strictly using the "Double-Bezel" (Doppelrand) technique for all major cards, inputs, and feature grids. Use exaggerated squircle radii (`rounded-[2rem]`).
4. **[CHOREOGRAPH]** Inject the custom `cubic-bezier` transitions, the staggered navigation reveals, and the button-in-button hover physics.
5. **[OUTPUT]** Deliver flawless, pixel-perfect React/Tailwind/HTML code. Do not include basic, generic fallbacks.
## 8. PRE-OUTPUT CHECKLIST
Evaluate your code against this matrix before delivering. This is the last filter.
- [ ] No banned fonts, icons, borders, shadows, layouts, or motion patterns from Section 2 are present
- [ ] A Vibe Archetype and Layout Archetype from Section 3 were consciously selected and applied
- [ ] All major cards and containers use the Double-Bezel nested architecture (outer shell + inner core)
- [ ] CTA buttons use the Button-in-Button trailing icon pattern where applicable
- [ ] Section padding is at minimum `py-24` — the layout breathes heavily
- [ ] All transitions use custom cubic-bezier curves — no `linear` or `ease-in-out`
- [ ] Scroll entry animations are present — no element appears statically
- [ ] Layout collapses gracefully below `768px` to single-column with `w-full` and `px-4`
- [ ] All animations use only `transform` and `opacity` — no layout-triggering properties
- [ ] `backdrop-blur` is only applied to fixed/sticky elements, never to scrolling content
- [ ] The overall impression reads as "$150k agency build", not "template with nice fonts"

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +1,41 @@
/* ============================================================
CamTalk — Web 端应用样式
双栏布局:左侧视频面板 + 右侧聊天面板
设计方向深色科技极简Ethereal Glass 美学
字体Inter 优先,系统字体栈兜底
颜色:冷灰体系,单一蓝色强调色
============================================================ */
:root {
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-bg: #0b0f1a;
--color-surface: #151b2b;
--color-surface-2: #1c2438;
--color-text: #e2e8f0;
--color-text-muted: #64748b;
--color-border: #1e293b;
--color-success: #22c55e;
--color-warning: #f59e0b;
--color-error: #ef4444;
--radius: 10px;
--radius-sm: 6px;
--color-primary: #3b82f6;
--color-primary-hover: #2563eb;
--color-bg: #0a0a0f;
--color-surface: #111118;
--color-surface-2: #1a1a24;
--color-surface-3: #22222e;
--color-text: #d4d4dc;
--color-text-muted: #5a5a6e;
--color-border: #1f1f2a;
--color-success: #34d399;
--color-warning: #fbbf24;
--color-error: #f87171;
--radius: 12px;
--radius-sm: 8px;
--transition-fast: 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
--transition-smooth: 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
[data-theme="light"] {
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-bg: #f1f5f9;
--color-bg: #f4f4f8;
--color-surface: #ffffff;
--color-surface-2: #f8fafc;
--color-text: #0f172a;
--color-text-muted: #64748b;
--color-border: #e2e8f0;
--color-surface-2: #f0f0f5;
--color-surface-3: #e8e8ee;
--color-text: #111118;
--color-text-muted: #6b6b80;
--color-border: #dddde5;
--color-success: #16a34a;
--color-warning: #d97706;
--color-error: #dc2626;
@@ -44,9 +52,12 @@ html, body, #root {
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
font-variant-numeric: tabular-nums;
background: var(--color-bg);
color: var(--color-text);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ---- App Shell ---- */
@@ -64,11 +75,14 @@ body {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
height: 48px;
padding: 0 24px;
height: 56px;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
box-shadow: 0 1px 8px rgba(10, 10, 15, 0.3);
flex-shrink: 0;
position: relative;
z-index: 10;
}
.header__left {
@@ -78,14 +92,17 @@ body {
}
.header__title {
font-size: 1.2rem;
font-weight: 700;
font-size: 1.15rem;
font-weight: 600;
letter-spacing: -0.02em;
color: var(--color-text);
}
.header__subtitle {
font-size: 0.8rem;
font-size: 0.78rem;
font-weight: 400;
color: var(--color-text-muted);
letter-spacing: 0.01em;
}
.header__right {
@@ -95,30 +112,34 @@ body {
}
.header__stats {
font-size: 0.75rem;
font-size: 0.72rem;
color: var(--color-text-muted);
padding: 4px 10px;
padding: 4px 12px;
background: var(--color-surface-2);
border-radius: 12px;
border-radius: 20px;
font-weight: 500;
letter-spacing: 0.01em;
}
.badge {
font-size: 0.75rem;
padding: 3px 10px;
border-radius: 12px;
font-size: 0.72rem;
padding: 4px 12px;
border-radius: 20px;
font-weight: 500;
letter-spacing: 0.01em;
transition: all var(--transition-fast);
}
.badge--connected {
color: var(--color-success);
background: rgba(34, 197, 94, 0.1);
border: 1px solid rgba(34, 197, 94, 0.3);
background: rgba(52, 211, 153, 0.08);
border: 1px solid rgba(52, 211, 153, 0.2);
}
.badge--connecting {
color: var(--color-warning);
background: rgba(245, 158, 11, 0.1);
border: 1px solid rgba(245, 158, 11, 0.3);
background: rgba(251, 191, 36, 0.08);
border: 1px solid rgba(251, 191, 36, 0.2);
}
.badge--disconnected {
@@ -132,10 +153,13 @@ body {
border: none;
font-size: 1.1rem;
cursor: pointer;
padding: 6px;
padding: 8px;
border-radius: var(--radius-sm);
transition: background 0.15s;
transition: all var(--transition-fast);
color: var(--color-text-muted);
display: inline-flex;
align-items: center;
justify-content: center;
}
.btn-icon:hover {
@@ -143,6 +167,10 @@ body {
color: var(--color-text);
}
.btn-icon:active {
transform: scale(0.92);
}
.lang-group {
display: flex;
gap: 2px;
@@ -158,9 +186,9 @@ body {
font-size: 0.72rem;
font-weight: 600;
padding: 4px 10px;
border-radius: 4px;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
transition: all var(--transition-fast);
}
.lang-btn:hover {
@@ -187,8 +215,8 @@ body {
flex-shrink: 0;
display: flex;
flex-direction: column;
padding: 16px;
gap: 12px;
padding: 20px;
gap: 14px;
}
.video-container {
@@ -198,6 +226,11 @@ body {
border-radius: var(--radius);
overflow: hidden;
border: 1px solid var(--color-border);
transition: border-color var(--transition-fast);
}
.video-container:hover {
border-color: var(--color-surface-3);
}
.video-container .video-preview {
@@ -210,7 +243,7 @@ body {
width: 100%;
height: 100%;
object-fit: cover;
background: #1a1a2e;
background: #12121a;
}
.video-placeholder {
@@ -220,14 +253,15 @@ body {
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
gap: 10px;
color: var(--color-text-muted);
font-size: 0.82rem;
font-weight: 400;
}
.video-placeholder__icon {
font-size: 2.4rem;
opacity: 0.2;
font-size: 2.2rem;
opacity: 0.15;
margin-bottom: 4px;
}
@@ -235,14 +269,15 @@ body {
position: absolute;
bottom: 16px;
left: 16px;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(8px);
background: rgba(10, 10, 15, 0.8);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: var(--color-success);
padding: 6px 14px;
border-radius: var(--radius-sm);
font-size: 0.8rem;
font-size: 0.78rem;
font-weight: 500;
animation: pulse 1.5s ease-in-out infinite;
animation: pulse 1.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
}
.live-badge {
@@ -252,22 +287,23 @@ body {
display: flex;
align-items: center;
gap: 6px;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(8px);
background: rgba(10, 10, 15, 0.8);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: white;
padding: 4px 12px;
padding: 5px 12px;
border-radius: var(--radius-sm);
font-size: 0.75rem;
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.5px;
letter-spacing: 0.04em;
}
.live-badge__dot {
width: 8px;
height: 8px;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--color-success);
animation: pulse 1.5s ease-in-out infinite;
animation: pulse 1.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
}
.video-indicator--loading { color: var(--color-warning); }
@@ -278,45 +314,49 @@ body {
position: absolute;
top: 12px;
right: 12px;
background: rgba(37, 99, 235, 0.85);
backdrop-filter: blur(8px);
background: rgba(59, 130, 246, 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: white;
padding: 3px 10px;
border-radius: var(--radius-sm);
font-size: 0.7rem;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.5px;
letter-spacing: 0.06em;
}
.observation-badge {
position: absolute;
top: 12px;
left: 12px;
background: rgba(34, 197, 94, 0.85);
backdrop-filter: blur(8px);
background: rgba(52, 211, 153, 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: white;
padding: 3px 10px;
border-radius: var(--radius-sm);
font-size: 0.7rem;
font-size: 0.68rem;
font-weight: 700;
animation: pulse 2s ease-in-out infinite;
letter-spacing: 0.04em;
animation: pulse 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
}
.video-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.35);
background: rgba(10, 10, 15, 0.4);
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(2px);
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px);
}
.video-overlay__spinner {
width: 36px;
height: 36px;
border: 3px solid rgba(255, 255, 255, 0.2);
border-top-color: white;
border: 2.5px solid rgba(255, 255, 255, 0.15);
border-top-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@@ -327,53 +367,66 @@ body {
flex-shrink: 0;
display: flex;
justify-content: center;
padding: 4px 0;
padding: 6px 0;
}
.video-controls__row {
display: flex;
gap: 6px;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
}
.btn--ctrl {
padding: 6px 12px;
padding: 7px 14px;
font-size: 0.78rem;
border-radius: var(--radius-sm);
border: 1px solid var(--color-border);
background: var(--color-surface-2);
color: var(--color-text-muted);
cursor: pointer;
transition: all 0.15s;
transition: all var(--transition-fast);
white-space: nowrap;
}
.btn--ctrl:hover {
background: var(--color-border);
background: var(--color-surface-3);
color: var(--color-text);
border-color: var(--color-surface-3);
}
.btn--ctrl:active {
transform: translateY(1px);
}
.btn--ctrl-on {
background: rgba(34, 197, 94, 0.15);
background: rgba(52, 211, 153, 0.1);
color: var(--color-success);
border-color: rgba(34, 197, 94, 0.3);
border-color: rgba(52, 211, 153, 0.25);
}
.btn--ctrl-on:hover {
background: rgba(52, 211, 153, 0.18);
}
.btn--ctrl-off {
background: rgba(239, 68, 68, 0.1);
background: rgba(248, 113, 113, 0.08);
color: var(--color-error);
border-color: rgba(239, 68, 68, 0.2);
border-color: rgba(248, 113, 113, 0.18);
}
.btn--ctrl-off:hover {
background: rgba(248, 113, 113, 0.15);
}
.btn--speaking {
animation: micPulse 0.8s ease-in-out infinite;
box-shadow: 0 0 12px rgba(34, 197, 94, 0.5);
animation: micPulse 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
box-shadow: 0 0 12px rgba(52, 211, 153, 0.4);
}
@keyframes micPulse {
0%, 100% { box-shadow: 0 0 8px rgba(34, 197, 94, 0.4); transform: scale(1); }
50% { box-shadow: 0 0 16px rgba(34, 197, 94, 0.8); transform: scale(1.05); }
0%, 100% { box-shadow: 0 0 8px rgba(52, 211, 153, 0.3); transform: scale(1); }
50% { box-shadow: 0 0 18px rgba(52, 211, 153, 0.7); transform: scale(1.04); }
}
/* ---- 右侧聊天面板 ---- */
@@ -391,21 +444,23 @@ body {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
padding: 14px 24px;
font-weight: 600;
font-size: 0.85rem;
font-size: 0.82rem;
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
color: var(--color-text-muted);
letter-spacing: -0.01em;
}
.chat-panel-header__mode {
font-size: 0.7rem;
font-size: 0.68rem;
font-weight: 500;
color: var(--color-success);
background: rgba(34, 197, 94, 0.1);
padding: 2px 8px;
border-radius: 10px;
background: rgba(52, 211, 153, 0.08);
padding: 3px 10px;
border-radius: 12px;
letter-spacing: 0.02em;
}
.chat-panel-body {
@@ -420,32 +475,57 @@ body {
.chat-panel {
flex: 1;
overflow-y: auto;
padding: 12px 16px;
padding: 16px 24px;
display: flex;
flex-direction: column;
gap: 10px;
gap: 12px;
}
.chat-panel--empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
gap: 12px;
color: var(--color-text-muted);
font-size: 0.85rem;
}
.chat-panel--empty-icon {
font-size: 2.4rem;
opacity: 0.12;
margin-bottom: 4px;
}
.chat-panel--empty-hint {
font-size: 0.75rem;
opacity: 0.6;
max-width: 240px;
text-align: center;
line-height: 1.5;
}
.chat-message {
padding: 10px 14px;
padding: 12px 16px;
border-radius: var(--radius-sm);
background: var(--color-surface-2);
max-width: 92%;
max-width: min(92%, 65ch);
transition: background var(--transition-fast);
}
.chat-message:hover {
background: var(--color-surface-3);
}
.chat-message--user {
border-left: 2px solid var(--color-primary);
align-self: flex-end;
background: rgba(37, 99, 235, 0.08);
background: rgba(59, 130, 246, 0.06);
}
.chat-message--user:hover {
background: rgba(59, 130, 246, 0.1);
}
.chat-message--assistant {
@@ -457,24 +537,27 @@ body {
}
.chat-message__role {
font-size: 0.65rem;
font-size: 0.62rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
letter-spacing: 0.08em;
color: var(--color-text-muted);
margin-bottom: 3px;
margin-bottom: 4px;
}
.chat-message__content {
font-size: 0.82rem;
line-height: 1.55;
line-height: 1.65;
white-space: pre-wrap;
max-width: 65ch;
}
.chat-message__meta {
margin-top: 6px;
font-size: 0.65rem;
margin-top: 8px;
font-size: 0.62rem;
color: var(--color-text-muted);
font-weight: 400;
letter-spacing: 0.01em;
}
/* ---- Streaming Cursor ---- */
@@ -483,22 +566,24 @@ body {
display: inline;
animation: blink 0.8s step-end infinite;
color: var(--color-success);
font-weight: 300;
}
/* ---- System Message ---- */
.system-message {
text-align: center;
padding: 8px 16px;
margin: 0 20px;
padding: 10px 16px;
margin: 0 24px;
border-radius: var(--radius-sm);
font-size: 0.8rem;
font-size: 0.78rem;
font-weight: 500;
}
.system-message--warning {
background: rgba(245, 158, 11, 0.1);
background: rgba(251, 191, 36, 0.08);
color: var(--color-warning);
border: 1px solid rgba(245, 158, 11, 0.2);
border: 1px solid rgba(251, 191, 36, 0.15);
}
/* ---- Drawer (Config Panel) ---- */
@@ -507,9 +592,10 @@ body {
position: fixed;
inset: 0;
z-index: 100;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(2px);
animation: fadeIn 0.2s ease;
background: rgba(10, 10, 15, 0.5);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
animation: fadeIn 0.25s var(--transition-smooth);
}
.drawer {
@@ -522,22 +608,23 @@ body {
border-left: 1px solid var(--color-border);
display: flex;
flex-direction: column;
animation: slideInRight 0.25s ease;
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.3);
animation: slideInRight 0.35s var(--transition-smooth);
box-shadow: -8px 0 40px rgba(10, 10, 15, 0.5);
}
.drawer__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
padding: 16px 20px;
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
}
.drawer__title {
font-weight: 600;
font-size: 0.9rem;
font-size: 0.88rem;
letter-spacing: -0.01em;
}
.drawer__close {
@@ -546,9 +633,12 @@ body {
color: var(--color-text-muted);
cursor: pointer;
font-size: 1rem;
padding: 4px 8px;
padding: 6px 8px;
border-radius: var(--radius-sm);
transition: all 0.15s;
transition: all var(--transition-fast);
display: inline-flex;
align-items: center;
justify-content: center;
}
.drawer__close:hover {
@@ -556,26 +646,30 @@ body {
color: var(--color-text);
}
.drawer__close:active {
transform: scale(0.92);
}
.drawer__body {
flex: 1;
overflow-y: auto;
padding: 16px;
padding: 20px;
}
/* ---- Config Group ---- */
.config-group {
margin-bottom: 20px;
margin-bottom: 24px;
}
.config-group__title {
font-size: 0.7rem;
font-size: 0.68rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
letter-spacing: 0.1em;
color: var(--color-text-muted);
margin-bottom: 12px;
padding-bottom: 8px;
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 1px solid var(--color-border);
}
@@ -583,16 +677,23 @@ body {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
padding: 10px 8px;
margin: 0 -8px;
font-size: 0.82rem;
color: var(--color-text);
cursor: pointer;
border-radius: var(--radius-sm);
transition: background var(--transition-fast);
}
.config-row:hover {
background: var(--color-surface-2);
}
.config-row__info {
display: flex;
flex-direction: column;
gap: 2px;
gap: 3px;
}
.config-row__label {
@@ -601,8 +702,9 @@ body {
}
.config-row__desc {
font-size: 0.72rem;
font-size: 0.7rem;
color: var(--color-text-muted);
line-height: 1.4;
}
.config-row select,
@@ -611,23 +713,36 @@ body {
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
padding: 5px 10px;
font-size: 0.8rem;
padding: 6px 10px;
font-size: 0.78rem;
cursor: pointer;
transition: all var(--transition-fast);
}
.config-row select:hover,
.config-row input[type="checkbox"]:hover {
border-color: var(--color-surface-3);
}
.config-row select:focus,
.config-row input[type="checkbox"]:focus {
outline: 2px solid rgba(59, 130, 246, 0.4);
outline-offset: 1px;
}
.config-row input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
accent-color: var(--color-primary);
}
/* ---- Toast ---- */
.toast-container {
position: fixed;
top: 68px;
right: 20px;
top: 72px;
right: 24px;
z-index: 1000;
display: flex;
flex-direction: column;
@@ -637,16 +752,30 @@ body {
.toast {
padding: 10px 16px;
border-radius: var(--radius-sm);
font-size: 0.82rem;
font-size: 0.8rem;
font-weight: 500;
cursor: pointer;
animation: slideIn 0.3s ease-out;
animation: slideIn 0.35s var(--transition-smooth);
max-width: 320px;
backdrop-filter: blur(8px);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
box-shadow: 0 8px 32px rgba(10, 10, 15, 0.4);
}
.toast--error { background: rgba(239, 68, 68, 0.9); color: white; }
.toast--warning { background: rgba(245, 158, 11, 0.9); color: #000; }
.toast--info { background: rgba(37, 99, 235, 0.9); color: white; }
.toast--error {
background: rgba(248, 113, 113, 0.92);
color: white;
}
.toast--warning {
background: rgba(251, 191, 36, 0.92);
color: #111;
}
.toast--info {
background: rgba(59, 130, 246, 0.92);
color: white;
}
/* ---- Buttons ---- */
@@ -654,18 +783,23 @@ body {
padding: 8px 20px;
border: none;
border-radius: var(--radius-sm);
font-size: 0.85rem;
font-size: 0.82rem;
font-weight: 600;
cursor: pointer;
transition: all 0.15s;
transition: all var(--transition-fast);
display: inline-flex;
align-items: center;
gap: 6px;
letter-spacing: 0.01em;
}
.btn:active {
transform: translateY(1px);
}
.btn--lg {
padding: 12px 32px;
font-size: 0.95rem;
font-size: 0.92rem;
border-radius: var(--radius);
}
@@ -685,43 +819,46 @@ body {
}
.btn--secondary:hover {
background: var(--color-border);
background: var(--color-surface-3);
border-color: var(--color-surface-3);
}
.btn--warning {
background: var(--color-warning);
color: #000;
color: #111;
}
.btn--warning:hover { opacity: 0.9; }
.btn--warning:hover {
opacity: 0.9;
}
.btn--danger {
background: rgba(239, 68, 68, 0.15);
background: rgba(248, 113, 113, 0.1);
color: var(--color-error);
border: 1px solid rgba(239, 68, 68, 0.3);
border: 1px solid rgba(248, 113, 113, 0.2);
}
.btn--danger:hover {
background: rgba(239, 68, 68, 0.25);
background: rgba(248, 113, 113, 0.18);
}
.btn--active {
background: var(--color-primary);
color: white;
box-shadow: 0 0 12px rgba(37, 99, 235, 0.4);
animation: pulseBtn 2s ease-in-out infinite;
box-shadow: 0 0 12px rgba(59, 130, 246, 0.35);
animation: pulseBtn 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
}
/* ---- Animations ---- */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
50% { opacity: 0.55; }
}
@keyframes pulseBtn {
0%, 100% { box-shadow: 0 0 12px rgba(37, 99, 235, 0.4); }
50% { box-shadow: 0 0 20px rgba(37, 99, 235, 0.7); }
0%, 100% { box-shadow: 0 0 12px rgba(59, 130, 246, 0.35); }
50% { box-shadow: 0 0 22px rgba(59, 130, 246, 0.6); }
}
@keyframes blink {
@@ -734,7 +871,7 @@ body {
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(20px); }
from { opacity: 0; transform: translateX(16px); }
to { opacity: 1; transform: translateX(0); }
}
@@ -751,7 +888,7 @@ body {
/* ---- Scrollbar ---- */
::-webkit-scrollbar {
width: 6px;
width: 4px;
}
::-webkit-scrollbar-track {
@@ -760,7 +897,7 @@ body {
::-webkit-scrollbar-thumb {
background: var(--color-border);
border-radius: 3px;
border-radius: 2px;
}
::-webkit-scrollbar-thumb:hover {

View File

@@ -108,8 +108,12 @@ function App() {
className="btn-icon"
onClick={() => setShowConfig((v) => !v)}
title="设置"
aria-label="设置"
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="3" />
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
</svg>
</button>
</div>
</header>
@@ -161,8 +165,11 @@ function App() {
)}
{!isConnected && !stream && (
<div className="video-placeholder">
<span className="video-placeholder__icon">📷</span>
<span></span>
<svg className="video-placeholder__icon" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round">
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" />
<circle cx="12" cy="13" r="4" />
</svg>
<span></span>
</div>
)}
</div>

View File

@@ -44,13 +44,17 @@ export function ChatPanel({ messages, currentReply, connectionStatus }: ChatPane
if (connectionStatus !== "connected") {
return (
<div className="chat-panel chat-panel--empty">
<span className="chat-panel--empty-icon">💬</span>
<p></p>
<span className="chat-panel--empty-hint"> AI </span>
</div>
);
}
return (
<div className="chat-panel chat-panel--empty">
<span className="chat-panel--empty-icon">🎙</span>
<p></p>
<span className="chat-panel--empty-hint">AI </span>
</div>
);
}

6
功能创意.md Normal file
View File

@@ -0,0 +1,6 @@
1.视频录制
2.对话翻译
3.对话总结
4.手动对话功能
5.视频框大小可调整,可最小化然后拖动
6.增加对话情景选择功能,参考豆包(面试官,英语老师,辩论赛,同声翻译等)