neoagent 3.1.1-beta.7 → 3.1.1-beta.9
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/lib/main_timeline.dart +132 -325
- package/package.json +1 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/canvaskit/wimp.js.symbols +6366 -6365
- package/server/public/canvaskit/wimp.wasm +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +56062 -56147
- package/server/services/memory/intelligence.js +58 -21
- package/server/services/memory/manager.js +0 -11
|
@@ -21,37 +21,7 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
void
|
|
25
|
-
if (items.isEmpty) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
final currentIndex = items.indexWhere(
|
|
29
|
-
(item) => item.id == _selectedEventId,
|
|
30
|
-
);
|
|
31
|
-
final baseIndex = currentIndex == -1
|
|
32
|
-
? (offset > 0 ? 0 : items.length - 1)
|
|
33
|
-
: currentIndex;
|
|
34
|
-
final nextIndex = (baseIndex + offset).clamp(0, items.length - 1);
|
|
35
|
-
setState(() {
|
|
36
|
-
_selectedEventId = items[nextIndex].id;
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
TimelineEventItem? _resolveSelectedEvent(List<TimelineEventItem> items) {
|
|
41
|
-
if (items.isEmpty) {
|
|
42
|
-
_selectedEventId = null;
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
for (final item in items) {
|
|
46
|
-
if (item.id == _selectedEventId) {
|
|
47
|
-
return item;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
_selectedEventId = items.first.id;
|
|
51
|
-
return items.first;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
Future<void> _showMobileEventDetails(
|
|
24
|
+
Future<void> _showEventDetailsPopup(
|
|
55
25
|
List<TimelineEventItem> items,
|
|
56
26
|
TimelineEventItem initialEvent,
|
|
57
27
|
) async {
|
|
@@ -61,49 +31,82 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
61
31
|
selectedIndex = 0;
|
|
62
32
|
}
|
|
63
33
|
|
|
64
|
-
|
|
34
|
+
Widget buildDetail(StateSetter setPopupState) {
|
|
35
|
+
final selectedEvent = items[selectedIndex];
|
|
36
|
+
void selectOffset(int offset) {
|
|
37
|
+
final nextIndex = (selectedIndex + offset).clamp(
|
|
38
|
+
0,
|
|
39
|
+
items.length - 1,
|
|
40
|
+
);
|
|
41
|
+
if (nextIndex == selectedIndex) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
setPopupState(() {
|
|
45
|
+
selectedIndex = nextIndex;
|
|
46
|
+
});
|
|
47
|
+
_selectEvent(items[nextIndex]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return _TimelineDetailPane(
|
|
51
|
+
items: items,
|
|
52
|
+
selectedEvent: selectedEvent,
|
|
53
|
+
selectedIndex: selectedIndex,
|
|
54
|
+
onSelectPrevious: selectedIndex > 0 ? () => selectOffset(-1) : null,
|
|
55
|
+
onSelectNext: selectedIndex < items.length - 1
|
|
56
|
+
? () => selectOffset(1)
|
|
57
|
+
: null,
|
|
58
|
+
onOpenRun: selectedEvent.runId.isNotEmpty
|
|
59
|
+
? () => unawaited(
|
|
60
|
+
widget.controller.openRunDetails(selectedEvent.runId),
|
|
61
|
+
)
|
|
62
|
+
: null,
|
|
63
|
+
onClose: () => Navigator.of(context).pop(),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
final compact = MediaQuery.sizeOf(context).width < 760;
|
|
68
|
+
|
|
69
|
+
if (compact) {
|
|
70
|
+
await showModalBottomSheet<void>(
|
|
71
|
+
context: context,
|
|
72
|
+
isScrollControlled: true,
|
|
73
|
+
useSafeArea: true,
|
|
74
|
+
backgroundColor: Colors.transparent,
|
|
75
|
+
builder: (context) {
|
|
76
|
+
return StatefulBuilder(
|
|
77
|
+
builder: (context, setSheetState) {
|
|
78
|
+
return FractionallySizedBox(
|
|
79
|
+
heightFactor: 0.9,
|
|
80
|
+
child: Padding(
|
|
81
|
+
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
|
|
82
|
+
child: buildDetail(setSheetState),
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await showDialog<void>(
|
|
65
93
|
context: context,
|
|
66
|
-
|
|
67
|
-
useSafeArea: true,
|
|
68
|
-
backgroundColor: Colors.transparent,
|
|
94
|
+
barrierColor: Colors.black.withValues(alpha: 0.5),
|
|
69
95
|
builder: (context) {
|
|
70
96
|
return StatefulBuilder(
|
|
71
|
-
builder: (context,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
selectedIndex = nextIndex;
|
|
83
|
-
});
|
|
84
|
-
_selectEvent(items[nextIndex]);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return FractionallySizedBox(
|
|
88
|
-
heightFactor: 0.9,
|
|
89
|
-
child: Padding(
|
|
90
|
-
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
|
|
91
|
-
child: _TimelineDetailPane(
|
|
92
|
-
items: items,
|
|
93
|
-
selectedEvent: selectedEvent,
|
|
94
|
-
selectedIndex: selectedIndex,
|
|
95
|
-
onSelectPrevious: selectedIndex > 0
|
|
96
|
-
? () => selectOffset(-1)
|
|
97
|
-
: null,
|
|
98
|
-
onSelectNext: selectedIndex < items.length - 1
|
|
99
|
-
? () => selectOffset(1)
|
|
100
|
-
: null,
|
|
101
|
-
onOpenRun: selectedEvent.runId.isNotEmpty
|
|
102
|
-
? () => unawaited(
|
|
103
|
-
widget.controller.openRunDetails(selectedEvent.runId),
|
|
104
|
-
)
|
|
105
|
-
: null,
|
|
97
|
+
builder: (context, setDialogState) {
|
|
98
|
+
return Dialog(
|
|
99
|
+
backgroundColor: Colors.transparent,
|
|
100
|
+
insetPadding: const EdgeInsets.symmetric(
|
|
101
|
+
horizontal: 40,
|
|
102
|
+
vertical: 40,
|
|
103
|
+
),
|
|
104
|
+
child: ConstrainedBox(
|
|
105
|
+
constraints: const BoxConstraints(
|
|
106
|
+
maxWidth: 640,
|
|
107
|
+
maxHeight: 780,
|
|
106
108
|
),
|
|
109
|
+
child: buildDetail(setDialogState),
|
|
107
110
|
),
|
|
108
111
|
);
|
|
109
112
|
},
|
|
@@ -116,22 +119,59 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
116
119
|
Widget build(BuildContext context) {
|
|
117
120
|
final items = _sortedTimelineEvents(widget.controller.timelineItems);
|
|
118
121
|
final groups = _groupTimelineEvents(items);
|
|
119
|
-
final
|
|
120
|
-
final selectedIndex = selectedEvent == null
|
|
121
|
-
? -1
|
|
122
|
-
: items.indexWhere((item) => item.id == selectedEvent.id);
|
|
122
|
+
final controller = widget.controller;
|
|
123
123
|
|
|
124
124
|
return Padding(
|
|
125
125
|
padding: _pagePadding(context),
|
|
126
126
|
child: Column(
|
|
127
127
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
128
128
|
children: <Widget>[
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
_PageTitle(
|
|
130
|
+
title: 'Timeline',
|
|
131
|
+
subtitle:
|
|
132
|
+
'Emails, AI actions, recordings, tasks and run activity in one chronological feed.',
|
|
133
|
+
trailing: Wrap(
|
|
134
|
+
spacing: 10,
|
|
135
|
+
runSpacing: 10,
|
|
136
|
+
children: <Widget>[
|
|
137
|
+
for (final filter in const <({String id, String label})>[
|
|
138
|
+
(id: 'screen', label: 'Screen'),
|
|
139
|
+
(id: 'tasks', label: 'Tasks'),
|
|
140
|
+
(id: 'runs', label: 'Runs'),
|
|
141
|
+
])
|
|
142
|
+
FilterChip(
|
|
143
|
+
selected: controller.selectedTimelineSources.contains(
|
|
144
|
+
filter.id,
|
|
145
|
+
),
|
|
146
|
+
label: Text(filter.label),
|
|
147
|
+
onSelected: (_) =>
|
|
148
|
+
controller.toggleTimelineSource(filter.id),
|
|
149
|
+
avatar: Icon(
|
|
150
|
+
_timelineLaneIcon(filter.id),
|
|
151
|
+
size: 16,
|
|
152
|
+
color:
|
|
153
|
+
controller.selectedTimelineSources.contains(
|
|
154
|
+
filter.id,
|
|
155
|
+
)
|
|
156
|
+
? _bgSecondary
|
|
157
|
+
: _sourceColorForKind(filter.id),
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
OutlinedButton.icon(
|
|
161
|
+
onPressed: controller.isRefreshingTimeline
|
|
162
|
+
? null
|
|
163
|
+
: controller.refreshTimeline,
|
|
164
|
+
icon: controller.isRefreshingTimeline
|
|
165
|
+
? const SizedBox.square(
|
|
166
|
+
dimension: 14,
|
|
167
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
168
|
+
)
|
|
169
|
+
: const Icon(Icons.sync_outlined),
|
|
170
|
+
label: const Text('Refresh'),
|
|
171
|
+
),
|
|
172
|
+
],
|
|
173
|
+
),
|
|
133
174
|
),
|
|
134
|
-
const SizedBox(height: 18),
|
|
135
175
|
Expanded(
|
|
136
176
|
child: items.isEmpty
|
|
137
177
|
? Card(
|
|
@@ -145,66 +185,11 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
145
185
|
),
|
|
146
186
|
),
|
|
147
187
|
)
|
|
148
|
-
:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
groups: groups,
|
|
154
|
-
selectedEventId: selectedEvent?.id,
|
|
155
|
-
onSelectEvent: isCompact
|
|
156
|
-
? (item) => unawaited(
|
|
157
|
-
_showMobileEventDetails(items, item),
|
|
158
|
-
)
|
|
159
|
-
: _selectEvent,
|
|
160
|
-
);
|
|
161
|
-
final detailPane = _TimelineDetailPane(
|
|
162
|
-
items: items,
|
|
163
|
-
selectedEvent: selectedEvent,
|
|
164
|
-
selectedIndex: selectedIndex,
|
|
165
|
-
onSelectPrevious: selectedIndex > 0
|
|
166
|
-
? () => _moveSelection(items, -1)
|
|
167
|
-
: null,
|
|
168
|
-
onSelectNext:
|
|
169
|
-
selectedIndex >= 0 &&
|
|
170
|
-
selectedIndex < items.length - 1
|
|
171
|
-
? () => _moveSelection(items, 1)
|
|
172
|
-
: null,
|
|
173
|
-
onOpenRun:
|
|
174
|
-
selectedEvent != null &&
|
|
175
|
-
selectedEvent.runId.isNotEmpty
|
|
176
|
-
? () => unawaited(
|
|
177
|
-
widget.controller.openRunDetails(
|
|
178
|
-
selectedEvent.runId,
|
|
179
|
-
),
|
|
180
|
-
)
|
|
181
|
-
: null,
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
if (isWide) {
|
|
185
|
-
return Row(
|
|
186
|
-
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
187
|
-
children: <Widget>[
|
|
188
|
-
Expanded(flex: 10, child: feedPane),
|
|
189
|
-
const SizedBox(width: 16),
|
|
190
|
-
SizedBox(width: 420, child: detailPane),
|
|
191
|
-
],
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (isCompact) {
|
|
196
|
-
return feedPane;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return Column(
|
|
200
|
-
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
201
|
-
children: <Widget>[
|
|
202
|
-
Expanded(flex: 11, child: feedPane),
|
|
203
|
-
const SizedBox(height: 16),
|
|
204
|
-
Expanded(flex: 8, child: detailPane),
|
|
205
|
-
],
|
|
206
|
-
);
|
|
207
|
-
},
|
|
188
|
+
: _TimelineFeedPane(
|
|
189
|
+
groups: groups,
|
|
190
|
+
selectedEventId: _selectedEventId,
|
|
191
|
+
onSelectEvent: (item) =>
|
|
192
|
+
unawaited(_showEventDetailsPopup(items, item)),
|
|
208
193
|
),
|
|
209
194
|
),
|
|
210
195
|
],
|
|
@@ -213,179 +198,6 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
213
198
|
}
|
|
214
199
|
}
|
|
215
200
|
|
|
216
|
-
class _TimelineHeroHeader extends StatelessWidget {
|
|
217
|
-
const _TimelineHeroHeader({
|
|
218
|
-
required this.items,
|
|
219
|
-
required this.selectedEvent,
|
|
220
|
-
required this.controller,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
final List<TimelineEventItem> items;
|
|
224
|
-
final TimelineEventItem? selectedEvent;
|
|
225
|
-
final NeoAgentController controller;
|
|
226
|
-
|
|
227
|
-
@override
|
|
228
|
-
Widget build(BuildContext context) {
|
|
229
|
-
final compact = MediaQuery.sizeOf(context).width < 760;
|
|
230
|
-
final focusedDay =
|
|
231
|
-
selectedEvent?.occurredAt ??
|
|
232
|
-
(items.isEmpty ? null : items.first.occurredAt);
|
|
233
|
-
|
|
234
|
-
return Container(
|
|
235
|
-
width: double.infinity,
|
|
236
|
-
padding: EdgeInsets.all(compact ? 16 : 22),
|
|
237
|
-
decoration: BoxDecoration(
|
|
238
|
-
gradient: LinearGradient(
|
|
239
|
-
colors: <Color>[
|
|
240
|
-
_bgSecondary.withValues(alpha: 0.96),
|
|
241
|
-
_bgPrimary.withValues(alpha: 0.9),
|
|
242
|
-
],
|
|
243
|
-
begin: Alignment.topLeft,
|
|
244
|
-
end: Alignment.bottomRight,
|
|
245
|
-
),
|
|
246
|
-
borderRadius: BorderRadius.circular(compact ? 20 : 28),
|
|
247
|
-
border: Border.all(color: _borderLight),
|
|
248
|
-
boxShadow: <BoxShadow>[
|
|
249
|
-
BoxShadow(
|
|
250
|
-
color: Colors.black.withValues(alpha: 0.12),
|
|
251
|
-
blurRadius: 26,
|
|
252
|
-
offset: const Offset(0, 16),
|
|
253
|
-
),
|
|
254
|
-
],
|
|
255
|
-
),
|
|
256
|
-
child: Column(
|
|
257
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
258
|
-
children: <Widget>[
|
|
259
|
-
LayoutBuilder(
|
|
260
|
-
builder: (context, constraints) {
|
|
261
|
-
final isWide = constraints.maxWidth >= 940;
|
|
262
|
-
final heading = Column(
|
|
263
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
264
|
-
children: <Widget>[
|
|
265
|
-
Text(
|
|
266
|
-
'ACTIVITY FEED',
|
|
267
|
-
style: TextStyle(
|
|
268
|
-
color: _accentHover,
|
|
269
|
-
fontSize: compact ? 11 : 13,
|
|
270
|
-
fontWeight: FontWeight.w700,
|
|
271
|
-
letterSpacing: compact ? 2.4 : 4.2,
|
|
272
|
-
),
|
|
273
|
-
),
|
|
274
|
-
SizedBox(height: compact ? 8 : 14),
|
|
275
|
-
Text(
|
|
276
|
-
'Timeline',
|
|
277
|
-
style: TextStyle(
|
|
278
|
-
fontSize: compact ? 28 : 40,
|
|
279
|
-
fontWeight: FontWeight.w800,
|
|
280
|
-
height: 1,
|
|
281
|
-
),
|
|
282
|
-
),
|
|
283
|
-
SizedBox(height: compact ? 8 : 14),
|
|
284
|
-
Text(
|
|
285
|
-
'Emails, AI actions, recordings, tasks and run activity in one chronological feed.',
|
|
286
|
-
style: TextStyle(
|
|
287
|
-
color: _textSecondary,
|
|
288
|
-
fontSize: compact ? 13.5 : 16.5,
|
|
289
|
-
height: compact ? 1.28 : 1.35,
|
|
290
|
-
),
|
|
291
|
-
),
|
|
292
|
-
],
|
|
293
|
-
);
|
|
294
|
-
final summary = Wrap(
|
|
295
|
-
spacing: 10,
|
|
296
|
-
runSpacing: 10,
|
|
297
|
-
alignment: isWide ? WrapAlignment.end : WrapAlignment.start,
|
|
298
|
-
children: <Widget>[
|
|
299
|
-
if (items.isNotEmpty)
|
|
300
|
-
_TimelineStatPill(
|
|
301
|
-
icon: Icons.event_note_rounded,
|
|
302
|
-
label: '${items.length} events',
|
|
303
|
-
),
|
|
304
|
-
if (items.length > 1)
|
|
305
|
-
_TimelineStatPill(
|
|
306
|
-
icon: Icons.schedule_outlined,
|
|
307
|
-
label: _formatTimelineRange(items.first, items.last),
|
|
308
|
-
),
|
|
309
|
-
if (focusedDay != null)
|
|
310
|
-
_TimelineStatPill(
|
|
311
|
-
icon: Icons.calendar_today_outlined,
|
|
312
|
-
label: _formatTimelineDate(focusedDay.toLocal()),
|
|
313
|
-
),
|
|
314
|
-
],
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
if (isWide) {
|
|
318
|
-
return Row(
|
|
319
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
320
|
-
children: <Widget>[
|
|
321
|
-
Expanded(child: heading),
|
|
322
|
-
const SizedBox(width: 20),
|
|
323
|
-
ConstrainedBox(
|
|
324
|
-
constraints: const BoxConstraints(maxWidth: 440),
|
|
325
|
-
child: Align(
|
|
326
|
-
alignment: Alignment.topRight,
|
|
327
|
-
child: summary,
|
|
328
|
-
),
|
|
329
|
-
),
|
|
330
|
-
],
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return Column(
|
|
335
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
336
|
-
children: <Widget>[
|
|
337
|
-
heading,
|
|
338
|
-
const SizedBox(height: 18),
|
|
339
|
-
summary,
|
|
340
|
-
],
|
|
341
|
-
);
|
|
342
|
-
},
|
|
343
|
-
),
|
|
344
|
-
const SizedBox(height: 18),
|
|
345
|
-
Wrap(
|
|
346
|
-
spacing: 10,
|
|
347
|
-
runSpacing: 10,
|
|
348
|
-
children: <Widget>[
|
|
349
|
-
for (final filter in const <({String id, String label})>[
|
|
350
|
-
(id: 'screen', label: 'Screen'),
|
|
351
|
-
(id: 'tasks', label: 'Tasks'),
|
|
352
|
-
(id: 'runs', label: 'Runs'),
|
|
353
|
-
])
|
|
354
|
-
FilterChip(
|
|
355
|
-
selected: controller.selectedTimelineSources.contains(
|
|
356
|
-
filter.id,
|
|
357
|
-
),
|
|
358
|
-
label: Text(filter.label),
|
|
359
|
-
onSelected: (_) => controller.toggleTimelineSource(filter.id),
|
|
360
|
-
avatar: Icon(
|
|
361
|
-
_timelineLaneIcon(filter.id),
|
|
362
|
-
size: 16,
|
|
363
|
-
color:
|
|
364
|
-
controller.selectedTimelineSources.contains(filter.id)
|
|
365
|
-
? _bgSecondary
|
|
366
|
-
: _sourceColorForKind(filter.id),
|
|
367
|
-
),
|
|
368
|
-
),
|
|
369
|
-
OutlinedButton.icon(
|
|
370
|
-
onPressed: controller.isRefreshingTimeline
|
|
371
|
-
? null
|
|
372
|
-
: controller.refreshTimeline,
|
|
373
|
-
icon: controller.isRefreshingTimeline
|
|
374
|
-
? const SizedBox.square(
|
|
375
|
-
dimension: 14,
|
|
376
|
-
child: CircularProgressIndicator(strokeWidth: 2),
|
|
377
|
-
)
|
|
378
|
-
: const Icon(Icons.sync_outlined),
|
|
379
|
-
label: const Text('Refresh'),
|
|
380
|
-
),
|
|
381
|
-
],
|
|
382
|
-
),
|
|
383
|
-
],
|
|
384
|
-
),
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
201
|
class _TimelineFeedPane extends StatelessWidget {
|
|
390
202
|
const _TimelineFeedPane({
|
|
391
203
|
required this.groups,
|
|
@@ -751,6 +563,7 @@ class _TimelineDetailPane extends StatelessWidget {
|
|
|
751
563
|
this.onSelectPrevious,
|
|
752
564
|
this.onSelectNext,
|
|
753
565
|
this.onOpenRun,
|
|
566
|
+
this.onClose,
|
|
754
567
|
});
|
|
755
568
|
|
|
756
569
|
final List<TimelineEventItem> items;
|
|
@@ -759,6 +572,7 @@ class _TimelineDetailPane extends StatelessWidget {
|
|
|
759
572
|
final VoidCallback? onSelectPrevious;
|
|
760
573
|
final VoidCallback? onSelectNext;
|
|
761
574
|
final VoidCallback? onOpenRun;
|
|
575
|
+
final VoidCallback? onClose;
|
|
762
576
|
|
|
763
577
|
@override
|
|
764
578
|
Widget build(BuildContext context) {
|
|
@@ -814,6 +628,12 @@ class _TimelineDetailPane extends StatelessWidget {
|
|
|
814
628
|
onPressed: onSelectNext,
|
|
815
629
|
icon: const Icon(Icons.chevron_right_rounded),
|
|
816
630
|
),
|
|
631
|
+
if (onClose != null)
|
|
632
|
+
IconButton(
|
|
633
|
+
tooltip: 'Close',
|
|
634
|
+
onPressed: onClose,
|
|
635
|
+
icon: const Icon(Icons.close_rounded),
|
|
636
|
+
),
|
|
817
637
|
],
|
|
818
638
|
),
|
|
819
639
|
),
|
|
@@ -1310,19 +1130,6 @@ bool _isSameDay(DateTime a, DateTime b) {
|
|
|
1310
1130
|
return a.year == b.year && a.month == b.month && a.day == b.day;
|
|
1311
1131
|
}
|
|
1312
1132
|
|
|
1313
|
-
String _formatTimelineRange(TimelineEventItem first, TimelineEventItem last) {
|
|
1314
|
-
final start = last.occurredAt.toLocal();
|
|
1315
|
-
final end = first.occurredAt.toLocal();
|
|
1316
|
-
final startDate = _formatTimelineDate(start);
|
|
1317
|
-
final endDate = _formatTimelineDate(end);
|
|
1318
|
-
final startTime = _formatTimelineTime(start);
|
|
1319
|
-
final endTime = _formatTimelineTime(end);
|
|
1320
|
-
if (_isSameDay(start, end)) {
|
|
1321
|
-
return '$startDate · $startTime - $endTime';
|
|
1322
|
-
}
|
|
1323
|
-
return '$startDate -> $endDate';
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
1133
|
String _formatTimelineDate(DateTime value) {
|
|
1327
1134
|
return '${_monthShort(value.month)} ${value.day}, ${value.year}';
|
|
1328
1135
|
}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
42ed3501c09c6e3ffc03212056c51751
|
|
Binary file
|