aidevops 3.29.11 → 3.29.12
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-api/src/app.ts +15 -5
- package/packages/gui-shared/src/notifications.ts +6 -6
- package/packages/gui-web/src/AppNavigation.tsx +5 -0
- package/packages/gui-web/src/AppWorkspace.tsx +14 -5
- package/packages/gui-web/src/InventorySurfaces.tsx +39 -12
- package/packages/gui-web/src/styles.css +44 -11
- 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.29.
|
|
1
|
+
3.29.12
|
package/aidevops.sh
CHANGED
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import * as readline from "node:readline";
|
|
2
3
|
import { Hono } from "hono";
|
|
3
4
|
import { APP_ACTION_ROUTE_MANIFEST, APP_ACTION_STATUS_ROUTE_MANIFEST, BANNED_ROUTE_PATTERNS, createEnvelope, FILE_EXPLORER_ROUTE_MANIFEST, type GuiAppActionId, type GuiAppActionJobSummary, STATUS_ROUTE_MANIFEST, VAULT_STATUS_ROUTE_MANIFEST } from "../../gui-shared/src";
|
|
4
5
|
import { readFileExplorer } from "./file-adapter";
|
|
@@ -150,10 +151,16 @@ function startAppActionJob(appId: string, action: GuiAppActionId, command: strin
|
|
|
150
151
|
env: { ...process.env, AIDEVOPS_NON_INTERACTIVE: "true" },
|
|
151
152
|
stdio: ["ignore", "pipe", "pipe"],
|
|
152
153
|
});
|
|
153
|
-
child.stdout
|
|
154
|
-
|
|
154
|
+
if (child.stdout !== null) {
|
|
155
|
+
const stdoutLines = readline.createInterface({ input: child.stdout, terminal: false });
|
|
156
|
+
stdoutLines.on("line", (line) => appendJobLine(job, line));
|
|
157
|
+
}
|
|
158
|
+
if (child.stderr !== null) {
|
|
159
|
+
const stderrLines = readline.createInterface({ input: child.stderr, terminal: false });
|
|
160
|
+
stderrLines.on("line", (line) => appendJobLine(job, line));
|
|
161
|
+
}
|
|
155
162
|
child.on("error", (error) => {
|
|
156
|
-
|
|
163
|
+
appendJobLine(job, error.message);
|
|
157
164
|
job.status = "failed";
|
|
158
165
|
job.finished_at = new Date().toISOString();
|
|
159
166
|
job.exit_code = 127;
|
|
@@ -167,8 +174,11 @@ function startAppActionJob(appId: string, action: GuiAppActionId, command: strin
|
|
|
167
174
|
return job;
|
|
168
175
|
}
|
|
169
176
|
|
|
170
|
-
function
|
|
171
|
-
|
|
177
|
+
function appendJobLine(job: GuiAppActionJobSummary, line: string): void {
|
|
178
|
+
if (line.length === 0) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
job.output.push(redactOutputLine(line));
|
|
172
182
|
if (job.output.length > 400) {
|
|
173
183
|
job.output.splice(1, job.output.length - 400);
|
|
174
184
|
}
|
|
@@ -17,7 +17,7 @@ const toastSeverityRank: Record<GuiNotificationSeverity, number> = {
|
|
|
17
17
|
|
|
18
18
|
const warningLineMatchers: Array<(line: string) => boolean> = [
|
|
19
19
|
(line) => line.startsWith("Pulse stalled"),
|
|
20
|
-
(line) => /contribution\(s\)
|
|
20
|
+
(line) => /contribution(\(s\)|s)?\s+needs?/i.test(line),
|
|
21
21
|
(line) => line.startsWith("[OPENCODE MAINTENANCE]"),
|
|
22
22
|
(line) => line.startsWith("[WARNING]"),
|
|
23
23
|
(line) => line.startsWith("[WARN]"),
|
|
@@ -28,7 +28,7 @@ const toastTitleRules: Array<{ matches: (line: string) => boolean; title: string
|
|
|
28
28
|
{ matches: (line) => line.startsWith("[ERROR]"), title: "Startup error" },
|
|
29
29
|
{ matches: (line) => line.startsWith("[OPENCODE MAINTENANCE]"), title: "OpenCode maintenance" },
|
|
30
30
|
{ matches: (line) => line.startsWith("Pulse stalled"), title: "Pulse stalled" },
|
|
31
|
-
{ matches: (line) => /contribution\(s\)
|
|
31
|
+
{ matches: (line) => /contribution(\(s\)|s)?\s+needs?/i.test(line), title: "External contribution review" },
|
|
32
32
|
{ matches: (line) => line.startsWith("[WARNING]") || line.startsWith("[WARN]"), title: "Startup warning" },
|
|
33
33
|
{ matches: (line) => line.startsWith("Security: all protections active"), title: "Security protections active" },
|
|
34
34
|
];
|
|
@@ -93,10 +93,10 @@ function buildToastNotifications(greetingOutput: string): GuiNotificationSummary
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function buildGuiStateNotifications(input: BuildNotificationInput): GuiNotificationSummary[] {
|
|
96
|
-
const setupNeedsUpdate = input.setupTargets.filter((target) => target.needs_update);
|
|
97
|
-
const appNeedsUpdate = input.aiApps.filter((app) => app.needs_update);
|
|
98
|
-
const authErrors = input.oauthPool
|
|
99
|
-
const rateLimited = input.oauthPool
|
|
96
|
+
const setupNeedsUpdate = (input.setupTargets ?? []).filter((target) => target.needs_update);
|
|
97
|
+
const appNeedsUpdate = (input.aiApps ?? []).filter((app) => app.needs_update);
|
|
98
|
+
const authErrors = (input.oauthPool?.providers ?? []).filter((provider) => provider.auth_errors > 0);
|
|
99
|
+
const rateLimited = (input.oauthPool?.providers ?? []).filter((provider) => provider.rate_limited > 0);
|
|
100
100
|
const notifications: GuiNotificationSummary[] = [];
|
|
101
101
|
|
|
102
102
|
if (input.restartRequired) {
|
|
@@ -222,6 +222,11 @@ function ConversationSidebar({ conversationMode, selectedLocalRepoIndex, selecte
|
|
|
222
222
|
? []
|
|
223
223
|
: status.opencode_sessions.sessions.filter((session) => session.repo_path_ref === selectedRepo.path_ref);
|
|
224
224
|
const activeSessionId = sessions.some((session) => session.id_ref === selectedSessionId) ? selectedSessionId : sessions[0]?.id_ref;
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (activeSessionId !== selectedSessionId) {
|
|
227
|
+
setSelectedSessionId(activeSessionId);
|
|
228
|
+
}
|
|
229
|
+
}, [activeSessionId, selectedSessionId, setSelectedSessionId]);
|
|
225
230
|
const canSelectPreviousRepo = selectedLocalRepoIndex > 0;
|
|
226
231
|
const canSelectNextRepo = selectedLocalRepoIndex < repos.length - 1;
|
|
227
232
|
|
|
@@ -189,21 +189,30 @@ function NotificationsMenu({ notifications, openSurface }: { notifications: GuiN
|
|
|
189
189
|
<span>{active.length} active</span>
|
|
190
190
|
</div>
|
|
191
191
|
{preview.length === 0 ? <p>No current aidevops notifications.</p> : null}
|
|
192
|
-
{preview.map((notification) =>
|
|
192
|
+
{preview.map((notification) => (
|
|
193
|
+
<NotificationPreview
|
|
194
|
+
key={notification.id}
|
|
195
|
+
notification={notification}
|
|
196
|
+
onClick={() => {
|
|
197
|
+
const action = notification.actions.find((candidate) => candidate.enabled && candidate.kind === "surface" && isSurfaceId(candidate.surface_id));
|
|
198
|
+
openSurface(action?.kind === "surface" && isSurfaceId(action.surface_id) ? action.surface_id : "notifications");
|
|
199
|
+
}}
|
|
200
|
+
/>
|
|
201
|
+
))}
|
|
193
202
|
<button onClick={() => openSurface("notifications")} role="menuitem" type="button">Open notifications</button>
|
|
194
203
|
</div>
|
|
195
204
|
);
|
|
196
205
|
}
|
|
197
206
|
|
|
198
|
-
function NotificationPreview({ notification }: { notification: GuiNotificationSummary }): ReactElement {
|
|
207
|
+
function NotificationPreview({ notification, onClick }: { notification: GuiNotificationSummary; onClick: () => void }): ReactElement {
|
|
199
208
|
return (
|
|
200
|
-
<
|
|
209
|
+
<button className={`notification-preview ${notification.severity}`} onClick={onClick} role="menuitem" type="button">
|
|
201
210
|
<span aria-hidden="true"><NotificationIcon notification={notification} /></span>
|
|
202
211
|
<div>
|
|
203
212
|
<strong>{notification.title}</strong>
|
|
204
213
|
<small>{notification.category} · {notification.status}</small>
|
|
205
214
|
</div>
|
|
206
|
-
</
|
|
215
|
+
</button>
|
|
207
216
|
);
|
|
208
217
|
}
|
|
209
218
|
|
|
@@ -528,7 +537,7 @@ function NotificationCard({ notification, openSurface }: { notification: GuiNoti
|
|
|
528
537
|
return <button className="secondary-action" disabled={!action.enabled} key={action.id} onClick={() => openSurface(surfaceId)} type="button">{action.label}</button>;
|
|
529
538
|
}
|
|
530
539
|
|
|
531
|
-
return <button className="secondary-action" disabled key={action.id} title={action.command_preview} type="button">{action.label}</button>;
|
|
540
|
+
return <button className="secondary-action" disabled={!action.enabled} key={action.id} title={action.command_preview} type="button">{action.label}</button>;
|
|
532
541
|
})}
|
|
533
542
|
</div>
|
|
534
543
|
</div>
|
|
@@ -19,6 +19,7 @@ export function AppsSurface({ status }: { status: GuiStatusData }): ReactElement
|
|
|
19
19
|
const [appCollection, setAppCollection] = useState<AppCollectionId>("aidevops");
|
|
20
20
|
const [managedCategory, setManagedCategory] = useState<ManagedCategoryId>("core");
|
|
21
21
|
const [recommendedPlatform, setRecommendedPlatform] = useState<RecommendedPlatformFilterId>("all");
|
|
22
|
+
const [recommendedOs, setRecommendedOs] = useState<RecommendedOsId>("all");
|
|
22
23
|
const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
|
|
23
24
|
const [expandedAppId, setExpandedAppId] = useState<string | null>(() => sortedManagedApps(status.managed_apps).find((app) => managedCategoryForApp(app) === "core")?.id ?? null);
|
|
24
25
|
const [jobs, setJobs] = useState<Record<string, GuiAppActionJobSummary>>({});
|
|
@@ -27,7 +28,7 @@ export function AppsSurface({ status }: { status: GuiStatusData }): ReactElement
|
|
|
27
28
|
const selectedJob = selectedJobId === null ? null : jobs[selectedJobId] ?? null;
|
|
28
29
|
const managedApps = sortedManagedApps(status.managed_apps).map((app) => applyManagedPolicyToggles(app, policyToggles[app.id]));
|
|
29
30
|
const visibleManagedApps = managedApps.filter((app) => managedCategoryForApp(app) === managedCategory);
|
|
30
|
-
const visibleRecommendedApps = recommendedApps.filter((app) => recommendedPlatform
|
|
31
|
+
const visibleRecommendedApps = recommendedApps.filter((app) => recommendedAppMatchesFilters(app, recommendedPlatform, recommendedOs));
|
|
31
32
|
|
|
32
33
|
useEffect(() => {
|
|
33
34
|
setExpandedAppId(visibleManagedApps[0]?.id ?? null);
|
|
@@ -84,7 +85,7 @@ export function AppsSurface({ status }: { status: GuiStatusData }): ReactElement
|
|
|
84
85
|
))}
|
|
85
86
|
</div>
|
|
86
87
|
<p className="empty-state compact-notice">Install/update actions run as allowlisted background jobs and stream inside each app panel. xterm.js with a node-pty bridge is the right next step for full TUI apps such as OpenCode; this view starts with non-interactive command logs.</p>
|
|
87
|
-
</> : <RecommendedAppsSurface apps={visibleRecommendedApps} platform={recommendedPlatform} setPlatform={setRecommendedPlatform} />}
|
|
88
|
+
</> : <RecommendedAppsSurface apps={visibleRecommendedApps} os={recommendedOs} platform={recommendedPlatform} setOs={setRecommendedOs} setPlatform={setRecommendedPlatform} />}
|
|
88
89
|
</section>
|
|
89
90
|
);
|
|
90
91
|
}
|
|
@@ -224,7 +225,14 @@ function jobForApp(appId: string, jobs: Record<string, GuiAppActionJobSummary>,
|
|
|
224
225
|
return selectedJob;
|
|
225
226
|
}
|
|
226
227
|
|
|
227
|
-
|
|
228
|
+
let latestJob: GuiAppActionJobSummary | null = null;
|
|
229
|
+
for (const job of Object.values(jobs)) {
|
|
230
|
+
if (job.app_id === appId && (latestJob === null || job.started_at > latestJob.started_at)) {
|
|
231
|
+
latestJob = job;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return latestJob;
|
|
228
236
|
}
|
|
229
237
|
|
|
230
238
|
function sortedManagedApps(apps: GuiManagedAppSummary[]): GuiManagedAppSummary[] {
|
|
@@ -303,6 +311,15 @@ const recommendedPlatformTabs: TabOption<RecommendedPlatformFilterId>[] = [
|
|
|
303
311
|
{ id: "api", label: "API" },
|
|
304
312
|
];
|
|
305
313
|
|
|
314
|
+
const recommendedOsTabs: TabOption<RecommendedOsId>[] = [
|
|
315
|
+
{ id: "all", label: "All OS" },
|
|
316
|
+
{ id: "macos", label: "macOS" },
|
|
317
|
+
{ id: "linux", label: "Linux" },
|
|
318
|
+
{ id: "windows", label: "Windows" },
|
|
319
|
+
{ id: "ios", label: "iOS" },
|
|
320
|
+
{ id: "android", label: "Android" },
|
|
321
|
+
];
|
|
322
|
+
|
|
306
323
|
const recommendedApps = ([
|
|
307
324
|
{ name: "Affinity Studio", description: "Creative design suite.", websiteUrl: "https://www.affinity.studio/", alternativeToUrl: "https://alternativeto.net/software/affinity-1/", os: ["macos", "windows", "ios"], platforms: [] },
|
|
308
325
|
{ name: "Bitwarden", description: "Open source password manager.", websiteUrl: "https://bitwarden.com", alternativeToUrl: "https://alternativeto.net/software/bitwarden--free-password-manager/", repoUrl: "https://github.com/bitwarden", os: ["macos", "linux", "windows", "ios", "android"], platforms: ["webapp", "saas", "cli", "api"] },
|
|
@@ -359,16 +376,19 @@ function TabNav<T extends string>({ label, onChange, tabs, value }: { label: str
|
|
|
359
376
|
return <div aria-label={label} className="pill-tabs app-subnav" role="tablist">{tabs.map((tab) => <button aria-selected={value === tab.id} className={value === tab.id ? "active" : ""} key={tab.id} onClick={() => onChange(tab.id)} role="tab" type="button">{tab.label}</button>)}</div>;
|
|
360
377
|
}
|
|
361
378
|
|
|
362
|
-
function RecommendedAppsSurface({ apps, platform, setPlatform }: { apps: RecommendedApp[]; platform: RecommendedPlatformFilterId; setPlatform: (platform: RecommendedPlatformFilterId) => void }): ReactElement {
|
|
379
|
+
function RecommendedAppsSurface({ apps, os, platform, setOs, setPlatform }: { apps: RecommendedApp[]; os: RecommendedOsId; platform: RecommendedPlatformFilterId; setOs: (os: RecommendedOsId) => void; setPlatform: (platform: RecommendedPlatformFilterId) => void }): ReactElement {
|
|
363
380
|
return <>
|
|
364
|
-
<
|
|
381
|
+
<div className="recommended-filter-tabs">
|
|
382
|
+
<TabNav label="Recommended app platform filters" tabs={recommendedPlatformTabs} value={platform} onChange={setPlatform} />
|
|
383
|
+
<TabNav label="Recommended app operating system filters" tabs={recommendedOsTabs} value={os} onChange={setOs} />
|
|
384
|
+
</div>
|
|
365
385
|
<div className="recommended-app-grid">
|
|
366
|
-
{apps.map((app) => <RecommendedAppCard app={app} key={app.name} setPlatform={setPlatform} />)}
|
|
386
|
+
{apps.map((app) => <RecommendedAppCard app={app} key={app.name} setOs={setOs} setPlatform={setPlatform} />)}
|
|
367
387
|
</div>
|
|
368
388
|
</>;
|
|
369
389
|
}
|
|
370
390
|
|
|
371
|
-
function RecommendedAppCard({ app, setPlatform }: { app: RecommendedApp; setPlatform: (platform: RecommendedPlatformFilterId) => void }): ReactElement {
|
|
391
|
+
function RecommendedAppCard({ app, setOs, setPlatform }: { app: RecommendedApp; setOs: (os: RecommendedOsId) => void; setPlatform: (platform: RecommendedPlatformFilterId) => void }): ReactElement {
|
|
372
392
|
return <article className="recommended-app-card">
|
|
373
393
|
<div>
|
|
374
394
|
<strong>{app.name}</strong>
|
|
@@ -376,7 +396,7 @@ function RecommendedAppCard({ app, setPlatform }: { app: RecommendedApp; setPlat
|
|
|
376
396
|
</div>
|
|
377
397
|
<div className="app-icon-groups">
|
|
378
398
|
<PlatformIconList platforms={app.platforms} setPlatform={setPlatform} />
|
|
379
|
-
<OsIconList os={app.os} />
|
|
399
|
+
<OsIconList os={app.os} setOs={setOs} />
|
|
380
400
|
</div>
|
|
381
401
|
<div className="managed-app-links plain-links">
|
|
382
402
|
<OriginLink href={app.websiteUrl} label={text.website} />
|
|
@@ -410,15 +430,15 @@ function PlatformIcon({ id, setPlatform }: { id: RecommendedPlatformId; setPlatf
|
|
|
410
430
|
return <button aria-label={`Filter by ${label}`} data-tooltip={`Filter by ${label}`} onClick={() => setPlatform(id)} title={`Filter by ${label}`} type="button"><Icon aria-hidden="true" focusable="false" /></button>;
|
|
411
431
|
}
|
|
412
432
|
|
|
413
|
-
function OsIconList({ os }: { os: RecommendedOsId[] }): ReactElement | null {
|
|
433
|
+
function OsIconList({ os, setOs }: { os: RecommendedOsId[]; setOs: (os: RecommendedOsId) => void }): ReactElement | null {
|
|
414
434
|
if (os.length === 0) {
|
|
415
435
|
return null;
|
|
416
436
|
}
|
|
417
437
|
|
|
418
|
-
return <span className="os-icon-list">{os.map((item) => <OsIcon id={item} key={item} />)}</span>;
|
|
438
|
+
return <span className="os-icon-list">{os.map((item) => <OsIcon id={item} key={item} setOs={setOs} />)}</span>;
|
|
419
439
|
}
|
|
420
440
|
|
|
421
|
-
function OsIcon({ id }: { id: RecommendedOsId }): ReactElement {
|
|
441
|
+
function OsIcon({ id, setOs }: { id: RecommendedOsId; setOs: (os: RecommendedOsId) => void }): ReactElement {
|
|
422
442
|
const iconMap: Record<RecommendedOsId, { Icon: IconType; label: string }> = {
|
|
423
443
|
all: { Icon: FiGlobe, label: "Web app" },
|
|
424
444
|
macos: { Icon: FaApple, label: "macOS" },
|
|
@@ -429,7 +449,14 @@ function OsIcon({ id }: { id: RecommendedOsId }): ReactElement {
|
|
|
429
449
|
};
|
|
430
450
|
const { Icon, label } = iconMap[id];
|
|
431
451
|
|
|
432
|
-
return <
|
|
452
|
+
return <button aria-label={`Filter by ${label}`} data-tooltip={`Filter by ${label}`} onClick={() => setOs(id)} title={`Filter by ${label}`} type="button"><Icon aria-hidden="true" focusable="false" /></button>;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function recommendedAppMatchesFilters(app: RecommendedApp, platform: RecommendedPlatformFilterId, os: RecommendedOsId): boolean {
|
|
456
|
+
const platformMatches = platform === "all" || app.platforms.includes(platform);
|
|
457
|
+
const osMatches = os === "all" || app.os.includes(os);
|
|
458
|
+
|
|
459
|
+
return platformMatches && osMatches;
|
|
433
460
|
}
|
|
434
461
|
|
|
435
462
|
function applyManagedPolicyToggles(app: GuiManagedAppSummary, policy: Partial<Record<ManagedPolicyId, boolean>> | undefined): GuiManagedAppSummary {
|
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
--font-family-app: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
14
14
|
--font-size-app: 16px;
|
|
15
15
|
--glass-panel-shadow: inset 0 1px 0 rgb(255 255 255 / 58%), inset 0 -1px 0 rgb(0 0 0 / 3%), var(--shadow-soft);
|
|
16
|
+
--notification-error-bg: rgb(207 34 46 / 10%);
|
|
17
|
+
--notification-error-fg: #cf222e;
|
|
18
|
+
--notification-info-bg: rgb(9 105 218 / 10%);
|
|
19
|
+
--notification-info-fg: #0969da;
|
|
20
|
+
--notification-success-bg: rgb(26 127 55 / 10%);
|
|
21
|
+
--notification-success-fg: #1a7f37;
|
|
22
|
+
--notification-warning-bg: rgb(191 135 0 / 12%);
|
|
23
|
+
--notification-warning-fg: #9a6700;
|
|
16
24
|
--shell-gutter: 8px;
|
|
17
25
|
--shadow-soft: 0 18px 56px rgb(15 23 42 / 9%);
|
|
18
26
|
--surface-elevated: #ffffff;
|
|
@@ -43,6 +51,14 @@
|
|
|
43
51
|
--border-hover: color-mix(in srgb, rgb(255 255 255 / 22%) 74%, rgb(255 255 255 / 40%));
|
|
44
52
|
--code-bg: hsl(var(--accent-hue) 18% 6%);
|
|
45
53
|
--glass-panel-shadow: inset 0 1px 0 rgb(255 255 255 / 10%), inset 0 -1px 0 rgb(0 0 0 / 24%), var(--shadow-soft);
|
|
54
|
+
--notification-error-bg: rgb(255 123 114 / 13%);
|
|
55
|
+
--notification-error-fg: #ff7b72;
|
|
56
|
+
--notification-info-bg: rgb(88 166 255 / 13%);
|
|
57
|
+
--notification-info-fg: #58a6ff;
|
|
58
|
+
--notification-success-bg: rgb(86 211 100 / 13%);
|
|
59
|
+
--notification-success-fg: #56d364;
|
|
60
|
+
--notification-warning-bg: rgb(210 153 34 / 15%);
|
|
61
|
+
--notification-warning-fg: #d29922;
|
|
46
62
|
--shadow-soft: 0 24px 80px rgb(0 0 0 / 40%);
|
|
47
63
|
--surface-elevated: #151515;
|
|
48
64
|
--surface-muted: #202020;
|
|
@@ -1082,11 +1098,22 @@ code {
|
|
|
1082
1098
|
}
|
|
1083
1099
|
|
|
1084
1100
|
.notification-preview {
|
|
1101
|
+
background: var(--notification-bg, transparent);
|
|
1085
1102
|
border: 1px solid var(--border);
|
|
1086
1103
|
border-left: 4px solid var(--notification-color, var(--accent));
|
|
1087
1104
|
border-radius: 12px;
|
|
1105
|
+
color: var(--text-secondary);
|
|
1106
|
+
cursor: pointer;
|
|
1088
1107
|
gap: 10px;
|
|
1089
1108
|
padding: 10px;
|
|
1109
|
+
text-align: left;
|
|
1110
|
+
width: 100%;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.notification-preview:hover,
|
|
1114
|
+
.notification-preview:focus-visible {
|
|
1115
|
+
border-color: var(--notification-color, var(--accent));
|
|
1116
|
+
outline: none;
|
|
1090
1117
|
}
|
|
1091
1118
|
|
|
1092
1119
|
.notification-preview > span {
|
|
@@ -1106,29 +1133,29 @@ code {
|
|
|
1106
1133
|
|
|
1107
1134
|
.notification-preview.error,
|
|
1108
1135
|
.github-notification-card.error {
|
|
1109
|
-
--notification-color:
|
|
1110
|
-
--notification-bg:
|
|
1136
|
+
--notification-color: var(--notification-error-fg);
|
|
1137
|
+
--notification-bg: var(--notification-error-bg);
|
|
1111
1138
|
}
|
|
1112
1139
|
|
|
1113
1140
|
.notification-preview.warning,
|
|
1114
1141
|
.github-notification-card.warning {
|
|
1115
|
-
--notification-color:
|
|
1116
|
-
--notification-bg:
|
|
1142
|
+
--notification-color: var(--notification-warning-fg);
|
|
1143
|
+
--notification-bg: var(--notification-warning-bg);
|
|
1117
1144
|
}
|
|
1118
1145
|
|
|
1119
1146
|
.notification-preview.info,
|
|
1120
1147
|
.github-notification-card.info {
|
|
1121
|
-
--notification-color:
|
|
1122
|
-
--notification-bg:
|
|
1148
|
+
--notification-color: var(--notification-info-fg);
|
|
1149
|
+
--notification-bg: var(--notification-info-bg);
|
|
1123
1150
|
}
|
|
1124
1151
|
|
|
1125
1152
|
.notification-preview.success,
|
|
1126
1153
|
.github-notification-card.success {
|
|
1127
|
-
--notification-color:
|
|
1128
|
-
--notification-bg:
|
|
1154
|
+
--notification-color: var(--notification-success-fg);
|
|
1155
|
+
--notification-bg: var(--notification-success-bg);
|
|
1129
1156
|
}
|
|
1130
1157
|
|
|
1131
|
-
.popover-menu button,
|
|
1158
|
+
.popover-menu button:not(.notification-preview),
|
|
1132
1159
|
.popover-menu a {
|
|
1133
1160
|
align-items: center;
|
|
1134
1161
|
background: transparent;
|
|
@@ -1142,9 +1169,9 @@ code {
|
|
|
1142
1169
|
text-decoration: none;
|
|
1143
1170
|
}
|
|
1144
1171
|
|
|
1145
|
-
.popover-menu button:hover:not(:disabled),
|
|
1172
|
+
.popover-menu button:not(.notification-preview):hover:not(:disabled),
|
|
1146
1173
|
.popover-menu a:hover,
|
|
1147
|
-
.popover-menu button:focus-visible,
|
|
1174
|
+
.popover-menu button:not(.notification-preview):focus-visible,
|
|
1148
1175
|
.popover-menu a:focus-visible {
|
|
1149
1176
|
background: var(--surface-muted);
|
|
1150
1177
|
color: var(--accent);
|
|
@@ -2496,6 +2523,12 @@ button.managed-toggle:focus-visible {
|
|
|
2496
2523
|
justify-self: start;
|
|
2497
2524
|
}
|
|
2498
2525
|
|
|
2526
|
+
.recommended-filter-tabs {
|
|
2527
|
+
display: flex;
|
|
2528
|
+
flex-wrap: wrap;
|
|
2529
|
+
gap: 10px;
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2499
2532
|
.recommended-app-grid {
|
|
2500
2533
|
display: grid;
|
|
2501
2534
|
gap: 12px;
|
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.29.
|
|
15
|
+
# Version: 3.29.12
|
|
16
16
|
#
|
|
17
17
|
# Quick Install:
|
|
18
18
|
# npm install -g aidevops && aidevops update (recommended)
|