feat: M2.1 客户端骨架、BLE 修复、仓库整理与现状板
- BLE: 修复 BlueZ LocalName 与 Includes 冲突,串行注册与退避 - HTTP: 语音对话补 Live2D talking 标志与 session;Web 补 X-Session-Id - Flutter: 角色/对话/模型页 + API,Gradle/依赖升级,麦克风权限 - 文档: 新增 docs/STATUS.md,校准 CLAUDE/PROGRESS/README - 清理: 移除根目录截图与临时模型,运维脚本迁入 scripts/device
This commit is contained in:
92
clients/flutter/lib/models/character_pack.dart
Normal file
92
clients/flutter/lib/models/character_pack.dart
Normal file
@@ -0,0 +1,92 @@
|
||||
class CharacterPack {
|
||||
const CharacterPack({
|
||||
required this.filename,
|
||||
required this.name,
|
||||
required this.characterType,
|
||||
required this.renderType,
|
||||
this.live2dModel,
|
||||
});
|
||||
|
||||
final String filename;
|
||||
final String name;
|
||||
final String characterType;
|
||||
final String renderType;
|
||||
final String? live2dModel;
|
||||
|
||||
bool get isLive2d => renderType.toLowerCase() == 'live2d';
|
||||
|
||||
String get typeLabel {
|
||||
switch (characterType.toLowerCase()) {
|
||||
case 'human':
|
||||
return '数字人';
|
||||
case 'singer':
|
||||
return '歌姬';
|
||||
case 'live2d':
|
||||
return 'Live2D';
|
||||
case 'pet':
|
||||
default:
|
||||
return '宠物';
|
||||
}
|
||||
}
|
||||
|
||||
IconDataLike get iconHint {
|
||||
switch (characterType.toLowerCase()) {
|
||||
case 'human':
|
||||
return IconDataLike.person;
|
||||
case 'singer':
|
||||
return IconDataLike.mic;
|
||||
case 'live2d':
|
||||
return IconDataLike.sparkle;
|
||||
default:
|
||||
return IconDataLike.pets;
|
||||
}
|
||||
}
|
||||
|
||||
factory CharacterPack.fromJson(Map<String, dynamic> json) {
|
||||
final character = json['character'];
|
||||
final Map<String, dynamic> charMap = character is Map
|
||||
? Map<String, dynamic>.from(character)
|
||||
: const <String, dynamic>{};
|
||||
final filename = json['filename']?.toString() ?? '';
|
||||
return CharacterPack(
|
||||
filename: filename,
|
||||
name: charMap['name']?.toString().isNotEmpty == true
|
||||
? charMap['name'].toString()
|
||||
: filename,
|
||||
characterType: charMap['character_type']?.toString() ?? 'pet',
|
||||
renderType: charMap['render_type']?.toString() ?? 'video',
|
||||
live2dModel: charMap['live2d_model']?.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Avoid importing Flutter material in pure model unit tests.
|
||||
enum IconDataLike { pets, person, mic, sparkle }
|
||||
|
||||
class AvailableConfigs {
|
||||
const AvailableConfigs({
|
||||
required this.configs,
|
||||
required this.active,
|
||||
});
|
||||
|
||||
final List<CharacterPack> configs;
|
||||
final String active;
|
||||
|
||||
factory AvailableConfigs.fromJson(Map<String, dynamic> json) {
|
||||
final raw = json['configs'];
|
||||
final list = <CharacterPack>[];
|
||||
if (raw is List) {
|
||||
for (final item in raw) {
|
||||
if (item is Map<String, dynamic>) {
|
||||
list.add(CharacterPack.fromJson(item));
|
||||
} else if (item is Map) {
|
||||
list.add(CharacterPack.fromJson(Map<String, dynamic>.from(item)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return AvailableConfigs(
|
||||
configs: list,
|
||||
active: json['active']?.toString() ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user