- {/* 背景图(与原 Dart MobileLayout 一致) */}
-
-
+
+ {/* 背景图(与原 Dart MobileLayout 一致) */}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 浏览器提示(应用内浏览器检测) */}
+
+
+ {/* PWA 安装提示触发器(无可见 UI,3.5s 后弹 dialog) */}
+
+
+ {/* 配额告警弹窗 */}
+
setQuotaDialogOpen(false)}
+ isExhausted={quotaExhausted}
/>
-
-
-
-
-
-
-
-
-
- {/* 浏览器提示(应用内浏览器检测) */}
-
-
- {/* PWA 安装提示触发器(无可见 UI,3.5s 后弹 dialog) */}
-
-
- {/* 配额告警弹窗 */}
- setQuotaDialogOpen(false)}
- isExhausted={quotaExhausted}
- />
-
+
);
}
diff --git a/src/app/chat/components/function-button-bar.module.css b/src/app/chat/components/function-button-bar.module.css
deleted file mode 100644
index 4ef3e11e..00000000
--- a/src/app/chat/components/function-button-bar.module.css
+++ /dev/null
@@ -1,30 +0,0 @@
-/* FunctionButtonBar 功能按钮栏样式 */
-
-.bar {
- display: flex;
- gap: var(--spacing-2, 8px);
- padding: 0 var(--spacing-5, 20px) var(--spacing-2, 8px);
- overflow-x: auto;
-}
-
-.button {
- flex: 0 0 auto;
- height: 36px;
- padding: 0 var(--spacing-3, 12px);
- border: 0;
- border-radius: 9999px;
- background: var(--color-button-background, rgba(255, 255, 255, 0.1));
- color: var(--color-text-primary, #fff);
- font-size: var(--font-size-sm, 12px);
- cursor: pointer;
- display: inline-flex;
- align-items: center;
- gap: var(--spacing-1, 4px);
-}
-
-.icon {
- width: 16px;
- height: 16px;
- font-size: 16px;
- line-height: 1;
-}
diff --git a/src/app/chat/components/function-button-bar.tsx b/src/app/chat/components/function-button-bar.tsx
deleted file mode 100644
index a90737bb..00000000
--- a/src/app/chat/components/function-button-bar.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-"use client";
-/**
- * FunctionButtonBar 功能按钮栏
- *
- * 原始 Dart: lib/ui/chat/widgets/function_button_bar.dart(52 行)
- *
- * 横向滚动按钮组:Restart / History / Call / 等
- * 每个按钮:图标 + 标签
- */
-import styles from "./function-button-bar.module.css";
-
-export interface FunctionButtonItem {
- key: string;
- icon: string; // emoji 占位(生产环境可换为 react-icons / svg)
- label: string;
- onClick?: () => void;
-}
-
-export interface FunctionButtonBarProps {
- items?: readonly FunctionButtonItem[];
-}
-
-const DEFAULT_ITEMS: readonly FunctionButtonItem[] = [
- { key: "restart", icon: "↻", label: "Restart" },
- { key: "history", icon: "⏱", label: "History" },
- { key: "call", icon: "📞", label: "Call" },
-];
-
-export function FunctionButtonBar({
- items = DEFAULT_ITEMS,
-}: FunctionButtonBarProps) {
- return (
-
- {items.map((it) => (
-
- ))}
-
- );
-}
diff --git a/src/app/chat/components/image-upload-button.module.css b/src/app/chat/components/image-upload-button.module.css
deleted file mode 100644
index 6586656a..00000000
--- a/src/app/chat/components/image-upload-button.module.css
+++ /dev/null
@@ -1,62 +0,0 @@
-/* ImageUploadButton 图片上传按钮样式 */
-
-.button {
- flex: 0 0 auto;
- width: 40px;
- height: 40px;
- border: 1px solid var(--color-bubble-border, rgba(255, 255, 255, 0.2));
- border-radius: 9999px;
- background: var(--color-bubble-transparent, rgba(255, 255, 255, 0));
- color: var(--color-text-primary, #fff);
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: opacity 0.2s;
-}
-
-.button:disabled {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-.hidden {
- display: none;
-}
-
-.bottomSheet {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- z-index: 50;
- background: var(--color-dark-surface, #1f1a2e);
- border-top-left-radius: var(--radius-xl, 20px);
- border-top-right-radius: var(--radius-xl, 20px);
- padding: var(--spacing-4, 16px) 0;
- padding-bottom: max(var(--spacing-4, 16px), env(safe-area-inset-bottom));
- box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.3);
-}
-
-.sheetItem {
- display: flex;
- align-items: center;
- gap: var(--spacing-3, 12px);
- padding: var(--spacing-3, 12px) var(--spacing-5, 20px);
- background: transparent;
- border: 0;
- color: var(--color-text-primary, #fff);
- font-size: var(--font-size-base, 14px);
- width: 100%;
- text-align: left;
- cursor: pointer;
-}
-
-.sheetItem:hover,
-.sheetItem:focus-visible {
- background: rgba(255, 255, 255, 0.05);
-}
-
-.sheetIcon {
- font-size: 20px;
-}
diff --git a/src/app/chat/components/image-upload-button.tsx b/src/app/chat/components/image-upload-button.tsx
deleted file mode 100644
index 17c71ee3..00000000
--- a/src/app/chat/components/image-upload-button.tsx
+++ /dev/null
@@ -1,119 +0,0 @@
-"use client";
-/**
- * ImageUploadButton 图片上传按钮
- *
- * 原始 Dart: lib/ui/chat/widgets/image_upload_button.dart(159 行)
- *
- * 原 Dart 使用 image_picker 库(移动端原生相册/相机)
- * 本轮使用 Web File API(`
`)以避免添加原生依赖
- *
- * 行为:
- * - 点击按钮 → 弹出底部 sheet(相册 / 取消)
- * - 选择图片 → 读取为 base64 → dispatch ChatSendImage
- * - 上传中按钮显示加载指示器
- */
-import { useRef, useState } from "react";
-
-import { useChatDispatch } from "@/stores/chat/chat-context";
-
-import styles from "./image-upload-button.module.css";
-
-export interface ImageUploadButtonProps {
- disabled?: boolean;
-}
-
-export function ImageUploadButton({ disabled = false }: ImageUploadButtonProps) {
- const dispatch = useChatDispatch();
- const fileInputRef = useRef
(null);
- const [showSheet, setShowSheet] = useState(false);
- const [isUploading, setIsUploading] = useState(false);
-
- const handleFile = async (file: File) => {
- if (isUploading) return;
- setIsUploading(true);
- setShowSheet(false);
-
- try {
- const buffer = await file.arrayBuffer();
- const bytes = new Uint8Array(buffer);
- let binary = "";
- for (let i = 0; i < bytes.byteLength; i++) {
- binary += String.fromCharCode(bytes[i]);
- }
- const base64 = window.btoa(binary);
- const ext = file.name.split(".").pop()?.toLowerCase() || "png";
- const mime = file.type || `image/${ext}`;
- const dataUri = `data:${mime};base64,${base64}`;
-
- dispatch({ type: "ChatSendImage", imageBase64: dataUri });
- } catch (e) {
- console.error("[ImageUpload] failed:", e);
- } finally {
- setIsUploading(false);
- }
- };
-
- return (
- <>
-
-
- {/* 隐藏的文件 input(触发文件选择) */}
- {
- const file = e.currentTarget.files?.[0];
- if (file) void handleFile(file);
- e.currentTarget.value = ""; // 允许重选同一文件
- }}
- />
-
- {/* 底部选择 sheet(相册/取消) */}
- {showSheet && (
- <>
- setShowSheet(false)}
- />
-
-
-
-
- >
- )}
- >
- );
-}
diff --git a/src/app/chat/components/index.ts b/src/app/chat/components/index.ts
index ac5ac0c3..e2de7188 100644
--- a/src/app/chat/components/index.ts
+++ b/src/app/chat/components/index.ts
@@ -5,9 +5,8 @@
// 屏幕 + 顶层
export { ChatScreen } from "./chat-screen";
-// 顶部 + 菜单
+// 顶部
export { ChatHeader } from "./chat-header";
-export { ChatMenuBar } from "./chat-menu-bar";
// 消息区
export { ChatArea } from "./chat-area";
@@ -16,9 +15,6 @@ export { ChatArea } from "./chat-area";
export { ChatInputBar } from "./chat-input-bar";
export { ChatInputTextField } from "./chat-input-text-field";
export { ChatSendButton } from "./chat-send-button";
-export { ChatMoreButton } from "./chat-more-button";
-export { ImageUploadButton } from "./image-upload-button";
-export { FunctionButtonBar } from "./function-button-bar";
// 消息气泡
export { MessageBubble } from "./message-bubble";
diff --git a/src/app/chat/components/pwa-install-overlay.module.css b/src/app/chat/components/pwa-install-overlay.module.css
index 42c5813b..85554c11 100644
--- a/src/app/chat/components/pwa-install-overlay.module.css
+++ b/src/app/chat/components/pwa-install-overlay.module.css
@@ -1,5 +1,5 @@
/* PwaInstallOverlay PWA 安装弹窗触发器样式(无可见 UI,逻辑触发用) */
-/.hidden {
+.hidden {
display: none;
}
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
index 280910bc..d82d3395 100644
--- a/src/utils/storage.ts
+++ b/src/utils/storage.ts
@@ -1,3 +1,4 @@
+"use client";
/**
* SpAsyncUtil — 键值对持久化工具类(基于 unstorage)
*