@datum-cloud/activity-ui 0.6.0-dev.917556e → 0.6.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.
package/dist/index.esm.js CHANGED
@@ -1188,48 +1188,6 @@ function constructNow(date) {
1188
1188
  return constructFrom(date, Date.now());
1189
1189
  }
1190
1190
 
1191
- /**
1192
- * The {@link isSameDay} function options.
1193
- */
1194
-
1195
- /**
1196
- * @name isSameDay
1197
- * @category Day Helpers
1198
- * @summary Are the given dates in the same day (and year and month)?
1199
- *
1200
- * @description
1201
- * Are the given dates in the same day (and year and month)?
1202
- *
1203
- * @param laterDate - The first date to check
1204
- * @param earlierDate - The second date to check
1205
- * @param options - An object with options
1206
- *
1207
- * @returns The dates are in the same day (and year and month)
1208
- *
1209
- * @example
1210
- * // Are 4 September 06:00:00 and 4 September 18:00:00 in the same day?
1211
- * const result = isSameDay(new Date(2014, 8, 4, 6, 0), new Date(2014, 8, 4, 18, 0))
1212
- * //=> true
1213
- *
1214
- * @example
1215
- * // Are 4 September and 4 October in the same day?
1216
- * const result = isSameDay(new Date(2014, 8, 4), new Date(2014, 9, 4))
1217
- * //=> false
1218
- *
1219
- * @example
1220
- * // Are 4 September, 2014 and 4 September, 2015 in the same day?
1221
- * const result = isSameDay(new Date(2014, 8, 4), new Date(2015, 8, 4))
1222
- * //=> false
1223
- */
1224
- function isSameDay(laterDate, earlierDate, options) {
1225
- const [dateLeft_, dateRight_] = normalizeDates(
1226
- options?.in,
1227
- laterDate,
1228
- earlierDate,
1229
- );
1230
- return +startOfDay(dateLeft_) === +startOfDay(dateRight_);
1231
- }
1232
-
1233
1191
  /**
1234
1192
  * @name isDate
1235
1193
  * @category Common Helpers
@@ -3966,36 +3924,6 @@ function formatISO(date, options) {
3966
3924
  return result;
3967
3925
  }
3968
3926
 
3969
- /**
3970
- * The {@link isToday} function options.
3971
- */
3972
-
3973
- /**
3974
- * @name isToday
3975
- * @category Day Helpers
3976
- * @summary Is the given date today?
3977
- * @pure false
3978
- *
3979
- * @description
3980
- * Is the given date today?
3981
- *
3982
- * @param date - The date to check
3983
- * @param options - An object with options
3984
- *
3985
- * @returns The date is today
3986
- *
3987
- * @example
3988
- * // If today is 6 October 2014, is 6 October 14:00:00 today?
3989
- * const result = isToday(new Date(2014, 9, 6, 14, 0))
3990
- * //=> true
3991
- */
3992
- function isToday(date, options) {
3993
- return isSameDay(
3994
- constructFrom(date, date),
3995
- constructNow(date),
3996
- );
3997
- }
3998
-
3999
3927
  /**
4000
3928
  * The {@link subDays} function options.
4001
3929
  */
@@ -4023,36 +3951,6 @@ function subDays(date, amount, options) {
4023
3951
  return addDays(date, -amount, options);
4024
3952
  }
4025
3953
 
4026
- /**
4027
- * The {@link isYesterday} function options.
4028
- */
4029
-
4030
- /**
4031
- * @name isYesterday
4032
- * @category Day Helpers
4033
- * @summary Is the given date yesterday?
4034
- * @pure false
4035
- *
4036
- * @description
4037
- * Is the given date yesterday?
4038
- *
4039
- * @param date - The date to check
4040
- * @param options - An object with options
4041
- *
4042
- * @returns The date is yesterday
4043
- *
4044
- * @example
4045
- * // If today is 6 October 2014, is 5 October 14:00:00 yesterday?
4046
- * const result = isYesterday(new Date(2014, 9, 5, 14, 0))
4047
- * //=> true
4048
- */
4049
- function isYesterday(date, options) {
4050
- return isSameDay(
4051
- constructFrom(date, date),
4052
- subDays(constructNow(date), 1),
4053
- );
4054
- }
4055
-
4056
3954
  /**
4057
3955
  * The {@link subHours} function options.
4058
3956
  */
@@ -7994,7 +7892,7 @@ const createLucideIcon = (iconName, iconNode) => {
7994
7892
  */
7995
7893
 
7996
7894
 
7997
- const __iconNode$o = [
7895
+ const __iconNode$n = [
7998
7896
  [
7999
7897
  "path",
8000
7898
  {
@@ -8003,7 +7901,7 @@ const __iconNode$o = [
8003
7901
  }
8004
7902
  ]
8005
7903
  ];
8006
- const Activity = createLucideIcon("activity", __iconNode$o);
7904
+ const Activity = createLucideIcon("activity", __iconNode$n);
8007
7905
 
8008
7906
  /**
8009
7907
  * @license lucide-react v0.577.0 - ISC
@@ -8013,7 +7911,7 @@ const Activity = createLucideIcon("activity", __iconNode$o);
8013
7911
  */
8014
7912
 
8015
7913
 
8016
- const __iconNode$n = [
7914
+ const __iconNode$m = [
8017
7915
  ["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
8018
7916
  [
8019
7917
  "path",
@@ -8023,7 +7921,7 @@ const __iconNode$n = [
8023
7921
  }
8024
7922
  ]
8025
7923
  ];
8026
- const Bell = createLucideIcon("bell", __iconNode$n);
7924
+ const Bell = createLucideIcon("bell", __iconNode$m);
8027
7925
 
8028
7926
  /**
8029
7927
  * @license lucide-react v0.577.0 - ISC
@@ -8033,8 +7931,8 @@ const Bell = createLucideIcon("bell", __iconNode$n);
8033
7931
  */
8034
7932
 
8035
7933
 
8036
- const __iconNode$m = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
8037
- const Check = createLucideIcon("check", __iconNode$m);
7934
+ const __iconNode$l = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
7935
+ const Check = createLucideIcon("check", __iconNode$l);
8038
7936
 
8039
7937
  /**
8040
7938
  * @license lucide-react v0.577.0 - ISC
@@ -8044,8 +7942,8 @@ const Check = createLucideIcon("check", __iconNode$m);
8044
7942
  */
8045
7943
 
8046
7944
 
8047
- const __iconNode$l = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
8048
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$l);
7945
+ const __iconNode$k = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
7946
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$k);
8049
7947
 
8050
7948
  /**
8051
7949
  * @license lucide-react v0.577.0 - ISC
@@ -8055,8 +7953,8 @@ const ChevronDown = createLucideIcon("chevron-down", __iconNode$l);
8055
7953
  */
8056
7954
 
8057
7955
 
8058
- const __iconNode$k = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
8059
- const ChevronRight = createLucideIcon("chevron-right", __iconNode$k);
7956
+ const __iconNode$j = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
7957
+ const ChevronRight = createLucideIcon("chevron-right", __iconNode$j);
8060
7958
 
8061
7959
  /**
8062
7960
  * @license lucide-react v0.577.0 - ISC
@@ -8066,8 +7964,8 @@ const ChevronRight = createLucideIcon("chevron-right", __iconNode$k);
8066
7964
  */
8067
7965
 
8068
7966
 
8069
- const __iconNode$j = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
8070
- const ChevronUp = createLucideIcon("chevron-up", __iconNode$j);
7967
+ const __iconNode$i = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
7968
+ const ChevronUp = createLucideIcon("chevron-up", __iconNode$i);
8071
7969
 
8072
7970
  /**
8073
7971
  * @license lucide-react v0.577.0 - ISC
@@ -8077,12 +7975,12 @@ const ChevronUp = createLucideIcon("chevron-up", __iconNode$j);
8077
7975
  */
8078
7976
 
8079
7977
 
8080
- const __iconNode$i = [
7978
+ const __iconNode$h = [
8081
7979
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8082
7980
  ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
8083
7981
  ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
8084
7982
  ];
8085
- const CircleAlert = createLucideIcon("circle-alert", __iconNode$i);
7983
+ const CircleAlert = createLucideIcon("circle-alert", __iconNode$h);
8086
7984
 
8087
7985
  /**
8088
7986
  * @license lucide-react v0.577.0 - ISC
@@ -8092,11 +7990,11 @@ const CircleAlert = createLucideIcon("circle-alert", __iconNode$i);
8092
7990
  */
8093
7991
 
8094
7992
 
8095
- const __iconNode$h = [
7993
+ const __iconNode$g = [
8096
7994
  ["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
8097
7995
  ["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
8098
7996
  ];
8099
- const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$h);
7997
+ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$g);
8100
7998
 
8101
7999
  /**
8102
8000
  * @license lucide-react v0.577.0 - ISC
@@ -8106,11 +8004,11 @@ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$h);
8106
8004
  */
8107
8005
 
8108
8006
 
8109
- const __iconNode$g = [
8007
+ const __iconNode$f = [
8110
8008
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8111
8009
  ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
8112
8010
  ];
8113
- const CircleCheck = createLucideIcon("circle-check", __iconNode$g);
8011
+ const CircleCheck = createLucideIcon("circle-check", __iconNode$f);
8114
8012
 
8115
8013
  /**
8116
8014
  * @license lucide-react v0.577.0 - ISC
@@ -8120,12 +8018,12 @@ const CircleCheck = createLucideIcon("circle-check", __iconNode$g);
8120
8018
  */
8121
8019
 
8122
8020
 
8123
- const __iconNode$f = [
8021
+ const __iconNode$e = [
8124
8022
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8125
8023
  ["path", { d: "m15 9-6 6", key: "1uzhvr" }],
8126
8024
  ["path", { d: "m9 9 6 6", key: "z0biqf" }]
8127
8025
  ];
8128
- const CircleX = createLucideIcon("circle-x", __iconNode$f);
8026
+ const CircleX = createLucideIcon("circle-x", __iconNode$e);
8129
8027
 
8130
8028
  /**
8131
8029
  * @license lucide-react v0.577.0 - ISC
@@ -8135,11 +8033,11 @@ const CircleX = createLucideIcon("circle-x", __iconNode$f);
8135
8033
  */
8136
8034
 
8137
8035
 
8138
- const __iconNode$e = [
8036
+ const __iconNode$d = [
8139
8037
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8140
8038
  ["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
8141
8039
  ];
8142
- const Clock = createLucideIcon("clock", __iconNode$e);
8040
+ const Clock = createLucideIcon("clock", __iconNode$d);
8143
8041
 
8144
8042
  /**
8145
8043
  * @license lucide-react v0.577.0 - ISC
@@ -8149,11 +8047,11 @@ const Clock = createLucideIcon("clock", __iconNode$e);
8149
8047
  */
8150
8048
 
8151
8049
 
8152
- const __iconNode$d = [
8050
+ const __iconNode$c = [
8153
8051
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
8154
8052
  ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
8155
8053
  ];
8156
- const Copy = createLucideIcon("copy", __iconNode$d);
8054
+ const Copy = createLucideIcon("copy", __iconNode$c);
8157
8055
 
8158
8056
  /**
8159
8057
  * @license lucide-react v0.577.0 - ISC
@@ -8163,7 +8061,7 @@ const Copy = createLucideIcon("copy", __iconNode$d);
8163
8061
  */
8164
8062
 
8165
8063
 
8166
- const __iconNode$c = [
8064
+ const __iconNode$b = [
8167
8065
  [
8168
8066
  "path",
8169
8067
  {
@@ -8173,7 +8071,7 @@ const __iconNode$c = [
8173
8071
  ],
8174
8072
  ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
8175
8073
  ];
8176
- const Eye = createLucideIcon("eye", __iconNode$c);
8074
+ const Eye = createLucideIcon("eye", __iconNode$b);
8177
8075
 
8178
8076
  /**
8179
8077
  * @license lucide-react v0.577.0 - ISC
@@ -8183,8 +8081,8 @@ const Eye = createLucideIcon("eye", __iconNode$c);
8183
8081
  */
8184
8082
 
8185
8083
 
8186
- const __iconNode$b = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
8187
- const LoaderCircle = createLucideIcon("loader-circle", __iconNode$b);
8084
+ const __iconNode$a = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
8085
+ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$a);
8188
8086
 
8189
8087
  /**
8190
8088
  * @license lucide-react v0.577.0 - ISC
@@ -8194,7 +8092,7 @@ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$b);
8194
8092
  */
8195
8093
 
8196
8094
 
8197
- const __iconNode$a = [
8095
+ const __iconNode$9 = [
8198
8096
  [
8199
8097
  "path",
8200
8098
  {
@@ -8204,7 +8102,7 @@ const __iconNode$a = [
8204
8102
  ],
8205
8103
  ["path", { d: "m15 5 4 4", key: "1mk7zo" }]
8206
8104
  ];
8207
- const Pencil = createLucideIcon("pencil", __iconNode$a);
8105
+ const Pencil = createLucideIcon("pencil", __iconNode$9);
8208
8106
 
8209
8107
  /**
8210
8108
  * @license lucide-react v0.577.0 - ISC
@@ -8214,11 +8112,11 @@ const Pencil = createLucideIcon("pencil", __iconNode$a);
8214
8112
  */
8215
8113
 
8216
8114
 
8217
- const __iconNode$9 = [
8115
+ const __iconNode$8 = [
8218
8116
  ["path", { d: "M5 12h14", key: "1ays0h" }],
8219
8117
  ["path", { d: "M12 5v14", key: "s699le" }]
8220
8118
  ];
8221
- const Plus = createLucideIcon("plus", __iconNode$9);
8119
+ const Plus = createLucideIcon("plus", __iconNode$8);
8222
8120
 
8223
8121
  /**
8224
8122
  * @license lucide-react v0.577.0 - ISC
@@ -8228,13 +8126,13 @@ const Plus = createLucideIcon("plus", __iconNode$9);
8228
8126
  */
8229
8127
 
8230
8128
 
8231
- const __iconNode$8 = [
8129
+ const __iconNode$7 = [
8232
8130
  ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
8233
8131
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
8234
8132
  ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
8235
8133
  ["path", { d: "M8 16H3v5", key: "1cv678" }]
8236
8134
  ];
8237
- const RefreshCw = createLucideIcon("refresh-cw", __iconNode$8);
8135
+ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$7);
8238
8136
 
8239
8137
  /**
8240
8138
  * @license lucide-react v0.577.0 - ISC
@@ -8244,11 +8142,11 @@ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$8);
8244
8142
  */
8245
8143
 
8246
8144
 
8247
- const __iconNode$7 = [
8145
+ const __iconNode$6 = [
8248
8146
  ["path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8", key: "1p45f6" }],
8249
8147
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }]
8250
8148
  ];
8251
- const RotateCw = createLucideIcon("rotate-cw", __iconNode$7);
8149
+ const RotateCw = createLucideIcon("rotate-cw", __iconNode$6);
8252
8150
 
8253
8151
  /**
8254
8152
  * @license lucide-react v0.577.0 - ISC
@@ -8258,32 +8156,11 @@ const RotateCw = createLucideIcon("rotate-cw", __iconNode$7);
8258
8156
  */
8259
8157
 
8260
8158
 
8261
- const __iconNode$6 = [
8159
+ const __iconNode$5 = [
8262
8160
  ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
8263
8161
  ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
8264
8162
  ];
8265
- const Search = createLucideIcon("search", __iconNode$6);
8266
-
8267
- /**
8268
- * @license lucide-react v0.577.0 - ISC
8269
- *
8270
- * This source code is licensed under the ISC license.
8271
- * See the LICENSE file in the root directory of this source tree.
8272
- */
8273
-
8274
-
8275
- const __iconNode$5 = [
8276
- ["path", { d: "M10 5H3", key: "1qgfaw" }],
8277
- ["path", { d: "M12 19H3", key: "yhmn1j" }],
8278
- ["path", { d: "M14 3v4", key: "1sua03" }],
8279
- ["path", { d: "M16 17v4", key: "1q0r14" }],
8280
- ["path", { d: "M21 12h-9", key: "1o4lsq" }],
8281
- ["path", { d: "M21 19h-5", key: "1rlt1p" }],
8282
- ["path", { d: "M21 5h-7", key: "1oszz2" }],
8283
- ["path", { d: "M8 10v4", key: "tgpxqk" }],
8284
- ["path", { d: "M8 12H3", key: "a7s4jb" }]
8285
- ];
8286
- const SlidersHorizontal = createLucideIcon("sliders-horizontal", __iconNode$5);
8163
+ const Search = createLucideIcon("search", __iconNode$5);
8287
8164
 
8288
8165
  /**
8289
8166
  * @license lucide-react v0.577.0 - ISC
@@ -9784,14 +9661,6 @@ function ActionToggle({ value, onChange, className = '', disabled = false, }) {
9784
9661
 
9785
9662
  // Debounce delay for filter changes (ms)
9786
9663
  const FILTER_DEBOUNCE_MS$1 = 300;
9787
- // Backoff for watch stream reconnects. Without this, a persistently failing
9788
- // upstream gets re-opened on every render with no delay — see
9789
- // https://github.com/datum-cloud/infra/issues/5060, where a single browser
9790
- // tab reconnected roughly 2.5 times a minute against a stream that kept
9791
- // aborting, driving the proxy's memory up until the process was OOMKilled.
9792
- const RECONNECT_BASE_DELAY_MS = 1000;
9793
- const RECONNECT_MAX_DELAY_MS = 30000;
9794
- const MAX_RECONNECT_ATTEMPTS = 8;
9795
9664
  /**
9796
9665
  * Escape a value for safe interpolation into a CEL string literal.
9797
9666
  * Prevents injection via values containing backslashes or double quotes.
@@ -9886,12 +9755,6 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
9886
9755
  const resourceVersionRef = useRef();
9887
9756
  // Track the watch stop function
9888
9757
  const watchStopRef = useRef(null);
9889
- // Consecutive watch failures since the last successful event, used to back
9890
- // off reconnect attempts. Reset on any real event (including BOOKMARK) and
9891
- // on a manual startStreaming() call.
9892
- const reconnectAttemptsRef = useRef(0);
9893
- // Pending scheduled reconnect, if any.
9894
- const reconnectTimerRef = useRef(null);
9895
9758
  // Track whether streaming should restart after filter change
9896
9759
  const shouldRestartStreamingRef = useRef(false);
9897
9760
  // Track if we've done the initial load
@@ -9966,9 +9829,6 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
9966
9829
  console.error('Watch error:', event.object);
9967
9830
  return;
9968
9831
  }
9969
- // A real event (including a BOOKMARK keep-alive) means the connection is
9970
- // healthy — reset backoff so a later failure starts from the base delay.
9971
- reconnectAttemptsRef.current = 0;
9972
9832
  if (event.type === 'BOOKMARK') {
9973
9833
  // Update resource version for resume capability
9974
9834
  if (event.object.metadata?.resourceVersion) {
@@ -10034,13 +9894,15 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10034
9894
  setActivities((prev) => prev.filter((a) => a.metadata?.name !== event.object.metadata?.name));
10035
9895
  }
10036
9896
  }, [filters]);
10037
- // Open the watch connection. Does not touch userPaused/backoff state —
10038
- // callers (startStreaming, scheduleReconnect) own that.
10039
- const connectWatch = useCallback(() => {
9897
+ // Start watching for real-time updates
9898
+ const startStreaming = useCallback(() => {
10040
9899
  if (watchStopRef.current) {
10041
9900
  // Already watching
10042
9901
  return;
10043
9902
  }
9903
+ // Explicit start clears the user-paused flag so the auto-restart
9904
+ // effects can keep the stream alive after subsequent filter changes.
9905
+ setUserPaused(false);
10044
9906
  // Clear any previous watch error when starting a new stream
10045
9907
  setWatchError(null);
10046
9908
  const params = buildWatchParams();
@@ -10054,7 +9916,6 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10054
9916
  setWatchError(streamError);
10055
9917
  setIsStreaming(false);
10056
9918
  watchStopRef.current = null;
10057
- reconnectAttemptsRef.current += 1;
10058
9919
  },
10059
9920
  onClose: () => {
10060
9921
  setIsStreaming(false);
@@ -10065,50 +9926,11 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10065
9926
  setIsStreaming(true);
10066
9927
  setNewActivitiesCount(0);
10067
9928
  }, [client, buildWatchParams, handleWatchEvent]);
10068
- // Reconnect with exponential backoff. The first attempt (no prior
10069
- // failures) connects immediately; each subsequent failure doubles the
10070
- // delay up to RECONNECT_MAX_DELAY_MS. After MAX_RECONNECT_ATTEMPTS
10071
- // consecutive failures, this stops retrying automatically — the watch
10072
- // stays stopped (with watchError set) until the user calls
10073
- // startStreaming() again, which resets the attempt count.
10074
- const scheduleReconnect = useCallback(() => {
10075
- if (reconnectTimerRef.current) {
10076
- // A reconnect is already scheduled.
10077
- return;
10078
- }
10079
- if (reconnectAttemptsRef.current >= MAX_RECONNECT_ATTEMPTS) {
10080
- return;
10081
- }
10082
- const attempt = reconnectAttemptsRef.current;
10083
- const delay = attempt === 0 ? 0 : Math.min(RECONNECT_MAX_DELAY_MS, RECONNECT_BASE_DELAY_MS * 2 ** (attempt - 1));
10084
- reconnectTimerRef.current = setTimeout(() => {
10085
- reconnectTimerRef.current = null;
10086
- connectWatch();
10087
- }, delay);
10088
- }, [connectWatch]);
10089
- // Start watching for real-time updates. This is the user/effect-facing
10090
- // entry point: it resets backoff and any pending scheduled reconnect
10091
- // before connecting, so a manual retry always starts from a clean slate.
10092
- const startStreaming = useCallback(() => {
10093
- // Explicit start clears the user-paused flag so the auto-restart
10094
- // effects can keep the stream alive after subsequent filter changes.
10095
- setUserPaused(false);
10096
- reconnectAttemptsRef.current = 0;
10097
- if (reconnectTimerRef.current) {
10098
- clearTimeout(reconnectTimerRef.current);
10099
- reconnectTimerRef.current = null;
10100
- }
10101
- connectWatch();
10102
- }, [connectWatch]);
10103
9929
  // Stop watching. Marks the user as paused so the auto-start effects
10104
9930
  // below don't immediately re-open the stream this call just closed.
10105
9931
  const stopStreaming = useCallback(() => {
10106
9932
  setUserPaused(true);
10107
9933
  setIsStreaming(false);
10108
- if (reconnectTimerRef.current) {
10109
- clearTimeout(reconnectTimerRef.current);
10110
- reconnectTimerRef.current = null;
10111
- }
10112
9934
  if (watchStopRef.current) {
10113
9935
  watchStopRef.current();
10114
9936
  watchStopRef.current = null;
@@ -10271,10 +10093,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10271
10093
  }
10272
10094
  };
10273
10095
  }, [filters, timeRange]); // eslint-disable-line react-hooks/exhaustive-deps
10274
- // Auto-start streaming after initial load when enabled, and reconnect
10275
- // (with backoff — see scheduleReconnect) whenever a connection drops.
10276
- // Skipped when the user has explicitly paused — otherwise clicking Pause
10277
- // would be immediately undone by this effect on the next render.
10096
+ // Auto-start streaming after initial load when enabled. Skipped when
10097
+ // the user has explicitly paused — otherwise clicking Pause would be
10098
+ // immediately undone by this effect on the next render.
10278
10099
  useEffect(() => {
10279
10100
  if (enableStreaming &&
10280
10101
  autoStartStreaming &&
@@ -10282,9 +10103,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10282
10103
  activities.length > 0 &&
10283
10104
  !isStreaming &&
10284
10105
  !isLoading) {
10285
- scheduleReconnect();
10106
+ startStreaming();
10286
10107
  }
10287
- }, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
10108
+ }, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading, startStreaming]);
10288
10109
  // Restart streaming after filter change refresh completes. Also skipped
10289
10110
  // when the user has paused — a filter change shouldn't silently resume
10290
10111
  // a stream the user intentionally stopped.
@@ -10296,18 +10117,15 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10296
10117
  !isStreaming &&
10297
10118
  !isLoading) {
10298
10119
  shouldRestartStreamingRef.current = false;
10299
- scheduleReconnect();
10120
+ startStreaming();
10300
10121
  }
10301
- }, [enableStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
10122
+ }, [enableStreaming, userPaused, activities.length, isStreaming, isLoading, startStreaming]);
10302
10123
  // Cleanup on unmount
10303
10124
  useEffect(() => {
10304
10125
  return () => {
10305
10126
  if (watchStopRef.current) {
10306
10127
  watchStopRef.current();
10307
10128
  }
10308
- if (reconnectTimerRef.current) {
10309
- clearTimeout(reconnectTimerRef.current);
10310
- }
10311
10129
  if (filterDebounceRef.current) {
10312
10130
  clearTimeout(filterDebounceRef.current);
10313
10131
  }
@@ -10472,6 +10290,36 @@ function ActivityExpandedDetails({ activity, compact = false, }) {
10472
10290
  return (jsx(TooltipProvider, { children: compact ? (jsx(DetailPanelShell, { children: body })) : (jsx("div", { className: "mt-4 pt-4 border-t border-border", children: body })) }));
10473
10291
  }
10474
10292
 
10293
+ // Number of columns rendered for the feed variant. Used as the colSpan on
10294
+ // the expanded-detail row so it stretches across the full width.
10295
+ const ACTIVITY_FEED_COLUMN_COUNT = 5;
10296
+ /**
10297
+ * Get avatar initials from actor name
10298
+ */
10299
+ function getActorInitials(name) {
10300
+ const parts = name.split(/[@\s.]+/).filter(Boolean);
10301
+ if (parts.length === 0)
10302
+ return "?";
10303
+ if (parts.length === 1)
10304
+ return parts[0].charAt(0).toUpperCase();
10305
+ return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
10306
+ }
10307
+ /**
10308
+ * Get Tailwind classes for actor avatar based on actor type
10309
+ */
10310
+ function getActorAvatarClasses(actorType, compact) {
10311
+ const baseClasses = cn("rounded-full flex items-center justify-center shrink-0 font-semibold", compact ? "w-5 h-5 text-1xs" : "w-6 h-6 text-1xs");
10312
+ switch (actorType) {
10313
+ case "user":
10314
+ return cn(baseClasses, "bg-primary text-primary-foreground");
10315
+ case "controller":
10316
+ return cn(baseClasses, "bg-secondary text-secondary-foreground");
10317
+ case "machine account":
10318
+ return cn(baseClasses, "bg-muted text-muted-foreground");
10319
+ default:
10320
+ return cn(baseClasses, "bg-muted text-muted-foreground");
10321
+ }
10322
+ }
10475
10323
  /**
10476
10324
  * Extract verb from activity summary (e.g., "alice created HTTPProxy" -> "created")
10477
10325
  */
@@ -10480,24 +10328,24 @@ function extractVerb(summary) {
10480
10328
  if (words.length >= 2) {
10481
10329
  return words[1].toLowerCase();
10482
10330
  }
10483
- return 'unknown';
10331
+ return "unknown";
10484
10332
  }
10485
10333
  /**
10486
- * Normalize a verb to a canonical form used for coloring/icons/grouping.
10334
+ * Normalize verb to a canonical form for coloring
10487
10335
  */
10488
10336
  function normalizeVerb(verb) {
10489
10337
  const normalized = verb.toLowerCase();
10490
- if (normalized.includes('create') || normalized.includes('add'))
10491
- return 'create';
10492
- if (normalized.includes('delete') || normalized.includes('remove'))
10493
- return 'delete';
10494
- if (normalized.includes('update') ||
10495
- normalized.includes('patch') ||
10496
- normalized.includes('modify') ||
10497
- normalized.includes('change') ||
10498
- normalized.includes('edit'))
10499
- return 'update';
10500
- return 'other';
10338
+ if (normalized.includes("create") || normalized.includes("add"))
10339
+ return "create";
10340
+ if (normalized.includes("delete") || normalized.includes("remove"))
10341
+ return "delete";
10342
+ if (normalized.includes("update") ||
10343
+ normalized.includes("patch") ||
10344
+ normalized.includes("modify") ||
10345
+ normalized.includes("change") ||
10346
+ normalized.includes("edit"))
10347
+ return "update";
10348
+ return "other";
10501
10349
  }
10502
10350
  /**
10503
10351
  * Get icon container + icon color classes based on verb
@@ -10505,25 +10353,25 @@ function normalizeVerb(verb) {
10505
10353
  function getActionIconClasses(verb) {
10506
10354
  const normalizedVerb = normalizeVerb(verb);
10507
10355
  switch (normalizedVerb) {
10508
- case 'create':
10356
+ case "create":
10509
10357
  return {
10510
- container: 'bg-blue-50 dark:bg-blue-950',
10511
- icon: 'text-blue-500 dark:text-blue-400',
10358
+ container: "bg-[var(--success-100)]",
10359
+ icon: "text-[var(--success-500)]",
10512
10360
  };
10513
- case 'update':
10361
+ case "update":
10514
10362
  return {
10515
- container: 'bg-green-50 dark:bg-green-950',
10516
- icon: 'text-green-600 dark:text-green-400',
10363
+ container: "bg-[var(--info-100)]",
10364
+ icon: "text-[var(--info-500)]",
10517
10365
  };
10518
- case 'delete':
10366
+ case "delete":
10519
10367
  return {
10520
- container: 'bg-red-50 dark:bg-red-950',
10521
- icon: 'text-red-500 dark:text-red-400',
10368
+ container: "bg-destructive/10",
10369
+ icon: "text-destructive",
10522
10370
  };
10523
10371
  default:
10524
10372
  return {
10525
- container: 'bg-slate-100 dark:bg-slate-800',
10526
- icon: 'text-slate-500 dark:text-slate-400',
10373
+ container: "bg-muted",
10374
+ icon: "text-muted-foreground",
10527
10375
  };
10528
10376
  }
10529
10377
  }
@@ -10533,51 +10381,20 @@ function getActionIconClasses(verb) {
10533
10381
  function getTimelineIcon(verb) {
10534
10382
  const normalizedVerb = normalizeVerb(verb);
10535
10383
  switch (normalizedVerb) {
10536
- case 'create':
10384
+ case "create":
10537
10385
  return Plus;
10538
- case 'update':
10386
+ case "update":
10539
10387
  return Pencil;
10540
- case 'delete':
10388
+ case "delete":
10541
10389
  return Trash2;
10542
10390
  default:
10543
10391
  return Activity;
10544
10392
  }
10545
10393
  }
10546
-
10547
- // Number of columns rendered for the feed variant. Used as the colSpan on
10548
- // the expanded-detail row so it stretches across the full width.
10549
- const ACTIVITY_FEED_COLUMN_COUNT = 5;
10550
- /**
10551
- * Get avatar initials from actor name
10552
- */
10553
- function getActorInitials(name) {
10554
- const parts = name.split(/[@\s.]+/).filter(Boolean);
10555
- if (parts.length === 0)
10556
- return "?";
10557
- if (parts.length === 1)
10558
- return parts[0].charAt(0).toUpperCase();
10559
- return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
10560
- }
10561
- /**
10562
- * Get Tailwind classes for actor avatar based on actor type
10563
- */
10564
- function getActorAvatarClasses(actorType, compact) {
10565
- const baseClasses = cn("rounded-full flex items-center justify-center shrink-0 font-semibold", compact ? "w-5 h-5 text-1xs" : "w-6 h-6 text-1xs");
10566
- switch (actorType) {
10567
- case "user":
10568
- return cn(baseClasses, "bg-primary text-primary-foreground");
10569
- case "controller":
10570
- return cn(baseClasses, "bg-secondary text-secondary-foreground");
10571
- case "machine account":
10572
- return cn(baseClasses, "bg-muted text-muted-foreground");
10573
- default:
10574
- return cn(baseClasses, "bg-muted text-muted-foreground");
10575
- }
10576
- }
10577
10394
  /**
10578
10395
  * ActivityFeedItem renders a single activity in the feed or timeline
10579
10396
  */
10580
- function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isSelected = false, className = "", compact = false, isNew = false, variant = "feed", isLast = false, defaultExpanded = false, density = "default", }) {
10397
+ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isSelected = false, className = "", compact = false, isNew = false, variant = "feed", isLast = false, defaultExpanded = false, }) {
10581
10398
  const [isExpanded, setIsExpanded] = useState(defaultExpanded);
10582
10399
  const { spec, metadata } = activity;
10583
10400
  const { actor, summary, links, tenant } = spec;
@@ -10598,7 +10415,7 @@ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, ten
10598
10415
  if (isTimeline) {
10599
10416
  const { container: iconBg, icon: iconColor } = getActionIconClasses(verb);
10600
10417
  const Icon = getTimelineIcon(verb);
10601
- return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border", className), children: [jsxs("div", { className: cn("flex items-center gap-3 cursor-pointer group", density === "compact" ? "py-1.5" : "py-3", isSelected && "bg-muted/40"), onClick: toggleExpand, children: [jsx("div", { className: cn("rounded-md shrink-0 flex items-center justify-center", density === "compact" ? "w-6 h-6" : "w-8 h-8", iconBg, iconColor), children: jsx(Icon, { size: 16, strokeWidth: 2 }) }), jsx("div", { className: "flex-1 min-w-0 text-sm text-foreground leading-snug", children: jsx(ActivityFeedSummary, { summary: summary, links: links, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, resourceLinkContext: { tenant } }) }), tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(tenant)) : (jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: timestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx(ActivityExpandedDetails, { activity: activity, tenantLinkResolver: tenantLinkResolver, compact: true }))] }));
10418
+ return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border", className), children: [jsxs("div", { className: cn("flex items-center gap-3 py-3 cursor-pointer group", isSelected && "bg-muted/40"), onClick: toggleExpand, children: [jsx("div", { className: cn("w-8 h-8 rounded-md shrink-0 flex items-center justify-center", iconBg, iconColor), children: jsx(Icon, { size: 16, strokeWidth: 2 }) }), jsx("div", { className: "flex-1 min-w-0 text-sm text-foreground leading-snug", children: jsx(ActivityFeedSummary, { summary: summary, links: links, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, resourceLinkContext: { tenant } }) }), tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(tenant)) : (jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: timestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx(ActivityExpandedDetails, { activity: activity, tenantLinkResolver: tenantLinkResolver, compact: true }))] }));
10602
10419
  }
10603
10420
  // Feed variant — flat-column table row. Columns: Actor | Summary |
10604
10421
  // Tenant | When | expand toggle. Renders an additional TableRow with a
@@ -10891,7 +10708,7 @@ const getSelectedPreset$1 = (timeRange) => {
10891
10708
  /**
10892
10709
  * ActivityFeedFilters provides filter controls for the activity feed
10893
10710
  */
10894
- function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, layout = "inline", }) {
10711
+ function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, }) {
10895
10712
  const { resourceKinds, actorNames, apiGroups, resourceNamespaces, error: facetsError, } = useFacets(client, timeRange, filters);
10896
10713
  // Log facets error for debugging
10897
10714
  if (facetsError) {
@@ -11111,186 +10928,12 @@ function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTi
11111
10928
  clearTimeout(searchDebounceRef.current);
11112
10929
  onFiltersChangeRef.current({ ...filtersRef.current, search: undefined });
11113
10930
  }, []);
11114
- const searchInput = (jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search activities...", value: searchInputValue, onChange: handleSearchChange, className: "pl-8 h-7 text-xs pr-6" }), searchInputValue && (jsx("button", { onClick: handleSearchClear, className: "absolute right-1.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) }))] }));
11115
- const changeSourceControl = !hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled }));
11116
- const filterChips = activeFilterIds.map((filterId) => {
11117
- const config = FILTER_CONFIGS$1[filterId];
11118
- return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
11119
- ? getFilterOptions(filterId)
11120
- : undefined, onValuesChange: (values) => handleFilterChange(filterId, values), onClear: () => handleFilterClear(filterId), onPopoverClose: () => handlePopoverClose(filterId), inputMode: config.inputMode, placeholder: config.placeholder, searchPlaceholder: config.searchPlaceholder, autoOpen: pendingFilter === filterId, disabled: disabled }, filterId));
11121
- });
11122
- const addFilterControl = (jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }));
11123
- const timeRangeControl = (jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() }));
11124
- if (layout === "compact") {
11125
- const activeFilterCount = activeFilterIds.length +
11126
- (!hiddenFilters.includes("changeSource") &&
11127
- filters.changeSource &&
11128
- filters.changeSource !== "all"
11129
- ? 1
11130
- : 0);
11131
- return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [searchInput, jsxs(Popover, { children: [jsx(PopoverTrigger, { asChild: true, children: jsxs("button", { type: "button", disabled: disabled, className: cn("flex h-7 items-center gap-1.5 rounded-md border border-border bg-background px-2 text-xs", "text-muted-foreground hover:text-foreground hover:border-foreground/50 transition-colors", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", "disabled:cursor-not-allowed disabled:opacity-50"), children: [jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }), jsx("span", { className: "font-medium", children: "Filters" }), activeFilterCount > 0 && (jsx("span", { className: "ml-0.5 rounded-full bg-primary text-primary-foreground text-[10px] font-semibold leading-none px-1.5 py-0.5", children: activeFilterCount }))] }) }), jsx(PopoverContent, { className: "w-[320px] p-3", sideOffset: 4, align: "start", children: jsxs("div", { className: "flex flex-col gap-3", children: [changeSourceControl, jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [filterChips, addFilterControl] }), extraFilters, timeRangeControl] }) })] })] }) }));
11132
- }
11133
10931
  return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [!hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled })), jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search activities...", value: searchInputValue, onChange: handleSearchChange, className: "h-9 pl-9 pr-9 text-xs md:text-xs" }), searchInputValue && (jsx("button", { onClick: handleSearchClear, className: "absolute right-0 top-0 flex h-9 w-9 items-center justify-center rounded-r-lg text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) }))] }), activeFilterIds.map((filterId) => {
11134
10932
  const config = FILTER_CONFIGS$1[filterId];
11135
10933
  return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
11136
10934
  ? getFilterOptions(filterId)
11137
10935
  : undefined, onValuesChange: (values) => handleFilterChange(filterId, values), onClear: () => handleFilterClear(filterId), onPopoverClose: () => handlePopoverClose(filterId), inputMode: config.inputMode, placeholder: config.placeholder, searchPlaceholder: config.searchPlaceholder, autoOpen: pendingFilter === filterId, disabled: disabled }, filterId));
11138
- }), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }), timeRangeControl] }) }));
11139
- }
11140
-
11141
- const KIND_PLURALS = {
11142
- DNSRecordSet: "DNS records",
11143
- };
11144
- function pluralizeKind(kind, count) {
11145
- if (count === 1)
11146
- return kind;
11147
- return KIND_PLURALS[kind] ?? `${kind}s`;
11148
- }
11149
- const VERB_LABEL = {
11150
- create: "created",
11151
- update: "updated",
11152
- delete: "deleted",
11153
- other: "changed",
11154
- };
11155
- /**
11156
- * ActivityFeedGroupItem renders a run of consecutive same verb/kind/actor
11157
- * activities (see `groupActivities`) as a single collapsed row in the
11158
- * "digest" timeline. A group of size 1 renders exactly like a normal
11159
- * `ActivityFeedItem` — no visual change for ungrouped activities. Larger
11160
- * groups render a synthesized summary ("{actor} created 12 DNS records")
11161
- * that expands to reveal the individual activities underneath.
11162
- */
11163
- function ActivityFeedGroupItem({ group, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isLast = false, }) {
11164
- const [isExpanded, setIsExpanded] = useState(false);
11165
- if (group.count === 1) {
11166
- return (jsx(ActivityFeedItem, { activity: group.activities[0], resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: onActorClick, onActivityClick: onActivityClick, variant: "timeline", density: "compact", isLast: isLast }));
11167
- }
11168
- const { container: iconBg, icon: iconColor } = getActionIconClasses(group.verb);
11169
- const Icon = getTimelineIcon(group.verb);
11170
- const actorVisible = group.actorDisplayName || group.actorName;
11171
- const summary = `${actorVisible} ${VERB_LABEL[group.verb]} ${group.count} ${pluralizeKind(group.kind, group.count)}`;
11172
- const toggleExpand = (e) => {
11173
- e.stopPropagation();
11174
- setIsExpanded((prev) => !prev);
11175
- };
11176
- return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border"), children: [jsxs("div", { className: "flex items-center gap-3 py-1.5 cursor-pointer group", onClick: toggleExpand, children: [jsxs("div", { className: cn("w-6 h-6 rounded-md shrink-0 flex items-center justify-center relative", iconBg, iconColor), children: [jsx(Icon, { size: 14, strokeWidth: 2 }), jsx("span", { className: "absolute -bottom-1 -right-1 rounded-full bg-background border border-border px-1 text-[10px] font-semibold leading-tight text-foreground", children: group.count })] }), jsx("div", { className: "flex-1 min-w-0 text-xs text-foreground leading-normal", children: summary }), group.tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(group.tenant)) : (jsx(TenantBadge, { tenant: group.tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: group.latestTimestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx("div", { className: "pl-11 flex flex-col", children: group.activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: onActorClick, onActivityClick: onActivityClick, compact: true, density: "compact", variant: "timeline", isLast: index === group.activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name))) }))] }));
11177
- }
11178
-
11179
- /**
11180
- * Thin stat strip summarizing the activities currently loaded in the feed —
11181
- * e.g. "18 changes · 3 people · 1 system". Computed client-side over the
11182
- * loaded/paginated activities; it is a quick-glance headline, not a
11183
- * full-range aggregate (that would require a backend facet query).
11184
- */
11185
- function ActivityDigestSummary({ activities, className = "" }) {
11186
- const stats = useMemo(() => {
11187
- const humanActors = new Set();
11188
- const systemActors = new Set();
11189
- for (const activity of activities) {
11190
- const { actor, changeSource } = activity.spec;
11191
- if (changeSource === "human") {
11192
- humanActors.add(actor.name);
11193
- }
11194
- else {
11195
- systemActors.add(actor.name);
11196
- }
11197
- }
11198
- return {
11199
- total: activities.length,
11200
- people: humanActors.size,
11201
- systems: systemActors.size,
11202
- };
11203
- }, [activities]);
11204
- if (stats.total === 0)
11205
- return null;
11206
- const parts = [`${stats.total} ${stats.total === 1 ? "change" : "changes"}`];
11207
- if (stats.people > 0)
11208
- parts.push(`${stats.people} ${stats.people === 1 ? "person" : "people"}`);
11209
- if (stats.systems > 0)
11210
- parts.push(`${stats.systems} ${stats.systems === 1 ? "system" : "systems"}`);
11211
- return (jsx("div", { className: `px-1 pt-4 pb-2 text-xs text-muted-foreground ${className}`, children: parts.join(" · ") }));
11212
- }
11213
-
11214
- function tenantKey(tenant) {
11215
- if (!tenant)
11216
- return '';
11217
- return `${tenant.type}:${tenant.name}`;
11218
- }
11219
- function groupSignature(activity) {
11220
- const { spec } = activity;
11221
- const verb = normalizeVerb(extractVerb(spec.summary));
11222
- return [verb, spec.resource.kind, spec.actor.name, tenantKey(spec.tenant)].join('|');
11223
- }
11224
- /**
11225
- * Group consecutive activities that share verb + resource kind + actor +
11226
- * tenant. Activities are assumed to already be sorted newest-first (as
11227
- * returned by `useActivityFeed`); only adjacent items are merged, so a
11228
- * burst of matching activities collapses into one group without needing
11229
- * any time-window heuristics.
11230
- */
11231
- function groupActivities(activities) {
11232
- const groups = [];
11233
- for (const activity of activities) {
11234
- const signature = groupSignature(activity);
11235
- const last = groups[groups.length - 1];
11236
- if (last && last.key === signature) {
11237
- last.activities.push(activity);
11238
- last.count += 1;
11239
- continue;
11240
- }
11241
- const { spec } = activity;
11242
- groups.push({
11243
- key: signature,
11244
- verb: normalizeVerb(extractVerb(spec.summary)),
11245
- kind: spec.resource.kind,
11246
- actorName: spec.actor.name,
11247
- actorDisplayName: spec.actor.displayName,
11248
- tenant: spec.tenant,
11249
- activities: [activity],
11250
- count: 1,
11251
- latestTimestamp: activity.metadata?.creationTimestamp,
11252
- });
11253
- }
11254
- return groups;
11255
- }
11256
- function dayLabel(timestamp) {
11257
- if (!timestamp)
11258
- return 'Unknown date';
11259
- const date = new Date(timestamp);
11260
- if (Number.isNaN(date.getTime()))
11261
- return 'Unknown date';
11262
- if (isToday(date))
11263
- return 'Today';
11264
- if (isYesterday(date))
11265
- return 'Yesterday';
11266
- return format(date, 'MMMM d, yyyy');
11267
- }
11268
- /**
11269
- * Bucket activities into calendar-day sections (Today / Yesterday / a
11270
- * formatted date), then group consecutive matching activities within each
11271
- * day. Bucketing by day first (rather than grouping across the whole list)
11272
- * keeps a group from silently spanning a day boundary.
11273
- */
11274
- function groupActivitiesByDay(activities) {
11275
- const sections = [];
11276
- let currentLabel = null;
11277
- let currentBucket = [];
11278
- const flush = () => {
11279
- if (currentLabel !== null && currentBucket.length > 0) {
11280
- sections.push({ label: currentLabel, groups: groupActivities(currentBucket) });
11281
- }
11282
- };
11283
- for (const activity of activities) {
11284
- const label = dayLabel(activity.metadata?.creationTimestamp);
11285
- if (label !== currentLabel) {
11286
- flush();
11287
- currentLabel = label;
11288
- currentBucket = [];
11289
- }
11290
- currentBucket.push(activity);
11291
- }
11292
- flush();
11293
- return sections;
10936
+ }), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }), jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() })] }) }));
11294
10937
  }
11295
10938
 
11296
10939
  /**
@@ -11421,10 +11064,10 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
11421
11064
  // Parent containers must have proper height constraints (h-screen/h-full + overflow-hidden)
11422
11065
  const effectiveMaxHeight = maxHeight === "none" ? undefined : maxHeight;
11423
11066
  const listClasses = "flex-1 min-h-0 overflow-y-auto flex flex-col";
11424
- return (jsx(TooltipProvider, { delayDuration: 200, children: jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming && !watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--success-500)] opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-[var(--success-500)]" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming activity..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New activities will appear automatically" }) })] }) })), watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "relative flex h-2 w-2", children: jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-destructive" }) }), jsx("span", { className: "text-xs text-destructive", children: "Connection error" })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "Stream connection lost" }) })] }) })), newActivitiesCount > 0 && !watchError && (jsxs(Badge, { variant: "secondary", className: "text-xs", children: ["+", newActivitiesCount, " new"] }))] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: watchError ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: "2", children: [jsx("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), "Retry"] })) : isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(ActivityFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, hiddenFilters: hiddenFilters, layout: variant === "digest" ? "compact" : "inline" })), variant === "digest" && !isLoading && activities.length > 0 && (jsx(ActivityDigestSummary, { activities: activities })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsx(ApiErrorAlert, { error: watchError, onRetry: startStreaming, className: "mb-4", errorFormatter: errorFormatter }), !policiesLoading && hasPolicies === false && (jsxs("div", { className: "flex flex-col items-center py-12 px-8 text-center bg-muted border border-dashed border-border rounded-xl mb-4", children: [jsx("div", { className: "flex justify-center mb-4 text-muted-foreground", children: jsxs("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }), jsx("rect", { x: "9", y: "3", width: "6", height: "4", rx: "1" }), jsx("path", { d: "M9 12h6" }), jsx("path", { d: "M9 16h6" })] }) }), jsx("h3", { className: "m-0 mb-2 text-lg font-semibold text-foreground leading-snug", children: "Get started with activity logging" }), jsx("p", { className: "m-0 mb-6 text-sm leading-relaxed text-muted-foreground max-w-[400px]", children: "Activity policies define which resources to track and how to summarize changes. Create your first policy to start seeing activity logs here." }), onCreatePolicy && (jsx(Button, { onClick: onCreatePolicy, children: "Create Policy" }))] })), jsxs("div", { className: listClasses, ref: scrollContainerRef, style: effectiveMaxHeight ? { maxHeight: effectiveMaxHeight } : undefined, children: [!isLoading &&
11067
+ return (jsx(TooltipProvider, { delayDuration: 200, children: jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming && !watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--success-500)] opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-[var(--success-500)]" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming activity..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New activities will appear automatically" }) })] }) })), watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "relative flex h-2 w-2", children: jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-destructive" }) }), jsx("span", { className: "text-xs text-destructive", children: "Connection error" })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "Stream connection lost" }) })] }) })), newActivitiesCount > 0 && !watchError && (jsxs(Badge, { variant: "secondary", className: "text-xs", children: ["+", newActivitiesCount, " new"] }))] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: watchError ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: "2", children: [jsx("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), "Retry"] })) : isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(ActivityFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, hiddenFilters: hiddenFilters })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsx(ApiErrorAlert, { error: watchError, onRetry: startStreaming, className: "mb-4", errorFormatter: errorFormatter }), !policiesLoading && hasPolicies === false && (jsxs("div", { className: "flex flex-col items-center py-12 px-8 text-center bg-muted border border-dashed border-border rounded-xl mb-4", children: [jsx("div", { className: "flex justify-center mb-4 text-muted-foreground", children: jsxs("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }), jsx("rect", { x: "9", y: "3", width: "6", height: "4", rx: "1" }), jsx("path", { d: "M9 12h6" }), jsx("path", { d: "M9 16h6" })] }) }), jsx("h3", { className: "m-0 mb-2 text-lg font-semibold text-foreground leading-snug", children: "Get started with activity logging" }), jsx("p", { className: "m-0 mb-6 text-sm leading-relaxed text-muted-foreground max-w-[400px]", children: "Activity policies define which resources to track and how to summarize changes. Create your first policy to start seeing activity logs here." }), onCreatePolicy && (jsx(Button, { onClick: onCreatePolicy, children: "Create Policy" }))] })), jsxs("div", { className: listClasses, ref: scrollContainerRef, style: effectiveMaxHeight ? { maxHeight: effectiveMaxHeight } : undefined, children: [!isLoading &&
11425
11068
  activities.length === 0 &&
11426
11069
  hasPolicies !== false &&
11427
- (variant === "timeline" || variant === "digest") && (jsxs("div", { className: "py-12 text-center text-muted-foreground", children: [jsx("p", { className: "m-0", children: "No activities found" }), jsx("p", { className: "text-sm text-muted-foreground mt-2 m-0", children: "Try adjusting your filters or time range" })] })), variant === "digest" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), groupActivitiesByDay(activities).map((section) => (jsxs("div", { className: "flex flex-col", children: [jsx("div", { className: "sticky top-0 z-10 bg-card pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: section.label }), section.groups.map((group, index) => (jsx(ActivityFeedGroupItem, { group: group, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, isLast: index === section.groups.length - 1 }, `${section.label}-${group.key}-${index}`)))] }, section.label)))] })) : variant === "timeline" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })) : (jsxs(Table, { className: "w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-12", "aria-label": "Actor" }), jsx(TableHead, { children: "Summary" }), jsx(TableHead, { children: "Tenant" }), jsx(TableHead, { className: "w-[170px]", children: "When" }), jsx(TableHead, { className: "w-10", "aria-label": "Expand" })] }) }), jsxs(TableBody, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-3/4" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-5 w-32 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6" }) })] }, `sk-${index}`))) })), !isLoading &&
11070
+ variant === "timeline" && (jsxs("div", { className: "py-12 text-center text-muted-foreground", children: [jsx("p", { className: "m-0", children: "No activities found" }), jsx("p", { className: "text-sm text-muted-foreground mt-2 m-0", children: "Try adjusting your filters or time range" })] })), variant === "timeline" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })) : (jsxs(Table, { className: "w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-12", "aria-label": "Actor" }), jsx(TableHead, { children: "Summary" }), jsx(TableHead, { children: "Tenant" }), jsx(TableHead, { className: "w-[170px]", children: "When" }), jsx(TableHead, { className: "w-10", "aria-label": "Expand" })] }) }), jsxs(TableBody, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-3/4" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-5 w-32 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6" }) })] }, `sk-${index}`))) })), !isLoading &&
11428
11071
  activities.length === 0 &&
11429
11072
  hasPolicies !== false && (jsx(TableRow, { children: jsxs(TableCell, { colSpan: ACTIVITY_FEED_COLUMN_COUNT, className: "text-center py-12 text-muted-foreground", children: [jsx("div", { children: "No activities found" }), jsx("div", { className: "text-sm mt-2", children: "Try adjusting your filters or time range" })] }) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })] })), infiniteScroll && hasMore && (jsx("div", { ref: loadMoreTriggerRef, className: "h-px mt-4" })), !infiniteScroll && activities.length > 0 ? (jsxs("div", { className: "flex items-center justify-between gap-4 px-4 py-3 border-t border-border text-sm text-muted-foreground", children: [jsxs("span", { children: [activities.length, " ", activities.length === 1 ? "activity" : "activities", hasMore ? " so far" : ""] }), hasMore ? (jsx(Button, { type: "tertiary", theme: "outline", size: "small", htmlType: "button", onClick: handleLoadMoreClick, disabled: isLoading, children: isLoading ? "Loading…" : "Load more" })) : (jsx("span", { children: "End of results" }))] })) : null] })] }) }));
11430
11073
  }
@@ -16813,5 +16456,5 @@ const POLICY_FILTER_FIELDS = [
16813
16456
  },
16814
16457
  ];
16815
16458
 
16816
- export { ACTIVITY_FILTER_FIELDS, AUDIT_TEMPLATES, ActionMultiSelect, ActionToggle, ActivityApiClient, ActivityDigestSummary, ActivityExpandedDetails, ActivityFeed, ActivityFeedFilters, ActivityFeedGroupItem, ActivityFeedItem, ActivityFeedItemSkeleton, ActivityFeedSummary, ActivityLayout, ApiError, ApiErrorAlert, AuditEventViewer, AuditLogExpandedDetails, AuditLogFeedItem, AuditLogFilters, AuditLogQueryComponent, Badge, CelEditor, ChangeSourceToggle, Combobox, DateTimeRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EVENT_FACET_FIELDS, EVENT_FILTER_FIELDS, EVENT_TEMPLATES, EventExpandedDetails, EventFeedItem, EventTypeToggle, EventsFeed, FILTER_FIELDS, FilterBuilder, FilterBuilderWithAutocomplete, MultiCombobox, NetworkError, POLICY_FILTER_FIELDS, PolicyActivityView, PolicyActivityViewSkeleton, PolicyDetailView, PolicyEditView, PolicyEditor, PolicyList, PolicyPreviewPanel, PolicyPreviewResult, PolicyResourceForm, PolicyRuleEditor, PolicyRuleEditorDialog, PolicyRuleList, PolicyRuleListItem, ReindexJobCreate, ReindexJobDetailView, ReindexJobDialog, ReindexJobList, ResourceHistoryView, RulePreviewPanel, SampleInputTemplates, SimpleQueryBuilder, TenantBadge, TimeRangeDropdown, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserSelect, badgeVariants, buildAuditLogCEL, cn, copyToClipboard, defaultErrorFormatter, defaultResourceLinkResolver, extractEvent, extractFieldPaths, extractFieldPathsFromMany, generateShareableUrl, getReindexJobDuration, getReindexJobStatusMessage, groupActivities, groupActivitiesByDay, isEventRecord, isReindexJobRunning, isReindexJobTerminal, parseActivityFilters, parseApiError, parseEventFilters, parseTimeRange, serializeActivityFilters, serializeEventFilters, useActivityFeed, useAuditLogFacets, useAuditLogQuery, useEventFacets, useEventsFeed, useFacets, usePolicyEditor, usePolicyList, usePolicyPreview, useReindexJobs };
16459
+ export { ACTIVITY_FILTER_FIELDS, AUDIT_TEMPLATES, ActionMultiSelect, ActionToggle, ActivityApiClient, ActivityExpandedDetails, ActivityFeed, ActivityFeedFilters, ActivityFeedItem, ActivityFeedItemSkeleton, ActivityFeedSummary, ActivityLayout, ApiError, ApiErrorAlert, AuditEventViewer, AuditLogExpandedDetails, AuditLogFeedItem, AuditLogFilters, AuditLogQueryComponent, Badge, CelEditor, ChangeSourceToggle, Combobox, DateTimeRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EVENT_FACET_FIELDS, EVENT_FILTER_FIELDS, EVENT_TEMPLATES, EventExpandedDetails, EventFeedItem, EventTypeToggle, EventsFeed, FILTER_FIELDS, FilterBuilder, FilterBuilderWithAutocomplete, MultiCombobox, NetworkError, POLICY_FILTER_FIELDS, PolicyActivityView, PolicyActivityViewSkeleton, PolicyDetailView, PolicyEditView, PolicyEditor, PolicyList, PolicyPreviewPanel, PolicyPreviewResult, PolicyResourceForm, PolicyRuleEditor, PolicyRuleEditorDialog, PolicyRuleList, PolicyRuleListItem, ReindexJobCreate, ReindexJobDetailView, ReindexJobDialog, ReindexJobList, ResourceHistoryView, RulePreviewPanel, SampleInputTemplates, SimpleQueryBuilder, TenantBadge, TimeRangeDropdown, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserSelect, badgeVariants, buildAuditLogCEL, cn, copyToClipboard, defaultErrorFormatter, defaultResourceLinkResolver, extractEvent, extractFieldPaths, extractFieldPathsFromMany, generateShareableUrl, getReindexJobDuration, getReindexJobStatusMessage, isEventRecord, isReindexJobRunning, isReindexJobTerminal, parseActivityFilters, parseApiError, parseEventFilters, parseTimeRange, serializeActivityFilters, serializeEventFilters, useActivityFeed, useAuditLogFacets, useAuditLogQuery, useEventFacets, useEventsFeed, useFacets, usePolicyEditor, usePolicyList, usePolicyPreview, useReindexJobs };
16817
16460
  //# sourceMappingURL=index.esm.js.map