fix: reveal artifact files without blocking (#24)
Co-authored-by: Haitao Pan <haitao.pan@xworkmate.ai>
This commit is contained in:
parent
1e3d91b038
commit
a9974da2d7
@ -17,6 +17,7 @@ import '../../app/ui_feature_manifest.dart';
|
|||||||
import '../../i18n/app_language.dart';
|
import '../../i18n/app_language.dart';
|
||||||
import '../../models/app_models.dart';
|
import '../../models/app_models.dart';
|
||||||
import '../../runtime/gateway_acp_client.dart';
|
import '../../runtime/gateway_acp_client.dart';
|
||||||
|
import '../../runtime/local_file_revealer.dart';
|
||||||
import '../../runtime/runtime_models.dart';
|
import '../../runtime/runtime_models.dart';
|
||||||
import '../../theme/app_palette.dart';
|
import '../../theme/app_palette.dart';
|
||||||
import '../../theme/app_theme.dart';
|
import '../../theme/app_theme.dart';
|
||||||
@ -404,21 +405,7 @@ extension AssistantPageStateClosureInternal on AssistantPageStateInternal {
|
|||||||
entry.relativePath.contains(':\\')
|
entry.relativePath.contains(':\\')
|
||||||
? entry.relativePath
|
? entry.relativePath
|
||||||
: '${workspacePath.replaceAll(RegExp(r'[\\/]+$'), '')}${Platform.pathSeparator}${entry.relativePath}';
|
: '${workspacePath.replaceAll(RegExp(r'[\\/]+$'), '')}${Platform.pathSeparator}${entry.relativePath}';
|
||||||
if (Platform.isMacOS) {
|
await revealLocalFile(targetPath);
|
||||||
await Process.run('open', <String>['-R', targetPath]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Platform.isLinux) {
|
|
||||||
await Process.run('xdg-open', <String>[
|
|
||||||
File(targetPath).parent.path,
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Platform.isWindows) {
|
|
||||||
await Process.run('explorer.exe', <String>[
|
|
||||||
'/select,$targetPath',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
loadSnapshot: () => controller.loadAssistantArtifactSnapshot(
|
loadSnapshot: () => controller.loadAssistantArtifactSnapshot(
|
||||||
sessionKey: activeSessionKey,
|
sessionKey: activeSessionKey,
|
||||||
|
|||||||
39
lib/runtime/local_file_revealer.dart
Normal file
39
lib/runtime/local_file_revealer.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
typedef DetachedProcessLauncher =
|
||||||
|
Future<void> Function(
|
||||||
|
String executable,
|
||||||
|
List<String> arguments, {
|
||||||
|
required ProcessStartMode mode,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> revealLocalFile(
|
||||||
|
String targetPath, {
|
||||||
|
String? operatingSystem,
|
||||||
|
DetachedProcessLauncher? launchDetached,
|
||||||
|
}) async {
|
||||||
|
final launcher = launchDetached ?? _launchDetached;
|
||||||
|
switch (operatingSystem ?? Platform.operatingSystem) {
|
||||||
|
case 'macos':
|
||||||
|
await launcher('open', <String>[
|
||||||
|
'-R',
|
||||||
|
targetPath,
|
||||||
|
], mode: ProcessStartMode.detached);
|
||||||
|
case 'linux':
|
||||||
|
await launcher('xdg-open', <String>[
|
||||||
|
File(targetPath).parent.path,
|
||||||
|
], mode: ProcessStartMode.detached);
|
||||||
|
case 'windows':
|
||||||
|
await launcher('explorer.exe', <String>[
|
||||||
|
'/select,$targetPath',
|
||||||
|
], mode: ProcessStartMode.detached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _launchDetached(
|
||||||
|
String executable,
|
||||||
|
List<String> arguments, {
|
||||||
|
required ProcessStartMode mode,
|
||||||
|
}) async {
|
||||||
|
await Process.start(executable, arguments, mode: mode);
|
||||||
|
}
|
||||||
64
test/runtime/local_file_revealer_test.dart
Normal file
64
test/runtime/local_file_revealer_test.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:xworkmate/runtime/local_file_revealer.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('revealLocalFile', () {
|
||||||
|
test('reveals the file in Finder on macOS', () async {
|
||||||
|
final invocation = await _captureInvocation(
|
||||||
|
operatingSystem: 'macos',
|
||||||
|
targetPath: '/tmp/thread/report.pdf',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(invocation.executable, 'open');
|
||||||
|
expect(invocation.arguments, <String>['-R', '/tmp/thread/report.pdf']);
|
||||||
|
expect(invocation.mode, ProcessStartMode.detached);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('opens the parent directory on Linux', () async {
|
||||||
|
final invocation = await _captureInvocation(
|
||||||
|
operatingSystem: 'linux',
|
||||||
|
targetPath: '/tmp/thread/reports/report.pdf',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(invocation.executable, 'xdg-open');
|
||||||
|
expect(invocation.arguments, <String>['/tmp/thread/reports']);
|
||||||
|
expect(invocation.mode, ProcessStartMode.detached);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('selects the file in Explorer on Windows', () async {
|
||||||
|
final invocation = await _captureInvocation(
|
||||||
|
operatingSystem: 'windows',
|
||||||
|
targetPath: r'C:\thread\report.pdf',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(invocation.executable, 'explorer.exe');
|
||||||
|
expect(invocation.arguments, <String>[r'/select,C:\thread\report.pdf']);
|
||||||
|
expect(invocation.mode, ProcessStartMode.detached);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<_ProcessInvocation> _captureInvocation({
|
||||||
|
required String operatingSystem,
|
||||||
|
required String targetPath,
|
||||||
|
}) async {
|
||||||
|
late _ProcessInvocation invocation;
|
||||||
|
await revealLocalFile(
|
||||||
|
targetPath,
|
||||||
|
operatingSystem: operatingSystem,
|
||||||
|
launchDetached: (executable, arguments, {required mode}) async {
|
||||||
|
invocation = _ProcessInvocation(executable, arguments, mode);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return invocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProcessInvocation {
|
||||||
|
const _ProcessInvocation(this.executable, this.arguments, this.mode);
|
||||||
|
|
||||||
|
final String executable;
|
||||||
|
final List<String> arguments;
|
||||||
|
final ProcessStartMode mode;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user