rails_nexus 2.0.3 → 2.1.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.
@@ -33,6 +33,7 @@ table { border-collapse: collapse; width: 100%; }
33
33
  /* ─── Tailwind-like Utility Classes ─────────────────────────── */
34
34
 
35
35
  /* Display */
36
+ [hidden] { display: none !important; }
36
37
  .hidden { display: none !important; }
37
38
  .block { display: block; }
38
39
  .inline-block { display: inline-block; }
@@ -1026,22 +1027,30 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1026
1027
  /* ─── Sidebar ───────────────────────────────────────────────── */
1027
1028
  .rn-sidebar {
1028
1029
  width: 16rem;
1029
- background: var(--rn-sidebar-bg);
1030
- color: var(--rn-sidebar-text);
1030
+ overflow: hidden;
1031
+ background: var(--rn-surface);
1032
+ color: var(--rn-text-secondary);
1031
1033
  display: flex;
1032
1034
  flex-direction: column;
1033
1035
  flex-shrink: 0;
1034
- border-right: 1px solid var(--rn-sidebar-border);
1036
+ border-right: 1px solid var(--rn-border);
1035
1037
  position: sticky;
1036
1038
  top: 0;
1037
1039
  height: 100vh;
1038
1040
  z-index: 30;
1041
+ transition: width 200ms ease, opacity 150ms ease, border-color 150ms ease;
1042
+ }
1043
+ .rn-sidebar.is-hidden {
1044
+ width: 0;
1045
+ border-right-color: transparent;
1046
+ opacity: 0;
1047
+ pointer-events: none;
1039
1048
  }
1040
1049
  @media (max-width: 1023px) { .rn-sidebar { display: none; } }
1041
1050
 
1042
1051
  .rn-sidebar-logo {
1043
1052
  padding: 1rem;
1044
- border-bottom: 1px solid var(--rn-sidebar-border);
1053
+ border-bottom: 1px solid var(--rn-border);
1045
1054
  display: flex;
1046
1055
  align-items: center;
1047
1056
  justify-content: space-between;
@@ -1061,44 +1070,150 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1061
1070
  flex-shrink: 0;
1062
1071
  }
1063
1072
  .rn-logo-text {
1073
+ display: block;
1064
1074
  font-size: 0.9375rem;
1065
1075
  font-weight: 700;
1066
1076
  letter-spacing: -0.01em;
1067
- color: #f8fafc;
1077
+ color: var(--rn-text);
1078
+ }
1079
+ .rn-logo-copy {
1080
+ display: flex;
1081
+ min-width: 0;
1082
+ flex-direction: column;
1083
+ line-height: 1.15;
1084
+ }
1085
+ .rn-logo-subtitle {
1086
+ margin-top: 0.2rem;
1087
+ color: var(--rn-text-muted);
1088
+ font-size: 0.625rem;
1089
+ font-weight: 500;
1090
+ letter-spacing: 0.04em;
1091
+ text-transform: uppercase;
1068
1092
  }
1069
1093
 
1070
1094
  .rn-sidebar-nav {
1071
1095
  flex: 1;
1072
- padding: 0.75rem;
1096
+ padding: 0.75rem 0.625rem;
1073
1097
  display: flex;
1074
1098
  flex-direction: column;
1075
- gap: 0.125rem;
1099
+ gap: 0.375rem;
1076
1100
  overflow-y: auto;
1077
1101
  }
1078
-
1079
- .rn-nav-item {
1102
+ .rn-nav-section {
1103
+ display: block;
1104
+ border: 1px solid transparent;
1105
+ border-radius: 0.625rem;
1106
+ transition: background var(--rn-transition), border-color var(--rn-transition);
1107
+ }
1108
+ .rn-nav-section[open] {
1109
+ border-color: var(--rn-border-light);
1110
+ background: color-mix(in srgb, var(--rn-bg-secondary) 55%, transparent);
1111
+ }
1112
+ .rn-nav-section-toggle {
1113
+ min-height: 2.5rem;
1114
+ padding: 0.375rem 0.5rem;
1115
+ border-radius: 0.5625rem;
1116
+ display: flex;
1117
+ align-items: center;
1118
+ justify-content: space-between;
1119
+ gap: 0.5rem;
1120
+ color: var(--rn-text-secondary);
1121
+ cursor: pointer;
1122
+ font-size: 0.75rem;
1123
+ font-weight: 650;
1124
+ letter-spacing: 0.01em;
1125
+ list-style: none;
1126
+ transition: color var(--rn-transition), background var(--rn-transition);
1127
+ user-select: none;
1128
+ }
1129
+ .rn-nav-section-toggle::-webkit-details-marker { display: none; }
1130
+ .rn-nav-section-toggle:hover,
1131
+ .rn-nav-section-toggle:focus-visible {
1132
+ background: var(--rn-surface-hover);
1133
+ color: var(--rn-text);
1134
+ outline: none;
1135
+ }
1136
+ .rn-nav-section-toggle:focus-visible { box-shadow: inset 0 0 0 2px var(--rn-primary-ring); }
1137
+ .rn-nav-section-title {
1138
+ min-width: 0;
1080
1139
  display: flex;
1081
1140
  align-items: center;
1082
1141
  gap: 0.625rem;
1083
- padding: 0.5rem 0.75rem;
1142
+ }
1143
+ .rn-nav-section-icon {
1144
+ width: 1.625rem;
1145
+ height: 1.625rem;
1146
+ border: 1px solid var(--rn-border);
1084
1147
  border-radius: 0.5rem;
1085
- font-size: 0.875rem;
1148
+ display: inline-flex;
1149
+ align-items: center;
1150
+ justify-content: center;
1151
+ flex-shrink: 0;
1152
+ background: var(--rn-surface);
1153
+ color: var(--rn-text-muted);
1154
+ box-shadow: var(--rn-shadow-sm);
1155
+ transition: color var(--rn-transition), border-color var(--rn-transition), background var(--rn-transition);
1156
+ }
1157
+ .rn-nav-section-chevron {
1158
+ width: 1.125rem;
1159
+ height: 1.125rem;
1160
+ padding: 0.125rem;
1161
+ border-radius: 999px;
1162
+ flex-shrink: 0;
1163
+ color: var(--rn-text-muted);
1164
+ transition: transform var(--rn-transition);
1165
+ }
1166
+ .rn-nav-section[open] > .rn-nav-section-toggle { color: var(--rn-text); }
1167
+ .rn-nav-section[open] > .rn-nav-section-toggle .rn-nav-section-icon {
1168
+ border-color: color-mix(in srgb, var(--rn-primary) 25%, var(--rn-border));
1169
+ background: var(--rn-primary-light);
1170
+ color: var(--rn-primary);
1171
+ box-shadow: none;
1172
+ }
1173
+ .rn-nav-section[open] > .rn-nav-section-toggle .rn-nav-section-chevron { transform: rotate(90deg); }
1174
+ .rn-nav-section-items {
1175
+ margin: 0.125rem 0.5rem 0.5rem 1.3125rem;
1176
+ padding-left: 0.6875rem;
1177
+ border-left: 1px solid var(--rn-border);
1178
+ display: grid;
1179
+ gap: 0.125rem;
1180
+ }
1181
+
1182
+ .rn-nav-item {
1183
+ position: relative;
1184
+ min-height: 2.125rem;
1185
+ display: flex;
1186
+ align-items: center;
1187
+ gap: 0.5625rem;
1188
+ padding: 0.375rem 0.625rem;
1189
+ border-radius: 0.4375rem;
1190
+ font-size: 0.8125rem;
1086
1191
  font-weight: 500;
1087
- color: var(--rn-sidebar-text);
1088
- transition: all var(--rn-transition);
1192
+ color: var(--rn-text-secondary);
1193
+ transition: color var(--rn-transition), background var(--rn-transition);
1089
1194
  text-decoration: none;
1090
1195
  }
1091
1196
  .rn-nav-item:hover {
1092
- color: var(--rn-sidebar-text-hover);
1093
- background: var(--rn-sidebar-active-bg);
1197
+ color: var(--rn-text);
1198
+ background: var(--rn-surface-hover);
1094
1199
  text-decoration: none;
1095
1200
  }
1096
1201
  .rn-nav-item.active {
1097
- color: #f8fafc;
1098
- background: var(--rn-sidebar-active-bg);
1202
+ color: var(--rn-primary);
1203
+ background: var(--rn-primary-light);
1204
+ }
1205
+ .rn-nav-item.active::before {
1206
+ position: absolute;
1207
+ top: 0.5rem;
1208
+ bottom: 0.5rem;
1209
+ left: -0.75rem;
1210
+ width: 2px;
1211
+ border-radius: 999px;
1212
+ background: var(--rn-primary);
1213
+ content: "";
1099
1214
  }
1215
+ .rn-nav-item svg { flex-shrink: 0; }
1100
1216
  .rn-nav-item--muted {
1101
- font-size: 0.8125rem;
1102
1217
  opacity: 0.8;
1103
1218
  }
1104
1219
  .rn-nav-item--muted:hover {
@@ -1110,22 +1225,42 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1110
1225
  border-radius: 0.625rem;
1111
1226
  font-size: 0.625rem;
1112
1227
  font-weight: 600;
1113
- background: var(--rn-sidebar-active-bg);
1114
- color: var(--rn-sidebar-text);
1228
+ background: var(--rn-bg-secondary);
1229
+ color: var(--rn-text-secondary);
1115
1230
  line-height: 1.25;
1116
1231
  }
1232
+ .rn-nav-item.active .rn-nav-badge {
1233
+ background: color-mix(in srgb, var(--rn-primary) 12%, var(--rn-surface));
1234
+ color: var(--rn-primary);
1235
+ }
1117
1236
  .rn-sidebar-section-divider {
1118
1237
  height: 1px;
1119
- background: var(--rn-sidebar-border);
1238
+ background: var(--rn-border);
1120
1239
  margin: 0.5rem 0;
1121
1240
  opacity: 0.5;
1122
1241
  }
1123
1242
 
1124
1243
  .rn-sidebar-footer {
1125
1244
  padding: 0.75rem 1rem;
1126
- border-top: 1px solid var(--rn-sidebar-border);
1245
+ border-top: 1px solid var(--rn-border);
1246
+ display: flex;
1247
+ align-items: center;
1248
+ justify-content: space-between;
1249
+ gap: 0.75rem;
1127
1250
  font-size: 0.6875rem;
1128
- color: #475569;
1251
+ color: var(--rn-text-muted);
1252
+ }
1253
+ .rn-sidebar-status {
1254
+ display: inline-flex;
1255
+ align-items: center;
1256
+ gap: 0.375rem;
1257
+ }
1258
+ .rn-status-dot {
1259
+ width: 0.375rem;
1260
+ height: 0.375rem;
1261
+ border-radius: 999px;
1262
+ background: var(--rn-success);
1263
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--rn-success) 14%, transparent);
1129
1264
  }
1130
1265
 
1131
1266
  /* ─── Mobile Sidebar ────────────────────────────────────────── */
@@ -1134,7 +1269,9 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1134
1269
  inset-y: 0;
1135
1270
  left: 0;
1136
1271
  width: 16rem;
1137
- background: var(--rn-sidebar-bg);
1272
+ background: var(--rn-surface);
1273
+ display: flex;
1274
+ flex-direction: column;
1138
1275
  z-index: 50;
1139
1276
  transform: translateX(-100%);
1140
1277
  transition: transform 200ms ease-out;
@@ -1168,6 +1305,61 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1168
1305
  backdrop-filter: blur(8px);
1169
1306
  background: color-mix(in srgb, var(--rn-surface) 85%, transparent);
1170
1307
  }
1308
+ .rn-topbar-title,
1309
+ .rn-topbar-actions {
1310
+ display: flex;
1311
+ align-items: center;
1312
+ }
1313
+ .rn-topbar-title { gap: 0.625rem; }
1314
+ .rn-topbar-actions { gap: 0.375rem; }
1315
+ .rn-topbar-heading {
1316
+ display: block;
1317
+ color: var(--rn-text);
1318
+ font-size: 0.875rem;
1319
+ font-weight: 650;
1320
+ line-height: 1.1;
1321
+ }
1322
+ .rn-topbar-subtitle {
1323
+ display: block;
1324
+ margin-top: 0.15rem;
1325
+ color: var(--rn-text-muted);
1326
+ font-size: 0.6875rem;
1327
+ line-height: 1.1;
1328
+ }
1329
+ .rn-sidebar-toggle { display: inline-flex; }
1330
+
1331
+ /* ─── Shell Footer ─────────────────────────────────────────── */
1332
+ .rn-footer {
1333
+ position: static;
1334
+ min-height: 3.25rem;
1335
+ padding: 0.75rem 1.5rem;
1336
+ border-top: 1px solid var(--rn-border-light);
1337
+ display: flex;
1338
+ align-items: center;
1339
+ justify-content: space-between;
1340
+ gap: 1rem;
1341
+ background: var(--rn-surface);
1342
+ color: var(--rn-text-muted);
1343
+ font-size: 0.75rem;
1344
+ }
1345
+ .rn-footer-version { color: var(--rn-text-secondary); }
1346
+ .rn-footer-credit,
1347
+ .rn-footer-github {
1348
+ display: inline-flex;
1349
+ align-items: center;
1350
+ gap: 0.375rem;
1351
+ }
1352
+ .rn-footer a { font-weight: 600; }
1353
+ @media (max-width: 639px) {
1354
+ .rn-topbar { padding: 0 1rem; }
1355
+ .rn-topbar-subtitle { display: none; }
1356
+ .rn-footer {
1357
+ align-items: flex-start;
1358
+ flex-direction: column;
1359
+ gap: 0.25rem;
1360
+ padding: 0.75rem 1rem;
1361
+ }
1362
+ }
1171
1363
 
1172
1364
  .rn-icon-btn {
1173
1365
  display: inline-flex;
@@ -1594,26 +1786,82 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1594
1786
  white-space: nowrap;
1595
1787
  }
1596
1788
 
1789
+ .rn-exceptions-panel { overflow: hidden; }
1790
+ .rn-exceptions-main {
1791
+ min-width: 0;
1792
+ display: flex;
1793
+ flex-direction: column;
1794
+ gap: 0;
1795
+ }
1796
+ .rn-exceptions-results { min-width: 0; }
1797
+ .rn-filter-disclosure { overflow: hidden; }
1798
+ .rn-filter-summary {
1799
+ padding: 0.75rem 0.875rem;
1800
+ display: flex;
1801
+ align-items: center;
1802
+ justify-content: space-between;
1803
+ gap: 0.75rem;
1804
+ color: var(--rn-text-muted);
1805
+ cursor: pointer;
1806
+ list-style: none;
1807
+ user-select: none;
1808
+ }
1809
+ .rn-filter-summary::-webkit-details-marker { display: none; }
1810
+ .rn-filter-summary:hover { background: var(--rn-surface-hover); }
1811
+ .rn-filter-count {
1812
+ padding: 0.1rem 0.4rem;
1813
+ border-radius: 999px;
1814
+ background: var(--rn-primary-light);
1815
+ color: var(--rn-primary);
1816
+ font-size: 0.625rem;
1817
+ font-weight: 700;
1818
+ }
1819
+ .rn-filter-chevron { flex-shrink: 0; transition: transform var(--rn-transition); }
1820
+ .rn-filter-disclosure[open] .rn-filter-chevron { transform: rotate(180deg); }
1821
+ .rn-filter-disclosure[open] .rn-filter-summary { border-bottom: 1px solid var(--rn-border-light); }
1822
+ .rn-exceptions-toolbar {
1823
+ min-height: 3.25rem;
1824
+ padding: 0.625rem 1rem;
1825
+ display: flex;
1826
+ align-items: center;
1827
+ justify-content: space-between;
1828
+ gap: 1rem;
1829
+ }
1830
+ .rn-exceptions-toolbar-summary,
1831
+ .rn-exceptions-toolbar-actions {
1832
+ display: flex;
1833
+ align-items: center;
1834
+ gap: 0.75rem;
1835
+ }
1836
+ .rn-exceptions-toolbar-actions { margin-left: auto; }
1597
1837
  .rn-exceptions-table {
1598
1838
  table-layout: fixed;
1599
- min-width: 74rem;
1600
- }
1601
- .rn-exceptions-table .rn-col-severity { width: 2rem; padding-right: 0; }
1602
- .rn-exceptions-table .rn-col-exception { width: 11.5rem; }
1603
- .rn-exceptions-table .rn-col-platform { width: 6.5rem; }
1604
- .rn-exceptions-table .rn-col-source { width: 12rem; }
1605
- .rn-exceptions-table .rn-col-message { width: 16rem; }
1606
- .rn-exceptions-table .rn-col-count { width: 5rem; text-align: center; }
1607
- .rn-exceptions-table .rn-col-workflow { width: 12rem; }
1608
- .rn-exceptions-table .rn-col-time { width: 7rem; }
1609
- .rn-exceptions-table .rn-col-actions { width: 5rem; }
1839
+ width: 100%;
1840
+ min-width: 0;
1841
+ }
1842
+ .rn-exceptions-table th,
1843
+ .rn-exceptions-table td { padding-right: 0.75rem; padding-left: 0.75rem; }
1844
+ .rn-exceptions-table .rn-col-exception { width: 11rem; }
1845
+ .rn-exceptions-table .rn-col-source { width: 9rem; }
1846
+ .rn-exceptions-table .rn-col-message { width: auto; }
1847
+ .rn-exceptions-table .rn-col-count { width: 4rem; text-align: center; }
1848
+ .rn-exceptions-table .rn-col-time { width: 6rem; }
1849
+ .rn-exceptions-table .rn-col-actions { width: 4rem; }
1850
+ .rn-exceptions-table .rn-exception-cell {
1851
+ min-width: 0;
1852
+ display: flex;
1853
+ align-items: flex-start;
1854
+ gap: 0.625rem;
1855
+ }
1610
1856
  .rn-exceptions-table .rn-exception-name {
1611
1857
  display: block;
1612
1858
  overflow: hidden;
1859
+ color: var(--rn-text);
1613
1860
  text-overflow: ellipsis;
1614
1861
  white-space: nowrap;
1615
- font-weight: 600;
1862
+ font-weight: 650;
1616
1863
  font-size: 0.8125rem;
1864
+ line-height: 1.35;
1617
1865
  }
1618
1866
  .rn-exceptions-table .rn-source {
1619
1867
  display: flex;
@@ -1631,7 +1879,7 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1631
1879
  overflow: hidden;
1632
1880
  color: var(--rn-text-muted);
1633
1881
  font-size: 0.6875rem;
1634
- line-height: 1;
1882
+ line-height: 1.2;
1635
1883
  text-overflow: ellipsis;
1636
1884
  white-space: nowrap;
1637
1885
  }
@@ -1642,18 +1890,141 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1642
1890
  gap: 0.25rem;
1643
1891
  }
1644
1892
  .rn-exceptions-table .rn-workflow-state .rn-row-meta { flex-basis: 100%; }
1893
+ .rn-exceptions-table .rn-msg {
1894
+ max-width: none;
1895
+ display: -webkit-box;
1896
+ overflow: hidden;
1897
+ white-space: normal;
1898
+ line-height: 1.35;
1899
+ -webkit-box-orient: vertical;
1900
+ -webkit-line-clamp: 2;
1901
+ }
1902
+ .rn-severity-dot {
1903
+ display: block;
1904
+ width: 0.5rem;
1905
+ height: 0.5rem;
1906
+ margin-top: 0.3rem;
1907
+ border-radius: 999px;
1908
+ background: var(--rn-danger);
1909
+ box-shadow: 0 0 0 3px var(--rn-danger-light);
1910
+ }
1911
+ .rn-severity-warning { background: var(--rn-warning); box-shadow: 0 0 0 3px var(--rn-warning-light); }
1912
+ .rn-severity-info { background: var(--rn-info); box-shadow: 0 0 0 3px var(--rn-info-light); }
1913
+ .rn-occurrence-count {
1914
+ display: inline-flex;
1915
+ min-width: 1.75rem;
1916
+ height: 1.5rem;
1917
+ padding: 0 0.4rem;
1918
+ border: 1px solid var(--rn-border);
1919
+ border-radius: 999px;
1920
+ align-items: center;
1921
+ justify-content: center;
1922
+ background: var(--rn-bg-secondary);
1923
+ color: var(--rn-text-secondary);
1924
+ font-size: 0.6875rem;
1925
+ font-weight: 700;
1926
+ }
1927
+ .rn-row-actions {
1928
+ display: flex;
1929
+ align-items: center;
1930
+ justify-content: flex-end;
1931
+ gap: 0.125rem;
1932
+ opacity: 0.35;
1933
+ transition: opacity var(--rn-transition);
1934
+ }
1935
+ .rn-exceptions-table tr:hover .rn-row-actions,
1936
+ .rn-exceptions-table tr:focus-within .rn-row-actions,
1937
+ .rn-exceptions-table tr.selected .rn-row-actions { opacity: 1; }
1645
1938
  .rn-exceptions-table tbody tr {
1646
1939
  position: relative;
1647
1940
  border-left: 3px solid transparent;
1941
+ outline: none;
1648
1942
  }
1649
1943
  .rn-exceptions-table tbody tr:hover {
1650
1944
  border-left-color: var(--rn-primary);
1651
1945
  }
1946
+ .rn-exceptions-table tbody tr:focus-visible {
1947
+ border-left-color: var(--rn-primary);
1948
+ box-shadow: inset 0 0 0 2px var(--rn-primary-ring);
1949
+ }
1652
1950
  .rn-exceptions-table tbody tr.selected {
1653
1951
  border-left-color: var(--rn-primary);
1654
1952
  background: var(--rn-primary-light);
1655
1953
  }
1656
1954
 
1955
+ @media (max-width: 1100px) {
1956
+ .rn-exceptions-main { width: 100%; }
1957
+ .rn-exceptions-toolbar { flex-wrap: wrap; }
1958
+ .rn-exceptions-toolbar-actions { flex-wrap: wrap; }
1959
+ .rn-exceptions-table { display: block; min-width: 0; }
1960
+ .rn-exceptions-table thead { display: none; }
1961
+ .rn-exceptions-table tbody {
1962
+ padding: 0.75rem;
1963
+ display: grid;
1964
+ gap: 0.625rem;
1965
+ }
1966
+ .rn-exceptions-table tbody tr {
1967
+ padding: 0.875rem 0.875rem 0.875rem 0.75rem;
1968
+ border: 1px solid var(--rn-border-light);
1969
+ border-left: 3px solid transparent;
1970
+ border-radius: 0.625rem;
1971
+ display: grid;
1972
+ grid-template-columns: minmax(0, 1fr) auto auto;
1973
+ grid-template-areas:
1974
+ "exception exception actions"
1975
+ "message message message"
1976
+ "source count time";
1977
+ column-gap: 0.75rem;
1978
+ row-gap: 0.5rem;
1979
+ background: var(--rn-surface);
1980
+ box-shadow: var(--rn-shadow-sm);
1981
+ }
1982
+ .rn-exceptions-table tbody tr:hover,
1983
+ .rn-exceptions-table tbody tr.selected {
1984
+ border-left-color: var(--rn-primary);
1985
+ background: var(--rn-surface-hover);
1986
+ }
1987
+ .rn-exceptions-table tbody tr.selected { box-shadow: 0 0 0 2px var(--rn-primary-ring); }
1988
+ .rn-exceptions-table td {
1989
+ width: auto !important;
1990
+ padding: 0;
1991
+ border: 0;
1992
+ min-width: 0;
1993
+ }
1994
+ .rn-exceptions-table .rn-col-exception { grid-area: exception; }
1995
+ .rn-exceptions-table .rn-col-source { grid-area: source; }
1996
+ .rn-exceptions-table .rn-col-message { grid-area: message; }
1997
+ .rn-exceptions-table .rn-col-count { grid-area: count; text-align: left; }
1998
+ .rn-exceptions-table .rn-col-time { grid-area: time; text-align: right; }
1999
+ .rn-exceptions-table .rn-col-actions { grid-area: actions; }
2000
+ .rn-exceptions-table .rn-exception-name { font-size: 0.875rem; }
2001
+ .rn-exceptions-table .rn-source::before {
2002
+ margin-right: 0.375rem;
2003
+ color: var(--rn-text-muted);
2004
+ content: "Source";
2005
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
2006
+ font-size: 0.6875rem;
2007
+ }
2008
+ .rn-exceptions-table .rn-row-actions { opacity: 1; }
2009
+ }
2010
+
2011
+ @media (max-width: 639px) {
2012
+ .rn-exceptions-toolbar { align-items: flex-start; flex-direction: column; }
2013
+ .rn-exceptions-toolbar-summary { width: 100%; justify-content: space-between; }
2014
+ .rn-exceptions-toolbar-actions { width: 100%; margin-left: 0; justify-content: flex-end; }
2015
+ .rn-shortcuts-hint { display: none; }
2016
+ .rn-exceptions-table tbody { padding: 0.5rem; }
2017
+ .rn-exceptions-table tbody tr {
2018
+ grid-template-columns: minmax(0, 1fr) auto auto;
2019
+ grid-template-areas:
2020
+ "exception actions actions"
2021
+ "message message message"
2022
+ "source source source"
2023
+ "count time time";
2024
+ }
2025
+ .rn-exceptions-table .rn-col-time { text-align: left; }
2026
+ }
2027
+
1657
2028
  /* ─── Row count indicator ───────────────────────────────────── */
1658
2029
  .rn-count-indicator {
1659
2030
  width: 1.5rem;
@@ -1956,9 +2327,14 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
1956
2327
  .rn-font-semibold { font-weight: 600; }
1957
2328
 
1958
2329
  /* ─── Layout (override host app resets) ─────────────────── */
1959
- html { height: 100%; }
1960
- body { height: 100vh; overflow: hidden; margin: 0; }
1961
- body > div.min-h-full { height: 100vh; min-height: 100vh; }
2330
+ html { min-height: 100%; }
2331
+ body { min-height: 100%; height: auto; overflow: visible; margin: 0; }
2332
+ body > div.min-h-full { min-height: 100vh; height: auto; }
1962
2333
  body > div.min-h-full > .rn-sidebar { height: 100vh; min-height: 100vh; }
1963
- body > div.min-h-full > div:last-child { display: flex; flex-direction: column; overflow: hidden; }
1964
- body > div.min-h-full > div:last-child > main { flex: 1; overflow-y: auto; }
2334
+ body > div.min-h-full > div:last-child {
2335
+ min-height: 100vh;
2336
+ display: flex;
2337
+ flex-direction: column;
2338
+ overflow: visible;
2339
+ }
2340
+ body > div.min-h-full > div:last-child > main { flex: 1; overflow: visible; }
@@ -9,14 +9,6 @@ module RailsNexus
9
9
  # GET /rails_nexus/backup
10
10
  def index
11
11
  @configs = RailsNexus::BackupConfig.order(:name)
12
- @records = RailsNexus::Backup.order(started_at: :desc).limit(20)
13
- @stats = {
14
- total_configs: RailsNexus::BackupConfig.count,
15
- enabled_configs: RailsNexus::BackupConfig.enabled.count,
16
- total_backups: RailsNexus::Backup.count,
17
- successful: RailsNexus::Backup.successful.count,
18
- failed: RailsNexus::Backup.failed.count
19
- }
20
12
  end
21
13
 
22
14
  # GET /rails_nexus/backup/new
@@ -47,7 +39,13 @@ module RailsNexus
47
39
 
48
40
  # PATCH /rails_nexus/backup/:id
49
41
  def update
50
- if @config.update(config_params)
42
+ # Strip blank password fields so they don't overwrite stored values
43
+ filtered = config_params
44
+ %w[password encryption_password gpg_password].each do |attr|
45
+ filtered = filtered.except(attr) if filtered[attr].blank?
46
+ end
47
+
48
+ if @config.update(filtered)
51
49
  redirect_to backup_path, notice: "Backup config '#{@config.name}' updated."
52
50
  else
53
51
  render :edit, status: :unprocessable_entity
@@ -63,19 +61,24 @@ module RailsNexus
63
61
 
64
62
  # POST /rails_nexus/backup/:id/trigger
65
63
  def trigger
66
- result = RailsNexus::BackupService.run(@config)
67
-
68
- if result[:success]
69
- redirect_to backup_path, notice: "Backup '#{@config.name}' completed successfully."
70
- else
71
- redirect_to backup_path, alert: "Backup failed: #{result[:error]}"
64
+ begin
65
+ RailsNexus::BackupJob.perform_later(@config.id)
66
+ redirect_to backup_path, notice: "Backup '#{@config.name}' started in background."
67
+ rescue => e
68
+ # Fallback to synchronous if Active Job is not configured
69
+ result = RailsNexus::BackupService.run(@config)
70
+ if result[:success]
71
+ redirect_to backup_path, notice: "Backup '#{@config.name}' completed successfully."
72
+ else
73
+ redirect_to backup_path, alert: "Backup failed: #{result[:error]}"
74
+ end
72
75
  end
73
76
  end
74
77
 
75
78
  # GET /rails_nexus/backup/:id/history
76
79
  def history
77
80
  @config = RailsNexus::BackupConfig.find(params[:id])
78
- @records = RailsNexus::Backup.where(model_name: @config.name)
81
+ @records = RailsNexus::Backup.where(config_name: @config.name)
79
82
  .order(started_at: :desc)
80
83
  .limit(50)
81
84
  end
@@ -83,6 +86,7 @@ module RailsNexus
83
86
  # GET /rails_nexus/backup/history
84
87
  def all_history
85
88
  @records = RailsNexus::Backup.order(started_at: :desc).limit(100)
89
+ render :history
86
90
  end
87
91
 
88
92
  private
@@ -95,11 +99,15 @@ module RailsNexus
95
99
  params.require(:backup_config).permit(
96
100
  :name, :description, :database_name, :adapter, :host, :port,
97
101
  :username, :password, :storage_path, :keep_count,
98
- :compress, :encrypt, :encrypt_password,
99
- :rsync_enabled, :rsync_host, :rsync_port, :rsync_user, :rsync_path, :rsync_mirror,
102
+ :compress, :encrypted, :encryption_password,
103
+ :rsync_enabled, :rsync_host, :rsync_port, :rsync_user, :rsync_path, :rsync_mirror, :rsync_archive, :rsync_directories, :rsync_excludes,
100
104
  :notify_command, :notify_on_success, :notify_on_failure,
101
105
  :schedule_cron, :enabled,
102
- skip_tables: []
106
+ :s3_enabled, :s3_access_key, :s3_secret_key, :s3_bucket, :s3_region, :s3_prefix,
107
+ :gpg_enabled, :gpg_password,
108
+ :email_notify, :email_to,
109
+ :archive_enabled, :bzip2_compress, :split_chunks, :encrypt_base64, :mysql_additional_options,
110
+ skip_tables: [], archive_paths: [], archive_excludes: []
103
111
  )
104
112
  end
105
113
 
@@ -1,5 +1,10 @@
1
1
  module RailsNexus
2
2
  module ApplicationHelper
3
3
  include Pagy::Frontend
4
+
5
+ def page_title(text)
6
+ title = [RailsNexus.application_name.presence, text].compact.join(" :: ")
7
+ content_for(:title, title)
8
+ end
4
9
  end
5
10
  end