aidevops 3.31.0 → 3.31.1

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 CHANGED
@@ -58,7 +58,7 @@ The result: an AI operations platform that manages projects across every busines
58
58
  [![Copyright](https://img.shields.io/badge/Copyright-Marcus%20Quinn%202025--2026-blue.svg)](https://github.com/marcusquinn)
59
59
 
60
60
  <!-- Release & Version Info -->
61
- [![Version](https://img.shields.io/badge/Version-3.31.0-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
61
+ [![Version](https://img.shields.io/badge/Version-3.31.1-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
62
62
  [![npm version](https://img.shields.io/npm/v/aidevops)](https://www.npmjs.com/package/aidevops)
63
63
  [![Homebrew](https://img.shields.io/badge/homebrew-marcusquinn%2Ftap-orange)](https://github.com/marcusquinn/homebrew-tap)
64
64
  [![GitHub repository](https://img.shields.io/badge/github-repository-181717.svg?logo=github)](https://github.com/marcusquinn/aidevops)
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.31.0
1
+ 3.31.1
package/aidevops.sh CHANGED
@@ -5,7 +5,7 @@
5
5
  # AI DevOps Framework CLI
6
6
  # Usage: aidevops <command> [options]
7
7
  #
8
- # Version: 3.31.0
8
+ # Version: 3.31.1
9
9
 
10
10
  set -euo pipefail
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidevops",
3
- "version": "3.31.0",
3
+ "version": "3.31.1",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -813,7 +813,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate,
813
813
  toggleButton.centerYAnchor.constraint(equalTo: overlay.centerYAnchor, constant: 3.5),
814
814
  toggleButton.heightAnchor.constraint(equalToConstant: 22),
815
815
  toggleButton.widthAnchor.constraint(equalToConstant: 22),
816
- cameraButton.trailingAnchor.constraint(equalTo: overlay.trailingAnchor, constant: -92),
816
+ cameraButton.trailingAnchor.constraint(equalTo: overlay.trailingAnchor, constant: -24),
817
817
  cameraButton.centerYAnchor.constraint(equalTo: overlay.centerYAnchor, constant: 3.5),
818
818
  cameraButton.heightAnchor.constraint(equalToConstant: 22),
819
819
  cameraButton.widthAnchor.constraint(equalToConstant: 22)
@@ -1010,8 +1010,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate,
1010
1010
  let offsetKey = Int(actualOffset.rounded())
1011
1011
  var nextCapturedOffsets = capturedOffsets
1012
1012
  if capturedOffsets.contains(offsetKey) {
1013
- self.restoreWorkspaceScroll(originalScrollTop)
1014
- self.saveScreenshot(image: composite, nameComponent: pageName)
1013
+ self.savePageScreenshotAfterRestoringScroll(image: composite, nameComponent: pageName, scrollTop: originalScrollTop)
1015
1014
  return
1016
1015
  }
1017
1016
  nextCapturedOffsets.insert(offsetKey)
@@ -1027,8 +1026,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate,
1027
1026
  }
1028
1027
 
1029
1028
  if actualOffset + viewportHeight >= contentHeight - 1 {
1030
- self.restoreWorkspaceScroll(originalScrollTop)
1031
- self.saveScreenshot(image: composite, nameComponent: pageName)
1029
+ self.savePageScreenshotAfterRestoringScroll(image: composite, nameComponent: pageName, scrollTop: originalScrollTop)
1032
1030
  } else {
1033
1031
  let nextOffset = min(contentHeight - viewportHeight, actualOffset + viewportHeight)
1034
1032
  self.captureWorkspaceChunk(rect: rect, contentHeight: contentHeight, viewportHeight: viewportHeight, originalScrollTop: originalScrollTop, targetOffset: nextOffset, pageName: pageName, composite: composite, capturedOffsets: nextCapturedOffsets)
@@ -1042,6 +1040,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate,
1042
1040
  webView.evaluateJavaScript("document.querySelector('.workspace-scroll')?.scrollTo({ top: \(Int(scrollTop)), behavior: 'instant' }); true;", completionHandler: nil)
1043
1041
  }
1044
1042
 
1043
+ private func savePageScreenshotAfterRestoringScroll(image: NSImage, nameComponent: String, scrollTop: CGFloat) {
1044
+ webView.evaluateJavaScript("document.querySelector('.workspace-scroll')?.scrollTo({ top: \(Int(scrollTop)), behavior: 'instant' }); true;") { [weak self] _, _ in
1045
+ DispatchQueue.main.async {
1046
+ self?.saveScreenshot(image: image, nameComponent: nameComponent)
1047
+ }
1048
+ }
1049
+ }
1050
+
1045
1051
  private func saveScreenshot(image: NSImage, nameComponent: String) {
1046
1052
  guard let tiff = image.tiffRepresentation,
1047
1053
  let bitmap = NSBitmapImageRep(data: tiff),
@@ -1,10 +1,14 @@
1
- import { type ReactElement, useEffect, useState } from "react";
1
+ import { type ReactElement, useEffect, useRef, useState } from "react";
2
2
 
3
3
  export interface ScreenshotCapturedDetail {
4
4
  path: string;
5
5
  url: string;
6
6
  }
7
7
 
8
+ interface ScreenshotCapturedNotification extends ScreenshotCapturedDetail {
9
+ id: number;
10
+ }
11
+
8
12
  interface WebKitExternalLinkWindow extends Window {
9
13
  webkit?: {
10
14
  messageHandlers?: {
@@ -25,21 +29,23 @@ export function ScreenshotCaptureNotification({ notification, onDismiss }: { not
25
29
  <div className="screenshot-capture-notification" role="status">
26
30
  <div>
27
31
  <strong>Screenshot captured</strong>
28
- <span>Saved to <button onClick={revealScreenshot} type="button">{notification.path}</button>; path copied to clipboard.</span>
32
+ <span>Saved to <button className="screenshot-path-button" onClick={revealScreenshot} title={notification.path} type="button">{notification.path}</button>; path copied to clipboard.</span>
29
33
  </div>
30
- <button aria-label="Dismiss screenshot notification" onClick={onDismiss} type="button">×</button>
34
+ <button aria-label="Dismiss screenshot notification" className="screenshot-dismiss-button" onClick={onDismiss} type="button">×</button>
31
35
  </div>
32
36
  );
33
37
  }
34
38
 
35
39
  export function ScreenshotCaptureNotificationHost(): ReactElement | null {
36
- const [screenshotNotification, setScreenshotNotification] = useState<ScreenshotCapturedDetail | undefined>();
40
+ const nextNotificationId = useRef(0);
41
+ const [screenshotNotifications, setScreenshotNotifications] = useState<ScreenshotCapturedNotification[]>([]);
37
42
 
38
43
  useEffect(() => {
39
44
  const showScreenshotNotification = (event: Event) => {
40
45
  const detail = (event as CustomEvent<Partial<ScreenshotCapturedDetail>>).detail;
41
46
  if (detail !== undefined && typeof detail.path === "string" && typeof detail.url === "string") {
42
- setScreenshotNotification({ path: detail.path, url: detail.url });
47
+ nextNotificationId.current += 1;
48
+ setScreenshotNotifications((current) => [...current, { id: nextNotificationId.current, path: detail.path, url: detail.url }]);
43
49
  }
44
50
  };
45
51
 
@@ -47,5 +53,15 @@ export function ScreenshotCaptureNotificationHost(): ReactElement | null {
47
53
  return () => window.removeEventListener("aidevops:screenshot-captured", showScreenshotNotification);
48
54
  }, []);
49
55
 
50
- return screenshotNotification ? <ScreenshotCaptureNotification notification={screenshotNotification} onDismiss={() => setScreenshotNotification(undefined)} /> : null;
56
+ return screenshotNotifications.length > 0 ? (
57
+ <div className="screenshot-capture-notification-stack">
58
+ {screenshotNotifications.map((notification) => (
59
+ <ScreenshotCaptureNotification
60
+ key={notification.id}
61
+ notification={notification}
62
+ onDismiss={() => setScreenshotNotifications((current) => current.filter((item) => item.id !== notification.id))}
63
+ />
64
+ ))}
65
+ </div>
66
+ ) : null;
51
67
  }
@@ -298,6 +298,18 @@ code {
298
298
  z-index: 20;
299
299
  }
300
300
 
301
+ .screenshot-capture-notification-stack {
302
+ display: grid;
303
+ gap: 8px;
304
+ left: 50%;
305
+ max-width: min(840px, calc(100vw - 48px));
306
+ position: fixed;
307
+ top: calc(var(--desktop-titlebar-height) + 10px);
308
+ transform: translateX(-50%);
309
+ width: max-content;
310
+ z-index: 40;
311
+ }
312
+
301
313
  .screenshot-capture-notification {
302
314
  align-items: center;
303
315
  background: color-mix(in srgb, var(--surface-elevated) 94%, transparent);
@@ -307,13 +319,9 @@ code {
307
319
  color: var(--text-secondary);
308
320
  display: flex;
309
321
  gap: 12px;
310
- left: 50%;
311
322
  max-width: min(720px, calc(100vw - 48px));
312
323
  padding: 8px 10px 8px 14px;
313
- position: fixed;
314
- top: calc(var(--desktop-titlebar-height) + 10px);
315
- transform: translateX(-50%);
316
- z-index: 40;
324
+ width: 100%;
317
325
  }
318
326
 
319
327
  .screenshot-capture-notification > div {
@@ -333,31 +341,42 @@ code {
333
341
  line-height: 1.25;
334
342
  overflow: hidden;
335
343
  text-overflow: ellipsis;
336
- white-space: nowrap;
337
344
  }
338
345
 
339
- .screenshot-capture-notification button {
346
+ .screenshot-capture-notification .screenshot-path-button {
340
347
  background: transparent;
341
348
  border: 0;
342
349
  color: var(--accent);
343
350
  font: inherit;
344
351
  padding: 0;
345
352
  text-decoration: underline;
353
+ overflow-wrap: anywhere;
354
+ text-align: left;
346
355
  }
347
356
 
348
- .screenshot-capture-notification > button {
357
+ .screenshot-capture-notification .screenshot-dismiss-button {
349
358
  align-items: center;
350
359
  background: var(--surface-muted);
351
- border: 1px solid transparent;
360
+ border: 1px solid var(--border);
352
361
  border-radius: 999px;
353
362
  color: var(--text-secondary);
354
363
  display: inline-flex;
355
364
  flex: 0 0 auto;
356
- font-size: 1rem;
357
- height: 24px;
365
+ font: inherit;
366
+ font-size: 1.1rem;
367
+ height: 38px;
358
368
  justify-content: center;
359
369
  line-height: 1;
360
- width: 24px;
370
+ padding: 0;
371
+ text-decoration: none;
372
+ width: 38px;
373
+ }
374
+
375
+ .screenshot-capture-notification .screenshot-dismiss-button:hover,
376
+ .screenshot-capture-notification .screenshot-dismiss-button:focus-visible {
377
+ border-color: var(--border-accent);
378
+ color: var(--accent);
379
+ outline: none;
361
380
  }
362
381
 
363
382
  .machine-rail,
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.31.0
15
+ # Version: 3.31.1
16
16
  #
17
17
  # Quick Install:
18
18
  # npm install -g aidevops && aidevops update (recommended)