feat(mobile): refine composer ui with minimalist modern aesthetic
This commit is contained in:
parent
b18550b6ce
commit
6f9f0c75c9
@ -164,14 +164,18 @@ class MobileAssistantComposer extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8, bottom: 5),
|
||||
child: CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: palette.surfaceSecondary,
|
||||
padding: const EdgeInsets.only(right: 8, bottom: 7),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.light ? const Color(0xFFF4F6F8) : palette.surfaceSecondary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
key: const Key('mobile-assistant-composer-add-button'),
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Icon(Icons.add, color: palette.textPrimary, size: 22),
|
||||
icon: Icon(Icons.add, color: palette.textPrimary, size: 20),
|
||||
onPressed: showConfigurationMenu,
|
||||
),
|
||||
),
|
||||
@ -179,7 +183,7 @@ class MobileAssistantComposer extends StatelessWidget {
|
||||
Expanded(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: palette.surfaceSecondary,
|
||||
color: Theme.of(context).brightness == Brightness.light ? const Color(0xFFF4F6F8) : palette.surfaceSecondary,
|
||||
borderRadius: BorderRadius.circular(26),
|
||||
),
|
||||
child: Row(
|
||||
@ -214,21 +218,32 @@ class MobileAssistantComposer extends StatelessWidget {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12, bottom: 12),
|
||||
child: Icon(Icons.mic_none, color: palette.textMuted, size: 24),
|
||||
child: Icon(Icons.mic_none, color: Theme.of(context).brightness == Brightness.light ? const Color(0xFF9CA3AF) : palette.textMuted, size: 22),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8, bottom: 5),
|
||||
child: CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: palette.accent,
|
||||
padding: const EdgeInsets.only(left: 8, bottom: 7),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.light ? const Color(0xFF6EA8FF) : palette.accent,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: Theme.of(context).brightness == Brightness.light ? [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF6EA8FF).withValues(alpha: 0.15),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
)
|
||||
] : null,
|
||||
),
|
||||
child: IconButton(
|
||||
key: const Key('mobile-assistant-send-button'),
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.arrow_upward_rounded, color: Colors.white, size: 24),
|
||||
icon: const Icon(Icons.arrow_upward_rounded, color: Colors.white, size: 20),
|
||||
onPressed: onSend,
|
||||
),
|
||||
),
|
||||
|
||||
@ -388,6 +388,50 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'mobile target selection keeps a complete remote workspace when local home is unavailable',
|
||||
() async {
|
||||
final controller = AppController(
|
||||
environmentOverride: const <String, String>{},
|
||||
initialGatewayProviderCatalog: <SingleAgentProvider>[
|
||||
SingleAgentProvider.openclaw.copyWith(
|
||||
supportedTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
),
|
||||
],
|
||||
initialAvailableExecutionTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.agent,
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
controller.resolvedUserHomeDirectoryInternal = '';
|
||||
|
||||
await controller.ensureActiveAssistantThreadInternal();
|
||||
await controller.setAssistantExecutionTarget(
|
||||
AssistantExecutionTarget.gateway,
|
||||
);
|
||||
|
||||
final record = controller.taskThreadForSessionInternal(
|
||||
controller.currentSessionKey,
|
||||
);
|
||||
expect(controller.currentAssistantExecutionTarget.isGateway, isTrue);
|
||||
expect(record?.workspaceBinding.isComplete, isTrue);
|
||||
expect(record?.workspaceBinding.workspaceKind, WorkspaceKind.remoteFs);
|
||||
expect(
|
||||
record?.workspaceBinding.workspacePath,
|
||||
contains('/threads/${controller.currentSessionKey}'),
|
||||
);
|
||||
expect(
|
||||
controller.assistantWorkingDirectoryForSessionInternal(
|
||||
controller.currentSessionKey,
|
||||
),
|
||||
record?.workspaceBinding.workspacePath,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('writes inline ACP artifacts into the local thread workspace', () async {
|
||||
final controller = AppController(
|
||||
environmentOverride: const <String, String>{},
|
||||
|
||||
@ -1132,6 +1132,31 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'sendChatMessage runs Gateway task with remote workspace when local workspace is unavailable',
|
||||
() async {
|
||||
final fakeGoTaskService = _RecordingGoTaskServiceClient();
|
||||
final controller = _connectedGatewayController(fakeGoTaskService);
|
||||
addTearDown(controller.dispose);
|
||||
controller.resolvedUserHomeDirectoryInternal = '';
|
||||
|
||||
await controller.ensureActiveAssistantThreadInternal();
|
||||
await controller.setAssistantExecutionTarget(
|
||||
AssistantExecutionTarget.gateway,
|
||||
);
|
||||
await controller.sendChatMessage('mobile gateway task');
|
||||
|
||||
expect(fakeGoTaskService.requests, hasLength(1));
|
||||
final request = fakeGoTaskService.requests.single;
|
||||
expect(request.target, AssistantExecutionTarget.gateway);
|
||||
expect(request.provider.providerId, 'openclaw');
|
||||
expect(request.workingDirectory, startsWith('/owners/'));
|
||||
expect(request.workingDirectory, contains('/threads/'));
|
||||
expect(request.remoteWorkingDirectoryHint, request.workingDirectory);
|
||||
expect(request.prompt, contains(request.workingDirectory));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'sendChatMessage forwards inline attachment content and size',
|
||||
() async {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user