Skip to content
Draft
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
18 changes: 12 additions & 6 deletions src/components/DashboardView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import { resolveI18nText } from '@/services/contentResolver';
import { loggers, generateTaskPipelineOverride } from '@/utils';
import type { TaskConfig } from '@/types/maa';
import { normalizeAgentConfigs } from '@/types/interface';
import { getInterfaceLangKey } from '@/i18n';
import i18n, { getInterfaceLangKey } from '@/i18n';
import { isAprilFools, getAIText } from '@/utils/aprilFools';
import { getMxuSpecialTask } from '@/types/specialTasks';
import { startGlobalCallbackListener } from '@/components/connection/callbackCache';
import { cancelTaskQueueMonitor, startTaskQueueMonitor } from '@/services/taskMonitor';
Expand Down Expand Up @@ -691,13 +692,13 @@ function InstanceCard({ instanceId, instanceName, isActive, onSelect }: Instance
)}
title={
isStarting
? t('taskList.startingTasks')
? (isAprilFools() ? getAIText('startingTasks', i18n.language) : t('taskList.startingTasks'))
: isStopping
? t('taskList.stoppingTasks')
? (isAprilFools() ? getAIText('stoppingTasks', i18n.language) : t('taskList.stoppingTasks'))
: isRunning
? t('taskList.stopTasks')
? (isAprilFools() ? getAIText('stopTasks', i18n.language) : t('taskList.stopTasks'))
: canRun
? t('taskList.startTasks')
? (isAprilFools() ? getAIText('startTasks', i18n.language) : t('taskList.startTasks'))
: t('dashboard.noEnabledTasks')
}
>
Expand Down Expand Up @@ -766,8 +767,13 @@ function InstanceCard({ instanceId, instanceName, isActive, onSelect }: Instance
<>
<Loader2 className="w-3 h-3 animate-spin" />
<span className="truncate max-w-[80px]">
{runningTaskName || t('dashboard.running')}
{isAprilFools()
? `AI: ${runningTaskName || t('dashboard.running')}`
: runningTaskName || t('dashboard.running')}
</span>
{isAprilFools() && (
<span className="ai-thinking-dots text-[10px] opacity-70" />
)}
</>
) : taskStatus === 'Failed' ? (
<>
Expand Down
16 changes: 13 additions & 3 deletions src/components/LogsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef, useEffect, useCallback } from 'react';
import { useRef, useEffect, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Trash2, Copy, ChevronUp, ChevronDown, Archive } from 'lucide-react';
import clsx from 'clsx';
Expand All @@ -7,6 +7,8 @@ import { ContextMenu, useContextMenu, type MenuItem } from './ContextMenu';
import { isTauri } from '@/utils/paths';
import { useExportLogs } from '@/utils/useExportLogs';
import { ExportLogsModal } from './settings/ExportLogsModal';
import i18n from '@/i18n';
import { isAprilFools, getAIText, getTokenCount, formatTokenCount } from '@/utils/aprilFools';

export function LogsPanel() {
const { t } = useTranslation();
Expand All @@ -18,6 +20,7 @@ export function LogsPanel() {

// 获取当前实例的日志
const logs = activeInstanceId ? instanceLogs[activeInstanceId] || [] : [];
const aprilFools = useMemo(() => isAprilFools(), []);

useEffect(() => {
const el = logsContainerRef.current;
Expand Down Expand Up @@ -136,8 +139,15 @@ export function LogsPanel() {
}}
className="flex items-center justify-between px-3 py-2 border-b border-border hover:bg-bg-hover transition-colors cursor-pointer shrink-0 rounded-t-lg focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent/50 outline-none"
>
<span className="text-sm font-medium text-text-primary">{t('logs.title')}</span>
<span className="text-sm font-medium text-text-primary">
{aprilFools ? getAIText('logsTitle', i18n.language) : t('logs.title')}
</span>
<div className="flex items-center gap-2">
{aprilFools && logs.length > 0 && (
<span className="ai-token-counter text-[10px] text-text-muted font-mono">
Tokens: {formatTokenCount(getTokenCount())}
</span>
)}
{/* 导出日志 */}
<button
onClick={(e) => {
Expand Down Expand Up @@ -197,7 +207,7 @@ export function LogsPanel() {
>
{logs.length === 0 ? (
<div className="h-full flex items-center justify-center text-text-muted">
{t('logs.noLogs')}
{aprilFools ? getAIText('noLogs', i18n.language) : t('logs.noLogs')}
</div>
) : (
<>
Expand Down
7 changes: 6 additions & 1 deletion src/components/TaskItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { useAppStore, type TaskRunStatus } from '@/stores/appStore';
import { maaService } from '@/services/maaService';
import { useResolvedContent } from '@/services/contentResolver';
import { generateTaskPipelineOverride } from '@/utils';
import i18n from '@/i18n';
import { isAprilFools, getAIStatusText } from '@/utils/aprilFools';
import { OptionEditor, SwitchGrid, switchHasNestedOptions } from './OptionEditor';
import { ContextMenu, useContextMenu, type MenuItem } from './ContextMenu';
import { Tooltip } from './ui/Tooltip';
Expand Down Expand Up @@ -854,7 +856,7 @@ export function TaskItem({ instanceId, task }: TaskItemProps) {
'absolute left-0 top-0 bottom-0 w-1.5 rounded-l-lg transition-colors',
getStatusIndicatorClass(),
)}
title={t(`taskItem.status.${taskRunStatus}`)}
title={isAprilFools() ? getAIStatusText(taskRunStatus, i18n.language) : t(`taskItem.status.${taskRunStatus}`)}
/>
)}

Expand Down Expand Up @@ -955,6 +957,9 @@ export function TaskItem({ instanceId, task }: TaskItemProps) {
>
{displayName}
</span>
{isAprilFools() && taskRunStatus === 'running' && (
<span className="ai-thinking-dots text-xs text-accent flex-shrink-0" />
)}
{task.customName && (
<span className="flex-shrink-0 text-xs text-text-muted">({originalLabel})</span>
)}
Expand Down
13 changes: 8 additions & 5 deletions src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import { useAppStore } from '@/stores/appStore';
import { maaService } from '@/services/maaService';
import clsx from 'clsx';
import { loggers, generateTaskPipelineOverride, computeResourcePaths } from '@/utils';
import { isAprilFools, getAIText } from '@/utils/aprilFools';
import { getMxuSpecialTask } from '@/types/specialTasks';
import type { TaskConfig, ControllerConfig } from '@/types/maa';
import { normalizeAgentConfigs } from '@/types/interface';
import { parseWin32ScreencapMethod, parseWin32InputMethod } from '@/types/maa';
import { SchedulePanel } from './SchedulePanel';
import type { Instance } from '@/types/interface';
import { resolveI18nText } from '@/services/contentResolver';
import { getInterfaceLangKey } from '@/i18n';
import i18n, { getInterfaceLangKey } from '@/i18n';
import { PermissionModal } from './toolbar/PermissionModal';
import { ScheduleButton } from './toolbar/ScheduleButton';
import { startGlobalCallbackListener } from '@/components/connection/callbackCache';
Expand Down Expand Up @@ -1162,8 +1163,10 @@ export function Toolbar({ showAddPanel, onToggleAddPanel }: ToolbarProps) {
const isDisabled = (tasks.length === 0 || !canRun) && !instance?.isRunning;

// 获取启动按钮的文本
const _af = isAprilFools();
const getStartButtonText = () => {
if (isStarting) {
if (_af) return getAIText('startingTasks', i18n.language);
switch (autoConnectPhase) {
case 'searching':
return t('taskList.autoConnect.searching');
Expand All @@ -1175,7 +1178,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel }: ToolbarProps) {
return t('taskList.startingTasks');
}
}
return t('taskList.startTasks');
return _af ? getAIText('startTasks', i18n.language) : t('taskList.startTasks');
};

// 获取按钮的 title 提示
Expand Down Expand Up @@ -1297,17 +1300,17 @@ export function Toolbar({ showAddPanel, onToggleAddPanel }: ToolbarProps) {
) : isStopping ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
<span>{t('taskList.stoppingTasks')}</span>
<span>{_af ? getAIText('stoppingTasks', i18n.language) : t('taskList.stoppingTasks')}</span>
</>
) : instance?.isRunning ? (
<>
<StopCircle className="w-4 h-4" />
<span>{t('taskList.stopTasks')}</span>
<span>{_af ? getAIText('stopTasks', i18n.language) : t('taskList.stopTasks')}</span>
</>
) : (
<>
<Play className="w-4 h-4" />
<span>{t('taskList.startTasks')}</span>
<span>{getStartButtonText()}</span>
</>
)}
</button>
Expand Down
25 changes: 24 additions & 1 deletion src/components/settings/DebugSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Bug, RefreshCw, FolderOpen, ScrollText, Network, Archive } from 'lucide-react';
import { Bug, RefreshCw, FolderOpen, ScrollText, Network, Archive, Sparkles } from 'lucide-react';

import { useAppStore } from '@/stores/appStore';
import { maaService } from '@/services/maaService';
Expand All @@ -9,6 +9,7 @@ import { isTauri, getDebugDir, getConfigDir, openDirectory } from '@/utils/paths
import { useExportLogs } from '@/utils/useExportLogs';
import { SwitchButton } from '@/components/FormControls';
import { ExportLogsModal } from './ExportLogsModal';
import { getAprilFoolsOverride, setAprilFoolsOverride } from '@/utils/aprilFools';

export function DebugSection() {
const { t } = useTranslation();
Expand Down Expand Up @@ -36,6 +37,8 @@ export function DebugSection() {
} | null>(null);
const { exportModal, handleExportLogs, closeExportModal, openExportedFile } = useExportLogs();

const [aprilFoolsEnabled, setAprilFoolsEnabled] = useState(() => getAprilFoolsOverride() === true);

const version = projectInterface?.version || '0.1.0';

// 版本信息(用于调试展示)
Expand Down Expand Up @@ -274,6 +277,26 @@ export function DebugSection() {
</div>
<SwitchButton value={tcpCompatMode} onChange={(v) => setTcpCompatMode(v)} />
</div>

{/* 愚人节彩蛋(仅开发模式可见) */}
{devMode && (
<div className="flex items-center justify-between pt-4 border-t border-border">
<div className="flex items-center gap-3">
<Sparkles className="w-5 h-5 text-accent" />
<div>
<span className="font-medium text-text-primary">{t('debug.aprilFools')}</span>
<p className="text-xs text-text-muted mt-0.5">{t('debug.aprilFoolsHint')}</p>
</div>
</div>
<SwitchButton
value={aprilFoolsEnabled}
onChange={(v) => {
setAprilFoolsEnabled(v);
setAprilFoolsOverride(v || null);
}}
/>
</div>
)}
</div>

{/* 导出日志 Modal */}
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ export default {
tcpCompatMode: 'Communication Compat Mode',
tcpCompatModeHint:
'Try enabling this if the app crashes immediately after starting tasks. Only use in this case, as it may reduce performance',
aprilFools: 'April Fools Easter Egg',
aprilFoolsHint: 'Disguise all task execution as AI reasoning mode (auto-activates on April 1st)',
},

// Welcome dialog
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/ja-JP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ export default {
tcpCompatMode: '通信互換モード',
tcpCompatModeHint:
'タスク開始後にアプリがすぐにクラッシュする場合は有効にしてください。この場合のみ使用し、それ以外は性能に影響します',
aprilFools: 'エイプリルフール',
aprilFoolsHint: 'すべてのタスク実行をAI推論モードに偽装します(4月1日に自動有効化)',
},

// ウェルカムダイアログ
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/ko-KR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,8 @@ export default {
tcpCompatMode: '통신 호환 모드',
tcpCompatModeHint:
'작업 시작 후 앱이 즉시 충돌하면 활성화해 보세요. 이 경우에만 사용하세요, 성능에 영향을 줄 수 있습니다',
aprilFools: '만우절 이스터에그',
aprilFoolsHint: '모든 작업 실행을 AI 추론 모드로 위장합니다 (4월 1일 자동 활성화)',
},

// 환영 대화상자
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ export default {
saveDrawHint: '保存识别和操作的调试图像到日志目录(重启软件后自动关闭)',
tcpCompatMode: '通信兼容模式',
tcpCompatModeHint: '若启动任务后软件立即闪退,可尝试开启。仅限此情况使用,否则会影响运行效率',
aprilFools: '愚人节彩蛋',
aprilFoolsHint: '开启后所有任务执行将伪装成 AI 推理模式(4月1日自动激活)',
},

// 欢迎弹窗
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ export default {
saveDrawHint: '儲存識別和操作的除錯圖像到日誌目錄(重啟軟體後自動關閉)',
tcpCompatMode: '通訊相容模式',
tcpCompatModeHint: '若啟動任務後軟體立即閃退,可嘗試開啟。僅限此情況使用,否則會影響運行效率',
aprilFools: '愚人節彩蛋',
aprilFoolsHint: '開啟後所有任務執行將偽裝成 AI 推理模式(4月1日自動啟用)',
},

// 欢迎彈窗
Expand Down
76 changes: 76 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,79 @@ body {
.has-background-image .bg-bg-primary {
background-color: color-mix(in srgb, var(--color-bg-primary) 45%, transparent) !important;
}

/* ============================================================
April Fools "MaaGPT" Easter Egg Styles
============================================================ */

@keyframes aiThinkingDots {
0% {
content: '';
}
25% {
content: '.';
}
50% {
content: '..';
}
75% {
content: '...';
}
100% {
content: '';
}
}

.ai-thinking-dots::after {
content: '';
animation: aiThinkingDots 1.6s steps(1) infinite;
}

@keyframes aiTokenPulse {
0%,
100% {
opacity: 0.7;
}
50% {
opacity: 1;
}
}

.ai-token-counter {
animation: aiTokenPulse 2s ease-in-out infinite;
font-variant-numeric: tabular-nums;
}

.ai-think-block {
margin: 2px 0;
font-size: 0.7rem;
line-height: 1.4;
opacity: 0.9;
}

.ai-think-block summary {
font-size: 0.7rem;
list-style: none;
user-select: none;
}

.ai-think-block summary::-webkit-details-marker {
display: none;
}

@keyframes aiSparkle {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.6;
transform: scale(1.15);
}
}

.ai-sparkle {
display: inline-block;
animation: aiSparkle 1.5s ease-in-out infinite;
}
Loading
Loading