aidevops 3.26.1 → 3.27.0
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/README.md +1 -1
- package/VERSION +1 -1
- package/aidevops.sh +1 -1
- package/package.json +1 -1
- package/packages/gui-web/src/AppWorkspace.tsx +6 -112
- package/packages/gui-web/src/CommandPalette.tsx +200 -0
- package/packages/gui-web/src/styles.css +5 -3
- package/setup.sh +1 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ The result: an AI operations platform that manages projects across every busines
|
|
|
58
58
|
[](https://github.com/marcusquinn)
|
|
59
59
|
|
|
60
60
|
<!-- Release & Version Info -->
|
|
61
|
-
[](https://github.com/marcusquinn/aidevops/releases)
|
|
62
62
|
[](https://www.npmjs.com/package/aidevops)
|
|
63
63
|
[](https://github.com/marcusquinn/homebrew-tap)
|
|
64
64
|
[](https://github.com/marcusquinn/aidevops)
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.27.0
|
package/aidevops.sh
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/* jshint esversion: 11 */
|
|
2
|
-
import { type ReactElement, type ReactNode, useEffect,
|
|
2
|
+
import { type ReactElement, type ReactNode, useEffect, useState } from "react";
|
|
3
3
|
import { FiBell, FiChevronLeft, FiChevronRight, FiCommand, FiCpu, FiGlobe, FiHash, FiLogOut, FiMessageSquare, FiSearch, FiSettings, FiShield, FiUser } from "react-icons/fi";
|
|
4
4
|
import type { GuiFileRootId, GuiStatusData } from "../../gui-shared/src";
|
|
5
5
|
import { SurfaceGlyph } from "./AppNavigation";
|
|
6
6
|
import type { ConversationMode, ShellMode, SurfaceId, SurfaceNavItem } from "./app-model";
|
|
7
|
-
import { inventorySurfaceConfigs,
|
|
7
|
+
import { inventorySurfaceConfigs, text } from "./app-model";
|
|
8
|
+
import { CommandPalette, commandPaletteShortcutQuery } from "./CommandPalette";
|
|
8
9
|
import { FileExplorerSurface } from "./FileExplorerSurface";
|
|
9
10
|
import { AppsSurface, EditableInventorySurface, InstallationSurface } from "./InventorySurfaces";
|
|
10
11
|
import { AiProvidersSurface, LocalReposSurface, LockedVaultGate, OverviewSurface, PlannedSurface, ProjectsSurface, SecuritySurface, VaultSurface } from "./StatusSurfaces";
|
|
@@ -70,9 +71,10 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
|
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
const shortcutQuery = commandPaletteShortcutQuery(event);
|
|
75
|
+
if (shortcutQuery !== undefined && !isEditableShortcutTarget(event.target)) {
|
|
74
76
|
event.preventDefault();
|
|
75
|
-
setCommandInitialQuery(
|
|
77
|
+
setCommandInitialQuery(shortcutQuery);
|
|
76
78
|
setCommandOpen(true);
|
|
77
79
|
}
|
|
78
80
|
};
|
|
@@ -235,114 +237,6 @@ function AssistantPanel({ activeSurface, userName }: { activeSurface: SurfaceId;
|
|
|
235
237
|
);
|
|
236
238
|
}
|
|
237
239
|
|
|
238
|
-
interface CommandPaletteItem {
|
|
239
|
-
description: string;
|
|
240
|
-
icon: SurfaceNavItem["icon"];
|
|
241
|
-
id: string;
|
|
242
|
-
label: string;
|
|
243
|
-
searchText: string;
|
|
244
|
-
surface: SurfaceId;
|
|
245
|
-
tag: string;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const slashCommandItems: CommandPaletteItem[] = [
|
|
249
|
-
{ id: "slash-add-device", label: "/add-device", description: "Start device pairing setup", icon: "device", searchText: "/add-device add device pair machine", surface: "devices", tag: "Command" },
|
|
250
|
-
{ id: "slash-add-local-repo", label: "/add-local-repo", description: "Add a local repository path", icon: "folder", searchText: "/add-local-repo add local repo git", surface: "git", tag: "Command" },
|
|
251
|
-
{ id: "slash-add-remote-repo", label: "/add-remote-repo", description: "Register a remote repository", icon: "git", searchText: "/add-remote-repo add remote repo", surface: "projects", tag: "Command" },
|
|
252
|
-
{ id: "slash-add-secret", label: "/add-secret", description: "Add a protected secret reference", icon: "shield", searchText: "/add-secret add secret vault", surface: "security", tag: "Command" },
|
|
253
|
-
{ id: "slash-add-ai-provider", label: "/add-ai-provider", description: "Connect an AI provider account", icon: "users", searchText: "/add-ai-provider add ai provider oauth", surface: "aiProviders", tag: "Command" },
|
|
254
|
-
];
|
|
255
|
-
|
|
256
|
-
const plannedChannelItems: CommandPaletteItem[] = [
|
|
257
|
-
{ id: "channel-devops", label: "#devops", description: "Chat channel for aidevops operations", icon: "message", searchText: "#devops devops channel chat", surface: "messagingAccounts", tag: "Channel" },
|
|
258
|
-
{ id: "channel-comms", label: "#comms", description: "Chat channel for communications", icon: "message", searchText: "#comms comms channel chat", surface: "messagingAccounts", tag: "Channel" },
|
|
259
|
-
];
|
|
260
|
-
|
|
261
|
-
function CommandPalette({ close, initialQuery, openSurface, status }: { close: () => void; initialQuery: string; openSurface: (surface: SurfaceId) => void; status: GuiStatusData }): ReactElement {
|
|
262
|
-
const [query, setQuery] = useState(initialQuery);
|
|
263
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
264
|
-
const matches = useMemo(() => {
|
|
265
|
-
const normalizedQuery = query.trim().toLowerCase();
|
|
266
|
-
const surfaceItems: CommandPaletteItem[] = orderedNavItems.map((item) => ({
|
|
267
|
-
description: item.description,
|
|
268
|
-
icon: item.icon,
|
|
269
|
-
id: `surface-${item.id}`,
|
|
270
|
-
label: item.label,
|
|
271
|
-
searchText: `${item.label} ${item.description}`,
|
|
272
|
-
surface: item.id,
|
|
273
|
-
tag: "Surface",
|
|
274
|
-
}));
|
|
275
|
-
const sessionItems: CommandPaletteItem[] = status.opencode_sessions.sessions.map((session) => ({
|
|
276
|
-
description: `AI session in ${session.repo_path_ref}`,
|
|
277
|
-
icon: "terminal",
|
|
278
|
-
id: `session-${session.id_ref}`,
|
|
279
|
-
label: `#${session.title}`,
|
|
280
|
-
searchText: `#${session.title} ${session.title} ${session.repo_path_ref} ${session.agent} ${session.model}`,
|
|
281
|
-
surface: "git",
|
|
282
|
-
tag: "AI session",
|
|
283
|
-
}));
|
|
284
|
-
const directMessageItems: CommandPaletteItem[] = [{
|
|
285
|
-
description: "Direct message thread placeholder",
|
|
286
|
-
icon: "users",
|
|
287
|
-
id: "dm-local-user",
|
|
288
|
-
label: `@${status.machine.username || "local-user"}`,
|
|
289
|
-
searchText: `@${status.machine.username || "local-user"} direct message dm person local user`,
|
|
290
|
-
surface: "messagingAccounts",
|
|
291
|
-
tag: "Direct message",
|
|
292
|
-
}];
|
|
293
|
-
const items = [...surfaceItems, ...sessionItems, ...plannedChannelItems, ...directMessageItems, ...slashCommandItems];
|
|
294
|
-
const prefix = normalizedQuery.charAt(0);
|
|
295
|
-
const scopedItems = prefix === "/"
|
|
296
|
-
? slashCommandItems
|
|
297
|
-
: prefix === "@"
|
|
298
|
-
? directMessageItems
|
|
299
|
-
: prefix === "#"
|
|
300
|
-
? [...sessionItems, ...plannedChannelItems]
|
|
301
|
-
: items;
|
|
302
|
-
|
|
303
|
-
return scopedItems
|
|
304
|
-
.filter((item) => normalizedQuery.length === 0 || item.searchText.toLowerCase().includes(normalizedQuery))
|
|
305
|
-
.slice(0, 8);
|
|
306
|
-
}, [query, status.machine.username, status.opencode_sessions.sessions]);
|
|
307
|
-
|
|
308
|
-
useEffect(() => {
|
|
309
|
-
inputRef.current?.focus();
|
|
310
|
-
}, []);
|
|
311
|
-
|
|
312
|
-
useEffect(() => {
|
|
313
|
-
const closeOnEscape = (event: KeyboardEvent) => {
|
|
314
|
-
if (event.key === "Escape") {
|
|
315
|
-
close();
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
window.addEventListener("keydown", closeOnEscape);
|
|
320
|
-
return () => window.removeEventListener("keydown", closeOnEscape);
|
|
321
|
-
}, [close]);
|
|
322
|
-
|
|
323
|
-
return (
|
|
324
|
-
<div className="command-palette-backdrop" role="presentation">
|
|
325
|
-
<button aria-label="Close command palette" className="command-palette-scrim" onClick={close} type="button" />
|
|
326
|
-
<section aria-label="Command palette" className="command-palette">
|
|
327
|
-
<label className="command-input-row">
|
|
328
|
-
<FiSearch aria-hidden="true" />
|
|
329
|
-
<input ref={inputRef} onChange={(event) => setQuery(event.currentTarget.value)} placeholder="Search commands, #sessions, #channels, @people, or /actions" value={query} />
|
|
330
|
-
</label>
|
|
331
|
-
<ul>
|
|
332
|
-
{matches.map((item) => (
|
|
333
|
-
<li key={item.id}>
|
|
334
|
-
<button onClick={() => openSurface(item.surface)} type="button">
|
|
335
|
-
<span className="surface-icon" aria-hidden="true"><SurfaceGlyph icon={item.icon} /></span>
|
|
336
|
-
<span><strong>{item.label}</strong><small>{item.tag} · {item.description}</small></span>
|
|
337
|
-
</button>
|
|
338
|
-
</li>
|
|
339
|
-
))}
|
|
340
|
-
</ul>
|
|
341
|
-
</section>
|
|
342
|
-
</div>
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
240
|
function SurfaceContent({ activeItem, activeSurface, fileRoot, status }: {
|
|
347
241
|
activeItem: SurfaceNavItem;
|
|
348
242
|
activeSurface: SurfaceId;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/* jshint esversion: 11 */
|
|
2
|
+
import { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import { FiSearch } from "react-icons/fi";
|
|
5
|
+
import { SurfaceGlyph } from "./AppNavigation";
|
|
6
|
+
import type { SurfaceId, SurfaceNavItem } from "./app-model";
|
|
7
|
+
import { orderedNavItems } from "./app-model";
|
|
8
|
+
import type { GuiStatusData } from "../../gui-shared/src";
|
|
9
|
+
|
|
10
|
+
interface CommandPaletteItem {
|
|
11
|
+
description: string;
|
|
12
|
+
icon: SurfaceNavItem["icon"];
|
|
13
|
+
id: string;
|
|
14
|
+
label: string;
|
|
15
|
+
searchText: string;
|
|
16
|
+
surface: SurfaceId;
|
|
17
|
+
tag: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const commandPaletteRecentStorageKey = "aidevops-gui-command-palette-recents";
|
|
21
|
+
const commandPaletteRecentLimit = 7;
|
|
22
|
+
|
|
23
|
+
const slashCommandItems: CommandPaletteItem[] = [
|
|
24
|
+
{ id: "slash-add-device", label: "/add-device", description: "Start device pairing setup", icon: "device", searchText: "/add-device add device pair machine", surface: "devices", tag: "Command" },
|
|
25
|
+
{ id: "slash-add-local-repo", label: "/add-local-repo", description: "Add a local repository path", icon: "folder", searchText: "/add-local-repo add local repo git", surface: "git", tag: "Command" },
|
|
26
|
+
{ id: "slash-add-remote-repo", label: "/add-remote-repo", description: "Register a remote repository", icon: "git", searchText: "/add-remote-repo add remote repo", surface: "projects", tag: "Command" },
|
|
27
|
+
{ id: "slash-add-secret", label: "/add-secret", description: "Add a protected secret reference", icon: "shield", searchText: "/add-secret add secret vault", surface: "security", tag: "Command" },
|
|
28
|
+
{ id: "slash-add-ai-provider", label: "/add-ai-provider", description: "Connect an AI provider account", icon: "users", searchText: "/add-ai-provider add ai provider oauth", surface: "aiProviders", tag: "Command" },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const plannedChannelItems: CommandPaletteItem[] = [
|
|
32
|
+
{ id: "channel-devops", label: "#devops", description: "Chat channel for aidevops operations", icon: "message", searchText: "#devops devops channel chat", surface: "messagingAccounts", tag: "Channel" },
|
|
33
|
+
{ id: "channel-comms", label: "#comms", description: "Chat channel for communications", icon: "message", searchText: "#comms comms channel chat", surface: "messagingAccounts", tag: "Channel" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export function CommandPalette({ close, initialQuery, openSurface, status }: { close: () => void; initialQuery: string; openSurface: (surface: SurfaceId) => void; status: GuiStatusData }): ReactElement {
|
|
37
|
+
const allItems = useMemo(() => commandPaletteItems(status), [status]);
|
|
38
|
+
const [query, setQuery] = useState(initialQuery);
|
|
39
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
40
|
+
const [recentIds, setRecentIds] = useState<string[]>(() => readCommandPaletteRecentIds());
|
|
41
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
42
|
+
const matches = useMemo(() => commandPaletteMatches(allItems, query, recentIds), [allItems, query, recentIds]);
|
|
43
|
+
|
|
44
|
+
const selectItem = useCallback((item: CommandPaletteItem | undefined) => {
|
|
45
|
+
if (item) {
|
|
46
|
+
const nextRecentIds = rememberCommandPaletteItemId(item.id, recentIds);
|
|
47
|
+
setRecentIds(nextRecentIds);
|
|
48
|
+
openSurface(item.surface);
|
|
49
|
+
}
|
|
50
|
+
}, [openSurface, recentIds]);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
inputRef.current?.focus();
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
setActiveIndex((current) => Math.min(current, Math.max(0, matches.length - 1)));
|
|
58
|
+
}, [matches.length]);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const handlePaletteKeys = (event: KeyboardEvent) => {
|
|
62
|
+
if (event.key === "Escape") {
|
|
63
|
+
close();
|
|
64
|
+
} else if (event.key === "ArrowDown") {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
setActiveIndex((current) => Math.min(matches.length - 1, current + 1));
|
|
67
|
+
} else if (event.key === "ArrowUp") {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
setActiveIndex((current) => Math.max(0, current - 1));
|
|
70
|
+
} else if (event.key === "Enter") {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
selectItem(matches[activeIndex]);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
window.addEventListener("keydown", handlePaletteKeys);
|
|
77
|
+
return () => window.removeEventListener("keydown", handlePaletteKeys);
|
|
78
|
+
}, [activeIndex, close, matches, selectItem]);
|
|
79
|
+
|
|
80
|
+
return createPortal(
|
|
81
|
+
<div className="command-palette-backdrop" role="presentation">
|
|
82
|
+
<button aria-label="Close command palette" className="command-palette-scrim" onClick={close} type="button" />
|
|
83
|
+
<section aria-label="Command palette" className="command-palette">
|
|
84
|
+
<label className="command-input-row">
|
|
85
|
+
<FiSearch aria-hidden="true" />
|
|
86
|
+
<input aria-activedescendant={matches[activeIndex]?.id} aria-controls="command-palette-results" aria-expanded="true" aria-autocomplete="list" ref={inputRef} onChange={(event) => { setQuery(event.currentTarget.value); setActiveIndex(0); }} placeholder="Search commands, #sessions, #channels, @people, or /actions" role="combobox" value={query} />
|
|
87
|
+
</label>
|
|
88
|
+
<ul id="command-palette-results">
|
|
89
|
+
{matches.map((item, index) => (
|
|
90
|
+
<li key={item.id}>
|
|
91
|
+
<button className={index === activeIndex ? "active" : undefined} id={item.id} onClick={() => selectItem(item)} type="button">
|
|
92
|
+
<span className="surface-icon" aria-hidden="true"><SurfaceGlyph icon={item.icon} /></span>
|
|
93
|
+
<span><strong>{item.label}</strong><small>{item.tag} · {item.description}</small></span>
|
|
94
|
+
</button>
|
|
95
|
+
</li>
|
|
96
|
+
))}
|
|
97
|
+
</ul>
|
|
98
|
+
</section>
|
|
99
|
+
</div>,
|
|
100
|
+
document.body,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function commandPaletteShortcutQuery(event: Pick<KeyboardEvent, "key" | "metaKey" | "ctrlKey" | "altKey">): string | undefined {
|
|
105
|
+
if (event.metaKey || event.ctrlKey || event.altKey) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return new Map([["/", "/"], ["@", "@"], ["#", "#"], [".", ""]]).get(event.key);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function orderCommandItemsByRecency<T extends { id: string }>(items: T[], recentIds: string[]): T[] {
|
|
113
|
+
const recentItems = recentIds
|
|
114
|
+
.map((id) => items.find((item) => item.id === id))
|
|
115
|
+
.filter((item): item is T => item !== undefined);
|
|
116
|
+
const recentItemIds = new Set(recentItems.map((item) => item.id));
|
|
117
|
+
|
|
118
|
+
return [...recentItems, ...items.filter((item) => !recentItemIds.has(item.id))];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function rememberCommandPaletteItemId(itemId: string, currentIds: string[]): string[] {
|
|
122
|
+
const nextIds = [itemId, ...currentIds.filter((id) => id !== itemId)].slice(0, commandPaletteRecentLimit);
|
|
123
|
+
writeCommandPaletteRecentIds(nextIds);
|
|
124
|
+
return nextIds;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function commandPaletteMatches(items: CommandPaletteItem[], query: string, recentIds: string[]): CommandPaletteItem[] {
|
|
128
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
129
|
+
const prefix = normalizedQuery.charAt(0);
|
|
130
|
+
const scopedItems = itemsForCommandPrefix(items, prefix);
|
|
131
|
+
|
|
132
|
+
if (normalizedQuery.length === 0 || ["/", "@", "#"].includes(normalizedQuery)) {
|
|
133
|
+
return orderCommandItemsByRecency(scopedItems, recentIds).slice(0, 8);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return scopedItems
|
|
137
|
+
.filter((item) => item.searchText.toLowerCase().includes(normalizedQuery))
|
|
138
|
+
.slice(0, 8);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function commandPaletteItems(status: GuiStatusData): CommandPaletteItem[] {
|
|
142
|
+
const surfaceItems: CommandPaletteItem[] = orderedNavItems.map((item) => ({
|
|
143
|
+
description: item.description,
|
|
144
|
+
icon: item.icon,
|
|
145
|
+
id: `surface-${item.id}`,
|
|
146
|
+
label: item.label,
|
|
147
|
+
searchText: `${item.label} ${item.description}`,
|
|
148
|
+
surface: item.id,
|
|
149
|
+
tag: "Surface",
|
|
150
|
+
}));
|
|
151
|
+
const sessionItems: CommandPaletteItem[] = status.opencode_sessions.sessions.map((session) => ({
|
|
152
|
+
description: `AI session in ${session.repo_path_ref}`,
|
|
153
|
+
icon: "terminal",
|
|
154
|
+
id: `session-${session.id_ref}`,
|
|
155
|
+
label: `#${session.title}`,
|
|
156
|
+
searchText: `#${session.title} ${session.title} ${session.repo_path_ref} ${session.agent} ${session.model}`,
|
|
157
|
+
surface: "git",
|
|
158
|
+
tag: "AI session",
|
|
159
|
+
}));
|
|
160
|
+
const directMessageItems: CommandPaletteItem[] = [{
|
|
161
|
+
description: "Direct message thread placeholder",
|
|
162
|
+
icon: "users",
|
|
163
|
+
id: "dm-local-user",
|
|
164
|
+
label: `@${status.machine.username || "local-user"}`,
|
|
165
|
+
searchText: `@${status.machine.username || "local-user"} direct message dm person local user`,
|
|
166
|
+
surface: "messagingAccounts",
|
|
167
|
+
tag: "Direct message",
|
|
168
|
+
}];
|
|
169
|
+
|
|
170
|
+
return [...surfaceItems, ...sessionItems, ...plannedChannelItems, ...directMessageItems, ...slashCommandItems];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function itemsForCommandPrefix(items: CommandPaletteItem[], prefix: string): CommandPaletteItem[] {
|
|
174
|
+
const tagsByPrefix = new Map<string, string[]>([
|
|
175
|
+
["/", ["Command"]],
|
|
176
|
+
["@", ["Direct message"]],
|
|
177
|
+
["#", ["AI session", "Channel"]],
|
|
178
|
+
]);
|
|
179
|
+
const allowedTags = tagsByPrefix.get(prefix) ?? ["Surface"];
|
|
180
|
+
|
|
181
|
+
return items.filter((item) => allowedTags.includes(item.tag));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function readCommandPaletteRecentIds(): string[] {
|
|
185
|
+
try {
|
|
186
|
+
const storedValue = window.localStorage.getItem(commandPaletteRecentStorageKey);
|
|
187
|
+
const parsedValue: unknown = storedValue ? JSON.parse(storedValue) : [];
|
|
188
|
+
return Array.isArray(parsedValue) ? parsedValue.filter((value): value is string => typeof value === "string").slice(0, commandPaletteRecentLimit) : [];
|
|
189
|
+
} catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function writeCommandPaletteRecentIds(ids: string[]): void {
|
|
195
|
+
try {
|
|
196
|
+
window.localStorage.setItem(commandPaletteRecentStorageKey, JSON.stringify(ids.slice(0, commandPaletteRecentLimit)));
|
|
197
|
+
} catch {
|
|
198
|
+
// Ignore unavailable storage; command navigation still works without recents.
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -1066,12 +1066,14 @@ code {
|
|
|
1066
1066
|
|
|
1067
1067
|
.command-palette-backdrop {
|
|
1068
1068
|
align-items: center;
|
|
1069
|
-
|
|
1069
|
+
backdrop-filter: blur(8px) saturate(120%);
|
|
1070
|
+
background: rgb(0 0 0 / 34%);
|
|
1070
1071
|
display: flex;
|
|
1071
1072
|
inset: 0;
|
|
1072
1073
|
justify-content: center;
|
|
1073
1074
|
position: fixed;
|
|
1074
1075
|
z-index: 20;
|
|
1076
|
+
-webkit-backdrop-filter: blur(8px) saturate(120%);
|
|
1075
1077
|
}
|
|
1076
1078
|
|
|
1077
1079
|
.command-palette-scrim {
|
|
@@ -1092,7 +1094,6 @@ code {
|
|
|
1092
1094
|
max-width: min(680px, calc(100vw - 32px));
|
|
1093
1095
|
padding: 14px;
|
|
1094
1096
|
position: relative;
|
|
1095
|
-
transform: translateY(-2vh);
|
|
1096
1097
|
width: 100%;
|
|
1097
1098
|
}
|
|
1098
1099
|
|
|
@@ -1137,7 +1138,8 @@ code {
|
|
|
1137
1138
|
}
|
|
1138
1139
|
|
|
1139
1140
|
.command-palette button:hover,
|
|
1140
|
-
.command-palette button:focus-visible
|
|
1141
|
+
.command-palette button:focus-visible,
|
|
1142
|
+
.command-palette button.active {
|
|
1141
1143
|
background: var(--surface-muted);
|
|
1142
1144
|
color: var(--accent);
|
|
1143
1145
|
outline: none;
|
package/setup.sh
CHANGED
|
@@ -12,7 +12,7 @@ shopt -s inherit_errexit 2>/dev/null || true
|
|
|
12
12
|
# AI Assistant Server Access Framework Setup Script
|
|
13
13
|
# Helps developers set up the framework for their infrastructure
|
|
14
14
|
#
|
|
15
|
-
# Version: 3.
|
|
15
|
+
# Version: 3.27.0
|
|
16
16
|
#
|
|
17
17
|
# Quick Install:
|
|
18
18
|
# npm install -g aidevops && aidevops update (recommended)
|