diff --git a/messages/en-US.json b/messages/en-US.json
index af0c9546e..5d3b891c6 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -1519,7 +1519,7 @@
"navbar": "Navigation Menu",
"navbarDescription": "Main navigation menu for the application",
"navbarDocsLink": "Documentation",
- "commandPaletteTitle": "Command palette",
+ "commandPaletteTitle": "Command Palette",
"commandPaletteDescription": "Search for pages, organizations, resources, and actions",
"commandPaletteSearchPlaceholder": "Search pages, resources, actions...",
"commandPaletteNoResults": "No results found.",
@@ -1529,17 +1529,18 @@
"commandPaletteSites": "Sites",
"commandPaletteResources": "Resources",
"commandPaletteUsers": "Users",
- "commandPaletteClients": "Machine clients",
+ "commandPaletteClients": "Machine Clients",
"commandPaletteActions": "Actions",
- "commandPaletteCreateSite": "Create site",
- "commandPaletteCreateProxyResource": "Create public resource",
- "commandPaletteCreateUser": "Create user",
- "commandPaletteCreateApiKey": "Create API key",
- "commandPaletteCreateMachineClient": "Create machine client",
- "commandPaletteCreateAlertRule": "Create alert rule",
- "commandPaletteCreateIdentityProvider": "Create identity provider",
- "commandPaletteToggleTheme": "Toggle theme",
- "commandPaletteChooseOrganization": "Choose organization",
+ "commandPaletteCreateSite": "Create Site",
+ "commandPaletteCreateProxyResource": "Create Public Resource",
+ "commandPaletteCreatePrivateResource": "Create Private Resource",
+ "commandPaletteCreateUser": "Create User",
+ "commandPaletteCreateApiKey": "Create API Key",
+ "commandPaletteCreateMachineClient": "Create Machine Client",
+ "commandPaletteCreateAlertRule": "Create Alert Rule",
+ "commandPaletteCreateIdentityProvider": "Create Identity Provider",
+ "commandPaletteToggleTheme": "Toggle Theme",
+ "commandPaletteChooseOrganization": "Choose Organization",
"commandPaletteShortcutMac": "⌘K",
"commandPaletteShortcutWindows": "Ctrl K",
"otpErrorEnable": "Unable to enable 2FA",
@@ -1620,7 +1621,7 @@
"sidebarBillingAndLicenses": "Billing & Licenses",
"sidebarLogsAnalytics": "Analytics",
"commandSites": "Sites",
- "commandActionModeInfo": "Type \">\" to open action mode",
+ "commandActionModeInfo": "Type \">\" To Open Action Mode",
"commandResources": "Resources",
"commandProxyResources": "Public Resources",
"commandClientResources": "Private Resources",
@@ -1655,6 +1656,8 @@
"commandBilling": "Billing",
"commandEnterpriseLicenses": "Licenses",
"commandSettings": "Settings",
+ "commandLauncher": "Launcher",
+ "commandResourceLauncher": "Resource Launcher",
"commandSearchResults": "Search Results",
"alertingTitle": "Alerting",
"alertingDescription": "Define sources, triggers, and actions for notifications",
@@ -3262,7 +3265,7 @@
"editInternalResourceDialogAddUsers": "Add Users",
"editInternalResourceDialogAddClients": "Add Clients",
"editInternalResourceDialogDestinationLabel": "Destination",
- "editInternalResourceDialogDestinationDescription": "Choose where this resource runs and how clients reach it. Selecting multiple sites will create a high availability resource that can be accessed from any of the selected sites.",
+ "editInternalResourceDialogDestinationDescription": "Choose where this resource runs and how clients reach it",
"internalResourceFormMultiSiteRoutingHelp": "Selecting multiple sites enables resilient routing and failover for high availability.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Learn more",
"editInternalResourceDialogPortRestrictionsDescription": "Restrict access to specific TCP/UDP ports or allow/block all ports.",
diff --git a/src/app/[orgId]/page.tsx b/src/app/[orgId]/page.tsx
index d543b2752..614db095d 100644
--- a/src/app/[orgId]/page.tsx
+++ b/src/app/[orgId]/page.tsx
@@ -1,9 +1,11 @@
import { Layout } from "@app/components/Layout";
import ResourceLauncher from "@app/components/resource-launcher/ResourceLauncher";
+import { commandBarNavSections } from "@app/app/navigation";
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { fetchLauncherPageData } from "@app/lib/launcherServerData";
import { verifySession } from "@app/lib/auth/verifySession";
+import { pullEnv } from "@app/lib/pullEnv";
import UserProvider from "@app/providers/UserProvider";
import { ListUserOrgsResponse } from "@server/routers/org";
import { GetOrgOverviewResponse } from "@server/routers/org/getOrgOverview";
@@ -57,6 +59,8 @@ export default async function OrgPage(props: OrgPageProps) {
} catch (e) {}
const isAdminOrOwner = Boolean(overview?.isAdmin || overview?.isOwner);
+ const env = pullEnv();
+ const primaryOrg = orgs.find((o) => o.orgId === orgId)?.isPrimaryOrg;
const searchParams = new URLSearchParams(await props.searchParams);
const launcherData = overview
@@ -73,6 +77,9 @@ export default async function OrgPage(props: OrgPageProps) {
orgId={orgId}
orgs={orgs}
navItems={[]}
+ commandNavItems={commandBarNavSections(env, {
+ isPrimaryOrg: primaryOrg
+ })}
showSidebar={false}
launcherMode
showViewAsAdmin={isAdminOrOwner}
diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx
index 04c5ee684..b0650ce05 100644
--- a/src/app/navigation.tsx
+++ b/src/app/navigation.tsx
@@ -15,6 +15,7 @@ import {
GlobeLock,
KeyRound,
Laptop,
+ LayoutGrid,
Link as LinkIcon,
Logs,
MonitorUp,
@@ -49,7 +50,7 @@ export const orgLangingNavItems: SidebarNavItem[] = [
{
title: "sidebarAccount",
href: "/{orgId}",
- icon:
+ icon:
}
];
@@ -360,6 +361,16 @@ export const commandBarNavSections = (
env?: Env,
options?: OrgNavSectionsOptions
): CommandBarNavSection[] => [
+ {
+ heading: "commandLauncher",
+ items: [
+ {
+ title: "commandResourceLauncher",
+ href: "/{orgId}",
+ icon:
+ }
+ ]
+ },
{
heading: "network",
items: [
diff --git a/src/components/LayoutHeader.tsx b/src/components/LayoutHeader.tsx
index cd130f5fe..2440d2494 100644
--- a/src/components/LayoutHeader.tsx
+++ b/src/components/LayoutHeader.tsx
@@ -105,8 +105,11 @@ export function LayoutHeader({
{showTopBar && (
)}
diff --git a/src/components/LayoutMobileMenu.tsx b/src/components/LayoutMobileMenu.tsx
index 52536820e..ba6ae8f75 100644
--- a/src/components/LayoutMobileMenu.tsx
+++ b/src/components/LayoutMobileMenu.tsx
@@ -17,7 +17,7 @@ import {
import { useUserContext } from "@app/hooks/useUserContext";
import { cn } from "@app/lib/cn";
import { ListUserOrgsResponse } from "@server/routers/org";
-import { Menu, Server, Settings, SquareMousePointer } from "lucide-react";
+import { Menu, Server, Settings, LayoutGrid } from "lucide-react";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { usePathname } from "next/navigation";
@@ -151,7 +151,7 @@ export function LayoutMobileMenu({
}
>
-
+
{t(
@@ -207,7 +207,11 @@ export function LayoutMobileMenu({
{showTopBar && (
diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx
index c42e2c1fa..df1eb45d3 100644
--- a/src/components/LayoutSidebar.tsx
+++ b/src/components/LayoutSidebar.tsx
@@ -21,9 +21,9 @@ import { ListUserOrgsResponse } from "@server/routers/org";
import {
ArrowRight,
ExternalLink,
+ LayoutGrid,
PanelRightOpen,
- Server,
- SquareMousePointer
+ Server
} from "lucide-react";
import { useTranslations } from "next-intl";
import dynamic from "next/dynamic";
@@ -186,7 +186,7 @@ export function LayoutSidebar({
)}
>
-
+
@@ -195,7 +195,9 @@ export function LayoutSidebar({
sideOffset={8}
>
- {t("resourceSidebarLauncherTitle")}
+ {t(
+ "resourceSidebarLauncherTitle"
+ )}
@@ -208,7 +210,7 @@ export function LayoutSidebar({
)}
>
-
+
{t("resourceSidebarLauncherTitle")}
diff --git a/src/components/command-palette/CommandPalette.tsx b/src/components/command-palette/CommandPalette.tsx
index 514ef6e1a..8b0a044d2 100644
--- a/src/components/command-palette/CommandPalette.tsx
+++ b/src/components/command-palette/CommandPalette.tsx
@@ -13,7 +13,9 @@ import {
CommandList,
CommandSeparator
} from "@app/components/ui/command";
+import { Button } from "@app/components/ui/button";
import { cn } from "@app/lib/cn";
+import { useMediaQuery } from "@app/hooks/useMediaQuery";
import { ListUserOrgsResponse } from "@server/routers/org";
import {
ChevronRightIcon,
@@ -22,7 +24,8 @@ import {
LaptopIcon,
PlugIcon,
ServerIcon,
- UserIcon
+ UserIcon,
+ X
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
@@ -34,6 +37,7 @@ import React, {
useMemo,
useState
} from "react";
+import { useCanUseCommandPalette } from "./useCanUseCommandPalette";
import { useCommandPaletteActions } from "./useCommandPaletteActions";
import { useCommandPaletteNavigation } from "./useCommandPaletteNavigation";
import { useCommandPaletteSearch } from "./useCommandPaletteSearch";
@@ -57,6 +61,7 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
const router = useRouter();
const { open, setOpen } = useCommandPalette();
const [search, setSearch] = useState("");
+ const isDesktop = useMediaQuery("(min-width: 768px)");
const isActionMode = search.startsWith(">");
@@ -134,6 +139,20 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
value={search}
onValueChange={setSearch}
isLoading={!isActionMode && shouldSearch && isLoading}
+ trailing={
+ !isDesktop ? (
+
+ ) : undefined
+ }
/>
{t("commandPaletteNoResults")}
@@ -427,13 +446,14 @@ function isEditableTarget(target: EventTarget | null) {
);
}
-export function CommandPaletteProvider({
+function CommandPaletteProviderInner({
children,
orgId,
orgs,
navItems
}: CommandPaletteProviderProps) {
const [open, setOpen] = useState(false);
+ const canUseCommandPalette = useCanUseCommandPalette(orgId, orgs);
const toggle = useCallback(() => {
setOpen((current) => !current);
@@ -449,6 +469,10 @@ export function CommandPaletteProvider({
);
useEffect(() => {
+ if (!canUseCommandPalette) {
+ return;
+ }
+
function onKeyDown(event: KeyboardEvent) {
if (
event.key.toLowerCase() !== "k" ||
@@ -467,12 +491,18 @@ export function CommandPaletteProvider({
document.addEventListener("keydown", onKeyDown);
return () => document.removeEventListener("keydown", onKeyDown);
- }, [open, toggle]);
+ }, [canUseCommandPalette, open, toggle]);
return (
{children}
-
+ {canUseCommandPalette ? (
+
+ ) : null}
);
}
+
+export function CommandPaletteProvider(props: CommandPaletteProviderProps) {
+ return ;
+}
diff --git a/src/components/command-palette/CommandPaletteTrigger.tsx b/src/components/command-palette/CommandPaletteTrigger.tsx
index 785013ae2..02913dda7 100644
--- a/src/components/command-palette/CommandPaletteTrigger.tsx
+++ b/src/components/command-palette/CommandPaletteTrigger.tsx
@@ -6,11 +6,15 @@ import { cn } from "@app/lib/cn";
import { Search } from "lucide-react";
import { useEffect, useState } from "react";
import { useTranslations } from "next-intl";
+import { ListUserOrgsResponse } from "@server/routers/org";
import { useCommandPalette } from "./CommandPalette";
+import { useCanUseCommandPalette } from "./useCanUseCommandPalette";
type CommandPaletteTriggerProps = {
variant?: "header" | "mobile";
className?: string;
+ orgId?: string;
+ orgs?: ListUserOrgsResponse["orgs"];
};
function useIsMac() {
@@ -25,11 +29,18 @@ function useIsMac() {
export function CommandPaletteTrigger({
variant = "header",
- className
+ className,
+ orgId,
+ orgs
}: CommandPaletteTriggerProps) {
const t = useTranslations();
const { setOpen } = useCommandPalette();
const isMac = useIsMac();
+ const canUseCommandPalette = useCanUseCommandPalette(orgId, orgs);
+
+ if (!canUseCommandPalette) {
+ return null;
+ }
if (variant === "mobile") {
return (
@@ -49,13 +60,13 @@ export function CommandPaletteTrigger({
);
}