lv преди 2 седмици
родител
ревизия
46b2d18be3
променени са 4 файла, в които са добавени 207 реда и са изтрити 293 реда
  1. 14 38
      src-tauri/src/lib.rs
  2. 3 2
      src-tauri/tauri.conf.json
  3. 108 1
      src/App.css
  4. 82 252
      src/App.tsx

+ 14 - 38
src-tauri/src/lib.rs

@@ -16,11 +16,9 @@ use recording::RecordingSession;
 /// 子 webview 的 label
 pub(crate) const CONTENT_WEBVIEW_LABEL: &str = "content";
 
-const WIN_FRAME: (f64, f64, f64, f64) = (2.0, 2.0, 2.0, 26.0);
-
 /// 子 webview 初始尺寸:1280×720 横屏
-const DEFAULT_CONTENT_W: f64 = 1280.0 + 180.0;
-const DEFAULT_CONTENT_H: f64 = 720.0 + 34.0;
+const DEFAULT_CONTENT_W: f64 = 1280.0f64;
+const DEFAULT_CONTENT_H: f64 = 720.0f64;
 
 /// 子 webview 初始 url(空白页占位,等用户从任务列表选)
 const INITIAL_URL: &str = "about:blank";
@@ -124,30 +122,26 @@ fn set_window_size(
     app: tauri::AppHandle,
     width: f64,
     height: f64,
+    content_x: f64,
+    content_y: f64,
     content_width: f64,
     content_height: f64,
 ) -> Result<(), String> {
     println!(
-        "===========:{},{},{},{}",
-        width, height, content_width, content_height
+        "===========:w{},h{},cx{},cy{},cw{},ch{}",
+        width, height, content_x, content_y, content_width, content_height
     );
     // 这里取底层 `Window`(而不是 `WebviewWindow`),与 setup 阶段保持一致
     let window = app
         .get_window("main")
         .ok_or_else(|| "找不到主窗口".to_string())?;
     window
-        .set_size(LogicalSize::new(
-            width + WIN_FRAME.0 + WIN_FRAME.2,
-            height + WIN_FRAME.1 + WIN_FRAME.3,
-        ))
+        .set_size(LogicalSize::new(width, height))
         .map_err(|e| e.to_string())?;
 
     let content = find_content_webview(&app)?;
     content
-        .set_position(LogicalPosition::new(
-            WIN_FRAME.0 + width - content_width,
-            WIN_FRAME.1 + height - content_height,
-        ))
+        .set_position(LogicalPosition::new(content_x, content_y))
         .map_err(|e| e.to_string())?;
     content
         .set_size(LogicalSize::new(content_width, content_height))
@@ -157,8 +151,6 @@ fn set_window_size(
 
 #[cfg_attr(mobile, tauri::mobile_entry_point)]
 pub fn run() {
-    use tauri_plugin_decorum::WebviewWindowExt; // adds helper methods to WebviewWindow
-
     tauri::Builder::default()
         .plugin(tauri_plugin_opener::init())
         .plugin(shortcuts::record_shortcut_plugin())
@@ -173,22 +165,6 @@ pub fn run() {
             // 在主窗口(也是 React UI 所在的 webview)所属的 Window 上挂一个 child webview
             // 注意:`add_child` 定义在 `Window` 上,不在 `WebviewWindow` 上,所以这里取 Window
 
-            let main_window = app.get_webview_window("main").unwrap();
-            main_window.create_overlay_titlebar().unwrap();
-
-            // // Some macOS-specific helpers
-            // #[cfg(target_os = "macos")] {
-            // 	// Set a custom inset to the traffic lights
-            // 	window.set_traffic_lights_inset(12.0, 16.0).unwrap();
-
-            // 	// Make window transparent without privateApi
-            // 	window.make_transparent().unwrap()
-
-            // 	// Set window level
-            // 	// NSWindowLevel: https://developer.apple.com/documentation/appkit/nswindowlevel
-            // 	window.set_window_level(25).unwrap()
-            // }
-
             // 1) 先把窗口「客户区」调到目标值 = 左栏 + 工作区宽 × 工具栏高 + 工作区高
             //    必须放在 add_child 之前:否则 child webview 是在还很小的客户区里被创建,
             //    会被夹紧到当时的客户区大小并把右上方工具栏盖住。
@@ -218,14 +194,14 @@ pub fn run() {
                         });
                     }),
                 LogicalPosition::new(0, 0),
-                LogicalSize::new(DEFAULT_CONTENT_W, DEFAULT_CONTENT_H),
+                LogicalSize::new(
+                    DEFAULT_CONTENT_W + 180f64 + 4f64,
+                    DEFAULT_CONTENT_H + 32f64 + 24f64 + 4f64,
+                ),
             )?;
 
-            content.set_position(LogicalPosition::new(WIN_FRAME.0 + 180f64, WIN_FRAME.1))?;
-            content.set_size(LogicalSize::new(
-                DEFAULT_CONTENT_W + WIN_FRAME.0,
-                DEFAULT_CONTENT_W + WIN_FRAME.1,
-            ))?;
+            content.set_position(LogicalPosition::new(182f64, 34.0f64))?;
+            content.set_size(LogicalSize::new(DEFAULT_CONTENT_W, DEFAULT_CONTENT_W))?;
 
             Ok(())
         })

+ 3 - 2
src-tauri/tauri.conf.json

@@ -13,8 +13,9 @@
     "windows": [
       {
         "title": "auto-record",
-        "width": 1208,
-        "height": 832,
+        "width": 1464,
+        "height": 780,
+        "decorations": false,
         "maximizable": false,
         "minimizable": true,
         "closable": true,

+ 108 - 1
src/App.css

@@ -1,11 +1,118 @@
 /* ===== Tailwind CSS v4 入口(必须放在最顶部)===== */
 @import "tailwindcss";
 
+/* ===== 主题 token:与 Ant Design 6 设计规范对齐 =====
+ * 参考:https://ant-design.antgroup.com/docs/react/customize-theme-cn
+ * 仅声明 Tailwind 用到的 utility 所需 token;JS 侧后续接入 antd ConfigProvider 时
+ * 应保持 seed token 与此处一致(colorPrimary / colorSuccess / borderRadius 等)。
+ */
+@theme {
+  /* ---- 字体栈(antd 默认 system font stack) ---- */
+  --font-sans:
+    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
+    Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
+    "Segoe UI Symbol", "Noto Color Emoji";
+
+  /* ---- 品牌色:蓝色 10 阶(antd Blue palette,主色 = blue-6 #1677ff) ---- */
+  --color-primary-1: #e6f4ff;
+  --color-primary-2: #bae0ff;
+  --color-primary-3: #91caff;
+  --color-primary-4: #69b1ff;
+  --color-primary-5: #4096ff;
+  --color-primary-6: #1677ff;
+  /* colorPrimary */
+  --color-primary-7: #0958d9;
+  --color-primary-8: #003eb3;
+  --color-primary-9: #002c8c;
+  --color-primary-10: #001d66;
+  --color-primary: #1677ff;
+
+  /* ---- 状态色(与 antd seed token 完全一致) ---- */
+  --color-success: #52c41a;
+  --color-warning: #faad14;
+  --color-error: #ff4d4f;
+  --color-info: #1677ff;
+
+  /* ---- 中性灰:antd 13 级灰阶 ---- */
+  --color-gray-1: #ffffff;
+  --color-gray-2: #fafafa;
+  --color-gray-3: #f5f5f5;
+  --color-gray-4: #f0f0f0;
+  --color-gray-5: #d9d9d9;
+  --color-gray-6: #bfbfbf;
+  --color-gray-7: #8c8c8c;
+  --color-gray-8: #595959;
+  --color-gray-9: #434343;
+  --color-gray-10: #262626;
+  --color-gray-11: #1f1f1f;
+  --color-gray-12: #141414;
+  --color-gray-13: #000000;
+
+  /* ---- 字号(对齐 antd fontSize 系列;基础 14px) ----
+   * antd: SM=12 / base=14 / LG=16 / XL=20 / H3=24 / H2=30 / H1=38
+   */
+  --text-xs: 12px;
+  --text-xs--line-height: 1.66;
+  --text-sm: 14px;
+  --text-sm--line-height: 1.5714285714285714;
+  --text-base: 14px;
+  --text-base--line-height: 1.5714285714285714;
+  --text-lg: 16px;
+  --text-lg--line-height: 1.5;
+  --text-xl: 20px;
+  --text-xl--line-height: 1.4;
+  --text-2xl: 24px;
+  --text-2xl--line-height: 1.35;
+  --text-3xl: 30px;
+  --text-3xl--line-height: 1.3;
+  --text-4xl: 38px;
+  --text-4xl--line-height: 1.21;
+
+  /* ---- 间距:基于 4px step(Tailwind v4 默认即 4px,此处显式声明保持一致) ----
+   * 用法:p-1=4 / p-2=8 / p-3=12 / p-4=16 / p-5=20 / p-6=24 / p-8=32 / p-12=48
+   */
+  --spacing: 4px;
+
+  /* ---- 圆角:对齐 antd borderRadius 四档 ---- */
+  --radius-xs: 2px;
+  --radius-sm: 4px;
+  --radius-md: 6px;
+  /* antd 基础 borderRadius */
+  --radius-lg: 8px;
+  --radius: 6px;
+
+  /* ---- 阴影:对齐 antd 三档 boxShadow ---- */
+  --shadow-sm:
+    0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02),
+    0 2px 4px 0 rgba(0, 0, 0, 0.02);
+  --shadow-md:
+    0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12),
+    0 9px 28px 8px rgba(0, 0, 0, 0.05);
+  --shadow-lg:
+    0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12),
+    0 9px 28px 8px rgba(0, 0, 0, 0.05);
+}
+
+/* ===== 全局基础样式 ===== */
+:root {
+  font-family:
+    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
+    Arial, "Noto Sans", sans-serif;
+  font-size: 14px;
+  line-height: 1.5714;
+  color: #1f1f1f;
+  background-color: #ffffff;
+
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-text-size-adjust: 100%;
+}
 
 html,
 body,
 #root {
   height: 100%;
   margin: 0;
-  background-color: #000;
 }

+ 82 - 252
src/App.tsx

@@ -3,7 +3,7 @@ import { invoke } from "@tauri-apps/api/core";
 import { listen, type UnlistenFn } from "@tauri-apps/api/event";
 import { openUrl, revealItemInDir } from "@tauri-apps/plugin-opener";
 import { Button, ConfigProvider, Divider, InputNumber, Layout, Menu, notification, Space, theme, Tooltip } from "antd";
-import { mockTasks, type Task } from "./mocks/tasks";
+import { mockTasks } from "./mocks/tasks";
 import {
   EVT_RECORDING_FAILED,
   EVT_RECORDING_FINISHED,
@@ -41,7 +41,7 @@ import {
   VideoCameraOutlined,
 } from "@ant-design/icons";
 
-const { Header, Content, Footer, Sider } = Layout;
+const { Header, Content, Sider } = Layout;
 
 /**
  * 与 Rust 端常量保持一致(src-tauri/src/lib.rs):
@@ -50,14 +50,12 @@ const { Header, Content, Footer, Sider } = Layout;
  *
  * 导出以避免 noUnusedLocals 误报;后续如有组件需要可直接 import。
  */
-export const LEFT_PANEL_WIDTH = 180;
-export const TOOLBAR_HEIGHT = 48;
 
 /** 工具栏右侧的尺寸预设(统一横屏:宽 × 高) */
 const SIZE_PRESETS = [
-  { label: "1920×1080", w: 1920, h: 1080 },
-  { label: "1024×768", w: 1024, h: 768 },
   { label: "1280×720", w: 1280, h: 720 },
+  { label: "1920×1080", w: 1920, h: 1080 },
+  { label: "1024×768", w: 1024, h: 768 }
 ];
 
 
@@ -84,8 +82,11 @@ function App() {
   const [activeId, setActiveId] = useState<string | null>(null);
 
   const [contentSize, setContentSize] = useState<{ w: number; h: number }>({ w: 1280, h: 720 });
+  const contentSizeRef = useRef(contentSize);
   const [customMode, setCustomMode] = useState(false);
-
+  const isCustomSize = useCallback(() => {
+    return !SIZE_PRESETS.find(({ w, h }) => w == contentSize.w && h == contentSize.h);
+  }, [contentSize])
   // 录制状态机(由 lib/recorder.ts 内部单例维护,这里订阅以驱动按钮)
   const [recordState, setRecordState] = useState<RecordState>(getRecordState());
 
@@ -102,7 +103,7 @@ function App() {
   // antd 6 notification 必须用 hook + contextHolder 才能正确取到主题
   const [notifyApi, notifyContext] = notification.useNotification();
 
-  function handleSelectTask(key: string) {
+  function handleSelectTask({ key }: { key: string }) {
     const t = mockTasks.find((v) => v.id == key);
     if (!t) {
       return;
@@ -393,54 +394,78 @@ function App() {
   // 停止按钮仅在 recording 时可点
   const stopDisabled = recordState !== "recording";
 
-  const applyWorkAreaSize = useCallback(async (w: number, h: number) => {
+  const [collapsed, setCollapsed] = useState(false);
+
+  const applyWorkAreaSize = useCallback(async (w?: number, h?: number) => {
+    if (w && h) {
+      setContentSize({ w, h });
+      contentSizeRef.current = { w, h };
+    } else {
+      w = contentSize.w;
+      h = contentSize.h;
+    }
     try {
       // 先调主窗口,避免子 webview 越界
       await invoke("set_window_size", {
-        width: w + 180,
-        height: h + 46,
+        width: w + (collapsed ? 30 : 180) + 4,
+        height: h + 48 + 24 + 4,
+        contentX: (collapsed ? 30 : 180) + 2,
+        contentY: 50,
         contentWidth: w,
         contentHeight: h
       });
-      setContentSize({ w, h })
     } catch (e) {
-      console.error("调整尺寸失败:", e);
+      console.error("调整尺寸失败 ", e);
     }
-  }, [setContentSize]);
+  }, [contentSize, setContentSize]);
 
-  // 首次挂载时按默认 contentSize 调整窗口;依赖项故意为空(仅初始化用)。
   useEffect(() => {
-    applyWorkAreaSize(contentSize.w, contentSize.h);
-    // eslint-disable-next-line react-hooks/exhaustive-deps
+    applyWorkAreaSize();
   }, []);
 
   // 录制状态机(RecordState 类型已抽到 src/types/ipc.ts),录制功能下一阶段接入
-  const [collapsed, setCollapsed] = useState(false);
+  const handleCollaspsed = useCallback(async (c: boolean) => {
+    setCollapsed(c);
+    try {
+      // 先调主窗口,避免子 webview 越界
+      await invoke("set_window_size", {
+        width: contentSize.w + (collapsed ? 30 : 180),
+        height: contentSize.h + 60,
+        contentWidth: contentSize.w,
+        contentHeight: contentSize.h
+      });
+    } catch (e) {
+      console.error("调整尺寸失败:", e);
+    }
+  }, [setCollapsed]);
   const {
     token: { colorBgContainer },
   } = theme.useToken();
+
+  const [statusColor, setStatusColor] = useState("#888");
+  const [statusText, setStatusText] = useState("请选择任务");
   return (
     <Layout className="m-0 p-0 h-full overflow-hidden">
       <Header className="app-title h-8 select-none" style={{ paddingLeft: 8 }} >
         <div className="flex h-full flex-row items-center">
-          <Space size={8}>
-            <Button className="app-close" icon={<CloseOutlined />} size="large" />
-            <Button icon={<BankOutlined />} disabled size="large" />
-            <Button className="app-minimize" icon={<MinusOutlined />} size="large" />
+          <Space size={4}>
+            <Button className="app-close" icon={<CloseOutlined />} size="large" type="text" />
+            <Button icon={<BankOutlined />} disabled size="large" type="text" />
+            <Button className="app-minimize" icon={<MinusOutlined />} size="large" type="text" />
           </Space>
           <div className="w-32" />
-          <Tooltip title={activeId ? "截图整页(覆盖之前的自动截图)" : "请先选择任务"} placement="top">
+          <Tooltip title={activeId ? "截图整页(覆盖之前的自动截图)" : "请先选择任务"} placement="right">
             <Button
 
               shape="circle"
               icon={<PictureOutlined />}
               disabled={!activeId}
-              onClick={() => void handleManualCapture()}
+              onClick={handleManualCapture}
             />
           </Tooltip>
           <Divider orientation="vertical" />
           {/* 开始按钮:仅 idle + 已选任务时可点 */}
-          <Tooltip title={startConfig.tip} placement="top">
+          <Tooltip title={startConfig.tip} placement="right">
             <Button
 
               shape="circle"
@@ -456,7 +481,7 @@ function App() {
                 ? "无进行中的录制"
                 : "停止录制并落盘 mp4(F11)"
             }
-            placement="top"
+            placement="right"
           >
             <Button
 
@@ -478,7 +503,7 @@ function App() {
                     ? "在文件管理器中显示截图"
                     : "打开截图目录"
             }
-            placement="top"
+            placement="right"
           >
             <Button
 
@@ -495,7 +520,7 @@ function App() {
                 ? "暂无截图可预览"
                 : "在新窗口预览截图"
             }
-            placement="top"
+            placement="right"
           >
             <Button
 
@@ -512,7 +537,7 @@ function App() {
                 ? "暂无录制视频可预览"
                 : "在新窗口预览录制视频"
             }
-            placement="top"
+            placement="right"
           >
             <Button
 
@@ -523,23 +548,26 @@ function App() {
             />
           </Tooltip>
           <div className="flex-1">
-            {notifyContext}
+
           </div>
           {!customMode ? (
             <>
               {SIZE_PRESETS.map((p) => (
                 <Button
                   key={p.label}
+                  type={contentSize.w == p.w && contentSize.h == p.h ? "primary" : undefined}
 
-                  disabled={contentSize.w == p.w && contentSize.h == p.h}
-                  onClick={() => applyWorkAreaSize(p.w, p.h)}
+                  onClick={contentSize.w != p.w || contentSize.h != p.h ? () => applyWorkAreaSize(p.w, p.h) as any : undefined}
                 >
                   {p.label}
                 </Button>
               ))}
-              <Button onClick={() => setCustomMode(true)}>
-                自定义
-              </Button>
+              {isCustomSize() ?
+                <Button type="primary" onClick={() => setCustomMode(true)}>
+                  自定义({contentSize.w}x{contentSize.h})
+                </Button> : <Button onClick={() => setCustomMode(true)}>
+                  自定义
+                </Button>}
             </>
           ) : (
             <>
@@ -561,13 +589,19 @@ function App() {
                 style={{ width: 80 }}
               />
               <Button
-
                 type="primary"
-                onClick={() => applyWorkAreaSize(contentSize.w, contentSize.h)}
+                onClick={() => {
+                  setCustomMode(false);
+                  applyWorkAreaSize();
+
+                }}
               >
                 应用
               </Button>
-              <Button onClick={() => setCustomMode(false)}>
+              <Button onClick={() => {
+                setContentSize(contentSizeRef.current)
+                setCustomMode(false);
+              }}>
                 取消
               </Button>
             </>
@@ -575,13 +609,13 @@ function App() {
         </div>
       </Header>
       <Layout>
-        <Sider className="select-none" collapsible collapsed={collapsed} onCollapse={(c) => setCollapsed(c)} width={180} style={{ background: colorBgContainer }}>
+        <Sider className="select-none" collapsible collapsed={collapsed} onCollapse={(c) => handleCollaspsed(c)} width={180} style={{ background: colorBgContainer }}>
           <Menu
             mode="inline"
             defaultSelectedKeys={[activeId || '']}
             selectedKeys={[activeId || '']}
             style={{ height: '100%', borderInlineEnd: 0 }}
-            onClick={({ key }) => handleSelectTask(key)}
+            onClick={handleSelectTask}
 
           >
             {mockTasks.map((t) => <Menu.Item icon={<LinkOutlined />} key={t.id}>
@@ -590,221 +624,17 @@ function App() {
             )}
           </Menu>
         </Sider>
-        <Layout>
-          <Content className="m-0 p-1 h-full w-full bg-indigo-950" />
+        <Layout className="flex flex-row">
+          <Content className="w-full m-0 p-1 flex-1" />
+          <div className="w-full h-6 flex items-center bg-red-400 px-2 text-gray-3">
+            <div className="bg-gray-6 rounded-full w-3 h-3 m-2" style={{ backgroundColor: statusColor }} />
+            <div className="w-32">{statusText}</div>
+            <span className="flex-1">{notifyContext}</span>
+          </div>
         </Layout>
       </Layout>
-    </Layout>
+    </Layout >
   );
-  // <div data-tauri-decorum-tb className="flex h-screen w-screen overflow-hidden select-none">
-  //   {/* antd notification 的渲染容器 —— 必须挂在树中 */}
-  //   {notifyContext}
-  //   {/* ===== 左栏:任务列表(180px) ===== */}
-  //   <aside data-tauri-drag-region className="w-[180px] shrink-0 border-r border-gray-4 bg-gray-2 h-full flex flex-col">
-  //     <div className="px-3 py-2 text-xs text-gray-7 border-b border-gray-4 select-none">
-  //       任务列表
-  //     </div>
-  //     <div className="flex-1 overflow-y-auto">
-  //       <ul>
-  //         {mockTasks.map((t) => {
-  //           const active = activeId === t.id;
-  //           return (
-  //             <li
-  //               key={t.id}
-  //               className={
-  //                 "flex items-center justify-between px-3 py-2 text-sm cursor-pointer transition-colors " +
-  //                 (active
-  //                   ? "bg-primary-1 text-primary-7"
-  //                   : "hover:bg-gray-3 text-gray-10")
-  //               }
-  //               onClick={() => handleSelectTask(t)}
-  //             >
-  //               <span className="truncate">任务 {t.id}</span>
-  //               <button
-  //                 type="button"
-  //                 title="在系统浏览器打开"
-  //                 className="ml-2 inline-flex items-center justify-center w-6 h-6 rounded-sm hover:bg-gray-4 text-gray-7 hover:text-primary-6"
-  //                 onClick={(e) => {
-  //                   e.stopPropagation();
-  //                   void openInBrowser(t.url);
-  //                 }}
-  //               >
-  //                 {/* 外链图标(lucide external-link 同款 path,不引入新依赖) */}
-  //                 <svg
-  //                   width="14"
-  //                   height="14"
-  //                   viewBox="0 0 24 24"
-  //                   fill="none"
-  //                   stroke="currentColor"
-  //                   strokeWidth="2"
-  //                   strokeLinecap="round"
-  //                   strokeLinejoin="round"
-  //                 >
-  //                   <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
-  //                   <polyline points="15 3 21 3 21 9" />
-  //                   <line x1="10" y1="14" x2="21" y2="3" />
-  //                 </svg>
-  //               </button>
-  //             </li>
-  //           );
-  //         })}
-  //       </ul>
-  //     </div>
-  //   </aside>
-
-  //   {/* ===== 右侧主区 ===== */}
-  //   <main className="flex-1 flex flex-col min-w-0">
-  //     {/* 工具栏(48px) */}
-  //     <header className="h-12 shrink-0 flex items-center justify-between gap-2 px-3 border-b border-gray-4 bg-gray-1">
-  //       {/* 左侧:操作按钮区 */}
-  //       <div className="flex items-center gap-2">
-  //         <Tooltip title={activeId ? "截图整页(覆盖之前的自动截图)" : "请先选择任务"} placement="top">
-  //           <Button
-  //             
-  //             icon={<PictureOutlined />}
-  //             disabled={!activeId}
-  //             onClick={() => void handleManualCapture()}
-  //           />
-  //         </Tooltip>
-  //         <Divider type="vertical" />
-  //         {/* 开始按钮:仅 idle + 已选任务时可点 */}
-  //         <Tooltip title={startConfig.tip} placement="top">
-  //           <Button
-  //             
-  //             icon={startConfig.icon}
-  //             disabled={startDisabled}
-  //             onClick={() => void handleStartRecord()}
-  //           />
-  //         </Tooltip>
-  //         {/* 停止按钮:仅 recording 时可用 */}
-  //         <Tooltip
-  //           title={
-  //             stopDisabled
-  //               ? "无进行中的录制"
-  //               : "停止录制并落盘 mp4(F11)"
-  //           }
-  //           placement="top"
-  //         >
-  //           <Button
-  //             
-  //             icon={<StopOutlined />}
-  //             disabled={stopDisabled}
-  //             onClick={() => void handleStopRecord()}
-  //           />
-  //         </Tooltip>
-  //         <Divider type="vertical" />
-  //         {/* 打开文件夹:reveal mp4 → png → 兜底 screenshots 目录 */}
-  //         <Tooltip
-  //           title={
-  //             !activeId
-  //               ? "请先选择任务"
-  //               : assets?.recording_exists
-  //                 ? "在文件管理器中显示录制视频"
-  //                 : assets?.screenshot_exists
-  //                   ? "在文件管理器中显示截图"
-  //                   : "打开截图目录"
-  //           }
-  //           placement="top"
-  //         >
-  //           <Button
-  //             
-  //             icon={<FolderOpenOutlined />}
-  //             disabled={!assets}
-  //             onClick={() => void handleOpenFolder()}
-  //           />
-  //         </Tooltip>
-  //         {/* 预览截图:仅当 png 存在 */}
-  //         <Tooltip
-  //           title={
-  //             !assets?.screenshot_exists
-  //               ? "暂无截图可预览"
-  //               : "在新窗口预览截图"
-  //           }
-  //           placement="top"
-  //         >
-  //           <Button
-  //             
-  //             icon={<FileImageOutlined />}
-  //             disabled={!assets?.screenshot_exists}
-  //             onClick={() => void handlePreviewImage()}
-  //           />
-  //         </Tooltip>
-  //         {/* 预览视频:仅当 mp4 存在 */}
-  //         <Tooltip
-  //           title={
-  //             !assets?.recording_exists
-  //               ? "暂无录制视频可预览"
-  //               : "在新窗口预览录制视频"
-  //           }
-  //           placement="top"
-  //         >
-  //           <Button
-  //             
-  //             icon={<VideoCameraOutlined />}
-  //             disabled={!assets?.recording_exists}
-  //             onClick={() => void handlePreviewVideo()}
-  //           />
-  //         </Tooltip>
-  //       </div>
-
-  //       {/* 右侧:尺寸预设 / 自定义 */}
-  //       <div className="flex items-center gap-2">
-  //         {!customMode ? (
-  //           <>
-  //             {SIZE_PRESETS.map((p) => (
-  //               <Button
-  //                 key={p.label}
-  //                 
-  //                 disabled={contentSize.w == p.w && contentSize.h == p.h}
-  //                 onClick={() => applyWorkAreaSize(p.w, p.h)}
-  //               >
-  //                 {p.label}
-  //               </Button>
-  //             ))}
-  //             <Button  onClick={() => setCustomMode(true)}>
-  //               自定义
-  //             </Button>
-  //           </>
-  //         ) : (
-  //           <>
-  //             <InputNumber
-  //               
-  //               min={200}
-  //               max={3840}
-  //               value={contentSize.w}
-  //               onChange={(v) => setContentSize({ w: v || 0, h: contentSize.h })}
-  //               style={{ width: 80 }}
-  //             />
-  //             <span className="text-gray-7 select-none">×</span>
-  //             <InputNumber
-  //               
-  //               min={200}
-  //               max={2160}
-  //               value={contentSize.h}
-  //               onChange={(v) => setContentSize({ w: contentSize.w, h: v || 0 })}
-  //               style={{ width: 80 }}
-  //             />
-  //             <Button
-  //               
-  //               type="primary"
-  //               onClick={() => applyWorkAreaSize(contentSize.w, contentSize.h)}
-  //             >
-  //               应用
-  //             </Button>
-  //             <Button  onClick={() => setCustomMode(false)}>
-  //               取消
-  //             </Button>
-  //           </>
-  //         )}
-  //       </div>
-  //     </header>
-
-  //     {/* 工作区占位:实际由 Rust child webview 覆盖在此区域之上 */}
-  //     <section className="flex-1 bg-gray-7" />
-  //     <section className="h-6 bg-gray-5 broder border-gray-7" />
-  //   </main>
-  // </div>
-  // );
 }