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.
- package/README.md +10 -10
- package/VERSION +1 -1
- package/aidevops.sh +1 -1
- package/package.json +1 -1
- package/packages/gui-api/src/status-adapter.ts +1 -0
- package/packages/gui-api/src/status-vault.ts +61 -18
- package/packages/gui-desktop/scripts/install-macos-app.sh +88 -3
- package/packages/gui-shared/src/fixtures.ts +1 -6
- package/packages/gui-web/src/App.tsx +10 -56
- package/packages/gui-web/src/AppWorkspace.tsx +5 -5
- package/packages/gui-web/src/DesktopStatusBar.tsx +1 -1
- package/packages/gui-web/src/SecuritySurface.tsx +187 -0
- package/packages/gui-web/src/StatusSurfaces.tsx +4 -170
- package/packages/gui-web/src/VaultAccessModal.tsx +123 -0
- package/packages/gui-web/src/VaultBadges.tsx +96 -36
- package/packages/gui-web/src/VaultSurface.tsx +282 -0
- package/packages/gui-web/src/status-client.ts +72 -24
- package/packages/gui-web/src/styles.css +385 -34
- package/packages/gui-web/src/useAppNavigation.ts +55 -0
- package/packages/gui-web/src/useGuiStatus.ts +79 -0
- package/packages/gui-web/src/useVaultAccessDialog.ts +160 -0
- package/packages/gui-web/src/vault-command-bridge.ts +27 -0
- package/setup.sh +3 -1
- package/packages/gui-web/src/VaultPassphraseModal.tsx +0 -147
|
@@ -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:
|
|
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:
|
|
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-
|
|
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
|
+
}
|