diff --git a/astro.config.mjs b/astro.config.mjs
index d55582c..f1723d3 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -7,7 +7,7 @@ import tailwindcss from '@tailwindcss/vite';
 
 const isDev = process.env.NODE_ENV === 'development';
 
-let target = process.env.VITE_API_URL || 'https://localhost:51015';
+let target = process.env.VITE_API_URL || 'http://localhost:4005';
 const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' };
 let proxy = {
   '/root/': {
diff --git a/package.json b/package.json
index 55c6e19..5011d34 100644
--- a/package.json
+++ b/package.json
@@ -1,14 +1,14 @@
 {
-  "name": "@kevisual/astro-simplate-template",
+  "name": "@kevisual/aura-center",
   "version": "0.0.1",
   "description": "",
   "main": "index.js",
-  "basename": "/root/astro-simplate-template",
+  "basename": "/root/aura-center",
   "scripts": {
     "dev": "astro dev",
     "build": "astro build",
     "preview": "astro preview",
-    "pub": "envision deploy ./dist -k astro-simplate-template -v 0.0.1 -u",
+    "pub": "envision deploy ./dist -k aura-center -v 0.0.1 -u",
     "sn": "pnpm dlx shadcn@latest add "
   },
   "keywords": [],
@@ -34,6 +34,7 @@
     "react-dom": "^19.2.0",
     "react-toastify": "^11.0.5",
     "tailwind-merge": "^3.3.1",
+    "wavesurfer.js": "^7.11.0",
     "zustand": "^5.0.8"
   },
   "publishConfig": {
@@ -48,5 +49,10 @@
     "tailwindcss": "^4.1.14",
     "tw-animate-css": "^1.4.0"
   },
-  "packageManager": "pnpm@10.18.3"
+  "packageManager": "pnpm@10.18.3",
+  "onlyBuiltDependencies": [
+    "@tailwindcss/oxide",
+    "esbuild",
+    "sharp"
+  ]
 }
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ee097bd..acc97a1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -62,6 +62,9 @@ importers:
       tailwind-merge:
         specifier: ^3.3.1
         version: 3.3.1
+      wavesurfer.js:
+        specifier: ^7.11.0
+        version: 7.11.0
       zustand:
         specifier: ^5.0.8
         version: 5.0.8(@types/react@19.2.2)(react@19.2.0)
@@ -2273,6 +2276,9 @@ packages:
     resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
     engines: {node: '>=0.10.0'}
 
+  wavesurfer.js@7.11.0:
+    resolution: {integrity: sha512-LOGdIBIKv/roYuQYClhoqhwbIdQL1GfobLnS2vx0heoLD9lu57OUHWE2DIsCNXBvCsmmbkUvJq9W8bPLPbikGw==}
+
   web-namespaces@2.0.1:
     resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
 
@@ -4918,6 +4924,8 @@ snapshots:
 
   void-elements@3.1.0: {}
 
+  wavesurfer.js@7.11.0: {}
+
   web-namespaces@2.0.1: {}
 
   webidl-conversions@3.0.1: {}
diff --git a/src/apps/record/Wavesurfer.tsx b/src/apps/record/Wavesurfer.tsx
new file mode 100644
index 0000000..54c047d
--- /dev/null
+++ b/src/apps/record/Wavesurfer.tsx
@@ -0,0 +1,3 @@
+export const Wavesurfer = () => {
+  return 
Wavesurfer
;
+}
\ No newline at end of file
diff --git a/src/apps/record/components/Wavesurfer.tsx b/src/apps/record/components/Wavesurfer.tsx
new file mode 100644
index 0000000..5506766
--- /dev/null
+++ b/src/apps/record/components/Wavesurfer.tsx
@@ -0,0 +1,99 @@
+import { useEffect, useRef } from "react";
+import WaveSurfer from "wavesurfer.js";
+
+interface WavesurferProps {
+  audioUrl?: string;
+  isPlaying?: boolean;
+  onPlayStateChange?: (isPlaying: boolean) => void;
+}
+
+export const Wavesurfer = ({ 
+  audioUrl, 
+  isPlaying = false, 
+  onPlayStateChange
+}: WavesurferProps) => {
+  const waveformRef = useRef(null);
+  const wavesurferRef = useRef(null);
+
+  // 播放模式的波形
+  useEffect(() => {
+    if (audioUrl && waveformRef.current && !wavesurferRef.current) {
+      wavesurferRef.current = WaveSurfer.create({
+        container: waveformRef.current,
+        waveColor: '#10b981',
+        progressColor: '#059669',
+        cursorColor: '#047857',
+        barWidth: 2,
+        barRadius: 3,
+        height: 60,
+        normalize: true,
+      });
+
+      wavesurferRef.current.load(audioUrl);
+
+      // 监听播放状态
+      wavesurferRef.current.on('play', () => onPlayStateChange?.(true));
+      wavesurferRef.current.on('pause', () => onPlayStateChange?.(false));
+      wavesurferRef.current.on('finish', () => onPlayStateChange?.(false));
+    }
+
+    return () => {
+      if (wavesurferRef.current) {
+        wavesurferRef.current.destroy();
+        wavesurferRef.current = null;
+      }
+    };
+  }, [audioUrl, onPlayStateChange]);
+
+  // 外部播放控制
+  useEffect(() => {
+    if (wavesurferRef.current) {
+      if (isPlaying) {
+        wavesurferRef.current.play();
+      } else {
+        wavesurferRef.current.pause();
+      }
+    }
+  }, [isPlaying]);
+
+  // 清理函数
+  useEffect(() => {
+    return () => {
+      if (wavesurferRef.current) {
+        wavesurferRef.current.destroy();
+      }
+    };
+  }, []);
+
+  // 暴露播放控制方法
+  const togglePlayback = () => {
+    if (wavesurferRef.current) {
+      if (wavesurferRef.current.isPlaying()) {
+        wavesurferRef.current.pause();
+      } else {
+        wavesurferRef.current.play();
+      }
+    }
+  };
+
+  // 只在有音频URL时显示
+  if (!audioUrl) {
+    return null;
+  }
+
+  return (
+    
+      
+        
+        
+          点击波形或使用播放按钮控制播放
+        
+      
+    
 
+  );
+};
+
+// 暴露控制方法的接口
+export interface WavesurferRef {
+  togglePlayback: () => void;
+}
\ No newline at end of file
diff --git a/src/apps/record/index.tsx b/src/apps/record/index.tsx
new file mode 100644
index 0000000..cd1b03c
--- /dev/null
+++ b/src/apps/record/index.tsx
@@ -0,0 +1,295 @@
+import { useState, useRef } from "react";
+import { getText } from "./modules/get-text";
+import { Wavesurfer } from "./components/Wavesurfer";
+
+interface TranscriptionResult {
+  id: string;
+  text: string;
+  timestamp: Date;
+}
+
+export const RecordApp = () => {
+  const [isRecording, setIsRecording] = useState(false);
+  const [recordingTime, setRecordingTime] = useState(0);
+  const [audioBlob, setAudioBlob] = useState(null);
+  const [audioUrl, setAudioUrl] = useState("");
+  const [isPlaying, setIsPlaying] = useState(false);
+  const [showWaveform, setShowWaveform] = useState(false);
+  const [isTranscribing, setIsTranscribing] = useState(false);
+  const [transcriptions, setTranscriptions] = useState([]);
+  const [currentText, setCurrentText] = useState("");
+
+  const mediaRecorderRef = useRef(null);
+  const audioRef = useRef(null);
+  const intervalRef = useRef(null);
+
+  const startRecording = async () => {
+    try {
+      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+      const mediaRecorder = new MediaRecorder(stream);
+      mediaRecorderRef.current = mediaRecorder;
+
+      const chunks: BlobPart[] = [];
+
+      mediaRecorder.ondataavailable = (event) => {
+        if (event.data.size > 0) {
+          chunks.push(event.data);
+        }
+      };
+
+      mediaRecorder.onstop = () => {
+        const blob = new Blob(chunks, { type: "audio/webm" });
+        setAudioBlob(blob);
+        setAudioUrl(URL.createObjectURL(blob));
+        stream.getTracks().forEach(track => track.stop());
+      };
+
+      mediaRecorder.start();
+      setIsRecording(true);
+      setRecordingTime(0);
+
+      // 开始计时
+      intervalRef.current = setInterval(() => {
+        setRecordingTime(prev => prev + 1);
+      }, 1000);
+    } catch (error) {
+      console.error("录音失败:", error);
+    }
+  };
+
+  const stopRecording = () => {
+    if (mediaRecorderRef.current && isRecording) {
+      mediaRecorderRef.current.stop();
+      setIsRecording(false);
+      if (intervalRef.current) {
+        clearInterval(intervalRef.current);
+        intervalRef.current = null;
+      }
+    }
+  };
+
+  const playAudio = () => {
+    if (audioRef.current && audioUrl) {
+      if (isPlaying) {
+        audioRef.current.pause();
+        setIsPlaying(false);
+      } else {
+        audioRef.current.play();
+        setIsPlaying(true);
+        setShowWaveform(true); // 开始播放时显示波形
+      }
+    }
+  };
+
+  const handlePlayStateChange = (playing: boolean) => {
+    setIsPlaying(playing);
+    // 如果播放结束,隐藏波形
+    if (!playing) {
+      setShowWaveform(false);
+    }
+  };
+
+  const transcribeAudio = async () => {
+    if (!audioBlob) return;
+
+    setIsTranscribing(true);
+    try {
+      // 将音频转换为base64
+      const reader = new FileReader();
+      reader.onloadend = async () => {
+        const base64Audio = (reader.result as string).split(',')[1];
+        
+        try {
+          const result = await getText(base64Audio);
+          const text = typeof result === 'string' ? result : (result as any)?.data || '转录失败';
+          setCurrentText(text);
+          
+          // 添加到转录历史
+          const newTranscription: TranscriptionResult = {
+            id: Date.now().toString(),
+            text,
+            timestamp: new Date()
+          };
+          setTranscriptions(prev => [newTranscription, ...prev]);
+        } catch (error) {
+          console.error("转录失败:", error);
+          setCurrentText("转录失败,请重试");
+        } finally {
+          setIsTranscribing(false);
+        }
+      };
+      reader.readAsDataURL(audioBlob);
+    } catch (error) {
+      console.error("处理音频失败:", error);
+      setIsTranscribing(false);
+    }
+  };
+
+  const copyText = (text: string) => {
+    navigator.clipboard.writeText(text);
+  };
+
+  const refreshTranscriptions = () => {
+    setTranscriptions([]);
+    setCurrentText("");
+  };
+
+  const formatTime = (seconds: number) => {
+    const mins = Math.floor(seconds / 60);
+    const secs = seconds % 60;
+    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
+  };
+
+  return (
+    
+      {/* 标题 */}
+      
+        
语音录制与转文字
+      
+
+      {/* 录音控制区域 */}
+      
+        
+          {/* 录音/停止按钮 */}
+          
+
+          {/* 播放按钮 - 录制时隐藏 */}
+          {!isRecording && (
+            
+          )}
+
+          {/* 转文字按钮 - 录制时隐藏 */}
+          {!isRecording && audioUrl && (
+            
+          )}
+        
+
+        {/* 录音状态显示 */}
+        {isRecording && (
+          
+            
+              
+              
录音中: {formatTime(recordingTime)}
+            
+          
+        )}
+
+        {/* 播放波形显示 - 只在需要显示且有音频时显示 */}
+        {showWaveform && audioUrl && (
+          
+        )}
+
+        {/* 隐藏的音频元素 */}
+        {audioUrl && (
+          
+
+      {/* 转录结果区域 */}
+      
+        
+          
最近转录
+          
+            
+            
+          
+        
+
+        {/* 当前转录文本 */}
+        
+          {isTranscribing ? (
+            
+          ) : currentText ? (
+            
{currentText}
+          ) : (
+            
转录结果将显示在这里
+          )}
+        
+      
+
+      {/* 历史转录记录 */}
+      {transcriptions.length > 0 && (
+        
+          
历史记录
+          
+            {transcriptions.map((item) => (
+              
+                
+                  
{item.text}
+                  
+                    {item.timestamp.toLocaleString()}
+                  
+                
+                
+              
+            ))}
+          
+        
+      )}
+    
 
+  );
+};
\ No newline at end of file
diff --git a/src/apps/record/modules/get-text.ts b/src/apps/record/modules/get-text.ts
new file mode 100644
index 0000000..8cf41b1
--- /dev/null
+++ b/src/apps/record/modules/get-text.ts
@@ -0,0 +1,38 @@
+import { query } from '../../../modules/query'
+
+export type AsrResponse = {
+  audio_info: {
+    /**
+     * 音频时长,单位为 ms
+     */
+    duration: number;
+  };
+  result: {
+    additions: {
+      duration: string;
+    };
+    text: string;
+    utterances: Array<{
+      end_time: number;
+      start_time: number;
+      text: string;
+      words: Array<{
+        confidence: number;
+        end_time: number;
+        start_time: number;
+        text: string;
+      }>;
+    }>;
+  };
+}
+export const getText = async (base64Audio: string) => {
+  const res = await query.post({
+    path: 'asr',
+    key: 'text',
+    base64Audio
+  })
+  if (res.code !== 200) {
+    return '转录失败'
+  }
+  return res.data?.result?.text || '转录失败'
+}
\ No newline at end of file
diff --git a/src/modules/query.ts b/src/modules/query.ts
new file mode 100644
index 0000000..f0e3560
--- /dev/null
+++ b/src/modules/query.ts
@@ -0,0 +1,3 @@
+import { Query } from '@kevisual/query';
+
+export const query = new Query();
\ No newline at end of file
diff --git a/src/pages/record.astro b/src/pages/record.astro
new file mode 100644
index 0000000..f9e9558
--- /dev/null
+++ b/src/pages/record.astro
@@ -0,0 +1,17 @@
+---
+import { RecordApp } from '../apps/record/index';
+import '../styles/global.css';
+---
+
+
+  
+    
+    
+    语音录制与转文字
+  
+  
+    
+      
+    
+  
+
\ No newline at end of file