replace daemon indicator with ptyc/pql tool status
The status bar now shows whether ptyc and pql are findable on PATH (green = found, amber = missing) instead of the obsolete daemon connection state. Per D-056 there is no daemon to connect to. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,33 +1,20 @@
|
||||
import 'package:clide/builtin/ipc_status/src/status_item.dart';
|
||||
import 'package:clide/extension/extension.dart';
|
||||
import 'package:clide/kernel/kernel.dart';
|
||||
|
||||
class IpcStatusExtension extends ClideExtension {
|
||||
@override
|
||||
String get id => 'builtin.ipc-status';
|
||||
@override
|
||||
String get title => 'Daemon connection';
|
||||
String get title => 'Tool status';
|
||||
@override
|
||||
String get version => '0.1.0';
|
||||
|
||||
DaemonClient? _ipc;
|
||||
String get version => '0.2.0';
|
||||
|
||||
@override
|
||||
Future<void> activate(ClideExtensionContext ctx) async {
|
||||
_ipc = ctx.ipc;
|
||||
}
|
||||
|
||||
@override
|
||||
List<ContributionPoint> get contributions {
|
||||
final ipc = _ipc;
|
||||
if (ipc == null) return const [];
|
||||
return [
|
||||
StatusItemContribution(
|
||||
id: 'ipc-status.indicator',
|
||||
priority: 100, // right-side
|
||||
listenable: ipc,
|
||||
build: (_) => IpcStatusItem(ipc: ipc),
|
||||
),
|
||||
];
|
||||
}
|
||||
List<ContributionPoint> get contributions => [
|
||||
StatusItemContribution(
|
||||
id: 'ipc-status.indicator',
|
||||
priority: 100,
|
||||
build: (_) => const ToolStatusItem(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,53 +1,84 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:clide/kernel/kernel.dart';
|
||||
import 'package:clide/widgets/widgets.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class IpcStatusItem extends StatelessWidget {
|
||||
const IpcStatusItem({super.key, required this.ipc});
|
||||
class ToolStatusItem extends StatefulWidget {
|
||||
const ToolStatusItem({super.key});
|
||||
|
||||
final DaemonClient ipc;
|
||||
@override
|
||||
State<ToolStatusItem> createState() => _ToolStatusItemState();
|
||||
}
|
||||
|
||||
static const _ns = 'builtin.ipc-status';
|
||||
class _ToolStatusItemState extends State<ToolStatusItem> {
|
||||
bool _ptycOk = false;
|
||||
bool _pqlOk = false;
|
||||
bool _checked = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_check();
|
||||
}
|
||||
|
||||
Future<void> _check() async {
|
||||
final ptycResult = await _which('ptyc');
|
||||
final pqlResult = await _which('pql');
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_ptycOk = ptycResult;
|
||||
_pqlOk = pqlResult;
|
||||
_checked = true;
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> _which(String name) async {
|
||||
try {
|
||||
final r = await Process.run('which', [name]);
|
||||
return r.exitCode == 0;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final kernel = ClideKernel.of(context);
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
return ListenableBuilder(
|
||||
listenable: Listenable.merge([ipc, kernel.i18n]),
|
||||
builder: (ctx, _) {
|
||||
final connected = ipc.isConnected;
|
||||
final color = connected ? tokens.statusSuccess : tokens.statusError;
|
||||
final i = kernel.i18n;
|
||||
final label = connected
|
||||
? i.string('connected', namespace: _ns, placeholder: 'connected')
|
||||
: i.string('disconnected',
|
||||
namespace: _ns, placeholder: 'disconnected');
|
||||
final hint = connected
|
||||
? i.string('connected.hint',
|
||||
namespace: _ns,
|
||||
placeholder: 'clide daemon is reachable over the local socket')
|
||||
: i.string('disconnected.hint',
|
||||
namespace: _ns,
|
||||
placeholder:
|
||||
'clide daemon is not running — start it with `clide --daemon`');
|
||||
return Semantics(
|
||||
label: label,
|
||||
hint: hint,
|
||||
liveRegion: true,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ClideIcon(const PlugIcon(), size: 12, color: color),
|
||||
const SizedBox(width: 6),
|
||||
ClideText(label, fontSize: clideFontCaption, color: color),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
if (!_checked) return const SizedBox.shrink();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_Indicator(label: 'ptyc', ok: _ptycOk, tokens: tokens),
|
||||
const SizedBox(width: 10),
|
||||
_Indicator(label: 'pql', ok: _pqlOk, tokens: tokens),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Indicator extends StatelessWidget {
|
||||
const _Indicator({required this.label, required this.ok, required this.tokens});
|
||||
final String label;
|
||||
final bool ok;
|
||||
final SurfaceTokens tokens;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = ok ? tokens.statusSuccess : tokens.statusWarning;
|
||||
return Semantics(
|
||||
label: '$label ${ok ? "available" : "not found"}',
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ClideIcon(PhosphorIcons.circlesFour, size: 10, color: color),
|
||||
const SizedBox(width: 4),
|
||||
ClideText(label, fontSize: clideFontCaption, color: color, fontFamily: clideMonoFamily),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:clide/builtin/ipc_status/ipc_status.dart';
|
||||
import 'package:clide/builtin/ipc_status/src/status_item.dart';
|
||||
import 'package:clide/extension/extension.dart';
|
||||
import 'package:clide/kernel/kernel.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../helpers/kernel_fixture.dart';
|
||||
@@ -14,18 +12,7 @@ void main() {
|
||||
late KernelFixture f;
|
||||
|
||||
setUp(() async {
|
||||
f = await KernelFixture.create(
|
||||
i18nCatalogs: {
|
||||
'builtin.ipc-status': {
|
||||
const Locale('en', 'US'): const {
|
||||
'connected': {'translation': 'connected'},
|
||||
'connected.hint': {'translation': 'daemon reachable'},
|
||||
'disconnected': {'translation': 'disconnected'},
|
||||
'disconnected.hint': {'translation': 'daemon down'},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
f = await KernelFixture.create();
|
||||
});
|
||||
|
||||
tearDown(() async => f.dispose());
|
||||
@@ -41,21 +28,10 @@ void main() {
|
||||
expect(items.first.priority, 100);
|
||||
});
|
||||
|
||||
testWidgets('renders "disconnected" label until connected', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
harness(f, IpcStatusItem(ipc: f.services.ipc)),
|
||||
);
|
||||
expect(find.text('disconnected'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('flips to "connected" when the client reports connected',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
harness(f, IpcStatusItem(ipc: f.services.ipc)),
|
||||
);
|
||||
f.ipc.setConnected(true);
|
||||
testWidgets('renders without crashing', (tester) async {
|
||||
await tester.pumpWidget(harness(f, const ToolStatusItem()));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('connected'), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user