mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-22 16:23:51 +00:00
feat(ui): OS-aware search shortcuts and mobile settings titles
Show ⌘K or Ctrl+K by platform in the sidebar and command palette. Add mobile-only section headers and descriptions on settings layouts.
This commit is contained in:
+2
-2
@@ -496,7 +496,7 @@ top-anchored overlay:
|
||||
|
||||
- elevated shell with hairline ring and modal shadow (not a heavy floating card);
|
||||
- recessed-neutral header strip with outline search glyph and 14px input;
|
||||
- compact `⌘K` / `/` / `ESC` kbd chips matching the sidebar search trigger;
|
||||
- compact OS-aware mod+K (`⌘K` on macOS, `Ctrl+K` on Windows/Linux) / `/` / `ESC` kbd chips matching the sidebar search trigger;
|
||||
- nested base-color results body with group labels in sentence case;
|
||||
- dense result rows as inset 6px-radius pills (listbox anatomy), not full-bleed
|
||||
bars with global focus rings;
|
||||
@@ -511,7 +511,7 @@ top-anchored overlay:
|
||||
`application-settings-section` layer-card shell.
|
||||
|
||||
Preserve keyboard navigation (arrow keys, Enter via focused links, Escape to
|
||||
clear then close), `/` and `⌘K` open shortcuts, and the multi-step
|
||||
clear then close), `/` and mod+K (⌘K / Ctrl+K by OS) open shortcuts, and the multi-step
|
||||
server → destination → project → environment create flow.
|
||||
|
||||
### Toasts
|
||||
|
||||
@@ -1079,6 +1079,33 @@ body.terminal-is-fullscreen .terminal-fullscreen-shell [data-terminal-mobile-too
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.settings-mobile-header {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.settings-mobile-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.75rem;
|
||||
letter-spacing: -0.025em;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.dark .settings-mobile-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.settings-mobile-description {
|
||||
margin-top: 0.25rem;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.25rem;
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
.dark .settings-mobile-description {
|
||||
color: var(--coollabs-subtle);
|
||||
}
|
||||
|
||||
/* All settings sidebars share one alignment rule. Keep the sticky offset equal
|
||||
to the desktop main-content top padding so the menu does not jump upward
|
||||
when it changes from its natural position to sticky positioning. */
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
@mouseleave="tooltip.show = false"
|
||||
x-data="{
|
||||
tooltip: { text: '', x: 0, y: 0, show: false },
|
||||
// macOS/iOS use ⌘; Windows/Linux use Ctrl+
|
||||
modKeyLabel: (() => {
|
||||
const platform = navigator.userAgentData?.platform || navigator.platform || '';
|
||||
const ua = navigator.userAgent || '';
|
||||
return /Mac|iPhone|iPad|iPod/i.test(platform) || /Mac OS X|Macintosh/i.test(ua) ? '⌘' : 'Ctrl+';
|
||||
})(),
|
||||
init() {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||||
const userSettings = localStorage.getItem('theme');
|
||||
@@ -45,7 +51,8 @@
|
||||
{{-- Search is only useful when workspace resources are available --}}
|
||||
@if (isSubscribed() || ! isCloud())
|
||||
<div class="px-1 pb-3" :class="collapsed && 'lg:px-0 lg:flex lg:justify-center'">
|
||||
<button @click="$dispatch('open-global-search')" type="button" title="Search (Press / or ⌘K)"
|
||||
<button @click="$dispatch('open-global-search')" type="button"
|
||||
:title="'Search (Press / or ' + modKeyLabel + 'K)'"
|
||||
class="menu-item justify-between !bg-neutral-100 dark:!bg-white/[0.04] hover:!bg-neutral-200 dark:hover:!bg-white/[0.07] !text-fg-faint"
|
||||
:class="collapsed && 'lg:w-8 lg:justify-center lg:px-0'">
|
||||
<span class="flex items-center gap-2.5 min-w-0">
|
||||
@@ -53,7 +60,7 @@
|
||||
<span class="menu-item-label" :class="collapsed && 'lg:hidden'">Search</span>
|
||||
</span>
|
||||
<kbd class="px-1.5 py-0.5 text-[11px] font-medium text-fg-faint bg-neutral-200 dark:bg-white/[0.06] rounded-md border border-transparent dark:border-white/5"
|
||||
:class="collapsed && 'lg:hidden'">⌘K</kbd>
|
||||
:class="collapsed && 'lg:hidden'" x-text="modKeyLabel + 'K'"></kbd>
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
];
|
||||
@endphp
|
||||
|
||||
<section class="application-settings-workspace mt-4 w-full max-w-[1180px] lg:mt-0">
|
||||
<section class="application-settings-workspace w-full max-w-[1180px]">
|
||||
<header class="settings-mobile-header xl:hidden">
|
||||
<h1 class="settings-mobile-title">Notifications</h1>
|
||||
<p class="settings-mobile-description">Configure how your team receives deployment and system alerts.</p>
|
||||
</header>
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-10">
|
||||
<aside class="application-settings-navigation min-w-0 xl:self-start">
|
||||
<nav aria-label="Notification settings"
|
||||
|
||||
@@ -27,7 +27,11 @@
|
||||
])->filter();
|
||||
@endphp
|
||||
|
||||
<section class="application-settings-workspace mt-4 w-full max-w-[1180px] lg:mt-0">
|
||||
<section class="application-settings-workspace w-full max-w-[1180px]">
|
||||
<header class="settings-mobile-header xl:hidden">
|
||||
<h1 class="settings-mobile-title">Keys & Tokens</h1>
|
||||
<p class="settings-mobile-description">Manage SSH keys, cloud credentials, and API access tokens.</p>
|
||||
</header>
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-10">
|
||||
<aside class="application-settings-navigation min-w-0 xl:self-start">
|
||||
<nav aria-label="Keys and tokens"
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
];
|
||||
@endphp
|
||||
|
||||
<section class="application-settings-workspace mt-4 w-full max-w-[1180px] lg:mt-0">
|
||||
<section class="application-settings-workspace w-full max-w-[1180px]">
|
||||
<header class="settings-mobile-header xl:hidden">
|
||||
<h1 class="settings-mobile-title">Instance Settings</h1>
|
||||
<p class="settings-mobile-description">Configure global settings for this Coolify instance.</p>
|
||||
</header>
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-10">
|
||||
<aside class="application-settings-navigation min-w-0 xl:self-start">
|
||||
<nav aria-label="Instance settings"
|
||||
|
||||
@@ -28,7 +28,11 @@
|
||||
])->filter();
|
||||
@endphp
|
||||
|
||||
<section class="application-settings-workspace mt-4 w-full max-w-[1180px] lg:mt-0">
|
||||
<section class="application-settings-workspace w-full max-w-[1180px]">
|
||||
<header class="settings-mobile-header xl:hidden">
|
||||
<h1 class="settings-mobile-title">Team</h1>
|
||||
<p class="settings-mobile-description">Manage your team, members, and access settings.</p>
|
||||
</header>
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-10">
|
||||
<aside class="application-settings-navigation min-w-0 xl:self-start">
|
||||
<nav aria-label="Team settings"
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
searchQuery: '',
|
||||
creatableItems: [],
|
||||
isCreateMode: false,
|
||||
// macOS/iOS use ⌘; Windows/Linux use Ctrl+
|
||||
modKeyLabel: (() => {
|
||||
const platform = navigator.userAgentData?.platform || navigator.platform || '';
|
||||
const ua = navigator.userAgent || '';
|
||||
return /Mac|iPhone|iPad|iPod/i.test(platform) || /Mac OS X|Macintosh/i.test(ua) ? '⌘' : 'Ctrl+';
|
||||
})(),
|
||||
|
||||
// Client-side search function
|
||||
get searchResults() {
|
||||
@@ -313,7 +319,7 @@
|
||||
class="command-palette-input" autocomplete="off" spellcheck="false" />
|
||||
<div class="command-palette-shortcuts">
|
||||
<span class="command-palette-kbd">/</span>
|
||||
<span class="command-palette-kbd">⌘K</span>
|
||||
<span class="command-palette-kbd" x-text="modKeyLabel + 'K'"></span>
|
||||
<button type="button" @click="closeModal()" class="command-palette-kbd" title="Close">
|
||||
ESC
|
||||
</button>
|
||||
|
||||
@@ -33,3 +33,25 @@ it('draws a single border between the desktop sidebar and main content', functio
|
||||
expect($navbar)->toContain('border-r border-neutral-200')
|
||||
->and($layout)->not->toContain('lg:border-l border-neutral-200');
|
||||
});
|
||||
|
||||
it('shows section titles and descriptions above settings navigation on smaller screens', function (string $view, string $title, string $description) {
|
||||
$layout = file_get_contents(resource_path("views/components/{$view}"));
|
||||
$css = file_get_contents(resource_path('css/app.css'));
|
||||
|
||||
expect($layout)
|
||||
->toContain('<header class="settings-mobile-header xl:hidden">')
|
||||
->toContain("<h1 class=\"settings-mobile-title\">{$title}</h1>")
|
||||
->toContain("<p class=\"settings-mobile-description\">{$description}</p>")
|
||||
->toContain('<section class="application-settings-workspace w-full max-w-[1180px]">')
|
||||
->and($css)
|
||||
->toContain('.settings-mobile-title')
|
||||
->toContain('.settings-mobile-description')
|
||||
->toContain('color: #000000;')
|
||||
->toContain('.dark .settings-mobile-title')
|
||||
->toContain('color: #ffffff;');
|
||||
})->with([
|
||||
['notification/settings-layout.blade.php', 'Notifications', 'Configure how your team receives deployment and system alerts.'],
|
||||
['security/settings-layout.blade.php', 'Keys & Tokens', 'Manage SSH keys, cloud credentials, and API access tokens.'],
|
||||
['settings/layout.blade.php', 'Instance Settings', 'Configure global settings for this Coolify instance.'],
|
||||
['team/settings-layout.blade.php', 'Team', 'Manage your team, members, and access settings.'],
|
||||
]);
|
||||
|
||||
@@ -57,7 +57,8 @@ test('unsubscribed cloud sidebar does not expose global search', function () {
|
||||
|
||||
$response->assertSuccessful();
|
||||
// Search button is gated; global-search listener markup may still be present.
|
||||
$response->assertDontSee('title="Search (Press / or ⌘K)"', false);
|
||||
// Search button is gated; OS-aware title uses Alpine :title, so assert on the trigger instead.
|
||||
$response->assertDontSee("\$dispatch('open-global-search')", false);
|
||||
$response->assertDontSee('>Search</span>', false);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
it('uses OS-aware modifier key labels in the global search palette', function () {
|
||||
$bladeFile = file_get_contents(__DIR__.'/../../resources/views/livewire/global-search.blade.php');
|
||||
|
||||
expect($bladeFile)
|
||||
->toContain('modKeyLabel')
|
||||
->toContain("return /Mac|iPhone|iPad|iPod/i.test(platform) || /Mac OS X|Macintosh/i.test(ua) ? '⌘' : 'Ctrl+';")
|
||||
->toContain("x-text=\"modKeyLabel + 'K'\"")
|
||||
->not->toContain('>⌘K</span>');
|
||||
});
|
||||
|
||||
it('uses OS-aware modifier key labels in the sidebar search trigger', function () {
|
||||
$bladeFile = file_get_contents(__DIR__.'/../../resources/views/components/navbar.blade.php');
|
||||
|
||||
expect($bladeFile)
|
||||
->toContain('modKeyLabel')
|
||||
->toContain("return /Mac|iPhone|iPad|iPod/i.test(platform) || /Mac OS X|Macintosh/i.test(ua) ? '⌘' : 'Ctrl+';")
|
||||
->toContain(":title=\"'Search (Press / or ' + modKeyLabel + 'K)'\"")
|
||||
->toContain("x-text=\"modKeyLabel + 'K'\"")
|
||||
->not->toContain('>⌘K</kbd>')
|
||||
->not->toContain('title="Search (Press / or ⌘K)"');
|
||||
});
|
||||
Reference in New Issue
Block a user