aidevops 3.32.5 → 3.32.7

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.
@@ -1671,7 +1671,6 @@ code {
1671
1671
 
1672
1672
  .vault-modal-backdrop {
1673
1673
  align-items: center;
1674
- backdrop-filter: blur(10px) saturate(130%);
1675
1674
  background: rgb(0 0 0 / 46%);
1676
1675
  display: flex;
1677
1676
  inset: 0;
@@ -1679,26 +1678,30 @@ code {
1679
1678
  padding: 24px;
1680
1679
  position: fixed;
1681
1680
  z-index: 30;
1682
- -webkit-backdrop-filter: blur(10px) saturate(130%);
1683
1681
  }
1684
1682
 
1685
1683
  .vault-modal {
1686
1684
  background: var(--surface-elevated);
1687
1685
  border: 1px solid var(--border-accent);
1688
- border-radius: 24px;
1689
- box-shadow: 0 30px 90px rgb(0 0 0 / 42%);
1686
+ border-radius: 8px;
1690
1687
  display: grid;
1691
1688
  gap: 18px;
1692
1689
  max-width: min(620px, calc(100vw - 40px));
1690
+ max-height: calc(100vh - 32px);
1691
+ overflow-y: auto;
1693
1692
  padding: 22px;
1694
1693
  width: 100%;
1695
1694
  }
1696
1695
 
1696
+ @supports (height: 100dvh) {
1697
+ .vault-modal {
1698
+ max-height: calc(100dvh - 32px);
1699
+ }
1700
+ }
1701
+
1697
1702
  .vault-modal-header,
1698
1703
  .vault-modal-actions,
1699
1704
  .vault-modal-body p,
1700
- .vault-field,
1701
- .vault-confirm-check,
1702
1705
  .vault-valid,
1703
1706
  .vault-invalid {
1704
1707
  margin: 0;
@@ -1714,7 +1717,7 @@ code {
1714
1717
  align-items: center;
1715
1718
  background: color-mix(in srgb, var(--accent) 14%, transparent);
1716
1719
  border: 1px solid var(--border-accent);
1717
- border-radius: 16px;
1720
+ border-radius: 8px;
1718
1721
  color: var(--accent);
1719
1722
  display: inline-flex;
1720
1723
  height: 46px;
@@ -1727,32 +1730,6 @@ code {
1727
1730
  gap: 14px;
1728
1731
  }
1729
1732
 
1730
- .vault-field {
1731
- display: grid;
1732
- gap: 7px;
1733
- }
1734
-
1735
- .vault-field span,
1736
- .vault-confirm-check {
1737
- color: var(--text-secondary);
1738
- font-size: 0.9rem;
1739
- font-weight: 800;
1740
- }
1741
-
1742
- .vault-field input {
1743
- background: var(--surface-muted);
1744
- border: 1px solid var(--border);
1745
- border-radius: 12px;
1746
- color: var(--text-primary);
1747
- padding: 12px;
1748
- }
1749
-
1750
- .vault-confirm-check {
1751
- align-items: flex-start;
1752
- display: flex;
1753
- gap: 10px;
1754
- }
1755
-
1756
1733
  .warning-notice {
1757
1734
  border-color: var(--border-warning);
1758
1735
  color: #b45309;
@@ -1774,6 +1751,52 @@ code {
1774
1751
  justify-content: flex-end;
1775
1752
  }
1776
1753
 
1754
+ .vault-modal-actions button,
1755
+ .secrets-hero-actions button {
1756
+ align-items: center;
1757
+ display: inline-flex;
1758
+ gap: 7px;
1759
+ justify-content: center;
1760
+ min-height: 44px;
1761
+ }
1762
+
1763
+ .vault-command-preview {
1764
+ align-items: center;
1765
+ background: var(--code-bg);
1766
+ border: 1px solid var(--border);
1767
+ border-radius: 6px;
1768
+ display: flex;
1769
+ gap: 10px;
1770
+ min-width: 0;
1771
+ padding: 10px 12px;
1772
+ }
1773
+
1774
+ .vault-command-preview code {
1775
+ overflow-wrap: anywhere;
1776
+ }
1777
+
1778
+ .vault-modal-state-grid {
1779
+ display: grid;
1780
+ gap: 8px;
1781
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1782
+ }
1783
+
1784
+ .vault-modal-state-grid span {
1785
+ background: var(--surface-muted);
1786
+ border: 1px solid var(--border);
1787
+ border-radius: 6px;
1788
+ color: var(--text-secondary);
1789
+ display: grid;
1790
+ gap: 4px;
1791
+ padding: 9px;
1792
+ }
1793
+
1794
+ .vault-modal-state-grid strong {
1795
+ color: var(--text-muted);
1796
+ font-size: 0.75rem;
1797
+ text-transform: uppercase;
1798
+ }
1799
+
1777
1800
  .primary-action {
1778
1801
  background: var(--accent);
1779
1802
  border: 1px solid var(--border-accent);
@@ -1782,13 +1805,25 @@ code {
1782
1805
  font-weight: 900;
1783
1806
  }
1784
1807
 
1808
+ .secrets-surface .primary-action,
1809
+ .vault-modal .primary-action {
1810
+ background: #238636;
1811
+ border-color: #2ea043;
1812
+ color: #ffffff;
1813
+ }
1814
+
1815
+ .secrets-surface .primary-action:hover,
1816
+ .vault-modal .primary-action:hover {
1817
+ background: #1f6f31;
1818
+ }
1819
+
1785
1820
  .primary-action:disabled {
1786
1821
  cursor: not-allowed;
1787
1822
  opacity: 0.5;
1788
1823
  }
1789
1824
 
1790
1825
  .vault-modal-footnote {
1791
- color: var(--text-muted);
1826
+ color: var(--text-secondary);
1792
1827
  font-size: 0.82rem;
1793
1828
  margin: 0;
1794
1829
  }
@@ -3841,6 +3876,262 @@ button.managed-toggle:focus-visible {
3841
3876
  justify-self: start;
3842
3877
  }
3843
3878
 
3879
+ .secrets-hero {
3880
+ border-radius: 8px;
3881
+ display: grid;
3882
+ gap: 16px;
3883
+ }
3884
+
3885
+ .secrets-hero-actions {
3886
+ align-items: flex-end;
3887
+ display: flex;
3888
+ flex-direction: column;
3889
+ gap: 10px;
3890
+ }
3891
+
3892
+ .secrets-state-badge,
3893
+ .secret-health,
3894
+ .metadata-only-label {
3895
+ align-items: center;
3896
+ border: 1px solid var(--border);
3897
+ border-radius: 999px;
3898
+ display: inline-flex;
3899
+ font-size: 0.78rem;
3900
+ font-weight: 900;
3901
+ gap: 6px;
3902
+ padding: 5px 9px;
3903
+ width: fit-content;
3904
+ }
3905
+
3906
+ .secrets-state-badge.success,
3907
+ .secret-health.configured {
3908
+ background: var(--notification-success-bg);
3909
+ color: var(--notification-success-fg);
3910
+ }
3911
+
3912
+ .secrets-state-badge.warning,
3913
+ .secret-health.unchecked {
3914
+ background: var(--notification-warning-bg);
3915
+ color: var(--notification-warning-fg);
3916
+ }
3917
+
3918
+ .secrets-state-badge.error,
3919
+ .secret-health.missing {
3920
+ background: var(--notification-error-bg);
3921
+ color: var(--notification-error-fg);
3922
+ }
3923
+
3924
+ .secrets-state-badge.unknown,
3925
+ .metadata-only-label {
3926
+ background: var(--surface-muted);
3927
+ color: var(--text-secondary);
3928
+ }
3929
+
3930
+ .secrets-boundary {
3931
+ align-items: flex-start;
3932
+ background: var(--surface-muted);
3933
+ border: 1px solid var(--border-accent);
3934
+ border-radius: 8px;
3935
+ display: flex;
3936
+ gap: 12px;
3937
+ padding: 14px;
3938
+ }
3939
+
3940
+ .secrets-boundary > svg {
3941
+ color: var(--accent);
3942
+ flex: 0 0 auto;
3943
+ height: 20px;
3944
+ margin-top: 2px;
3945
+ width: 20px;
3946
+ }
3947
+
3948
+ .secrets-boundary div {
3949
+ display: grid;
3950
+ gap: 4px;
3951
+ }
3952
+
3953
+ .secrets-boundary span {
3954
+ color: var(--text-secondary);
3955
+ }
3956
+
3957
+ .secrets-metric-grid,
3958
+ .secrets-capability-grid {
3959
+ display: grid;
3960
+ gap: 12px;
3961
+ grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
3962
+ }
3963
+
3964
+ .secret-metric-card,
3965
+ .secret-capability-card {
3966
+ background: var(--surface-elevated);
3967
+ border: 1px solid var(--border);
3968
+ border-radius: 8px;
3969
+ display: flex;
3970
+ gap: 12px;
3971
+ min-width: 0;
3972
+ padding: 14px;
3973
+ }
3974
+
3975
+ .secret-metric-icon,
3976
+ .secret-capability-card > span {
3977
+ align-items: center;
3978
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
3979
+ border-radius: 6px;
3980
+ color: var(--accent);
3981
+ display: inline-flex;
3982
+ flex: 0 0 auto;
3983
+ height: 36px;
3984
+ justify-content: center;
3985
+ width: 36px;
3986
+ }
3987
+
3988
+ .secret-metric-card > div,
3989
+ .secret-capability-card > div {
3990
+ display: grid;
3991
+ gap: 4px;
3992
+ min-width: 0;
3993
+ }
3994
+
3995
+ .secret-metric-card span:not(.secret-metric-icon),
3996
+ .secret-metric-card small {
3997
+ color: var(--text-secondary);
3998
+ }
3999
+
4000
+ .secret-metric-card strong {
4001
+ font-size: 1.35rem;
4002
+ letter-spacing: -0.03em;
4003
+ }
4004
+
4005
+ .secret-capability-card h3,
4006
+ .secret-capability-card p {
4007
+ margin: 0;
4008
+ }
4009
+
4010
+ .secret-capability-card h3 {
4011
+ font-size: 1rem;
4012
+ }
4013
+
4014
+ .secret-capability-card p {
4015
+ color: var(--text-secondary);
4016
+ }
4017
+
4018
+ .secrets-guidance.is-error {
4019
+ border-color: var(--border-danger);
4020
+ }
4021
+
4022
+ .secrets-evidence-strip {
4023
+ display: grid;
4024
+ gap: 8px;
4025
+ grid-template-columns: repeat(4, minmax(0, 1fr));
4026
+ margin-top: 14px;
4027
+ }
4028
+
4029
+ .secrets-evidence-strip span {
4030
+ background: var(--surface-muted);
4031
+ border-radius: 6px;
4032
+ color: var(--text-secondary);
4033
+ display: grid;
4034
+ gap: 3px;
4035
+ padding: 9px;
4036
+ }
4037
+
4038
+ .secrets-evidence-strip strong {
4039
+ color: var(--text-secondary);
4040
+ font-size: 0.72rem;
4041
+ text-transform: uppercase;
4042
+ }
4043
+
4044
+ .secrets-toolbar {
4045
+ align-items: center;
4046
+ display: flex;
4047
+ gap: 12px;
4048
+ justify-content: space-between;
4049
+ margin: 14px 0;
4050
+ }
4051
+
4052
+ .secrets-search {
4053
+ align-items: center;
4054
+ background: var(--surface-muted);
4055
+ border: 1px solid var(--border);
4056
+ border-radius: 8px;
4057
+ display: flex;
4058
+ flex: 1 1 260px;
4059
+ gap: 8px;
4060
+ max-width: 420px;
4061
+ min-height: 44px;
4062
+ padding: 0 12px;
4063
+ }
4064
+
4065
+ .secrets-search:focus-within {
4066
+ border-color: var(--border-accent);
4067
+ outline: 2px solid var(--accent);
4068
+ outline-offset: 2px;
4069
+ }
4070
+
4071
+ .secrets-search input {
4072
+ background: transparent;
4073
+ border: 0;
4074
+ color: var(--text-primary);
4075
+ flex: 1 1 auto;
4076
+ min-width: 0;
4077
+ outline: 0;
4078
+ }
4079
+
4080
+ .secrets-filters {
4081
+ display: flex;
4082
+ flex-wrap: wrap;
4083
+ gap: 6px;
4084
+ }
4085
+
4086
+ .secrets-filters button {
4087
+ background: var(--surface-muted);
4088
+ border: 1px solid var(--border);
4089
+ border-radius: 999px;
4090
+ color: var(--text-secondary);
4091
+ min-height: 36px;
4092
+ padding: 6px 10px;
4093
+ }
4094
+
4095
+ .secrets-filters button[aria-pressed="true"] {
4096
+ background: var(--accent-aaa-bg);
4097
+ color: #ffffff;
4098
+ }
4099
+
4100
+ .secrets-table-wrap {
4101
+ overflow-x: auto;
4102
+ }
4103
+
4104
+ .secrets-reference-table {
4105
+ border-collapse: collapse;
4106
+ width: 100%;
4107
+ }
4108
+
4109
+ .secrets-reference-table th,
4110
+ .secrets-reference-table td {
4111
+ border-bottom: 1px solid var(--border);
4112
+ padding: 12px 10px;
4113
+ text-align: left;
4114
+ }
4115
+
4116
+ .secrets-reference-table th {
4117
+ color: var(--text-secondary);
4118
+ font-size: 0.75rem;
4119
+ text-transform: uppercase;
4120
+ }
4121
+
4122
+ .secrets-reference-table td {
4123
+ color: var(--text-secondary);
4124
+ }
4125
+
4126
+ .secrets-reference-table td:first-child {
4127
+ color: var(--text-primary);
4128
+ }
4129
+
4130
+ :root:not([data-theme="dark"]) .secrets-state-badge.success,
4131
+ :root:not([data-theme="dark"]) .secret-health.configured {
4132
+ color: #116329;
4133
+ }
4134
+
3844
4135
  .vault-collection-list li {
3845
4136
  align-items: center;
3846
4137
  }
@@ -4602,6 +4893,19 @@ button.managed-toggle:focus-visible {
4602
4893
  min-width: 0;
4603
4894
  width: 100%;
4604
4895
  }
4896
+
4897
+ .secrets-hero-actions {
4898
+ align-items: flex-start;
4899
+ }
4900
+
4901
+ .secrets-toolbar {
4902
+ align-items: stretch;
4903
+ flex-direction: column;
4904
+ }
4905
+
4906
+ .secrets-search {
4907
+ max-width: none;
4908
+ }
4605
4909
  }
4606
4910
 
4607
4911
  @media (max-width: 720px) {
@@ -4691,4 +4995,51 @@ button.managed-toggle:focus-visible {
4691
4995
  .setup-path-row {
4692
4996
  grid-template-columns: 1fr;
4693
4997
  }
4998
+
4999
+ .vault-modal-state-grid,
5000
+ .secrets-evidence-strip {
5001
+ grid-template-columns: 1fr;
5002
+ }
5003
+
5004
+ .vault-modal-actions {
5005
+ align-items: stretch;
5006
+ flex-direction: column-reverse;
5007
+ }
5008
+
5009
+ .secrets-reference-table,
5010
+ .secrets-reference-table tbody,
5011
+ .secrets-reference-table tr,
5012
+ .secrets-reference-table td {
5013
+ display: block;
5014
+ width: 100%;
5015
+ }
5016
+
5017
+ .secrets-reference-table thead {
5018
+ display: none;
5019
+ }
5020
+
5021
+ .secrets-reference-table tr {
5022
+ background: var(--surface-muted);
5023
+ border: 1px solid var(--border);
5024
+ border-radius: 8px;
5025
+ margin-bottom: 10px;
5026
+ padding: 8px;
5027
+ }
5028
+
5029
+ .secrets-reference-table td {
5030
+ align-items: center;
5031
+ border-bottom: 0;
5032
+ display: flex;
5033
+ gap: 10px;
5034
+ justify-content: space-between;
5035
+ padding: 8px;
5036
+ }
5037
+
5038
+ .secrets-reference-table td::before {
5039
+ color: var(--text-muted);
5040
+ content: attr(data-label);
5041
+ font-size: 0.75rem;
5042
+ font-weight: 900;
5043
+ text-transform: uppercase;
5044
+ }
4694
5045
  }
@@ -0,0 +1,55 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import type { SurfaceId } from "./app-model";
3
+ import { type NavigationHistory, nextHistoryIndex, useNavigationHistoryKeyboard } from "./navigation-history";
4
+
5
+ interface AppNavigationController {
6
+ activeSurface: SurfaceId;
7
+ canGoBack: boolean;
8
+ canGoForward: boolean;
9
+ goBack: () => void;
10
+ goForward: () => void;
11
+ setActiveSurface: (surface: SurfaceId) => void;
12
+ }
13
+
14
+ export function useAppNavigation(): AppNavigationController {
15
+ const [navigation, setNavigation] = useState<NavigationHistory>({ entries: ["overview"], index: 0 });
16
+ const pendingWorkspaceSurface = useRef<SurfaceId | null>(null);
17
+ const activeSurface = navigation.entries[navigation.index] ?? "overview";
18
+ useNavigationHistoryKeyboard(setNavigation);
19
+
20
+ useEffect(() => {
21
+ if (pendingWorkspaceSurface.current !== activeSurface) {
22
+ return;
23
+ }
24
+
25
+ pendingWorkspaceSurface.current = null;
26
+ if (typeof window.matchMedia !== "function" || !window.matchMedia("(max-width: 980px)").matches) return;
27
+ const animationFrame = window.requestAnimationFrame(() => document.querySelector<HTMLElement>(".app-inset")?.scrollIntoView({ block: "start" }));
28
+ return () => window.cancelAnimationFrame(animationFrame);
29
+ }, [activeSurface]);
30
+
31
+ const setActiveSurface = (surface: SurfaceId) => {
32
+ setNavigation((current) => navigateToSurface(current, surface, pendingWorkspaceSurface));
33
+ };
34
+
35
+ return {
36
+ activeSurface,
37
+ canGoBack: navigation.index > 0,
38
+ canGoForward: navigation.index < navigation.entries.length - 1,
39
+ goBack: () => setNavigation((current) => ({ ...current, index: nextHistoryIndex(current, -1) })),
40
+ goForward: () => setNavigation((current) => ({ ...current, index: nextHistoryIndex(current, 1) })),
41
+ setActiveSurface,
42
+ };
43
+ }
44
+
45
+ function navigateToSurface(current: NavigationHistory, surface: SurfaceId, pendingWorkspaceSurface: { current: SurfaceId | null }): NavigationHistory {
46
+ const currentSurface = current.entries[current.index] ?? "overview";
47
+ if (currentSurface === surface) {
48
+ return current;
49
+ }
50
+
51
+ const entries = current.entries.slice(0, current.index + 1);
52
+ entries.push(surface);
53
+ pendingWorkspaceSurface.current = surface;
54
+ return { entries, index: entries.length - 1 };
55
+ }
@@ -0,0 +1,79 @@
1
+ import type { GuiResponseEnvelope, GuiStatusData } from "@aidevops/gui-shared";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { fetchStatus, mockedStatus, unavailableStatus } from "./status-client";
4
+ import { type VaultDialogIntent, vaultDialogIntentForStatus } from "./VaultBadges";
5
+
6
+ interface GuiStatusController {
7
+ markVaultTerminalLaunch: () => void;
8
+ refreshStatus: () => Promise<void>;
9
+ setVaultDialogIntent: (intent: VaultDialogIntent | null) => void;
10
+ status: GuiResponseEnvelope<GuiStatusData>;
11
+ statusLoading: boolean;
12
+ vaultDialogIntent: VaultDialogIntent | null;
13
+ }
14
+
15
+ export function useGuiStatus(): GuiStatusController {
16
+ const [status, setStatus] = useState<GuiResponseEnvelope<GuiStatusData>>(mockedStatus());
17
+ const [statusLoading, setStatusLoading] = useState(true);
18
+ const [vaultDialogIntent, setVaultDialogIntent] = useState<VaultDialogIntent | null>(null);
19
+ const hasPromptedVaultSetup = useRef(false);
20
+ const refreshVaultAfterTerminal = useRef(false);
21
+ const currentVaultIntent = vaultDialogIntentForStatus(status.data.vault);
22
+ const shouldPromptSetup = shouldPromptVaultSetup(statusLoading, status.data.vault, hasPromptedVaultSetup.current);
23
+
24
+ const refreshStatus = useCallback(async () => {
25
+ setStatusLoading(true);
26
+ try {
27
+ setStatus(await fetchStatus());
28
+ } catch {
29
+ setStatus(unavailableStatus());
30
+ } finally {
31
+ setStatusLoading(false);
32
+ }
33
+ }, []);
34
+
35
+ useEffect(() => {
36
+ void refreshStatus();
37
+ }, [refreshStatus]);
38
+
39
+ useEffect(() => {
40
+ const refreshAfterTerminal = () => {
41
+ if (refreshVaultAfterTerminal.current) {
42
+ refreshVaultAfterTerminal.current = false;
43
+ void refreshStatus();
44
+ }
45
+ };
46
+ window.addEventListener("focus", refreshAfterTerminal);
47
+ return () => window.removeEventListener("focus", refreshAfterTerminal);
48
+ }, [refreshStatus]);
49
+
50
+ useEffect(() => {
51
+ setVaultDialogIntent((openIntent) => openIntent !== null && openIntent !== currentVaultIntent ? null : openIntent);
52
+ }, [currentVaultIntent]);
53
+
54
+ useEffect(() => {
55
+ if (shouldPromptSetup) {
56
+ hasPromptedVaultSetup.current = true;
57
+ setVaultDialogIntent("setup");
58
+ }
59
+ }, [shouldPromptSetup]);
60
+
61
+ return {
62
+ markVaultTerminalLaunch: () => {
63
+ refreshVaultAfterTerminal.current = true;
64
+ },
65
+ refreshStatus,
66
+ setVaultDialogIntent,
67
+ status,
68
+ statusLoading,
69
+ vaultDialogIntent,
70
+ };
71
+ }
72
+
73
+ export function shouldPromptVaultSetup(statusLoading: boolean, vault: GuiStatusData["vault"], hasPromptedVaultSetup: boolean): boolean {
74
+ const setupIsReady = vault.helper_status === "available"
75
+ && vault.status === "uninitialized"
76
+ && vault.setup_state === "uninitialized"
77
+ && vault.readiness.setup_required;
78
+ return !statusLoading && setupIsReady && !hasPromptedVaultSetup;
79
+ }