surface clide-owned commands in the slash typeahead
The composer sourced its slash suggestions only from the CLI probe (activeClaudeConfig.slashCommands), which never advertises clide-owned commands, so /resume and /fork were missing from the typeahead. Union kClideOwnedCommands onto the command source unconditionally — whether a caller supplies a resolver or the default probe is used — de-duped via a Set so /clear (in both) appears once. T-162. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,9 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- The composer slash typeahead now lists clide-owned commands — `/resume` and
|
||||||
|
`/fork` (and `/clear`) surface even though the CLI probe doesn't advertise
|
||||||
|
them, unioned onto whatever command source the composer uses. (T-162)
|
||||||
- Clicking a decision opens it in the decision reader again — the decisions
|
- Clicking a decision opens it in the decision reader again — the decisions
|
||||||
extension no longer tears down and re-contributes its panel tab on every
|
extension no longer tears down and re-contributes its panel tab on every
|
||||||
selection; it activates a static tab and reveals the panel like the ticket
|
selection; it activates a static tab and reveals the panel like the ticket
|
||||||
|
|||||||
@@ -116,7 +116,14 @@ class _ClaudeComposerState extends State<ClaudeComposer> {
|
|||||||
if (!_focus.hasFocus) _closeTypeahead();
|
if (!_focus.hasFocus) _closeTypeahead();
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<String> _commands() => (widget.slashCommandsResolver ?? () => activeClaudeConfig?.slashCommands ?? const <String>[])();
|
Iterable<String> _commands() {
|
||||||
|
// Always union the clide-owned commands (/clear, /resume, /fork) onto the
|
||||||
|
// command source — whether that's a caller-supplied resolver or the default
|
||||||
|
// ClaudeConfig probe — since the CLI probe never advertises them (T-162).
|
||||||
|
// The Set literal de-dupes (e.g. 'clear' present in both).
|
||||||
|
final base = widget.slashCommandsResolver?.call() ?? activeClaudeConfig?.slashCommands ?? const <String>[];
|
||||||
|
return {...base, ...kClideOwnedCommands};
|
||||||
|
}
|
||||||
|
|
||||||
/// Recompute the active slash query + suggestions from the current text and
|
/// Recompute the active slash query + suggestions from the current text and
|
||||||
/// caret, opening/updating/closing the typeahead overlay accordingly.
|
/// caret, opening/updating/closing the typeahead overlay accordingly.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ library;
|
|||||||
|
|
||||||
import 'package:clide/builtin/claude/src/claude_composer.dart';
|
import 'package:clide/builtin/claude/src/claude_composer.dart';
|
||||||
import 'package:clide/builtin/claude/src/clipboard_paste.dart';
|
import 'package:clide/builtin/claude/src/clipboard_paste.dart';
|
||||||
|
import 'package:clide/builtin/claude/src/slash_commands.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@@ -292,5 +293,45 @@ void main() {
|
|||||||
));
|
));
|
||||||
expect(find.text('Stop ⎋'), findsNothing);
|
expect(find.text('Stop ⎋'), findsNothing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// T-162: clide-owned commands surface in typeahead even when absent from the
|
||||||
|
// CLI probe (slashCommandsResolver).
|
||||||
|
testWidgets('clide-owned /resume surfaces even when absent from the probe list', (tester) async {
|
||||||
|
// The probe list (CLI-sourced) only has 'model' — no 'resume' or 'fork'.
|
||||||
|
await pumpWithCommands(tester, ['model']);
|
||||||
|
await tester.enterText(find.byType(EditableText), '/res');
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// /resume must appear (sourced from kClideOwnedCommands).
|
||||||
|
expect(find.text('/resume'), findsOneWidget);
|
||||||
|
// /model must not appear (doesn't match '/res').
|
||||||
|
expect(find.text('/model'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('clide-owned /clear surfaces without duplicate when also in probe', (tester) async {
|
||||||
|
// 'clear' is in both the probe list AND kClideOwnedCommands.
|
||||||
|
await pumpWithCommands(tester, ['clear', 'model']);
|
||||||
|
await tester.enterText(find.byType(EditableText), '/cl');
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// /clear must appear exactly once (filterSlashCommands de-dupes via seen set).
|
||||||
|
expect(find.text('/clear'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('clide-owned commands are reachable via the default resolver', (tester) async {
|
||||||
|
// No slashCommandsResolver → default path; kClideOwnedCommands must be included.
|
||||||
|
final submitted = <String>[];
|
||||||
|
await tester.pumpWidget(harness(
|
||||||
|
f,
|
||||||
|
ClaudeComposer(onSubmit: submitted.add),
|
||||||
|
));
|
||||||
|
await tester.enterText(find.byType(EditableText), '/fo');
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// /fork is a kClideOwnedCommands member; it must appear without a probe.
|
||||||
|
expect(find.text('/fork'), findsOneWidget);
|
||||||
|
// Sanity: kClideOwnedCommands is the source (not a coincidence).
|
||||||
|
expect(kClideOwnedCommands, contains('fork'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user