@upstash/react-redis-browser 0.1.6 → 0.1.8

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.mjs CHANGED
@@ -2808,7 +2808,7 @@ function formatTime(seconds) {
2808
2808
  if (parts.length === 0) {
2809
2809
  parts.push("0s");
2810
2810
  }
2811
- return parts.slice(0, 2).join(" ");
2811
+ return parts.slice(0, 1).join(" ");
2812
2812
  }
2813
2813
 
2814
2814
  // src/components/ui/toast.tsx
@@ -2923,6 +2923,20 @@ import {
2923
2923
  } from "react";
2924
2924
  import { useInfiniteQuery } from "@tanstack/react-query";
2925
2925
 
2926
+ // src/components/databrowser/hooks/use-fetch-key-type.tsx
2927
+ import { useQuery } from "@tanstack/react-query";
2928
+ var FETCH_KEY_TYPE_QUERY_KEY = "fetch-key-type";
2929
+ var useFetchKeyType = (key) => {
2930
+ const { redisNoPipeline: redis } = useDatabrowser();
2931
+ return useQuery({
2932
+ queryKey: [FETCH_KEY_TYPE_QUERY_KEY, key],
2933
+ queryFn: async () => {
2934
+ if (!key) return "none";
2935
+ return await redis.type(key);
2936
+ }
2937
+ });
2938
+ };
2939
+
2926
2940
  // src/components/databrowser/hooks/use-fetch-keys.ts
2927
2941
  import { useCallback, useRef } from "react";
2928
2942
 
@@ -3050,6 +3064,8 @@ var KeysContext = createContext2(void 0);
3050
3064
  var FETCH_KEYS_QUERY_KEY = "use-fetch-keys";
3051
3065
  var KeysProvider = ({ children }) => {
3052
3066
  const { search } = useDatabrowserStore();
3067
+ const cleanSearchKey = search.key.replace("*", "");
3068
+ const { data: exactMatchType, isFetching, isLoading } = useFetchKeyType(cleanSearchKey);
3053
3069
  const { fetchKeys, resetCache } = useFetchKeys(search);
3054
3070
  const pageRef = useRef2(0);
3055
3071
  const query = useInfiniteQuery({
@@ -3064,6 +3080,7 @@ var KeysProvider = ({ children }) => {
3064
3080
  getNextPageParam: (lastPage, __, lastPageIndex) => {
3065
3081
  return lastPage.hasNextPage ? lastPageIndex + 1 : void 0;
3066
3082
  },
3083
+ enabled: !isFetching,
3067
3084
  refetchOnMount: false
3068
3085
  });
3069
3086
  const refetch = useCallback2(() => {
@@ -3072,6 +3089,9 @@ var KeysProvider = ({ children }) => {
3072
3089
  }, [query, resetCache]);
3073
3090
  const keys = useMemo2(() => {
3074
3091
  const keys2 = query.data?.pages.flatMap((page) => page.keys) ?? [];
3092
+ if (exactMatchType && exactMatchType !== "none" && (search.type === void 0 || search.type === exactMatchType)) {
3093
+ keys2.push([cleanSearchKey, exactMatchType]);
3094
+ }
3075
3095
  const keysSet = /* @__PURE__ */ new Set();
3076
3096
  const dedupedKeys = [];
3077
3097
  for (const key of keys2) {
@@ -3080,13 +3100,18 @@ var KeysProvider = ({ children }) => {
3080
3100
  dedupedKeys.push(key);
3081
3101
  }
3082
3102
  return dedupedKeys;
3083
- }, [query.data]);
3103
+ }, [query.data, cleanSearchKey, exactMatchType]);
3084
3104
  return /* @__PURE__ */ jsx4(
3085
3105
  KeysContext.Provider,
3086
3106
  {
3087
3107
  value: {
3088
3108
  keys,
3089
- query,
3109
+ // @ts-expect-error Ignore the error with spread syntax
3110
+ query: {
3111
+ ...query,
3112
+ isLoading: isLoading || query.isLoading,
3113
+ isFetching: isFetching || query.isFetching
3114
+ },
3090
3115
  refetch
3091
3116
  },
3092
3117
  children
@@ -3154,7 +3179,7 @@ Button.displayName = "Button";
3154
3179
  import { useMutation } from "@tanstack/react-query";
3155
3180
 
3156
3181
  // src/components/databrowser/components/sidebar/db-size.tsx
3157
- import { useQuery } from "@tanstack/react-query";
3182
+ import { useQuery as useQuery2 } from "@tanstack/react-query";
3158
3183
 
3159
3184
  // src/components/ui/skeleton.tsx
3160
3185
  import { jsx as jsx6 } from "react/jsx-runtime";
@@ -3173,7 +3198,7 @@ import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
3173
3198
  var FETCH_DB_SIZE_QUERY_KEY = "fetch-db-size";
3174
3199
  var DisplayDbSize = () => {
3175
3200
  const { redis } = useDatabrowser();
3176
- const { data: keyCount } = useQuery({
3201
+ const { data: keyCount } = useQuery2({
3177
3202
  queryKey: [FETCH_DB_SIZE_QUERY_KEY],
3178
3203
  queryFn: async () => {
3179
3204
  return await redis.dbsize();
@@ -3270,76 +3295,6 @@ import { useMutation as useMutation2 } from "@tanstack/react-query";
3270
3295
  // src/components/databrowser/hooks/use-delete-key-cache.ts
3271
3296
  import { useCallback as useCallback3 } from "react";
3272
3297
 
3273
- // src/components/databrowser/hooks/use-fetch-simple-key.tsx
3274
- import { useQuery as useQuery2 } from "@tanstack/react-query";
3275
- var FETCH_SIMPLE_KEY_QUERY_KEY = "fetch-simple-key";
3276
- var useFetchSimpleKey = (dataKey, type) => {
3277
- const { redisNoPipeline: redis } = useDatabrowser();
3278
- const { deleteKeyCache } = useDeleteKeyCache();
3279
- return useQuery2({
3280
- queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey],
3281
- queryFn: async () => {
3282
- let result;
3283
- if (type === "string") result = await redis.get(dataKey);
3284
- else if (type === "json") result = await redis.json.get(dataKey);
3285
- else throw new Error(`Invalid type when fetching simple key: ${type}`);
3286
- if (type === "json" && result !== null)
3287
- result = JSON.stringify(sortObject(JSON.parse(result)));
3288
- if (result === null) deleteKeyCache(dataKey);
3289
- return result;
3290
- }
3291
- });
3292
- };
3293
- var sortObject = (obj) => {
3294
- if (typeof obj !== "object" || obj === null) return obj;
3295
- return Object.fromEntries(
3296
- Object.entries(obj).sort((a, b) => a[0].localeCompare(b[0])).map(
3297
- ([key, value]) => typeof value === "object" && !Array.isArray(value) && value !== null ? [key, sortObject(value)] : [key, value]
3298
- )
3299
- );
3300
- };
3301
-
3302
- // src/components/databrowser/hooks/use-delete-key-cache.ts
3303
- var useDeleteKeyCache = () => {
3304
- const { setSelectedKey } = useDatabrowserStore();
3305
- const { refetch } = useKeys();
3306
- const deleteKeyCache = useCallback3(
3307
- (key) => {
3308
- setSelectedKey(void 0);
3309
- queryClient.invalidateQueries({
3310
- queryKey: [FETCH_KEYS_QUERY_KEY]
3311
- });
3312
- queryClient.invalidateQueries({
3313
- queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, key]
3314
- });
3315
- refetch();
3316
- },
3317
- [setSelectedKey, refetch]
3318
- );
3319
- return { deleteKeyCache };
3320
- };
3321
-
3322
- // src/components/databrowser/hooks/use-delete-key.ts
3323
- var useDeleteKey = () => {
3324
- const { redis } = useDatabrowser();
3325
- const { deleteKeyCache } = useDeleteKeyCache();
3326
- const deleteKey = useMutation2({
3327
- mutationFn: async (key) => {
3328
- return Boolean(await redis.del(key));
3329
- },
3330
- onSuccess: (_, key) => {
3331
- deleteKeyCache(key);
3332
- queryClient.invalidateQueries({
3333
- queryKey: [FETCH_DB_SIZE_QUERY_KEY]
3334
- });
3335
- }
3336
- });
3337
- return deleteKey;
3338
- };
3339
-
3340
- // src/components/databrowser/hooks/use-edit-list-item.tsx
3341
- import { useMutation as useMutation3 } from "@tanstack/react-query";
3342
-
3343
3298
  // src/components/databrowser/hooks/use-fetch-list-items.tsx
3344
3299
  import { useInfiniteQuery as useInfiniteQuery2 } from "@tanstack/react-query";
3345
3300
  var LIST_DISPLAY_PAGE_SIZE = 50;
@@ -3365,13 +3320,15 @@ var useFetchListItems = ({ dataKey, type }) => {
3365
3320
  enabled: type === "zset",
3366
3321
  queryKey: [FETCH_LIST_ITEMS_QUERY_KEY, dataKey, "zset"],
3367
3322
  initialPageParam: 0,
3368
- queryFn: async ({ pageParam: lastIndex }) => {
3369
- const res = await redis.zrange(dataKey, lastIndex, lastIndex + LIST_DISPLAY_PAGE_SIZE - 1, {
3323
+ queryFn: async ({ pageParam: page }) => {
3324
+ const start = page * LIST_DISPLAY_PAGE_SIZE;
3325
+ const end = start + LIST_DISPLAY_PAGE_SIZE - 1;
3326
+ const res = await redis.zrange(dataKey, start, end, {
3370
3327
  withScores: true,
3371
3328
  rev: true
3372
3329
  });
3373
3330
  return {
3374
- cursor: res.length < LIST_DISPLAY_PAGE_SIZE ? void 0 : lastIndex + LIST_DISPLAY_PAGE_SIZE,
3331
+ cursor: res.length < LIST_DISPLAY_PAGE_SIZE ? void 0 : page + 1,
3375
3332
  keys: transformArray(res)
3376
3333
  };
3377
3334
  },
@@ -3396,13 +3353,14 @@ var useFetchListItems = ({ dataKey, type }) => {
3396
3353
  enabled: type === "list",
3397
3354
  queryKey: [FETCH_LIST_ITEMS_QUERY_KEY, dataKey, "list"],
3398
3355
  initialPageParam: 0,
3399
- queryFn: async ({ pageParam }) => {
3400
- const lastIndex = Number(pageParam);
3401
- const values = await redis.lrange(dataKey, lastIndex, lastIndex + LIST_DISPLAY_PAGE_SIZE);
3356
+ queryFn: async ({ pageParam: page }) => {
3357
+ const start = page * LIST_DISPLAY_PAGE_SIZE;
3358
+ const end = start + LIST_DISPLAY_PAGE_SIZE - 1;
3359
+ const values = await redis.lrange(dataKey, start, end);
3402
3360
  return {
3403
- cursor: values.length < LIST_DISPLAY_PAGE_SIZE ? void 0 : lastIndex + LIST_DISPLAY_PAGE_SIZE,
3361
+ cursor: values.length < LIST_DISPLAY_PAGE_SIZE ? void 0 : page + 1,
3404
3362
  keys: values.map((value, i) => ({
3405
- key: (lastIndex + i).toString(),
3363
+ key: (start + i).toString(),
3406
3364
  value
3407
3365
  }))
3408
3366
  };
@@ -3453,7 +3411,81 @@ function transformArray(inputArray) {
3453
3411
  }, []);
3454
3412
  }
3455
3413
 
3414
+ // src/components/databrowser/hooks/use-fetch-simple-key.tsx
3415
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
3416
+ var FETCH_SIMPLE_KEY_QUERY_KEY = "fetch-simple-key";
3417
+ var useFetchSimpleKey = (dataKey, type) => {
3418
+ const { redisNoPipeline: redis } = useDatabrowser();
3419
+ const { deleteKeyCache } = useDeleteKeyCache();
3420
+ return useQuery3({
3421
+ queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey],
3422
+ queryFn: async () => {
3423
+ let result;
3424
+ if (type === "string") result = await redis.get(dataKey);
3425
+ else if (type === "json") result = await redis.json.get(dataKey);
3426
+ else throw new Error(`Invalid type when fetching simple key: ${type}`);
3427
+ if (type === "json" && result !== null)
3428
+ result = JSON.stringify(sortObject(JSON.parse(result)));
3429
+ if (result === null) deleteKeyCache(dataKey);
3430
+ return result;
3431
+ }
3432
+ });
3433
+ };
3434
+ var sortObject = (obj) => {
3435
+ if (typeof obj !== "object" || obj === null) return obj;
3436
+ return Object.fromEntries(
3437
+ Object.entries(obj).sort((a, b) => a[0].localeCompare(b[0])).map(
3438
+ ([key, value]) => typeof value === "object" && !Array.isArray(value) && value !== null ? [key, sortObject(value)] : [key, value]
3439
+ )
3440
+ );
3441
+ };
3442
+
3443
+ // src/components/databrowser/hooks/use-delete-key-cache.ts
3444
+ var useDeleteKeyCache = () => {
3445
+ const { setSelectedKey } = useDatabrowserStore();
3446
+ const { refetch } = useKeys();
3447
+ const deleteKeyCache = useCallback3(
3448
+ (key) => {
3449
+ setSelectedKey(void 0);
3450
+ queryClient.invalidateQueries({
3451
+ queryKey: [FETCH_KEYS_QUERY_KEY]
3452
+ });
3453
+ queryClient.invalidateQueries({
3454
+ queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, key]
3455
+ });
3456
+ queryClient.invalidateQueries({
3457
+ queryKey: [FETCH_LIST_ITEMS_QUERY_KEY, key]
3458
+ });
3459
+ queryClient.invalidateQueries({
3460
+ queryKey: [FETCH_KEY_TYPE_QUERY_KEY, key]
3461
+ });
3462
+ refetch();
3463
+ },
3464
+ [setSelectedKey, refetch]
3465
+ );
3466
+ return { deleteKeyCache };
3467
+ };
3468
+
3469
+ // src/components/databrowser/hooks/use-delete-key.ts
3470
+ var useDeleteKey = () => {
3471
+ const { redis } = useDatabrowser();
3472
+ const { deleteKeyCache } = useDeleteKeyCache();
3473
+ const deleteKey = useMutation2({
3474
+ mutationFn: async (key) => {
3475
+ return Boolean(await redis.del(key));
3476
+ },
3477
+ onSuccess: (_, key) => {
3478
+ deleteKeyCache(key);
3479
+ queryClient.invalidateQueries({
3480
+ queryKey: [FETCH_DB_SIZE_QUERY_KEY]
3481
+ });
3482
+ }
3483
+ });
3484
+ return deleteKey;
3485
+ };
3486
+
3456
3487
  // src/components/databrowser/hooks/use-edit-list-item.tsx
3488
+ import { useMutation as useMutation3 } from "@tanstack/react-query";
3457
3489
  var useEditListItem = () => {
3458
3490
  const { redis } = useDatabrowser();
3459
3491
  return useMutation3({
@@ -3534,914 +3566,940 @@ var useEditListItem = () => {
3534
3566
  };
3535
3567
 
3536
3568
  // src/components/databrowser/hooks/use-fetch-ttl.ts
3537
- import { useEffect as useEffect3 } from "react";
3538
- import { useQuery as useQuery3 } from "@tanstack/react-query";
3539
- var FETCH_TTL_QUERY_KEY = "fetch-ttl";
3540
- var useFetchTTL = (dataKey) => {
3569
+ import { useEffect as useEffect5 } from "react";
3570
+ import { useQuery as useQuery6 } from "@tanstack/react-query";
3571
+
3572
+ // src/components/databrowser/components/display/ttl-badge.tsx
3573
+ import { useEffect as useEffect4, useState as useState5 } from "react";
3574
+ import { IconChevronDown } from "@tabler/icons-react";
3575
+
3576
+ // src/components/databrowser/components/display/header-badges.tsx
3577
+ import bytes from "bytes";
3578
+
3579
+ // src/components/databrowser/hooks/use-fetch-key-length.ts
3580
+ import { useQuery as useQuery4 } from "@tanstack/react-query";
3581
+ var FETCH_KEY_LENGTH_QUERY_KEY = "fetch-key-length";
3582
+ var useFetchKeyLength = ({ dataKey, type }) => {
3541
3583
  const { redis } = useDatabrowser();
3542
- const { isLoading, error, data } = useQuery3({
3543
- queryKey: [FETCH_TTL_QUERY_KEY, dataKey],
3584
+ return useQuery4({
3585
+ queryKey: [FETCH_KEY_LENGTH_QUERY_KEY, dataKey],
3544
3586
  queryFn: async () => {
3545
- return await redis.ttl(dataKey);
3587
+ switch (type) {
3588
+ case "set": {
3589
+ return await redis.scard(dataKey);
3590
+ }
3591
+ case "zset": {
3592
+ return await redis.zcard(dataKey);
3593
+ }
3594
+ case "list": {
3595
+ return await redis.llen(dataKey);
3596
+ }
3597
+ case "hash": {
3598
+ return await redis.hlen(dataKey);
3599
+ }
3600
+ case "stream": {
3601
+ return await redis.xlen(dataKey);
3602
+ }
3603
+ }
3604
+ return null;
3546
3605
  }
3547
3606
  });
3548
- useEffect3(() => {
3549
- if (data === -2) {
3550
- queryClient.invalidateQueries({
3551
- queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey]
3552
- });
3553
- }
3554
- }, [data === -2]);
3555
- return { isLoading, error, data };
3556
3607
  };
3557
3608
 
3558
- // src/components/databrowser/hooks/use-set-simple-key.tsx
3559
- import { useMutation as useMutation4 } from "@tanstack/react-query";
3560
- var useSetSimpleKey = (dataKey, type) => {
3609
+ // src/components/databrowser/hooks/use-fetch-key-size.ts
3610
+ import { useQuery as useQuery5 } from "@tanstack/react-query";
3611
+ var FETCH_KEY_SIZE_QUERY_KEY = "fetch-key-size";
3612
+ var useFetchKeySize = (dataKey) => {
3561
3613
  const { redis } = useDatabrowser();
3562
- return useMutation4({
3563
- mutationFn: async (value) => {
3564
- if (type === "string") {
3565
- await redis.set(dataKey, value);
3566
- } else if (type === "json") {
3567
- await redis.json.set(dataKey, "$", JSON.parse(value));
3568
- } else {
3569
- throw new Error(`Invalid type when setting simple key: ${type}`);
3570
- }
3571
- },
3572
- onSuccess: (_, value) => {
3573
- queryClient.setQueryData([FETCH_SIMPLE_KEY_QUERY_KEY, dataKey], value);
3574
- queryClient.invalidateQueries({
3575
- queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey]
3576
- });
3614
+ return useQuery5({
3615
+ queryKey: [FETCH_KEY_SIZE_QUERY_KEY, dataKey],
3616
+ queryFn: async () => {
3617
+ return await redis.eval(`return redis.call("MEMORY", "USAGE", KEYS[1])`, [dataKey], []);
3577
3618
  }
3578
3619
  });
3579
3620
  };
3580
3621
 
3581
- // src/components/databrowser/hooks/use-set-ttl.ts
3582
- import { useMutation as useMutation5 } from "@tanstack/react-query";
3583
- var useSetTTL = () => {
3584
- const { redis } = useDatabrowser();
3585
- const updateTTL = useMutation5({
3586
- mutationFn: async ({ dataKey, ttl }) => {
3587
- await (ttl === void 0 ? redis.persist(dataKey) : redis.expire(dataKey, ttl));
3588
- },
3589
- onSuccess: (_, { dataKey }) => {
3590
- queryClient.invalidateQueries({
3591
- queryKey: [FETCH_TTL_QUERY_KEY, dataKey]
3592
- });
3593
- queryClient.invalidateQueries({
3594
- queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey]
3595
- });
3622
+ // src/components/databrowser/components/display/header-badges.tsx
3623
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
3624
+ var LengthBadge = ({
3625
+ dataKey,
3626
+ type,
3627
+ content
3628
+ }) => {
3629
+ const { data, isLoading } = useFetchKeyLength({ dataKey, type });
3630
+ const length = content?.length ?? data;
3631
+ return /* @__PURE__ */ jsx8(Badge, { label: "Length:", children: isLoading ? /* @__PURE__ */ jsx8(Skeleton, { className: "ml-1 h-3 w-10 rounded-md opacity-50" }) : length });
3632
+ };
3633
+ var SizeBadge = ({ dataKey }) => {
3634
+ const { data: size } = useFetchKeySize(dataKey);
3635
+ return /* @__PURE__ */ jsx8(Badge, { label: "Size:", children: size === void 0 || size === null ? /* @__PURE__ */ jsx8(Skeleton, { className: "ml-1 h-3 w-10 rounded-md opacity-50" }) : bytes(size, {
3636
+ unitSeparator: " "
3637
+ }) });
3638
+ };
3639
+ var HeaderTTLBadge = ({ dataKey }) => {
3640
+ const { data: expireAt } = useFetchKeyExpire(dataKey);
3641
+ const { mutate: setTTL, isPending } = useSetTTL();
3642
+ return /* @__PURE__ */ jsx8(
3643
+ TTLBadge,
3644
+ {
3645
+ expireAt,
3646
+ setTTL: (ttl) => setTTL({ dataKey, ttl }),
3647
+ isPending
3596
3648
  }
3597
- });
3598
- return updateTTL;
3649
+ );
3599
3650
  };
3651
+ var Badge = ({ children, label }) => /* @__PURE__ */ jsxs3("div", { className: "flex h-6 items-center gap-0.5 rounded-md bg-white px-2 text-xs text-zinc-700", children: [
3652
+ /* @__PURE__ */ jsx8("span", { className: "text-zinc-500", children: label }),
3653
+ /* @__PURE__ */ jsx8("span", { className: "font-medium", children })
3654
+ ] });
3600
3655
 
3601
- // src/components/databrowser/components/item-context-menu.tsx
3602
- import { useState as useState4 } from "react";
3603
- import { ContextMenuSeparator as ContextMenuSeparator2 } from "@radix-ui/react-context-menu";
3656
+ // src/components/databrowser/components/display/ttl-popover.tsx
3657
+ import { useEffect as useEffect3, useMemo as useMemo3, useState as useState4 } from "react";
3658
+ import { Controller, useForm } from "react-hook-form";
3604
3659
 
3605
- // src/components/ui/context-menu.tsx
3660
+ // src/components/ui/input.tsx
3606
3661
  import * as React4 from "react";
3607
- import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
3608
- import { CheckIcon, ChevronRightIcon, DotFilledIcon } from "@radix-ui/react-icons";
3609
- import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
3610
- var ContextMenu = ContextMenuPrimitive.Root;
3611
- var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
3612
- var ContextMenuSubTrigger = React4.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs3(
3613
- ContextMenuPrimitive.SubTrigger,
3614
- {
3615
- ref,
3616
- className: cn(
3617
- "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-900 dark:focus:bg-neutral-800 dark:focus:text-neutral-50 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-50",
3618
- inset && "pl-8",
3619
- className
3620
- ),
3621
- ...props,
3622
- children: [
3623
- children,
3624
- /* @__PURE__ */ jsx8(ChevronRightIcon, { className: "ml-auto h-4 w-4" })
3625
- ]
3626
- }
3627
- ));
3628
- ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
3629
- var ContextMenuSubContent = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
3630
- ContextMenuPrimitive.SubContent,
3631
- {
3632
- ref,
3633
- className: cn(
3634
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
3635
- className
3636
- ),
3637
- ...props
3662
+ import { jsx as jsx9 } from "react/jsx-runtime";
3663
+ var Input = React4.forwardRef(
3664
+ ({ className, type, ...props }, ref) => {
3665
+ return /* @__PURE__ */ jsx9(
3666
+ "input",
3667
+ {
3668
+ type,
3669
+ className: cn(
3670
+ "flex h-8 w-full rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-zinc-950 disabled:cursor-not-allowed disabled:opacity-50",
3671
+ className
3672
+ ),
3673
+ ref,
3674
+ ...props
3675
+ }
3676
+ );
3638
3677
  }
3639
- ));
3640
- ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
3641
- var ContextMenuContent = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(ContextMenuPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx8(
3642
- ContextMenuPrimitive.Content,
3678
+ );
3679
+ Input.displayName = "Input";
3680
+
3681
+ // src/components/ui/popover.tsx
3682
+ import * as React5 from "react";
3683
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
3684
+ import { jsx as jsx10 } from "react/jsx-runtime";
3685
+ var Popover = PopoverPrimitive.Root;
3686
+ var PopoverTrigger = PopoverPrimitive.Trigger;
3687
+ var PopoverContent = React5.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx10(PopoverPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx10(
3688
+ PopoverPrimitive.Content,
3643
3689
  {
3644
3690
  ref,
3691
+ align,
3692
+ sideOffset,
3645
3693
  className: cn(
3646
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
3694
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border border-zinc-200 bg-white p-4 text-zinc-950 shadow-md outline-none dark:border-zinc-800 mt-0.5 dark:bg-zinc-950 dark:text-zinc-50",
3647
3695
  className
3648
3696
  ),
3649
3697
  ...props
3650
3698
  }
3651
3699
  ) }));
3652
- ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
3653
- var ContextMenuItem = React4.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx8(
3654
- ContextMenuPrimitive.Item,
3655
- {
3656
- ref,
3657
- className: cn(
3658
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
3659
- inset && "pl-8",
3660
- className
3661
- ),
3662
- ...props
3663
- }
3664
- ));
3665
- ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
3666
- var ContextMenuCheckboxItem = React4.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs3(
3667
- ContextMenuPrimitive.CheckboxItem,
3700
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
3701
+
3702
+ // src/components/ui/select.tsx
3703
+ import * as React6 from "react";
3704
+ import * as SelectPrimitive from "@radix-ui/react-select";
3705
+ import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
3706
+ var Select = SelectPrimitive.Root;
3707
+ var SelectGroup = SelectPrimitive.Group;
3708
+ var SelectValue = SelectPrimitive.Value;
3709
+ var SelectTrigger = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
3710
+ SelectPrimitive.Trigger,
3668
3711
  {
3669
3712
  ref,
3670
3713
  className: cn(
3671
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
3714
+ "relative flex h-8 w-full items-center justify-between rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm ring-offset-white placeholder:text-zinc-500 focus:ring-zinc-950 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-800 dark:bg-zinc-950 dark:ring-offset-zinc-950 dark:placeholder:text-zinc-400 dark:focus:ring-zinc-300",
3672
3715
  className
3673
3716
  ),
3674
- checked,
3675
3717
  ...props,
3676
3718
  children: [
3677
- /* @__PURE__ */ jsx8("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx8(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx8(CheckIcon, { className: "h-4 w-4" }) }) }),
3678
- children
3719
+ children,
3720
+ /* @__PURE__ */ jsx11(SelectPrimitive.Icon, { asChild: true, className: "absolute right-2", children: /* @__PURE__ */ jsx11(
3721
+ "svg",
3722
+ {
3723
+ width: "16",
3724
+ height: "16",
3725
+ viewBox: "0 0 16 16",
3726
+ fill: "none",
3727
+ xmlns: "http://www.w3.org/2000/svg",
3728
+ children: /* @__PURE__ */ jsx11(
3729
+ "path",
3730
+ {
3731
+ d: "M4 6L8 10L12 6",
3732
+ stroke: "black",
3733
+ strokeOpacity: "0.4",
3734
+ strokeWidth: "1.4",
3735
+ strokeLinecap: "round",
3736
+ strokeLinejoin: "round"
3737
+ }
3738
+ )
3739
+ }
3740
+ ) })
3679
3741
  ]
3680
3742
  }
3681
3743
  ));
3682
- ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
3683
- var ContextMenuRadioItem = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs3(
3684
- ContextMenuPrimitive.RadioItem,
3744
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
3745
+ var SelectContent = React6.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx11(SelectPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx11(
3746
+ SelectPrimitive.Content,
3685
3747
  {
3686
3748
  ref,
3687
3749
  className: cn(
3688
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
3750
+ "relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white text-zinc-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-neutral-50",
3751
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
3689
3752
  className
3690
3753
  ),
3754
+ position,
3691
3755
  ...props,
3692
- children: [
3693
- /* @__PURE__ */ jsx8("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx8(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx8(DotFilledIcon, { className: "h-4 w-4 fill-current" }) }) }),
3694
- children
3695
- ]
3756
+ children: /* @__PURE__ */ jsx11(
3757
+ SelectPrimitive.Viewport,
3758
+ {
3759
+ className: cn(
3760
+ "p-1",
3761
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
3762
+ ),
3763
+ children
3764
+ }
3765
+ )
3696
3766
  }
3697
- ));
3698
- ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
3699
- var ContextMenuLabel = React4.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx8(
3700
- ContextMenuPrimitive.Label,
3767
+ ) }));
3768
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
3769
+ var SelectLabel = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
3770
+ SelectPrimitive.Label,
3701
3771
  {
3702
3772
  ref,
3703
- className: cn(
3704
- "px-2 py-1.5 text-sm font-semibold text-neutral-950 dark:text-neutral-50",
3705
- inset && "pl-8",
3706
- className
3707
- ),
3773
+ className: cn("px-2 py-1.5 text-sm font-semibold", className),
3708
3774
  ...props
3709
3775
  }
3710
3776
  ));
3711
- ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
3712
- var ContextMenuSeparator = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
3713
- ContextMenuPrimitive.Separator,
3777
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
3778
+ var SelectItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
3779
+ SelectPrimitive.Item,
3714
3780
  {
3715
3781
  ref,
3716
- className: cn("-mx-1 my-1 h-px bg-neutral-200 dark:bg-neutral-800", className),
3717
- ...props
3718
- }
3719
- ));
3720
- ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
3721
- var ContextMenuShortcut = ({ className, ...props }) => {
3722
- return /* @__PURE__ */ jsx8(
3723
- "span",
3724
- {
3725
- className: cn(
3726
- "ml-auto text-xs tracking-widest text-neutral-500 dark:text-neutral-400",
3727
- className
3728
- ),
3729
- ...props
3730
- }
3731
- );
3732
- };
3733
- ContextMenuShortcut.displayName = "ContextMenuShortcut";
3734
-
3735
- // src/components/ui/alert-dialog.tsx
3736
- import * as React5 from "react";
3737
- import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
3738
- import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
3739
- var AlertDialog = AlertDialogPrimitive.Root;
3740
- var AlertDialogTrigger = AlertDialogPrimitive.Trigger;
3741
- var AlertDialogPortal = ({ ...props }) => /* @__PURE__ */ jsx9(AlertDialogPrimitive.Portal, { container: portalRoot, ...props });
3742
- AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName;
3743
- var AlertDialogOverlay = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
3744
- AlertDialogPrimitive.Overlay,
3745
- {
3746
3782
  className: cn(
3747
- "fixed inset-0 z-50 bg-white/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 dark:bg-zinc-950/80",
3783
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
3748
3784
  className
3749
3785
  ),
3750
3786
  ...props,
3751
- ref
3752
- }
3753
- ));
3754
- AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
3755
- var AlertDialogContent = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs4(AlertDialogPortal, { children: [
3756
- /* @__PURE__ */ jsx9(AlertDialogOverlay, {}),
3757
- /* @__PURE__ */ jsx9(
3758
- AlertDialogPrimitive.Content,
3759
- {
3760
- ref,
3761
- className: cn(
3762
- "antialiased data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-zinc-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] dark:border-zinc-800 dark:bg-zinc-950 sm:rounded-lg md:w-full",
3763
- className
3764
- ),
3765
- ...props
3766
- }
3767
- )
3768
- ] }));
3769
- AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
3770
- var AlertDialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx9("div", { className: cn("flex flex-col space-y-2 text-center sm:text-left", className), ...props });
3771
- AlertDialogHeader.displayName = "AlertDialogHeader";
3772
- var AlertDialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx9(
3773
- "div",
3774
- {
3775
- className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
3776
- ...props
3777
- }
3778
- );
3779
- AlertDialogFooter.displayName = "AlertDialogFooter";
3780
- var AlertDialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
3781
- AlertDialogPrimitive.Title,
3782
- {
3783
- ref,
3784
- className: cn("text-lg font-semibold", className),
3785
- ...props
3786
- }
3787
- ));
3788
- AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
3789
- var AlertDialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
3790
- AlertDialogPrimitive.Description,
3791
- {
3792
- ref,
3793
- className: cn("text-sm text-zinc-500 dark:text-zinc-400", className),
3794
- ...props
3787
+ children: [
3788
+ /* @__PURE__ */ jsx11("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx11(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11(
3789
+ "svg",
3790
+ {
3791
+ width: "15",
3792
+ height: "15",
3793
+ viewBox: "0 0 15 15",
3794
+ fill: "none",
3795
+ xmlns: "http://www.w3.org/2000/svg",
3796
+ className: "h-4 w-4",
3797
+ children: /* @__PURE__ */ jsx11(
3798
+ "path",
3799
+ {
3800
+ d: "M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",
3801
+ fill: "currentColor",
3802
+ fillRule: "evenodd",
3803
+ clipRule: "evenodd"
3804
+ }
3805
+ )
3806
+ }
3807
+ ) }) }) }),
3808
+ /* @__PURE__ */ jsx11(SelectPrimitive.ItemText, { children })
3809
+ ]
3795
3810
  }
3796
3811
  ));
3797
- AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
3798
- var AlertDialogAction = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(AlertDialogPrimitive.Action, { ref, className: cn(buttonVariants(), className), ...props }));
3799
- AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
3800
- var AlertDialogCancel = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
3801
- AlertDialogPrimitive.Cancel,
3812
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
3813
+ var SelectSeparator = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
3814
+ SelectPrimitive.Separator,
3802
3815
  {
3803
3816
  ref,
3804
- className: cn(buttonVariants({ variant: "outline" }), "mt-2 sm:!mt-0", className),
3817
+ className: cn("-mx-1 my-1 h-px bg-neutral-100 dark:bg-neutral-800", className),
3805
3818
  ...props
3806
3819
  }
3807
3820
  ));
3808
- AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
3821
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
3809
3822
 
3810
- // src/components/databrowser/components/display/delete-alert-dialog.tsx
3811
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
3812
- function DeleteAlertDialog({
3813
- children,
3814
- onDeleteConfirm,
3815
- open,
3816
- onOpenChange,
3817
- deletionType
3818
- }) {
3819
- return /* @__PURE__ */ jsxs5(AlertDialog, { open, onOpenChange, children: [
3820
- children && /* @__PURE__ */ jsx10(AlertDialogTrigger, { asChild: true, children }),
3821
- /* @__PURE__ */ jsxs5(AlertDialogContent, { children: [
3822
- /* @__PURE__ */ jsxs5(AlertDialogHeader, { children: [
3823
- /* @__PURE__ */ jsx10(AlertDialogTitle, { children: deletionType === "item" ? "Delete Item" : "Delete Key" }),
3824
- /* @__PURE__ */ jsxs5(AlertDialogDescription, { className: "mt-5", children: [
3825
- "Are you sure you want to delete this ",
3826
- deletionType,
3827
- "?",
3828
- /* @__PURE__ */ jsx10("br", {}),
3829
- "This action cannot be undone."
3830
- ] })
3831
- ] }),
3832
- /* @__PURE__ */ jsxs5(AlertDialogFooter, { children: [
3833
- /* @__PURE__ */ jsx10(AlertDialogCancel, { type: "button", children: "Cancel" }),
3834
- /* @__PURE__ */ jsx10(
3835
- AlertDialogAction,
3836
- {
3837
- className: "bg-red-500 text-gray-50 hover:bg-red-600",
3838
- onClick: onDeleteConfirm,
3839
- children: "Yes, Delete"
3840
- }
3841
- )
3842
- ] })
3843
- ] })
3844
- ] });
3845
- }
3846
-
3847
- // src/components/databrowser/components/item-context-menu.tsx
3848
- import { Fragment, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
3849
- var ItemContextMenu = ({
3850
- children,
3851
- dataKey,
3852
- type
3823
+ // src/components/ui/spinner.tsx
3824
+ import { Fragment, jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
3825
+ var Spinner = ({
3826
+ isLoading,
3827
+ className,
3828
+ isLoadingText,
3829
+ children
3853
3830
  }) => {
3854
- const { mutate: editItem } = useEditListItem();
3855
- const [isAlertOpen, setAlertOpen] = useState4(false);
3856
- const [data, setData] = useState4();
3857
- return /* @__PURE__ */ jsxs6(Fragment, { children: [
3858
- /* @__PURE__ */ jsx11(
3859
- DeleteAlertDialog,
3860
- {
3861
- deletionType: "item",
3862
- open: isAlertOpen,
3863
- onOpenChange: setAlertOpen,
3864
- onDeleteConfirm: (e) => {
3865
- e.stopPropagation();
3866
- if (data) {
3867
- editItem({
3868
- type,
3869
- dataKey,
3870
- itemKey: data?.key,
3871
- // For deletion
3872
- newKey: void 0
3873
- });
3874
- }
3875
- setAlertOpen(false);
3876
- }
3877
- }
3878
- ),
3879
- /* @__PURE__ */ jsxs6(ContextMenu, { children: [
3880
- /* @__PURE__ */ jsx11(
3881
- ContextMenuTrigger,
3882
- {
3883
- asChild: true,
3884
- onContextMenu: (e) => {
3885
- const el = e.target;
3886
- const item = el.closest("[data-item-key]");
3887
- if (item && item instanceof HTMLElement && item.dataset.itemKey !== void 0) {
3888
- setData({
3889
- key: item.dataset.itemKey,
3890
- value: item.dataset.itemValue
3891
- });
3892
- } else {
3893
- throw new Error("Key not found");
3894
- }
3895
- },
3896
- children
3897
- }
3898
- ),
3899
- /* @__PURE__ */ jsxs6(ContextMenuContent, { children: [
3900
- /* @__PURE__ */ jsx11(
3901
- ContextMenuItem,
3902
- {
3903
- onClick: () => {
3904
- if (!data) return;
3905
- navigator.clipboard.writeText(data?.key);
3906
- toast({
3907
- description: "Key copied to clipboard"
3908
- });
3909
- },
3910
- children: "Copy key"
3911
- }
3912
- ),
3913
- data?.value && /* @__PURE__ */ jsx11(
3914
- ContextMenuItem,
3915
- {
3916
- onClick: () => {
3917
- navigator.clipboard.writeText(data?.value ?? "");
3918
- toast({
3919
- description: "Value copied to clipboard"
3920
- });
3921
- },
3922
- children: "Copy value"
3923
- }
3924
- ),
3925
- /* @__PURE__ */ jsx11(ContextMenuSeparator2, {}),
3926
- /* @__PURE__ */ jsx11(ContextMenuItem, { disabled: type === "stream", onClick: () => setAlertOpen(true), children: "Delete item" })
3927
- ] })
3928
- ] })
3929
- ] });
3930
- };
3931
-
3932
- // src/components/databrowser/components/sidebar/infinite-scroll.tsx
3933
- import { IconLoader2 } from "@tabler/icons-react";
3934
-
3935
- // src/components/ui/scroll-area.tsx
3936
- import * as React6 from "react";
3937
- import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
3938
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
3939
- var ScrollArea = React6.forwardRef(({ className, children, onScroll, ...props }, ref) => /* @__PURE__ */ jsxs7(
3940
- ScrollAreaPrimitive.Root,
3941
- {
3942
- ref,
3943
- className: cn("relative overflow-hidden", className),
3944
- ...props,
3945
- children: [
3946
- /* @__PURE__ */ jsx12(
3947
- ScrollAreaPrimitive.Viewport,
3948
- {
3949
- onScroll,
3950
- className: "h-full w-full rounded-[inherit] [&>div]:!block",
3951
- children
3952
- }
3953
- ),
3954
- /* @__PURE__ */ jsx12(ScrollBar, {}),
3955
- /* @__PURE__ */ jsx12(ScrollAreaPrimitive.Corner, {})
3956
- ]
3957
- }
3958
- ));
3959
- ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
3960
- var ScrollBar = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
3961
- ScrollAreaPrimitive.ScrollAreaScrollbar,
3962
- {
3963
- ref,
3964
- orientation: "vertical",
3965
- className: cn("flex h-full w-2 touch-none select-none transition-colors", className),
3966
- ...props,
3967
- children: /* @__PURE__ */ jsx12(
3968
- ScrollAreaPrimitive.ScrollAreaThumb,
3831
+ return /* @__PURE__ */ jsx12("div", { className: className ?? "flex items-center", children: isLoading ? /* @__PURE__ */ jsxs5(Fragment, { children: [
3832
+ isLoadingText,
3833
+ /* @__PURE__ */ jsx12(
3834
+ "svg",
3969
3835
  {
3970
- className: cn("relative flex-1 rounded-full bg-neutral-200/70 dark:bg-neutral-800")
3836
+ xmlns: "http://www.w3.org/2000/svg",
3837
+ width: "24",
3838
+ height: "24",
3839
+ viewBox: "0 0 24 24",
3840
+ fill: "none",
3841
+ stroke: "currentColor",
3842
+ strokeWidth: "2",
3843
+ strokeLinecap: "round",
3844
+ strokeLinejoin: "round",
3845
+ className: cn("h-4 w-4 animate-spin", isLoadingText ? "ml-2" : ""),
3846
+ children: /* @__PURE__ */ jsx12("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
3971
3847
  }
3972
3848
  )
3973
- }
3974
- ));
3975
- ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
3849
+ ] }) : children });
3850
+ };
3976
3851
 
3977
- // src/components/databrowser/components/sidebar/infinite-scroll.tsx
3978
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
3979
- var InfiniteScroll = ({
3980
- query,
3981
- children
3982
- }) => {
3983
- const handleScroll = (e) => {
3984
- const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
3985
- if (scrollTop + clientHeight > scrollHeight - 100) {
3986
- if (query.isFetching || !query.hasNextPage) {
3987
- return;
3988
- }
3989
- query.fetchNextPage();
3990
- }
3852
+ // src/components/databrowser/components/display/ttl-popover.tsx
3853
+ import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
3854
+ var timeUnits = [
3855
+ { label: "Seconds", value: 1 },
3856
+ { label: "Minutes", value: 60 },
3857
+ { label: "Hours", value: 60 * 60 },
3858
+ { label: "Days", value: 60 * 60 * 24 }
3859
+ ];
3860
+ function TTLPopover({
3861
+ children,
3862
+ ttl,
3863
+ setTTL,
3864
+ isPending
3865
+ }) {
3866
+ const [open, setOpen] = useState4(false);
3867
+ const defaultValues = useMemo3(() => {
3868
+ return { type: "Seconds", value: ttl };
3869
+ }, [ttl]);
3870
+ const { control, handleSubmit, formState, reset } = useForm({
3871
+ defaultValues
3872
+ });
3873
+ useEffect3(() => {
3874
+ reset(defaultValues, {
3875
+ keepValues: true
3876
+ });
3877
+ }, [defaultValues]);
3878
+ const onSubmit = handleSubmit(async ({ value, type }) => {
3879
+ await setTTL(value * timeUnits.find((unit) => unit.label === type).value);
3880
+ setOpen(false);
3881
+ });
3882
+ const handlePersist = async () => {
3883
+ await setTTL(TTL_INFINITE);
3884
+ setOpen(false);
3991
3885
  };
3992
- return /* @__PURE__ */ jsxs8(
3993
- ScrollArea,
3886
+ return /* @__PURE__ */ jsxs6(
3887
+ Popover,
3994
3888
  {
3995
- type: "always",
3996
- className: "block h-full w-full transition-all",
3997
- onScroll: handleScroll,
3889
+ open,
3890
+ onOpenChange: (isOpen) => {
3891
+ if (isOpen) reset();
3892
+ setOpen(isOpen);
3893
+ },
3998
3894
  children: [
3999
- children,
4000
- /* @__PURE__ */ jsx13("div", { className: "flex h-[100px] justify-center py-2 text-zinc-300", children: query.isFetching && /* @__PURE__ */ jsx13(IconLoader2, { className: "animate-spin", size: 16 }) })
3895
+ /* @__PURE__ */ jsx13(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx13("button", { children }) }),
3896
+ /* @__PURE__ */ jsx13(PopoverContent, { className: "w-[300px]", align: "end", children: /* @__PURE__ */ jsxs6(
3897
+ "form",
3898
+ {
3899
+ className: "space-y-4",
3900
+ onSubmit: (e) => {
3901
+ onSubmit(e);
3902
+ e.stopPropagation();
3903
+ },
3904
+ children: [
3905
+ /* @__PURE__ */ jsx13("h4", { className: "font-medium leading-none", children: "Expiration" }),
3906
+ /* @__PURE__ */ jsxs6("div", { children: [
3907
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center", children: [
3908
+ /* @__PURE__ */ jsx13(
3909
+ Controller,
3910
+ {
3911
+ rules: {
3912
+ required: "Please enter an expiration time",
3913
+ min: { value: -1, message: "TTL can't be lower than -1" }
3914
+ },
3915
+ control,
3916
+ name: "value",
3917
+ render: ({ field }) => /* @__PURE__ */ jsx13(Input, { min: "-1", ...field, className: "grow rounded-r-none" })
3918
+ }
3919
+ ),
3920
+ /* @__PURE__ */ jsx13(
3921
+ Controller,
3922
+ {
3923
+ control,
3924
+ name: "type",
3925
+ render: ({ field }) => /* @__PURE__ */ jsxs6(Select, { value: field.value, onValueChange: field.onChange, children: [
3926
+ /* @__PURE__ */ jsx13(SelectTrigger, { className: "w-auto rounded-l-none border-l-0 pr-8", children: /* @__PURE__ */ jsx13(SelectValue, {}) }),
3927
+ /* @__PURE__ */ jsx13(SelectContent, { children: timeUnits.map((unit) => /* @__PURE__ */ jsx13(SelectItem, { value: unit.label, children: unit.label }, unit.label)) })
3928
+ ] })
3929
+ }
3930
+ )
3931
+ ] }),
3932
+ formState.errors.value && /* @__PURE__ */ jsx13("p", { className: "mt-2 text-xs text-red-500", children: formState.errors.value.message }),
3933
+ /* @__PURE__ */ jsx13("p", { className: "mt-2 text-xs text-zinc-500", children: "TTL sets a timer to automatically delete keys after a defined period." })
3934
+ ] }),
3935
+ /* @__PURE__ */ jsxs6("div", { className: "flex justify-between", children: [
3936
+ /* @__PURE__ */ jsx13(
3937
+ Button,
3938
+ {
3939
+ type: "button",
3940
+ variant: "outline",
3941
+ disabled: ttl === TTL_INFINITE,
3942
+ onClick: handlePersist,
3943
+ children: "Persist"
3944
+ }
3945
+ ),
3946
+ /* @__PURE__ */ jsxs6("div", { className: "flex gap-2", children: [
3947
+ /* @__PURE__ */ jsx13(Button, { variant: "outline", onClick: () => setOpen(false), type: "button", children: "Cancel" }),
3948
+ /* @__PURE__ */ jsx13(Button, { variant: "primary", type: "submit", children: /* @__PURE__ */ jsx13(Spinner, { isLoading: isPending, isLoadingText: "Saving", children: "Save" }) })
3949
+ ] })
3950
+ ] })
3951
+ ]
3952
+ }
3953
+ ) })
4001
3954
  ]
4002
3955
  }
4003
3956
  );
4004
- };
4005
-
4006
- // src/components/databrowser/components/display/display-header.tsx
4007
- import { IconPlus } from "@tabler/icons-react";
4008
-
4009
- // src/components/databrowser/components/type-tag.tsx
4010
- import {
4011
- IconArrowsSort,
4012
- IconCodeDots,
4013
- IconHash,
4014
- IconLayersIntersect,
4015
- IconList,
4016
- IconQuote
4017
- } from "@tabler/icons-react";
4018
- import { jsx as jsx14 } from "react/jsx-runtime";
4019
- var iconsMap = {
4020
- string: /* @__PURE__ */ jsx14(IconQuote, { size: 15, stroke: 1.3 }),
4021
- set: /* @__PURE__ */ jsx14(IconLayersIntersect, { size: 15, stroke: 1.3 }),
4022
- hash: /* @__PURE__ */ jsx14(IconHash, { size: 15, stroke: 1.3 }),
4023
- json: /* @__PURE__ */ jsx14(IconCodeDots, { size: 15, stroke: 1.3 }),
4024
- zset: /* @__PURE__ */ jsx14(IconArrowsSort, { size: 15, stroke: 1.3 }),
4025
- list: /* @__PURE__ */ jsx14(IconList, { size: 15, stroke: 1.3 }),
4026
- stream: /* @__PURE__ */ jsx14(IconList, { size: 15, stroke: 1.3 })
4027
- };
4028
- var tagVariants = cva("inline-flex shrink-0 items-center rounded-md justify-center", {
4029
- variants: {
4030
- variant: {
4031
- string: "bg-sky-200 text-sky-800",
4032
- hash: "bg-amber-200 text-amber-800",
4033
- set: "bg-indigo-200 text-indigo-800",
4034
- zset: "bg-pink-200 text-pink-800",
4035
- json: "bg-purple-200 text-purple-800",
4036
- list: "bg-orange-200 text-orange-800",
4037
- stream: "bg-green-200 text-green-800"
4038
- },
4039
- type: {
4040
- icon: "size-5",
4041
- badge: "h-6 px-2 uppercase whitespace-nowrap text-xs font-medium leading-none tracking-wide"
4042
- }
4043
- },
4044
- defaultVariants: {
4045
- variant: "string",
4046
- type: "icon"
4047
- }
4048
- });
4049
- function TypeTag({ className, variant, type }) {
4050
- return /* @__PURE__ */ jsx14("span", { className: cn(tagVariants({ variant, type, className })), children: type === "icon" ? iconsMap[variant] : DATA_TYPE_NAMES[variant] });
4051
3957
  }
4052
3958
 
4053
- // src/components/databrowser/components/display/header-badges.tsx
4054
- import { useEffect as useEffect5 } from "react";
4055
- import { IconChevronDown } from "@tabler/icons-react";
4056
- import bytes from "bytes";
3959
+ // src/components/databrowser/components/display/ttl-badge.tsx
3960
+ import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
3961
+ var TTL_INFINITE = -1;
3962
+ var TTL_NOT_FOUND = -2;
3963
+ var calculateTTL = (expireAt) => {
3964
+ if (!expireAt) return;
3965
+ if (expireAt === TTL_INFINITE) return TTL_INFINITE;
3966
+ return Math.max(0, Math.floor((expireAt - Date.now()) / 1e3));
3967
+ };
3968
+ var TTLBadge = ({
3969
+ label = "TTL:",
3970
+ expireAt,
3971
+ setTTL,
3972
+ isPending
3973
+ }) => {
3974
+ const [ttl, setTTLLabel] = useState5(() => calculateTTL(expireAt));
3975
+ useEffect4(() => {
3976
+ setTTLLabel(calculateTTL(expireAt));
3977
+ const interval = setInterval(() => {
3978
+ setTTLLabel(calculateTTL(expireAt));
3979
+ }, 1e3);
3980
+ return () => clearInterval(interval);
3981
+ }, [expireAt]);
3982
+ return /* @__PURE__ */ jsx14(Badge, { label, children: ttl === void 0 ? /* @__PURE__ */ jsx14(Skeleton, { className: "ml-1 h-3 w-10 rounded-md opacity-50" }) : /* @__PURE__ */ jsx14(TTLPopover, { ttl, setTTL, isPending, children: /* @__PURE__ */ jsxs7("div", { className: "flex gap-[2px]", children: [
3983
+ ttl === TTL_INFINITE ? "Forever" : formatTime(ttl),
3984
+ /* @__PURE__ */ jsx14(IconChevronDown, { className: "mt-[1px] text-zinc-400", size: 12 })
3985
+ ] }) }) });
3986
+ };
4057
3987
 
4058
- // src/components/databrowser/hooks/use-fetch-key-length.ts
4059
- import { useQuery as useQuery4 } from "@tanstack/react-query";
4060
- var FETCH_KEY_LENGTH_QUERY_KEY = "fetch-key-length";
4061
- var useFetchKeyLength = ({ dataKey, type }) => {
3988
+ // src/components/databrowser/hooks/use-fetch-ttl.ts
3989
+ var FETCH_TTL_QUERY_KEY = "fetch-ttl";
3990
+ var useFetchKeyExpire = (dataKey) => {
4062
3991
  const { redis } = useDatabrowser();
4063
- return useQuery4({
4064
- queryKey: [FETCH_KEY_LENGTH_QUERY_KEY, dataKey],
3992
+ const { isLoading, error, data } = useQuery6({
3993
+ queryKey: [FETCH_TTL_QUERY_KEY, dataKey],
4065
3994
  queryFn: async () => {
4066
- switch (type) {
4067
- case "set": {
4068
- return await redis.scard(dataKey);
4069
- }
4070
- case "zset": {
4071
- return await redis.zcard(dataKey);
4072
- }
4073
- case "list": {
4074
- return await redis.llen(dataKey);
4075
- }
4076
- case "hash": {
4077
- return await redis.hlen(dataKey);
4078
- }
4079
- case "stream": {
4080
- return await redis.xlen(dataKey);
4081
- }
3995
+ const ttl = await redis.ttl(dataKey);
3996
+ return ttl === TTL_INFINITE || ttl === TTL_NOT_FOUND ? ttl : Date.now() + ttl * 1e3;
3997
+ }
3998
+ });
3999
+ useEffect5(() => {
4000
+ if (data === TTL_NOT_FOUND) {
4001
+ queryClient.invalidateQueries({
4002
+ queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey]
4003
+ });
4004
+ }
4005
+ }, [data === TTL_NOT_FOUND]);
4006
+ return { isLoading, error, data };
4007
+ };
4008
+
4009
+ // src/components/databrowser/hooks/use-set-simple-key.tsx
4010
+ import { useMutation as useMutation4 } from "@tanstack/react-query";
4011
+ var useSetSimpleKey = (dataKey, type) => {
4012
+ const { redis } = useDatabrowser();
4013
+ return useMutation4({
4014
+ mutationFn: async (value) => {
4015
+ if (type === "string") {
4016
+ await redis.set(dataKey, value);
4017
+ } else if (type === "json") {
4018
+ await redis.json.set(dataKey, "$", JSON.parse(value));
4019
+ } else {
4020
+ throw new Error(`Invalid type when setting simple key: ${type}`);
4082
4021
  }
4083
- return null;
4022
+ },
4023
+ onSuccess: (_, value) => {
4024
+ queryClient.setQueryData([FETCH_SIMPLE_KEY_QUERY_KEY, dataKey], value);
4025
+ queryClient.invalidateQueries({
4026
+ queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey]
4027
+ });
4084
4028
  }
4085
4029
  });
4086
4030
  };
4087
4031
 
4088
- // src/components/databrowser/hooks/use-fetch-key-size.ts
4089
- import { useQuery as useQuery5 } from "@tanstack/react-query";
4090
- var FETCH_KEY_SIZE_QUERY_KEY = "fetch-key-size";
4091
- var useFetchKeySize = (dataKey) => {
4032
+ // src/components/databrowser/hooks/use-set-ttl.ts
4033
+ import { useMutation as useMutation5 } from "@tanstack/react-query";
4034
+ var useSetTTL = () => {
4092
4035
  const { redis } = useDatabrowser();
4093
- return useQuery5({
4094
- queryKey: [FETCH_KEY_SIZE_QUERY_KEY, dataKey],
4095
- queryFn: async () => {
4096
- return await redis.eval(`return redis.call("MEMORY", "USAGE", KEYS[1])`, [dataKey], []);
4036
+ const updateTTL = useMutation5({
4037
+ mutationFn: async ({ dataKey, ttl }) => {
4038
+ await (ttl === void 0 || ttl === TTL_INFINITE ? redis.persist(dataKey) : redis.expire(dataKey, ttl));
4039
+ },
4040
+ onSuccess: (_, { dataKey }) => {
4041
+ queryClient.removeQueries({
4042
+ queryKey: [FETCH_TTL_QUERY_KEY, dataKey]
4043
+ });
4044
+ queryClient.invalidateQueries({
4045
+ queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey]
4046
+ });
4097
4047
  }
4098
4048
  });
4049
+ return updateTTL;
4099
4050
  };
4100
4051
 
4101
- // src/components/databrowser/components/display/ttl-popover.tsx
4102
- import { useEffect as useEffect4, useMemo as useMemo3, useState as useState5 } from "react";
4103
- import { Controller, useForm } from "react-hook-form";
4052
+ // src/components/databrowser/components/item-context-menu.tsx
4053
+ import { useState as useState6 } from "react";
4054
+ import { ContextMenuSeparator as ContextMenuSeparator2 } from "@radix-ui/react-context-menu";
4104
4055
 
4105
- // src/components/ui/input.tsx
4056
+ // src/components/ui/context-menu.tsx
4106
4057
  import * as React7 from "react";
4107
- import { jsx as jsx15 } from "react/jsx-runtime";
4108
- var Input = React7.forwardRef(
4109
- ({ className, type, ...props }, ref) => {
4110
- return /* @__PURE__ */ jsx15(
4111
- "input",
4112
- {
4113
- type,
4114
- className: cn(
4115
- "flex h-8 w-full rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-zinc-950 disabled:cursor-not-allowed disabled:opacity-50",
4116
- className
4117
- ),
4118
- ref,
4119
- ...props
4120
- }
4121
- );
4058
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
4059
+ import { CheckIcon, ChevronRightIcon, DotFilledIcon } from "@radix-ui/react-icons";
4060
+ import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
4061
+ var ContextMenu = ContextMenuPrimitive.Root;
4062
+ var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
4063
+ var ContextMenuSubTrigger = React7.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs8(
4064
+ ContextMenuPrimitive.SubTrigger,
4065
+ {
4066
+ ref,
4067
+ className: cn(
4068
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-900 dark:focus:bg-neutral-800 dark:focus:text-neutral-50 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-50",
4069
+ inset && "pl-8",
4070
+ className
4071
+ ),
4072
+ ...props,
4073
+ children: [
4074
+ children,
4075
+ /* @__PURE__ */ jsx15(ChevronRightIcon, { className: "ml-auto h-4 w-4" })
4076
+ ]
4122
4077
  }
4123
- );
4124
- Input.displayName = "Input";
4125
-
4126
- // src/components/ui/popover.tsx
4127
- import * as React8 from "react";
4128
- import * as PopoverPrimitive from "@radix-ui/react-popover";
4129
- import { jsx as jsx16 } from "react/jsx-runtime";
4130
- var Popover = PopoverPrimitive.Root;
4131
- var PopoverTrigger = PopoverPrimitive.Trigger;
4132
- var PopoverContent = React8.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx16(PopoverPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx16(
4133
- PopoverPrimitive.Content,
4078
+ ));
4079
+ ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
4080
+ var ContextMenuSubContent = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx15(
4081
+ ContextMenuPrimitive.SubContent,
4134
4082
  {
4135
4083
  ref,
4136
- align,
4137
- sideOffset,
4138
4084
  className: cn(
4139
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border border-zinc-200 bg-white p-4 text-zinc-950 shadow-md outline-none dark:border-zinc-800 mt-0.5 dark:bg-zinc-950 dark:text-zinc-50",
4085
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
4086
+ className
4087
+ ),
4088
+ ...props
4089
+ }
4090
+ ));
4091
+ ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
4092
+ var ContextMenuContent = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx15(ContextMenuPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx15(
4093
+ ContextMenuPrimitive.Content,
4094
+ {
4095
+ ref,
4096
+ className: cn(
4097
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
4140
4098
  className
4141
4099
  ),
4142
4100
  ...props
4143
4101
  }
4144
4102
  ) }));
4145
- PopoverContent.displayName = PopoverPrimitive.Content.displayName;
4103
+ ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
4104
+ var ContextMenuItem = React7.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx15(
4105
+ ContextMenuPrimitive.Item,
4106
+ {
4107
+ ref,
4108
+ className: cn(
4109
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
4110
+ inset && "pl-8",
4111
+ className
4112
+ ),
4113
+ ...props
4114
+ }
4115
+ ));
4116
+ ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
4117
+ var ContextMenuCheckboxItem = React7.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs8(
4118
+ ContextMenuPrimitive.CheckboxItem,
4119
+ {
4120
+ ref,
4121
+ className: cn(
4122
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
4123
+ className
4124
+ ),
4125
+ checked,
4126
+ ...props,
4127
+ children: [
4128
+ /* @__PURE__ */ jsx15("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx15(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx15(CheckIcon, { className: "h-4 w-4" }) }) }),
4129
+ children
4130
+ ]
4131
+ }
4132
+ ));
4133
+ ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
4134
+ var ContextMenuRadioItem = React7.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs8(
4135
+ ContextMenuPrimitive.RadioItem,
4136
+ {
4137
+ ref,
4138
+ className: cn(
4139
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
4140
+ className
4141
+ ),
4142
+ ...props,
4143
+ children: [
4144
+ /* @__PURE__ */ jsx15("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx15(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx15(DotFilledIcon, { className: "h-4 w-4 fill-current" }) }) }),
4145
+ children
4146
+ ]
4147
+ }
4148
+ ));
4149
+ ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
4150
+ var ContextMenuLabel = React7.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx15(
4151
+ ContextMenuPrimitive.Label,
4152
+ {
4153
+ ref,
4154
+ className: cn(
4155
+ "px-2 py-1.5 text-sm font-semibold text-neutral-950 dark:text-neutral-50",
4156
+ inset && "pl-8",
4157
+ className
4158
+ ),
4159
+ ...props
4160
+ }
4161
+ ));
4162
+ ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
4163
+ var ContextMenuSeparator = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx15(
4164
+ ContextMenuPrimitive.Separator,
4165
+ {
4166
+ ref,
4167
+ className: cn("-mx-1 my-1 h-px bg-neutral-200 dark:bg-neutral-800", className),
4168
+ ...props
4169
+ }
4170
+ ));
4171
+ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
4172
+ var ContextMenuShortcut = ({ className, ...props }) => {
4173
+ return /* @__PURE__ */ jsx15(
4174
+ "span",
4175
+ {
4176
+ className: cn(
4177
+ "ml-auto text-xs tracking-widest text-neutral-500 dark:text-neutral-400",
4178
+ className
4179
+ ),
4180
+ ...props
4181
+ }
4182
+ );
4183
+ };
4184
+ ContextMenuShortcut.displayName = "ContextMenuShortcut";
4146
4185
 
4147
- // src/components/ui/select.tsx
4148
- import * as React9 from "react";
4149
- import * as SelectPrimitive from "@radix-ui/react-select";
4150
- import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
4151
- var Select = SelectPrimitive.Root;
4152
- var SelectGroup = SelectPrimitive.Group;
4153
- var SelectValue = SelectPrimitive.Value;
4154
- var SelectTrigger = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs9(
4155
- SelectPrimitive.Trigger,
4186
+ // src/components/ui/alert-dialog.tsx
4187
+ import * as React8 from "react";
4188
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
4189
+ import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
4190
+ var AlertDialog = AlertDialogPrimitive.Root;
4191
+ var AlertDialogTrigger = AlertDialogPrimitive.Trigger;
4192
+ var AlertDialogPortal = ({ ...props }) => /* @__PURE__ */ jsx16(AlertDialogPrimitive.Portal, { container: portalRoot, ...props });
4193
+ AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName;
4194
+ var AlertDialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
4195
+ AlertDialogPrimitive.Overlay,
4156
4196
  {
4157
- ref,
4158
4197
  className: cn(
4159
- "relative flex h-8 w-full items-center justify-between rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm ring-offset-white placeholder:text-zinc-500 focus:ring-zinc-950 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-800 dark:bg-zinc-950 dark:ring-offset-zinc-950 dark:placeholder:text-zinc-400 dark:focus:ring-zinc-300",
4198
+ "fixed inset-0 z-50 bg-white/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 dark:bg-zinc-950/80",
4160
4199
  className
4161
4200
  ),
4162
4201
  ...props,
4163
- children: [
4164
- children,
4165
- /* @__PURE__ */ jsx17(SelectPrimitive.Icon, { asChild: true, className: "absolute right-2", children: /* @__PURE__ */ jsx17(
4166
- "svg",
4167
- {
4168
- width: "16",
4169
- height: "16",
4170
- viewBox: "0 0 16 16",
4171
- fill: "none",
4172
- xmlns: "http://www.w3.org/2000/svg",
4173
- children: /* @__PURE__ */ jsx17(
4174
- "path",
4175
- {
4176
- d: "M4 6L8 10L12 6",
4177
- stroke: "black",
4178
- strokeOpacity: "0.4",
4179
- strokeWidth: "1.4",
4180
- strokeLinecap: "round",
4181
- strokeLinejoin: "round"
4182
- }
4183
- )
4184
- }
4185
- ) })
4186
- ]
4202
+ ref
4187
4203
  }
4188
4204
  ));
4189
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
4190
- var SelectContent = React9.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx17(SelectPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx17(
4191
- SelectPrimitive.Content,
4205
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
4206
+ var AlertDialogContent = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs9(AlertDialogPortal, { children: [
4207
+ /* @__PURE__ */ jsx16(AlertDialogOverlay, {}),
4208
+ /* @__PURE__ */ jsx16(
4209
+ AlertDialogPrimitive.Content,
4210
+ {
4211
+ ref,
4212
+ className: cn(
4213
+ "antialiased data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-zinc-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] dark:border-zinc-800 dark:bg-zinc-950 sm:rounded-lg md:w-full",
4214
+ className
4215
+ ),
4216
+ ...props
4217
+ }
4218
+ )
4219
+ ] }));
4220
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
4221
+ var AlertDialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx16("div", { className: cn("flex flex-col space-y-2 text-center sm:text-left", className), ...props });
4222
+ AlertDialogHeader.displayName = "AlertDialogHeader";
4223
+ var AlertDialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx16(
4224
+ "div",
4225
+ {
4226
+ className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
4227
+ ...props
4228
+ }
4229
+ );
4230
+ AlertDialogFooter.displayName = "AlertDialogFooter";
4231
+ var AlertDialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
4232
+ AlertDialogPrimitive.Title,
4192
4233
  {
4193
4234
  ref,
4194
- className: cn(
4195
- "relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white text-zinc-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-neutral-50",
4196
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
4197
- className
4198
- ),
4199
- position,
4200
- ...props,
4201
- children: /* @__PURE__ */ jsx17(
4202
- SelectPrimitive.Viewport,
4203
- {
4204
- className: cn(
4205
- "p-1",
4206
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
4207
- ),
4208
- children
4209
- }
4210
- )
4235
+ className: cn("text-lg font-semibold", className),
4236
+ ...props
4211
4237
  }
4212
- ) }));
4213
- SelectContent.displayName = SelectPrimitive.Content.displayName;
4214
- var SelectLabel = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
4215
- SelectPrimitive.Label,
4238
+ ));
4239
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
4240
+ var AlertDialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
4241
+ AlertDialogPrimitive.Description,
4216
4242
  {
4217
4243
  ref,
4218
- className: cn("px-2 py-1.5 text-sm font-semibold", className),
4244
+ className: cn("text-sm text-zinc-500 dark:text-zinc-400", className),
4219
4245
  ...props
4220
4246
  }
4221
4247
  ));
4222
- SelectLabel.displayName = SelectPrimitive.Label.displayName;
4223
- var SelectItem = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs9(
4224
- SelectPrimitive.Item,
4248
+ AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
4249
+ var AlertDialogAction = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx16(AlertDialogPrimitive.Action, { ref, className: cn(buttonVariants(), className), ...props }));
4250
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
4251
+ var AlertDialogCancel = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
4252
+ AlertDialogPrimitive.Cancel,
4225
4253
  {
4226
4254
  ref,
4227
- className: cn(
4228
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
4229
- className
4255
+ className: cn(buttonVariants({ variant: "outline" }), "mt-2 sm:!mt-0", className),
4256
+ ...props
4257
+ }
4258
+ ));
4259
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
4260
+
4261
+ // src/components/databrowser/components/display/delete-alert-dialog.tsx
4262
+ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
4263
+ function DeleteAlertDialog({
4264
+ children,
4265
+ onDeleteConfirm,
4266
+ open,
4267
+ onOpenChange,
4268
+ deletionType
4269
+ }) {
4270
+ return /* @__PURE__ */ jsxs10(AlertDialog, { open, onOpenChange, children: [
4271
+ children && /* @__PURE__ */ jsx17(AlertDialogTrigger, { asChild: true, children }),
4272
+ /* @__PURE__ */ jsxs10(AlertDialogContent, { children: [
4273
+ /* @__PURE__ */ jsxs10(AlertDialogHeader, { children: [
4274
+ /* @__PURE__ */ jsx17(AlertDialogTitle, { children: deletionType === "item" ? "Delete Item" : "Delete Key" }),
4275
+ /* @__PURE__ */ jsxs10(AlertDialogDescription, { className: "mt-5", children: [
4276
+ "Are you sure you want to delete this ",
4277
+ deletionType,
4278
+ "?",
4279
+ /* @__PURE__ */ jsx17("br", {}),
4280
+ "This action cannot be undone."
4281
+ ] })
4282
+ ] }),
4283
+ /* @__PURE__ */ jsxs10(AlertDialogFooter, { children: [
4284
+ /* @__PURE__ */ jsx17(AlertDialogCancel, { type: "button", children: "Cancel" }),
4285
+ /* @__PURE__ */ jsx17(
4286
+ AlertDialogAction,
4287
+ {
4288
+ className: "bg-red-500 text-gray-50 hover:bg-red-600",
4289
+ onClick: onDeleteConfirm,
4290
+ children: "Yes, Delete"
4291
+ }
4292
+ )
4293
+ ] })
4294
+ ] })
4295
+ ] });
4296
+ }
4297
+
4298
+ // src/components/databrowser/components/item-context-menu.tsx
4299
+ import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
4300
+ var ItemContextMenu = ({
4301
+ children,
4302
+ dataKey,
4303
+ type
4304
+ }) => {
4305
+ const { mutate: editItem } = useEditListItem();
4306
+ const [isAlertOpen, setAlertOpen] = useState6(false);
4307
+ const [data, setData] = useState6();
4308
+ return /* @__PURE__ */ jsxs11(Fragment2, { children: [
4309
+ /* @__PURE__ */ jsx18(
4310
+ DeleteAlertDialog,
4311
+ {
4312
+ deletionType: "item",
4313
+ open: isAlertOpen,
4314
+ onOpenChange: setAlertOpen,
4315
+ onDeleteConfirm: (e) => {
4316
+ e.stopPropagation();
4317
+ if (data) {
4318
+ editItem({
4319
+ type,
4320
+ dataKey,
4321
+ itemKey: data?.key,
4322
+ // For deletion
4323
+ newKey: void 0
4324
+ });
4325
+ }
4326
+ setAlertOpen(false);
4327
+ }
4328
+ }
4230
4329
  ),
4330
+ /* @__PURE__ */ jsxs11(ContextMenu, { children: [
4331
+ /* @__PURE__ */ jsx18(
4332
+ ContextMenuTrigger,
4333
+ {
4334
+ asChild: true,
4335
+ onContextMenu: (e) => {
4336
+ const el = e.target;
4337
+ const item = el.closest("[data-item-key]");
4338
+ if (item && item instanceof HTMLElement && item.dataset.itemKey !== void 0) {
4339
+ setData({
4340
+ key: item.dataset.itemKey,
4341
+ value: item.dataset.itemValue
4342
+ });
4343
+ } else {
4344
+ throw new Error("Key not found");
4345
+ }
4346
+ },
4347
+ children
4348
+ }
4349
+ ),
4350
+ /* @__PURE__ */ jsxs11(ContextMenuContent, { children: [
4351
+ /* @__PURE__ */ jsx18(
4352
+ ContextMenuItem,
4353
+ {
4354
+ onClick: () => {
4355
+ if (!data) return;
4356
+ navigator.clipboard.writeText(data?.key);
4357
+ toast({
4358
+ description: "Key copied to clipboard"
4359
+ });
4360
+ },
4361
+ children: "Copy key"
4362
+ }
4363
+ ),
4364
+ data?.value && /* @__PURE__ */ jsx18(
4365
+ ContextMenuItem,
4366
+ {
4367
+ onClick: () => {
4368
+ navigator.clipboard.writeText(data?.value ?? "");
4369
+ toast({
4370
+ description: "Value copied to clipboard"
4371
+ });
4372
+ },
4373
+ children: "Copy value"
4374
+ }
4375
+ ),
4376
+ /* @__PURE__ */ jsx18(ContextMenuSeparator2, {}),
4377
+ /* @__PURE__ */ jsx18(ContextMenuItem, { disabled: type === "stream", onClick: () => setAlertOpen(true), children: "Delete item" })
4378
+ ] })
4379
+ ] })
4380
+ ] });
4381
+ };
4382
+
4383
+ // src/components/databrowser/components/sidebar/infinite-scroll.tsx
4384
+ import { IconLoader2 } from "@tabler/icons-react";
4385
+
4386
+ // src/components/ui/scroll-area.tsx
4387
+ import * as React9 from "react";
4388
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
4389
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
4390
+ var ScrollArea = React9.forwardRef(({ className, children, onScroll, ...props }, ref) => /* @__PURE__ */ jsxs12(
4391
+ ScrollAreaPrimitive.Root,
4392
+ {
4393
+ ref,
4394
+ className: cn("relative overflow-hidden", className),
4231
4395
  ...props,
4232
4396
  children: [
4233
- /* @__PURE__ */ jsx17("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx17(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17(
4234
- "svg",
4397
+ /* @__PURE__ */ jsx19(
4398
+ ScrollAreaPrimitive.Viewport,
4235
4399
  {
4236
- width: "15",
4237
- height: "15",
4238
- viewBox: "0 0 15 15",
4239
- fill: "none",
4240
- xmlns: "http://www.w3.org/2000/svg",
4241
- className: "h-4 w-4",
4242
- children: /* @__PURE__ */ jsx17(
4243
- "path",
4244
- {
4245
- d: "M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",
4246
- fill: "currentColor",
4247
- fillRule: "evenodd",
4248
- clipRule: "evenodd"
4249
- }
4250
- )
4400
+ onScroll,
4401
+ className: "h-full w-full rounded-[inherit] [&>div]:!block",
4402
+ children
4251
4403
  }
4252
- ) }) }) }),
4253
- /* @__PURE__ */ jsx17(SelectPrimitive.ItemText, { children })
4404
+ ),
4405
+ /* @__PURE__ */ jsx19(ScrollBar, {}),
4406
+ /* @__PURE__ */ jsx19(ScrollAreaPrimitive.Corner, {})
4254
4407
  ]
4255
4408
  }
4256
4409
  ));
4257
- SelectItem.displayName = SelectPrimitive.Item.displayName;
4258
- var SelectSeparator = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
4259
- SelectPrimitive.Separator,
4410
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
4411
+ var ScrollBar = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx19(
4412
+ ScrollAreaPrimitive.ScrollAreaScrollbar,
4260
4413
  {
4261
4414
  ref,
4262
- className: cn("-mx-1 my-1 h-px bg-neutral-100 dark:bg-neutral-800", className),
4263
- ...props
4264
- }
4265
- ));
4266
- SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
4267
-
4268
- // src/components/ui/spinner.tsx
4269
- import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
4270
- var Spinner = ({
4271
- isLoading,
4272
- className,
4273
- isLoadingText,
4274
- children
4275
- }) => {
4276
- return /* @__PURE__ */ jsx18("div", { className: className ?? "flex items-center", children: isLoading ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
4277
- isLoadingText,
4278
- /* @__PURE__ */ jsx18(
4279
- "svg",
4415
+ orientation: "vertical",
4416
+ className: cn("flex h-full w-2 touch-none select-none transition-colors", className),
4417
+ ...props,
4418
+ children: /* @__PURE__ */ jsx19(
4419
+ ScrollAreaPrimitive.ScrollAreaThumb,
4280
4420
  {
4281
- xmlns: "http://www.w3.org/2000/svg",
4282
- width: "24",
4283
- height: "24",
4284
- viewBox: "0 0 24 24",
4285
- fill: "none",
4286
- stroke: "currentColor",
4287
- strokeWidth: "2",
4288
- strokeLinecap: "round",
4289
- strokeLinejoin: "round",
4290
- className: cn("h-4 w-4 animate-spin", isLoadingText ? "ml-2" : ""),
4291
- children: /* @__PURE__ */ jsx18("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
4421
+ className: cn("relative flex-1 rounded-full bg-neutral-200/70 dark:bg-neutral-800")
4292
4422
  }
4293
4423
  )
4294
- ] }) : children });
4295
- };
4424
+ }
4425
+ ));
4426
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
4296
4427
 
4297
- // src/components/databrowser/components/display/ttl-popover.tsx
4298
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
4299
- var PERSISTED_KEY = -1;
4300
- var timeUnits = [
4301
- { label: "Seconds", value: 1 },
4302
- { label: "Minutes", value: 60 },
4303
- { label: "Hours", value: 60 * 60 },
4304
- { label: "Days", value: 60 * 60 * 24 }
4305
- ];
4306
- function TTLPopover({
4307
- children,
4308
- ttl,
4309
- dataKey
4310
- }) {
4311
- const [open, setOpen] = useState5(false);
4312
- const { mutateAsync: setTTL, isPending } = useSetTTL();
4313
- const defaultValues = useMemo3(() => {
4314
- return { type: "Seconds", value: ttl };
4315
- }, [ttl]);
4316
- const { control, handleSubmit, formState, reset } = useForm({
4317
- defaultValues
4318
- });
4319
- useEffect4(() => {
4320
- reset(defaultValues, {
4321
- keepValues: true
4322
- });
4323
- }, [defaultValues]);
4324
- const onSubmit = handleSubmit(async ({ value, type }) => {
4325
- await setTTL({
4326
- dataKey,
4327
- ttl: value * timeUnits.find((unit) => unit.label === type).value
4328
- });
4329
- setOpen(false);
4330
- });
4331
- const handlePersist = async () => {
4332
- await setTTL({
4333
- dataKey,
4334
- ttl: void 0
4335
- });
4336
- setOpen(false);
4428
+ // src/components/databrowser/components/sidebar/infinite-scroll.tsx
4429
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
4430
+ var InfiniteScroll = ({
4431
+ query,
4432
+ children
4433
+ }) => {
4434
+ const handleScroll = (e) => {
4435
+ const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
4436
+ if (scrollTop + clientHeight > scrollHeight - 100) {
4437
+ if (query.isFetching || !query.hasNextPage) {
4438
+ return;
4439
+ }
4440
+ query.fetchNextPage();
4441
+ }
4337
4442
  };
4338
- return /* @__PURE__ */ jsxs11(
4339
- Popover,
4443
+ return /* @__PURE__ */ jsxs13(
4444
+ ScrollArea,
4340
4445
  {
4341
- open,
4342
- onOpenChange: (isOpen) => {
4343
- if (isOpen) reset();
4344
- setOpen(isOpen);
4345
- },
4446
+ type: "always",
4447
+ className: "block h-full w-full transition-all",
4448
+ onScroll: handleScroll,
4346
4449
  children: [
4347
- /* @__PURE__ */ jsx19(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx19("button", { children }) }),
4348
- /* @__PURE__ */ jsx19(PopoverContent, { className: "w-[300px]", align: "end", children: /* @__PURE__ */ jsxs11("form", { className: "space-y-4", onSubmit, children: [
4349
- /* @__PURE__ */ jsx19("h4", { className: "font-medium leading-none", children: "Expiration" }),
4350
- /* @__PURE__ */ jsxs11("div", { children: [
4351
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center", children: [
4352
- /* @__PURE__ */ jsx19(
4353
- Controller,
4354
- {
4355
- rules: {
4356
- required: "Please enter an expiration time",
4357
- min: { value: -1, message: "TTL can't be lower than -1" }
4358
- },
4359
- control,
4360
- name: "value",
4361
- render: ({ field }) => /* @__PURE__ */ jsx19(Input, { min: "-1", ...field, className: "grow rounded-r-none" })
4362
- }
4363
- ),
4364
- /* @__PURE__ */ jsx19(
4365
- Controller,
4366
- {
4367
- control,
4368
- name: "type",
4369
- render: ({ field }) => /* @__PURE__ */ jsxs11(Select, { value: field.value, onValueChange: field.onChange, children: [
4370
- /* @__PURE__ */ jsx19(SelectTrigger, { className: "w-auto rounded-l-none border-l-0 pr-8", children: /* @__PURE__ */ jsx19(SelectValue, {}) }),
4371
- /* @__PURE__ */ jsx19(SelectContent, { children: timeUnits.map((unit) => /* @__PURE__ */ jsx19(SelectItem, { value: unit.label, children: unit.label }, unit.label)) })
4372
- ] })
4373
- }
4374
- )
4375
- ] }),
4376
- formState.errors.value && /* @__PURE__ */ jsx19("p", { className: "mt-2 text-xs text-red-500", children: formState.errors.value.message }),
4377
- /* @__PURE__ */ jsx19("p", { className: "mt-2 text-xs text-zinc-500", children: "TTL sets a timer to automatically delete keys after a defined period." })
4378
- ] }),
4379
- /* @__PURE__ */ jsxs11("div", { className: "flex justify-between", children: [
4380
- /* @__PURE__ */ jsx19(
4381
- Button,
4382
- {
4383
- type: "button",
4384
- variant: "outline",
4385
- disabled: ttl === PERSISTED_KEY,
4386
- onClick: handlePersist,
4387
- children: "Persist"
4388
- }
4389
- ),
4390
- /* @__PURE__ */ jsxs11("div", { className: "flex gap-2", children: [
4391
- /* @__PURE__ */ jsx19(Button, { variant: "outline", onClick: () => setOpen(false), type: "button", children: "Cancel" }),
4392
- /* @__PURE__ */ jsx19(Button, { variant: "primary", type: "submit", children: /* @__PURE__ */ jsx19(Spinner, { isLoading: isPending, isLoadingText: "Saving", children: "Save" }) })
4393
- ] })
4394
- ] })
4395
- ] }) })
4450
+ children,
4451
+ /* @__PURE__ */ jsx20("div", { className: "flex h-[100px] justify-center py-2 text-zinc-300", children: query.isFetching && /* @__PURE__ */ jsx20(IconLoader2, { className: "animate-spin", size: 16 }) })
4396
4452
  ]
4397
4453
  }
4398
4454
  );
4399
- }
4400
-
4401
- // src/components/databrowser/components/display/header-badges.tsx
4402
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
4403
- var LengthBadge = ({
4404
- dataKey,
4405
- type,
4406
- content
4407
- }) => {
4408
- const { data, isLoading } = useFetchKeyLength({ dataKey, type });
4409
- const length = content?.length ?? data;
4410
- return /* @__PURE__ */ jsx20(Badge, { label: "Length:", children: isLoading ? /* @__PURE__ */ jsx20(Skeleton, { className: "ml-1 h-3 w-10 rounded-md opacity-50" }) : length });
4411
- };
4412
- var SizeBadge = ({ dataKey }) => {
4413
- const { data: size } = useFetchKeySize(dataKey);
4414
- return /* @__PURE__ */ jsx20(Badge, { label: "Size:", children: size === void 0 || size === null ? /* @__PURE__ */ jsx20(Skeleton, { className: "ml-1 h-3 w-10 rounded-md opacity-50" }) : bytes(size, {
4415
- unitSeparator: " "
4416
- }) });
4417
4455
  };
4418
- var TTL_INFINITE = -1;
4419
- var TTL_NOT_FOUND = -2;
4420
- var TTLBadge = ({ dataKey }) => {
4421
- const { data: ttl } = useFetchTTL(dataKey);
4422
- const { deleteKeyCache } = useDeleteKeyCache();
4423
- useEffect5(() => {
4424
- const interval = setInterval(() => {
4425
- queryClient.setQueryData([FETCH_TTL_QUERY_KEY, dataKey], (ttl2) => {
4426
- if (ttl2 === void 0 || ttl2 === TTL_INFINITE) return ttl2;
4427
- if (ttl2 <= 1) {
4428
- deleteKeyCache(dataKey);
4429
- return TTL_NOT_FOUND;
4430
- }
4431
- return ttl2 - 1;
4432
- });
4433
- }, 1e3);
4434
- return () => clearInterval(interval);
4435
- }, []);
4436
- return /* @__PURE__ */ jsx20(Badge, { label: "TTL:", children: ttl === void 0 ? /* @__PURE__ */ jsx20(Skeleton, { className: "ml-1 h-3 w-10 rounded-md opacity-50" }) : /* @__PURE__ */ jsx20(TTLPopover, { dataKey, ttl, children: /* @__PURE__ */ jsxs12("div", { className: "flex gap-[2px]", children: [
4437
- ttl === TTL_INFINITE ? "Forever" : formatTime(ttl),
4438
- /* @__PURE__ */ jsx20(IconChevronDown, { className: "mt-[1px] text-zinc-400", size: 12 })
4439
- ] }) }) });
4456
+
4457
+ // src/components/databrowser/components/display/display-header.tsx
4458
+ import { IconPlus } from "@tabler/icons-react";
4459
+
4460
+ // src/components/databrowser/components/type-tag.tsx
4461
+ import {
4462
+ IconArrowsSort,
4463
+ IconCodeDots,
4464
+ IconHash,
4465
+ IconLayersIntersect,
4466
+ IconList,
4467
+ IconQuote
4468
+ } from "@tabler/icons-react";
4469
+ import { jsx as jsx21 } from "react/jsx-runtime";
4470
+ var iconsMap = {
4471
+ string: /* @__PURE__ */ jsx21(IconQuote, { size: 15, stroke: 1.3 }),
4472
+ set: /* @__PURE__ */ jsx21(IconLayersIntersect, { size: 15, stroke: 1.3 }),
4473
+ hash: /* @__PURE__ */ jsx21(IconHash, { size: 15, stroke: 1.3 }),
4474
+ json: /* @__PURE__ */ jsx21(IconCodeDots, { size: 15, stroke: 1.3 }),
4475
+ zset: /* @__PURE__ */ jsx21(IconArrowsSort, { size: 15, stroke: 1.3 }),
4476
+ list: /* @__PURE__ */ jsx21(IconList, { size: 15, stroke: 1.3 }),
4477
+ stream: /* @__PURE__ */ jsx21(IconList, { size: 15, stroke: 1.3 })
4440
4478
  };
4441
- var Badge = ({ children, label }) => /* @__PURE__ */ jsxs12("div", { className: "flex h-6 items-center gap-0.5 rounded-md bg-white px-2 text-xs text-zinc-700", children: [
4442
- /* @__PURE__ */ jsx20("span", { className: "text-zinc-500", children: label }),
4443
- /* @__PURE__ */ jsx20("span", { className: "font-medium", children })
4444
- ] });
4479
+ var tagVariants = cva("inline-flex shrink-0 items-center rounded-md justify-center", {
4480
+ variants: {
4481
+ variant: {
4482
+ string: "bg-sky-200 text-sky-800",
4483
+ hash: "bg-amber-200 text-amber-800",
4484
+ set: "bg-indigo-200 text-indigo-800",
4485
+ zset: "bg-pink-200 text-pink-800",
4486
+ json: "bg-purple-200 text-purple-800",
4487
+ list: "bg-orange-200 text-orange-800",
4488
+ stream: "bg-green-200 text-green-800"
4489
+ },
4490
+ type: {
4491
+ icon: "size-5",
4492
+ badge: "h-6 px-2 uppercase whitespace-nowrap text-xs font-medium leading-none tracking-wide"
4493
+ }
4494
+ },
4495
+ defaultVariants: {
4496
+ variant: "string",
4497
+ type: "icon"
4498
+ }
4499
+ });
4500
+ function TypeTag({ className, variant, type }) {
4501
+ return /* @__PURE__ */ jsx21("span", { className: cn(tagVariants({ variant, type, className })), children: type === "icon" ? iconsMap[variant] : DATA_TYPE_NAMES[variant] });
4502
+ }
4445
4503
 
4446
4504
  // src/components/databrowser/components/display/key-actions.tsx
4447
4505
  import { IconDotsVertical } from "@tabler/icons-react";
@@ -4450,10 +4508,10 @@ import { IconDotsVertical } from "@tabler/icons-react";
4450
4508
  import * as React10 from "react";
4451
4509
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
4452
4510
  import { CheckIcon as CheckIcon2, ChevronRightIcon as ChevronRightIcon2, DotFilledIcon as DotFilledIcon2 } from "@radix-ui/react-icons";
4453
- import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
4511
+ import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
4454
4512
  var DropdownMenu = DropdownMenuPrimitive.Root;
4455
4513
  var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
4456
- var DropdownMenuSubTrigger = React10.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs13(
4514
+ var DropdownMenuSubTrigger = React10.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs14(
4457
4515
  DropdownMenuPrimitive.SubTrigger,
4458
4516
  {
4459
4517
  ref,
@@ -4465,12 +4523,12 @@ var DropdownMenuSubTrigger = React10.forwardRef(({ className, inset, children, .
4465
4523
  ...props,
4466
4524
  children: [
4467
4525
  children,
4468
- /* @__PURE__ */ jsx21(ChevronRightIcon2, { className: "ml-auto size-4" })
4526
+ /* @__PURE__ */ jsx22(ChevronRightIcon2, { className: "ml-auto size-4" })
4469
4527
  ]
4470
4528
  }
4471
4529
  ));
4472
4530
  DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
4473
- var DropdownMenuSubContent = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx21(
4531
+ var DropdownMenuSubContent = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx22(
4474
4532
  DropdownMenuPrimitive.SubContent,
4475
4533
  {
4476
4534
  ref,
@@ -4482,7 +4540,7 @@ var DropdownMenuSubContent = React10.forwardRef(({ className, ...props }, ref) =
4482
4540
  }
4483
4541
  ));
4484
4542
  DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
4485
- var DropdownMenuContent = React10.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx21(DropdownMenuPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx21(
4543
+ var DropdownMenuContent = React10.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx22(DropdownMenuPrimitive.Portal, { container: portalRoot, children: /* @__PURE__ */ jsx22(
4486
4544
  DropdownMenuPrimitive.Content,
4487
4545
  {
4488
4546
  ref,
@@ -4496,7 +4554,7 @@ var DropdownMenuContent = React10.forwardRef(({ className, sideOffset = 4, ...pr
4496
4554
  }
4497
4555
  ) }));
4498
4556
  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
4499
- var DropdownMenuItem = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx21(
4557
+ var DropdownMenuItem = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx22(
4500
4558
  DropdownMenuPrimitive.Item,
4501
4559
  {
4502
4560
  ref,
@@ -4509,7 +4567,7 @@ var DropdownMenuItem = React10.forwardRef(({ className, inset, ...props }, ref)
4509
4567
  }
4510
4568
  ));
4511
4569
  DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
4512
- var DropdownMenuCheckboxItem = React10.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs13(
4570
+ var DropdownMenuCheckboxItem = React10.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs14(
4513
4571
  DropdownMenuPrimitive.CheckboxItem,
4514
4572
  {
4515
4573
  ref,
@@ -4520,13 +4578,13 @@ var DropdownMenuCheckboxItem = React10.forwardRef(({ className, children, checke
4520
4578
  checked,
4521
4579
  ...props,
4522
4580
  children: [
4523
- /* @__PURE__ */ jsx21("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx21(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx21(CheckIcon2, { className: "size-4" }) }) }),
4581
+ /* @__PURE__ */ jsx22("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx22(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx22(CheckIcon2, { className: "size-4" }) }) }),
4524
4582
  children
4525
4583
  ]
4526
4584
  }
4527
4585
  ));
4528
4586
  DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
4529
- var DropdownMenuRadioItem = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs13(
4587
+ var DropdownMenuRadioItem = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs14(
4530
4588
  DropdownMenuPrimitive.RadioItem,
4531
4589
  {
4532
4590
  ref,
@@ -4536,13 +4594,13 @@ var DropdownMenuRadioItem = React10.forwardRef(({ className, children, ...props
4536
4594
  ),
4537
4595
  ...props,
4538
4596
  children: [
4539
- /* @__PURE__ */ jsx21("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx21(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx21(DotFilledIcon2, { className: "size-4 fill-current" }) }) }),
4597
+ /* @__PURE__ */ jsx22("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx22(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx22(DotFilledIcon2, { className: "size-4 fill-current" }) }) }),
4540
4598
  children
4541
4599
  ]
4542
4600
  }
4543
4601
  ));
4544
4602
  DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
4545
- var DropdownMenuLabel = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx21(
4603
+ var DropdownMenuLabel = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx22(
4546
4604
  DropdownMenuPrimitive.Label,
4547
4605
  {
4548
4606
  ref,
@@ -4551,7 +4609,7 @@ var DropdownMenuLabel = React10.forwardRef(({ className, inset, ...props }, ref)
4551
4609
  }
4552
4610
  ));
4553
4611
  DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
4554
- var DropdownMenuSeparator = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx21(
4612
+ var DropdownMenuSeparator = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx22(
4555
4613
  DropdownMenuPrimitive.Separator,
4556
4614
  {
4557
4615
  ref,
@@ -4561,18 +4619,18 @@ var DropdownMenuSeparator = React10.forwardRef(({ className, ...props }, ref) =>
4561
4619
  ));
4562
4620
  DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
4563
4621
  var DropdownMenuShortcut = ({ className, ...props }) => {
4564
- return /* @__PURE__ */ jsx21("span", { className: cn("ml-auto text-xs tracking-widest opacity-60", className), ...props });
4622
+ return /* @__PURE__ */ jsx22("span", { className: cn("ml-auto text-xs tracking-widest opacity-60", className), ...props });
4565
4623
  };
4566
4624
  DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
4567
4625
 
4568
4626
  // src/components/databrowser/components/display/key-actions.tsx
4569
- import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
4627
+ import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
4570
4628
  function KeyActions({ dataKey, content }) {
4571
4629
  const { mutateAsync: deleteKey } = useDeleteKey();
4572
- return /* @__PURE__ */ jsxs14(DropdownMenu, { children: [
4573
- /* @__PURE__ */ jsx22(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx22(Button, { size: "icon-sm", children: /* @__PURE__ */ jsx22(IconDotsVertical, { className: "size-4 text-zinc-500" }) }) }),
4574
- /* @__PURE__ */ jsxs14(DropdownMenuContent, { className: "", align: "end", children: [
4575
- content && /* @__PURE__ */ jsx22(
4630
+ return /* @__PURE__ */ jsxs15(DropdownMenu, { children: [
4631
+ /* @__PURE__ */ jsx23(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx23(Button, { size: "icon-sm", children: /* @__PURE__ */ jsx23(IconDotsVertical, { className: "size-4 text-zinc-500" }) }) }),
4632
+ /* @__PURE__ */ jsxs15(DropdownMenuContent, { className: "", align: "end", children: [
4633
+ content && /* @__PURE__ */ jsx23(
4576
4634
  DropdownMenuItem,
4577
4635
  {
4578
4636
  onClick: () => {
@@ -4584,12 +4642,12 @@ function KeyActions({ dataKey, content }) {
4584
4642
  children: "Copy content"
4585
4643
  }
4586
4644
  ),
4587
- /* @__PURE__ */ jsx22(
4645
+ /* @__PURE__ */ jsx23(
4588
4646
  DeleteAlertDialog,
4589
4647
  {
4590
4648
  deletionType: "key",
4591
4649
  onDeleteConfirm: async () => await deleteKey(dataKey),
4592
- children: /* @__PURE__ */ jsx22(DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: "Delete key" })
4650
+ children: /* @__PURE__ */ jsx23(DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: "Delete key" })
4593
4651
  }
4594
4652
  )
4595
4653
  ] })
@@ -4597,7 +4655,7 @@ function KeyActions({ dataKey, content }) {
4597
4655
  }
4598
4656
 
4599
4657
  // src/components/databrowser/components/display/display-header.tsx
4600
- import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
4658
+ import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
4601
4659
  var DisplayHeader = ({
4602
4660
  dataKey,
4603
4661
  type,
@@ -4607,34 +4665,33 @@ var DisplayHeader = ({
4607
4665
  const handleAddItem = () => {
4608
4666
  setSelectedListItem({ key: type === "stream" ? "*" : "", isNew: true });
4609
4667
  };
4610
- return /* @__PURE__ */ jsxs15("div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
4611
- /* @__PURE__ */ jsxs15("div", { className: "flex min-h-10 items-center justify-between gap-4", children: [
4612
- /* @__PURE__ */ jsx23("h2", { className: "grow truncate text-base", children: dataKey.trim() === "" ? /* @__PURE__ */ jsx23("span", { className: "ml-1 text-zinc-500", children: "(Empty Key)" }) : /* @__PURE__ */ jsx23("span", { className: "font-semibold", children: dataKey }) }),
4613
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
4614
- type !== "string" && type !== "json" && /* @__PURE__ */ jsx23(Button, { onClick: handleAddItem, size: "icon-sm", children: /* @__PURE__ */ jsx23(IconPlus, { className: "size-4 text-zinc-500" }) }),
4615
- /* @__PURE__ */ jsx23(KeyActions, { dataKey, content })
4668
+ return /* @__PURE__ */ jsxs16("div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
4669
+ /* @__PURE__ */ jsxs16("div", { className: "flex min-h-10 items-center justify-between gap-4", children: [
4670
+ /* @__PURE__ */ jsx24("h2", { className: "grow truncate text-base", children: dataKey.trim() === "" ? /* @__PURE__ */ jsx24("span", { className: "ml-1 text-zinc-500", children: "(Empty Key)" }) : /* @__PURE__ */ jsx24("span", { className: "font-semibold", children: dataKey }) }),
4671
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-1", children: [
4672
+ type !== "string" && type !== "json" && /* @__PURE__ */ jsx24(Button, { onClick: handleAddItem, size: "icon-sm", children: /* @__PURE__ */ jsx24(IconPlus, { className: "size-4 text-zinc-500" }) }),
4673
+ /* @__PURE__ */ jsx24(KeyActions, { dataKey, content })
4616
4674
  ] })
4617
4675
  ] }),
4618
- /* @__PURE__ */ jsxs15("div", { className: "flex h-10 flex-wrap items-center gap-1.5", children: [
4619
- /* @__PURE__ */ jsx23(TypeTag, { variant: type, type: "badge" }),
4620
- /* @__PURE__ */ jsx23(SizeBadge, { dataKey }),
4621
- /* @__PURE__ */ jsx23(LengthBadge, { dataKey, type, content }),
4622
- /* @__PURE__ */ jsx23(TTLBadge, { dataKey })
4676
+ /* @__PURE__ */ jsxs16("div", { className: "flex h-10 flex-wrap items-center gap-1.5", children: [
4677
+ /* @__PURE__ */ jsx24(TypeTag, { variant: type, type: "badge" }),
4678
+ /* @__PURE__ */ jsx24(SizeBadge, { dataKey }),
4679
+ /* @__PURE__ */ jsx24(LengthBadge, { dataKey, type, content }),
4680
+ /* @__PURE__ */ jsx24(HeaderTTLBadge, { dataKey })
4623
4681
  ] })
4624
4682
  ] });
4625
4683
  };
4626
4684
 
4627
4685
  // src/components/databrowser/components/display/display-list-edit.tsx
4628
- import { useEffect as useEffect8 } from "react";
4629
4686
  import { Controller as Controller2, FormProvider, useForm as useForm2, useFormContext } from "react-hook-form";
4630
4687
 
4631
4688
  // src/components/ui/tooltip.tsx
4632
4689
  import * as React11 from "react";
4633
4690
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
4634
- import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
4691
+ import { Fragment as Fragment3, jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
4635
4692
  var Tooltip = TooltipPrimitive.Root;
4636
4693
  var TooltipTrigger = TooltipPrimitive.Trigger;
4637
- var TooltipContent = React11.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx24(
4694
+ var TooltipContent = React11.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx25(
4638
4695
  TooltipPrimitive.Content,
4639
4696
  {
4640
4697
  ref,
@@ -4651,31 +4708,103 @@ var SimpleTooltip = ({
4651
4708
  content,
4652
4709
  children
4653
4710
  }) => {
4654
- if (!content) return /* @__PURE__ */ jsx24(Fragment3, { children });
4655
- return /* @__PURE__ */ jsxs16(Tooltip, { delayDuration: 400, children: [
4656
- /* @__PURE__ */ jsx24(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx24("div", { children }) }),
4657
- /* @__PURE__ */ jsx24(TooltipContent, { children: content })
4711
+ if (!content) return /* @__PURE__ */ jsx25(Fragment3, { children });
4712
+ return /* @__PURE__ */ jsxs17(Tooltip, { delayDuration: 400, children: [
4713
+ /* @__PURE__ */ jsx25(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx25("div", { children }) }),
4714
+ /* @__PURE__ */ jsx25(TooltipContent, { children: content })
4658
4715
  ] });
4659
4716
  };
4660
4717
 
4718
+ // src/components/databrowser/hooks/use-fetch-hash-ttl.ts
4719
+ import { useQuery as useQuery7 } from "@tanstack/react-query";
4720
+ var FETCH_HASH_FIELD_TTLS_QUERY_KEY = "fetch-hash-field-ttls";
4721
+ var useFetchHashFieldExpires = ({
4722
+ dataKey,
4723
+ fields
4724
+ }) => {
4725
+ const { redis } = useDatabrowser();
4726
+ return useQuery7({
4727
+ queryKey: [FETCH_HASH_FIELD_TTLS_QUERY_KEY, dataKey, fields],
4728
+ queryFn: async () => {
4729
+ const cachedExpires = /* @__PURE__ */ new Map();
4730
+ for (const field of fields) {
4731
+ const expireAt = queryClient.getQueryData([
4732
+ FETCH_HASH_FIELD_TTLS_QUERY_KEY,
4733
+ dataKey,
4734
+ field
4735
+ ]);
4736
+ if (expireAt !== void 0) cachedExpires.set(field, expireAt);
4737
+ }
4738
+ const filteredFields = fields.filter((field) => !cachedExpires.has(field));
4739
+ if (filteredFields.length === 0) return Object.fromEntries(cachedExpires.entries());
4740
+ const res = await redis.httl(dataKey, filteredFields);
4741
+ const expireAts = res.map(
4742
+ (ttl) => ttl === TTL_INFINITE || ttl === TTL_NOT_FOUND ? ttl : Date.now() + ttl * 1e3
4743
+ );
4744
+ for (const [i, field] of filteredFields.entries()) {
4745
+ queryClient.setQueryData([FETCH_HASH_FIELD_TTLS_QUERY_KEY, dataKey, field], expireAts[i]);
4746
+ }
4747
+ const newExpiresArray = expireAts.map((expireAt, i) => [filteredFields[i], expireAt]);
4748
+ return Object.fromEntries([...cachedExpires.entries(), ...newExpiresArray]);
4749
+ }
4750
+ });
4751
+ };
4752
+
4753
+ // src/components/databrowser/hooks/use-set-hash-ttl.ts
4754
+ import { useMutation as useMutation6 } from "@tanstack/react-query";
4755
+ var useSetHashTTL = () => {
4756
+ const { redis } = useDatabrowser();
4757
+ return useMutation6({
4758
+ mutationFn: async ({
4759
+ dataKey,
4760
+ field,
4761
+ ttl
4762
+ }) => {
4763
+ await (ttl === void 0 || ttl === TTL_INFINITE ? redis.hpersist(dataKey, field) : redis.hexpire(dataKey, field, ttl));
4764
+ },
4765
+ onSuccess: (_, { dataKey }) => {
4766
+ queryClient.removeQueries({
4767
+ queryKey: [FETCH_HASH_FIELD_TTLS_QUERY_KEY, dataKey]
4768
+ });
4769
+ }
4770
+ });
4771
+ };
4772
+
4773
+ // src/components/databrowser/components/display/hash/hash-field-ttl-badge.tsx
4774
+ import { jsx as jsx26 } from "react/jsx-runtime";
4775
+ var HashFieldTTLBadge = ({ dataKey, field }) => {
4776
+ const { data } = useFetchHashFieldExpires({ dataKey, fields: [field] });
4777
+ const { mutate: setTTL, isPending } = useSetHashTTL();
4778
+ const expireAt = data?.[field];
4779
+ return /* @__PURE__ */ jsx26(
4780
+ TTLBadge,
4781
+ {
4782
+ label: "Field TTL:",
4783
+ expireAt,
4784
+ setTTL: (ttl) => setTTL({ dataKey, field, ttl }),
4785
+ isPending
4786
+ }
4787
+ );
4788
+ };
4789
+
4661
4790
  // src/components/databrowser/components/display/input/use-field.tsx
4662
- import { useEffect as useEffect7, useState as useState7 } from "react";
4791
+ import { useEffect as useEffect7, useState as useState8 } from "react";
4663
4792
  import { useController } from "react-hook-form";
4664
4793
 
4665
4794
  // src/components/databrowser/components/display/input/content-type-select.tsx
4666
4795
  import { useMemo as useMemo4 } from "react";
4667
- import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
4796
+ import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
4668
4797
  var ContentTypeSelect = ({
4669
4798
  value,
4670
4799
  onChange,
4671
4800
  data
4672
4801
  }) => {
4673
4802
  const isValidJSON = useMemo4(() => checkIsValidJSON(data), [data]);
4674
- return /* @__PURE__ */ jsxs17(Select, { value, onValueChange: onChange, children: [
4675
- /* @__PURE__ */ jsx25(SelectTrigger, { className: "h-6 w-auto border-none bg-transparent pl-0 pr-6 text-xs text-zinc-500", children: /* @__PURE__ */ jsx25(SelectValue, { placeholder: "Text" }) }),
4676
- /* @__PURE__ */ jsx25(SelectContent, { children: /* @__PURE__ */ jsxs17(SelectGroup, { children: [
4677
- /* @__PURE__ */ jsx25(SelectItem, { value: "Text", children: "Text" }),
4678
- /* @__PURE__ */ jsx25(SelectItem, { disabled: !isValidJSON, value: "JSON", children: "JSON" })
4803
+ return /* @__PURE__ */ jsxs18(Select, { value, onValueChange: onChange, children: [
4804
+ /* @__PURE__ */ jsx27(SelectTrigger, { className: "h-6 w-auto border-none bg-transparent pl-0 pr-6 text-xs text-zinc-500", children: /* @__PURE__ */ jsx27(SelectValue, { placeholder: "Text" }) }),
4805
+ /* @__PURE__ */ jsx27(SelectContent, { children: /* @__PURE__ */ jsxs18(SelectGroup, { children: [
4806
+ /* @__PURE__ */ jsx27(SelectItem, { value: "Text", children: "Text" }),
4807
+ /* @__PURE__ */ jsx27(SelectItem, { disabled: !isValidJSON, value: "JSON", children: "JSON" })
4679
4808
  ] }) })
4680
4809
  ] });
4681
4810
  };
@@ -4685,12 +4814,12 @@ import { useEffect as useEffect6, useRef as useRef3 } from "react";
4685
4814
  import { Editor, useMonaco } from "@monaco-editor/react";
4686
4815
 
4687
4816
  // src/components/databrowser/copy-button.tsx
4688
- import { useState as useState6 } from "react";
4817
+ import { useState as useState7 } from "react";
4689
4818
  import { IconCheck, IconCopy } from "@tabler/icons-react";
4690
- import { jsx as jsx26 } from "react/jsx-runtime";
4819
+ import { jsx as jsx28 } from "react/jsx-runtime";
4691
4820
  function CopyButton({ value, ...props }) {
4692
- const [copied, setCopied] = useState6(false);
4693
- return /* @__PURE__ */ jsx26(
4821
+ const [copied, setCopied] = useState7(false);
4822
+ return /* @__PURE__ */ jsx28(
4694
4823
  Button,
4695
4824
  {
4696
4825
  onClick: (e) => {
@@ -4707,7 +4836,7 @@ function CopyButton({ value, ...props }) {
4707
4836
  variant: "secondary",
4708
4837
  size: "icon-sm",
4709
4838
  ...props,
4710
- children: copied ? /* @__PURE__ */ jsx26(IconCheck, { className: "size-4 text-green-500" }) : /* @__PURE__ */ jsx26(IconCopy, { className: "size-4 text-zinc-500" })
4839
+ children: copied ? /* @__PURE__ */ jsx28(IconCheck, { className: "size-4 text-green-500" }) : /* @__PURE__ */ jsx28(IconCopy, { className: "size-4 text-zinc-500" })
4711
4840
  }
4712
4841
  );
4713
4842
  }
@@ -4720,7 +4849,7 @@ var handleCopyClick = async (textToCopy) => {
4720
4849
  };
4721
4850
 
4722
4851
  // src/components/databrowser/components/display/input/custom-editor.tsx
4723
- import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
4852
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
4724
4853
  var CustomEditor = ({
4725
4854
  language,
4726
4855
  value,
@@ -4737,7 +4866,7 @@ var CustomEditor = ({
4737
4866
  }
4738
4867
  monaco?.editor.setModelLanguage(editorRef.current.getModel(), language);
4739
4868
  }, [monaco, language]);
4740
- return /* @__PURE__ */ jsxs18(
4869
+ return /* @__PURE__ */ jsxs19(
4741
4870
  "div",
4742
4871
  {
4743
4872
  className: cn("group/editor relative", height === void 0 && "h-full p-2"),
@@ -4745,7 +4874,7 @@ var CustomEditor = ({
4745
4874
  height
4746
4875
  },
4747
4876
  children: [
4748
- /* @__PURE__ */ jsx27(
4877
+ /* @__PURE__ */ jsx29(
4749
4878
  Editor,
4750
4879
  {
4751
4880
  loading: void 0,
@@ -4786,7 +4915,7 @@ var CustomEditor = ({
4786
4915
  }
4787
4916
  }
4788
4917
  ),
4789
- showCopyButton && /* @__PURE__ */ jsx27(
4918
+ showCopyButton && /* @__PURE__ */ jsx29(
4790
4919
  CopyButton,
4791
4920
  {
4792
4921
  value,
@@ -4799,7 +4928,7 @@ var CustomEditor = ({
4799
4928
  };
4800
4929
 
4801
4930
  // src/components/databrowser/components/display/input/use-field.tsx
4802
- import { Fragment as Fragment4, jsx as jsx28 } from "react/jsx-runtime";
4931
+ import { Fragment as Fragment4, jsx as jsx30 } from "react/jsx-runtime";
4803
4932
  var useField = ({
4804
4933
  name,
4805
4934
  form,
@@ -4812,16 +4941,19 @@ var useField = ({
4812
4941
  name,
4813
4942
  control: form.control
4814
4943
  });
4815
- const [contentType, setContentType] = useState7(
4944
+ const [contentType, setContentType] = useState8(
4816
4945
  () => checkIsValidJSON(field.value) ? "JSON" : "Text"
4817
4946
  );
4818
4947
  useEffect7(() => {
4819
- if (!checkIsValidJSON(data)) {
4820
- return;
4948
+ if (contentType === "JSON" && checkIsValidJSON(data)) {
4949
+ form.setValue(name, formatJSON(data), {
4950
+ shouldDirty: false
4951
+ });
4952
+ } else {
4953
+ form.setValue(name, data, {
4954
+ shouldDirty: false
4955
+ });
4821
4956
  }
4822
- form.setValue(name, formatJSON(data), {
4823
- shouldDirty: false
4824
- });
4825
4957
  }, [data]);
4826
4958
  const handleTypeChange = (type) => {
4827
4959
  setContentType(type);
@@ -4836,8 +4968,8 @@ var useField = ({
4836
4968
  }
4837
4969
  };
4838
4970
  return {
4839
- selector: /* @__PURE__ */ jsx28(ContentTypeSelect, { value: contentType, onChange: handleTypeChange, data: field.value }),
4840
- editor: /* @__PURE__ */ jsx28(Fragment4, { children: /* @__PURE__ */ jsx28(
4971
+ selector: /* @__PURE__ */ jsx30(ContentTypeSelect, { value: contentType, onChange: handleTypeChange, data: field.value }),
4972
+ editor: /* @__PURE__ */ jsx30(Fragment4, { children: /* @__PURE__ */ jsx30(
4841
4973
  CustomEditor,
4842
4974
  {
4843
4975
  language: contentType === "JSON" ? "json" : "plaintext",
@@ -4861,13 +4993,13 @@ var checkIsValidJSON = (value) => {
4861
4993
  };
4862
4994
 
4863
4995
  // src/components/databrowser/components/display/display-list-edit.tsx
4864
- import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
4996
+ import { jsx as jsx31, jsxs as jsxs20 } from "react/jsx-runtime";
4865
4997
  var ListEditDisplay = ({
4866
4998
  dataKey,
4867
4999
  type,
4868
5000
  item
4869
5001
  }) => {
4870
- return /* @__PURE__ */ jsx29("div", { className: "grow rounded-md bg-zinc-100 p-3", children: /* @__PURE__ */ jsx29(ListEditForm, { item, type, dataKey }, item.key) });
5002
+ return /* @__PURE__ */ jsx31("div", { className: "grow rounded-md bg-zinc-100 p-3", children: /* @__PURE__ */ jsx31(ListEditForm, { item, type, dataKey }, item.key) });
4871
5003
  };
4872
5004
  var ListEditForm = ({
4873
5005
  type,
@@ -4892,12 +5024,6 @@ var ListEditForm = ({
4892
5024
  value: itemValue
4893
5025
  }
4894
5026
  });
4895
- useEffect8(() => {
4896
- form.reset({
4897
- key: itemKey,
4898
- value: itemValue
4899
- });
4900
- }, [itemKey, itemValue]);
4901
5027
  const { mutateAsync: editItem, isPending } = useEditListItem();
4902
5028
  const { setSelectedListItem } = useDatabrowserStore();
4903
5029
  const [keyLabel, valueLabel] = headerLabels[type];
@@ -4912,9 +5038,9 @@ var ListEditForm = ({
4912
5038
  });
4913
5039
  setSelectedListItem(void 0);
4914
5040
  });
4915
- return /* @__PURE__ */ jsx29(FormProvider, { ...form, children: /* @__PURE__ */ jsxs19("form", { onSubmit, className: "flex flex-col gap-2", children: [
4916
- /* @__PURE__ */ jsxs19("div", { className: "flex grow flex-col gap-2", children: [
4917
- type !== "list" && /* @__PURE__ */ jsx29(
5041
+ return /* @__PURE__ */ jsx31(FormProvider, { ...form, children: /* @__PURE__ */ jsxs20("form", { onSubmit, className: "flex flex-col gap-2", children: [
5042
+ /* @__PURE__ */ jsxs20("div", { className: "flex grow flex-col gap-2", children: [
5043
+ type !== "list" && /* @__PURE__ */ jsx31(
4918
5044
  FormItem,
4919
5045
  {
4920
5046
  readOnly: type === "stream",
@@ -4924,7 +5050,7 @@ var ListEditForm = ({
4924
5050
  data: itemKey
4925
5051
  }
4926
5052
  ),
4927
- type === "zset" ? /* @__PURE__ */ jsx29(NumberFormItem, { name: "value", label: valueLabel }) : type !== "set" && /* @__PURE__ */ jsx29(
5053
+ type === "zset" ? /* @__PURE__ */ jsx31(NumberFormItem, { name: "value", label: valueLabel }) : type !== "set" && /* @__PURE__ */ jsx31(
4928
5054
  FormItem,
4929
5055
  {
4930
5056
  readOnly: type === "stream",
@@ -4935,43 +5061,55 @@ var ListEditForm = ({
4935
5061
  }
4936
5062
  )
4937
5063
  ] }),
4938
- /* @__PURE__ */ jsxs19("div", { className: "flex justify-end gap-2", children: [
4939
- /* @__PURE__ */ jsx29(
4940
- Button,
4941
- {
4942
- type: "button",
4943
- onClick: () => {
4944
- setSelectedListItem(void 0);
4945
- },
4946
- children: "Cancel"
4947
- }
4948
- ),
4949
- /* @__PURE__ */ jsx29(
4950
- SimpleTooltip,
4951
- {
4952
- content: type === "stream" && !isNew ? "Streams are not mutable" : void 0,
4953
- children: /* @__PURE__ */ jsx29(
4954
- Button,
4955
- {
4956
- variant: "primary",
4957
- type: "submit",
4958
- disabled: !form.formState.isValid || !form.formState.isDirty || type === "stream" && !isNew,
4959
- children: /* @__PURE__ */ jsx29(Spinner, { isLoading: isPending, isLoadingText: "Saving", children: "Save" })
4960
- }
4961
- )
4962
- }
4963
- )
4964
- ] })
5064
+ /* @__PURE__ */ jsxs20(
5065
+ "div",
5066
+ {
5067
+ className: cn(
5068
+ "flex items-center",
5069
+ type === "hash" && itemKey !== "" ? "justify-between" : "justify-end"
5070
+ ),
5071
+ children: [
5072
+ type === "hash" && itemKey !== "" && /* @__PURE__ */ jsx31(HashFieldTTLBadge, { dataKey, field: itemKey }),
5073
+ /* @__PURE__ */ jsxs20("div", { className: "flex gap-2", children: [
5074
+ /* @__PURE__ */ jsx31(
5075
+ Button,
5076
+ {
5077
+ type: "button",
5078
+ onClick: () => {
5079
+ setSelectedListItem(void 0);
5080
+ },
5081
+ children: "Cancel"
5082
+ }
5083
+ ),
5084
+ /* @__PURE__ */ jsx31(
5085
+ SimpleTooltip,
5086
+ {
5087
+ content: type === "stream" && !isNew ? "Streams are not mutable" : void 0,
5088
+ children: /* @__PURE__ */ jsx31(
5089
+ Button,
5090
+ {
5091
+ variant: "primary",
5092
+ type: "submit",
5093
+ disabled: !form.formState.isValid || !form.formState.isDirty || type === "stream" && !isNew,
5094
+ children: /* @__PURE__ */ jsx31(Spinner, { isLoading: isPending, isLoadingText: "Saving", children: "Save" })
5095
+ }
5096
+ )
5097
+ }
5098
+ )
5099
+ ] })
5100
+ ]
5101
+ }
5102
+ )
4965
5103
  ] }) });
4966
5104
  };
4967
5105
  var NumberFormItem = ({ name, label }) => {
4968
- return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-1", children: [
4969
- /* @__PURE__ */ jsx29("div", { className: "flex", children: /* @__PURE__ */ jsx29("span", { className: "text-xs font-medium text-zinc-700", children: label }) }),
4970
- /* @__PURE__ */ jsx29(
5106
+ return /* @__PURE__ */ jsxs20("div", { className: "flex flex-col gap-1", children: [
5107
+ /* @__PURE__ */ jsx31("div", { className: "flex", children: /* @__PURE__ */ jsx31("span", { className: "text-xs font-medium text-zinc-700", children: label }) }),
5108
+ /* @__PURE__ */ jsx31(
4971
5109
  Controller2,
4972
5110
  {
4973
5111
  name,
4974
- render: ({ field }) => /* @__PURE__ */ jsx29(
5112
+ render: ({ field }) => /* @__PURE__ */ jsx31(
4975
5113
  "input",
4976
5114
  {
4977
5115
  className: "plain-input rounded-md border border-zinc-300 px-3 py-1 shadow-sm",
@@ -4999,19 +5137,41 @@ var FormItem = ({
4999
5137
  readOnly,
5000
5138
  data
5001
5139
  });
5002
- return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-1", children: [
5003
- /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1 text-xs", children: [
5004
- /* @__PURE__ */ jsx29("span", { className: "font-medium text-zinc-700", children: label }),
5140
+ return /* @__PURE__ */ jsxs20("div", { className: "flex flex-col gap-1", children: [
5141
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-1 text-xs", children: [
5142
+ /* @__PURE__ */ jsx31("span", { className: "font-medium text-zinc-700", children: label }),
5005
5143
  " ",
5006
- /* @__PURE__ */ jsx29("span", { className: "text-zinc-300", children: "/" }),
5144
+ /* @__PURE__ */ jsx31("span", { className: "text-zinc-300", children: "/" }),
5007
5145
  selector
5008
5146
  ] }),
5009
- /* @__PURE__ */ jsx29("div", { className: "overflow-hidden rounded-md border border-zinc-300 bg-white p-2 shadow-sm", children: editor })
5147
+ /* @__PURE__ */ jsx31("div", { className: "overflow-hidden rounded-md border border-zinc-300 bg-white p-2 shadow-sm", children: editor })
5010
5148
  ] });
5011
5149
  };
5012
5150
 
5151
+ // src/components/databrowser/components/display/hash/hash-field-ttl-info.tsx
5152
+ import { useEffect as useEffect8, useState as useState9 } from "react";
5153
+ import { jsx as jsx32 } from "react/jsx-runtime";
5154
+ var HashFieldTTLInfo = ({
5155
+ dataKey,
5156
+ field,
5157
+ fields
5158
+ }) => {
5159
+ const { data } = useFetchHashFieldExpires({ dataKey, fields });
5160
+ const expireAt = data?.[field];
5161
+ const [ttl, setTTL] = useState9(() => calculateTTL(expireAt));
5162
+ useEffect8(() => {
5163
+ setTTL(calculateTTL(expireAt));
5164
+ const interval = setInterval(() => {
5165
+ setTTL(calculateTTL(expireAt));
5166
+ }, 1e3);
5167
+ return () => clearInterval(interval);
5168
+ }, [expireAt]);
5169
+ if (!expireAt || expireAt === TTL_NOT_FOUND || expireAt === TTL_INFINITE) return;
5170
+ return /* @__PURE__ */ jsx32("span", { className: "block min-w-[30px] whitespace-nowrap text-right text-red-600", children: formatTime(ttl ?? 0) });
5171
+ };
5172
+
5013
5173
  // src/components/databrowser/components/display/display-list.tsx
5014
- import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
5174
+ import { Fragment as Fragment5, jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
5015
5175
  var headerLabels = {
5016
5176
  list: ["Index", "Content"],
5017
5177
  hash: ["Field", "Value"],
@@ -5022,10 +5182,10 @@ var headerLabels = {
5022
5182
  var ListDisplay = ({ dataKey, type }) => {
5023
5183
  const { selectedListItem } = useDatabrowserStore();
5024
5184
  const query = useFetchListItems({ dataKey, type });
5025
- return /* @__PURE__ */ jsxs20("div", { className: "flex h-full flex-col gap-2", children: [
5026
- /* @__PURE__ */ jsx30(DisplayHeader, { dataKey, type }),
5027
- selectedListItem && /* @__PURE__ */ jsx30(ListEditDisplay, { dataKey, type, item: selectedListItem }),
5028
- /* @__PURE__ */ jsx30("div", { className: cn("min-h-0 grow", selectedListItem && "hidden"), children: /* @__PURE__ */ jsx30(InfiniteScroll, { query, children: /* @__PURE__ */ jsx30("div", { className: "pr-3", children: /* @__PURE__ */ jsx30("table", { className: "w-full", children: /* @__PURE__ */ jsx30(ItemContextMenu, { dataKey, type, children: /* @__PURE__ */ jsx30("tbody", { children: /* @__PURE__ */ jsx30(ListItems, { dataKey, type, query }) }) }) }) }) }) })
5185
+ return /* @__PURE__ */ jsxs21("div", { className: "flex h-full flex-col gap-2", children: [
5186
+ /* @__PURE__ */ jsx33(DisplayHeader, { dataKey, type }),
5187
+ selectedListItem && /* @__PURE__ */ jsx33(ListEditDisplay, { dataKey, type, item: selectedListItem }),
5188
+ /* @__PURE__ */ jsx33("div", { className: cn("min-h-0 grow", selectedListItem && "hidden"), children: /* @__PURE__ */ jsx33(InfiniteScroll, { query, children: /* @__PURE__ */ jsx33("div", { className: "pr-3", children: /* @__PURE__ */ jsx33("table", { className: "w-full", children: /* @__PURE__ */ jsx33(ItemContextMenu, { dataKey, type, children: /* @__PURE__ */ jsx33("tbody", { children: /* @__PURE__ */ jsx33(ListItems, { dataKey, type, query }) }) }) }) }) }) })
5029
5189
  ] });
5030
5190
  };
5031
5191
  var ListItems = ({
@@ -5035,8 +5195,9 @@ var ListItems = ({
5035
5195
  }) => {
5036
5196
  const { setSelectedListItem } = useDatabrowserStore();
5037
5197
  const keys = useMemo5(() => query.data?.pages.flatMap((page) => page.keys) ?? [], [query.data]);
5198
+ const fields = useMemo5(() => keys.map((key) => key.key), [keys]);
5038
5199
  const { mutate: editItem } = useEditListItem();
5039
- return /* @__PURE__ */ jsx30(Fragment5, { children: keys.map(({ key, value }, i) => /* @__PURE__ */ jsxs20(
5200
+ return /* @__PURE__ */ jsx33(Fragment5, { children: keys.map(({ key, value }, i) => /* @__PURE__ */ jsxs21(
5040
5201
  "tr",
5041
5202
  {
5042
5203
  "data-item-key": key,
@@ -5044,9 +5205,9 @@ var ListItems = ({
5044
5205
  onClick: () => {
5045
5206
  setSelectedListItem({ key });
5046
5207
  },
5047
- className: "h-10 border-b border-b-zinc-100 hover:bg-zinc-50",
5208
+ className: cn("h-10 border-b border-b-zinc-100 hover:bg-zinc-100 "),
5048
5209
  children: [
5049
- /* @__PURE__ */ jsx30(
5210
+ /* @__PURE__ */ jsx33(
5050
5211
  "td",
5051
5212
  {
5052
5213
  className: cn(
@@ -5056,38 +5217,49 @@ var ListItems = ({
5056
5217
  children: key
5057
5218
  }
5058
5219
  ),
5059
- value !== void 0 && /* @__PURE__ */ jsx30(
5220
+ value !== void 0 && /* @__PURE__ */ jsx33(
5060
5221
  "td",
5061
5222
  {
5062
5223
  className: cn("cursor-pointer truncate px-3", type === "zset" ? "w-24" : "max-w-0"),
5063
5224
  children: value
5064
5225
  }
5065
5226
  ),
5066
- type !== "stream" && /* @__PURE__ */ jsx30(
5227
+ type !== "stream" && /* @__PURE__ */ jsx33(
5067
5228
  "td",
5068
5229
  {
5069
- width: 20,
5070
- className: "px-3",
5230
+ className: "w-0 min-w-0 p-0",
5071
5231
  onClick: (e) => {
5072
5232
  e.stopPropagation();
5073
5233
  },
5074
- children: /* @__PURE__ */ jsx30(
5075
- DeleteAlertDialog,
5076
- {
5077
- deletionType: "item",
5078
- onDeleteConfirm: (e) => {
5079
- e.stopPropagation();
5080
- editItem({
5081
- type,
5082
- dataKey,
5083
- itemKey: key,
5084
- // For deletion
5085
- newKey: void 0
5086
- });
5087
- },
5088
- children: /* @__PURE__ */ jsx30(Button, { size: "icon-sm", variant: "secondary", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx30(IconTrash, { className: "size-4 text-zinc-500" }) })
5089
- }
5090
- )
5234
+ children: /* @__PURE__ */ jsxs21("div", { className: "flex items-center justify-end gap-2", children: [
5235
+ type === "hash" && /* @__PURE__ */ jsx33(HashFieldTTLInfo, { dataKey, field: key, fields }),
5236
+ /* @__PURE__ */ jsx33(
5237
+ DeleteAlertDialog,
5238
+ {
5239
+ deletionType: "item",
5240
+ onDeleteConfirm: (e) => {
5241
+ e.stopPropagation();
5242
+ editItem({
5243
+ type,
5244
+ dataKey,
5245
+ itemKey: key,
5246
+ // For deletion
5247
+ newKey: void 0
5248
+ });
5249
+ },
5250
+ children: /* @__PURE__ */ jsx33(
5251
+ Button,
5252
+ {
5253
+ className: "",
5254
+ size: "icon-sm",
5255
+ variant: "secondary",
5256
+ onClick: (e) => e.stopPropagation(),
5257
+ children: /* @__PURE__ */ jsx33(IconTrash, { className: "size-4 text-zinc-500" })
5258
+ }
5259
+ )
5260
+ }
5261
+ )
5262
+ ] })
5091
5263
  }
5092
5264
  )
5093
5265
  ]
@@ -5099,16 +5271,16 @@ var ListItems = ({
5099
5271
  // src/components/databrowser/components/display/display-simple.tsx
5100
5272
  import { useEffect as useEffect9 } from "react";
5101
5273
  import { useForm as useForm3 } from "react-hook-form";
5102
- import { Fragment as Fragment6, jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
5274
+ import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs22 } from "react/jsx-runtime";
5103
5275
  var EditorDisplay = ({ dataKey, type }) => {
5104
5276
  const { data } = useFetchSimpleKey(dataKey, type);
5105
- return /* @__PURE__ */ jsxs21("div", { className: "flex h-full w-full flex-col gap-2", children: [
5106
- /* @__PURE__ */ jsx31(DisplayHeader, { dataKey, type, content: data ?? void 0 }),
5107
- /* @__PURE__ */ jsx31(
5277
+ return /* @__PURE__ */ jsxs22("div", { className: "flex h-full w-full flex-col gap-2", children: [
5278
+ /* @__PURE__ */ jsx34(DisplayHeader, { dataKey, type, content: data ?? void 0 }),
5279
+ /* @__PURE__ */ jsx34(
5108
5280
  "div",
5109
5281
  {
5110
5282
  className: "flex h-full grow flex-col gap-2\n rounded-md bg-zinc-100 p-3",
5111
- children: data === void 0 ? /* @__PURE__ */ jsx31(Spinner, { isLoadingText: "", isLoading: true }) : data === null ? /* @__PURE__ */ jsx31(Fragment6, {}) : /* @__PURE__ */ jsx31(EditorDisplayForm, { dataKey, type, data }, dataKey)
5283
+ children: data === void 0 ? /* @__PURE__ */ jsx34(Spinner, { isLoadingText: "", isLoading: true }) : data === null ? /* @__PURE__ */ jsx34(Fragment6, {}) : /* @__PURE__ */ jsx34(EditorDisplayForm, { dataKey, type, data }, dataKey)
5112
5284
  }
5113
5285
  )
5114
5286
  ] });
@@ -5122,26 +5294,21 @@ var EditorDisplayForm = ({
5122
5294
  defaultValues: { value: data }
5123
5295
  });
5124
5296
  useEffect9(() => {
5125
- form.reset(
5126
- { value: data },
5127
- {
5128
- keepValues: false
5129
- }
5130
- );
5297
+ form.reset({ value: data });
5131
5298
  }, [data]);
5132
5299
  const { editor, selector } = useField({ name: "value", form, data });
5133
5300
  const { mutateAsync: setKey, isPending: isSettingKey } = useSetSimpleKey(dataKey, type);
5134
5301
  const handleCancel = () => {
5135
- form.reset();
5302
+ form.reset({ value: data });
5136
5303
  };
5137
- return /* @__PURE__ */ jsxs21(Fragment6, { children: [
5138
- /* @__PURE__ */ jsxs21("div", { className: "flex grow flex-col gap-1", children: [
5139
- /* @__PURE__ */ jsx31("div", { className: "flex shrink-0 items-center gap-2", children: type === "json" ? /* @__PURE__ */ jsx31("div", {}) : selector }),
5140
- /* @__PURE__ */ jsx31("div", { className: "grow rounded-md border border-zinc-300 bg-white p-1", children: editor })
5304
+ return /* @__PURE__ */ jsxs22(Fragment6, { children: [
5305
+ /* @__PURE__ */ jsxs22("div", { className: "flex grow flex-col gap-1", children: [
5306
+ /* @__PURE__ */ jsx34("div", { className: "flex shrink-0 items-center gap-2", children: type === "json" ? /* @__PURE__ */ jsx34("div", {}) : selector }),
5307
+ /* @__PURE__ */ jsx34("div", { className: "grow rounded-md border border-zinc-300 bg-white p-1", children: editor })
5141
5308
  ] }),
5142
- /* @__PURE__ */ jsx31("div", { className: "flex shrink-0 items-center gap-2", children: /* @__PURE__ */ jsxs21("div", { className: "ml-auto flex gap-2", children: [
5143
- form.formState.isDirty && /* @__PURE__ */ jsx31(Button, { onClick: handleCancel, children: "Cancel" }),
5144
- /* @__PURE__ */ jsx31(
5309
+ /* @__PURE__ */ jsx34("div", { className: "flex shrink-0 items-center gap-2", children: /* @__PURE__ */ jsxs22("div", { className: "ml-auto flex gap-2", children: [
5310
+ form.formState.isDirty && /* @__PURE__ */ jsx34(Button, { onClick: handleCancel, children: "Cancel" }),
5311
+ /* @__PURE__ */ jsx34(
5145
5312
  Button,
5146
5313
  {
5147
5314
  variant: "primary",
@@ -5149,7 +5316,7 @@ var EditorDisplayForm = ({
5149
5316
  await setKey(value);
5150
5317
  }),
5151
5318
  disabled: !form.formState.isValid || !form.formState.isDirty,
5152
- children: /* @__PURE__ */ jsx31(Spinner, { isLoading: isSettingKey, isLoadingText: "Saving", children: "Save" })
5319
+ children: /* @__PURE__ */ jsx34(Spinner, { isLoading: isSettingKey, isLoadingText: "Saving", children: "Save" })
5153
5320
  }
5154
5321
  )
5155
5322
  ] }) })
@@ -5157,19 +5324,19 @@ var EditorDisplayForm = ({
5157
5324
  };
5158
5325
 
5159
5326
  // src/components/databrowser/components/display/index.tsx
5160
- import { Fragment as Fragment7, jsx as jsx32 } from "react/jsx-runtime";
5327
+ import { Fragment as Fragment7, jsx as jsx35 } from "react/jsx-runtime";
5161
5328
  var DataDisplay = () => {
5162
5329
  const { selectedKey } = useDatabrowserStore();
5163
5330
  const { query } = useKeys();
5164
5331
  const type = useKeyType(selectedKey);
5165
- return /* @__PURE__ */ jsx32("div", { className: "h-full rounded-xl border bg-white p-1", children: !selectedKey ? /* @__PURE__ */ jsx32("div", {}) : !type ? query.isLoading ? /* @__PURE__ */ jsx32("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx32("span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ jsx32("div", {}) : /* @__PURE__ */ jsx32(Fragment7, { children: type === "string" || type === "json" ? /* @__PURE__ */ jsx32(EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ jsx32(ListDisplay, { dataKey: selectedKey, type }) }) });
5332
+ return /* @__PURE__ */ jsx35("div", { className: "h-full rounded-xl border bg-white p-1", children: !selectedKey ? /* @__PURE__ */ jsx35("div", {}) : !type ? query.isLoading ? /* @__PURE__ */ jsx35("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx35("span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ jsx35("div", {}) : /* @__PURE__ */ jsx35(Fragment7, { children: type === "string" || type === "json" ? /* @__PURE__ */ jsx35(EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ jsx35(ListDisplay, { dataKey: selectedKey, type }) }) });
5166
5333
  };
5167
5334
 
5168
5335
  // src/components/databrowser/components/sidebar/index.tsx
5169
5336
  import { IconRefresh } from "@tabler/icons-react";
5170
5337
 
5171
5338
  // src/components/databrowser/components/add-key-modal.tsx
5172
- import { useState as useState8 } from "react";
5339
+ import { useState as useState10 } from "react";
5173
5340
  import { DialogDescription as DialogDescription2 } from "@radix-ui/react-dialog";
5174
5341
  import { PlusIcon } from "@radix-ui/react-icons";
5175
5342
  import { Controller as Controller3, useForm as useForm4 } from "react-hook-form";
@@ -5177,12 +5344,12 @@ import { Controller as Controller3, useForm as useForm4 } from "react-hook-form"
5177
5344
  // src/components/ui/dialog.tsx
5178
5345
  import * as React12 from "react";
5179
5346
  import * as DialogPrimitive from "@radix-ui/react-dialog";
5180
- import { jsx as jsx33, jsxs as jsxs22 } from "react/jsx-runtime";
5347
+ import { jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
5181
5348
  var Dialog = DialogPrimitive.Root;
5182
5349
  var DialogTrigger = DialogPrimitive.Trigger;
5183
- var DialogPortal = (props) => /* @__PURE__ */ jsx33(DialogPrimitive.Portal, { container: portalRoot, ...props });
5350
+ var DialogPortal = (props) => /* @__PURE__ */ jsx36(DialogPrimitive.Portal, { container: portalRoot, ...props });
5184
5351
  DialogPortal.displayName = DialogPrimitive.Portal.displayName;
5185
- var DialogOverlay = React12.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx33(
5352
+ var DialogOverlay = React12.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx36(
5186
5353
  DialogPrimitive.Overlay,
5187
5354
  {
5188
5355
  ref,
@@ -5196,9 +5363,9 @@ var DialogOverlay = React12.forwardRef(({ className, ...props }, ref) => /* @__P
5196
5363
  }
5197
5364
  ));
5198
5365
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
5199
- var DialogContent = React12.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs22(DialogPortal, { children: [
5200
- /* @__PURE__ */ jsx33(DialogOverlay, {}),
5201
- /* @__PURE__ */ jsxs22(
5366
+ var DialogContent = React12.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs23(DialogPortal, { children: [
5367
+ /* @__PURE__ */ jsx36(DialogOverlay, {}),
5368
+ /* @__PURE__ */ jsxs23(
5202
5369
  DialogPrimitive.Content,
5203
5370
  {
5204
5371
  ref,
@@ -5218,8 +5385,8 @@ var DialogContent = React12.forwardRef(({ className, children, ...props }, ref)
5218
5385
  ...props,
5219
5386
  children: [
5220
5387
  children,
5221
- /* @__PURE__ */ jsxs22(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-zinc-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-zinc-100 data-[state=open]:text-zinc-500 dark:ring-offset-zinc-950 dark:focus:ring-zinc-300 dark:data-[state=open]:bg-zinc-800 dark:data-[state=open]:text-zinc-400", children: [
5222
- /* @__PURE__ */ jsx33(
5388
+ /* @__PURE__ */ jsxs23(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-zinc-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-zinc-100 data-[state=open]:text-zinc-500 dark:ring-offset-zinc-950 dark:focus:ring-zinc-300 dark:data-[state=open]:bg-zinc-800 dark:data-[state=open]:text-zinc-400", children: [
5389
+ /* @__PURE__ */ jsx36(
5223
5390
  "svg",
5224
5391
  {
5225
5392
  width: "15",
@@ -5228,7 +5395,7 @@ var DialogContent = React12.forwardRef(({ className, children, ...props }, ref)
5228
5395
  fill: "none",
5229
5396
  xmlns: "http://www.w3.org/2000/svg",
5230
5397
  className: "h-4 w-4",
5231
- children: /* @__PURE__ */ jsx33(
5398
+ children: /* @__PURE__ */ jsx36(
5232
5399
  "path",
5233
5400
  {
5234
5401
  d: "M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z",
@@ -5239,16 +5406,16 @@ var DialogContent = React12.forwardRef(({ className, children, ...props }, ref)
5239
5406
  )
5240
5407
  }
5241
5408
  ),
5242
- /* @__PURE__ */ jsx33("span", { className: "sr-only", children: "Close" })
5409
+ /* @__PURE__ */ jsx36("span", { className: "sr-only", children: "Close" })
5243
5410
  ] })
5244
5411
  ]
5245
5412
  }
5246
5413
  )
5247
5414
  ] }));
5248
5415
  DialogContent.displayName = DialogPrimitive.Content.displayName;
5249
- var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx33("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
5416
+ var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx36("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
5250
5417
  DialogHeader.displayName = "DialogHeader";
5251
- var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx33(
5418
+ var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx36(
5252
5419
  "div",
5253
5420
  {
5254
5421
  className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
@@ -5256,7 +5423,7 @@ var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx33(
5256
5423
  }
5257
5424
  );
5258
5425
  DialogFooter.displayName = "DialogFooter";
5259
- var DialogTitle = React12.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx33(
5426
+ var DialogTitle = React12.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx36(
5260
5427
  DialogPrimitive.Title,
5261
5428
  {
5262
5429
  ref,
@@ -5265,7 +5432,7 @@ var DialogTitle = React12.forwardRef(({ className, ...props }, ref) => /* @__PUR
5265
5432
  }
5266
5433
  ));
5267
5434
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
5268
- var DialogDescription = React12.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx33(
5435
+ var DialogDescription = React12.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx36(
5269
5436
  DialogPrimitive.Description,
5270
5437
  {
5271
5438
  ref,
@@ -5276,10 +5443,10 @@ var DialogDescription = React12.forwardRef(({ className, ...props }, ref) => /*
5276
5443
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
5277
5444
 
5278
5445
  // src/components/databrowser/components/add-key-modal.tsx
5279
- import { jsx as jsx34, jsxs as jsxs23 } from "react/jsx-runtime";
5446
+ import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
5280
5447
  function AddKeyModal() {
5281
5448
  const { setSelectedKey } = useDatabrowserStore();
5282
- const [open, setOpen] = useState8(false);
5449
+ const [open, setOpen] = useState10(false);
5283
5450
  const { mutateAsync: addKey, isPending } = useAddKey();
5284
5451
  const { control, handleSubmit, formState, reset } = useForm4({
5285
5452
  defaultValues: {
@@ -5299,7 +5466,7 @@ function AddKeyModal() {
5299
5466
  });
5300
5467
  }, 100);
5301
5468
  });
5302
- return /* @__PURE__ */ jsxs23(
5469
+ return /* @__PURE__ */ jsxs24(
5303
5470
  Dialog,
5304
5471
  {
5305
5472
  open,
@@ -5308,24 +5475,24 @@ function AddKeyModal() {
5308
5475
  setOpen(open2);
5309
5476
  },
5310
5477
  children: [
5311
- /* @__PURE__ */ jsx34(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx34(Button, { variant: "primary", size: "icon-sm", children: /* @__PURE__ */ jsx34(PlusIcon, { className: "size-4" }) }) }),
5312
- /* @__PURE__ */ jsxs23(DialogContent, { className: "max-w-[400px]", children: [
5313
- /* @__PURE__ */ jsx34(DialogHeader, { children: /* @__PURE__ */ jsx34(DialogTitle, { children: "Create new key" }) }),
5314
- /* @__PURE__ */ jsx34("div", { className: "sr-only", children: /* @__PURE__ */ jsx34(DialogDescription2, { children: "Create new key" }) }),
5315
- /* @__PURE__ */ jsxs23("form", { className: "mt-4", onSubmit, children: [
5316
- /* @__PURE__ */ jsxs23("div", { className: "flex gap-1", children: [
5317
- /* @__PURE__ */ jsx34(
5478
+ /* @__PURE__ */ jsx37(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx37(Button, { variant: "primary", size: "icon-sm", children: /* @__PURE__ */ jsx37(PlusIcon, { className: "size-4" }) }) }),
5479
+ /* @__PURE__ */ jsxs24(DialogContent, { className: "max-w-[400px]", children: [
5480
+ /* @__PURE__ */ jsx37(DialogHeader, { children: /* @__PURE__ */ jsx37(DialogTitle, { children: "Create new key" }) }),
5481
+ /* @__PURE__ */ jsx37("div", { className: "sr-only", children: /* @__PURE__ */ jsx37(DialogDescription2, { children: "Create new key" }) }),
5482
+ /* @__PURE__ */ jsxs24("form", { className: "mt-4", onSubmit, children: [
5483
+ /* @__PURE__ */ jsxs24("div", { className: "flex gap-1", children: [
5484
+ /* @__PURE__ */ jsx37(
5318
5485
  Controller3,
5319
5486
  {
5320
5487
  control,
5321
5488
  name: "type",
5322
- render: ({ field }) => /* @__PURE__ */ jsxs23(Select, { value: field.value, onValueChange: field.onChange, children: [
5323
- /* @__PURE__ */ jsx34(SelectTrigger, { className: "h-8 w-auto pl-[3px] pr-8", children: /* @__PURE__ */ jsx34(SelectValue, {}) }),
5324
- /* @__PURE__ */ jsx34(SelectContent, { children: /* @__PURE__ */ jsx34(SelectGroup, { children: DATA_TYPES.map((type) => /* @__PURE__ */ jsx34(SelectItem, { value: type, children: /* @__PURE__ */ jsx34(TypeTag, { variant: type, type: "badge" }) }, type)) }) })
5489
+ render: ({ field }) => /* @__PURE__ */ jsxs24(Select, { value: field.value, onValueChange: field.onChange, children: [
5490
+ /* @__PURE__ */ jsx37(SelectTrigger, { className: "h-8 w-auto pl-[3px] pr-8", children: /* @__PURE__ */ jsx37(SelectValue, {}) }),
5491
+ /* @__PURE__ */ jsx37(SelectContent, { children: /* @__PURE__ */ jsx37(SelectGroup, { children: DATA_TYPES.map((type) => /* @__PURE__ */ jsx37(SelectItem, { value: type, children: /* @__PURE__ */ jsx37(TypeTag, { variant: type, type: "badge" }) }, type)) }) })
5325
5492
  ] })
5326
5493
  }
5327
5494
  ),
5328
- /* @__PURE__ */ jsx34(
5495
+ /* @__PURE__ */ jsx37(
5329
5496
  Controller3,
5330
5497
  {
5331
5498
  rules: {
@@ -5333,14 +5500,14 @@ function AddKeyModal() {
5333
5500
  },
5334
5501
  control,
5335
5502
  name: "key",
5336
- render: ({ field }) => /* @__PURE__ */ jsx34(Input, { placeholder: "mykey", ...field, className: "h-8 grow" })
5503
+ render: ({ field }) => /* @__PURE__ */ jsx37(Input, { placeholder: "mykey", ...field, className: "h-8 grow" })
5337
5504
  }
5338
5505
  )
5339
5506
  ] }),
5340
- formState.errors.key && /* @__PURE__ */ jsx34("p", { className: "mb-3 mt-2 text-xs text-red-500", children: formState.errors.key?.message }),
5341
- /* @__PURE__ */ jsx34("p", { className: "mt-2 text-xs text-zinc-500", children: "After creating the key, you can edit the value" }),
5342
- /* @__PURE__ */ jsxs23("div", { className: "mt-6 flex justify-end gap-2", children: [
5343
- /* @__PURE__ */ jsx34(
5507
+ formState.errors.key && /* @__PURE__ */ jsx37("p", { className: "mb-3 mt-2 text-xs text-red-500", children: formState.errors.key?.message }),
5508
+ /* @__PURE__ */ jsx37("p", { className: "mt-2 text-xs text-zinc-500", children: "After creating the key, you can edit the value" }),
5509
+ /* @__PURE__ */ jsxs24("div", { className: "mt-6 flex justify-end gap-2", children: [
5510
+ /* @__PURE__ */ jsx37(
5344
5511
  Button,
5345
5512
  {
5346
5513
  type: "button",
@@ -5351,7 +5518,7 @@ function AddKeyModal() {
5351
5518
  children: "Cancel"
5352
5519
  }
5353
5520
  ),
5354
- /* @__PURE__ */ jsx34(Button, { variant: "primary", type: "submit", children: /* @__PURE__ */ jsx34(Spinner, { isLoading: isPending, isLoadingText: "Creating", children: "Create" }) })
5521
+ /* @__PURE__ */ jsx37(Button, { variant: "primary", type: "submit", children: /* @__PURE__ */ jsx37(Spinner, { isLoading: isPending, isLoadingText: "Creating", children: "Create" }) })
5355
5522
  ] })
5356
5523
  ] })
5357
5524
  ] })
@@ -5361,24 +5528,24 @@ function AddKeyModal() {
5361
5528
  }
5362
5529
 
5363
5530
  // src/components/databrowser/components/sidebar/empty.tsx
5364
- import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
5531
+ import { jsx as jsx38, jsxs as jsxs25 } from "react/jsx-runtime";
5365
5532
  var Empty = () => {
5366
- return /* @__PURE__ */ jsx35("div", { className: "flex h-full w-full items-center justify-center rounded-md border border-dashed px-4 py-6 text-center", children: /* @__PURE__ */ jsxs24("div", { className: "space-y-5", children: [
5367
- /* @__PURE__ */ jsx35("p", { className: "text-md font-medium", children: "Data on a break" }),
5368
- /* @__PURE__ */ jsx35("p", { className: "text-balance text-center", children: '"Quick, lure it back with some CLI magic!"' })
5533
+ return /* @__PURE__ */ jsx38("div", { className: "flex h-full w-full items-center justify-center rounded-md border border-dashed px-4 py-6 text-center", children: /* @__PURE__ */ jsxs25("div", { className: "space-y-5", children: [
5534
+ /* @__PURE__ */ jsx38("p", { className: "text-md font-medium", children: "Data on a break" }),
5535
+ /* @__PURE__ */ jsx38("p", { className: "text-balance text-center", children: '"Quick, lure it back with some CLI magic!"' })
5369
5536
  ] }) });
5370
5537
  };
5371
5538
 
5372
5539
  // src/components/databrowser/components/sidebar-context-menu.tsx
5373
- import { useState as useState9 } from "react";
5540
+ import { useState as useState11 } from "react";
5374
5541
  import { ContextMenuSeparator as ContextMenuSeparator3 } from "@radix-ui/react-context-menu";
5375
- import { Fragment as Fragment8, jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
5542
+ import { Fragment as Fragment8, jsx as jsx39, jsxs as jsxs26 } from "react/jsx-runtime";
5376
5543
  var SidebarContextMenu = ({ children }) => {
5377
5544
  const { mutate: deleteKey } = useDeleteKey();
5378
- const [isAlertOpen, setAlertOpen] = useState9(false);
5379
- const [dataKey, setDataKey] = useState9("");
5380
- return /* @__PURE__ */ jsxs25(Fragment8, { children: [
5381
- /* @__PURE__ */ jsx36(
5545
+ const [isAlertOpen, setAlertOpen] = useState11(false);
5546
+ const [dataKey, setDataKey] = useState11("");
5547
+ return /* @__PURE__ */ jsxs26(Fragment8, { children: [
5548
+ /* @__PURE__ */ jsx39(
5382
5549
  DeleteAlertDialog,
5383
5550
  {
5384
5551
  deletionType: "key",
@@ -5391,8 +5558,8 @@ var SidebarContextMenu = ({ children }) => {
5391
5558
  }
5392
5559
  }
5393
5560
  ),
5394
- /* @__PURE__ */ jsxs25(ContextMenu, { children: [
5395
- /* @__PURE__ */ jsx36(
5561
+ /* @__PURE__ */ jsxs26(ContextMenu, { children: [
5562
+ /* @__PURE__ */ jsx39(
5396
5563
  ContextMenuTrigger,
5397
5564
  {
5398
5565
  onContextMenu: (e) => {
@@ -5407,8 +5574,8 @@ var SidebarContextMenu = ({ children }) => {
5407
5574
  children
5408
5575
  }
5409
5576
  ),
5410
- /* @__PURE__ */ jsxs25(ContextMenuContent, { children: [
5411
- /* @__PURE__ */ jsx36(
5577
+ /* @__PURE__ */ jsxs26(ContextMenuContent, { children: [
5578
+ /* @__PURE__ */ jsx39(
5412
5579
  ContextMenuItem,
5413
5580
  {
5414
5581
  onClick: () => {
@@ -5420,18 +5587,18 @@ var SidebarContextMenu = ({ children }) => {
5420
5587
  children: "Copy key"
5421
5588
  }
5422
5589
  ),
5423
- /* @__PURE__ */ jsx36(ContextMenuSeparator3, {}),
5424
- /* @__PURE__ */ jsx36(ContextMenuItem, { onClick: () => setAlertOpen(true), children: "Delete key" })
5590
+ /* @__PURE__ */ jsx39(ContextMenuSeparator3, {}),
5591
+ /* @__PURE__ */ jsx39(ContextMenuItem, { onClick: () => setAlertOpen(true), children: "Delete key" })
5425
5592
  ] })
5426
5593
  ] })
5427
5594
  ] });
5428
5595
  };
5429
5596
 
5430
5597
  // src/components/databrowser/components/sidebar/keys-list.tsx
5431
- import { Fragment as Fragment9, jsx as jsx37, jsxs as jsxs26 } from "react/jsx-runtime";
5598
+ import { Fragment as Fragment9, jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
5432
5599
  var KeysList = () => {
5433
5600
  const { keys } = useKeys();
5434
- return /* @__PURE__ */ jsx37("div", { className: "pr-3", children: /* @__PURE__ */ jsx37(SidebarContextMenu, { children: /* @__PURE__ */ jsx37(Fragment9, { children: keys.map((data, i) => /* @__PURE__ */ jsx37(KeyItem, { nextKey: keys.at(i + 1)?.[0] ?? "", data }, data[0])) }) }) });
5601
+ return /* @__PURE__ */ jsx40("div", { className: "pr-3", children: /* @__PURE__ */ jsx40(SidebarContextMenu, { children: /* @__PURE__ */ jsx40(Fragment9, { children: keys.map((data, i) => /* @__PURE__ */ jsx40(KeyItem, { nextKey: keys.at(i + 1)?.[0] ?? "", data }, data[0])) }) }) });
5435
5602
  };
5436
5603
  var keyStyles = {
5437
5604
  string: "border-sky-400 !bg-sky-50 text-sky-900",
@@ -5447,7 +5614,7 @@ var KeyItem = ({ data, nextKey }) => {
5447
5614
  const [dataKey, dataType] = data;
5448
5615
  const isKeySelected = selectedKey === dataKey;
5449
5616
  const isNextKeySelected = selectedKey === nextKey;
5450
- return /* @__PURE__ */ jsxs26(
5617
+ return /* @__PURE__ */ jsxs27(
5451
5618
  Button,
5452
5619
  {
5453
5620
  "data-key": dataKey,
@@ -5460,28 +5627,28 @@ var KeyItem = ({ data, nextKey }) => {
5460
5627
  ),
5461
5628
  onClick: () => setSelectedKey(dataKey),
5462
5629
  children: [
5463
- /* @__PURE__ */ jsx37(TypeTag, { variant: dataType, type: "icon" }),
5464
- /* @__PURE__ */ jsx37("p", { className: "truncate whitespace-nowrap", children: dataKey }),
5465
- !isKeySelected && !isNextKeySelected && /* @__PURE__ */ jsx37("span", { className: "absolute -bottom-px left-3 right-3 h-px bg-zinc-100" })
5630
+ /* @__PURE__ */ jsx40(TypeTag, { variant: dataType, type: "icon" }),
5631
+ /* @__PURE__ */ jsx40("p", { className: "truncate whitespace-nowrap", children: dataKey }),
5632
+ !isKeySelected && !isNextKeySelected && /* @__PURE__ */ jsx40("span", { className: "absolute -bottom-px left-3 right-3 h-px bg-zinc-100" })
5466
5633
  ]
5467
5634
  }
5468
5635
  );
5469
5636
  };
5470
5637
 
5471
5638
  // src/components/databrowser/components/sidebar/search-input.tsx
5472
- import { useState as useState10 } from "react";
5639
+ import { useState as useState12 } from "react";
5473
5640
  import { IconX } from "@tabler/icons-react";
5474
- import { jsx as jsx38, jsxs as jsxs27 } from "react/jsx-runtime";
5641
+ import { jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
5475
5642
  var SearchInput = () => {
5476
5643
  const { setSearchKey, search } = useDatabrowserStore();
5477
- const [state, setState] = useState10(search.key);
5644
+ const [state, setState] = useState12(search.key);
5478
5645
  const submit = (value) => {
5479
5646
  if (value.trim() !== "" && !value.includes("*")) value = `${value}*`;
5480
5647
  setSearchKey(value);
5481
5648
  setState(value);
5482
5649
  };
5483
- return /* @__PURE__ */ jsxs27("div", { className: "relative grow", children: [
5484
- /* @__PURE__ */ jsx38(
5650
+ return /* @__PURE__ */ jsxs28("div", { className: "relative grow", children: [
5651
+ /* @__PURE__ */ jsx41(
5485
5652
  Input,
5486
5653
  {
5487
5654
  placeholder: "Search",
@@ -5496,7 +5663,7 @@ var SearchInput = () => {
5496
5663
  value: state
5497
5664
  }
5498
5665
  ),
5499
- search.key && /* @__PURE__ */ jsxs27(
5666
+ state && /* @__PURE__ */ jsxs28(
5500
5667
  Button,
5501
5668
  {
5502
5669
  type: "button",
@@ -5505,10 +5672,11 @@ var SearchInput = () => {
5505
5672
  className: "absolute right-1 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100",
5506
5673
  onClick: () => {
5507
5674
  setSearchKey("");
5675
+ setState("");
5508
5676
  },
5509
5677
  children: [
5510
- /* @__PURE__ */ jsx38(IconX, { size: 16 }),
5511
- /* @__PURE__ */ jsx38("span", { className: "sr-only", children: "Clear" })
5678
+ /* @__PURE__ */ jsx41(IconX, { size: 16 }),
5679
+ /* @__PURE__ */ jsx41("span", { className: "sr-only", children: "Clear" })
5512
5680
  ]
5513
5681
  }
5514
5682
  )
@@ -5516,19 +5684,19 @@ var SearchInput = () => {
5516
5684
  };
5517
5685
 
5518
5686
  // src/components/databrowser/components/sidebar/skeleton-buttons.tsx
5519
- import { jsx as jsx39, jsxs as jsxs28 } from "react/jsx-runtime";
5687
+ import { jsx as jsx42, jsxs as jsxs29 } from "react/jsx-runtime";
5520
5688
  var DEFAULT_SKELETON_COUNT = 6;
5521
- var LoadingSkeleton = () => /* @__PURE__ */ jsx39("div", { className: "grid", children: Array.from({ length: DEFAULT_SKELETON_COUNT }).fill(0).map((_, idx) => /* @__PURE__ */ jsxs28("div", { className: "flex h-10 items-center gap-2 px-3", children: [
5522
- /* @__PURE__ */ jsx39(Skeleton, { className: "size-5 shrink-0 rounded" }),
5523
- /* @__PURE__ */ jsx39(Skeleton, { className: "h-4 grow rounded" })
5689
+ var LoadingSkeleton = () => /* @__PURE__ */ jsx42("div", { className: "grid", children: Array.from({ length: DEFAULT_SKELETON_COUNT }).fill(0).map((_, idx) => /* @__PURE__ */ jsxs29("div", { className: "flex h-10 items-center gap-2 px-3", children: [
5690
+ /* @__PURE__ */ jsx42(Skeleton, { className: "size-5 shrink-0 rounded" }),
5691
+ /* @__PURE__ */ jsx42(Skeleton, { className: "h-4 grow rounded" })
5524
5692
  ] }, idx)) });
5525
5693
 
5526
5694
  // src/components/databrowser/components/sidebar/type-selector.tsx
5527
- import { jsx as jsx40, jsxs as jsxs29 } from "react/jsx-runtime";
5695
+ import { jsx as jsx43, jsxs as jsxs30 } from "react/jsx-runtime";
5528
5696
  var ALL_TYPES_KEY = "all";
5529
5697
  function DataTypeSelector() {
5530
5698
  const { search, setSearchType } = useDatabrowserStore();
5531
- return /* @__PURE__ */ jsxs29(
5699
+ return /* @__PURE__ */ jsxs30(
5532
5700
  Select,
5533
5701
  {
5534
5702
  onValueChange: (type) => {
@@ -5540,9 +5708,9 @@ function DataTypeSelector() {
5540
5708
  },
5541
5709
  value: search.type === void 0 ? ALL_TYPES_KEY : search.type,
5542
5710
  children: [
5543
- /* @__PURE__ */ jsx40(SelectTrigger, { className: "!w-auto select-none whitespace-nowrap rounded-r-none border-r-0 border-zinc-300 pr-8", children: /* @__PURE__ */ jsx40(SelectValue, {}) }),
5544
- /* @__PURE__ */ jsx40(SelectContent, { children: /* @__PURE__ */ jsx40(SelectGroup, { children: [[ALL_TYPES_KEY, "All Types"], ...Object.entries(DATA_TYPE_NAMES)].map(
5545
- ([key, value]) => /* @__PURE__ */ jsx40(SelectItem, { value: key, children: value }, key)
5711
+ /* @__PURE__ */ jsx43(SelectTrigger, { className: "!w-auto select-none whitespace-nowrap rounded-r-none border-r-0 border-zinc-300 pr-8", children: /* @__PURE__ */ jsx43(SelectValue, {}) }),
5712
+ /* @__PURE__ */ jsx43(SelectContent, { children: /* @__PURE__ */ jsx43(SelectGroup, { children: [[ALL_TYPES_KEY, "All Types"], ...Object.entries(DATA_TYPE_NAMES)].map(
5713
+ ([key, value]) => /* @__PURE__ */ jsx43(SelectItem, { value: key, children: value }, key)
5546
5714
  ) }) })
5547
5715
  ]
5548
5716
  }
@@ -5550,15 +5718,15 @@ function DataTypeSelector() {
5550
5718
  }
5551
5719
 
5552
5720
  // src/components/databrowser/components/sidebar/index.tsx
5553
- import { jsx as jsx41, jsxs as jsxs30 } from "react/jsx-runtime";
5721
+ import { jsx as jsx44, jsxs as jsxs31 } from "react/jsx-runtime";
5554
5722
  function Sidebar() {
5555
5723
  const { keys, query, refetch } = useKeys();
5556
- return /* @__PURE__ */ jsxs30("div", { className: "flex h-full flex-col gap-2 rounded-xl border bg-white p-1", children: [
5557
- /* @__PURE__ */ jsxs30("div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
5558
- /* @__PURE__ */ jsxs30("div", { className: "flex h-10 items-center justify-between pl-1", children: [
5559
- /* @__PURE__ */ jsx41(DisplayDbSize, {}),
5560
- /* @__PURE__ */ jsxs30("div", { className: "flex gap-1", children: [
5561
- /* @__PURE__ */ jsx41(
5724
+ return /* @__PURE__ */ jsxs31("div", { className: "flex h-full flex-col gap-2 rounded-xl border bg-white p-1", children: [
5725
+ /* @__PURE__ */ jsxs31("div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
5726
+ /* @__PURE__ */ jsxs31("div", { className: "flex h-10 items-center justify-between pl-1", children: [
5727
+ /* @__PURE__ */ jsx44(DisplayDbSize, {}),
5728
+ /* @__PURE__ */ jsxs31("div", { className: "flex gap-1", children: [
5729
+ /* @__PURE__ */ jsx44(
5562
5730
  Button,
5563
5731
  {
5564
5732
  className: "h-7 w-7 px-0",
@@ -5573,39 +5741,45 @@ function Sidebar() {
5573
5741
  queryClient.invalidateQueries({
5574
5742
  queryKey: [FETCH_DB_SIZE_QUERY_KEY]
5575
5743
  });
5744
+ queryClient.invalidateQueries({
5745
+ queryKey: [FETCH_KEY_TYPE_QUERY_KEY]
5746
+ });
5576
5747
  },
5577
- children: /* @__PURE__ */ jsx41(Spinner, { isLoading: query.isFetching, children: /* @__PURE__ */ jsx41(IconRefresh, { size: 16 }) })
5748
+ children: /* @__PURE__ */ jsx44(Spinner, { isLoading: query.isFetching, children: /* @__PURE__ */ jsx44(IconRefresh, { size: 16 }) })
5578
5749
  }
5579
5750
  ),
5580
- /* @__PURE__ */ jsx41(AddKeyModal, {})
5751
+ /* @__PURE__ */ jsx44(AddKeyModal, {})
5581
5752
  ] })
5582
5753
  ] }),
5583
- /* @__PURE__ */ jsxs30("div", { className: "flex h-10 items-center", children: [
5584
- /* @__PURE__ */ jsx41(DataTypeSelector, {}),
5585
- /* @__PURE__ */ jsx41(SearchInput, {})
5754
+ /* @__PURE__ */ jsxs31("div", { className: "flex h-10 items-center", children: [
5755
+ /* @__PURE__ */ jsx44(DataTypeSelector, {}),
5756
+ /* @__PURE__ */ jsx44(SearchInput, {})
5586
5757
  ] })
5587
5758
  ] }),
5588
- query.isLoading ? /* @__PURE__ */ jsx41(LoadingSkeleton, {}) : keys.length > 0 ? /* @__PURE__ */ jsx41(InfiniteScroll, { query, children: /* @__PURE__ */ jsx41(KeysList, {}) }) : /* @__PURE__ */ jsx41(Empty, {})
5759
+ query.isLoading && keys.length === 0 ? /* @__PURE__ */ jsx44(LoadingSkeleton, {}) : keys.length > 0 ? (
5760
+ // Infinite scroll already has a loader at the bottom
5761
+ /* @__PURE__ */ jsx44(InfiniteScroll, { query, children: /* @__PURE__ */ jsx44(KeysList, {}) })
5762
+ ) : /* @__PURE__ */ jsx44(Empty, {})
5589
5763
  ] });
5590
5764
  }
5591
5765
 
5592
5766
  // src/components/databrowser/index.tsx
5593
- import { jsx as jsx42, jsxs as jsxs31 } from "react/jsx-runtime";
5767
+ import { jsx as jsx45, jsxs as jsxs32 } from "react/jsx-runtime";
5594
5768
  var RedisBrowser = ({ token, url }) => {
5595
5769
  const credentials = useMemo6(() => ({ token, url }), [token, url]);
5596
5770
  useEffect10(() => {
5597
5771
  queryClient.resetQueries();
5598
5772
  }, [credentials.url]);
5599
- return /* @__PURE__ */ jsx42(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx42(TooltipProvider, { children: /* @__PURE__ */ jsx42(DatabrowserProvider, { redisCredentials: credentials, children: /* @__PURE__ */ jsx42(KeysProvider, { children: /* @__PURE__ */ jsxs31("div", { className: "ups-db", style: { height: "100%" }, children: [
5600
- /* @__PURE__ */ jsxs31(
5773
+ return /* @__PURE__ */ jsx45(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx45(TooltipProvider, { children: /* @__PURE__ */ jsx45(DatabrowserProvider, { redisCredentials: credentials, children: /* @__PURE__ */ jsx45(KeysProvider, { children: /* @__PURE__ */ jsxs32("div", { className: "ups-db", style: { height: "100%" }, children: [
5774
+ /* @__PURE__ */ jsxs32(
5601
5775
  PanelGroup,
5602
5776
  {
5603
5777
  autoSaveId: "persistence",
5604
5778
  direction: "horizontal",
5605
5779
  className: "h-full w-full gap-0.5 text-sm antialiased",
5606
5780
  children: [
5607
- /* @__PURE__ */ jsx42(Panel, { defaultSize: 30, minSize: 30, children: /* @__PURE__ */ jsx42(Sidebar, {}) }),
5608
- /* @__PURE__ */ jsx42(PanelResizeHandle, { className: "h-fullm flex w-1.5 items-center justify-center rounded-full hover:bg-zinc-300/20", children: /* @__PURE__ */ jsx42(
5781
+ /* @__PURE__ */ jsx45(Panel, { defaultSize: 30, minSize: 30, children: /* @__PURE__ */ jsx45(Sidebar, {}) }),
5782
+ /* @__PURE__ */ jsx45(PanelResizeHandle, { className: "h-fullm flex w-1.5 items-center justify-center rounded-full hover:bg-zinc-300/20", children: /* @__PURE__ */ jsx45(
5609
5783
  IconDotsVertical2,
5610
5784
  {
5611
5785
  size: 16,
@@ -5613,11 +5787,11 @@ var RedisBrowser = ({ token, url }) => {
5613
5787
  className: "pointer-events-none shrink-0 opacity-20"
5614
5788
  }
5615
5789
  ) }),
5616
- /* @__PURE__ */ jsx42(Panel, { minSize: 40, children: /* @__PURE__ */ jsx42(DataDisplay, {}) })
5790
+ /* @__PURE__ */ jsx45(Panel, { minSize: 40, children: /* @__PURE__ */ jsx45(DataDisplay, {}) })
5617
5791
  ]
5618
5792
  }
5619
5793
  ),
5620
- /* @__PURE__ */ jsx42(Toaster, {})
5794
+ /* @__PURE__ */ jsx45(Toaster, {})
5621
5795
  ] }) }) }) }) });
5622
5796
  };
5623
5797
  export {