Skip to content

Commit 729b761

Browse files
authored
feat: ask ai (#936)
* feat: ask ai in ssh terminal Fixes #934 * new(ask_ai): settings * fix: app hot reload * new: l10n * chore: deps. * opt.
1 parent 860c11d commit 729b761

40 files changed

+2050
-108
lines changed

.github/workflows/analysis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ jobs:
3232
path: |
3333
${{ env.PUB_CACHE }}
3434
~/.pub-cache
35-
.dart_tool/package_config.json
3635
key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}-${{ hashFiles('**/pubspec.yaml') }}
3736
restore-keys: |
3837
${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}-

lib/app.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,16 @@ import 'package:server_box/view/page/home.dart';
1111

1212
part 'intro.dart';
1313

14-
class MyApp extends StatelessWidget {
14+
class MyApp extends StatefulWidget {
1515
const MyApp({super.key});
1616

17+
@override
18+
State<MyApp> createState() => _MyAppState();
19+
}
20+
21+
class _MyAppState extends State<MyApp> {
22+
late final Future<List<IntroPageBuilder>> _introFuture = _IntroPage.builders;
23+
1724
@override
1825
Widget build(BuildContext context) {
1926
_setup(context);
@@ -91,7 +98,7 @@ class MyApp extends StatelessWidget {
9198
theme: light.fixWindowsFont,
9299
darkTheme: (tMode < 3 ? dark : dark.toAmoled).fixWindowsFont,
93100
home: FutureBuilder<List<IntroPageBuilder>>(
94-
future: _IntroPage.builders,
101+
future: _introFuture,
95102
builder: (context, snapshot) {
96103
context.setLibL10n();
97104
final appL10n = AppLocalizations.of(context);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import 'package:meta/meta.dart';
2+
3+
/// Chat message exchanged with the Ask AI service.
4+
enum AskAiMessageRole { user, assistant }
5+
6+
@immutable
7+
class AskAiMessage {
8+
const AskAiMessage({
9+
required this.role,
10+
required this.content,
11+
});
12+
13+
final AskAiMessageRole role;
14+
final String content;
15+
16+
String get apiRole {
17+
switch (role) {
18+
case AskAiMessageRole.user:
19+
return 'user';
20+
case AskAiMessageRole.assistant:
21+
return 'assistant';
22+
}
23+
}
24+
}
25+
26+
/// Recommended command returned by the AI tool call.
27+
@immutable
28+
class AskAiCommand {
29+
const AskAiCommand({
30+
required this.command,
31+
this.description = '',
32+
this.toolName,
33+
});
34+
35+
final String command;
36+
final String description;
37+
final String? toolName;
38+
}
39+
40+
@immutable
41+
sealed class AskAiEvent {
42+
const AskAiEvent();
43+
}
44+
45+
/// Incremental text delta emitted while streaming the AI response.
46+
class AskAiContentDelta extends AskAiEvent {
47+
const AskAiContentDelta(this.delta);
48+
final String delta;
49+
}
50+
51+
/// Emits when a tool call returns a runnable command suggestion.
52+
class AskAiToolSuggestion extends AskAiEvent {
53+
const AskAiToolSuggestion(this.command);
54+
final AskAiCommand command;
55+
}
56+
57+
/// Signals that the stream finished successfully.
58+
class AskAiCompleted extends AskAiEvent {
59+
const AskAiCompleted({
60+
required this.fullText,
61+
required this.commands,
62+
});
63+
64+
final String fullText;
65+
final List<AskAiCommand> commands;
66+
}
67+
68+
/// Signals that the stream terminated with an error before completion.
69+
class AskAiStreamError extends AskAiEvent {
70+
const AskAiStreamError(this.error, this.stackTrace);
71+
72+
final Object error;
73+
final StackTrace? stackTrace;
74+
}

0 commit comments

Comments
 (0)