- 后台托盘(mac/Windows):window_manager 拦截关窗改隐藏,服务持续运行; 托盘单击恢复窗口、右键菜单(显示主界面/退出),退出入口仅放托盘菜单 - 品牌:App 更名"资安信插件助手"(mac PRODUCT_NAME/xib、win main.cpp+Runner.rc(code_page 65001)、 MaterialApp/侧栏/托盘 tooltip/日志);图标全家桶换资安信盾牌 logo (tool/gen_app_icons.dart 生成 mac AppIcon 7 尺寸/win ICO 7 尺寸/托盘白底圆盘版/assets logo) - 修复启动布局断言:SliverList 内 Row(stretch) 需 IntrinsicHeight 包裹, 否则子项收到 h=Infinity 约束(用户实测报错,test/ui_smoke_test.dart 守护复现) - Agent.stop 幂等(托盘退出与窗口销毁竞态安全) - 测试修复:smoke 测试改 tester.runAsync 包真实 socket I/O(fake async zone 里会永久挂起)、 显式 pump 替代 pumpAndSettle;13 个测试全过,analyze 零问题,release 构建实测通过
93 lines
2.7 KiB
Dart
93 lines
2.7 KiB
Dart
import 'dart:async';
|
||
|
||
import 'print/backend.dart';
|
||
import 'print/mock_backend.dart';
|
||
import 'server/http_server.dart';
|
||
import 'server/print_queue.dart';
|
||
|
||
/// 启动参数:--mock 模拟打印;--port=NNN / --port NNN 指定监听端口(默认 38200-38202 顺延)。
|
||
class AgentOptions {
|
||
AgentOptions({this.mock = false, this.port});
|
||
|
||
final bool mock;
|
||
final int? port;
|
||
|
||
factory AgentOptions.parse(List<String> args) {
|
||
var mock = false;
|
||
int? port;
|
||
for (var i = 0; i < args.length; i++) {
|
||
final arg = args[i];
|
||
if (arg == '--mock') {
|
||
mock = true;
|
||
} else if (arg.startsWith('--port=')) {
|
||
port = int.tryParse(arg.substring('--port='.length));
|
||
} else if ((arg == '--port' || arg == '-p') && i + 1 < args.length) {
|
||
port = int.tryParse(args[++i]);
|
||
}
|
||
}
|
||
return AgentOptions(mock: mock, port: port);
|
||
}
|
||
}
|
||
|
||
/// 运行日志环形缓冲(GUI 展示用),同时打到 stdout。
|
||
class AgentLog {
|
||
final lines = <String>[];
|
||
final _controller = StreamController<String>.broadcast();
|
||
|
||
Stream<String> get stream => _controller.stream;
|
||
|
||
void write(String line) {
|
||
final stamped = '${DateTime.now().toIso8601String().substring(11, 19)} $line';
|
||
lines.add(stamped);
|
||
if (lines.length > 500) {
|
||
lines.removeRange(0, lines.length - 500);
|
||
}
|
||
// ignore: avoid_print
|
||
print(stamped);
|
||
_controller.add(stamped);
|
||
}
|
||
|
||
void dispose() => _controller.close();
|
||
}
|
||
|
||
/// 助手核心:组合打印后端、任务队列与 HTTP 服务(Flutter 窗口与 headless 入口共用)。
|
||
class Agent {
|
||
Agent(this.options) : log = AgentLog();
|
||
|
||
final AgentOptions options;
|
||
final AgentLog log;
|
||
|
||
late final PrintQueue queue;
|
||
late final PrintBackend backend;
|
||
late final AgentHttpServer server;
|
||
int? port;
|
||
|
||
Future<int> start() async {
|
||
final backendName = osBackendName();
|
||
backend = options.mock ? MockBackend(onLog: log.write) : createOsBackend(onLog: log.write);
|
||
queue = PrintQueue(backend, onLog: log.write);
|
||
server = AgentHttpServer(
|
||
queue: queue,
|
||
backend: backend,
|
||
mock: options.mock,
|
||
manifestBackendName: backendName,
|
||
);
|
||
final ports = options.port == null ? const [38200, 38201, 38202] : <int>[options.port!];
|
||
port = await server.start(candidatePorts: ports);
|
||
log.write('资安信插件助手已启动 http://127.0.0.1:$port'
|
||
'(后端=$backendName,mock=${options.mock},agent=desktop-plugin)');
|
||
return port!;
|
||
}
|
||
|
||
bool _stopped = false;
|
||
|
||
/// 幂等停止:托盘退出与窗口销毁可能先后各调一次。
|
||
Future<void> stop() async {
|
||
if (_stopped) return;
|
||
_stopped = true;
|
||
await server.stop();
|
||
queue.dispose();
|
||
log.dispose();
|
||
}
|
||
}
|