@tduncan / Cortana / commits / 75729f4

refactor: reorganize sidebar components and update Android default host IP address

abdulmominsakib committed Apr 2, 2026 at 12:44 UTC 75729f42fbac4c916c4be1bfd421ae45c81b7f04
15 files changed +372 -157
.gitignore
+4 -2
@@ -47,5 +47,7 @@ app.*.map.json
47 # FVM Version Cache
48 .fvm/
49 playstore_keys
50 -.agent
51 -.claude
\ No newline at end of file
50 +.agent/
51 +.claude/
52 +.dart_skills/
53 +private/
\ No newline at end of file
lib/app.dart
+4 -4
@@ -5,9 +5,9 @@ import 'package:shadcn_ui/shadcn_ui.dart';
5 import 'package:localmind/core/theme/app_theme.dart';
6 import 'package:localmind/core/providers/app_providers.dart';
7 import 'package:localmind/features/servers/data/models/server.dart';
8 -import 'package:localmind/features/chat/screens/chat_screen.dart';
9 -import 'package:localmind/features/conversations/views/conversation_sidebar.dart';
10 -import 'package:localmind/features/conversations/views/conversation_drawer.dart';
8 +import 'package:localmind/features/chat/views/chat_screen.dart';
9 +import 'package:localmind/features/sidebar/sidebar_widget.dart';
10 +import 'package:localmind/features/sidebar/sidebar_drawer.dart';
11 import 'package:localmind/features/servers/views/server_list_screen.dart';
12 import 'package:localmind/features/servers/views/add_server_screen.dart';
13 import 'package:localmind/features/personas/views/persona_list_screen.dart';
@@ -200,7 +200,7 @@ class AppShell extends StatelessWidget {
200 return Scaffold(
201 body: Row(
202 children: [
203 - const ConversationSidebar(),
203 + const SidebarWidget(),
204 VerticalDivider(
205 width: 1,
206 thickness: 1,
lib/features/chat/data/chat_service.dart
+8 -8
@@ -168,12 +168,12 @@ class LMStudioChatService implements ChatService {
168 cancelToken: _cancelToken,
169 );
170
171 - final stream = response.data!.stream;
171 + final stream = response.data!.stream.cast<List<int>>().transform(utf8.decoder);
172 String buffer = '';
173 String currentEventType = '';
174
175 await for (final chunk in stream) {
176 - buffer += String.fromCharCodes(chunk);
176 + buffer += chunk;
177 final lines = buffer.split('\n');
178 buffer = lines.removeLast();
179
@@ -415,11 +415,11 @@ class OpenAICompatibleChatService implements ChatService {
415 cancelToken: _cancelToken,
416 );
417
418 - final stream = response.data!.stream;
418 + final stream = response.data!.stream.cast<List<int>>().transform(utf8.decoder);
419 String buffer = '';
420
421 await for (final chunk in stream) {
422 - buffer += String.fromCharCodes(chunk);
422 + buffer += chunk;
423 final lines = buffer.split('\n');
424 buffer = lines.removeLast();
425
@@ -490,11 +490,11 @@ class OllamaChatService implements ChatService {
490 cancelToken: _cancelToken,
491 );
492
493 - final stream = response.data!.stream;
493 + final stream = response.data!.stream.cast<List<int>>().transform(utf8.decoder);
494 String buffer = '';
495
496 await for (final chunk in stream) {
497 - buffer += String.fromCharCodes(chunk);
497 + buffer += chunk;
498 final lines = buffer.split('\n');
499 buffer = lines.removeLast();
500
@@ -570,11 +570,11 @@ class OpenRouterChatService implements ChatService {
570 cancelToken: _cancelToken,
571 );
572
573 - final stream = response.data!.stream;
573 + final stream = response.data!.stream.cast<List<int>>().transform(utf8.decoder);
574 String buffer = '';
575
576 await for (final chunk in stream) {
577 - buffer += String.fromCharCodes(chunk);
577 + buffer += chunk;
578 final lines = buffer.split('\n');
579 buffer = lines.removeLast();
580
lib/features/chat/data/models/message.g.dart
+2 -1
@@ -8,7 +8,7 @@ part of 'message.dart';
8
9 class MessageAdapter extends TypeAdapter<Message> {
10 @override
11 - final int typeId = 1;
11 + final typeId = 1;
12
13 @override
14 Message read(BinaryReader reader) {
@@ -31,6 +31,7 @@ class MessageAdapter extends TypeAdapter<Message> {
31 attachmentPaths: (fields[9] as List?)?.cast<String>(),
32 generationTimeMs: (fields[10] as num?)?.toInt(),
33 reasoningContent: fields[11] as String?,
34 + toolCalls: (fields[12] as List?)?.cast<ToolCallData>(),
35 toolCallId: fields[13] as String?,
36 );
37 }
lib/features/chat/views/chat_screen.dart renamed
+22 -9
@@ -272,6 +272,9 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
272 .read(chatProvider.notifier)
273 .deleteMessage(messageId);
274 },
275 + hasSmartReplies:
276 + !chatState.isStreaming &&
277 + ref.read(smartRepliesProvider).isNotEmpty,
278 ),
279 if (_showScrollToBottom && chatState.messages.isNotEmpty)
280 Positioned(
@@ -339,15 +342,20 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
342 ),
343 ),
344 ),
345 + if (!chatState.isStreaming)
346 + Positioned(
347 + bottom: 8,
348 + left: 0,
349 + right: 0,
350 + child: _SmartReplyChips(
351 + onSend: (message) {
352 + ref.read(chatProvider.notifier).sendMessage(message);
353 + },
354 + ),
355 + ),
356 ],
357 ),
358 ),
345 - if (!chatState.isStreaming)
346 - _SmartReplyChips(
347 - onSend: (message) {
348 - ref.read(chatProvider.notifier).sendMessage(message);
349 - },
350 - ),
359 ChatInputBar(
360 isStreaming: chatState.isStreaming,
361 onSend: (message) {
@@ -895,6 +903,7 @@ class _MessageList extends StatelessWidget {
903 required this.isStreaming,
904 required this.onRetry,
905 required this.onDelete,
906 + this.hasSmartReplies = false,
907 });
908
909 final ScrollController scrollController;
@@ -903,6 +912,7 @@ class _MessageList extends StatelessWidget {
912 final bool isStreaming;
913 final void Function(String) onRetry;
914 final void Function(String) onDelete;
915 + final bool hasSmartReplies;
916
917 @override
918 Widget build(BuildContext context) {
@@ -919,7 +929,10 @@ class _MessageList extends StatelessWidget {
929
930 return ListView.builder(
931 controller: scrollController,
922 - padding: EdgeInsets.only(top: 16, bottom: 16),
932 + padding: EdgeInsets.only(
933 + top: 16,
934 + bottom: 16 + (hasSmartReplies ? 56 : 0),
935 + ),
936 itemCount:
937 allMessages.length +
938 (streamingMessage != null && isStreaming ? 1 : 0),
@@ -1044,10 +1057,10 @@ class _SmartReplyChipsState extends ConsumerState<_SmartReplyChips>
1057 return AnimatedSize(
1058 duration: const Duration(milliseconds: 200),
1059 curve: Curves.easeOut,
1047 - child: Container(
1060 + child: SizedBox(
1061 height: 40,
1049 - padding: const EdgeInsets.symmetric(horizontal: 8),
1062 child: ListView.separated(
1063 + padding: EdgeInsets.only(left: 8),
1064 scrollDirection: Axis.horizontal,
1065 itemCount: suggestions.length,
1066 separatorBuilder: (_, _) => const SizedBox(width: 6),
lib/features/conversations/views/components/conversation_list.dart
-1
@@ -82,7 +82,6 @@ class ConversationList extends ConsumerWidget {
82 );
83 }
84
85 -
85 void _showRenameDialog(
86 BuildContext context,
87 WidgetRef ref,
lib/features/onboarding/screens/onboarding_server_setup_screen.dart
+1 -1
@@ -64,7 +64,7 @@ class _OnboardingServerSetupScreenState
64 _hostController = TextEditingController(
65 text: widget.selectedType == ServerType.openRouter
66 ? ''
67 - : (Platform.isAndroid ? '10.0.2.2' : '127.0.0.1'),
67 + : (Platform.isAndroid ? '192.168.0.0' : '127.0.0.1'),
68 );
69 _portController = TextEditingController(text: defaultPort);
70 _apiKeyController = TextEditingController();
lib/features/servers/views/components/server_type_selector.dart
+91 -30
@@ -1,4 +1,5 @@
1 import 'package:flutter/material.dart';
2 +import 'package:hugeicons/hugeicons.dart';
3 import '../../../../core/models/enums.dart';
4
5 class ServerTypeSelector extends StatelessWidget {
@@ -13,36 +14,96 @@ class ServerTypeSelector extends StatelessWidget {
14
15 @override
16 Widget build(BuildContext context) {
16 - return SegmentedButton<ServerType>(
17 - segments: const [
18 - ButtonSegment<ServerType>(
19 - value: ServerType.lmStudio,
20 - label: Text('LM Studio'),
21 - icon: Icon(Icons.terminal, size: 18),
22 - ),
23 - ButtonSegment<ServerType>(
24 - value: ServerType.openAICompatible,
25 - label: Text('OpenAI'),
26 - icon: Icon(Icons.api, size: 18),
27 - ),
28 - ButtonSegment<ServerType>(
29 - value: ServerType.ollama,
30 - label: Text('Ollama'),
31 - icon: Icon(Icons.pets, size: 18),
32 - ),
33 - ButtonSegment<ServerType>(
34 - value: ServerType.openRouter,
35 - label: Text('OpenRouter'),
36 - icon: Icon(Icons.cloud, size: 18),
37 - ),
38 - ],
39 - selected: {selectedType},
40 - onSelectionChanged: (Set<ServerType> selected) {
41 - if (selected.isNotEmpty) {
42 - onChanged(selected.first);
43 - }
44 - },
45 - showSelectedIcon: false,
17 + final theme = Theme.of(context);
18 + final colorScheme = theme.colorScheme;
19 +
20 + final types = [
21 + (
22 + ServerType.lmStudio,
23 + 'LM Studio',
24 + HugeIcons.strokeRoundedAiComputer,
25 + Colors.blue,
26 + ),
27 + (
28 + ServerType.openAICompatible,
29 + 'OpenAI',
30 + HugeIcons.strokeRoundedApi,
31 + Colors.green,
32 + ),
33 + (
34 + ServerType.ollama,
35 + 'Ollama',
36 + HugeIcons.strokeRoundedPencilEdit02,
37 + Colors.orange,
38 + ),
39 + (
40 + ServerType.openRouter,
41 + 'OpenRouter',
42 + HugeIcons.strokeRoundedCloud,
43 + Colors.purple,
44 + ),
45 + ];
46 +
47 + return GridView.count(
48 + shrinkWrap: true,
49 + physics: const NeverScrollableScrollPhysics(),
50 + crossAxisCount: 2,
51 + mainAxisSpacing: 8,
52 + crossAxisSpacing: 8,
53 + childAspectRatio: 2.0,
54 + children: types.map((item) {
55 + final type = item.$1;
56 + final label = item.$2;
57 + final icon = item.$3;
58 + final accentColor = item.$4;
59 + final isSelected = selectedType == type;
60 +
61 + return GestureDetector(
62 + onTap: () => onChanged(type),
63 + child: Container(
64 + decoration: BoxDecoration(
65 + color: isSelected
66 + ? accentColor.withValues(alpha: 0.15)
67 + : colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
68 + borderRadius: BorderRadius.circular(12),
69 + border: Border.all(
70 + color: isSelected
71 + ? accentColor.withValues(alpha: 0.5)
72 + : colorScheme.outline.withValues(alpha: 0.3),
73 + width: isSelected ? 2 : 1,
74 + ),
75 + ),
76 + child: Column(
77 + mainAxisAlignment: MainAxisAlignment.center,
78 + children: [
79 + Container(
80 + width: 32,
81 + height: 32,
82 + decoration: BoxDecoration(
83 + color: isSelected
84 + ? accentColor.withValues(alpha: 0.2)
85 + : colorScheme.surface,
86 + borderRadius: BorderRadius.circular(8),
87 + ),
88 + child: HugeIcon(
89 + icon: icon,
90 + size: 18,
91 + color: isSelected ? accentColor : colorScheme.onSurface,
92 + ),
93 + ),
94 + const SizedBox(height: 6),
95 + Text(
96 + label,
97 + style: theme.textTheme.bodySmall?.copyWith(
98 + fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500,
99 + color: isSelected ? accentColor : colorScheme.onSurface,
100 + ),
101 + ),
102 + ],
103 + ),
104 + ),
105 + );
106 + }).toList(),
107 );
108 }
109 }
lib/features/settings/data/models/app_settings.g.dart
+73 -84
@@ -6,90 +6,6 @@ part of 'app_settings.dart';
6 // TypeAdapterGenerator
7 // **************************************************************************
8
9 -class SyntaxThemeNameAdapter extends TypeAdapter<SyntaxThemeName> {
10 - @override
11 - final typeId = 5;
12 -
13 - @override
14 - SyntaxThemeName read(BinaryReader reader) {
15 - switch (reader.readByte()) {
16 - case 0:
17 - return SyntaxThemeName.vscodeDark;
18 - case 1:
19 - return SyntaxThemeName.vscodeLight;
20 - case 2:
21 - return SyntaxThemeName.dracula;
22 - case 3:
23 - return SyntaxThemeName.monokaiSublime;
24 - case 4:
25 - return SyntaxThemeName.ayuLight;
26 - case 5:
27 - return SyntaxThemeName.ayuDark;
28 - case 6:
29 - return SyntaxThemeName.gravityLight;
30 - case 7:
31 - return SyntaxThemeName.gravityDark;
32 - case 8:
33 - return SyntaxThemeName.obsidian;
34 - case 9:
35 - return SyntaxThemeName.oceanSunset;
36 - case 10:
37 - return SyntaxThemeName.standard;
38 - default:
39 - return SyntaxThemeName.vscodeDark;
40 - }
41 - }
42 -
43 - @override
44 - void write(BinaryWriter writer, SyntaxThemeName obj) {
45 - switch (obj) {
46 - case SyntaxThemeName.vscodeDark:
47 - writer.writeByte(0);
48 - break;
49 - case SyntaxThemeName.vscodeLight:
50 - writer.writeByte(1);
51 - break;
52 - case SyntaxThemeName.dracula:
53 - writer.writeByte(2);
54 - break;
55 - case SyntaxThemeName.monokaiSublime:
56 - writer.writeByte(3);
57 - break;
58 - case SyntaxThemeName.ayuLight:
59 - writer.writeByte(4);
60 - break;
61 - case SyntaxThemeName.ayuDark:
62 - writer.writeByte(5);
63 - break;
64 - case SyntaxThemeName.gravityLight:
65 - writer.writeByte(6);
66 - break;
67 - case SyntaxThemeName.gravityDark:
68 - writer.writeByte(7);
69 - break;
70 - case SyntaxThemeName.obsidian:
71 - writer.writeByte(8);
72 - break;
73 - case SyntaxThemeName.oceanSunset:
74 - writer.writeByte(9);
75 - break;
76 - case SyntaxThemeName.standard:
77 - writer.writeByte(10);
78 - break;
79 - }
80 - }
81 -
82 - @override
83 - int get hashCode => typeId.hashCode;
84 -
85 - @override
86 - bool operator ==(Object other) =>
87 - identical(this, other) ||
88 - other is SyntaxThemeNameAdapter &&
89 - runtimeType == other.runtimeType &&
90 - typeId == other.typeId;
91 -}
92 -
9 class AppSettingsAdapter extends TypeAdapter<AppSettings> {
10 @override
11 final typeId = 4;
@@ -173,3 +89,76 @@ class AppSettingsAdapter extends TypeAdapter<AppSettings> {
89 runtimeType == other.runtimeType &&
90 typeId == other.typeId;
91 }
92 +
93 +class SyntaxThemeNameAdapter extends TypeAdapter<SyntaxThemeName> {
94 + @override
95 + final typeId = 5;
96 +
97 + @override
98 + SyntaxThemeName read(BinaryReader reader) {
99 + switch (reader.readByte()) {
100 + case 0:
101 + return SyntaxThemeName.vscodeDark;
102 + case 1:
103 + return SyntaxThemeName.vscodeLight;
104 + case 2:
105 + return SyntaxThemeName.dracula;
106 + case 3:
107 + return SyntaxThemeName.monokaiSublime;
108 + case 4:
109 + return SyntaxThemeName.ayuLight;
110 + case 5:
111 + return SyntaxThemeName.ayuDark;
112 + case 6:
113 + return SyntaxThemeName.gravityLight;
114 + case 7:
115 + return SyntaxThemeName.gravityDark;
116 + case 8:
117 + return SyntaxThemeName.obsidian;
118 + case 9:
119 + return SyntaxThemeName.oceanSunset;
120 + case 10:
121 + return SyntaxThemeName.standard;
122 + default:
123 + return SyntaxThemeName.vscodeDark;
124 + }
125 + }
126 +
127 + @override
128 + void write(BinaryWriter writer, SyntaxThemeName obj) {
129 + switch (obj) {
130 + case SyntaxThemeName.vscodeDark:
131 + writer.writeByte(0);
132 + case SyntaxThemeName.vscodeLight:
133 + writer.writeByte(1);
134 + case SyntaxThemeName.dracula:
135 + writer.writeByte(2);
136 + case SyntaxThemeName.monokaiSublime:
137 + writer.writeByte(3);
138 + case SyntaxThemeName.ayuLight:
139 + writer.writeByte(4);
140 + case SyntaxThemeName.ayuDark:
141 + writer.writeByte(5);
142 + case SyntaxThemeName.gravityLight:
143 + writer.writeByte(6);
144 + case SyntaxThemeName.gravityDark:
145 + writer.writeByte(7);
146 + case SyntaxThemeName.obsidian:
147 + writer.writeByte(8);
148 + case SyntaxThemeName.oceanSunset:
149 + writer.writeByte(9);
150 + case SyntaxThemeName.standard:
151 + writer.writeByte(10);
152 + }
153 + }
154 +
155 + @override
156 + int get hashCode => typeId.hashCode;
157 +
158 + @override
159 + bool operator ==(Object other) =>
160 + identical(this, other) ||
161 + other is SyntaxThemeNameAdapter &&
162 + runtimeType == other.runtimeType &&
163 + typeId == other.typeId;
164 +}
lib/features/sidebar/components/active_server_indicator.dart new
+151
@@ -0,0 +1,151 @@
1 +import 'package:flutter/material.dart';
2 +import 'package:flutter_riverpod/flutter_riverpod.dart';
3 +import 'package:hugeicons/hugeicons.dart';
4 +import 'package:go_router/go_router.dart';
5 +
6 +import '../../../core/models/enums.dart';
7 +import '../../../core/routes/app_routes.dart';
8 +import '../../servers/providers/server_providers.dart';
9 +import '../../servers/views/components/server_icon_picker.dart';
10 +
11 +class ActiveServerIndicator extends ConsumerWidget {
12 + const ActiveServerIndicator({super.key});
13 +
14 + Color _getStatusColor(ConnectionStatus status) {
15 + switch (status) {
16 + case ConnectionStatus.connected:
17 + return Colors.green;
18 + case ConnectionStatus.error:
19 + return Colors.red;
20 + case ConnectionStatus.checking:
21 + return Colors.orange;
22 + case ConnectionStatus.disconnected:
23 + return Colors.grey;
24 + }
25 + }
26 +
27 + String _getStatusText(ConnectionStatus status) {
28 + switch (status) {
29 + case ConnectionStatus.connected:
30 + return 'Online';
31 + case ConnectionStatus.error:
32 + return 'Error';
33 + case ConnectionStatus.checking:
34 + return 'Checking...';
35 + case ConnectionStatus.disconnected:
36 + return 'Offline';
37 + }
38 + }
39 +
40 + @override
41 + Widget build(BuildContext context, WidgetRef ref) {
42 + final theme = Theme.of(context);
43 + final colorScheme = theme.colorScheme;
44 + final activeServer = ref.watch(activeServerProvider);
45 + final connectionStatus = ref.watch(connectionStatusProvider);
46 +
47 + if (activeServer == null) {
48 + return Padding(
49 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
50 + child: Container(
51 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
52 + decoration: BoxDecoration(
53 + color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
54 + borderRadius: BorderRadius.circular(8),
55 + ),
56 + child: Row(
57 + children: [
58 + HugeIcon(
59 + icon: HugeIcons.strokeRoundedServerStack01,
60 + size: 18,
61 + color: colorScheme.onSurfaceVariant,
62 + ),
63 + const SizedBox(width: 10),
64 + Expanded(
65 + child: Text(
66 + 'No server selected',
67 + style: theme.textTheme.bodySmall?.copyWith(
68 + color: colorScheme.onSurfaceVariant,
69 + ),
70 + overflow: TextOverflow.ellipsis,
71 + ),
72 + ),
73 + ],
74 + ),
75 + ),
76 + );
77 + }
78 +
79 + final statusColor = _getStatusColor(connectionStatus);
80 + final iconName = activeServer.iconName;
81 + final serverIcon = iconName != null
82 + ? (getHugeIconByName(iconName)?.icon ??
83 + getDefaultServerIcon(activeServer.type.name)?.icon)
84 + : getDefaultServerIcon(activeServer.type.name)?.icon;
85 +
86 + return Padding(
87 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
88 + child: GestureDetector(
89 + onTap: () {
90 + context.go(AppRoutes.servers);
91 + if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
92 + Navigator.pop(context);
93 + }
94 + },
95 + child: Container(
96 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
97 + decoration: BoxDecoration(
98 + color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
99 + borderRadius: BorderRadius.circular(8),
100 + border: Border.all(
101 + color: colorScheme.outline.withValues(alpha: 0.2),
102 + ),
103 + ),
104 + child: Row(
105 + children: [
106 + HugeIcon(
107 + icon: serverIcon ?? HugeIcons.strokeRoundedServerStack01,
108 + size: 18,
109 + color: colorScheme.onSurface,
110 + ),
111 + const SizedBox(width: 10),
112 + Expanded(
113 + child: Column(
114 + crossAxisAlignment: CrossAxisAlignment.start,
115 + mainAxisSize: MainAxisSize.min,
116 + children: [
117 + Text(
118 + activeServer.name,
119 + style: theme.textTheme.bodySmall?.copyWith(
120 + fontWeight: FontWeight.w500,
121 + color: colorScheme.onSurface,
122 + ),
123 + overflow: TextOverflow.ellipsis,
124 + maxLines: 1,
125 + ),
126 + Text(
127 + _getStatusText(connectionStatus),
128 + style: theme.textTheme.labelSmall?.copyWith(
129 + color: statusColor,
130 + fontSize: 10,
131 + fontWeight: FontWeight.w500,
132 + ),
133 + ),
134 + ],
135 + ),
136 + ),
137 + Container(
138 + width: 8,
139 + height: 8,
140 + decoration: BoxDecoration(
141 + color: statusColor,
142 + shape: BoxShape.circle,
143 + ),
144 + ),
145 + ],
146 + ),
147 + ),
148 + ),
149 + );
150 + }
151 +}
lib/features/sidebar/components/conversation_drawer_header.dart renamed
lib/features/sidebar/components/drawer_nav_item.dart renamed
lib/features/sidebar/sidebar_drawer.dart renamed
+2 -5
@@ -1,14 +1,11 @@
1 import 'package:flutter/material.dart';
2 -import 'conversation_sidebar.dart';
2 +import 'sidebar_widget.dart';
3
4 class ConversationDrawer extends StatelessWidget {
5 const ConversationDrawer({super.key});
6
7 @override
8 Widget build(BuildContext context) {
9 - return const Drawer(
10 - child: ConversationSidebar(),
11 - );
9 + return const Drawer(child: SidebarWidget());
10 }
11 }
14 -
lib/features/sidebar/sidebar_widget.dart renamed
+10 -8
@@ -3,17 +3,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
3 import 'package:go_router/go_router.dart';
4 import 'package:hugeicons/hugeicons.dart';
5
6 -import '../../../core/routes/app_routes.dart';
7 -import '../providers/conversation_providers.dart';
6 +import '../../core/routes/app_routes.dart';
7 +import '../conversations/providers/conversation_providers.dart';
8 +import 'components/active_server_indicator.dart';
9 import 'components/conversation_drawer_header.dart';
9 -import 'components/conversation_empty_state.dart';
10 -import 'components/conversation_list.dart';
11 -import 'components/conversation_search_bar.dart';
10 +import '../conversations/views/components/conversation_empty_state.dart';
11 +import '../conversations/views/components/conversation_list.dart';
12 +import '../conversations/views/components/conversation_search_bar.dart';
13 import 'components/drawer_nav_item.dart';
13 -import 'components/new_chat_button.dart';
14 +import '../conversations/views/components/new_chat_button.dart';
15
15 -class ConversationSidebar extends ConsumerWidget {
16 - const ConversationSidebar({super.key});
16 +class SidebarWidget extends ConsumerWidget {
17 + const SidebarWidget({super.key});
18
19 @override
20 Widget build(BuildContext context, WidgetRef ref) {
@@ -55,6 +56,7 @@ class ConversationSidebar extends ConsumerWidget {
56 ),
57 ),
58 const Divider(height: 1),
59 + const ActiveServerIndicator(),
60 const SizedBox(height: 8),
61 DrawerNavItem(
62 iconData: HugeIcons.strokeRoundedMessageMultiple01,
lib/hive_registrar.g.dart
+4 -4
@@ -3,30 +3,30 @@
3 // Check in to version control
4
5 import 'package:hive_ce/hive_ce.dart';
6 -import 'package:localmind/features/settings/data/models/app_settings.dart';
7 -import 'package:localmind/features/conversations/data/models/conversation.dart';
6 import 'package:localmind/features/chat/data/models/message.dart';
7 +import 'package:localmind/features/conversations/data/models/conversation.dart';
8 import 'package:localmind/features/personas/data/models/persona.dart';
9 import 'package:localmind/features/servers/data/models/server.dart';
10 +import 'package:localmind/features/settings/data/models/app_settings.dart';
11
12 extension HiveRegistrar on HiveInterface {
13 void registerAdapters() {
14 - registerAdapter(SyntaxThemeNameAdapter());
14 registerAdapter(AppSettingsAdapter());
15 registerAdapter(ConversationAdapter());
16 registerAdapter(MessageAdapter());
17 registerAdapter(PersonaAdapter());
18 registerAdapter(ServerAdapter());
19 + registerAdapter(SyntaxThemeNameAdapter());
20 }
21 }
22
23 extension IsolatedHiveRegistrar on IsolatedHiveInterface {
24 void registerAdapters() {
25 - registerAdapter(SyntaxThemeNameAdapter());
25 registerAdapter(AppSettingsAdapter());
26 registerAdapter(ConversationAdapter());
27 registerAdapter(MessageAdapter());
28 registerAdapter(PersonaAdapter());
29 registerAdapter(ServerAdapter());
30 + registerAdapter(SyntaxThemeNameAdapter());
31 }
32 }