mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
feat.airapp与融合桌面
This commit is contained in:
7
.trae/specs/air-app-whiteboard/checklist.md
Normal file
7
.trae/specs/air-app-whiteboard/checklist.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Checklist
|
||||
|
||||
- [x] Main app builds in Debug.
|
||||
- [x] AirAppHost builds in Debug.
|
||||
- [x] Tests project builds in Debug.
|
||||
- [x] `AirAppLauncherServiceTests` pass.
|
||||
- [ ] Manual UI verification on a running desktop session.
|
||||
26
.trae/specs/air-app-whiteboard/spec.md
Normal file
26
.trae/specs/air-app-whiteboard/spec.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Air APP Whiteboard
|
||||
|
||||
## Goal
|
||||
|
||||
Allow the built-in whiteboard desktop components to open a full-screen Air APP that runs in `LanMountainDesktop.AirAppHost` and reuses the same persisted whiteboard note as the source component instance.
|
||||
|
||||
## Scope
|
||||
|
||||
- Add a toolbar surface-mode button to `WhiteboardWidget`.
|
||||
- In component mode, the button opens the `whiteboard` Air APP through `IAirAppLauncherService`.
|
||||
- In Air APP mode, the same button saves the current note and closes the Air APP window.
|
||||
- `DesktopWhiteboard` and `DesktopBlackboardLandscape` share the same mechanism and keep using their component id plus placement id as the note identity.
|
||||
- `LanMountainDesktop.AirAppHost` may reference the host assembly to reuse built-in UI controls, but the host app must not reference AirAppHost as a normal assembly dependency.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Third-party Air APP SDK declarations.
|
||||
- Whiteboard feature rewrites or alternate whiteboard persistence.
|
||||
- Taskbar minimization behavior; v1 closes the Air APP window when the user exits from the bottom toolbar.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Building the main app also builds and copies `LanMountainDesktop.AirAppHost` output.
|
||||
- Clicking the whiteboard toolbar full-screen button launches a separate AirAppHost process.
|
||||
- Repeated opens of the same whiteboard component instance activate the existing process instead of spawning duplicates.
|
||||
- Closing and reopening the Air APP keeps the same whiteboard contents.
|
||||
8
.trae/specs/air-app-whiteboard/tasks.md
Normal file
8
.trae/specs/air-app-whiteboard/tasks.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Add `whiteboard` launch support to `AirAppLauncherService`.
|
||||
- [x] Add whiteboard single-instance keys based on component id and placement id.
|
||||
- [x] Add component/Air APP surface modes to `WhiteboardWidget`.
|
||||
- [x] Render `WhiteboardWidget` full screen from `LanMountainDesktop.AirAppHost`.
|
||||
- [x] Keep AirAppHost build/copy output available from the main app build.
|
||||
- [x] Add launcher argument and instance-key tests.
|
||||
8
.trae/specs/air-app-window-chrome/checklist.md
Normal file
8
.trae/specs/air-app-window-chrome/checklist.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Checklist
|
||||
|
||||
- [x] Descriptor supports Standard, Borderless, FullScreen, Tool, and BackgroundOnly modes.
|
||||
- [x] World Clock Air APP keeps the LanMountain custom title bar.
|
||||
- [x] Whiteboard Air APP opens as a fullscreen titlebar-less window.
|
||||
- [x] Air APP windows do not use fused desktop bottom-most services.
|
||||
- [x] Air APP windows do not use `Topmost=true` promotion.
|
||||
- [ ] Manual verification for each chrome mode once non-built-in Air APP declarations are added.
|
||||
22
.trae/specs/air-app-window-chrome/spec.md
Normal file
22
.trae/specs/air-app-window-chrome/spec.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Air APP Window Chrome
|
||||
|
||||
## Goal
|
||||
|
||||
Give Air APPs explicit window chrome modes so title bars, fullscreen windows, borderless windows, tool windows, and future background-only apps are configured by the Air APP host instead of ad hoc component code.
|
||||
|
||||
## Behavior
|
||||
|
||||
- Air APP host resolves an `AirAppWindowDescriptor` from launch options before creating content.
|
||||
- Supported chrome modes are `Standard`, `Borderless`, `FullScreen`, `Tool`, and `BackgroundOnly`.
|
||||
- `Standard` uses the LanMountain custom title bar and normal app-window behavior.
|
||||
- `Borderless` hides the custom title bar while keeping a normal app window.
|
||||
- `FullScreen` hides the custom title bar, removes rounded shell chrome, and enters fullscreen.
|
||||
- `Tool` keeps host-owned chrome but disables resizing and hides the taskbar entry.
|
||||
- `BackgroundOnly` is reserved for a later background Air APP lifecycle and is not used by built-in v1 apps.
|
||||
- Built-in `world-clock` uses `Standard`; built-in `whiteboard` uses `FullScreen`.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Third-party plugin Air APP declarations.
|
||||
- Replacing Launcher lifecycle IPC.
|
||||
- Moving title-bar rendering into desktop components.
|
||||
7
.trae/specs/air-app-window-chrome/tasks.md
Normal file
7
.trae/specs/air-app-window-chrome/tasks.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Add `AirAppWindowChromeMode` and `AirAppWindowDescriptor`.
|
||||
- [x] Map built-in `world-clock` to `Standard` chrome.
|
||||
- [x] Map built-in `whiteboard` to `FullScreen` chrome.
|
||||
- [x] Apply descriptor settings from `AirAppWindow`.
|
||||
- [x] Add regression tests for supported modes and built-in mode mapping.
|
||||
@@ -0,0 +1,14 @@
|
||||
- [x] ComponentCategoryIconResolver 基于 IconKey 正确解析分类图标
|
||||
- [x] IconKey 为 "Clock" 时解析为 Icon.Clock
|
||||
- [x] IconKey 为 "WeatherSunny" 时解析为 Icon.WeatherSunny
|
||||
- [x] IconKey 为 "News" 时解析为 Icon.News
|
||||
- [x] IconKey 为 "Edit" 时解析为 Icon.Edit
|
||||
- [x] IconKey 为无效值时回退到 Icon.Apps
|
||||
- [x] 分类 ID 为 "all" 时返回 Icon.Apps
|
||||
- [x] ComponentLibraryCategoryViewModel.Icon 类型为 FluentIcons.Common.Icon
|
||||
- [x] FusedDesktopComponentLibraryControl.axaml.cs 不再包含硬编码 ResolveCategoryIcon 方法
|
||||
- [x] ComponentLibraryWindow.axaml.cs 不再包含硬编码 ResolveCategoryIcon 方法
|
||||
- [x] MainWindow.ComponentSystem.cs 不再包含硬编码 ResolveComponentLibraryCategoryIcon 方法
|
||||
- [x] 三处组件库入口对同一分类显示相同图标
|
||||
- [x] dotnet build 无编译错误
|
||||
- [x] dotnet test 全部通过
|
||||
73
.trae/specs/fused-desktop-category-icon-unification/spec.md
Normal file
73
.trae/specs/fused-desktop-category-icon-unification/spec.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 融合桌面组件库分类图标统一规格
|
||||
|
||||
## Why
|
||||
|
||||
融合桌面组件库窗口(FusedDesktopComponentLibraryControl)的分类图标使用了手动硬编码的 `ResolveCategoryIcon` 方法映射分类 ID 到 `Symbol` 枚举,与阑山桌面主窗口(MainWindow)中的映射存在不一致(例如 `Info` 分类在主窗口映射到 `Symbol.Apps`,在融合桌面映射到 `Symbol.Info`)。同时,`DesktopComponentDefinition.IconKey` 字段已经存储了正确的 FluentIcon 枚举名称字符串,但未被利用。需要统一三处图标映射逻辑,确保所有组件库入口的分类图标一致且正确。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **统一分类图标映射**:将三处分散的 `ResolveCategoryIcon`/`ResolveComponentLibraryCategoryIcon` 方法合并为共享的统一映射
|
||||
- **使用 `IconKey` 驱动图标**:分类图标应基于该分类下组件的 `IconKey` 字段推导,而非硬编码的分类 ID 映射
|
||||
- **使用 `FluentIcons.Common.Icon` 枚举**:`fi:FluentIcon` 控件使用 `Icon` 枚举(非 `Symbol` 枚举),分类图标应使用 `Icon` 枚举以与 `fi:FluentIcon` 兼容
|
||||
- **修改 ViewModel**:`ComponentLibraryCategoryViewModel.Icon` 属性类型从 `Symbol` 改为 `Icon`
|
||||
|
||||
## Impact
|
||||
|
||||
- 受影响文件:
|
||||
- `LanMountainDesktop/ViewModels/ComponentLibraryWindowViewModel.cs`(Icon 属性类型从 Symbol 改为 Icon)
|
||||
- `LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml`(绑定路径不变,但 Icon 类型变化)
|
||||
- `LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml.cs`(移除硬编码映射,使用统一方法)
|
||||
- `LanMountainDesktop/Views/ComponentLibraryWindow.axaml.cs`(移除硬编码映射,使用统一方法)
|
||||
- `LanMountainDesktop/Views/MainWindow.ComponentSystem.cs`(移除硬编码映射,使用统一方法)
|
||||
- 新增共享映射工具类(或在现有服务中添加)
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 统一分类图标映射
|
||||
|
||||
系统 SHALL 提供一个共享的分类图标映射方法,所有组件库入口(阑山桌面主窗口、融合桌面组件库、独立组件库窗口)均使用此方法。
|
||||
|
||||
#### Scenario: 图标映射来源
|
||||
- **GIVEN** 一个组件分类 ID
|
||||
- **WHEN** 需要获取该分类的图标
|
||||
- **THEN** 系统应基于该分类下组件的 `IconKey` 字段推导分类图标
|
||||
- **AND** 推导规则为:取该分类下第一个组件的 `IconKey`,解析为 `FluentIcons.Common.Icon` 枚举值
|
||||
- **AND** 若 `IconKey` 无法解析为有效的 `Icon` 枚举值,则回退到 `Icon.Apps`
|
||||
|
||||
#### Scenario: 特殊分类处理
|
||||
- **GIVEN** 分类 ID 为 "all"
|
||||
- **WHEN** 需要获取该分类的图标
|
||||
- **THEN** 系统应返回 `Icon.Apps`
|
||||
|
||||
#### Scenario: 三处映射一致性
|
||||
- **GIVEN** 任意一个组件分类
|
||||
- **WHEN** 在阑山桌面主窗口、融合桌面组件库、独立组件库窗口中显示该分类
|
||||
- **THEN** 三处应显示完全相同的图标
|
||||
|
||||
### Requirement: ViewModel 使用 Icon 枚举
|
||||
|
||||
`ComponentLibraryCategoryViewModel.Icon` 属性 SHALL 使用 `FluentIcons.Common.Icon` 枚举类型(而非 `FluentIcons.Common.Symbol`),以与 `fi:FluentIcon` 控件的 `Icon` 属性兼容。
|
||||
|
||||
#### Scenario: XAML 绑定兼容
|
||||
- **GIVEN** `ComponentLibraryCategoryViewModel.Icon` 属性类型为 `Icon`
|
||||
- **WHEN** 在 XAML 中通过 `{Binding Icon}` 绑定到 `fi:FluentIcon` 控件
|
||||
- **THEN** 图标应正确渲染,无需额外转换
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 分类图标解析
|
||||
|
||||
原实现使用硬编码的 `if/switch` 语句将分类 ID 映射到 `Symbol` 枚举,新实现改为:
|
||||
|
||||
- 使用 `DesktopComponentDefinition.IconKey` 字段作为图标来源
|
||||
- 通过 `Enum.TryParse<Icon>(iconKey, ignoreCase: true, out var icon)` 解析
|
||||
- 解析失败时回退到 `Icon.Apps`
|
||||
- 移除所有三处硬编码映射方法
|
||||
|
||||
### Requirement: ComponentLibraryCategoryViewModel.Icon 类型
|
||||
|
||||
原类型为 `Symbol`,修改为 `Icon`,与 `fi:FluentIcon` 控件的 `Icon` 依赖属性类型一致。
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
无移除的需求。
|
||||
38
.trae/specs/fused-desktop-category-icon-unification/tasks.md
Normal file
38
.trae/specs/fused-desktop-category-icon-unification/tasks.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Task 1: 创建共享分类图标映射工具
|
||||
- [x] SubTask 1.1: 在 `LanMountainDesktop.ComponentSystem` 命名空间下创建 `ComponentCategoryIconResolver` 静态类
|
||||
- [x] SubTask 1.2: 实现 `ResolveCategoryIcon(string categoryId, IEnumerable<DesktopComponentDefinition> categoryComponents)` 方法,基于 IconKey 解析为 `FluentIcons.Common.Icon`
|
||||
- [x] SubTask 1.3: 添加单元测试验证图标解析逻辑(TDD:先写失败测试,再实现)
|
||||
|
||||
- [x] Task 2: 修改 ViewModel 的 Icon 属性类型
|
||||
- [x] SubTask 2.1: 将 `ComponentLibraryCategoryViewModel.Icon` 属性类型从 `Symbol` 改为 `Icon`
|
||||
- [x] SubTask 2.2: 更新构造函数参数类型
|
||||
|
||||
- [x] Task 3: 更新 FusedDesktopComponentLibraryControl.axaml.cs
|
||||
- [x] SubTask 3.1: 移除 `ResolveCategoryIcon` 硬编码方法
|
||||
- [x] SubTask 3.2: 在 `LoadCategories` 中使用 `ComponentCategoryIconResolver.ResolveCategoryIcon`
|
||||
- [x] SubTask 3.3: 更新 "all" 分类图标从 `Symbol.Apps` 改为 `Icon.Apps`
|
||||
|
||||
- [x] Task 4: 更新 ComponentLibraryWindow.axaml.cs
|
||||
- [x] SubTask 4.1: 移除 `ResolveCategoryIcon` 硬编码方法
|
||||
- [x] SubTask 4.2: 使用 `ComponentCategoryIconResolver.ResolveCategoryIcon`
|
||||
|
||||
- [x] Task 5: 更新 MainWindow.ComponentSystem.cs
|
||||
- [x] SubTask 5.1: 移除 `ResolveComponentLibraryCategoryIcon` 硬编码方法
|
||||
- [x] SubTask 5.2: 使用 `ComponentCategoryIconResolver.ResolveCategoryIcon`
|
||||
- [x] SubTask 5.3: 更新 `ComponentLibraryCategory` 记录的 `Icon` 字段类型从 `Symbol` 改为 `Icon`
|
||||
- [x] SubTask 5.4: 更新 `GetComponentLibraryCategories` 方法中的图标解析调用
|
||||
|
||||
- [x] Task 6: 更新 XAML 绑定
|
||||
- [x] SubTask 6.1: 验证 `FusedDesktopComponentLibraryControl.axaml` 中 `fi:FluentIcon Icon="{Binding Icon}"` 绑定在新类型下正常工作
|
||||
|
||||
- [x] Task 7: 构建验证
|
||||
- [x] SubTask 7.1: 运行 `dotnet build` 确保无编译错误
|
||||
- [x] SubTask 7.2: 运行 `dotnet test` 确保所有测试通过
|
||||
|
||||
# Task Dependencies
|
||||
- Task 2 依赖于 Task 1(共享映射工具)
|
||||
- Task 3、4、5 依赖于 Task 1 和 Task 2
|
||||
- Task 6 依赖于 Task 2(类型变更后验证绑定)
|
||||
- Task 7 依赖于所有前置任务
|
||||
10
.trae/specs/launcher-managed-air-app-lifecycle/checklist.md
Normal file
10
.trae/specs/launcher-managed-air-app-lifecycle/checklist.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Checklist
|
||||
|
||||
- [x] `LanMountainDesktop.Shared.IPC` builds in Debug.
|
||||
- [x] `LanMountainDesktop.Launcher` builds in Debug.
|
||||
- [x] `LanMountainDesktop` builds in Debug.
|
||||
- [x] `LanMountainDesktop.AirAppHost` builds in Debug.
|
||||
- [x] `LanMountainDesktop.Tests` builds in Debug.
|
||||
- [x] Air APP launcher and lifecycle unit tests pass.
|
||||
- [x] Direct-host fallback starts Launcher in `air-app-broker` mode instead of debug/normal launch mode.
|
||||
- [ ] Manual process-lifetime verification with the running desktop.
|
||||
22
.trae/specs/launcher-managed-air-app-lifecycle/spec.md
Normal file
22
.trae/specs/launcher-managed-air-app-lifecycle/spec.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Launcher Managed Air APP Lifecycle
|
||||
|
||||
## Goal
|
||||
|
||||
Make Launcher the authoritative lifecycle manager for built-in Air APP processes. The desktop host requests Air APP operations through IPC, while Launcher creates, activates, tracks, and cleans up Air APP host processes.
|
||||
|
||||
## Behavior
|
||||
|
||||
- Launcher exposes `IAirAppLifecycleService` on the dedicated `LanMountainDesktop.Launcher.AirApp.v1` pipe.
|
||||
- Desktop host calls Launcher IPC for `world-clock` and `whiteboard`; it does not directly start `LanMountainDesktop.AirAppHost`.
|
||||
- If the dedicated pipe is unavailable, the desktop host starts Launcher with the hidden `air-app-broker --requester-pid <pid>` command and retries the Air APP request.
|
||||
- `air-app-broker` starts only the Air APP lifecycle IPC broker. It bypasses OOBE, Splash, debug preview windows, and normal desktop launch orchestration.
|
||||
- Launcher keeps one Air APP process per `{appId}:{sourceComponentId}:{sourcePlacementId}` key.
|
||||
- AirAppHost receives Launcher pipe and instance key at startup, registers after the window opens, and unregisters on close.
|
||||
- Launcher remains alive while the main desktop process or any Air APP process is alive.
|
||||
- Broker mode remains alive while the requester desktop process or any Air APP process is alive; after both are gone, it exits.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Third-party plugin-declared Air APP metadata.
|
||||
- Cross-machine IPC.
|
||||
- Persisting the Air APP instance table across OS reboot.
|
||||
11
.trae/specs/launcher-managed-air-app-lifecycle/tasks.md
Normal file
11
.trae/specs/launcher-managed-air-app-lifecycle/tasks.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Add shared Air APP lifecycle IPC contracts.
|
||||
- [x] Add Launcher Air APP lifecycle service and dedicated IPC host.
|
||||
- [x] Make Launcher remain alive while desktop or Air APP processes exist.
|
||||
- [x] Route desktop Air APP launch requests through Launcher IPC.
|
||||
- [x] Add hidden `air-app-broker` Launcher command for direct-host development fallback.
|
||||
- [x] Make desktop fallback start `air-app-broker --requester-pid <pid>` instead of normal `launch`.
|
||||
- [x] Add broker lifetime and command recognition tests.
|
||||
- [x] Add AirAppHost registration and unregister best-effort calls.
|
||||
- [x] Add lifecycle service and request-building tests.
|
||||
7
.trae/specs/window-layer-isolation/checklist.md
Normal file
7
.trae/specs/window-layer-isolation/checklist.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Checklist
|
||||
|
||||
- [x] Air APP window code does not call fused desktop bottom-most APIs.
|
||||
- [x] Air APP window code does not set `Topmost = true`.
|
||||
- [x] Fused desktop overlay and widget windows still use bottom-most APIs.
|
||||
- [x] Fused desktop widget reload path refreshes desktop layer after showing.
|
||||
- [ ] Manual Windows z-order verification with fused desktop and Air APP windows.
|
||||
18
.trae/specs/window-layer-isolation/spec.md
Normal file
18
.trae/specs/window-layer-isolation/spec.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Window Layer Isolation
|
||||
|
||||
## Goal
|
||||
|
||||
Keep fused desktop component windows and Air APP windows in separate z-order roles.
|
||||
|
||||
## Behavior
|
||||
|
||||
- Fused desktop windows are desktop-surface windows. They may use `IWindowBottomMostService` and region passthrough, must stay attached to the Windows desktop icon host when supported, and must not cover ordinary apps.
|
||||
- Air APP windows are ordinary application windows. They must not use the fused desktop bottom-most service, must not attach to the desktop icon host, and must not use global `Topmost` promotion.
|
||||
- Re-showing or reloading fused desktop widgets refreshes their desktop layer after the window is visible.
|
||||
- Air APP activation uses normal window activation; repeated-open foreground recovery remains owned by Launcher lifecycle activation.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Changing Air APP lifecycle IPC.
|
||||
- Changing whiteboard note sharing.
|
||||
- Implementing third-party Air APP SDK behavior.
|
||||
7
.trae/specs/window-layer-isolation/tasks.md
Normal file
7
.trae/specs/window-layer-isolation/tasks.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Remove Air APP `Topmost` promotion from `AirAppWindow`.
|
||||
- [x] Add explicit desktop-layer refresh for fused desktop widget windows.
|
||||
- [x] Refresh fused desktop widget windows after show/reload.
|
||||
- [x] Add window-role diagnostics for desktop-surface and Air APP windows.
|
||||
- [x] Add static regression tests for window-layer isolation.
|
||||
Reference in New Issue
Block a user