Pārlūkot izejas kodu

refactor(desktop-pet): 移除已退役的 WPF 版

liaoxg 1 dienu atpakaļ
vecāks
revīzija
982f3a77c4
28 mainītis faili ar 0 papildinājumiem un 1438 dzēšanām
  1. 0 28
      cfc-desktop-pet/XafPet.Desktop.sln
  2. 0 6
      cfc-desktop-pet/src/XafPet.Desktop/App.xaml
  3. 0 16
      cfc-desktop-pet/src/XafPet.Desktop/App.xaml.cs
  4. 0 46
      cfc-desktop-pet/src/XafPet.Desktop/Assets/animations.css
  5. 0 51
      cfc-desktop-pet/src/XafPet.Desktop/Assets/interactions.js
  6. 0 105
      cfc-desktop-pet/src/XafPet.Desktop/Assets/pet-engine.js
  7. 0 30
      cfc-desktop-pet/src/XafPet.Desktop/Assets/pet.html
  8. 0 27
      cfc-desktop-pet/src/XafPet.Desktop/DefaultPets/fubao/pet-config.json
  9. 0 27
      cfc-desktop-pet/src/XafPet.Desktop/DefaultPets/xibao/pet-config.json
  10. 0 16
      cfc-desktop-pet/src/XafPet.Desktop/MainWindow.xaml
  11. 0 218
      cfc-desktop-pet/src/XafPet.Desktop/MainWindow.xaml.cs
  12. 0 79
      cfc-desktop-pet/src/XafPet.Desktop/PetEngine/ApiClient.cs
  13. 0 80
      cfc-desktop-pet/src/XafPet.Desktop/PetEngine/CfcApiModels.cs
  14. 0 74
      cfc-desktop-pet/src/XafPet.Desktop/PetEngine/PetConfig.cs
  15. 0 29
      cfc-desktop-pet/src/XafPet.Desktop/PetEngine/PetState.cs
  16. 0 62
      cfc-desktop-pet/src/XafPet.Desktop/PetEngine/StateManager.cs
  17. 0 11
      cfc-desktop-pet/src/XafPet.Desktop/PetEngine/WebViewHost.xaml
  18. 0 77
      cfc-desktop-pet/src/XafPet.Desktop/PetEngine/WebViewHost.xaml.cs
  19. 0 20
      cfc-desktop-pet/src/XafPet.Desktop/SettingsWindow.xaml
  20. 0 44
      cfc-desktop-pet/src/XafPet.Desktop/SettingsWindow.xaml.cs
  21. 0 23
      cfc-desktop-pet/src/XafPet.Desktop/SingleInstanceHelper.cs
  22. 0 43
      cfc-desktop-pet/src/XafPet.Desktop/TokenStore.cs
  23. 0 29
      cfc-desktop-pet/src/XafPet.Desktop/XafPet.Desktop.csproj
  24. 0 96
      cfc-desktop-pet/tests/XafPet.Desktop.Tests/ApiClientTests.cs
  25. 0 68
      cfc-desktop-pet/tests/XafPet.Desktop.Tests/PetConfigTests.cs
  26. 0 8
      cfc-desktop-pet/tests/XafPet.Desktop.Tests/README.md
  27. 0 107
      cfc-desktop-pet/tests/XafPet.Desktop.Tests/StateManagerTests.cs
  28. 0 18
      cfc-desktop-pet/tests/XafPet.Desktop.Tests/XafPet.Desktop.Tests.csproj

+ 0 - 28
cfc-desktop-pet/XafPet.Desktop.sln

@@ -1,28 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31903.59
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XafPet.Desktop", "src\XafPet.Desktop\XafPet.Desktop.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XafPet.Desktop.Tests", "tests\XafPet.Desktop.Tests\XafPet.Desktop.Tests.csproj", "{D4E5F6A7-B8C9-0123-DEF0-234567890123}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
-		{D4E5F6A7-B8C9-0123-DEF0-234567890123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{D4E5F6A7-B8C9-0123-DEF0-234567890123}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{D4E5F6A7-B8C9-0123-DEF0-234567890123}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{D4E5F6A7-B8C9-0123-DEF0-234567890123}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-EndGlobal

+ 0 - 6
cfc-desktop-pet/src/XafPet.Desktop/App.xaml

@@ -1,6 +0,0 @@
-<Application x:Class="XafPet.Desktop.App"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             StartupUri="MainWindow.xaml">
-    <Application.Resources></Application.Resources>
-</Application>

+ 0 - 16
cfc-desktop-pet/src/XafPet.Desktop/App.xaml.cs

@@ -1,16 +0,0 @@
-using System.Windows;
-
-namespace XafPet.Desktop;
-
-public partial class App : Application
-{
-    protected override void OnStartup(StartupEventArgs e)
-    {
-        base.OnStartup(e);
-        if (!SingleInstanceHelper.TryEnterFirstInstance())
-        {
-            Current.Shutdown();
-            return;
-        }
-    }
-}

+ 0 - 46
cfc-desktop-pet/src/XafPet.Desktop/Assets/animations.css

@@ -1,46 +0,0 @@
-/* ===== 浠艾福桌面宠物动画定义 ===== */
-@keyframes wings_idle { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(-3px) } }
-@keyframes wings_spread { 0% { transform: scale(1) } 30% { transform: scale(1.15, 0.95) } 70% { transform: scale(0.95, 1.1) } 100% { transform: scale(1) } }
-@keyframes head_tilt { 0%, 100% { transform: rotate(0deg) } 25% { transform: rotate(-8deg) } 75% { transform: rotate(6deg) } }
-@keyframes hand_gesture { 0%, 100% { transform: translateY(0) } 20% { transform: translateY(-8px) } 40% { transform: translateY(0) } 60% { transform: translateY(-5px) } 80% { transform: translateY(0) } }
-@keyframes breath_loop { 0%, 100% { transform: scale(1, 1) } 50% { transform: scale(1.04, 0.96) } }
-@keyframes celebrate { 0%, 100% { transform: translateY(0) rotate(0) } 25% { transform: translateY(-16px) rotate(-4deg) } 50% { transform: translateY(0) rotate(0) } 75% { transform: translateY(-16px) rotate(4deg) } }
-@keyframes sad_animation { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(2px) } }
-@keyframes tail_wrap { 0%, 100% { transform: rotate(0deg) } 50% { transform: rotate(3deg) } }
-@keyframes belly_show { 0%, 100% { transform: rotate(0deg) scale(1) } 50% { transform: rotate(-4deg) scale(1.05) } }
-@keyframes paw_press { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(3px) } }
-@keyframes tail_flick { 0%, 100% { transform: rotate(0deg) } 30% { transform: rotate(10deg) } 60% { transform: rotate(-10deg) } }
-@keyframes blink_eye { 0%, 90%, 100% { transform: scaleY(1) } 95% { transform: scaleY(0.1) } }
-@keyframes look_around { 0%, 100% { transform: rotate(0deg) } 25% { transform: rotate(-12deg) } 75% { transform: rotate(12deg) } }
-@keyframes wave { 0%, 100% { transform: rotate(0deg) } 25% { transform: rotate(-15deg) } 50% { transform: rotate(10deg) } 75% { transform: rotate(-15deg) } }
-@keyframes stretch { 0%, 100% { transform: scaleY(1) } 50% { transform: scaleY(1.15) } }
-@keyframes clap { 0%, 100% { transform: scale(1) } 40% { transform: scale(1.1, 0.9) } 70% { transform: scale(0.95, 1.05) } }
-@keyframes bow { 0%, 100% { transform: rotate(0deg) } 50% { transform: rotate(10deg) } }
-@keyframes whistle { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(-4px) } }
-@keyframes ear_scrape { 0%, 100% { transform: rotate(0deg) } 50% { transform: rotate(6deg) } }
-@keyframes pet_touch { 0% { transform: scale(1) } 50% { transform: scale(1.08) } 100% { transform: scale(1) } }
-@keyframes puzzle_active { 0%, 100% { transform: rotate(0deg) } 50% { transform: rotate(180deg) } }
-@keyframes worried_walk { 0%, 100% { transform: translateX(0) } 25% { transform: translateX(6px) } 75% { transform: translateX(-6px) } }
-
-.anim-wings_idle { animation: wings_idle 2s ease-in-out infinite; }
-.anim-wings_spread { animation: wings_spread 1.2s ease-in-out 1; }
-.anim-head_tilt { animation: head_tilt 1.6s ease-in-out infinite; }
-.anim-hand_gesture { animation: hand_gesture 1.8s ease-in-out infinite; }
-.anim-breath_loop { animation: breath_loop 2.4s ease-in-out infinite; }
-.anim-celebrate { animation: celebrate 0.8s ease-in-out 3; }
-.anim-sad_animation { animation: sad_animation 3s ease-in-out infinite; }
-.anim-tail_wrap { animation: tail_wrap 2s ease-in-out infinite; }
-.anim-belly_show { animation: belly_show 1.4s ease-in-out 2; }
-.anim-paw_press { animation: paw_press 1s ease-in-out 2; }
-.anim-tail_flick { animation: tail_flick 1.5s ease-in-out 2; }
-.anim-blink_eye { animation: blink_eye 3.5s ease-in-out infinite; }
-.anim-look_around { animation: look_around 2s ease-in-out 1; }
-.anim-wave { animation: wave 1.2s ease-in-out 2; }
-.anim-stretch { animation: stretch 1.8s ease-in-out 1; }
-.anim-clap { animation: clap 0.6s ease-in-out 3; }
-.anim-bow { animation: bow 1.2s ease-in-out 2; }
-.anim-whistle { animation: whistle 0.9s ease-in-out 3; }
-.anim-ear_scrape { animation: ear_scrape 1s ease-in-out 2; }
-.anim-pet_touch { animation: pet_touch 0.6s ease-in-out 1; }
-.anim-puzzle_active { animation: puzzle_active 1s ease-in-out infinite; }
-.anim-worried_walk { animation: worried_walk 1.2s ease-in-out infinite; }

+ 0 - 51
cfc-desktop-pet/src/XafPet.Desktop/Assets/interactions.js

@@ -1,51 +0,0 @@
-(function () {
-  'use strict';
-
-  const petEl = document.getElementById('pet');
-
-  let dragStartX = 0, dragStartY = 0;
-  let pressTimer = null;
-  let isDragging = false;
-
-  petEl.addEventListener('mousedown', function (e) {
-    dragStartX = e.clientX;
-    dragStartY = e.clientY;
-    isDragging = false;
-    pressTimer = setTimeout(() => {
-      notifyInteract('pet', {});
-    }, 1500);
-  });
-
-  petEl.addEventListener('mousemove', function (e) {
-    const dx = e.clientX - dragStartX;
-    const dy = e.clientY - dragStartY;
-    if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
-      isDragging = true;
-      clearTimeout(pressTimer);
-      notifyDragStart();
-    }
-  });
-
-  petEl.addEventListener('mouseup', function (e) {
-    clearTimeout(pressTimer);
-    if (!isDragging) {
-      notifyInteract('click', {});
-    } else {
-      notifyDragEnd();
-    }
-  });
-
-  function notifyInteract(action, data) {
-    try {
-      chrome.webview.hostObjects.sync.cfcHost.onInteract(action, JSON.stringify(data));
-    } catch (err) { console.log('[interaction]', action, data); }
-  }
-
-  function notifyDragStart() {
-    try { chrome.webview.hostObjects.sync.cfcHost.onDragStart(); } catch (e) { /* noop */ }
-  }
-
-  function notifyDragEnd() {
-    try { chrome.webview.hostObjects.sync.cfcHost.onDragEnd(); } catch (e) { /* noop */ }
-  }
-})();

+ 0 - 105
cfc-desktop-pet/src/XafPet.Desktop/Assets/pet-engine.js

@@ -1,105 +0,0 @@
-(function () {
-  'use strict';
-
-  const petImg = document.getElementById('pet-img');
-  const petEl = document.getElementById('pet');
-  const bubble = document.getElementById('bubble');
-
-  let currentState = 'idle';
-  let currentAnimation = null;
-  let config = null;
-  let bubbleTimer = null;
-
-  const DEFAULT_IMAGE = 'data:image/svg+xml;utf8,' +
-    encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="120" height="140">' +
-      '<text y="100" font-size="90" text-anchor="middle">🦜</text></svg>');
-
-  const BUBBLE_TEXTS = {
-    happy: '完成了!继续保持~',
-    busy: '有任务在等你呢~',
-    worried: '任务要过期啦,快去看看!',
-    excited: '打卡成功,太棒了!',
-    remind: '该喝水啦~',
-    tired: '有点累了呢…',
-    sad: '要照顾好自己哦…',
-    waitingForInteraction: '来和我互动一下吧!',
-    miniGame: '我们一起玩吧!',
-    sleeping: 'zZ…',
-    custom: '自定义状态'
-  };
-
-  window.cfcPetAPI = {
-    notifyState: function (stateJson) {
-      const payload = typeof stateJson === 'string' ? JSON.parse(stateJson) : stateJson;
-      setState(payload.state, payload.reason || null, payload.data || null);
-    },
-    setCustomState: function (stateName, dataJson) {
-      const data = typeof dataJson === 'string' ? JSON.parse(dataJson) : dataJson;
-      setState(stateName, 'custom', data);
-    },
-    loadConfig: function (configJson) {
-      config = typeof configJson === 'string' ? JSON.parse(configJson) : configJson;
-      applyImage(config);
-    },
-    showBubble: function (text, durationMs) {
-      bubble.textContent = text;
-      bubble.style.display = 'block';
-      clearTimeout(bubbleTimer);
-      if (durationMs) bubbleTimer = setTimeout(hideBubble, durationMs);
-    },
-    hideBubble: hideBubble,
-    getState: function () { return currentState; }
-  };
-
-  function hideBubble() { bubble.style.display = 'none'; }
-
-  function applyImage(cfg) {
-    if (!cfg || !cfg.default_image) return;
-    const src = cfg.image_base_url + cfg.default_image;
-    petImg.src = src;
-    petImg.onerror = function () { petImg.src = DEFAULT_IMAGE; };
-  }
-
-  function setState(stateName, reason, data) {
-    currentState = stateName;
-    if (currentAnimation) { petEl.classList.remove(currentAnimation); currentAnimation = null; }
-
-    let animClass = null;
-    if (config && config.actions && config.actions[stateName]) {
-      animClass = 'anim-' + config.actions[stateName];
-    } else {
-      animClass = 'anim-' + stateName;
-    }
-
-    const styleEl = document.getElementById('pet-animations');
-    const sheet = styleEl ? styleEl.sheet : null;
-    const hasClass = sheet && Array.from(sheet.cssRules).some(r => r.selectorText === '.' + animClass);
-    if (hasClass) { petEl.classList.add(animClass); currentAnimation = animClass; }
-
-    if (reason === 'drink_water') {
-      bubble.textContent = '该喝水啦~';
-      bubble.style.display = 'block';
-      clearTimeout(bubbleTimer);
-      bubbleTimer = setTimeout(hideBubble, 8000);
-    } else if (reason === 'welcome') {
-      bubble.textContent = '我来了!';
-      bubble.style.display = 'block';
-      clearTimeout(bubbleTimer);
-      bubbleTimer = setTimeout(hideBubble, 3000);
-    } else if (BUBBLE_TEXTS[stateName]) {
-      bubble.textContent = BUBBLE_TEXTS[stateName];
-      bubble.style.display = 'block';
-      clearTimeout(bubbleTimer);
-      bubbleTimer = setTimeout(hideBubble, 5000);
-    }
-  }
-
-  petImg.src = DEFAULT_IMAGE;
-
-  setInterval(function () {
-    if (document.getElementById('pet-img')) {
-      petImg.classList.add('anim-blink_eye');
-      setTimeout(() => petImg.classList.remove('anim-blink_eye'), 200);
-    }
-  }, 3500);
-})();

+ 0 - 30
cfc-desktop-pet/src/XafPet.Desktop/Assets/pet.html

@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-<meta charset="UTF-8" />
-<title>Xaf Pet</title>
-<link rel="stylesheet" href="animations.css" />
-<style>
-  html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; background: transparent; }
-  #pet-stage { position: fixed; inset: 0; display: flex; align-items: flex-end; justify-content: center; pointer-events: none; }
-  #pet { width: 120px; height: 140px; pointer-events: auto; cursor: grab; user-select: none; }
-  #pet img { width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
-  #bubble { position: absolute; bottom: 150px; left: 50%; transform: translateX(-50%);
-            background: #fff; border: 2px solid #4A9BD7; border-radius: 12px; padding: 6px 12px;
-            font: 13px/1.4 'PingFang SC', sans-serif; color: #333; white-space: nowrap;
-            display: none; box-shadow: 0 2px 8px rgba(0,0,0,.12); }
-  #bubble::after { content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
-                   border: 6px solid transparent; border-top-color: #4A9BD7; }
-</style>
-</head>
-<body>
-<div id="pet-stage">
-  <div id="pet">
-    <img id="pet-img" src="" alt="pet" draggable="false" />
-    <div id="bubble"></div>
-  </div>
-</div>
-<script src="pet-engine.js"></script>
-<script src="interactions.js"></script>
-</body>
-</html>

+ 0 - 27
cfc-desktop-pet/src/XafPet.Desktop/DefaultPets/fubao/pet-config.json

@@ -1,27 +0,0 @@
-{
-  "version": "1.0",
-  "type": "animal",
-  "species": "british_shorthair",
-  "gender": "male",
-  "image_base_url": "../DefaultPets/fubao/",
-  "default_image": "standing.png",
-  "actions": {
-    "idle": "tail_wrap",
-    "happy": "belly_show",
-    "busy": "paw_press",
-    "waitingForInteraction": "tail_flick",
-    "sleeping": "breath_loop",
-    "celebrate": "celebrate",
-    "sad": "sad_animation"
-  },
-  "sounds": {
-    "click": "tap.mp3",
-    "complete": "bell.mp3",
-    "remind": "water_please.mp3"
-  },
-  "parameters": {
-    "move_distance": 12,
-    "animation_speed": 1.0,
-    "interaction_timeout": 300
-  }
-}

+ 0 - 27
cfc-desktop-pet/src/XafPet.Desktop/DefaultPets/xibao/pet-config.json

@@ -1,27 +0,0 @@
-{
-  "version": "1.0",
-  "type": "animal",
-  "species": "cockatiel",
-  "gender": "female",
-  "image_base_url": "../DefaultPets/xibao/",
-  "default_image": "standing.png",
-  "actions": {
-    "idle": "wings_idle",
-    "happy": "wings_spread",
-    "busy": "head_tilt",
-    "waitingForInteraction": "hand_gesture",
-    "sleeping": "breath_loop",
-    "celebrate": "celebrate",
-    "sad": "sad_animation"
-  },
-  "sounds": {
-    "click": "pip.mp3",
-    "complete": "cheer.mp3",
-    "remind": "water_please.mp3"
-  },
-  "parameters": {
-    "move_distance": 15,
-    "animation_speed": 1.0,
-    "interaction_timeout": 300
-  }
-}

+ 0 - 16
cfc-desktop-pet/src/XafPet.Desktop/MainWindow.xaml

@@ -1,16 +0,0 @@
-<Window x:Class="XafPet.Desktop.MainWindow"
-        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:pet="clr-namespace:XafPet.Desktop.PetEngine"
-        Title="浠艾福桌面宠物"
-        Width="200" Height="200"
-        WindowStyle="None"
-        AllowsTransparency="True"
-        Background="Transparent"
-        Topmost="True"
-        ShowInTaskbar="False"
-        ResizeMode="NoResize">
-    <Grid x:Name="RootGrid" Background="Transparent">
-        <pet:WebViewHost x:Name="PetHost" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
-    </Grid>
-</Window>

+ 0 - 218
cfc-desktop-pet/src/XafPet.Desktop/MainWindow.xaml.cs

@@ -1,218 +0,0 @@
-using System;
-using System.Drawing;
-using System.IO;
-using System.Net.Http;
-using System.Text.Json;
-using System.Timers;
-using System.Windows;
-using System.Windows.Forms;
-using System.Windows.Input;
-using XafPet.Desktop.PetEngine;
-using Timer = System.Timers.Timer;
-
-namespace XafPet.Desktop;
-
-public partial class MainWindow : Window
-{
-    private NotifyIcon? _trayIcon;
-    private ApiClient? _api;
-    private Timer? _taskTimer;
-    private Timer? _energyTimer;
-    private Timer? _waterTimer;
-    private string _baseUrl = "http://localhost:9082";
-
-    public MainWindow()
-    {
-        InitializeComponent();
-        RootGrid.MouseLeftButtonDown += (s, e) =>
-        {
-            if (e.ButtonState == MouseButtonState.Pressed) DragMove();
-        };
-        PetHost.InteractionReceived += OnInteractionReceived;
-        Loaded += OnLoaded;
-        Closed += OnClosed;
-    }
-
-    private void OnLoaded(object sender, RoutedEventArgs e)
-    {
-        var token = TokenStore.Load();
-        _api = new ApiClient(new HttpClient(), _baseUrl, token ?? "dev-token");
-        StartPolling();
-        SetupTray();
-        _ = SyncMascotAsync();
-    }
-
-    private void StartPolling()
-    {
-        _taskTimer = new Timer(30_000) { AutoReset = true };
-        _taskTimer.Elapsed += async (_, _) => await PollTasksAsync();
-        _taskTimer.Start();
-
-        _energyTimer = new Timer(60_000) { AutoReset = true };
-        _energyTimer.Elapsed += async (_, _) => await PollHealthAsync();
-        _energyTimer.Start();
-
-        StartWaterReminder();
-    }
-
-    private async Task PollTasksAsync()
-    {
-        if (_api == null) return;
-        try
-        {
-            var tasks = await _api.GetTodayTasksAsync();
-            var state = StateManager.MapState(tasks ?? new List<CfcTask>(), null, null, false);
-            PushState(state, "tasks");
-        }
-        catch (ApiException ex) { PushBubble($"服务暂时不可用({ex.ApiCode})"); }
-        catch (Exception) { PushBubble("连接后端失败,请检查服务"); }
-    }
-
-    private async Task PollHealthAsync()
-    {
-        if (_api == null) return;
-        try
-        {
-            var energy = await _api.GetEnergyOverviewAsync();
-            var checkin = await _api.GetDailyCheckinAsync();
-            var state = StateManager.MapState(null, energy, checkin, false);
-            PushState(state, "health");
-        }
-        catch (Exception) { /* 静默 */ }
-    }
-
-    private void PushState(PetState state, string reason)
-    {
-        var payload = new PetStatePayload(StateManager.StateToCamelCase(state), reason, null);
-        PetHost.NotifyState(JsonSerializer.Serialize(payload));
-    }
-
-    private void PushBubble(string text)
-    {
-        PetHost.ShowBubble(text, 5000);
-    }
-
-    private void SetupTray()
-    {
-        var menu = new ContextMenuStrip();
-        menu.Items.Add("设置", null, (_, _) => ShowSettings());
-        menu.Items.Add("显示/隐藏", null, (_, _) => ToggleVisible());
-        menu.Items.Add("退出", null, (_, _) => { _trayIcon?.Dispose(); System.Windows.Application.Current.Shutdown(); });
-
-        _trayIcon = new NotifyIcon
-        {
-            Text = "浠艾福桌面宠物",
-            Icon = SystemIcons.Application,
-            ContextMenuStrip = menu,
-            Visible = true
-        };
-        _trayIcon.DoubleClick += (_, _) => ToggleVisible();
-    }
-
-    private void ToggleVisible()
-    {
-        Visibility = Visibility == Visibility.Visible ? Visibility.Hidden : Visibility.Visible;
-    }
-
-    private void ShowSettings()
-    {
-        var win = new SettingsWindow(TokenStore.Load(), _baseUrl) { Owner = this };
-        if (win.ShowDialog() == true)
-        {
-            _baseUrl = win.BaseUrl;
-            if (!string.IsNullOrEmpty(win.Token)) TokenStore.Save(win.Token);
-            _api = new ApiClient(new HttpClient(), _baseUrl, TokenStore.Load() ?? "dev-token");
-            StopPolling();
-            StartPolling();
-        }
-    }
-
-    private void StopPolling()
-    {
-        _taskTimer?.Stop(); _energyTimer?.Stop(); _waterTimer?.Stop();
-        _taskTimer?.Dispose(); _energyTimer?.Dispose(); _waterTimer?.Dispose();
-        _taskTimer = _energyTimer = _waterTimer = null;
-    }
-
-    private void OnClosed(object? sender, EventArgs e)
-    {
-        StopPolling();
-        _trayIcon?.Dispose();
-    }
-
-    // ── 喝水提醒 ─────────────────────────────────────────────
-    private void StartWaterReminder()
-    {
-        _waterTimer = new Timer(60 * 60 * 1000) { AutoReset = true };
-        _waterTimer.Elapsed += async (_, _) => await CheckWaterAsync();
-        _waterTimer.Start();
-        _ = CheckWaterAsync();
-    }
-
-    private async Task CheckWaterAsync()
-    {
-        if (_api == null) return;
-        var hour = DateTime.Now.Hour;
-        if (hour < 10 || hour > 20) return;
-        try
-        {
-            var checkin = await _api.GetDailyCheckinAsync();
-            if (checkin != null && !checkin.Items.Contains("water"))
-            {
-                PushState(PetState.WaitingForInteraction, "drink_water");
-                PetHost.ShowBubble("该喝水啦~点我确认完成!", 15000);
-            }
-        }
-        catch (Exception) { }
-    }
-
-    private void OnInteractionReceived(string action, string? dataJson)
-    {
-        switch (action)
-        {
-            case "click":
-                _ = HandleClickAsync();
-                break;
-            case "pet":
-                PetHost.ShowBubble("嘻嘻~", 2000);
-                break;
-        }
-    }
-
-    private async Task HandleClickAsync()
-    {
-        if (_api == null) return;
-        try
-        {
-            var result = await _api.CompleteDrinkWaterAsync();
-            if (result?.IsSuccess == true)
-            {
-                PushState(PetState.Happy, "drink_complete");
-                PetHost.ShowBubble("喝水打卡成功!太棒了~", 4000);
-            }
-        }
-        catch (Exception)
-        {
-            PushBubble("打卡失败,请稍后再试");
-        }
-    }
-
-    // ── 吉祥物同步 ───────────────────────────────────────────
-    private async Task SyncMascotAsync()
-    {
-        if (_api == null) return;
-        try
-        {
-            var mascot = await _api.GetMascotAsync();
-            var petName = mascot?.MascotId ?? "xibao";
-            var cfgPath = Path.Combine(AppContext.BaseDirectory, "DefaultPets", petName, "pet-config.json");
-            if (File.Exists(cfgPath))
-            {
-                var json = File.ReadAllText(cfgPath);
-                var parsed = PetConfig.Parse(json);
-                PetHost.LoadPetConfig(JsonSerializer.Serialize(parsed));
-            }
-        }
-        catch (Exception) { /* 默认浠宝 */ }
-    }
-}

+ 0 - 79
cfc-desktop-pet/src/XafPet.Desktop/PetEngine/ApiClient.cs

@@ -1,79 +0,0 @@
-using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Text;
-using System.Text.Json;
-
-namespace XafPet.Desktop.PetEngine;
-
-/// <summary>CFC 后端 API 客户端。所有接口统一 POST + JWT Bearer。</summary>
-public sealed class ApiClient
-{
-    private readonly HttpClient _http;
-    private readonly string _baseUrl;
-    private string _token;
-
-    private static readonly JsonSerializerOptions JsonOpts = new(JsonSerializerDefaults.Web);
-
-    public ApiClient(HttpClient http, string baseUrl, string token)
-    {
-        _http = http;
-        _baseUrl = baseUrl.TrimEnd('/');
-        _token = token;
-    }
-
-    public void UpdateToken(string token) => _token = token;
-
-    private async Task<T?> PostAsync<T>(string path, object? body = null)
-    {
-        using var req = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}{path}");
-        req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
-        if (body != null)
-        {
-            req.Content = new StringContent(JsonSerializer.Serialize(body, JsonOpts),
-                Encoding.UTF8, "application/json");
-        }
-        var resp = await _http.SendAsync(req);
-        var json = await resp.Content.ReadAsStringAsync();
-        var result = JsonSerializer.Deserialize<ApiResult<T>>(json, JsonOpts)
-            ?? throw new ApiException(-1, "响应解析失败");
-        if (!result.IsSuccess)
-            throw new ApiException(result.Code, result.Message ?? "未知错误");
-        return result.Data;
-    }
-
-    /// <summary>POST /api/tasks/today — 今日任务</summary>
-    public Task<List<CfcTask>?> GetTodayTasksAsync()
-        => PostAsync<List<CfcTask>>("/api/tasks/today", new Dictionary<string, object>());
-
-    /// <summary>POST /api/energy/overview — 五维能量概览</summary>
-    public Task<EnergyOverview?> GetEnergyOverviewAsync()
-        => PostAsync<EnergyOverview>("/api/energy/overview");
-
-    /// <summary>POST /api/daily/checkin — 今日打卡状态</summary>
-    public Task<DailyCheckin?> GetDailyCheckinAsync()
-        => PostAsync<DailyCheckin>("/api/daily/checkin");
-
-    /// <summary>POST /api/daily/checkin — 完成喝水打卡(body: {"type":"water"})</summary>
-    public async Task<ApiResult<object>?> CompleteDrinkWaterAsync()
-    {
-        using var req = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/api/daily/checkin");
-        req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
-        req.Content = new StringContent("""{"type":"water"}""", Encoding.UTF8, "application/json");
-        var resp = await _http.SendAsync(req);
-        var json = await resp.Content.ReadAsStringAsync();
-        var result = JsonSerializer.Deserialize<ApiResult<object>>(json, JsonOpts)
-            ?? throw new ApiException(-1, "响应解析失败");
-        if (!result.IsSuccess)
-            throw new ApiException(result.Code, result.Message ?? "未知错误");
-        return result;
-    }
-
-    /// <summary>POST /api/user/mascot/get — 获取用户已选吉祥物</summary>
-    public Task<MascotInfo?> GetMascotAsync()
-        => PostAsync<MascotInfo>("/api/user/mascot/get");
-
-    /// <summary>POST /api/user/mascot/set — 设置吉祥物</summary>
-    public Task<object?> SetMascotAsync(string mascotId)
-        => PostAsync<object>("/api/user/mascot/set", new { mascotId });
-
-}

+ 0 - 80
cfc-desktop-pet/src/XafPet.Desktop/PetEngine/CfcApiModels.cs

@@ -1,80 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace XafPet.Desktop.PetEngine;
-
-/// <summary>CFC 统一响应包装 Result<T></summary>
-public sealed class ApiResult<T>
-{
-    [JsonPropertyName("code")]
-    public int Code { get; set; }
-
-    [JsonPropertyName("message")]
-    public string? Message { get; set; }
-
-    [JsonPropertyName("data")]
-    public T? Data { get; set; }
-
-    public bool IsSuccess => Code == 200;
-}
-
-public sealed class CfcTask
-{
-    [JsonPropertyName("id")]
-    public long Id { get; set; }
-
-    [JsonPropertyName("title")]
-    public string? Title { get; set; }
-
-    [JsonPropertyName("status")]
-    public string? Status { get; set; }   // PENDING / IN_PROGRESS / COMPLETED / REJECTED
-
-    [JsonPropertyName("dueDate")]
-    public string? DueDate { get; set; }
-}
-
-public sealed class DimensionValue
-{
-    [JsonPropertyName("dimension")]
-    public string? Dimension { get; set; }
-
-    [JsonPropertyName("value")]
-    public int Value { get; set; }
-
-    [JsonPropertyName("color")]
-    public string? Color { get; set; }
-}
-
-public sealed class EnergyOverview
-{
-    [JsonPropertyName("dimensions")]
-    public List<DimensionValue> Dimensions { get; set; } = new();
-}
-
-public sealed class DailyCheckin
-{
-    [JsonPropertyName("checked")]
-    public bool Checked { get; set; }
-
-    [JsonPropertyName("items")]
-    public List<string> Items { get; set; } = new();
-}
-
-/// <summary>API 返回非 200 code 时抛出</summary>
-public sealed class ApiException : Exception
-{
-    public int ApiCode { get; }
-
-    public ApiException(int apiCode, string message) : base(message)
-    {
-        ApiCode = apiCode;
-    }
-}
-
-public sealed class MascotInfo
-{
-    [JsonPropertyName("mascotId")]
-    public string? MascotId { get; set; }
-
-    [JsonPropertyName("name")]
-    public string? Name { get; set; }
-}

+ 0 - 74
cfc-desktop-pet/src/XafPet.Desktop/PetEngine/PetConfig.cs

@@ -1,74 +0,0 @@
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace XafPet.Desktop.PetEngine;
-
-[JsonConverter(typeof(JsonStringEnumConverter))]
-public enum PetType { Animal, Human, Custom }
-
-/// <summary>宠物形象配置(pet-config.json)</summary>
-public sealed class PetConfig
-{
-    [JsonPropertyName("version")]
-    public string Version { get; set; } = "1.0";
-
-    [JsonPropertyName("type")]
-    public PetType Type { get; set; } = PetType.Custom;
-
-    [JsonPropertyName("species")]
-    public string Species { get; set; } = "other";
-
-    [JsonPropertyName("gender")]
-    public string Gender { get; set; } = "neutral";
-
-    [JsonPropertyName("image_base_url")]
-    public string ImageBaseUrl { get; set; } = "";
-
-    [JsonPropertyName("default_image")]
-    public string DefaultImage { get; set; } = "standing.png";
-
-    [JsonPropertyName("actions")]
-    public Dictionary<string, string> Actions { get; set; } = new();
-
-    [JsonPropertyName("sounds")]
-    public Dictionary<string, string> Sounds { get; set; } = new();
-
-    [JsonPropertyName("parameters")]
-    public PetParameters Parameters { get; set; } = new();
-
-    [JsonIgnore]
-    public int CustomStateCount => Actions.Keys.Count(k => k.StartsWith("custom_state_"));
-
-    /// <summary>解析 JSON 字符串;缺省 actions 时补默认映射(key=value)</summary>
-    public static PetConfig Parse(string json)
-    {
-        var config = JsonSerializer.Deserialize<PetConfig>(json)
-            ?? throw new JsonException("pet-config.json 反序列化结果为 null");
-        if (config.Actions.Count == 0)
-        {
-            config.Actions = new Dictionary<string, string>
-            {
-                ["idle"] = "idle",
-                ["happy"] = "happy",
-                ["busy"] = "busy",
-                ["sleeping"] = "sleeping"
-            };
-        }
-        return config;
-    }
-
-    public string GetActionForState(string stateName)
-        => Actions.TryGetValue(stateName, out var v) ? v : stateName;
-}
-
-public sealed class PetParameters
-{
-    [JsonPropertyName("move_distance")]
-    public int MoveDistance { get; set; } = 15;
-
-    [JsonPropertyName("animation_speed")]
-    public double AnimationSpeed { get; set; } = 1.0;
-
-    [JsonPropertyName("interaction_timeout")]
-    public int InteractionTimeout { get; set; } = 300; // 秒
-}

+ 0 - 29
cfc-desktop-pet/src/XafPet.Desktop/PetEngine/PetState.cs

@@ -1,29 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace XafPet.Desktop.PetEngine;
-
-/// <summary>宠物核心状态枚举(C# 侧定义,JS 侧同名接收)</summary>
-public enum PetState
-{
-    Idle,                  // 默认
-    Happy,                 // 任务全完成
-    Busy,                  // 有任务进行中
-    Worried,               // 任务超期未完成
-    Excited,               // 健康打卡完成
-    Remind,                // 提醒用户(喝水/任务)
-    Energetic,             // 身维度能量高
-    Tired,                 // 身维度能量低
-    Curious,               // 智维度能量高
-    Sad,                   // 全维度低
-    Sleeping,              // 用户无操作 >15min
-    Watching,              // 用户活跃,注视光标
-    WaitingForInteraction, // 等待用户互动(打卡确认)
-    MiniGame               // 小游戏模式
-}
-
-/// <summary>推送 WebView 的状态负载</summary>
-public sealed record PetStatePayload(
-    [property: JsonPropertyName("state")] string State,     // 枚举名 camelCase:如 "waitingForInteraction"
-    [property: JsonPropertyName("reason")] string? Reason,  // 可选:如 "drink_water"、"task_complete"
-    [property: JsonPropertyName("data")] object? Data       // 可选:任务列表/能量值等
-);

+ 0 - 62
cfc-desktop-pet/src/XafPet.Desktop/PetEngine/StateManager.cs

@@ -1,62 +0,0 @@
-using System.Text.RegularExpressions;
-
-namespace XafPet.Desktop.PetEngine;
-
-/// <summary>把 CFC API 数据转换为宠物状态(纯逻辑,无 UI 依赖)</summary>
-public static class StateManager
-{
-    /// <summary>核心状态映射。优先级:Sleeping > Excited > Happy > Worried > Busy > Sad > Tired/Energetic/Curious > Idle</summary>
-    public static PetState MapState(
-        List<CfcTask>? tasks,
-        EnergyOverview? energy,
-        DailyCheckin? checkin,
-        bool userInactive)
-    {
-        // 1. 用户无操作 → 睡眠(最高优先级,除非有未完成任务需要提醒)
-        var hasPendingTasks = tasks?.Any(t => t.Status is "PENDING" or "IN_PROGRESS") ?? false;
-        if (userInactive && !hasPendingTasks) return PetState.Sleeping;
-
-        // 2. 健康打卡完成 → 兴奋(次高)
-        if (checkin is { Checked: true }) return PetState.Excited;
-
-        // 3. 任务状态
-        if (tasks is { Count: > 0 })
-        {
-            var allDone = tasks.All(t => t.Status == "COMPLETED");
-            if (allDone) return PetState.Happy;
-
-            // 超期未完成(dueDate 早于今天且非完成态)→ 担忧
-            var today = DateOnly.FromDateTime(DateTime.Today);
-            var overdue = tasks.Any(t =>
-                t.Status is "PENDING" or "IN_PROGRESS" &&
-                DateOnly.TryParse(t.DueDate, out var due) && due < today);
-            if (overdue) return PetState.Worried;
-
-            return PetState.Busy;
-        }
-
-        // 4. 五维能量
-        if (energy is { Dimensions.Count: > 0 })
-        {
-            var dims = energy.Dimensions;
-            var allLow = dims.All(d => d.Value < 30);
-            if (allLow) return PetState.Sad;
-
-            var body = dims.FirstOrDefault(d => d.Dimension == "身");
-            if (body != null && body.Value < 30) return PetState.Tired;
-            if (body != null && body.Value >= 85) return PetState.Energetic;
-
-            var mind = dims.FirstOrDefault(d => d.Dimension == "智");
-            if (mind != null && mind.Value >= 85) return PetState.Curious;
-        }
-
-        return PetState.Idle;
-    }
-
-    /// <summary>枚举 → camelCase(JS 侧状态名)。WaitingForInteraction → waitingForInteraction</summary>
-    public static string StateToCamelCase(PetState state)
-    {
-        var name = state.ToString();
-        return Regex.Replace(name, "(^[A-Z])", m => m.Value.ToLowerInvariant());
-    }
-}

+ 0 - 11
cfc-desktop-pet/src/XafPet.Desktop/PetEngine/WebViewHost.xaml

@@ -1,11 +0,0 @@
-<UserControl x:Class="XafPet.Desktop.PetEngine.WebViewHost"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
-             Background="Transparent">
-    <Grid>
-        <wv2:WebView2 x:Name="PetWebView"
-                       DefaultBackgroundColor="Transparent"
-                       HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
-    </Grid>
-</UserControl>

+ 0 - 77
cfc-desktop-pet/src/XafPet.Desktop/PetEngine/WebViewHost.xaml.cs

@@ -1,77 +0,0 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using System.Windows.Controls;
-using Microsoft.Web.WebView2.Core;
-
-namespace XafPet.Desktop.PetEngine;
-
-public partial class WebViewHost : UserControl
-{
-    public event Action<string, string?>? InteractionReceived;
-
-    public WebViewHost()
-    {
-        InitializeComponent();
-        Loaded += async (_, _) => await InitializeWebViewAsync();
-    }
-
-    private async Task InitializeWebViewAsync()
-    {
-        var env = await CoreWebView2Environment.CreateAsync(
-            userDataFolder: Path.Combine(
-                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
-                "XafPet", "WebView2"));
-        await PetWebView.EnsureCoreWebView2Async(env);
-
-        PetWebView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
-        PetWebView.CoreWebView2.Settings.AreDevToolsEnabled = true;
-        PetWebView.CoreWebView2.Settings.IsStatusBarEnabled = false;
-
-        PetWebView.CoreWebView2.AddHostObjectToScript("cfcHost", new CfcHostBridge(this));
-
-        var htmlPath = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "Assets", "pet.html"));
-        PetWebView.CoreWebView2.Navigate($"file:///{htmlPath.Replace('\\', '/')}");
-    }
-
-    /// <summary>C# → JS:推送宠物状态</summary>
-    public void NotifyState(string stateJson)
-    {
-        PetWebView.ExecuteScriptAsync($"window.cfcPetAPI?.notifyState({stateJson})");
-    }
-
-    /// <summary>C# → JS:推送自定义状态</summary>
-    public void SetCustomState(string stateName, string dataJson)
-    {
-        PetWebView.ExecuteScriptAsync(
-            $"window.cfcPetAPI?.setCustomState('{stateName}', {dataJson})");
-    }
-
-    /// <summary>C# → JS:显示气泡</summary>
-    public void ShowBubble(string text, int durationMs = 5000)
-    {
-        var escaped = text.Replace("\\", "\\\\").Replace("'", "\\'");
-        PetWebView.ExecuteScriptAsync(
-            $"window.cfcPetAPI?.showBubble('{escaped}', {durationMs})");
-    }
-
-    /// <summary>C# → JS:加载配置</summary>
-    public void LoadPetConfig(string configJson)
-    {
-        PetWebView.ExecuteScriptAsync($"window.cfcPetAPI?.loadConfig({configJson})");
-    }
-
-    internal void RaiseInteraction(string action, string? dataJson)
-        => InteractionReceived?.Invoke(action, dataJson);
-}
-
-/// <summary>暴露给 JS 的 C# 宿主对象(window.chrome.webview.hostObjects.sync.cfcHost)</summary>
-internal sealed class CfcHostBridge
-{
-    private readonly WebViewHost _host;
-    public CfcHostBridge(WebViewHost host) => _host = host;
-    public void onInteract(string action, string dataJson)
-        => _host.RaiseInteraction(action, dataJson);
-    public void onDragStart() { }
-    public void onDragEnd() { }
-}

+ 0 - 20
cfc-desktop-pet/src/XafPet.Desktop/SettingsWindow.xaml

@@ -1,20 +0,0 @@
-<Window x:Class="XafPet.Desktop.SettingsWindow"
-        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        Title="浠艾福桌面宠物 - 设置"
-        Width="360" Height="340"
-        WindowStartupLocation="CenterScreen">
-    <StackPanel Margin="20">
-        <TextBlock Text="后端地址" FontWeight="SemiBold" Margin="0,0,0,6"/>
-        <TextBox x:Name="BaseUrlBox" Text="http://localhost:9082" Padding="6"/>
-
-        <TextBlock Text="JWT Token" FontWeight="SemiBold" Margin="0,16,0,6"/>
-        <PasswordBox x:Name="TokenBox" Padding="6"/>
-
-        <TextBlock Text="宠物形象" FontWeight="SemiBold" Margin="0,16,0,6"/>
-        <ComboBox x:Name="PetCombo" Padding="6" MinWidth="120"/>
-
-        <Button Content="保存" Click="OnSaveClick" Margin="0,24,0,0" Padding="10,6"
-                HorizontalAlignment="Right" Width="100"/>
-    </StackPanel>
-</Window>

+ 0 - 44
cfc-desktop-pet/src/XafPet.Desktop/SettingsWindow.xaml.cs

@@ -1,44 +0,0 @@
-using System.IO;
-using System.Text.Json;
-using System.Windows;
-using XafPet.Desktop.PetEngine;
-
-namespace XafPet.Desktop;
-
-public partial class SettingsWindow : Window
-{
-    public SettingsWindow(string? currentToken, string currentBaseUrl)
-    {
-        InitializeComponent();
-        BaseUrlBox.Text = currentBaseUrl;
-        if (!string.IsNullOrEmpty(currentToken)) TokenBox.Password = currentToken;
-        LoadPets();
-    }
-
-    private void LoadPets()
-    {
-        var dirs = new List<string>();
-        var builtin = Path.Combine(AppContext.BaseDirectory, "DefaultPets");
-        if (Directory.Exists(builtin)) dirs.AddRange(Directory.GetDirectories(builtin));
-        var userDir = Path.Combine(
-            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
-            "XafPet", "pets");
-        if (Directory.Exists(userDir)) dirs.AddRange(Directory.GetDirectories(userDir));
-
-        foreach (var d in dirs)
-        {
-            if (File.Exists(Path.Combine(d, "pet-config.json")))
-                PetCombo.Items.Add(Path.GetFileName(d));
-        }
-        if (PetCombo.Items.Count > 0) PetCombo.SelectedIndex = 0;
-    }
-
-    public string BaseUrl => BaseUrlBox.Text.Trim();
-    public string? Token => TokenBox.Password.Length > 0 ? TokenBox.Password : null;
-
-    private void OnSaveClick(object sender, RoutedEventArgs e)
-    {
-        DialogResult = true;
-        Close();
-    }
-}

+ 0 - 23
cfc-desktop-pet/src/XafPet.Desktop/SingleInstanceHelper.cs

@@ -1,23 +0,0 @@
-using System.IO;
-using System.Runtime.InteropServices;
-
-namespace XafPet.Desktop;
-
-/// <summary>简单的单实例检测(按进程名匹配)</summary>
-public static class SingleInstanceHelper
-{
-    [DllImport("user32.dll")]
-    private static extern bool SetForegroundWindow(IntPtr hWnd);
-
-    public static bool TryEnterFirstInstance()
-    {
-        var name = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
-        var instances = System.Diagnostics.Process.GetProcessesByName(name);
-        if (instances.Length > 1)
-        {
-            SetForegroundWindow(instances[0].MainWindowHandle);
-            return false;
-        }
-        return true;
-    }
-}

+ 0 - 43
cfc-desktop-pet/src/XafPet.Desktop/TokenStore.cs

@@ -1,43 +0,0 @@
-using System.IO;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace XafPet.Desktop;
-
-public static class TokenStore
-{
-    private static string _path = Path.Combine(
-        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
-        "XafPet", "token.bin");
-
-    public static void SetPath(string path) => _path = path;
-
-    public static void Save(string token)
-    {
-        Directory.CreateDirectory(Path.GetDirectoryName(_path)!);
-        var encrypted = ProtectedData.Protect(
-            Encoding.UTF8.GetBytes(token), null, DataProtectionScope.CurrentUser);
-        File.WriteAllBytes(_path, encrypted);
-    }
-
-    public static string? Load()
-    {
-        if (!File.Exists(_path)) return null;
-        try
-        {
-            var encrypted = File.ReadAllBytes(_path);
-            var decrypted = ProtectedData.Unprotect(
-                encrypted, null, DataProtectionScope.CurrentUser);
-            return Encoding.UTF8.GetString(decrypted);
-        }
-        catch (CryptographicException)
-        {
-            return null;
-        }
-    }
-
-    public static void Clear()
-    {
-        if (File.Exists(_path)) File.Delete(_path);
-    }
-}

+ 0 - 29
cfc-desktop-pet/src/XafPet.Desktop/XafPet.Desktop.csproj

@@ -1,29 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <OutputType>WinExe</OutputType>
-    <TargetFramework>net6.0-windows</TargetFramework>
-    <LangVersion>11</LangVersion>
-    <Nullable>enable</Nullable>
-    <UseWPF>true</UseWPF>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <AssemblyName>XafPet.Desktop</AssemblyName>
-    <RootNamespace>XafPet.Desktop</RootNamespace>
-  </PropertyGroup>
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2210.55" />
-  </ItemGroup>
-  <ItemGroup>
-    <FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="Assets\**" CopyToOutputDirectory="PreserveNewest" />
-    <None Include="DefaultPets\**" CopyToOutputDirectory="PreserveNewest" />
-  </ItemGroup>
-  <!-- 发布配置(单文件 exe,Self-Contained) -->
-  <PropertyGroup>
-    <PublishSingleFile>true</PublishSingleFile>
-    <SelfContained>true</SelfContained>
-    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
-    <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
-  </PropertyGroup>
-</Project>

+ 0 - 96
cfc-desktop-pet/tests/XafPet.Desktop.Tests/ApiClientTests.cs

@@ -1,96 +0,0 @@
-using System.Net;
-using System.Text.Json;
-using Moq;
-using Moq.Protected;
-using XafPet.Desktop.PetEngine;
-using Xunit;
-
-namespace XafPet.Desktop.Tests;
-
-public class ApiClientTests
-{
-    private static ApiClient CreateClientWithResponse(HttpStatusCode code, string json, string authToken = "test-token")
-    {
-        var handler = new Mock<HttpMessageHandler>();
-        handler.Protected()
-            .Setup<Task<HttpResponseMessage>>("SendAsync",
-                ItExpr.IsAny<HttpRequestMessage>(),
-                ItExpr.IsAny<CancellationToken>())
-            .ReturnsAsync(new HttpResponseMessage(code)
-            {
-                Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json")
-            });
-        var http = new HttpClient(handler.Object);
-        return new ApiClient(http, "http://localhost:9082", authToken);
-    }
-
-    [Fact]
-    public async Task GetTodayTasks_ReturnsTaskList()
-    {
-        var json = """
-        { "code": 200, "message": "ok", "data": [
-            { "id": 1, "title": "喝水", "status": "PENDING", "dueDate": "2026-10-10" }
-        ] }
-        """;
-        var client = CreateClientWithResponse(HttpStatusCode.OK, json);
-
-        var tasks = await client.GetTodayTasksAsync();
-
-        Assert.Single(tasks);
-        Assert.Equal("喝水", tasks[0].Title);
-        Assert.Equal("PENDING", tasks[0].Status);
-    }
-
-    [Fact]
-    public async Task GetEnergyOverview_ReturnsDimensions()
-    {
-        var json = """
-        { "code": 200, "message": "ok", "data": {
-            "dimensions": [
-              { "dimension": "身", "value": 80, "color": "#FF8C42" },
-              { "dimension": "智", "value": 45, "color": "#6366F1" }
-            ]
-        } }
-        """;
-        var client = CreateClientWithResponse(HttpStatusCode.OK, json);
-
-        var overview = await client.GetEnergyOverviewAsync();
-
-        Assert.Equal(2, overview.Dimensions.Count);
-        Assert.Equal(80, overview.Dimensions[0].Value);
-    }
-
-    [Fact]
-    public async Task GetDailyCheckin_ReturnsStatus()
-    {
-        var json = """
-        { "code": 200, "message": "ok", "data": { "checked": true, "items": ["water", "exercise"] } }
-        """;
-        var client = CreateClientWithResponse(HttpStatusCode.OK, json);
-
-        var status = await client.GetDailyCheckinAsync();
-
-        Assert.True(status.Checked);
-        Assert.Contains("water", status.Items);
-    }
-
-    [Fact]
-    public async Task CompleteDrinkWater_PostsCorrectEndpoint()
-    {
-        var json = """{ "code": 200, "message": "打卡成功", "data": null }""";
-        var client = CreateClientWithResponse(HttpStatusCode.OK, json);
-
-        var result = await client.CompleteDrinkWaterAsync();
-
-        Assert.Equal("打卡成功", result.Message);
-    }
-
-    [Fact]
-    public async Task NonSuccessCode_ThrowsApiException()
-    {
-        var json = """{ "code": 500, "message": "服务器错误", "data": null }""";
-        var client = CreateClientWithResponse(HttpStatusCode.OK, json);
-
-        await Assert.ThrowsAsync<ApiException>(() => client.GetTodayTasksAsync());
-    }
-}

+ 0 - 68
cfc-desktop-pet/tests/XafPet.Desktop.Tests/PetConfigTests.cs

@@ -1,68 +0,0 @@
-using System.Text.Json;
-using XafPet.Desktop.PetEngine;
-using Xunit;
-
-namespace XafPet.Desktop.Tests;
-
-public class PetConfigTests
-{
-    private const string ValidJson = """
-    {
-      "version": "1.0",
-      "type": "animal",
-      "species": "cockatiel",
-      "gender": "female",
-      "image_base_url": "./DefaultPets/xibao/",
-      "default_image": "standing.png",
-      "actions": {
-        "idle": "wings_idle",
-        "happy": "wings_spread",
-        "custom_state_1": "user_animation_1"
-      },
-      "parameters": {
-        "move_distance": 15,
-        "animation_speed": 1.0,
-        "interaction_timeout": 300
-      }
-    }
-    """;
-
-    [Fact]
-    public void Parse_ValidJson_LoadsAllFields()
-    {
-        var config = PetConfig.Parse(ValidJson);
-
-        Assert.Equal("1.0", config.Version);
-        Assert.Equal(PetType.Animal, config.Type);
-        Assert.Equal("cockatiel", config.Species);
-        Assert.Equal("female", config.Gender);
-        Assert.Equal("./DefaultPets/xibao/", config.ImageBaseUrl);
-        Assert.Equal("standing.png", config.DefaultImage);
-        Assert.Equal("wings_idle", config.Actions["idle"]);
-        Assert.Equal(15, config.Parameters.MoveDistance);
-    }
-
-    [Fact]
-    public void Parse_MissingActionsKey_UsesDefaults()
-    {
-        var json = """{ "version": "1.0", "type": "human", "image_base_url": "./pets/", "default_image": "a.png" }""";
-        var config = PetConfig.Parse(json);
-
-        Assert.Equal("idle", config.Actions["idle"]);
-    }
-
-    [Fact]
-    public void Parse_InvalidJson_ThrowsJsonException()
-    {
-        Assert.Throws<JsonException>(() => PetConfig.Parse("{ not valid json"));
-    }
-
-    [Fact]
-    public void Parse_CustomStates_CountedCorrectly()
-    {
-        var config = PetConfig.Parse(ValidJson);
-
-        Assert.Equal(1, config.CustomStateCount);
-        Assert.Equal("user_animation_1", config.GetActionForState("custom_state_1"));
-    }
-}

+ 0 - 8
cfc-desktop-pet/tests/XafPet.Desktop.Tests/README.md

@@ -1,8 +0,0 @@
-```json
-{
-  "version": "1.0",
-  "type": "project",
-  "name": "XafPet.Desktop",
-  "description": "Desktop pet application scaffold for XafPet (Care Family)"
-}
-```

+ 0 - 107
cfc-desktop-pet/tests/XafPet.Desktop.Tests/StateManagerTests.cs

@@ -1,107 +0,0 @@
-using XafPet.Desktop.PetEngine;
-using Xunit;
-
-namespace XafPet.Desktop.Tests;
-
-public class StateManagerTests
-{
-    [Fact]
-    public void AllTasksCompleted_ReturnsHappy()
-    {
-        var tasks = new List<CfcTask>
-        {
-            new() { Id = 1, Title = "喝水", Status = "COMPLETED" },
-            new() { Id = 2, Title = "运动", Status = "COMPLETED" }
-        };
-        var state = StateManager.MapState(tasks, null, null, false);
-        Assert.Equal(PetState.Happy, state);
-    }
-
-    [Fact]
-    public void SomeTasksPending_ReturnsBusy()
-    {
-        var tasks = new List<CfcTask>
-        {
-            new() { Id = 1, Title = "喝水", Status = "COMPLETED" },
-            new() { Id = 2, Title = "运动", Status = "PENDING" }
-        };
-        var state = StateManager.MapState(tasks, null, null, false);
-        Assert.Equal(PetState.Busy, state);
-    }
-
-    [Fact]
-    public void NoTasks_ReturnsIdle()
-    {
-        var state = StateManager.MapState(new List<CfcTask>(), null, null, false);
-        Assert.Equal(PetState.Idle, state);
-    }
-
-    [Fact]
-    public void CheckinDone_OverridesToExcited()
-    {
-        var state = StateManager.MapState(new List<CfcTask>(), null,
-            new DailyCheckin { Checked = true, Items = new List<string> { "water" } }, false);
-        Assert.Equal(PetState.Excited, state);
-    }
-
-    [Fact]
-    public void LowBodyEnergy_ReturnsTired()
-    {
-        var energy = new EnergyOverview
-        {
-            Dimensions = new List<DimensionValue>
-            {
-                new() { Dimension = "身", Value = 20, Color = "#FF8C42" },
-                new() { Dimension = "智", Value = 80, Color = "#6366F1" }
-            }
-        };
-        var state = StateManager.MapState(new List<CfcTask>(), energy, null, false);
-        Assert.Equal(PetState.Tired, state);
-    }
-
-    [Fact]
-    public void AllDimensionsLow_ReturnsSad()
-    {
-        var energy = new EnergyOverview
-        {
-            Dimensions = new List<DimensionValue>
-            {
-                new() { Dimension = "身", Value = 15, Color = "#FF8C42" },
-                new() { Dimension = "智", Value = 20, Color = "#6366F1" },
-                new() { Dimension = "富", Value = 25, Color = "#F59E0B" }
-            }
-        };
-        var state = StateManager.MapState(new List<CfcTask>(), energy, null, false);
-        Assert.Equal(PetState.Sad, state);
-    }
-
-    [Fact]
-    public void HighBodyEnergy_ReturnsEnergetic()
-    {
-        var energy = new EnergyOverview
-        {
-            Dimensions = new List<DimensionValue>
-            {
-                new() { Dimension = "身", Value = 90, Color = "#FF8C42" }
-            }
-        };
-        var state = StateManager.MapState(new List<CfcTask>(), energy, null, false);
-        Assert.Equal(PetState.Energetic, state);
-    }
-
-    [Fact]
-    public void UserInactive_ReturnsSleeping()
-    {
-        var state = StateManager.MapState(new List<CfcTask>(), null, null, true);
-        Assert.Equal(PetState.Sleeping, state);
-    }
-
-    [Fact]
-    public void StateToCamelCase_ConvertsEnumName()
-    {
-        Assert.Equal("waitingForInteraction",
-            StateManager.StateToCamelCase(PetState.WaitingForInteraction));
-        Assert.Equal("miniGame", StateManager.StateToCamelCase(PetState.MiniGame));
-        Assert.Equal("idle", StateManager.StateToCamelCase(PetState.Idle));
-    }
-}

+ 0 - 18
cfc-desktop-pet/tests/XafPet.Desktop.Tests/XafPet.Desktop.Tests.csproj

@@ -1,18 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFramework>net6.0-windows</TargetFramework>
-    <LangVersion>11</LangVersion>
-    <Nullable>enable</Nullable>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <IsPackable>false</IsPackable>
-  </PropertyGroup>
-  <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
-    <PackageReference Include="xunit" Version="2.5.3" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
-    <PackageReference Include="Moq" Version="4.20.69" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\src\XafPet.Desktop\XafPet.Desktop.csproj" />
-  </ItemGroup>
-</Project>