feat(learn): the assistant's conversation a dialog on the desktop and in expanded windows (Todo's ADR 0033, D11)
- halkyon_ui's sheetShowsAsDialog says whether showAdaptiveSheet shows a dialog here, for a sheet of its own that can't go through it. - Learn's conversation, a half-height sheet dragged up, asks it: where sheets are dialogs it is a dialog up to 720 wide and as tall as the window, with no handle to drag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
c83f7c46ff
commit
4898d0a04a
1 changed files with 145 additions and 23 deletions
|
|
@ -25,7 +25,9 @@ import 'package:material_ui/material_ui.dart';
|
|||
|
||||
/// Opens a conversation with [choice] in [scope] for [task] in a sheet over
|
||||
/// the screen it is asked on (ADR 0021, D47): at half height, dragged up to
|
||||
/// the full, with follow-up questions at its foot. Asks [question] with
|
||||
/// the full, with follow-up questions at its foot; where halkyon_ui's
|
||||
/// sheets are dialogs (from expanded windows on, and on the desktop), a
|
||||
/// dialog as tall as the window allows. Asks [question] with
|
||||
/// [chosen] at once where given, a new conversation called [title] where
|
||||
/// given, or continues the kept conversation [conversationId]. [choice]
|
||||
/// null shows a kept conversation whose assistant has gone, which can't go
|
||||
|
|
@ -66,15 +68,65 @@ Future<void> showConversation(
|
|||
await cubit.ask(question, chosen);
|
||||
}
|
||||
}());
|
||||
// Closing the sheet closes the cubit, which stops an answer on its way.
|
||||
Widget provided(Widget child) =>
|
||||
BlocProvider(create: (context) => cubit, child: child);
|
||||
// A dialog where halkyon_ui's sheets are (Todo's ADR 0033, D11): from
|
||||
// expanded windows on, and on the desktop at every width.
|
||||
if (sheetShowsAsDialog(context)) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => provided(
|
||||
_ConversationDialog(name: name, scope: scope, choice: choice),
|
||||
),
|
||||
);
|
||||
}
|
||||
return showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (context) => BlocProvider(
|
||||
// Closing the sheet closes the cubit, which stops an answer on its
|
||||
// way.
|
||||
create: (context) => cubit,
|
||||
child: _ConversationSheet(name: name, scope: scope, choice: choice),
|
||||
builder: (context) =>
|
||||
provided(_ConversationSheet(name: name, scope: scope, choice: choice)),
|
||||
);
|
||||
}
|
||||
|
||||
/// The conversation as a dialog, as tall as the window lets it, up to a
|
||||
/// reading width: nothing to drag.
|
||||
class const _ConversationDialog({
|
||||
required final String name,
|
||||
required final AssistantScope scope,
|
||||
required final AssistantChoice? choice,
|
||||
}) extends StatefulWidget {
|
||||
@override
|
||||
State<_ConversationDialog> createState() => _ConversationDialogState();
|
||||
}
|
||||
|
||||
class _ConversationDialogState() extends State<_ConversationDialog> {
|
||||
final _controller = ScrollController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Dialog(
|
||||
insetPadding: context.windowSize.isAtLeastMedium
|
||||
? const EdgeInsets.symmetric(horizontal: 40, vertical: 32)
|
||||
: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 720),
|
||||
child: SizedBox.expand(
|
||||
child: _ConversationView(
|
||||
controller: _controller,
|
||||
name: widget.name,
|
||||
scope: widget.scope,
|
||||
choice: widget.choice,
|
||||
handle: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -99,6 +151,7 @@ class const _ConversationSheet({
|
|||
name: name,
|
||||
scope: scope,
|
||||
choice: choice,
|
||||
handle: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -112,6 +165,9 @@ class const _ConversationView({
|
|||
/// Who answers; null for an assistant no longer offered, which can't go
|
||||
/// on.
|
||||
required final AssistantChoice? choice,
|
||||
|
||||
/// Whether a handle to drag it by sits on top: in a bottom sheet.
|
||||
required final bool handle,
|
||||
}) extends StatelessWidget {
|
||||
/// How close to its end the conversation counts as followed: closer, it
|
||||
/// follows the answer as it grows.
|
||||
|
|
@ -141,7 +197,7 @@ class const _ConversationView({
|
|||
controller: controller,
|
||||
slivers: [
|
||||
PinnedHeaderSliver(
|
||||
child: _Header(name: name, scope: scope),
|
||||
child: _Header(name: name, scope: scope, handle: handle),
|
||||
),
|
||||
if (!state.keeps)
|
||||
SliverToBoxAdapter(
|
||||
|
|
@ -202,11 +258,12 @@ class const _ConversationView({
|
|||
}
|
||||
}
|
||||
|
||||
/// The handle to drag the sheet by, who answers and how far it looks, and
|
||||
/// Close.
|
||||
/// The handle to drag the sheet by (where [handle]), who answers and how
|
||||
/// far it looks, and Close.
|
||||
class const _Header({
|
||||
required final String name,
|
||||
required final AssistantScope scope,
|
||||
required final bool handle,
|
||||
}) extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -217,19 +274,20 @@ class const _Header({
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onSurfaceVariant.withValues(
|
||||
alpha: 0.4,
|
||||
if (handle)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onSurfaceVariant.withValues(
|
||||
alpha: 0.4,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 4, 4, 4),
|
||||
child: Row(
|
||||
|
|
|
|||
Loading…
Reference in a new issue