The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit 89b58109a0e6Commit detailsView latest

apps/dashboard/src/components/command-palette.tsx 163 lines · 4.3 KB · tsx
1
import { useEffect, useState } from 'react';
2
import {
3
  BarChart3,
4
  History,
5
  LayoutDashboard,
6
  LogOut,
7
  Moon,
8
  Plus,
9
  ScanLine,
10
  Search,
11
  Settings,
12
  Store,
13
  Sun,
14
} from 'lucide-react';
15
import { Button } from '@/components/ui/button';
16
import {
17
  CommandDialog,
18
  CommandEmpty,
19
  CommandGroup,
20
  CommandInput,
21
  CommandItem,
22
  CommandList,
23
  CommandSeparator,
24
  CommandShortcut,
25
} from '@/components/ui/command';
26
import { pageMeta, pages, type Page } from '@/lib/navigation';
27
28
const pageIcons = {
29
  dashboard: LayoutDashboard,
30
  history: History,
31
  agents: ScanLine,
32
  insights: BarChart3,
33
  connect: Plus,
34
  marketplace: Store,
35
  settings: Settings,
36
} as const;
37
38
export function CommandPalette({
39
  open,
40
  onOpenChange,
41
  navigate,
42
  theme,
43
  onToggleTheme,
44
  onSignOut,
45
  agents,
46
  attention,
47
}: {
48
  open: boolean;
49
  onOpenChange: (open: boolean) => void;
50
  navigate: (page: Page) => void;
51
  theme: 'dark' | 'light';
52
  onToggleTheme: () => void;
53
  onSignOut: () => void;
54
  agents: { agentId: string; name?: string }[];
55
  attention: { setup: number; invites: number };
56
}) {
57
  const go = (page: Page) => {
58
    onOpenChange(false);
59
    navigate(page);
60
  };
61
62
  return (
63
    <CommandDialog
64
      open={open}
65
      onOpenChange={onOpenChange}
66
      title="Jump to"
67
      description="Search pages, agents, and actions"
68
      className="bg-popover"
69
    >
70
      <CommandInput placeholder="Go to a page, agent, or action…" />
71
      <CommandList>
72
        <CommandEmpty>Nothing matches.</CommandEmpty>
73
        {attention.setup + attention.invites > 0 ? (
74
          <CommandGroup heading="Needs you">
75
            {attention.setup > 0 ? (
76
              <CommandItem onSelect={() => go('connect')}>
77
                <Plus />
78
                Pending setup
79
                <CommandShortcut>{attention.setup}</CommandShortcut>
80
              </CommandItem>
81
            ) : null}
82
            {attention.invites > 0 ? (
83
              <CommandItem onSelect={() => go('dashboard')}>
84
                <LayoutDashboard />
85
                Pending invites
86
                <CommandShortcut>{attention.invites}</CommandShortcut>
87
              </CommandItem>
88
            ) : null}
89
          </CommandGroup>
90
        ) : null}
91
        <CommandGroup heading="Pages">
92
          {pages.map((page) => {
93
            const Icon = pageIcons[page];
94
            return (
95
              <CommandItem key={page} value={pageMeta[page].label} onSelect={() => go(page)}>
96
                <Icon />
97
                {pageMeta[page].label}
98
              </CommandItem>
99
            );
100
          })}
101
        </CommandGroup>
102
        {agents.length ? (
103
          <CommandGroup heading="Agents">
104
            {agents.map((agent) => (
105
              <CommandItem
106
                key={agent.agentId}
107
                value={`${agent.name ?? ''} ${agent.agentId}`}
108
                onSelect={() => go('agents')}
109
              >
110
                <ScanLine />
111
                {agent.name || agent.agentId}
112
              </CommandItem>
113
            ))}
114
          </CommandGroup>
115
        ) : null}
116
        <CommandSeparator />
117
        <CommandGroup heading="Account">
118
          <CommandItem
119
            onSelect={() => {
120
              onToggleTheme();
121
              onOpenChange(false);
122
            }}
123
          >
124
            {theme === 'dark' ? <Sun /> : <Moon />}
125
            {theme === 'dark' ? 'Switch to light' : 'Switch to dark'}
126
          </CommandItem>
127
          <CommandItem
128
            onSelect={() => {
129
              onOpenChange(false);
130
              onSignOut();
131
            }}
132
          >
133
            <LogOut />
134
            Sign out
135
          </CommandItem>
136
        </CommandGroup>
137
      </CommandList>
138
    </CommandDialog>
139
  );
140
}
141
142
export function CommandTrigger({ onOpen }: { onOpen: () => void }) {
143
  const [mod, setMod] = useState('Ctrl');
144
  useEffect(() => {
145
    setMod(navigator.platform.toLowerCase().includes('mac') ? '⌘' : 'Ctrl');
146
  }, []);
147
  return (
148
    <Button
149
      type="button"
150
      variant="outline"
151
      size="sm"
152
      onClick={onOpen}
153
      className="commandTrigger text-muted-foreground h-8 max-w-xs flex-1 justify-start border-border bg-transparent px-2 shadow-none hover:bg-sidebar-accent sm:min-w-[220px]"
154
    >
155
      <Search />
156
      <span className="flex-1 text-left">search</span>
157
      <kbd className="commandKbd">
158
        {mod}
159
        {mod === '⌘' ? '' : '+'}K
160
      </kbd>
161
    </Button>
162
  );
163
}