From 77e552a074e6c16b309a468ef5c9e20ee0b83470 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 22 Mar 2025 20:08:03 -0400 Subject: [PATCH 01/70] Add `Category` enum. --- src/CommandPalette/Category.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/CommandPalette/Category.php diff --git a/src/CommandPalette/Category.php b/src/CommandPalette/Category.php new file mode 100644 index 00000000000..9239e0aa9c9 --- /dev/null +++ b/src/CommandPalette/Category.php @@ -0,0 +1,15 @@ + Date: Sat, 22 Mar 2025 20:08:14 -0400 Subject: [PATCH 02/70] Implement base `Command`. --- src/CommandPalette/Command.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/CommandPalette/Command.php diff --git a/src/CommandPalette/Command.php b/src/CommandPalette/Command.php new file mode 100644 index 00000000000..03d707087ca --- /dev/null +++ b/src/CommandPalette/Command.php @@ -0,0 +1,30 @@ +getShortName()); + } + + public function toArray(): array + { + return [ + 'type' => $this->type(), + 'category' => $this->category->name, + 'text' => $this->text, + ]; + } +} From e679ea136f09db7e31fe93b5322f6aa302bb8f09 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 22 Mar 2025 20:08:39 -0400 Subject: [PATCH 03/70] =?UTF-8?q?Implement=20`Link`=20command=20type=20(ie?= =?UTF-8?q?.=20for=20=E2=80=98Navigation=E2=80=99=20items).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CommandPalette/Link.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/CommandPalette/Link.php diff --git a/src/CommandPalette/Link.php b/src/CommandPalette/Link.php new file mode 100644 index 00000000000..4d2f74f6c22 --- /dev/null +++ b/src/CommandPalette/Link.php @@ -0,0 +1,22 @@ +url = $url; + + return $this; + } + + public function toArray(): array + { + return array_merge(parent::toArray(), [ + 'url' => $this->url, + ]); + } +} From cac82d55b9cca6e1dd31787530e7b85efa891071 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 22 Mar 2025 20:09:01 -0400 Subject: [PATCH 04/70] Implement `Palette` builder. --- src/CommandPalette/Palette.php | 84 ++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/CommandPalette/Palette.php diff --git a/src/CommandPalette/Palette.php b/src/CommandPalette/Palette.php new file mode 100644 index 00000000000..c1000e6b5a4 --- /dev/null +++ b/src/CommandPalette/Palette.php @@ -0,0 +1,84 @@ +items = collect(); + } + + public function addCommand(Command $command): self + { + $this->items->push( + $this->validateCommandArray($command->toArray()), + ); + + return $this; + } + + public function build(): array + { + return $this + ->buildNavigation() + ->buildActions() + ->buildHistory() + ->sort() + ->toArray(); + } + + protected function buildNavigation(): self + { + // TODO: + // Use same blink cache as NavComposer? + // Do we also want to show/cache resolved item children? + + Nav::build() + ->flatMap(fn ($section) => $section['items']) + ->each(fn ($item) => $this->addCommand( + (new Link( + text: __($item->section()).' > '.__($item->display()), + category: Category::Navigation, + ))->url($item->url()) + )); + + return $this; + } + + protected function buildActions(): self + { + return $this; + } + + protected function buildHistory(): self + { + return $this; + } + + protected function sort(): self + { + // TODO: Sort categories? Or sort in JS? + + return $this; + } + + public function validateCommandArray(array $command): array + { + throw_unless(is_string(Arr::get($command, 'type')), new \Exception('Must output command [type] string!')); + throw_unless(is_string(Arr::get($command, 'category')), new \Exception('Must output command [category] string!')); + throw_unless(is_string(Arr::get($command, 'text')), new \Exception('Must output command [text] string!')); + + return $command; + } + + public function toArray(): array + { + return $this->items->toArray(); + } +} From 7418338f47950983379056b8b2cd22a97882bbda Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 22 Mar 2025 20:09:16 -0400 Subject: [PATCH 05/70] Register `CommandPalette` facade. --- src/Facades/CommandPalette.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/Facades/CommandPalette.php diff --git a/src/Facades/CommandPalette.php b/src/Facades/CommandPalette.php new file mode 100644 index 00000000000..55a89c19069 --- /dev/null +++ b/src/Facades/CommandPalette.php @@ -0,0 +1,17 @@ + Date: Sat, 22 Mar 2025 20:09:49 -0400 Subject: [PATCH 06/70] Test basic `Navigation` commands are built for JS component. --- tests/CommandPalette/CommandPaletteTest.php | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/CommandPalette/CommandPaletteTest.php diff --git a/tests/CommandPalette/CommandPaletteTest.php b/tests/CommandPalette/CommandPaletteTest.php new file mode 100644 index 00000000000..b76fa9f5da8 --- /dev/null +++ b/tests/CommandPalette/CommandPaletteTest.php @@ -0,0 +1,67 @@ +actingAs(tap(User::make()->makeSuper())->save()); + } + + #[Test] + public function it_builds_an_array_that_can_be_converted_to_json() + { + $commands = CommandPalette::build(); + + $this->assertTrue(is_array($commands)); + $this->assertTrue(is_string(json_encode($commands))); + } + + #[Test] + public function it_can_build_commands_off_nav_items() + { + $navigationCommands = collect(CommandPalette::build()) + ->filter(fn ($item) => $item['category'] === 'Navigation') + ->map(fn ($item) => Arr::except($item, 'category')) + ->all(); + + $expected = [ + ['type' => 'link', 'text' => 'Top Level > Dashboard', 'url' => 'http://localhost/cp/dashboard'], + ['type' => 'link', 'text' => 'Content > Collections', 'url' => 'http://localhost/cp/collections'], + ['type' => 'link', 'text' => 'Content > Navigation', 'url' => 'http://localhost/cp/navigation'], + ['type' => 'link', 'text' => 'Content > Taxonomies', 'url' => 'http://localhost/cp/taxonomies'], + ['type' => 'link', 'text' => 'Content > Assets', 'url' => 'http://localhost/cp/assets'], + ['type' => 'link', 'text' => 'Content > Globals', 'url' => 'http://localhost/cp/globals'], + ['type' => 'link', 'text' => 'Fields > Blueprints', 'url' => 'http://localhost/cp/fields/blueprints'], + ['type' => 'link', 'text' => 'Fields > Fieldsets', 'url' => 'http://localhost/cp/fields/fieldsets'], + ['type' => 'link', 'text' => 'Tools > Forms', 'url' => 'http://localhost/cp/forms'], + ['type' => 'link', 'text' => 'Tools > Updates', 'url' => 'http://localhost/cp/updater'], + ['type' => 'link', 'text' => 'Tools > Addons', 'url' => 'http://localhost/cp/addons'], + ['type' => 'link', 'text' => 'Tools > Utilities', 'url' => 'http://localhost/cp/utilities'], + ['type' => 'link', 'text' => 'Tools > GraphQL', 'url' => 'http://localhost/cp/graphql'], + ['type' => 'link', 'text' => 'Settings > Site', 'url' => 'http://localhost/cp/sites'], + ['type' => 'link', 'text' => 'Settings > Preferences', 'url' => 'http://localhost/cp/preferences'], + ['type' => 'link', 'text' => 'Users > Users', 'url' => 'http://localhost/cp/users'], + ['type' => 'link', 'text' => 'Users > Groups', 'url' => 'http://localhost/cp/user-groups'], + ['type' => 'link', 'text' => 'Users > Permissions', 'url' => 'http://localhost/cp/roles'], + ]; + + $this->assertEquals($expected, $navigationCommands); + } +} From 8fcba2b07201e22a91b6986344b9a9a2675a9cfc Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 22 Mar 2025 20:10:27 -0400 Subject: [PATCH 07/70] Data composer, so that command palette data is available in main CP layout everywhere. --- .../View/Composers/CommandPaletteComposer.php | 19 +++++++++++++++++++ src/Providers/CpServiceProvider.php | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 src/Http/View/Composers/CommandPaletteComposer.php diff --git a/src/Http/View/Composers/CommandPaletteComposer.php b/src/Http/View/Composers/CommandPaletteComposer.php new file mode 100644 index 00000000000..3be6394a950 --- /dev/null +++ b/src/Http/View/Composers/CommandPaletteComposer.php @@ -0,0 +1,19 @@ +with('commandPalette', Blink::once('command-palette-composer', fn () => CommandPalette::build())); + } +} diff --git a/src/Providers/CpServiceProvider.php b/src/Providers/CpServiceProvider.php index 666ba482b33..c3e2b38b730 100644 --- a/src/Providers/CpServiceProvider.php +++ b/src/Providers/CpServiceProvider.php @@ -12,6 +12,7 @@ use Statamic\Facades\User; use Statamic\Fieldtypes\Sets; use Statamic\Http\Middleware\CP\StartSession; +use Statamic\Http\View\Composers\CommandPaletteComposer; use Statamic\Http\View\Composers\CustomLogoComposer; use Statamic\Http\View\Composers\FieldComposer; use Statamic\Http\View\Composers\JavascriptComposer; @@ -32,6 +33,7 @@ public function boot() View::composer(SessionExpiryComposer::VIEWS, SessionExpiryComposer::class); View::composer(JavascriptComposer::VIEWS, JavascriptComposer::class); View::composer(NavComposer::VIEWS, NavComposer::class); + View::composer(CommandPaletteComposer::VIEWS, CommandPaletteComposer::class); View::composer(CustomLogoComposer::VIEWS, CustomLogoComposer::class); Blade::directive('cp_svg', function ($expression) { From 40a97a9346a1954b4a64a436c78a318659a40613 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 22 Mar 2025 20:10:47 -0400 Subject: [PATCH 08/70] Dummy partial for now until vue. --- resources/views/layout.blade.php | 1 + resources/views/partials/command-palette.blade.php | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 resources/views/partials/command-palette.blade.php diff --git a/resources/views/layout.blade.php b/resources/views/layout.blade.php index 0e751fc58aa..ef655ba7e5f 100644 --- a/resources/views/layout.blade.php +++ b/resources/views/layout.blade.php @@ -13,6 +13,7 @@ class="{{ $user->preferredTheme() === 'dark' ? 'dark' : '' }}" @include('statamic::partials.session-expiry') @include('statamic::partials.licensing-alerts') @include('statamic::partials.global-header') + @include('statamic::partials.command-palette')
+@stop + +@yield('command-palette') From bda1870a33491d335df0da5e0504aa4d115a6df7 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Mon, 24 Mar 2025 16:08:28 -0400 Subject: [PATCH 09/70] Simplify/remove `Nav::buildWithoutPreferences()` method. Just use `build()` flags. --- src/CP/Navigation/Nav.php | 17 +++++------------ src/CP/Navigation/NavBuilder.php | 14 +++++++++++++- src/CP/Navigation/NavTransformer.php | 5 ++++- src/Facades/CP/Nav.php | 1 - .../CP/Preferences/Nav/DefaultNavController.php | 7 ++++--- .../CP/Preferences/Nav/RoleNavController.php | 7 ++++--- tests/CP/Navigation/NavPreferencesTest.php | 6 +++--- 7 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/CP/Navigation/Nav.php b/src/CP/Navigation/Nav.php index 27b4ebee964..17f03571603 100644 --- a/src/CP/Navigation/Nav.php +++ b/src/CP/Navigation/Nav.php @@ -126,21 +126,14 @@ protected function removeChildItem($section, $name, $childName) * Build navigation. * * @param mixed $preferences + * @param bool $preferences * @return \Illuminate\Support\Collection */ - public function build($preferences = true, $withHidden = false) + public function build($preferences = true, $editing = false) { - return (new NavBuilder($this->makeBaseItems(), $withHidden))->build($preferences); - } - - /** - * Build navigation without applying preferences. - * - * @return \Illuminate\Support\Collection - */ - public function buildWithoutPreferences($withHidden = false) - { - return $this->build(false, $withHidden); + return (new NavBuilder($this->makeBaseItems())) + ->withHidden($editing) + ->build($preferences); } /** diff --git a/src/CP/Navigation/NavBuilder.php b/src/CP/Navigation/NavBuilder.php index 4219e56c2e5..4d5e61706ee 100644 --- a/src/CP/Navigation/NavBuilder.php +++ b/src/CP/Navigation/NavBuilder.php @@ -34,10 +34,22 @@ class NavBuilder * @param array $items * @param bool $withHidden */ - public function __construct($items, $withHidden = false) + public function __construct($items) { $this->items = $items; + } + + /** + * Build with hidden items. + * + * @param bool $withHidden + * @return $this + */ + public function withHidden(bool $withHidden = false): self + { $this->withHidden = $withHidden; + + return $this; } /** diff --git a/src/CP/Navigation/NavTransformer.php b/src/CP/Navigation/NavTransformer.php index 2f1f8001ebc..5f71ba47054 100644 --- a/src/CP/Navigation/NavTransformer.php +++ b/src/CP/Navigation/NavTransformer.php @@ -18,7 +18,10 @@ class NavTransformer */ public function __construct(array $submitted) { - $this->coreNav = Nav::buildWithoutPreferences(true); + $this->coreNav = Nav::build( + preferences: false, + editing: true, + ); $this->submitted = $this->removeEmptyCustomSections($submitted); } diff --git a/src/Facades/CP/Nav.php b/src/Facades/CP/Nav.php index 485ec1ad99b..3e1ef0b96e5 100644 --- a/src/Facades/CP/Nav.php +++ b/src/Facades/CP/Nav.php @@ -16,7 +16,6 @@ * @method static NavItem|null findOrCreate(string $section, string $name) * @method static self remove(string $section, $name = null) * @method static Collection build() - * @method static Collection buildWithoutPreferences() * @method static void clearCachedUrls() * @method static array items() * diff --git a/src/Http/Controllers/CP/Preferences/Nav/DefaultNavController.php b/src/Http/Controllers/CP/Preferences/Nav/DefaultNavController.php index b0fcedc41af..672dda1f624 100644 --- a/src/Http/Controllers/CP/Preferences/Nav/DefaultNavController.php +++ b/src/Http/Controllers/CP/Preferences/Nav/DefaultNavController.php @@ -20,9 +20,10 @@ public function edit() { $preferences = Preference::default()->get('nav'); - $nav = $preferences - ? Nav::build($preferences, true) - : Nav::buildWithoutPreferences(true); + $nav = Nav::build( + preferences: $preferences ?: false, + editing: true, + ); return $this->navBuilder($nav, [ 'title' => __('Default'), diff --git a/src/Http/Controllers/CP/Preferences/Nav/RoleNavController.php b/src/Http/Controllers/CP/Preferences/Nav/RoleNavController.php index 61c70fe1cd6..01b4fd7a7e9 100644 --- a/src/Http/Controllers/CP/Preferences/Nav/RoleNavController.php +++ b/src/Http/Controllers/CP/Preferences/Nav/RoleNavController.php @@ -29,9 +29,10 @@ public function edit($handle) $preferences = $role->getPreference('nav') ?? Preference::default()->get('nav'); - $nav = $preferences - ? Nav::build($preferences, true) - : Nav::buildWithoutPreferences(true); + $nav = Nav::build( + preferences: $preferences ?: false, + editing: true, + ); return $this->navBuilder($nav, [ 'title' => __($role->title()), diff --git a/tests/CP/Navigation/NavPreferencesTest.php b/tests/CP/Navigation/NavPreferencesTest.php index bdeaffffe75..49e6f38e09d 100644 --- a/tests/CP/Navigation/NavPreferencesTest.php +++ b/tests/CP/Navigation/NavPreferencesTest.php @@ -1694,17 +1694,17 @@ public function it_checks_active_status_on_moved_items() $this->assertTrue($tags->isActive()); } - private function buildNavWithPreferences($preferences, $withHidden = false) + private function buildNavWithPreferences($preferences, $editingHidden = false) { $this->actingAs(tap(Facades\User::make()->makeSuper())->save()); - return Facades\CP\Nav::build($preferences, $withHidden)->pluck('items', 'display'); + return Facades\CP\Nav::build(preferences: $preferences, editing: $editingHidden)->pluck('items', 'display'); } private function buildDefaultNav() { $this->actingAs(tap(Facades\User::make()->makeSuper())->save()); - return Facades\CP\Nav::buildWithoutPreferences()->pluck('items', 'display'); + return Facades\CP\Nav::build(preferences: false)->pluck('items', 'display'); } } From 1657daf38e38e4f99ae66be9a3b174a32a798407 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Mon, 24 Mar 2025 16:25:14 -0400 Subject: [PATCH 10/70] Allow nav builder to add its items to command palette using facade API. --- resources/views/layout.blade.php | 3 ++- src/CP/Navigation/Nav.php | 3 ++- src/CP/Navigation/NavBuilder.php | 42 ++++++++++++++++++++++++++++++++ src/CommandPalette/Palette.php | 20 --------------- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/resources/views/layout.blade.php b/resources/views/layout.blade.php index ef655ba7e5f..a411dff47ec 100644 --- a/resources/views/layout.blade.php +++ b/resources/views/layout.blade.php @@ -13,7 +13,6 @@ class="{{ $user->preferredTheme() === 'dark' ? 'dark' : '' }}" @include('statamic::partials.session-expiry') @include('statamic::partials.licensing-alerts') @include('statamic::partials.global-header') - @include('statamic::partials.command-palette')
+ + @include('statamic::partials.command-palette')
@include('statamic::partials.scripts') diff --git a/src/CP/Navigation/Nav.php b/src/CP/Navigation/Nav.php index 17f03571603..139972defec 100644 --- a/src/CP/Navigation/Nav.php +++ b/src/CP/Navigation/Nav.php @@ -129,10 +129,11 @@ protected function removeChildItem($section, $name, $childName) * @param bool $preferences * @return \Illuminate\Support\Collection */ - public function build($preferences = true, $editing = false) + public function build($preferences = true, bool $editing = false) { return (new NavBuilder($this->makeBaseItems())) ->withHidden($editing) + ->withCommandPalette(! $editing) ->build($preferences); } diff --git a/src/CP/Navigation/NavBuilder.php b/src/CP/Navigation/NavBuilder.php index 4d5e61706ee..7c993fdcaab 100644 --- a/src/CP/Navigation/NavBuilder.php +++ b/src/CP/Navigation/NavBuilder.php @@ -4,7 +4,10 @@ use Exception; use Illuminate\Support\Facades\Cache; +use Statamic\CommandPalette\Category; +use Statamic\CommandPalette\Link; use Statamic\Facades\Blink; +use Statamic\Facades\CommandPalette; use Statamic\Facades\Preference; use Statamic\Facades\User; use Statamic\Support\Arr; @@ -18,6 +21,7 @@ class NavBuilder protected $items = []; protected $pendingItems = []; protected $withHidden = false; + protected $withCommandPalette = false; protected $itemsWithChildrenClosures = []; protected $sections = []; protected $sectionsOriginalItemIds = []; @@ -52,6 +56,19 @@ public function withHidden(bool $withHidden = false): self return $this; } + /** + * Build with command palette. + * + * @param bool $withHidden + * @return $this + */ + public function withCommandPalette(bool $withCommandPalette = false): self + { + $this->withCommandPalette = $withCommandPalette; + + return $this; + } + /** * Build navigation. * @@ -78,6 +95,7 @@ public function build($preferences = true) ->applyPreferenceOverrides($preferences) ->buildSections() ->blinkUrls() + ->addToCommandPalette() ->get(); } @@ -1060,6 +1078,30 @@ public static function clearCachedUrls() Blink::forget(static::ALL_URLS_CACHE_KEY); } + /** + * Add built items to command palette. + * + * @return $this + */ + protected function addToCommandPalette() + { + if (! $this->withCommandPalette) { + return $this; + } + + $this->built + ->flatMap(fn ($section) => $section['items']) + ->filter(fn ($item) => $item->url()) + ->each(fn ($item) => CommandPalette::addCommand( + (new Link( + text: __($item->section()).' > '.__($item->display()), + category: Category::Navigation, + ))->url($item->url()) + )); + + return $this; + } + /** * Get built nav. * diff --git a/src/CommandPalette/Palette.php b/src/CommandPalette/Palette.php index c1000e6b5a4..da562533077 100644 --- a/src/CommandPalette/Palette.php +++ b/src/CommandPalette/Palette.php @@ -2,7 +2,6 @@ namespace Statamic\CommandPalette; -use Statamic\Facades\CP\Nav; use Statamic\Support\Arr; class Palette @@ -26,31 +25,12 @@ public function addCommand(Command $command): self public function build(): array { return $this - ->buildNavigation() ->buildActions() ->buildHistory() ->sort() ->toArray(); } - protected function buildNavigation(): self - { - // TODO: - // Use same blink cache as NavComposer? - // Do we also want to show/cache resolved item children? - - Nav::build() - ->flatMap(fn ($section) => $section['items']) - ->each(fn ($item) => $this->addCommand( - (new Link( - text: __($item->section()).' > '.__($item->display()), - category: Category::Navigation, - ))->url($item->url()) - )); - - return $this; - } - protected function buildActions(): self { return $this; From 7c71621e6d51c21d71f97f6126dd944ccf6f1042 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Mon, 24 Mar 2025 16:26:28 -0400 Subject: [PATCH 11/70] Todo. --- src/CommandPalette/Palette.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CommandPalette/Palette.php b/src/CommandPalette/Palette.php index da562533077..2cfe439d59c 100644 --- a/src/CommandPalette/Palette.php +++ b/src/CommandPalette/Palette.php @@ -38,6 +38,8 @@ protected function buildActions(): self protected function buildHistory(): self { + // TODO: Set up ajax route for caching command palette history as user runs commands. + return $this; } From 1e094fe2fcae9c76b62ce5641dd0a735005a97bd Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Mon, 24 Mar 2025 17:02:54 -0400 Subject: [PATCH 12/70] Pass these tests again. --- tests/CommandPalette/CommandPaletteTest.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/CommandPalette/CommandPaletteTest.php b/tests/CommandPalette/CommandPaletteTest.php index b76fa9f5da8..e690d539144 100644 --- a/tests/CommandPalette/CommandPaletteTest.php +++ b/tests/CommandPalette/CommandPaletteTest.php @@ -3,6 +3,7 @@ namespace Tests\CommandPalette; use PHPUnit\Framework\Attributes\Test; +use Statamic\Facades; use Statamic\Facades\CommandPalette; use Statamic\Facades\User; use Statamic\Support\Arr; @@ -21,12 +22,18 @@ public function setUp(): void { parent::setUp(); - $this->actingAs(tap(User::make()->makeSuper())->save()); + // TODO: Other tests are leaving behind forms without titles that are causing failures here? + Facades\Form::shouldReceive('all')->andReturn(collect()); } #[Test] public function it_builds_an_array_that_can_be_converted_to_json() { + $this + ->actingAs(tap(User::make()->makeSuper())->save()) + ->get(cp_route('dashboard')) + ->assertStatus(200); + $commands = CommandPalette::build(); $this->assertTrue(is_array($commands)); @@ -36,6 +43,11 @@ public function it_builds_an_array_that_can_be_converted_to_json() #[Test] public function it_can_build_commands_off_nav_items() { + $this + ->actingAs(tap(User::make()->makeSuper())->save()) + ->get(cp_route('dashboard')) + ->assertStatus(200); + $navigationCommands = collect(CommandPalette::build()) ->filter(fn ($item) => $item['category'] === 'Navigation') ->map(fn ($item) => Arr::except($item, 'category')) From db483111cda29567ac0fc56d580b20405cf1fe01 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Fri, 4 Apr 2025 16:56:38 -0400 Subject: [PATCH 13/70] Build Collection. --- resources/views/layout.blade.php | 4 ++-- src/CommandPalette/Palette.php | 31 ++++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/resources/views/layout.blade.php b/resources/views/layout.blade.php index a411dff47ec..06e17357243 100644 --- a/resources/views/layout.blade.php +++ b/resources/views/layout.blade.php @@ -55,9 +55,9 @@ class="@yield('content-class')" - - @include('statamic::partials.command-palette') + +
@include('statamic::partials.scripts') diff --git a/src/CommandPalette/Palette.php b/src/CommandPalette/Palette.php index 2cfe439d59c..5ac9fb86659 100644 --- a/src/CommandPalette/Palette.php +++ b/src/CommandPalette/Palette.php @@ -2,6 +2,7 @@ namespace Statamic\CommandPalette; +use Illuminate\Support\Collection; use Statamic\Support\Arr; class Palette @@ -22,17 +23,37 @@ public function addCommand(Command $command): self return $this; } - public function build(): array + public function build(): Collection { return $this ->buildActions() ->buildHistory() - ->sort() - ->toArray(); + ->get(); } protected function buildActions(): self { + $this->addCommand( + (new Link( + text: 'Save', + category: Category::Actions, + ))->url('/cp') + ); + + $this->addCommand( + (new Link( + text: 'Duplicate', + category: Category::Actions, + ))->url('/cp') + ); + + $this->addCommand( + (new Link( + text: 'Delete', + category: Category::Actions, + ))->url('/cp') + ); + return $this; } @@ -59,8 +80,8 @@ public function validateCommandArray(array $command): array return $command; } - public function toArray(): array + public function get(): Collection { - return $this->items->toArray(); + return $this->items; } } From d3ab51d84613dc4a002337e1e02bf27b9987172f Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Fri, 4 Apr 2025 16:59:56 -0400 Subject: [PATCH 14/70] Component. --- resources/js/bootstrap/components.js | 3 +++ resources/js/components/CommandPalette.vue | 19 +++++++++++++++++++ .../views/partials/command-palette.blade.php | 4 +++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 resources/js/components/CommandPalette.vue diff --git a/resources/js/bootstrap/components.js b/resources/js/bootstrap/components.js index d5d7c3f77f3..9da02573eb4 100644 --- a/resources/js/bootstrap/components.js +++ b/resources/js/bootstrap/components.js @@ -63,6 +63,7 @@ import CodeBlock from '../components/CodeBlock.vue'; import BlueprintResetter from '../components/blueprints/BlueprintResetter.vue'; import { defineAsyncComponent } from 'vue'; import DateTime from '../components/DateTime.vue'; +import CommandPalette from '../components/CommandPalette.vue'; export default function registerGlobalComponents(app) { // Core @@ -152,4 +153,6 @@ export default function registerGlobalComponents(app) { app.component('stack-test', StackTest); app.component('blueprint-resetter', BlueprintResetter); + + app.component('command-palette', CommandPalette); } diff --git a/resources/js/components/CommandPalette.vue b/resources/js/components/CommandPalette.vue new file mode 100644 index 00000000000..1ebd3cb332f --- /dev/null +++ b/resources/js/components/CommandPalette.vue @@ -0,0 +1,19 @@ + + + diff --git a/resources/views/partials/command-palette.blade.php b/resources/views/partials/command-palette.blade.php index 17ec0199b6d..13d88efcd30 100644 --- a/resources/views/partials/command-palette.blade.php +++ b/resources/views/partials/command-palette.blade.php @@ -4,7 +4,9 @@ @endphp @section('command-palette') - + @stop @yield('command-palette') From 07376bec1ee064b2a46ff74bde6ecc6b009f310c Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Fri, 4 Apr 2025 17:10:18 -0400 Subject: [PATCH 15/70] Improve nav link transformation. --- src/CP/Navigation/NavBuilder.php | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/CP/Navigation/NavBuilder.php b/src/CP/Navigation/NavBuilder.php index 7c993fdcaab..ed7a244e677 100644 --- a/src/CP/Navigation/NavBuilder.php +++ b/src/CP/Navigation/NavBuilder.php @@ -1080,10 +1080,8 @@ public static function clearCachedUrls() /** * Add built items to command palette. - * - * @return $this */ - protected function addToCommandPalette() + protected function addToCommandPalette(): self { if (! $this->withCommandPalette) { return $this; @@ -1092,16 +1090,30 @@ protected function addToCommandPalette() $this->built ->flatMap(fn ($section) => $section['items']) ->filter(fn ($item) => $item->url()) - ->each(fn ($item) => CommandPalette::addCommand( - (new Link( - text: __($item->section()).' > '.__($item->display()), - category: Category::Navigation, - ))->url($item->url()) - )); + ->each(fn ($item) => CommandPalette::addCommand(static::transformToLink($item))); return $this; } + /** + * Add built items to command palette. + */ + protected static function transformToLink(NavItem $item): Link + { + $text = $item->section() !== 'Top Level' + ? __($item->section()).' > '.__($item->display()) + : __($item->display()); + + $link = new Link( + text: $text, + category: Category::Navigation, + ); + + $link->url($item->url()); + + return $link; + } + /** * Get built nav. * From 921bf04338530f514633c5ecab1888e491729896 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Fri, 4 Apr 2025 17:10:40 -0400 Subject: [PATCH 16/70] Fuzzysort example. --- package-lock.json | 7 ++++ package.json | 1 + resources/js/components/CommandPalette.vue | 41 +++++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index e3d09fc08bf..7d0df05505f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,7 @@ "cookies-js": "^1.2.2", "floating-vue": "^5.2.2", "fuse.js": "^7.0.0", + "fuzzysort": "^3.1.0", "highlight.js": "^11.7.0", "imask": "^7.6.1", "laravel-echo": "^1.16.0", @@ -3541,6 +3542,12 @@ "node": ">=10" } }, + "node_modules/fuzzysort": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.1.0.tgz", + "integrity": "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==", + "license": "MIT" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", diff --git a/package.json b/package.json index 32863fb9154..96ae2e85b68 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "cookies-js": "^1.2.2", "floating-vue": "^5.2.2", "fuse.js": "^7.0.0", + "fuzzysort": "^3.1.0", "highlight.js": "^11.7.0", "imask": "^7.6.1", "laravel-echo": "^1.16.0", diff --git a/resources/js/components/CommandPalette.vue b/resources/js/components/CommandPalette.vue index 1ebd3cb332f..1e0ac2dfcda 100644 --- a/resources/js/components/CommandPalette.vue +++ b/resources/js/components/CommandPalette.vue @@ -1,8 +1,23 @@ From ea3226f1f09c2a69a7d36ef43fdb79244f1d9f8b Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Fri, 4 Apr 2025 17:47:05 -0400 Subject: [PATCH 17/70] Composition. --- resources/js/components/CommandPalette.vue | 79 ++++++++++------------ 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/resources/js/components/CommandPalette.vue b/resources/js/components/CommandPalette.vue index 1e0ac2dfcda..39c2b87e1b9 100644 --- a/resources/js/components/CommandPalette.vue +++ b/resources/js/components/CommandPalette.vue @@ -1,3 +1,39 @@ + + - - From 153f0427a080af31476895bdb651f2071dc451cf Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 02:25:16 -0400 Subject: [PATCH 18/70] Reka Combobox proof of concept. --- package-lock.json | 174 +++++++++++++++++- package.json | 1 + resources/js/bootstrap/components.js | 3 +- .../js/components/CommandPaletteReka.vue | 124 +++++++++++++ 4 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 resources/js/components/CommandPaletteReka.vue diff --git a/package-lock.json b/package-lock.json index 7d0df05505f..4ca1bf14768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "pusher-js": "^8.4.0-rc2", "qs": "^6.9.7", "read-time-estimate": "0.0.3", + "reka-ui": "^2.2.0", "resize-observer-polyfill": "^1.5.1", "semver": "^7.7.1", "speakingurl": "^14.0.1", @@ -640,18 +641,31 @@ } }, "node_modules/@floating-ui/dom": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", - "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.4" + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/utils": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", - "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@floating-ui/vue": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.6.tgz", + "integrity": "sha512-XFlUzGHGv12zbgHNk5FN2mUB7ROul3oG2ENdTpWdE+qMFxyNxWSRmsoyhiEnpmabNm6WnUvR1OvJfUfN4ojC1A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0", + "@floating-ui/utils": "^0.2.9", + "vue-demi": ">=0.13.0" + } }, "node_modules/@he-tree/dnd-utils": { "version": "0.1.0-alpha.4", @@ -730,6 +744,24 @@ "vue": "^3.2.37" } }, + "node_modules/@internationalized/date": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz", + "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.0.tgz", + "integrity": "sha512-PtrRcJVy7nw++wn4W2OuePQQfTqDzfusSuY1QTtui4wa7r+rGVtR75pO8CyKvHvzyQYi3Q1uO5sY0AsB4e65Bw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1274,6 +1306,15 @@ "node": ">= 6" } }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/container-queries": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz", @@ -1298,6 +1339,32 @@ "tailwindcss": ">=3.0.0 || insiders" } }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.6.tgz", + "integrity": "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/vue-virtual": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.6.tgz", + "integrity": "sha512-GYdZ3SJBQPzgxhuCE2fvpiH46qzHiVx5XzBSdtESgiqh4poj8UgckjGWYEhxaBbcVt1oLzh1m3Ql4TyH32TOzQ==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.0.0" + } + }, "node_modules/@tiptap/core": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.8.0.tgz", @@ -2363,6 +2430,18 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -2987,6 +3066,12 @@ "node": ">=8" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4545,6 +4630,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -5376,6 +5467,69 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/reka-ui": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.2.0.tgz", + "integrity": "sha512-eeRrLI4LwJ6dkdwks6KFNKGs0+beqZlHO3JMHen7THDTh+yJ5Z0KNwONmOhhV/0hZC2uJCEExgG60QPzGstkQg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.13", + "@floating-ui/vue": "^1.1.6", + "@internationalized/date": "^3.5.0", + "@internationalized/number": "^3.5.0", + "@tanstack/vue-virtual": "^3.12.0", + "@vueuse/core": "^12.5.0", + "@vueuse/shared": "^12.5.0", + "aria-hidden": "^1.2.4", + "defu": "^6.1.4", + "ohash": "^2.0.11" + }, + "peerDependencies": { + "vue": ">= 3.2.0" + } + }, + "node_modules/reka-ui/node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/reka-ui/node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6080,6 +6234,12 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", diff --git a/package.json b/package.json index 96ae2e85b68..bd715af4539 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "pusher-js": "^8.4.0-rc2", "qs": "^6.9.7", "read-time-estimate": "0.0.3", + "reka-ui": "^2.2.0", "resize-observer-polyfill": "^1.5.1", "semver": "^7.7.1", "speakingurl": "^14.0.1", diff --git a/resources/js/bootstrap/components.js b/resources/js/bootstrap/components.js index 9da02573eb4..e00ab2f99a9 100644 --- a/resources/js/bootstrap/components.js +++ b/resources/js/bootstrap/components.js @@ -64,6 +64,7 @@ import BlueprintResetter from '../components/blueprints/BlueprintResetter.vue'; import { defineAsyncComponent } from 'vue'; import DateTime from '../components/DateTime.vue'; import CommandPalette from '../components/CommandPalette.vue'; +import CommandPaletteReka from '../components/CommandPaletteReka.vue'; export default function registerGlobalComponents(app) { // Core @@ -154,5 +155,5 @@ export default function registerGlobalComponents(app) { app.component('blueprint-resetter', BlueprintResetter); - app.component('command-palette', CommandPalette); + app.component('command-palette', CommandPaletteReka); } diff --git a/resources/js/components/CommandPaletteReka.vue b/resources/js/components/CommandPaletteReka.vue new file mode 100644 index 00000000000..70c9cf22ef5 --- /dev/null +++ b/resources/js/components/CommandPaletteReka.vue @@ -0,0 +1,124 @@ + + + From 3518f8d9a07c79820264c0aa0afe759be87daae9 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 08:16:31 -0400 Subject: [PATCH 19/70] Delete initial prototype. --- resources/js/bootstrap/components.js | 1 - resources/js/components/CommandPalette.vue | 51 ---------------------- 2 files changed, 52 deletions(-) delete mode 100644 resources/js/components/CommandPalette.vue diff --git a/resources/js/bootstrap/components.js b/resources/js/bootstrap/components.js index e00ab2f99a9..ecf9852c8b6 100644 --- a/resources/js/bootstrap/components.js +++ b/resources/js/bootstrap/components.js @@ -63,7 +63,6 @@ import CodeBlock from '../components/CodeBlock.vue'; import BlueprintResetter from '../components/blueprints/BlueprintResetter.vue'; import { defineAsyncComponent } from 'vue'; import DateTime from '../components/DateTime.vue'; -import CommandPalette from '../components/CommandPalette.vue'; import CommandPaletteReka from '../components/CommandPaletteReka.vue'; export default function registerGlobalComponents(app) { diff --git a/resources/js/components/CommandPalette.vue b/resources/js/components/CommandPalette.vue deleted file mode 100644 index 39c2b87e1b9..00000000000 --- a/resources/js/components/CommandPalette.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - From cc351d226efe0f3aed2b5a25c098c182f378848c Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 08:18:18 -0400 Subject: [PATCH 20/70] Delete blade view partial. --- resources/views/layout.blade.php | 2 -- resources/views/partials/command-palette.blade.php | 12 ------------ 2 files changed, 14 deletions(-) delete mode 100644 resources/views/partials/command-palette.blade.php diff --git a/resources/views/layout.blade.php b/resources/views/layout.blade.php index 8050779d161..196d6bb7926 100644 --- a/resources/views/layout.blade.php +++ b/resources/views/layout.blade.php @@ -64,8 +64,6 @@ class="@yield('content-class') pt-14" - @include('statamic::partials.command-palette') - @include('statamic::partials.scripts') @yield('scripts') diff --git a/resources/views/partials/command-palette.blade.php b/resources/views/partials/command-palette.blade.php deleted file mode 100644 index 13d88efcd30..00000000000 --- a/resources/views/partials/command-palette.blade.php +++ /dev/null @@ -1,12 +0,0 @@ -@php - use function Statamic\trans as __; - ray($commandPalette); -@endphp - -@section('command-palette') - -@stop - -@yield('command-palette') From d924be4b5aea829a136491109efe7b990bfc3b11 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 08:53:50 -0400 Subject: [PATCH 21/70] Ditch view composer in favour of ajax on mount. --- .../View/Composers/CommandPaletteComposer.php | 19 ------------------- src/Providers/CpServiceProvider.php | 2 -- 2 files changed, 21 deletions(-) delete mode 100644 src/Http/View/Composers/CommandPaletteComposer.php diff --git a/src/Http/View/Composers/CommandPaletteComposer.php b/src/Http/View/Composers/CommandPaletteComposer.php deleted file mode 100644 index 3be6394a950..00000000000 --- a/src/Http/View/Composers/CommandPaletteComposer.php +++ /dev/null @@ -1,19 +0,0 @@ -with('commandPalette', Blink::once('command-palette-composer', fn () => CommandPalette::build())); - } -} diff --git a/src/Providers/CpServiceProvider.php b/src/Providers/CpServiceProvider.php index 01a15ef6ccc..a7f160658df 100644 --- a/src/Providers/CpServiceProvider.php +++ b/src/Providers/CpServiceProvider.php @@ -12,7 +12,6 @@ use Statamic\Facades\User; use Statamic\Fieldtypes\Sets; use Statamic\Http\Middleware\CP\StartSession; -use Statamic\Http\View\Composers\CommandPaletteComposer; use Statamic\Http\View\Composers\CustomLogoComposer; use Statamic\Http\View\Composers\FieldComposer; use Statamic\Http\View\Composers\JavascriptComposer; @@ -33,7 +32,6 @@ public function boot() View::composer(SessionExpiryComposer::VIEWS, SessionExpiryComposer::class); View::composer(JavascriptComposer::VIEWS, JavascriptComposer::class); View::composer(NavComposer::VIEWS, NavComposer::class); - View::composer(CommandPaletteComposer::VIEWS, CommandPaletteComposer::class); View::composer(CustomLogoComposer::VIEWS, CustomLogoComposer::class); Blade::directive('cp_svg', function ($expression) { From 739a27cd889da53aa452e19a9aca798dd5aafed5 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 11:00:58 -0400 Subject: [PATCH 22/70] Make icon configurable. --- src/CommandPalette/Command.php | 10 ++++++++++ src/CommandPalette/Palette.php | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/CommandPalette/Command.php b/src/CommandPalette/Command.php index 03d707087ca..2774888bdad 100644 --- a/src/CommandPalette/Command.php +++ b/src/CommandPalette/Command.php @@ -4,6 +4,8 @@ abstract class Command { + protected $icon = 'entry'; + public function __construct(protected string $text, protected Category $category) { // @@ -14,6 +16,13 @@ public function __construct(protected string $text, protected Category $category // // TODO: Implement keyboard shortcut configuration... // } + public function icon(string $icon): self + { + $this->icon = $icon; + + return $this; + } + public function type(): string { return strtolower((new \ReflectionClass(get_called_class()))->getShortName()); @@ -24,6 +33,7 @@ public function toArray(): array return [ 'type' => $this->type(), 'category' => $this->category->name, + 'icon' => $this->icon, 'text' => $this->text, ]; } diff --git a/src/CommandPalette/Palette.php b/src/CommandPalette/Palette.php index 5ac9fb86659..122d9d1a250 100644 --- a/src/CommandPalette/Palette.php +++ b/src/CommandPalette/Palette.php @@ -37,21 +37,21 @@ protected function buildActions(): self (new Link( text: 'Save', category: Category::Actions, - ))->url('/cp') + ))->url('/cp')->icon('save') ); $this->addCommand( (new Link( text: 'Duplicate', category: Category::Actions, - ))->url('/cp') + ))->url('/cp')->icon('duplicate') ); $this->addCommand( (new Link( text: 'Delete', category: Category::Actions, - ))->url('/cp') + ))->url('/cp')->icon('delete') ); return $this; From 633e492b0a02bc05ef56e290b495e62a060f6036 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 11:01:30 -0400 Subject: [PATCH 23/70] Use nicer arrow in nav link items. --- src/CP/Navigation/NavBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CP/Navigation/NavBuilder.php b/src/CP/Navigation/NavBuilder.php index ed7a244e677..6b8e7445efd 100644 --- a/src/CP/Navigation/NavBuilder.php +++ b/src/CP/Navigation/NavBuilder.php @@ -1101,7 +1101,7 @@ protected function addToCommandPalette(): self protected static function transformToLink(NavItem $item): Link { $text = $item->section() !== 'Top Level' - ? __($item->section()).' > '.__($item->display()) + ? __($item->section()).' » '.__($item->display()) : __($item->display()); $link = new Link( From be9377e4c19da4efe45b2fc3b113548fa41ac286 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 11:03:57 -0400 Subject: [PATCH 24/70] Delete reka prototype. --- resources/js/bootstrap/components.js | 3 - .../js/components/CommandPaletteReka.vue | 124 ------------------ 2 files changed, 127 deletions(-) delete mode 100644 resources/js/components/CommandPaletteReka.vue diff --git a/resources/js/bootstrap/components.js b/resources/js/bootstrap/components.js index ecf9852c8b6..d5d7c3f77f3 100644 --- a/resources/js/bootstrap/components.js +++ b/resources/js/bootstrap/components.js @@ -63,7 +63,6 @@ import CodeBlock from '../components/CodeBlock.vue'; import BlueprintResetter from '../components/blueprints/BlueprintResetter.vue'; import { defineAsyncComponent } from 'vue'; import DateTime from '../components/DateTime.vue'; -import CommandPaletteReka from '../components/CommandPaletteReka.vue'; export default function registerGlobalComponents(app) { // Core @@ -153,6 +152,4 @@ export default function registerGlobalComponents(app) { app.component('stack-test', StackTest); app.component('blueprint-resetter', BlueprintResetter); - - app.component('command-palette', CommandPaletteReka); } diff --git a/resources/js/components/CommandPaletteReka.vue b/resources/js/components/CommandPaletteReka.vue deleted file mode 100644 index 70c9cf22ef5..00000000000 --- a/resources/js/components/CommandPaletteReka.vue +++ /dev/null @@ -1,124 +0,0 @@ - - - From 862bbb9c39349ec4bd410913c3474b587ac7f157 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 11:06:03 -0400 Subject: [PATCH 25/70] Self. --- src/CommandPalette/Link.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CommandPalette/Link.php b/src/CommandPalette/Link.php index 4d2f74f6c22..c297c7b73b7 100644 --- a/src/CommandPalette/Link.php +++ b/src/CommandPalette/Link.php @@ -6,7 +6,7 @@ class Link extends Command { protected $url; - public function url(string $url) + public function url(string $url): self { $this->url = $url; From ce79411df24ad5581c06125428fd3cd894fcf525 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sat, 5 Apr 2025 11:12:01 -0400 Subject: [PATCH 26/70] =?UTF-8?q?Wire=20up=20reka=20combobox=20to=20Jack?= =?UTF-8?q?=E2=80=99s=20UI=20=F0=9F=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/ui/CommandPalette/Index.vue | 150 ++++++++++++++---- .../js/components/ui/CommandPalette/Item.vue | 4 +- 2 files changed, 120 insertions(+), 34 deletions(-) diff --git a/resources/js/components/ui/CommandPalette/Index.vue b/resources/js/components/ui/CommandPalette/Index.vue index d373dd3084c..d3e7b404b9b 100644 --- a/resources/js/components/ui/CommandPalette/Index.vue +++ b/resources/js/components/ui/CommandPalette/Index.vue @@ -1,12 +1,75 @@