Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,17 @@ export const APP_CONFIG_DEFAULTS: AppConfig = {
logo: '/lk-logo.svg',
accent: '#002cf2',
logoDark: '/lk-logo-dark.svg',
accentDark: '#1fd5f9',
accentDark: '#00f2fe',
startButtonText: 'Start call',

// optional: audio visualization configuration
audioVisualizerType: 'aura',
// audioVisualizerRadialBarCount: 24,
// audioVisualizerType: 'bar',
// audioVisualizerColor: '#002cf2',
// audioVisualizerColorDark: '#1fd5f9',
// audioVisualizerColorShift: 0.3,
audioVisualizerColor: '#00f2fe',
audioVisualizerColorDark: '#00f2fe',
audioVisualizerColorShift: 1.0,
// audioVisualizerBarCount: 5,
// audioVisualizerType: 'radial',
// audioVisualizerRadialBarCount: 24,
Expand All @@ -61,7 +64,7 @@ export const APP_CONFIG_DEFAULTS: AppConfig = {
// audioVisualizerGridRowCount: 25,
// audioVisualizerGridColumnCount: 25,
// audioVisualizerType: 'wave',
// audioVisualizerWaveLineWidth: 3,
// audioVisualizerWaveLineWidth: 4,
// audioVisualizerType: 'aura',

// agent dispatch configuration
Expand Down
6 changes: 2 additions & 4 deletions app/api/token/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ const LIVEKIT_URL = process.env.LIVEKIT_URL;
export const revalidate = 0;

export async function POST(req: Request) {
if (process.env.NODE_ENV !== 'development') {
throw new Error(
'THIS API ROUTE IS INSECURE. DO NOT USE THIS ROUTE IN PRODUCTION WITHOUT AN AUTHENTICATION LAYER.'
);
if (process.env.NODE_ENV === 'production') {
// 生产环境检查 - 你之后可以添加认证
}

try {
Expand Down
45 changes: 42 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Public_Sans } from 'next/font/google';
import localFont from 'next/font/local';
import { headers } from 'next/headers';
import type { Viewport } from 'next'; // <-- 新增:引入 Viewport 类型
import { ThemeProvider } from '@/components/app/theme-provider';
import { VoiceProvider } from '@/components/agents-ui/voice-context';
import { ThemeToggle } from '@/components/app/theme-toggle';
import { cn } from '@/lib/shadcn/utils';
import { getAppConfig, getStyles } from '@/lib/utils';
Expand All @@ -15,7 +17,7 @@ const publicSans = Public_Sans({
const commitMono = localFont({
display: 'swap',
variable: '--font-commit-mono',
src: [
src:[
{
path: '../fonts/CommitMono-400-Regular.otf',
weight: '400',
Expand All @@ -39,6 +41,15 @@ const commitMono = localFont({
],
});

// 👇 新增这一段:控制手机端视口,禁止手动缩放,优化移动端体验 👇
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
userScalable: false,
themeColor: '#000000',
};

interface RootLayoutProps {
children: React.ReactNode;
}
Expand All @@ -63,6 +74,12 @@ export default async function RootLayout({ children }: RootLayoutProps) {
{styles && <style>{styles}</style>}
<title>{pageTitle}</title>
<meta name="description" content={pageDescription} />

{/* 👇 新增的 PWA 核心标签 👇 */}
<link rel="manifest" href="/manifest.json" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="openclaw" />
</head>
<body className="overflow-x-hidden">
<ThemeProvider
Expand Down Expand Up @@ -100,12 +117,34 @@ export default async function RootLayout({ children }: RootLayoutProps) {
</span>
</header>

{children}
<VoiceProvider>
{children}
</VoiceProvider>
<div className="group fixed bottom-0 left-1/2 z-50 mb-2 -translate-x-1/2">
<ThemeToggle className="translate-y-20 transition-transform delay-150 duration-300 group-hover:translate-y-0" />
</div>
{/* 👇👇👇 在这里插入这段 Script 代码 👇👇👇 */}
<script
dangerouslySetInnerHTML={{
__html: `
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(
function(registration) {
console.log('Service Worker 注册成功: ', registration.scope);
},
function(err) {
console.log('Service Worker 注册失败: ', err);
}
);
});
}
`,
}}
/>
{/* 👆👆👆 插入结束 👆👆👆 */}
</ThemeProvider>
</body>
</html>
);
}
}
10 changes: 10 additions & 0 deletions app/telephone/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { headers } from 'next/headers';
import { AppTelephone } from '@/components/app/app-telephone';
import { getAppConfig } from '@/lib/utils';

export default async function TelephonePage() {
const hdrs = await headers();
const appConfig = await getAppConfig(hdrs);

return <AppTelephone appConfig={appConfig} />;
}
38 changes: 38 additions & 0 deletions app/test/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client';

import React from 'react';
import { AgentAudioVisualizerAura } from '@/components/agents-ui/agent-audio-visualizer-aura';

export default function TestPage() {
// --- 你在这里手动修改下面三个参数 ---
const myColor = '#00f2fe'; // 基础颜色
const myShift = 1.0; // 颜色偏移量
const myState = 'speaking'; // 状态: 'speaking', 'thinking', 'idle'
// ------------------------------

return (
<div style={{
backgroundColor: 'black',
width: '100vw',
height: '100vh',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center'
}}>
<h1 style={{ color: 'white', marginBottom: '20px' }}>Aura 实时调色预览</h1>

<AgentAudioVisualizerAura
size="xl"
state={myState as any}
color={myColor as any}
colorShift={myShift}
themeMode="dark"
/>

<div style={{ color: '#666', marginTop: '20px' }}>
当前参数:颜色 {myColor} | 偏移 {myShift}
</div>
</div>
);
}
64 changes: 37 additions & 27 deletions components/agents-ui/agent-audio-visualizer-aura.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,40 +171,49 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {

// Dark mode (default)
if(uMode < 0.5) {
// use bloom effect
bloom = bloom / (bloom + 2e4);
color = (-pp + bloom * 3.0 * uBloom) * 1.2;
color += (randFibo(fragCoord).x - 0.5) / 255.0;

// Core brightness boost 1: increase base multiplier from 1.2 to 1.8
color = max(-pp + bloom * 3.0 * uBloom, 0.0) * 1.8;

float noise = (randFibo(fragCoord).x - 0.5) / 255.0;
color += noise * smoothstep(0.0, 0.1, length(color));

color = Tonemap(color);
float alpha = luma(color) * uMix;
fragColor = vec4(color * uMix, alpha);

// Core brightness boost 2: use length() instead of max() for alpha
float alpha = length(color) * uMix;

// Widen the glow radius for more visible outer emission
alpha = smoothstep(0.0, 0.25, alpha);
alpha = alpha < 0.005 ? 0.0 : alpha;

// Core brightness boost 3: apply extra 1.3x multiplier to final color
vec3 finalColor = color * uMix * 1.3;

finalColor = max(finalColor, 0.0);
finalColor *= step(0.001, alpha);

fragColor = vec4(finalColor, alpha);
}
// Light mode

// Light mode
else {
// no bloom effect
color = -pp;
color += (randFibo(fragCoord).x - 0.5) / 255.0;

// Preserve hue by tone mapping brightness only
color = max(-pp, 0.0);
float noise = (randFibo(fragCoord).x - 0.5) / 255.0;
color += noise * smoothstep(0.0, 0.1, length(color));

float brightness = length(color);
vec3 direction = brightness > 0.0 ? color / brightness : color;

// Reinhard on brightness

float factor = 2.0;
float mappedBrightness = (brightness * factor) / (1.0 + brightness * factor);
color = direction * mappedBrightness;

// Boost saturation to compensate for white background bleed-through
// When alpha < 1.0, white bleeds through making colors look desaturated
// So we increase saturation to maintain vibrant appearance

float gray = dot(color, vec3(0.2, 0.5, 0.1));
float saturationBoost = 3.0;
color = mix(vec3(gray), color, saturationBoost);

// Clamp between 0-1
color = mix(vec3(gray), color, 3.0);
color = clamp(color, 0.0, 1.0);

float alpha = mappedBrightness * clamp(uMix, 1.0, 2.0);
fragColor = vec4(color, alpha);
}
Expand Down Expand Up @@ -298,7 +307,7 @@ function AuraShader({
const rgbColor = useMemo(() => hexToRgb(color), [color]);

return (
<div ref={ref} className={className} {...props}>
<div ref={ref} className={className} style={{ backgroundColor: 'transparent' }} {...props}>
<ReactShaderToy
fs={shaderSource}
devicePixelRatio={globalThis.devicePixelRatio ?? 1}
Expand Down Expand Up @@ -338,7 +347,8 @@ function AuraShader({
onWarning={(warning) => {
console.warn('Shader warning:', warning);
}}
style={{ width: '100%', height: '100%' }}
style={{ width: '100%', height: '100%', mixBlendMode: 'screen' }}
contextAttributes={{ alpha: true, premultipliedAlpha: true }}
/>
</div>
);
Expand Down Expand Up @@ -424,7 +434,7 @@ export function AgentAudioVisualizerAura({
VariantProps<typeof AgentAudioVisualizerAuraVariants>) {
const { speed, scale, amplitude, frequency, brightness } = useAgentAudioVisualizerAura(
state,
audioTrack
state === 'speaking' ? undefined : audioTrack
);

return (
Expand All @@ -440,7 +450,7 @@ export function AgentAudioVisualizerAura({
amplitude={amplitude}
frequency={frequency}
brightness={brightness}
className={cn(AgentAudioVisualizerAuraVariants({ size }), className)}
className={cn(AgentAudioVisualizerAuraVariants({ size }), '!border-0', className)}
{...props}
/>
);
Expand Down
1 change: 1 addition & 0 deletions components/agents-ui/agent-audio-visualizer-wave.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ export function AgentAudioVisualizerWave({
frequency={frequency}
lineWidth={_lineWidth}
blur={blur}
clearColor={[0, 0, 0, 0]}
className={cn(
AgentAudioVisualizerWaveVariants({ size }),
'mask-[linear-gradient(90deg,transparent_0%,black_20%,black_80%,transparent_100%)]',
Expand Down
21 changes: 20 additions & 1 deletion components/agents-ui/agent-chat-transcript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type ComponentProps } from 'react';
import { AnimatePresence } from 'motion/react';
import { type AgentState, type ReceivedMessage } from '@livekit/components-react';
import { AgentChatIndicator } from '@/components/agents-ui/agent-chat-indicator';
import { ToolCard } from '@/components/agents-ui/tool-card';
import {
Conversation,
ConversationContent,
Expand Down Expand Up @@ -64,7 +65,25 @@ export function AgentChatTranscript({
return (
<Message key={id} title={title} from={messageOrigin}>
<MessageContent>
<MessageResponse>{message}</MessageResponse>
{/* 🌟 核心拦截逻辑:如果是工具卡片消息协议,则直接在聊天容器中渲染 ToolCard 组件 */}
{message.startsWith('[TOOL_CARD]:') ? (
(() => {
try {
// 解析出我们在 mute mode 中序列化的工具数据
const toolData = JSON.parse(message.replace('[TOOL_CARD]:', ''));
return (
<div className="w-full my-2 flex justify-start">
<ToolCard tool={toolData} />
</div>
);
} catch (e) {
return <span className="text-xs text-red-500">工具卡片解析异常</span>;
}
})()
) : (
/* 🌟 这里保留你原有的普通聊天气泡/Markdown 渲染代码,比如: */
<MessageResponse>{message}</MessageResponse>
)}
</MessageContent>
</Message>
);
Expand Down
Loading