neoagent 2.4.1-beta.30 → 2.4.1-beta.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
- package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +391 -382
- package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +78 -56
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +18 -16
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +18 -17
- package/flutter_app/lib/features/onboarding/onboarding_shell.dart +2 -1
- package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +16 -13
- package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +15 -11
- package/flutter_app/lib/main_account_settings.dart +1 -1
- package/flutter_app/lib/main_admin.dart +1 -1
- package/flutter_app/lib/main_app_shell.dart +190 -191
- package/flutter_app/lib/main_chat.dart +381 -228
- package/flutter_app/lib/main_devices.dart +1 -1
- package/flutter_app/lib/main_launcher.dart +1 -1
- package/flutter_app/lib/main_operations.dart +3 -3
- package/flutter_app/lib/main_shared.dart +336 -340
- package/flutter_app/lib/main_spacing.dart +4 -4
- package/flutter_app/lib/main_theme.dart +20 -14
- package/flutter_app/lib/src/theme/palette.dart +76 -34
- package/flutter_app/pubspec.lock +2 -2
- package/flutter_app/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +60738 -60521
- package/server/services/ai/tools.js +51 -7
- package/server/services/messaging/formatting_guides.js +26 -1
- package/server/services/messaging/manager.js +5 -2
- package/server/services/tasks/runtime.js +182 -7
- package/server/services/tasks/task_repository.js +13 -0
|
@@ -119,6 +119,31 @@ class _AmbientBackdropState extends State<_AmbientBackdrop>
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
class _ControlSurfaceBackdrop extends StatelessWidget {
|
|
123
|
+
const _ControlSurfaceBackdrop({required this.child});
|
|
124
|
+
|
|
125
|
+
final Widget child;
|
|
126
|
+
|
|
127
|
+
@override
|
|
128
|
+
Widget build(BuildContext context) {
|
|
129
|
+
return DecoratedBox(
|
|
130
|
+
decoration: BoxDecoration(
|
|
131
|
+
gradient: LinearGradient(
|
|
132
|
+
colors: <Color>[
|
|
133
|
+
_bgPrimary,
|
|
134
|
+
Color.lerp(_bgPrimary, _accentAlt, 0.025)!,
|
|
135
|
+
Color.lerp(_bgPrimary, _accent, 0.02)!,
|
|
136
|
+
],
|
|
137
|
+
stops: const <double>[0, 0.58, 1],
|
|
138
|
+
begin: const Alignment(-0.95, -1),
|
|
139
|
+
end: const Alignment(0.96, 1),
|
|
140
|
+
),
|
|
141
|
+
),
|
|
142
|
+
child: child,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
122
147
|
class _AuroraFieldPainter extends CustomPainter {
|
|
123
148
|
const _AuroraFieldPainter({
|
|
124
149
|
required this.progress,
|
|
@@ -393,7 +418,6 @@ List<AppSection> _mainSections(NeoAgentController controller) {
|
|
|
393
418
|
return <AppSection>[
|
|
394
419
|
AppSection.chat,
|
|
395
420
|
AppSection.recordings,
|
|
396
|
-
AppSection.runs,
|
|
397
421
|
AppSection.devices,
|
|
398
422
|
AppSection.tasks,
|
|
399
423
|
AppSection.widgets,
|
|
@@ -401,6 +425,7 @@ List<AppSection> _mainSections(NeoAgentController controller) {
|
|
|
401
425
|
AppSection.memory,
|
|
402
426
|
if (controller.showHealthSection) AppSection.health,
|
|
403
427
|
AppSection.settings,
|
|
428
|
+
AppSection.runs,
|
|
404
429
|
AppSection.agents,
|
|
405
430
|
AppSection.messaging,
|
|
406
431
|
];
|
|
@@ -425,11 +450,19 @@ List<Widget> _buildSidebarItems(
|
|
|
425
450
|
continue;
|
|
426
451
|
}
|
|
427
452
|
|
|
453
|
+
final defaultSection = sections.first;
|
|
454
|
+
final parentSection = group == SidebarGroup.settings
|
|
455
|
+
? AppSection.settings
|
|
456
|
+
: defaultSection;
|
|
457
|
+
final childSections = group == SidebarGroup.settings
|
|
458
|
+
? sections.where((section) => section != parentSection).toList()
|
|
459
|
+
: sections;
|
|
460
|
+
final hasChildren =
|
|
461
|
+
childSections.length > 1 ||
|
|
462
|
+
(group == SidebarGroup.settings && childSections.isNotEmpty);
|
|
428
463
|
final active =
|
|
429
464
|
selectedSidebarSection &&
|
|
430
|
-
controller.selectedSection.sidebarSection
|
|
431
|
-
final defaultSection = sections.first;
|
|
432
|
-
final hasChildren = sections.length > 1;
|
|
465
|
+
controller.selectedSection.sidebarSection == parentSection;
|
|
433
466
|
final expanded = expandedGroup == group;
|
|
434
467
|
|
|
435
468
|
widgets.add(
|
|
@@ -441,12 +474,14 @@ List<Widget> _buildSidebarItems(
|
|
|
441
474
|
? Icon(
|
|
442
475
|
expanded ? Icons.expand_less : Icons.expand_more,
|
|
443
476
|
size: 16,
|
|
444
|
-
color: active ?
|
|
477
|
+
color: active ? _textSecondary : _textMuted,
|
|
445
478
|
)
|
|
446
479
|
: null,
|
|
447
480
|
onTap: hasChildren
|
|
448
|
-
?
|
|
449
|
-
|
|
481
|
+
? group == SidebarGroup.settings
|
|
482
|
+
? () => onSelect(parentSection)
|
|
483
|
+
: () => onToggleGroup(group)
|
|
484
|
+
: () => onSelect(parentSection),
|
|
450
485
|
),
|
|
451
486
|
);
|
|
452
487
|
|
|
@@ -454,19 +489,23 @@ List<Widget> _buildSidebarItems(
|
|
|
454
489
|
continue;
|
|
455
490
|
}
|
|
456
491
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
492
|
+
widgets.add(
|
|
493
|
+
_SidebarSubnav(
|
|
494
|
+
children: childSections
|
|
495
|
+
.map(
|
|
496
|
+
(section) => _SidebarButton(
|
|
497
|
+
label: section.label,
|
|
498
|
+
icon: section.icon,
|
|
499
|
+
active: controller.selectedSection.sidebarSection == section,
|
|
500
|
+
iconSize: 16,
|
|
501
|
+
fontSize: 13,
|
|
502
|
+
compact: true,
|
|
503
|
+
onTap: () => onSelect(section),
|
|
504
|
+
),
|
|
505
|
+
)
|
|
506
|
+
.toList(growable: false),
|
|
507
|
+
),
|
|
508
|
+
);
|
|
470
509
|
}
|
|
471
510
|
return widgets;
|
|
472
511
|
}
|
|
@@ -1067,14 +1106,14 @@ class _PulseHaloState extends State<_PulseHalo>
|
|
|
1067
1106
|
}
|
|
1068
1107
|
}
|
|
1069
1108
|
|
|
1070
|
-
class _SidebarButton extends
|
|
1109
|
+
class _SidebarButton extends StatefulWidget {
|
|
1071
1110
|
const _SidebarButton({
|
|
1072
1111
|
required this.label,
|
|
1073
1112
|
required this.icon,
|
|
1074
1113
|
this.active = false,
|
|
1075
|
-
this.indent = 0,
|
|
1076
1114
|
this.iconSize = 18,
|
|
1077
1115
|
this.fontSize = 13,
|
|
1116
|
+
this.compact = false,
|
|
1078
1117
|
this.trailing,
|
|
1079
1118
|
required this.onTap,
|
|
1080
1119
|
});
|
|
@@ -1082,114 +1121,106 @@ class _SidebarButton extends StatelessWidget {
|
|
|
1082
1121
|
final String label;
|
|
1083
1122
|
final IconData icon;
|
|
1084
1123
|
final bool active;
|
|
1085
|
-
final double indent;
|
|
1086
1124
|
final double iconSize;
|
|
1087
1125
|
final double fontSize;
|
|
1126
|
+
final bool compact;
|
|
1088
1127
|
final Widget? trailing;
|
|
1089
1128
|
final VoidCallback? onTap;
|
|
1090
1129
|
|
|
1130
|
+
@override
|
|
1131
|
+
State<_SidebarButton> createState() => _SidebarButtonState();
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
class _SidebarButtonState extends State<_SidebarButton> {
|
|
1135
|
+
bool _hovering = false;
|
|
1136
|
+
|
|
1091
1137
|
@override
|
|
1092
1138
|
Widget build(BuildContext context) {
|
|
1139
|
+
final active = widget.active;
|
|
1140
|
+
final BoxDecoration decoration = active
|
|
1141
|
+
? BoxDecoration(
|
|
1142
|
+
color: _bgCard,
|
|
1143
|
+
borderRadius: BorderRadius.circular(9),
|
|
1144
|
+
border: Border.all(color: _border),
|
|
1145
|
+
boxShadow: <BoxShadow>[
|
|
1146
|
+
BoxShadow(
|
|
1147
|
+
color: Colors.black.withValues(alpha: 0.05),
|
|
1148
|
+
blurRadius: 10,
|
|
1149
|
+
offset: const Offset(0, 2),
|
|
1150
|
+
),
|
|
1151
|
+
],
|
|
1152
|
+
)
|
|
1153
|
+
: BoxDecoration(
|
|
1154
|
+
color: _hovering
|
|
1155
|
+
? _bgTertiary.withValues(alpha: 0.72)
|
|
1156
|
+
: Colors.transparent,
|
|
1157
|
+
borderRadius: BorderRadius.circular(9),
|
|
1158
|
+
);
|
|
1093
1159
|
return Padding(
|
|
1094
|
-
padding: const EdgeInsets.only(bottom:
|
|
1095
|
-
child:
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
:
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
: null,
|
|
1115
|
-
child: Material(
|
|
1116
|
-
color: Colors.transparent,
|
|
1117
|
-
child: InkWell(
|
|
1118
|
-
borderRadius: BorderRadius.circular(18),
|
|
1119
|
-
onTap: onTap,
|
|
1160
|
+
padding: const EdgeInsets.only(bottom: 2),
|
|
1161
|
+
child: MouseRegion(
|
|
1162
|
+
onEnter: (_) => setState(() => _hovering = true),
|
|
1163
|
+
onExit: (_) => setState(() => _hovering = false),
|
|
1164
|
+
child: Material(
|
|
1165
|
+
color: Colors.transparent,
|
|
1166
|
+
child: InkWell(
|
|
1167
|
+
borderRadius: BorderRadius.circular(9),
|
|
1168
|
+
onTap: widget.onTap,
|
|
1169
|
+
child: AnimatedContainer(
|
|
1170
|
+
duration: const Duration(milliseconds: 180),
|
|
1171
|
+
curve: Curves.easeOutCubic,
|
|
1172
|
+
width: double.infinity,
|
|
1173
|
+
padding: EdgeInsets.fromLTRB(
|
|
1174
|
+
12,
|
|
1175
|
+
widget.compact ? 8 : 10,
|
|
1176
|
+
12,
|
|
1177
|
+
widget.compact ? 8 : 10,
|
|
1178
|
+
),
|
|
1179
|
+
decoration: decoration,
|
|
1120
1180
|
child: Stack(
|
|
1181
|
+
alignment: Alignment.centerLeft,
|
|
1121
1182
|
children: <Widget>[
|
|
1122
1183
|
if (active)
|
|
1123
|
-
Positioned
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
stops: const <double>[0, 0.44, 1],
|
|
1135
|
-
begin: Alignment.topLeft,
|
|
1136
|
-
end: Alignment.bottomRight,
|
|
1137
|
-
),
|
|
1184
|
+
Positioned(
|
|
1185
|
+
left: -12,
|
|
1186
|
+
child: Container(
|
|
1187
|
+
width: 3,
|
|
1188
|
+
height: 18,
|
|
1189
|
+
decoration: BoxDecoration(
|
|
1190
|
+
borderRadius: BorderRadius.circular(999),
|
|
1191
|
+
gradient: LinearGradient(
|
|
1192
|
+
colors: <Color>[_accentAlt, _accent],
|
|
1193
|
+
begin: Alignment.topCenter,
|
|
1194
|
+
end: Alignment.bottomCenter,
|
|
1138
1195
|
),
|
|
1139
1196
|
),
|
|
1140
1197
|
),
|
|
1141
1198
|
),
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
active
|
|
1160
|
-
? _accentAlt.withValues(alpha: 0.95)
|
|
1161
|
-
: _textMuted.withValues(alpha: 0.08),
|
|
1162
|
-
],
|
|
1163
|
-
begin: Alignment.topCenter,
|
|
1164
|
-
end: Alignment.bottomCenter,
|
|
1165
|
-
),
|
|
1166
|
-
borderRadius: BorderRadius.circular(999),
|
|
1167
|
-
),
|
|
1168
|
-
),
|
|
1169
|
-
Icon(
|
|
1170
|
-
icon,
|
|
1171
|
-
size: iconSize,
|
|
1172
|
-
color: active ? _accentHover : _textSecondary,
|
|
1173
|
-
),
|
|
1174
|
-
const SizedBox(width: 10),
|
|
1175
|
-
Expanded(
|
|
1176
|
-
child: Text(
|
|
1177
|
-
label,
|
|
1178
|
-
style: TextStyle(
|
|
1179
|
-
fontSize: fontSize,
|
|
1180
|
-
fontWeight: active
|
|
1181
|
-
? FontWeight.w800
|
|
1182
|
-
: FontWeight.w600,
|
|
1183
|
-
color: active ? _textPrimary : _textSecondary,
|
|
1184
|
-
),
|
|
1199
|
+
Row(
|
|
1200
|
+
children: <Widget>[
|
|
1201
|
+
Icon(
|
|
1202
|
+
widget.icon,
|
|
1203
|
+
size: widget.iconSize,
|
|
1204
|
+
color: active ? _accent : _textMuted,
|
|
1205
|
+
),
|
|
1206
|
+
const SizedBox(width: 11),
|
|
1207
|
+
Expanded(
|
|
1208
|
+
child: Text(
|
|
1209
|
+
widget.label,
|
|
1210
|
+
maxLines: 1,
|
|
1211
|
+
overflow: TextOverflow.ellipsis,
|
|
1212
|
+
style: GoogleFonts.geist(
|
|
1213
|
+
fontSize: widget.fontSize,
|
|
1214
|
+
fontWeight: FontWeight.w600,
|
|
1215
|
+
color: active ? _textPrimary : _textSecondary,
|
|
1185
1216
|
),
|
|
1186
1217
|
),
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1218
|
+
),
|
|
1219
|
+
if (widget.trailing != null) ...<Widget>[
|
|
1220
|
+
const SizedBox(width: 8),
|
|
1221
|
+
widget.trailing!,
|
|
1191
1222
|
],
|
|
1192
|
-
|
|
1223
|
+
],
|
|
1193
1224
|
),
|
|
1194
1225
|
],
|
|
1195
1226
|
),
|
|
@@ -1201,38 +1232,20 @@ class _SidebarButton extends StatelessWidget {
|
|
|
1201
1232
|
}
|
|
1202
1233
|
}
|
|
1203
1234
|
|
|
1204
|
-
class
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
final Widget child;
|
|
1208
|
-
final bool active;
|
|
1209
|
-
|
|
1210
|
-
@override
|
|
1211
|
-
State<_HoverLift> createState() => _HoverLiftState();
|
|
1212
|
-
}
|
|
1235
|
+
class _SidebarSubnav extends StatelessWidget {
|
|
1236
|
+
const _SidebarSubnav({required this.children});
|
|
1213
1237
|
|
|
1214
|
-
|
|
1215
|
-
bool _hovering = false;
|
|
1238
|
+
final List<Widget> children;
|
|
1216
1239
|
|
|
1217
1240
|
@override
|
|
1218
1241
|
Widget build(BuildContext context) {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
duration: const Duration(milliseconds: 180),
|
|
1225
|
-
curve: Curves.easeOutCubic,
|
|
1226
|
-
scale: lifted ? 1.012 : 1,
|
|
1227
|
-
child: AnimatedSlide(
|
|
1228
|
-
duration: const Duration(milliseconds: 180),
|
|
1229
|
-
curve: Curves.easeOutCubic,
|
|
1230
|
-
offset: _hovering && !widget.active
|
|
1231
|
-
? const Offset(0.01, 0)
|
|
1232
|
-
: Offset.zero,
|
|
1233
|
-
child: widget.child,
|
|
1234
|
-
),
|
|
1242
|
+
return Container(
|
|
1243
|
+
margin: const EdgeInsets.fromLTRB(14, 2, 0, 2),
|
|
1244
|
+
padding: const EdgeInsets.only(left: 12),
|
|
1245
|
+
decoration: BoxDecoration(
|
|
1246
|
+
border: Border(left: BorderSide(color: _border)),
|
|
1235
1247
|
),
|
|
1248
|
+
child: Column(children: children),
|
|
1236
1249
|
);
|
|
1237
1250
|
}
|
|
1238
1251
|
}
|
|
@@ -1252,21 +1265,21 @@ class _SidebarIconButton extends StatelessWidget {
|
|
|
1252
1265
|
Widget build(BuildContext context) {
|
|
1253
1266
|
return Tooltip(
|
|
1254
1267
|
message: tooltip,
|
|
1255
|
-
child:
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
child: Icon(icon, size: 17, color: _textSecondary),
|
|
1268
|
+
child: Material(
|
|
1269
|
+
color: Colors.transparent,
|
|
1270
|
+
borderRadius: BorderRadius.circular(9),
|
|
1271
|
+
child: InkWell(
|
|
1272
|
+
borderRadius: BorderRadius.circular(9),
|
|
1273
|
+
onTap: onTap,
|
|
1274
|
+
child: Container(
|
|
1275
|
+
width: 32,
|
|
1276
|
+
height: 32,
|
|
1277
|
+
decoration: BoxDecoration(
|
|
1278
|
+
borderRadius: BorderRadius.circular(9),
|
|
1279
|
+
color: _bgSecondary.withValues(alpha: 0.01),
|
|
1280
|
+
border: Border.all(color: Colors.transparent),
|
|
1269
1281
|
),
|
|
1282
|
+
child: Icon(icon, size: 17, color: _textSecondary),
|
|
1270
1283
|
),
|
|
1271
1284
|
),
|
|
1272
1285
|
),
|
|
@@ -1379,7 +1392,7 @@ class _BrandLockup extends StatelessWidget {
|
|
|
1379
1392
|
final titleColor = Theme.of(context).colorScheme.onSurface;
|
|
1380
1393
|
final title = Text(
|
|
1381
1394
|
'NeoAgent',
|
|
1382
|
-
style: GoogleFonts.
|
|
1395
|
+
style: GoogleFonts.geist(
|
|
1383
1396
|
fontSize: titleFontSize,
|
|
1384
1397
|
fontWeight: FontWeight.w700,
|
|
1385
1398
|
color: titleColor,
|
|
@@ -1500,186 +1513,180 @@ class _ChatBubble extends StatelessWidget {
|
|
|
1500
1513
|
const SizedBox(width: 12),
|
|
1501
1514
|
],
|
|
1502
1515
|
Flexible(
|
|
1503
|
-
child:
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
begin: Alignment.topLeft,
|
|
1512
|
-
end: Alignment.bottomRight,
|
|
1513
|
-
)
|
|
1514
|
-
: LinearGradient(
|
|
1515
|
-
colors: <Color>[
|
|
1516
|
-
_bgCard,
|
|
1517
|
-
_bgSecondary.withValues(alpha: 0.94),
|
|
1518
|
-
],
|
|
1519
|
-
begin: Alignment.topLeft,
|
|
1520
|
-
end: Alignment.bottomRight,
|
|
1521
|
-
),
|
|
1522
|
-
borderRadius: BorderRadius.only(
|
|
1523
|
-
topLeft: const Radius.circular(14),
|
|
1524
|
-
topRight: const Radius.circular(14),
|
|
1525
|
-
bottomLeft: Radius.circular(isUser ? 14 : 4),
|
|
1526
|
-
bottomRight: Radius.circular(isUser ? 4 : 14),
|
|
1527
|
-
),
|
|
1528
|
-
border: Border.all(
|
|
1529
|
-
color: isUser
|
|
1530
|
-
? Colors.white.withValues(alpha: 0.18)
|
|
1531
|
-
: _borderLight.withValues(alpha: 0.72),
|
|
1516
|
+
child: ConstrainedBox(
|
|
1517
|
+
constraints: const BoxConstraints(maxWidth: 672),
|
|
1518
|
+
child: AnimatedContainer(
|
|
1519
|
+
duration: const Duration(milliseconds: 220),
|
|
1520
|
+
curve: Curves.easeOutCubic,
|
|
1521
|
+
padding: const EdgeInsets.symmetric(
|
|
1522
|
+
horizontal: 20,
|
|
1523
|
+
vertical: 16,
|
|
1532
1524
|
),
|
|
1533
|
-
|
|
1534
|
-
|
|
1525
|
+
decoration: BoxDecoration(
|
|
1526
|
+
gradient: isUser
|
|
1527
|
+
? LinearGradient(
|
|
1528
|
+
colors: <Color>[
|
|
1529
|
+
_accentAlt,
|
|
1530
|
+
Color.lerp(_accent, _accentAlt, 0.28)!,
|
|
1531
|
+
],
|
|
1532
|
+
begin: Alignment.topLeft,
|
|
1533
|
+
end: Alignment.bottomRight,
|
|
1534
|
+
)
|
|
1535
|
+
: null,
|
|
1536
|
+
color: isUser ? null : _bgCard,
|
|
1537
|
+
borderRadius: BorderRadius.only(
|
|
1538
|
+
topLeft: Radius.circular(isUser ? 21 : 8),
|
|
1539
|
+
topRight: Radius.circular(isUser ? 8 : 21),
|
|
1540
|
+
bottomLeft: const Radius.circular(21),
|
|
1541
|
+
bottomRight: const Radius.circular(21),
|
|
1542
|
+
),
|
|
1543
|
+
border: Border.all(
|
|
1544
|
+
color: isUser ? Colors.transparent : _border,
|
|
1545
|
+
),
|
|
1546
|
+
boxShadow: <BoxShadow>[
|
|
1535
1547
|
BoxShadow(
|
|
1536
|
-
color:
|
|
1537
|
-
blurRadius:
|
|
1548
|
+
color: Colors.black.withValues(alpha: 0.05),
|
|
1549
|
+
blurRadius: 10,
|
|
1538
1550
|
offset: const Offset(0, 2),
|
|
1539
1551
|
),
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
if (!isUser && entry.platformTag != null)
|
|
1554
|
-
Padding(
|
|
1555
|
-
padding: const EdgeInsets.only(bottom: 8),
|
|
1556
|
-
child: _StatusPill(
|
|
1557
|
-
label: entry.platformTag!,
|
|
1558
|
-
color: entry.platform == 'live' ? _info : _warning,
|
|
1559
|
-
),
|
|
1560
|
-
),
|
|
1561
|
-
MarkdownBody(
|
|
1562
|
-
data: entry.content,
|
|
1563
|
-
selectable: false,
|
|
1564
|
-
styleSheet: MarkdownStyleSheet.fromTheme(Theme.of(context))
|
|
1565
|
-
.copyWith(
|
|
1566
|
-
p: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
1567
|
-
color: isUser ? Colors.white : _textPrimary,
|
|
1568
|
-
height: 1.65,
|
|
1569
|
-
),
|
|
1570
|
-
code: Theme.of(context).textTheme.bodyMedium
|
|
1571
|
-
?.copyWith(
|
|
1572
|
-
fontFamily:
|
|
1573
|
-
GoogleFonts.jetBrainsMono().fontFamily,
|
|
1574
|
-
backgroundColor: _bgPrimary,
|
|
1575
|
-
color: isUser ? Colors.white : _textPrimary,
|
|
1576
|
-
),
|
|
1577
|
-
blockquoteDecoration: BoxDecoration(
|
|
1578
|
-
borderRadius: BorderRadius.circular(14),
|
|
1579
|
-
color: const Color(0x22000000),
|
|
1580
|
-
),
|
|
1552
|
+
],
|
|
1553
|
+
),
|
|
1554
|
+
child: Column(
|
|
1555
|
+
crossAxisAlignment: isUser
|
|
1556
|
+
? CrossAxisAlignment.end
|
|
1557
|
+
: CrossAxisAlignment.start,
|
|
1558
|
+
children: <Widget>[
|
|
1559
|
+
if (!isUser && entry.platformTag != null)
|
|
1560
|
+
Padding(
|
|
1561
|
+
padding: const EdgeInsets.only(bottom: 8),
|
|
1562
|
+
child: _StatusPill(
|
|
1563
|
+
label: entry.platformTag!,
|
|
1564
|
+
color: entry.platform == 'live' ? _info : _warning,
|
|
1581
1565
|
),
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
.
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
)
|
|
1602
|
-
? Icons.audiotrack_outlined
|
|
1603
|
-
: Icons.attach_file_rounded;
|
|
1604
|
-
return Container(
|
|
1605
|
-
padding: const EdgeInsets.symmetric(
|
|
1606
|
-
horizontal: 10,
|
|
1607
|
-
vertical: 7,
|
|
1608
|
-
),
|
|
1609
|
-
decoration: BoxDecoration(
|
|
1610
|
-
color: isUser
|
|
1611
|
-
? const Color(0x1FFFFFFF)
|
|
1612
|
-
: _bgSecondary,
|
|
1613
|
-
borderRadius: BorderRadius.circular(999),
|
|
1614
|
-
border: Border.all(
|
|
1615
|
-
color: isUser
|
|
1616
|
-
? const Color(0x40FFFFFF)
|
|
1617
|
-
: _border,
|
|
1566
|
+
),
|
|
1567
|
+
MarkdownBody(
|
|
1568
|
+
data: entry.content,
|
|
1569
|
+
selectable: false,
|
|
1570
|
+
styleSheet:
|
|
1571
|
+
MarkdownStyleSheet.fromTheme(
|
|
1572
|
+
Theme.of(context),
|
|
1573
|
+
).copyWith(
|
|
1574
|
+
p: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
1575
|
+
color: isUser ? Colors.white : _textPrimary,
|
|
1576
|
+
fontSize: 14.5,
|
|
1577
|
+
height: 1.62,
|
|
1578
|
+
),
|
|
1579
|
+
code: Theme.of(context).textTheme.bodyMedium
|
|
1580
|
+
?.copyWith(
|
|
1581
|
+
fontFamily:
|
|
1582
|
+
GoogleFonts.geistMono().fontFamily,
|
|
1583
|
+
backgroundColor: _bgPrimary,
|
|
1584
|
+
color: isUser ? Colors.white : _textPrimary,
|
|
1618
1585
|
),
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
icon,
|
|
1625
|
-
size: 14,
|
|
1626
|
-
color: isUser
|
|
1627
|
-
? Colors.white
|
|
1628
|
-
: _textSecondary,
|
|
1629
|
-
),
|
|
1630
|
-
const SizedBox(width: 6),
|
|
1631
|
-
ConstrainedBox(
|
|
1632
|
-
constraints: const BoxConstraints(
|
|
1633
|
-
maxWidth: 180,
|
|
1634
|
-
),
|
|
1635
|
-
child: Text(
|
|
1636
|
-
attachment.name,
|
|
1637
|
-
maxLines: 1,
|
|
1638
|
-
overflow: TextOverflow.ellipsis,
|
|
1639
|
-
style: TextStyle(
|
|
1640
|
-
color: isUser
|
|
1641
|
-
? Colors.white
|
|
1642
|
-
: _textPrimary,
|
|
1643
|
-
fontSize: 12,
|
|
1644
|
-
),
|
|
1645
|
-
),
|
|
1646
|
-
),
|
|
1647
|
-
],
|
|
1648
|
-
),
|
|
1649
|
-
);
|
|
1650
|
-
})
|
|
1651
|
-
.toList(growable: false),
|
|
1586
|
+
blockquoteDecoration: BoxDecoration(
|
|
1587
|
+
borderRadius: BorderRadius.circular(14),
|
|
1588
|
+
color: const Color(0x22000000),
|
|
1589
|
+
),
|
|
1590
|
+
),
|
|
1652
1591
|
),
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1592
|
+
if (sharedAttachments.isNotEmpty) ...<Widget>[
|
|
1593
|
+
const SizedBox(height: 10),
|
|
1594
|
+
_SharedAttachmentChipWrap(
|
|
1595
|
+
attachments: sharedAttachments,
|
|
1596
|
+
isUser: isUser,
|
|
1597
|
+
),
|
|
1598
|
+
],
|
|
1599
|
+
if (!isUser &&
|
|
1600
|
+
entry.runId?.trim().isNotEmpty == true) ...<Widget>[
|
|
1601
|
+
const SizedBox(height: 12),
|
|
1602
|
+
_MessageRunPreview(
|
|
1603
|
+
runId: entry.runId!.trim(),
|
|
1604
|
+
onLoadRunDetail: onLoadRunDetail,
|
|
1605
|
+
),
|
|
1606
|
+
],
|
|
1607
|
+
const SizedBox(height: 10),
|
|
1608
|
+
Text(
|
|
1609
|
+
entry.createdAtLabel,
|
|
1610
|
+
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
|
1611
|
+
color: isUser
|
|
1612
|
+
? const Color(0xCCFFFFFF)
|
|
1613
|
+
: _textSecondary,
|
|
1614
|
+
),
|
|
1660
1615
|
),
|
|
1661
1616
|
],
|
|
1662
|
-
|
|
1663
|
-
Text(
|
|
1664
|
-
entry.createdAtLabel,
|
|
1665
|
-
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
|
1666
|
-
color: isUser ? const Color(0xCCFFFFFF) : _textSecondary,
|
|
1667
|
-
),
|
|
1668
|
-
),
|
|
1669
|
-
],
|
|
1617
|
+
),
|
|
1670
1618
|
),
|
|
1671
1619
|
),
|
|
1672
1620
|
),
|
|
1673
|
-
if (isUser) ...<Widget>[
|
|
1674
|
-
const SizedBox(width: 12),
|
|
1675
|
-
const _MessageAvatar(assistant: false),
|
|
1676
|
-
],
|
|
1677
1621
|
],
|
|
1678
1622
|
),
|
|
1679
1623
|
);
|
|
1680
1624
|
}
|
|
1681
1625
|
}
|
|
1682
1626
|
|
|
1627
|
+
class _SharedAttachmentChipWrap extends StatelessWidget {
|
|
1628
|
+
const _SharedAttachmentChipWrap({
|
|
1629
|
+
required this.attachments,
|
|
1630
|
+
required this.isUser,
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
final List<SharedChatAttachment> attachments;
|
|
1634
|
+
final bool isUser;
|
|
1635
|
+
|
|
1636
|
+
@override
|
|
1637
|
+
Widget build(BuildContext context) {
|
|
1638
|
+
return Wrap(
|
|
1639
|
+
spacing: 8,
|
|
1640
|
+
runSpacing: 8,
|
|
1641
|
+
children: attachments
|
|
1642
|
+
.map((attachment) {
|
|
1643
|
+
final mimeType = attachment.mimeType.toLowerCase();
|
|
1644
|
+
final icon = mimeType.startsWith('video/')
|
|
1645
|
+
? Icons.videocam_outlined
|
|
1646
|
+
: mimeType.startsWith('image/')
|
|
1647
|
+
? Icons.image_outlined
|
|
1648
|
+
: mimeType.startsWith('audio/')
|
|
1649
|
+
? Icons.audiotrack_outlined
|
|
1650
|
+
: Icons.attach_file_rounded;
|
|
1651
|
+
return Container(
|
|
1652
|
+
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
|
|
1653
|
+
decoration: BoxDecoration(
|
|
1654
|
+
color: isUser ? const Color(0x1FFFFFFF) : _bgSecondary,
|
|
1655
|
+
borderRadius: BorderRadius.circular(999),
|
|
1656
|
+
border: Border.all(
|
|
1657
|
+
color: isUser ? const Color(0x40FFFFFF) : _border,
|
|
1658
|
+
),
|
|
1659
|
+
),
|
|
1660
|
+
child: Row(
|
|
1661
|
+
mainAxisSize: MainAxisSize.min,
|
|
1662
|
+
children: <Widget>[
|
|
1663
|
+
Icon(
|
|
1664
|
+
icon,
|
|
1665
|
+
size: 14,
|
|
1666
|
+
color: isUser ? Colors.white : _textSecondary,
|
|
1667
|
+
),
|
|
1668
|
+
const SizedBox(width: 6),
|
|
1669
|
+
ConstrainedBox(
|
|
1670
|
+
constraints: const BoxConstraints(maxWidth: 180),
|
|
1671
|
+
child: Text(
|
|
1672
|
+
attachment.name,
|
|
1673
|
+
maxLines: 1,
|
|
1674
|
+
overflow: TextOverflow.ellipsis,
|
|
1675
|
+
style: TextStyle(
|
|
1676
|
+
color: isUser ? Colors.white : _textPrimary,
|
|
1677
|
+
fontSize: 12,
|
|
1678
|
+
),
|
|
1679
|
+
),
|
|
1680
|
+
),
|
|
1681
|
+
],
|
|
1682
|
+
),
|
|
1683
|
+
);
|
|
1684
|
+
})
|
|
1685
|
+
.toList(growable: false),
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1683
1690
|
class _MessageRunPreview extends StatefulWidget {
|
|
1684
1691
|
const _MessageRunPreview({
|
|
1685
1692
|
required this.runId,
|
|
@@ -1926,33 +1933,22 @@ class _MessageAvatar extends StatelessWidget {
|
|
|
1926
1933
|
@override
|
|
1927
1934
|
Widget build(BuildContext context) {
|
|
1928
1935
|
return Container(
|
|
1929
|
-
width: 30,
|
|
1930
|
-
height: 30,
|
|
1936
|
+
width: assistant ? 34 : 30,
|
|
1937
|
+
height: assistant ? 34 : 30,
|
|
1931
1938
|
decoration: BoxDecoration(
|
|
1932
|
-
borderRadius: BorderRadius.circular(
|
|
1933
|
-
gradient: assistant
|
|
1934
|
-
? LinearGradient(
|
|
1935
|
-
colors: <Color>[_accentHover, _accent, _accentAlt],
|
|
1936
|
-
begin: Alignment.topLeft,
|
|
1937
|
-
end: Alignment.bottomRight,
|
|
1938
|
-
)
|
|
1939
|
-
: null,
|
|
1939
|
+
borderRadius: BorderRadius.circular(assistant ? 10 : 999),
|
|
1940
1940
|
color: assistant ? null : _bgTertiary,
|
|
1941
|
-
|
|
1942
|
-
? <BoxShadow>[
|
|
1943
|
-
BoxShadow(
|
|
1944
|
-
color: _accentAlt.withValues(alpha: 0.42),
|
|
1945
|
-
blurRadius: 14,
|
|
1946
|
-
offset: const Offset(0, 2),
|
|
1947
|
-
),
|
|
1948
|
-
]
|
|
1949
|
-
: null,
|
|
1950
|
-
),
|
|
1951
|
-
child: Icon(
|
|
1952
|
-
assistant ? Icons.auto_awesome : Icons.person,
|
|
1953
|
-
size: 16,
|
|
1954
|
-
color: assistant ? Colors.white : _textSecondary,
|
|
1941
|
+
border: assistant ? null : Border.all(color: _borderLight),
|
|
1955
1942
|
),
|
|
1943
|
+
clipBehavior: Clip.antiAlias,
|
|
1944
|
+
child: assistant
|
|
1945
|
+
? Image.asset(
|
|
1946
|
+
MediaQuery.platformBrightnessOf(context) == Brightness.dark
|
|
1947
|
+
? 'assets/branding/app_icon_1024.png'
|
|
1948
|
+
: 'assets/branding/app_icon_light_1024.png',
|
|
1949
|
+
filterQuality: FilterQuality.high,
|
|
1950
|
+
)
|
|
1951
|
+
: Icon(Icons.person, size: 16, color: _textSecondary),
|
|
1956
1952
|
);
|
|
1957
1953
|
}
|
|
1958
1954
|
}
|