@google/gemini-cli 0.11.0-preview.0 → 0.12.0-nightly.20251023.c4c0c0d1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/google-gemini-cli-0.12.0-nightly.20251022.0542de95.tgz +0 -0
- package/dist/package.json +4 -3
- package/dist/src/commands/extensions/disable.js +13 -6
- package/dist/src/commands/extensions/disable.js.map +1 -1
- package/dist/src/commands/extensions/enable.js +13 -6
- package/dist/src/commands/extensions/enable.js.map +1 -1
- package/dist/src/commands/extensions/install.js +12 -2
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +11 -3
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/link.js +12 -2
- package/dist/src/commands/extensions/link.js.map +1 -1
- package/dist/src/commands/extensions/list.js +13 -4
- package/dist/src/commands/extensions/list.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.js +12 -2
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/update.js +17 -13
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/extensions.js +1 -0
- package/dist/src/commands/extensions.js.map +1 -1
- package/dist/src/commands/mcp/list.js +10 -3
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/commands/mcp/list.test.js +12 -6
- package/dist/src/commands/mcp/list.test.js.map +1 -1
- package/dist/src/config/config.js +12 -0
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +11 -0
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager.d.ts +38 -0
- package/dist/src/config/extension-manager.js +412 -0
- package/dist/src/config/extension-manager.js.map +1 -0
- package/dist/src/config/extension.d.ts +4 -51
- package/dist/src/config/extension.js +1 -535
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/extension.test.js +316 -159
- package/dist/src/config/extension.test.js.map +1 -1
- package/dist/src/config/extensions/consent.d.ts +38 -0
- package/dist/src/config/extensions/consent.js +123 -0
- package/dist/src/config/extensions/consent.js.map +1 -0
- package/dist/src/config/extensions/extensionEnablement.js +1 -1
- package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
- package/dist/src/config/extensions/extensionSettings.d.ts +15 -0
- package/dist/src/config/extensions/extensionSettings.js +63 -0
- package/dist/src/config/extensions/extensionSettings.js.map +1 -0
- package/dist/src/config/extensions/extensionSettings.test.d.ts +6 -0
- package/dist/src/config/extensions/extensionSettings.test.js +137 -0
- package/dist/src/config/extensions/extensionSettings.test.js.map +1 -0
- package/dist/src/config/extensions/github.d.ts +2 -2
- package/dist/src/config/extensions/github.js +3 -8
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +25 -7
- package/dist/src/config/extensions/github.test.js.map +1 -1
- package/dist/src/config/extensions/storage.d.ts +14 -0
- package/dist/src/config/extensions/storage.js +32 -0
- package/dist/src/config/extensions/storage.js.map +1 -0
- package/dist/src/config/extensions/update.d.ts +4 -4
- package/dist/src/config/extensions/update.js +11 -18
- package/dist/src/config/extensions/update.js.map +1 -1
- package/dist/src/config/extensions/update.test.js +32 -58
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/extensions/variableSchema.d.ts +0 -6
- package/dist/src/config/extensions/variableSchema.js.map +1 -1
- package/dist/src/config/extensions/variables.d.ts +4 -0
- package/dist/src/config/extensions/variables.js +6 -0
- package/dist/src/config/extensions/variables.js.map +1 -1
- package/dist/src/config/settings.d.ts +2 -1
- package/dist/src/config/settings.js +4 -7
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settings.test.js +113 -14
- package/dist/src/config/settings.test.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +9 -0
- package/dist/src/config/settingsSchema.js +9 -0
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/gemini.js +22 -5
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/nonInteractiveCli.js +14 -1
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +84 -2
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/test-utils/createExtension.d.ts +3 -1
- package/dist/src/test-utils/createExtension.js +3 -3
- package/dist/src/test-utils/createExtension.js.map +1 -1
- package/dist/src/ui/AppContainer.js +101 -47
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +138 -79
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +19 -10
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.test.js +8 -0
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +5 -6
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.js +249 -393
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +0 -29
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.d.ts +7 -1
- package/dist/src/ui/components/views/ExtensionsList.js +4 -10
- package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js +34 -21
- package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.js +328 -335
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js +10 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +30 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.d.ts +14 -4
- package/dist/src/ui/hooks/useExtensionUpdates.js +14 -17
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +23 -30
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.js +4 -0
- package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.test.js +19 -21
- package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +22 -8
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +6 -0
- package/dist/src/ui/hooks/useReactToolScheduler.test.js +65 -0
- package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js +30 -48
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/types.d.ts +2 -1
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/CodeColorizer.js +2 -1
- package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
- package/dist/src/utils/envVarResolver.d.ts +2 -2
- package/dist/src/utils/envVarResolver.js +10 -7
- package/dist/src/utils/envVarResolver.js.map +1 -1
- package/dist/src/zed-integration/schema.d.ts +4 -4
- package/dist/src/zed-integration/zedIntegration.js +3 -3
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
|
@@ -608,210 +608,95 @@ describe('InputPrompt', () => {
|
|
|
608
608
|
unmount();
|
|
609
609
|
});
|
|
610
610
|
describe('cursor-based completion trigger', () => {
|
|
611
|
-
it(
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
617
|
-
...mockCommandCompletion,
|
|
611
|
+
it.each([
|
|
612
|
+
{
|
|
613
|
+
name: 'should trigger completion when cursor is after @ without spaces',
|
|
614
|
+
text: '@src/components',
|
|
615
|
+
cursor: [0, 15],
|
|
618
616
|
showSuggestions: true,
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(mockBuffer, ['/test/project/src'], path.join('test', 'project', 'src'), mockSlashCommands, mockCommandContext, false, false, expect.any(Object));
|
|
625
|
-
unmount();
|
|
626
|
-
});
|
|
627
|
-
it('should trigger completion when cursor is after / without spaces', async () => {
|
|
628
|
-
mockBuffer.text = '/memory';
|
|
629
|
-
mockBuffer.lines = ['/memory'];
|
|
630
|
-
mockBuffer.cursor = [0, 7];
|
|
631
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
632
|
-
...mockCommandCompletion,
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
name: 'should trigger completion when cursor is after / without spaces',
|
|
620
|
+
text: '/memory',
|
|
621
|
+
cursor: [0, 7],
|
|
633
622
|
showSuggestions: true,
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
unmount();
|
|
640
|
-
});
|
|
641
|
-
it('should NOT trigger completion when cursor is after space following @', async () => {
|
|
642
|
-
mockBuffer.text = '@src/file.ts hello';
|
|
643
|
-
mockBuffer.lines = ['@src/file.ts hello'];
|
|
644
|
-
mockBuffer.cursor = [0, 18];
|
|
645
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
646
|
-
...mockCommandCompletion,
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: 'should NOT trigger completion when cursor is after space following @',
|
|
626
|
+
text: '@src/file.ts hello',
|
|
627
|
+
cursor: [0, 18],
|
|
647
628
|
showSuggestions: false,
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
unmount();
|
|
654
|
-
});
|
|
655
|
-
it('should NOT trigger completion when cursor is after space following /', async () => {
|
|
656
|
-
mockBuffer.text = '/memory add';
|
|
657
|
-
mockBuffer.lines = ['/memory add'];
|
|
658
|
-
mockBuffer.cursor = [0, 11];
|
|
659
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
660
|
-
...mockCommandCompletion,
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
name: 'should NOT trigger completion when cursor is after space following /',
|
|
632
|
+
text: '/memory add',
|
|
633
|
+
cursor: [0, 11],
|
|
661
634
|
showSuggestions: false,
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
unmount();
|
|
668
|
-
});
|
|
669
|
-
it('should NOT trigger completion when cursor is not after @ or /', async () => {
|
|
670
|
-
mockBuffer.text = 'hello world';
|
|
671
|
-
mockBuffer.lines = ['hello world'];
|
|
672
|
-
mockBuffer.cursor = [0, 5];
|
|
673
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
674
|
-
...mockCommandCompletion,
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
name: 'should NOT trigger completion when cursor is not after @ or /',
|
|
638
|
+
text: 'hello world',
|
|
639
|
+
cursor: [0, 5],
|
|
675
640
|
showSuggestions: false,
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
unmount();
|
|
682
|
-
});
|
|
683
|
-
it('should handle multiline text correctly', async () => {
|
|
684
|
-
mockBuffer.text = 'first line\n/memory';
|
|
685
|
-
mockBuffer.lines = ['first line', '/memory'];
|
|
686
|
-
mockBuffer.cursor = [1, 7];
|
|
687
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
688
|
-
...mockCommandCompletion,
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
name: 'should handle multiline text correctly',
|
|
644
|
+
text: 'first line\n/memory',
|
|
645
|
+
cursor: [1, 7],
|
|
689
646
|
showSuggestions: false,
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(mockBuffer, ['/test/project/src'], path.join('test', 'project', 'src'), mockSlashCommands, mockCommandContext, false, false, expect.any(Object));
|
|
696
|
-
unmount();
|
|
697
|
-
});
|
|
698
|
-
it('should handle single line slash command correctly', async () => {
|
|
699
|
-
mockBuffer.text = '/memory';
|
|
700
|
-
mockBuffer.lines = ['/memory'];
|
|
701
|
-
mockBuffer.cursor = [0, 7];
|
|
702
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
703
|
-
...mockCommandCompletion,
|
|
704
|
-
showSuggestions: true,
|
|
705
|
-
suggestions: [{ label: 'show', value: 'show' }],
|
|
706
|
-
});
|
|
707
|
-
const { unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
708
|
-
await wait();
|
|
709
|
-
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(mockBuffer, ['/test/project/src'], path.join('test', 'project', 'src'), mockSlashCommands, mockCommandContext, false, false, expect.any(Object));
|
|
710
|
-
unmount();
|
|
711
|
-
});
|
|
712
|
-
it('should handle Unicode characters (emojis) correctly in paths', async () => {
|
|
713
|
-
// Test with emoji in path after @
|
|
714
|
-
mockBuffer.text = '@src/file👍.txt';
|
|
715
|
-
mockBuffer.lines = ['@src/file👍.txt'];
|
|
716
|
-
mockBuffer.cursor = [0, 14]; // After the emoji character
|
|
717
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
718
|
-
...mockCommandCompletion,
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
name: 'should handle Unicode characters (emojis) correctly in paths',
|
|
650
|
+
text: '@src/file👍.txt',
|
|
651
|
+
cursor: [0, 14],
|
|
719
652
|
showSuggestions: true,
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
unmount();
|
|
726
|
-
});
|
|
727
|
-
it('should handle Unicode characters with spaces after them', async () => {
|
|
728
|
-
// Test with emoji followed by space - should NOT trigger completion
|
|
729
|
-
mockBuffer.text = '@src/file👍.txt hello';
|
|
730
|
-
mockBuffer.lines = ['@src/file👍.txt hello'];
|
|
731
|
-
mockBuffer.cursor = [0, 20]; // After the space
|
|
732
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
733
|
-
...mockCommandCompletion,
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
name: 'should handle Unicode characters with spaces after them',
|
|
656
|
+
text: '@src/file👍.txt hello',
|
|
657
|
+
cursor: [0, 20],
|
|
734
658
|
showSuggestions: false,
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
unmount();
|
|
741
|
-
});
|
|
742
|
-
it('should handle escaped spaces in paths correctly', async () => {
|
|
743
|
-
// Test with escaped space in path - should trigger completion
|
|
744
|
-
mockBuffer.text = '@src/my\\ file.txt';
|
|
745
|
-
mockBuffer.lines = ['@src/my\\ file.txt'];
|
|
746
|
-
mockBuffer.cursor = [0, 16]; // After the escaped space and filename
|
|
747
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
748
|
-
...mockCommandCompletion,
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
name: 'should handle escaped spaces in paths correctly',
|
|
662
|
+
text: '@src/my\\ file.txt',
|
|
663
|
+
cursor: [0, 16],
|
|
749
664
|
showSuggestions: true,
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
unmount();
|
|
756
|
-
});
|
|
757
|
-
it('should NOT trigger completion after unescaped space following escaped space', async () => {
|
|
758
|
-
// Test: @path/my\ file.txt hello (unescaped space after escaped space)
|
|
759
|
-
mockBuffer.text = '@path/my\\ file.txt hello';
|
|
760
|
-
mockBuffer.lines = ['@path/my\\ file.txt hello'];
|
|
761
|
-
mockBuffer.cursor = [0, 24]; // After "hello"
|
|
762
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
763
|
-
...mockCommandCompletion,
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
name: 'should NOT trigger completion after unescaped space following escaped space',
|
|
668
|
+
text: '@path/my\\ file.txt hello',
|
|
669
|
+
cursor: [0, 24],
|
|
764
670
|
showSuggestions: false,
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
unmount();
|
|
771
|
-
});
|
|
772
|
-
it('should handle multiple escaped spaces in paths', async () => {
|
|
773
|
-
// Test with multiple escaped spaces
|
|
774
|
-
mockBuffer.text = '@docs/my\\ long\\ file\\ name.md';
|
|
775
|
-
mockBuffer.lines = ['@docs/my\\ long\\ file\\ name.md'];
|
|
776
|
-
mockBuffer.cursor = [0, 29]; // At the end
|
|
777
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
778
|
-
...mockCommandCompletion,
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
name: 'should handle multiple escaped spaces in paths',
|
|
674
|
+
text: '@docs/my\\ long\\ file\\ name.md',
|
|
675
|
+
cursor: [0, 29],
|
|
779
676
|
showSuggestions: true,
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
await wait();
|
|
786
|
-
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(mockBuffer, ['/test/project/src'], path.join('test', 'project', 'src'), mockSlashCommands, mockCommandContext, false, false, expect.any(Object));
|
|
787
|
-
unmount();
|
|
788
|
-
});
|
|
789
|
-
it('should handle escaped spaces in slash commands', async () => {
|
|
790
|
-
// Test escaped spaces with slash commands (though less common)
|
|
791
|
-
mockBuffer.text = '/memory\\ test';
|
|
792
|
-
mockBuffer.lines = ['/memory\\ test'];
|
|
793
|
-
mockBuffer.cursor = [0, 13]; // At the end
|
|
794
|
-
mockedUseCommandCompletion.mockReturnValue({
|
|
795
|
-
...mockCommandCompletion,
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
name: 'should handle escaped spaces in slash commands',
|
|
680
|
+
text: '/memory\\ test',
|
|
681
|
+
cursor: [0, 13],
|
|
796
682
|
showSuggestions: true,
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
mockBuffer.
|
|
807
|
-
mockBuffer.
|
|
808
|
-
mockBuffer.cursor = [0, 25]; // After the escaped space and emoji
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
name: 'should handle Unicode characters with escaped spaces',
|
|
686
|
+
text: `@${path.join('files', 'emoji\\ 👍\\ test.txt')}`,
|
|
687
|
+
cursor: [0, 25],
|
|
688
|
+
showSuggestions: true,
|
|
689
|
+
},
|
|
690
|
+
])('$name', async ({ text, cursor, showSuggestions }) => {
|
|
691
|
+
mockBuffer.text = text;
|
|
692
|
+
mockBuffer.lines = text.split('\n');
|
|
693
|
+
mockBuffer.cursor = cursor;
|
|
809
694
|
mockedUseCommandCompletion.mockReturnValue({
|
|
810
695
|
...mockCommandCompletion,
|
|
811
|
-
showSuggestions
|
|
812
|
-
suggestions:
|
|
813
|
-
{ label: '
|
|
814
|
-
|
|
696
|
+
showSuggestions,
|
|
697
|
+
suggestions: showSuggestions
|
|
698
|
+
? [{ label: 'suggestion', value: 'suggestion' }]
|
|
699
|
+
: [],
|
|
815
700
|
});
|
|
816
701
|
const { unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
817
702
|
await wait();
|
|
@@ -876,165 +761,132 @@ describe('InputPrompt', () => {
|
|
|
876
761
|
});
|
|
877
762
|
});
|
|
878
763
|
describe('Highlighting and Cursor Display', () => {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
mockBuffer.lines = [text];
|
|
941
|
-
mockBuffer.viewportVisualLines = [text];
|
|
942
|
-
mockBuffer.visualCursor = [0, 8]; // cursor after '👍' (length is 6 + 2 for emoji)
|
|
943
|
-
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
944
|
-
await wait();
|
|
945
|
-
const frame = stdout.lastFrame();
|
|
946
|
-
expect(frame).toContain(`hello 👍${chalk.inverse(' ')}`);
|
|
947
|
-
unmount();
|
|
948
|
-
});
|
|
949
|
-
it('should display cursor on an empty line', async () => {
|
|
950
|
-
mockBuffer.text = '';
|
|
951
|
-
mockBuffer.lines = [''];
|
|
952
|
-
mockBuffer.viewportVisualLines = [''];
|
|
953
|
-
mockBuffer.visualCursor = [0, 0];
|
|
954
|
-
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
955
|
-
await wait();
|
|
956
|
-
const frame = stdout.lastFrame();
|
|
957
|
-
expect(frame).toContain(chalk.inverse(' '));
|
|
958
|
-
unmount();
|
|
959
|
-
});
|
|
960
|
-
it('should display cursor on a space between words', async () => {
|
|
961
|
-
mockBuffer.text = 'hello world';
|
|
962
|
-
mockBuffer.lines = ['hello world'];
|
|
963
|
-
mockBuffer.viewportVisualLines = ['hello world'];
|
|
964
|
-
mockBuffer.visualCursor = [0, 5]; // cursor on the space
|
|
965
|
-
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
966
|
-
await wait();
|
|
967
|
-
const frame = stdout.lastFrame();
|
|
968
|
-
expect(frame).toContain(`hello${chalk.inverse(' ')}world`);
|
|
969
|
-
unmount();
|
|
970
|
-
});
|
|
971
|
-
it('should display cursor in the middle of a line in a multiline block', async () => {
|
|
972
|
-
const text = 'first line\nsecond line\nthird line';
|
|
973
|
-
mockBuffer.text = text;
|
|
974
|
-
mockBuffer.lines = text.split('\n');
|
|
975
|
-
mockBuffer.viewportVisualLines = text.split('\n');
|
|
976
|
-
mockBuffer.visualCursor = [1, 3]; // cursor on 'o' in 'second'
|
|
977
|
-
mockBuffer.visualToLogicalMap = [
|
|
978
|
-
[0, 0],
|
|
979
|
-
[1, 0],
|
|
980
|
-
[2, 0],
|
|
981
|
-
];
|
|
982
|
-
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
983
|
-
await wait();
|
|
984
|
-
const frame = stdout.lastFrame();
|
|
985
|
-
expect(frame).toContain(`sec${chalk.inverse('o')}nd line`);
|
|
986
|
-
unmount();
|
|
987
|
-
});
|
|
988
|
-
it('should display cursor at the beginning of a line in a multiline block', async () => {
|
|
989
|
-
const text = 'first line\nsecond line';
|
|
990
|
-
mockBuffer.text = text;
|
|
991
|
-
mockBuffer.lines = text.split('\n');
|
|
992
|
-
mockBuffer.viewportVisualLines = text.split('\n');
|
|
993
|
-
mockBuffer.visualCursor = [1, 0]; // cursor on 's' in 'second'
|
|
994
|
-
mockBuffer.visualToLogicalMap = [
|
|
995
|
-
[0, 0],
|
|
996
|
-
[1, 0],
|
|
997
|
-
];
|
|
998
|
-
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
999
|
-
await wait();
|
|
1000
|
-
const frame = stdout.lastFrame();
|
|
1001
|
-
expect(frame).toContain(`${chalk.inverse('s')}econd line`);
|
|
1002
|
-
unmount();
|
|
1003
|
-
});
|
|
1004
|
-
it('should display cursor at the end of a line in a multiline block', async () => {
|
|
1005
|
-
const text = 'first line\nsecond line';
|
|
1006
|
-
mockBuffer.text = text;
|
|
1007
|
-
mockBuffer.lines = text.split('\n');
|
|
1008
|
-
mockBuffer.viewportVisualLines = text.split('\n');
|
|
1009
|
-
mockBuffer.visualCursor = [0, 10]; // cursor after 'first line'
|
|
1010
|
-
mockBuffer.visualToLogicalMap = [
|
|
1011
|
-
[0, 0],
|
|
1012
|
-
[1, 0],
|
|
1013
|
-
];
|
|
1014
|
-
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
1015
|
-
await wait();
|
|
1016
|
-
const frame = stdout.lastFrame();
|
|
1017
|
-
expect(frame).toContain(`first line${chalk.inverse(' ')}`);
|
|
1018
|
-
unmount();
|
|
764
|
+
describe('single-line scenarios', () => {
|
|
765
|
+
it.each([
|
|
766
|
+
{
|
|
767
|
+
name: 'mid-word',
|
|
768
|
+
text: 'hello world',
|
|
769
|
+
visualCursor: [0, 3],
|
|
770
|
+
expected: `hel${chalk.inverse('l')}o world`,
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
name: 'at the beginning of the line',
|
|
774
|
+
text: 'hello',
|
|
775
|
+
visualCursor: [0, 0],
|
|
776
|
+
expected: `${chalk.inverse('h')}ello`,
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
name: 'at the end of the line',
|
|
780
|
+
text: 'hello',
|
|
781
|
+
visualCursor: [0, 5],
|
|
782
|
+
expected: `hello${chalk.inverse(' ')}`,
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
name: 'on a highlighted token',
|
|
786
|
+
text: 'run @path/to/file',
|
|
787
|
+
visualCursor: [0, 9],
|
|
788
|
+
expected: `@path/${chalk.inverse('t')}o/file`,
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
name: 'for multi-byte unicode characters',
|
|
792
|
+
text: 'hello 👍 world',
|
|
793
|
+
visualCursor: [0, 6],
|
|
794
|
+
expected: `hello ${chalk.inverse('👍')} world`,
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
name: 'at the end of a line with unicode characters',
|
|
798
|
+
text: 'hello 👍',
|
|
799
|
+
visualCursor: [0, 8],
|
|
800
|
+
expected: `hello 👍${chalk.inverse(' ')}`,
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
name: 'on an empty line',
|
|
804
|
+
text: '',
|
|
805
|
+
visualCursor: [0, 0],
|
|
806
|
+
expected: chalk.inverse(' '),
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
name: 'on a space between words',
|
|
810
|
+
text: 'hello world',
|
|
811
|
+
visualCursor: [0, 5],
|
|
812
|
+
expected: `hello${chalk.inverse(' ')}world`,
|
|
813
|
+
},
|
|
814
|
+
])('should display cursor correctly $name', async ({ text, visualCursor, expected }) => {
|
|
815
|
+
mockBuffer.text = text;
|
|
816
|
+
mockBuffer.lines = [text];
|
|
817
|
+
mockBuffer.viewportVisualLines = [text];
|
|
818
|
+
mockBuffer.visualCursor = visualCursor;
|
|
819
|
+
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
820
|
+
await wait();
|
|
821
|
+
const frame = stdout.lastFrame();
|
|
822
|
+
expect(frame).toContain(expected);
|
|
823
|
+
unmount();
|
|
824
|
+
});
|
|
1019
825
|
});
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
826
|
+
describe('multi-line scenarios', () => {
|
|
827
|
+
it.each([
|
|
828
|
+
{
|
|
829
|
+
name: 'in the middle of a line',
|
|
830
|
+
text: 'first line\nsecond line\nthird line',
|
|
831
|
+
visualCursor: [1, 3],
|
|
832
|
+
visualToLogicalMap: [
|
|
833
|
+
[0, 0],
|
|
834
|
+
[1, 0],
|
|
835
|
+
[2, 0],
|
|
836
|
+
],
|
|
837
|
+
expected: `sec${chalk.inverse('o')}nd line`,
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
name: 'at the beginning of a line',
|
|
841
|
+
text: 'first line\nsecond line',
|
|
842
|
+
visualCursor: [1, 0],
|
|
843
|
+
visualToLogicalMap: [
|
|
844
|
+
[0, 0],
|
|
845
|
+
[1, 0],
|
|
846
|
+
],
|
|
847
|
+
expected: `${chalk.inverse('s')}econd line`,
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
name: 'at the end of a line',
|
|
851
|
+
text: 'first line\nsecond line',
|
|
852
|
+
visualCursor: [0, 10],
|
|
853
|
+
visualToLogicalMap: [
|
|
854
|
+
[0, 0],
|
|
855
|
+
[1, 0],
|
|
856
|
+
],
|
|
857
|
+
expected: `first line${chalk.inverse(' ')}`,
|
|
858
|
+
},
|
|
859
|
+
])('should display cursor correctly $name in a multiline block', async ({ text, visualCursor, expected, visualToLogicalMap }) => {
|
|
860
|
+
mockBuffer.text = text;
|
|
861
|
+
mockBuffer.lines = text.split('\n');
|
|
862
|
+
mockBuffer.viewportVisualLines = text.split('\n');
|
|
863
|
+
mockBuffer.visualCursor = visualCursor;
|
|
864
|
+
mockBuffer.visualToLogicalMap = visualToLogicalMap;
|
|
865
|
+
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
866
|
+
await wait();
|
|
867
|
+
const frame = stdout.lastFrame();
|
|
868
|
+
expect(frame).toContain(expected);
|
|
869
|
+
unmount();
|
|
870
|
+
});
|
|
871
|
+
it('should display cursor on a blank line in a multiline block', async () => {
|
|
872
|
+
const text = 'first line\n\nthird line';
|
|
873
|
+
mockBuffer.text = text;
|
|
874
|
+
mockBuffer.lines = text.split('\n');
|
|
875
|
+
mockBuffer.viewportVisualLines = text.split('\n');
|
|
876
|
+
mockBuffer.visualCursor = [1, 0]; // cursor on the blank line
|
|
877
|
+
mockBuffer.visualToLogicalMap = [
|
|
878
|
+
[0, 0],
|
|
879
|
+
[1, 0],
|
|
880
|
+
[2, 0],
|
|
881
|
+
];
|
|
882
|
+
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
883
|
+
await wait();
|
|
884
|
+
const frame = stdout.lastFrame();
|
|
885
|
+
const lines = frame.split('\n');
|
|
886
|
+
// The line with the cursor should just be an inverted space inside the box border
|
|
887
|
+
expect(lines.find((l) => l.includes(chalk.inverse(' ')))).not.toBeUndefined();
|
|
888
|
+
unmount();
|
|
889
|
+
});
|
|
1038
890
|
});
|
|
1039
891
|
});
|
|
1040
892
|
describe('multiline rendering', () => {
|
|
@@ -1111,8 +963,9 @@ describe('InputPrompt', () => {
|
|
|
1111
963
|
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
1112
964
|
await vi.runAllTimersAsync();
|
|
1113
965
|
// Simulate a paste operation (this should set the paste protection)
|
|
1114
|
-
|
|
1115
|
-
|
|
966
|
+
act(() => {
|
|
967
|
+
stdin.write(`\x1b[200~pasted content\x1b[201~`);
|
|
968
|
+
});
|
|
1116
969
|
// Simulate an Enter key press immediately after paste
|
|
1117
970
|
stdin.write('\r');
|
|
1118
971
|
await vi.runAllTimersAsync();
|
|
@@ -1456,7 +1309,7 @@ describe('InputPrompt', () => {
|
|
|
1456
1309
|
expect(frame).toContain('git push');
|
|
1457
1310
|
unmount();
|
|
1458
1311
|
});
|
|
1459
|
-
it
|
|
1312
|
+
it('expands and collapses long suggestion via Right/Left arrows', async () => {
|
|
1460
1313
|
props.shellModeActive = false;
|
|
1461
1314
|
const longValue = 'l'.repeat(200);
|
|
1462
1315
|
vi.mocked(useReverseSearchCompletion).mockReturnValue({
|
|
@@ -1684,48 +1537,51 @@ describe('InputPrompt', () => {
|
|
|
1684
1537
|
expect(mockBuffer.handleInput).toHaveBeenCalled();
|
|
1685
1538
|
unmount();
|
|
1686
1539
|
});
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1540
|
+
describe('command queuing while streaming', () => {
|
|
1541
|
+
beforeEach(() => {
|
|
1542
|
+
props.streamingState = StreamingState.Responding;
|
|
1543
|
+
props.setQueueErrorMessage = vi.fn();
|
|
1544
|
+
props.onSubmit = vi.fn();
|
|
1545
|
+
});
|
|
1546
|
+
it.each([
|
|
1547
|
+
{
|
|
1548
|
+
name: 'should prevent slash commands',
|
|
1549
|
+
bufferText: '/help',
|
|
1550
|
+
shellMode: false,
|
|
1551
|
+
shouldSubmit: false,
|
|
1552
|
+
errorMessage: 'Slash commands cannot be queued',
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
name: 'should prevent shell commands',
|
|
1556
|
+
bufferText: 'ls',
|
|
1557
|
+
shellMode: true,
|
|
1558
|
+
shouldSubmit: false,
|
|
1559
|
+
errorMessage: 'Shell commands cannot be queued',
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: 'should allow regular messages',
|
|
1563
|
+
bufferText: 'regular message',
|
|
1564
|
+
shellMode: false,
|
|
1565
|
+
shouldSubmit: true,
|
|
1566
|
+
errorMessage: null,
|
|
1567
|
+
},
|
|
1568
|
+
])('$name', async ({ bufferText, shellMode, shouldSubmit, errorMessage }) => {
|
|
1569
|
+
props.buffer.text = bufferText;
|
|
1570
|
+
props.shellModeActive = shellMode;
|
|
1571
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
1572
|
+
await wait();
|
|
1573
|
+
stdin.write('\r');
|
|
1574
|
+
await wait();
|
|
1575
|
+
if (shouldSubmit) {
|
|
1576
|
+
expect(props.onSubmit).toHaveBeenCalledWith(bufferText);
|
|
1577
|
+
expect(props.setQueueErrorMessage).not.toHaveBeenCalled();
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
expect(props.onSubmit).not.toHaveBeenCalled();
|
|
1581
|
+
expect(props.setQueueErrorMessage).toHaveBeenCalledWith(errorMessage);
|
|
1582
|
+
}
|
|
1583
|
+
unmount();
|
|
1584
|
+
});
|
|
1729
1585
|
});
|
|
1730
1586
|
});
|
|
1731
1587
|
function clean(str) {
|