@upstash/react-redis-browser 0.1.10 → 0.1.11-canary

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.js CHANGED
@@ -4,9 +4,97 @@ var _react = require('react'); var React = _interopRequireWildcard(_react); var
4
4
  // src/store.tsx
5
5
 
6
6
  var _zustand = require('zustand');
7
+ var _jsxruntime = require('react/jsx-runtime');
8
+ var DatabrowserContext = _react.createContext.call(void 0, void 0);
9
+ var DatabrowserProvider = ({ children }) => {
10
+ const store = _react.useMemo.call(void 0, () => createDatabrowserStore(), []);
11
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserContext.Provider, { value: { store }, children });
12
+ };
13
+ var useDatabrowser = () => {
14
+ const context = _react.useContext.call(void 0, DatabrowserContext);
15
+ if (!context) {
16
+ throw new Error("useDatabrowser must be used within a DatabrowserProvider");
17
+ }
18
+ return context;
19
+ };
20
+ var useDatabrowserStore = () => {
21
+ const { store } = useDatabrowser();
22
+ return _zustand.useStore.call(void 0, store);
23
+ };
24
+ var createDatabrowserStore = () => _zustand.create.call(void 0, (set, get) => ({
25
+ selectedTab: void 0,
26
+ tabs: {},
27
+ addTab: () => {
28
+ const id = crypto.randomUUID();
29
+ const newTabData = {
30
+ selectedKey: void 0,
31
+ search: { key: "", type: void 0 }
32
+ };
33
+ set((old) => ({
34
+ tabs: { ...old.tabs, [id]: newTabData },
35
+ selectedTab: old.selectedTab === void 0 ? id : old.selectedTab
36
+ }));
37
+ },
38
+ removeTab: (id) => {
39
+ set((old) => {
40
+ const newTabs = { ...old.tabs };
41
+ delete newTabs[id];
42
+ let selectedTab = old.selectedTab;
43
+ if (selectedTab === id) {
44
+ const tabIds = Object.keys(newTabs);
45
+ selectedTab = tabIds.length > 0 ? tabIds[0] : void 0;
46
+ }
47
+ return { tabs: newTabs, selectedTab };
48
+ });
49
+ },
50
+ selectTab: (id) => {
51
+ set({ selectedTab: id });
52
+ },
53
+ getSelectedKey: (tabId) => {
54
+ return _optionalChain([get, 'call', _2 => _2(), 'access', _3 => _3.tabs, 'access', _4 => _4[tabId], 'optionalAccess', _5 => _5.selectedKey]);
55
+ },
56
+ setSelectedKey: (tabId, key) => {
57
+ set((old) => ({
58
+ ...old,
59
+ tabs: {
60
+ ...old.tabs,
61
+ [tabId]: { ...old.tabs[tabId], selectedKey: key, selectedListItem: void 0 }
62
+ }
63
+ }));
64
+ },
65
+ setSelectedListItem: (tabId, item) => {
66
+ set((old) => ({
67
+ ...old,
68
+ tabs: { ...old.tabs, [tabId]: { ...old.tabs[tabId], selectedListItem: item } }
69
+ }));
70
+ },
71
+ setSearch: (tabId, search) => set((old) => ({ ...old, tabs: { ...old.tabs, [tabId]: { ...old.tabs[tabId], search } } })),
72
+ setSearchKey: (tabId, key) => set((old) => ({
73
+ ...old,
74
+ tabs: {
75
+ ...old.tabs,
76
+ [tabId]: { ...old.tabs[tabId], search: { ...old.tabs[tabId].search, key } }
77
+ }
78
+ })),
79
+ setSearchType: (tabId, type) => set((old) => ({
80
+ ...old,
81
+ tabs: {
82
+ ...old.tabs,
83
+ [tabId]: { ...old.tabs[tabId], search: { ...old.tabs[tabId].search, type } }
84
+ }
85
+ })),
86
+ searchHistory: [],
87
+ addSearchHistory: (key) => {
88
+ set((old) => ({ ...old, searchHistory: [key, ...old.searchHistory] }));
89
+ }
90
+ }));
7
91
 
8
- // src/lib/clients.ts
92
+ // src/components/databrowser/index.tsx
93
+ var _reacttooltip = require('@radix-ui/react-tooltip'); var TooltipPrimitive = _interopRequireWildcard(_reacttooltip);
9
94
  var _reactquery = require('@tanstack/react-query');
95
+
96
+ // src/lib/clients.ts
97
+
10
98
  var _redis = require('@upstash/redis');
11
99
 
12
100
  // src/components/ui/use-toast.ts
@@ -136,8 +224,8 @@ var redisClient = ({
136
224
  credentials,
137
225
  pipelining
138
226
  }) => {
139
- const token = _optionalChain([credentials, 'optionalAccess', _2 => _2.token]) || process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_TOKEN;
140
- const url = _optionalChain([credentials, 'optionalAccess', _3 => _3.url]) || process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_URL;
227
+ const token = _optionalChain([credentials, 'optionalAccess', _6 => _6.token]) || process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_TOKEN;
228
+ const url = _optionalChain([credentials, 'optionalAccess', _7 => _7.url]) || process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_URL;
141
229
  if (!url) {
142
230
  throw new Error("Redis URL is missing!");
143
231
  }
@@ -180,140 +268,53 @@ var queryClient = new (0, _reactquery.QueryClient)({
180
268
  })
181
269
  });
182
270
 
183
- // src/store.tsx
184
- var _jsxruntime = require('react/jsx-runtime');
185
- var DatabrowserContext = _react.createContext.call(void 0, void 0);
186
- var DatabrowserProvider = ({
271
+ // src/redis-context.tsx
272
+
273
+
274
+ var RedisContext = _react.createContext.call(void 0, void 0);
275
+ var RedisProvider = ({
187
276
  children,
188
277
  redisCredentials
189
278
  }) => {
190
- const redisInstance = _react.useMemo.call(void 0, () => redisClient({ credentials: redisCredentials, pipelining: true }), [redisCredentials]);
191
- const redisInstanceNoPipeline = _react.useMemo.call(void 0, () => redisClient({ credentials: redisCredentials, pipelining: false }), [redisCredentials]);
192
- const [store] = _react.useState.call(void 0, () => {
193
- return createDatabrowserStore();
194
- });
195
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserContext.Provider, { value: { redis: redisInstance, redisNoPipeline: redisInstanceNoPipeline, store }, children });
279
+ const redisInstance = _react.useMemo.call(void 0,
280
+ () => redisClient({ credentials: redisCredentials, pipelining: true }),
281
+ [redisCredentials]
282
+ );
283
+ const redisInstanceNoPipeline = _react.useMemo.call(void 0,
284
+ () => redisClient({ credentials: redisCredentials, pipelining: false }),
285
+ [redisCredentials]
286
+ );
287
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
288
+ RedisContext.Provider,
289
+ {
290
+ value: { redis: redisInstance, redisNoPipeline: redisInstanceNoPipeline },
291
+ children
292
+ }
293
+ );
196
294
  };
197
- var useDatabrowser = () => {
198
- const context = _react.useContext.call(void 0, DatabrowserContext);
295
+ var useRedis = () => {
296
+ const context = _react.useContext.call(void 0, RedisContext);
199
297
  if (!context) {
200
- throw new Error("useDatabrowser must be used within a DatabrowserProvider");
298
+ throw new Error("useRedis must be used within a RedisProvider");
201
299
  }
202
300
  return context;
203
301
  };
204
- var useDatabrowserStore = () => {
205
- const { store } = useDatabrowser();
206
- return _zustand.useStore.call(void 0, store);
207
- };
208
- var createDatabrowserStore = () => _zustand.create.call(void 0, (set) => ({
209
- selectedKey: void 0,
210
- setSelectedKey: (key) => {
211
- set((old) => ({ ...old, selectedKey: key, selectedListItem: void 0 }));
212
- },
213
- selectedListItem: void 0,
214
- setSelectedListItem: (item) => {
215
- set((old) => ({ ...old, selectedListItem: item }));
216
- },
217
- search: { key: "", type: void 0 },
218
- setSearch: (search) => set({ search }),
219
- setSearchKey: (key) => set((state) => ({ search: { ...state.search, key } })),
220
- setSearchType: (type) => set((state) => ({ search: { ...state.search, type } }))
221
- }));
222
-
223
- // src/components/databrowser/index.tsx
224
- var _reacttooltip = require('@radix-ui/react-tooltip'); var TooltipPrimitive = _interopRequireWildcard(_reacttooltip);
225
- var _iconsreact = require('@tabler/icons-react');
226
302
 
303
+ // src/components/databrowser/components/databrowser-instance.tsx
227
304
  var _reactresizablepanels = require('react-resizable-panels');
228
305
 
229
- // src/components/ui/toaster.tsx
230
- var _reactportal = require('@radix-ui/react-portal');
231
-
232
- // src/lib/portal-root.ts
233
- var root;
234
- if (typeof document !== "undefined") {
235
- const id = "react-redis-browser-portal-root";
236
- root = _nullishCoalesce(document.querySelector(`#${id}`), () => ( document.createElement("div")));
237
- root.classList.add("ups-db");
238
- root.id = "react-redis-browser-portal-root";
239
- document.body.append(root);
240
- }
241
- var portalRoot = root;
242
-
243
- // src/components/ui/toast.tsx
244
-
245
- var _reacticons = require('@radix-ui/react-icons');
246
- var _reacttoast = require('@radix-ui/react-toast'); var ToastPrimitives = _interopRequireWildcard(_reacttoast);
247
-
248
- // node_modules/class-variance-authority/node_modules/clsx/dist/clsx.mjs
249
- function r(e) {
250
- var t, f, n = "";
251
- if ("string" == typeof e || "number" == typeof e) n += e;
252
- else if ("object" == typeof e) if (Array.isArray(e)) for (t = 0; t < e.length; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
253
- else for (t in e) e[t] && (n && (n += " "), n += t);
254
- return n;
255
- }
256
- function clsx() {
257
- for (var e, t, f = 0, n = ""; f < arguments.length; ) (e = arguments[f++]) && (t = r(e)) && (n && (n += " "), n += t);
258
- return n;
259
- }
260
-
261
- // node_modules/class-variance-authority/dist/index.mjs
262
- var falsyToString = (value) => typeof value === "boolean" ? "".concat(value) : value === 0 ? "0" : value;
263
- var cx = clsx;
264
- var cva = (base, config) => {
265
- return (props) => {
266
- var ref;
267
- if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
268
- const { variants, defaultVariants } = config;
269
- const getVariantClassNames = Object.keys(variants).map((variant) => {
270
- const variantProp = props === null || props === void 0 ? void 0 : props[variant];
271
- const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
272
- if (variantProp === null) return null;
273
- const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
274
- return variants[variant][variantKey];
275
- });
276
- const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
277
- let [key, value] = param;
278
- if (value === void 0) {
279
- return acc;
280
- }
281
- acc[key] = value;
282
- return acc;
283
- }, {});
284
- const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (ref = config.compoundVariants) === null || ref === void 0 ? void 0 : ref.reduce((acc, param1) => {
285
- let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param1;
286
- return Object.entries(compoundVariantOptions).every((param) => {
287
- let [key, value] = param;
288
- return Array.isArray(value) ? value.includes({
289
- ...defaultVariants,
290
- ...propsWithoutUndefined
291
- }[key]) : {
292
- ...defaultVariants,
293
- ...propsWithoutUndefined
294
- }[key] === value;
295
- }) ? [
296
- ...acc,
297
- cvClass,
298
- cvClassName
299
- ] : acc;
300
- }, []);
301
- return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
302
- };
303
- };
304
-
305
306
  // node_modules/clsx/dist/clsx.mjs
306
- function r2(e) {
307
+ function r(e) {
307
308
  var t, f, n = "";
308
309
  if ("string" == typeof e || "number" == typeof e) n += e;
309
310
  else if ("object" == typeof e) if (Array.isArray(e)) {
310
311
  var o = e.length;
311
- for (t = 0; t < o; t++) e[t] && (f = r2(e[t])) && (n && (n += " "), n += f);
312
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
312
313
  } else for (f in e) e[f] && (n && (n += " "), n += f);
313
314
  return n;
314
315
  }
315
- function clsx2() {
316
- for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r2(e)) && (n && (n += " "), n += t);
316
+ function clsx() {
317
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
317
318
  return n;
318
319
  }
319
320
 
@@ -358,15 +359,15 @@ var getGroupRecursive = (classParts, classPartObject) => {
358
359
  return void 0;
359
360
  }
360
361
  const classRest = classParts.join(CLASS_PART_SEPARATOR);
361
- return _optionalChain([classPartObject, 'access', _4 => _4.validators, 'access', _5 => _5.find, 'call', _6 => _6(({
362
+ return _optionalChain([classPartObject, 'access', _8 => _8.validators, 'access', _9 => _9.find, 'call', _10 => _10(({
362
363
  validator
363
- }) => validator(classRest)), 'optionalAccess', _7 => _7.classGroupId]);
364
+ }) => validator(classRest)), 'optionalAccess', _11 => _11.classGroupId]);
364
365
  };
365
366
  var arbitraryPropertyRegex = /^\[(.+)\]$/;
366
367
  var getGroupIdForArbitraryProperty = (className) => {
367
368
  if (arbitraryPropertyRegex.test(className)) {
368
369
  const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];
369
- const property = _optionalChain([arbitraryPropertyClassName, 'optionalAccess', _8 => _8.substring, 'call', _9 => _9(0, arbitraryPropertyClassName.indexOf(":"))]);
370
+ const property = _optionalChain([arbitraryPropertyClassName, 'optionalAccess', _12 => _12.substring, 'call', _13 => _13(0, arbitraryPropertyClassName.indexOf(":"))]);
370
371
  if (property) {
371
372
  return "arbitrary.." + property;
372
373
  }
@@ -2779,7 +2780,7 @@ var twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
2779
2780
 
2780
2781
  // src/lib/utils.ts
2781
2782
  function cn(...inputs) {
2782
- return twMerge(clsx2(inputs));
2783
+ return twMerge(clsx(inputs));
2783
2784
  }
2784
2785
  function formatNumber(value) {
2785
2786
  const intl = new Intl.NumberFormat("en-US");
@@ -2811,6 +2812,82 @@ function formatTime(seconds) {
2811
2812
  return parts.slice(0, 1).join(" ");
2812
2813
  }
2813
2814
 
2815
+ // src/components/ui/toaster.tsx
2816
+ var _reactportal = require('@radix-ui/react-portal');
2817
+
2818
+ // src/lib/portal-root.ts
2819
+ var root;
2820
+ if (typeof document !== "undefined") {
2821
+ const id = "react-redis-browser-portal-root";
2822
+ root = _nullishCoalesce(document.querySelector(`#${id}`), () => ( document.createElement("div")));
2823
+ root.classList.add("ups-db");
2824
+ root.id = "react-redis-browser-portal-root";
2825
+ document.body.append(root);
2826
+ }
2827
+ var portalRoot = root;
2828
+
2829
+ // src/components/ui/toast.tsx
2830
+
2831
+ var _reacticons = require('@radix-ui/react-icons');
2832
+ var _reacttoast = require('@radix-ui/react-toast'); var ToastPrimitives = _interopRequireWildcard(_reacttoast);
2833
+
2834
+ // node_modules/class-variance-authority/node_modules/clsx/dist/clsx.mjs
2835
+ function r2(e) {
2836
+ var t, f, n = "";
2837
+ if ("string" == typeof e || "number" == typeof e) n += e;
2838
+ else if ("object" == typeof e) if (Array.isArray(e)) for (t = 0; t < e.length; t++) e[t] && (f = r2(e[t])) && (n && (n += " "), n += f);
2839
+ else for (t in e) e[t] && (n && (n += " "), n += t);
2840
+ return n;
2841
+ }
2842
+ function clsx2() {
2843
+ for (var e, t, f = 0, n = ""; f < arguments.length; ) (e = arguments[f++]) && (t = r2(e)) && (n && (n += " "), n += t);
2844
+ return n;
2845
+ }
2846
+
2847
+ // node_modules/class-variance-authority/dist/index.mjs
2848
+ var falsyToString = (value) => typeof value === "boolean" ? "".concat(value) : value === 0 ? "0" : value;
2849
+ var cx = clsx2;
2850
+ var cva = (base, config) => {
2851
+ return (props) => {
2852
+ var ref;
2853
+ if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
2854
+ const { variants, defaultVariants } = config;
2855
+ const getVariantClassNames = Object.keys(variants).map((variant) => {
2856
+ const variantProp = props === null || props === void 0 ? void 0 : props[variant];
2857
+ const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
2858
+ if (variantProp === null) return null;
2859
+ const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
2860
+ return variants[variant][variantKey];
2861
+ });
2862
+ const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
2863
+ let [key, value] = param;
2864
+ if (value === void 0) {
2865
+ return acc;
2866
+ }
2867
+ acc[key] = value;
2868
+ return acc;
2869
+ }, {});
2870
+ const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (ref = config.compoundVariants) === null || ref === void 0 ? void 0 : ref.reduce((acc, param1) => {
2871
+ let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param1;
2872
+ return Object.entries(compoundVariantOptions).every((param) => {
2873
+ let [key, value] = param;
2874
+ return Array.isArray(value) ? value.includes({
2875
+ ...defaultVariants,
2876
+ ...propsWithoutUndefined
2877
+ }[key]) : {
2878
+ ...defaultVariants,
2879
+ ...propsWithoutUndefined
2880
+ }[key] === value;
2881
+ }) ? [
2882
+ ...acc,
2883
+ cvClass,
2884
+ cvClassName
2885
+ ] : acc;
2886
+ }, []);
2887
+ return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
2888
+ };
2889
+ };
2890
+
2814
2891
  // src/components/ui/toast.tsx
2815
2892
 
2816
2893
  var ToastProvider = ToastPrimitives.Provider;
@@ -2917,12 +2994,61 @@ function Toaster() {
2917
2994
 
2918
2995
 
2919
2996
 
2997
+ // src/tab-provider.tsx
2998
+
2999
+
3000
+ var TabIdContext = _react.createContext.call(void 0, void 0);
3001
+ var TabIdProvider = ({ children, value }) => {
3002
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TabIdContext.Provider, { value, children });
3003
+ };
3004
+ var useTab = () => {
3005
+ const {
3006
+ selectedTab,
3007
+ tabs,
3008
+ setSelectedKey,
3009
+ setSelectedListItem,
3010
+ setSearch,
3011
+ setSearchKey,
3012
+ setSearchType
3013
+ } = useDatabrowserStore();
3014
+ if (!selectedTab) throw new Error("selectedTab is undefined when using useTab()");
3015
+ return _react.useMemo.call(void 0,
3016
+ () => ({
3017
+ selectedKey: _optionalChain([tabs, 'access', _14 => _14[selectedTab], 'optionalAccess', _15 => _15.selectedKey]),
3018
+ selectedListItem: _optionalChain([tabs, 'access', _16 => _16[selectedTab], 'optionalAccess', _17 => _17.selectedListItem]),
3019
+ search: _optionalChain([tabs, 'access', _18 => _18[selectedTab], 'optionalAccess', _19 => _19.search]),
3020
+ setSelectedKey: (key) => setSelectedKey(selectedTab, key),
3021
+ setSelectedListItem: (item) => setSelectedListItem(selectedTab, item),
3022
+ setSearch: (search) => setSearch(selectedTab, search),
3023
+ setSearchKey: (key) => setSearchKey(selectedTab, key),
3024
+ setSearchType: (type) => setSearchType(selectedTab, type)
3025
+ }),
3026
+ [selectedTab, tabs]
3027
+ );
3028
+ };
3029
+
3030
+ // src/components/databrowser/hooks/use-fetch-key-type.ts
3031
+
3032
+ var FETCH_KEY_TYPE_QUERY_KEY = "fetch-key-type";
3033
+ var useFetchKeyType = (key) => {
3034
+ const { redis } = useRedis();
3035
+ return _reactquery.useQuery.call(void 0, {
3036
+ queryKey: [FETCH_KEY_TYPE_QUERY_KEY, key],
3037
+ queryFn: async () => {
3038
+ if (!key) return "none";
3039
+ return await redis.type(key);
3040
+ }
3041
+ });
3042
+ };
3043
+
3044
+ // src/components/databrowser/hooks/use-keys.tsx
3045
+
2920
3046
  var KeysContext = _react.createContext.call(void 0, void 0);
2921
3047
  var FETCH_KEYS_QUERY_KEY = "use-fetch-keys";
2922
3048
  var SCAN_COUNT = 100;
2923
3049
  var KeysProvider = ({ children }) => {
2924
- const { search } = useDatabrowserStore();
2925
- const { redisNoPipeline: redis } = useDatabrowser();
3050
+ const { search } = useTab();
3051
+ const { redisNoPipeline: redis } = useRedis();
2926
3052
  const query = _reactquery.useInfiniteQuery.call(void 0, {
2927
3053
  queryKey: [FETCH_KEYS_QUERY_KEY, search],
2928
3054
  initialPageParam: "0",
@@ -2950,6 +3076,9 @@ var KeysProvider = ({ children }) => {
2950
3076
  index += 2;
2951
3077
  }
2952
3078
  }
3079
+ for (const [key, type] of keys2) {
3080
+ queryClient.setQueryData([FETCH_KEY_TYPE_QUERY_KEY, key], type);
3081
+ }
2953
3082
  return {
2954
3083
  cursor: cursor === "0" ? void 0 : cursor,
2955
3084
  keys: keys2,
@@ -2961,7 +3090,7 @@ var KeysProvider = ({ children }) => {
2961
3090
  refetchOnMount: false
2962
3091
  });
2963
3092
  const keys = _react.useMemo.call(void 0, () => {
2964
- const keys2 = _nullishCoalesce(_optionalChain([query, 'access', _10 => _10.data, 'optionalAccess', _11 => _11.pages, 'access', _12 => _12.flatMap, 'call', _13 => _13((page) => page.keys)]), () => ( []));
3093
+ const keys2 = _nullishCoalesce(_optionalChain([query, 'access', _20 => _20.data, 'optionalAccess', _21 => _21.pages, 'access', _22 => _22.flatMap, 'call', _23 => _23((page) => page.keys)]), () => ( []));
2965
3094
  const keysSet = /* @__PURE__ */ new Set();
2966
3095
  const dedupedKeys = [];
2967
3096
  for (const key of keys2) {
@@ -2992,19 +3121,19 @@ var useKeys = () => {
2992
3121
  var useKeyType = (key) => {
2993
3122
  const { keys } = useKeys();
2994
3123
  const keyTuple = _react.useMemo.call(void 0, () => keys.find(([k, _]) => k === key), [keys, key]);
2995
- return _optionalChain([keyTuple, 'optionalAccess', _14 => _14[1]]);
3124
+ return _optionalChain([keyTuple, 'optionalAccess', _24 => _24[1]]);
2996
3125
  };
2997
3126
 
2998
3127
  // src/components/databrowser/components/display/display-list.tsx
2999
3128
 
3000
-
3129
+ var _iconsreact = require('@tabler/icons-react');
3001
3130
 
3002
3131
  // src/components/ui/button.tsx
3003
3132
 
3004
3133
  var _reactslot = require('@radix-ui/react-slot');
3005
3134
 
3006
3135
  var buttonVariants = cva(
3007
- "inline-flex items-center justify-center rounded-md text-sm ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-300",
3136
+ "inline-flex items-center justify-center rounded-md text-sm ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-zinc-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-300",
3008
3137
  {
3009
3138
  variants: {
3010
3139
  variant: {
@@ -3061,7 +3190,7 @@ function Skeleton({ className, ...props }) {
3061
3190
 
3062
3191
  var FETCH_DB_SIZE_QUERY_KEY = "fetch-db-size";
3063
3192
  var DisplayDbSize = () => {
3064
- const { redis } = useDatabrowser();
3193
+ const { redis } = useRedis();
3065
3194
  const { data: keyCount } = _reactquery.useQuery.call(void 0, {
3066
3195
  queryKey: [FETCH_DB_SIZE_QUERY_KEY],
3067
3196
  queryFn: async () => {
@@ -3079,7 +3208,7 @@ var DisplayDbSize = () => {
3079
3208
 
3080
3209
  // src/components/databrowser/hooks/use-add-key.ts
3081
3210
  var useAddKey = () => {
3082
- const { redis } = useDatabrowser();
3211
+ const { redis } = useRedis();
3083
3212
  const mutation = _reactquery.useMutation.call(void 0, {
3084
3213
  mutationFn: async ({ key, type }) => {
3085
3214
  if (await redis.exists(key)) throw new Error(`Key "${key}" already exists`);
@@ -3159,16 +3288,12 @@ var useAddKey = () => {
3159
3288
  // src/components/databrowser/hooks/use-delete-key-cache.ts
3160
3289
 
3161
3290
 
3162
- // src/components/databrowser/hooks/use-fetch-key-type.tsx
3163
-
3164
- var FETCH_KEY_TYPE_QUERY_KEY = "fetch-key-type";
3165
-
3166
3291
  // src/components/databrowser/hooks/use-fetch-list-items.tsx
3167
3292
 
3168
3293
  var LIST_DISPLAY_PAGE_SIZE = 50;
3169
3294
  var FETCH_LIST_ITEMS_QUERY_KEY = "use-fetch-list-items";
3170
3295
  var useFetchListItems = ({ dataKey, type }) => {
3171
- const { redisNoPipeline: redis } = useDatabrowser();
3296
+ const { redisNoPipeline: redis } = useRedis();
3172
3297
  const setQuery = _reactquery.useInfiniteQuery.call(void 0, {
3173
3298
  enabled: type === "set",
3174
3299
  queryKey: [FETCH_LIST_ITEMS_QUERY_KEY, dataKey, "set"],
@@ -3247,7 +3372,7 @@ var useFetchListItems = ({ dataKey, type }) => {
3247
3372
  // +1 since first message is the last one
3248
3373
  LIST_DISPLAY_PAGE_SIZE + 1
3249
3374
  );
3250
- const lastMessageId = messages.length > 0 ? _optionalChain([messages, 'access', _15 => _15.at, 'call', _16 => _16(-1), 'optionalAccess', _17 => _17[0]]) : void 0;
3375
+ const lastMessageId = messages.length > 0 ? _optionalChain([messages, 'access', _25 => _25.at, 'call', _26 => _26(-1), 'optionalAccess', _27 => _27[0]]) : void 0;
3251
3376
  return {
3252
3377
  cursor: messages.length < LIST_DISPLAY_PAGE_SIZE ? void 0 : lastMessageId,
3253
3378
  keys: messages.map(([id, fields]) => ({
@@ -3283,7 +3408,7 @@ function transformArray(inputArray) {
3283
3408
 
3284
3409
  var FETCH_SIMPLE_KEY_QUERY_KEY = "fetch-simple-key";
3285
3410
  var useFetchSimpleKey = (dataKey, type) => {
3286
- const { redisNoPipeline: redis } = useDatabrowser();
3411
+ const { redisNoPipeline: redis } = useRedis();
3287
3412
  const { deleteKeyCache } = useDeleteKeyCache();
3288
3413
  return _reactquery.useQuery.call(void 0, {
3289
3414
  queryKey: [FETCH_SIMPLE_KEY_QUERY_KEY, dataKey],
@@ -3310,7 +3435,7 @@ var sortObject = (obj) => {
3310
3435
 
3311
3436
  // src/components/databrowser/hooks/use-delete-key-cache.ts
3312
3437
  var useDeleteKeyCache = () => {
3313
- const { setSelectedKey } = useDatabrowserStore();
3438
+ const { setSelectedKey } = useTab();
3314
3439
  const deleteKeyCache = _react.useCallback.call(void 0,
3315
3440
  (key) => {
3316
3441
  setSelectedKey(void 0);
@@ -3334,7 +3459,7 @@ var useDeleteKeyCache = () => {
3334
3459
 
3335
3460
  // src/components/databrowser/hooks/use-delete-key.ts
3336
3461
  var useDeleteKey = () => {
3337
- const { redis } = useDatabrowser();
3462
+ const { redis } = useRedis();
3338
3463
  const { deleteKeyCache } = useDeleteKeyCache();
3339
3464
  const deleteKey = _reactquery.useMutation.call(void 0, {
3340
3465
  mutationFn: async (key) => {
@@ -3353,7 +3478,7 @@ var useDeleteKey = () => {
3353
3478
  // src/components/databrowser/hooks/use-edit-list-item.tsx
3354
3479
 
3355
3480
  var useEditListItem = () => {
3356
- const { redis } = useDatabrowser();
3481
+ const { redis } = useRedis();
3357
3482
  return _reactquery.useMutation.call(void 0, {
3358
3483
  mutationFn: async ({
3359
3484
  type,
@@ -3411,7 +3536,7 @@ var useEditListItem = () => {
3411
3536
  }
3412
3537
  case "stream": {
3413
3538
  if (!isNew || !newKey) throw new Error("Stream data type is not mutable");
3414
- const opts = transformArray(_nullishCoalesce(_optionalChain([newValue, 'optionalAccess', _18 => _18.split, 'call', _19 => _19("\n")]), () => ( []))).map(
3539
+ const opts = transformArray(_nullishCoalesce(_optionalChain([newValue, 'optionalAccess', _28 => _28.split, 'call', _29 => _29("\n")]), () => ( []))).map(
3415
3540
  ({ key, value }) => [key, value]
3416
3541
  );
3417
3542
  pipe.xadd(dataKey, newKey, Object.fromEntries(opts));
@@ -3446,7 +3571,7 @@ var _bytes = require('bytes'); var _bytes2 = _interopRequireDefault(_bytes);
3446
3571
 
3447
3572
  var FETCH_KEY_LENGTH_QUERY_KEY = "fetch-key-length";
3448
3573
  var useFetchKeyLength = ({ dataKey, type }) => {
3449
- const { redis } = useDatabrowser();
3574
+ const { redis } = useRedis();
3450
3575
  return _reactquery.useQuery.call(void 0, {
3451
3576
  queryKey: [FETCH_KEY_LENGTH_QUERY_KEY, dataKey],
3452
3577
  queryFn: async () => {
@@ -3476,7 +3601,7 @@ var useFetchKeyLength = ({ dataKey, type }) => {
3476
3601
 
3477
3602
  var FETCH_KEY_SIZE_QUERY_KEY = "fetch-key-size";
3478
3603
  var useFetchKeySize = (dataKey) => {
3479
- const { redis } = useDatabrowser();
3604
+ const { redis } = useRedis();
3480
3605
  return _reactquery.useQuery.call(void 0, {
3481
3606
  queryKey: [FETCH_KEY_SIZE_QUERY_KEY, dataKey],
3482
3607
  queryFn: async () => {
@@ -3493,7 +3618,7 @@ var LengthBadge = ({
3493
3618
  content
3494
3619
  }) => {
3495
3620
  const { data, isLoading } = useFetchKeyLength({ dataKey, type });
3496
- const length = _nullishCoalesce(_optionalChain([content, 'optionalAccess', _20 => _20.length]), () => ( data));
3621
+ const length = _nullishCoalesce(_optionalChain([content, 'optionalAccess', _30 => _30.length]), () => ( data));
3497
3622
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Badge, { label: "Length:", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Skeleton, { className: "ml-1 h-3 w-10 rounded-md opacity-50" }) : length });
3498
3623
  };
3499
3624
  var SizeBadge = ({ dataKey }) => {
@@ -3503,7 +3628,7 @@ var SizeBadge = ({ dataKey }) => {
3503
3628
  }) });
3504
3629
  };
3505
3630
  var HeaderTTLBadge = ({ dataKey }) => {
3506
- const { data: expireAt } = useFetchKeyExpire(dataKey);
3631
+ const { data: expireAt } = useFetchTTL(dataKey);
3507
3632
  const { mutate: setTTL, isPending } = useSetTTL();
3508
3633
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3509
3634
  TTLBadge,
@@ -3853,8 +3978,8 @@ var TTLBadge = ({
3853
3978
 
3854
3979
  // src/components/databrowser/hooks/use-fetch-ttl.ts
3855
3980
  var FETCH_TTL_QUERY_KEY = "fetch-ttl";
3856
- var useFetchKeyExpire = (dataKey) => {
3857
- const { redis } = useDatabrowser();
3981
+ var useFetchTTL = (dataKey) => {
3982
+ const { redis } = useRedis();
3858
3983
  const { isLoading, error, data } = _reactquery.useQuery.call(void 0, {
3859
3984
  queryKey: [FETCH_TTL_QUERY_KEY, dataKey],
3860
3985
  queryFn: async () => {
@@ -3875,7 +4000,7 @@ var useFetchKeyExpire = (dataKey) => {
3875
4000
  // src/components/databrowser/hooks/use-set-simple-key.tsx
3876
4001
 
3877
4002
  var useSetSimpleKey = (dataKey, type) => {
3878
- const { redis } = useDatabrowser();
4003
+ const { redis } = useRedis();
3879
4004
  return _reactquery.useMutation.call(void 0, {
3880
4005
  mutationFn: async (value) => {
3881
4006
  if (type === "string") {
@@ -3898,7 +4023,7 @@ var useSetSimpleKey = (dataKey, type) => {
3898
4023
  // src/components/databrowser/hooks/use-set-ttl.ts
3899
4024
 
3900
4025
  var useSetTTL = () => {
3901
- const { redis } = useDatabrowser();
4026
+ const { redis } = useRedis();
3902
4027
  const updateTTL = _reactquery.useMutation.call(void 0, {
3903
4028
  mutationFn: async ({ dataKey, ttl }) => {
3904
4029
  await (ttl === void 0 || ttl === TTL_INFINITE ? redis.persist(dataKey) : redis.expire(dataKey, ttl));
@@ -4184,7 +4309,7 @@ var ItemContextMenu = ({
4184
4309
  editItem({
4185
4310
  type,
4186
4311
  dataKey,
4187
- itemKey: _optionalChain([data, 'optionalAccess', _21 => _21.key]),
4312
+ itemKey: _optionalChain([data, 'optionalAccess', _31 => _31.key]),
4188
4313
  // For deletion
4189
4314
  newKey: void 0
4190
4315
  });
@@ -4219,7 +4344,7 @@ var ItemContextMenu = ({
4219
4344
  {
4220
4345
  onClick: () => {
4221
4346
  if (!data) return;
4222
- navigator.clipboard.writeText(_optionalChain([data, 'optionalAccess', _22 => _22.key]));
4347
+ navigator.clipboard.writeText(_optionalChain([data, 'optionalAccess', _32 => _32.key]));
4223
4348
  toast({
4224
4349
  description: "Key copied to clipboard"
4225
4350
  });
@@ -4227,11 +4352,11 @@ var ItemContextMenu = ({
4227
4352
  children: "Copy key"
4228
4353
  }
4229
4354
  ),
4230
- _optionalChain([data, 'optionalAccess', _23 => _23.value]) && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
4355
+ _optionalChain([data, 'optionalAccess', _33 => _33.value]) && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
4231
4356
  ContextMenuItem,
4232
4357
  {
4233
4358
  onClick: () => {
4234
- navigator.clipboard.writeText(_nullishCoalesce(_optionalChain([data, 'optionalAccess', _24 => _24.value]), () => ( "")));
4359
+ navigator.clipboard.writeText(_nullishCoalesce(_optionalChain([data, 'optionalAccess', _34 => _34.value]), () => ( "")));
4235
4360
  toast({
4236
4361
  description: "Value copied to clipboard"
4237
4362
  });
@@ -4253,7 +4378,7 @@ var ItemContextMenu = ({
4253
4378
 
4254
4379
  var _reactscrollarea = require('@radix-ui/react-scroll-area'); var ScrollAreaPrimitive = _interopRequireWildcard(_reactscrollarea);
4255
4380
 
4256
- var ScrollArea = React9.forwardRef(({ className, children, onScroll, ...props }, ref) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
4381
+ var ScrollArea = React9.forwardRef(({ className, children, onScroll, roundedInherit = true, ...props }, ref) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
4257
4382
  ScrollAreaPrimitive.Root,
4258
4383
  {
4259
4384
  ref,
@@ -4264,7 +4389,7 @@ var ScrollArea = React9.forwardRef(({ className, children, onScroll, ...props },
4264
4389
  ScrollAreaPrimitive.Viewport,
4265
4390
  {
4266
4391
  onScroll,
4267
- className: "h-full w-full rounded-[inherit] [&>div]:!block",
4392
+ className: cn("h-full w-full [&>div]:!block", roundedInherit && "rounded-[inherit]"),
4268
4393
  children
4269
4394
  }
4270
4395
  ),
@@ -4295,7 +4420,8 @@ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
4295
4420
 
4296
4421
  var InfiniteScroll = ({
4297
4422
  query,
4298
- children
4423
+ children,
4424
+ ...props
4299
4425
  }) => {
4300
4426
  const handleScroll = (e) => {
4301
4427
  const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
@@ -4310,8 +4436,9 @@ var InfiniteScroll = ({
4310
4436
  ScrollArea,
4311
4437
  {
4312
4438
  type: "always",
4313
- className: "block h-full w-full transition-all",
4439
+ className: "block h-full w-full overflow-visible rounded-lg border border-zinc-200 bg-white p-1 pr-3 transition-all",
4314
4440
  onScroll: handleScroll,
4441
+ ...props,
4315
4442
  children: [
4316
4443
  children,
4317
4444
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-[100px] justify-center py-2 text-zinc-300", children: query.isFetching && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _iconsreact.IconLoader2, { className: "animate-spin", size: 16 }) })
@@ -4394,14 +4521,14 @@ var DropdownMenuSubTrigger = React10.forwardRef(({ className, inset, children, .
4394
4521
  {
4395
4522
  ref,
4396
4523
  className: cn(
4397
- "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-zinc-100 data-[state=open]:bg-zinc-100 dark:focus:bg-zinc-800 dark:data-[state=open]:bg-zinc-800",
4524
+ "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-neutral-100 data-[state=open]:bg-neutral-100 dark:focus:bg-neutral-800 dark:data-[state=open]:bg-neutral-800 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
4398
4525
  inset && "pl-8",
4399
4526
  className
4400
4527
  ),
4401
4528
  ...props,
4402
4529
  children: [
4403
4530
  children,
4404
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacticons.ChevronRightIcon, { className: "ml-auto size-4" })
4531
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacticons.ChevronRightIcon, { className: "ml-auto" })
4405
4532
  ]
4406
4533
  }
4407
4534
  ));
@@ -4411,7 +4538,7 @@ var DropdownMenuSubContent = React10.forwardRef(({ className, ...props }, ref) =
4411
4538
  {
4412
4539
  ref,
4413
4540
  className: cn(
4414
- "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",
4541
+ "z-50 min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] 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",
4415
4542
  className
4416
4543
  ),
4417
4544
  ...props
@@ -4424,8 +4551,8 @@ var DropdownMenuContent = React10.forwardRef(({ className, sideOffset = 4, ...pr
4424
4551
  ref,
4425
4552
  sideOffset,
4426
4553
  className: cn(
4427
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-md dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
4428
- "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",
4554
+ "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-md dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
4555
+ "origin-[--radix-dropdown-menu-content-transform-origin] 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",
4429
4556
  className
4430
4557
  ),
4431
4558
  ...props
@@ -4456,7 +4583,7 @@ var DropdownMenuCheckboxItem = React10.forwardRef(({ className, children, checke
4456
4583
  checked,
4457
4584
  ...props,
4458
4585
  children: [
4459
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacticons.CheckIcon, { className: "size-4" }) }) }),
4586
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacticons.CheckIcon, { className: "h-4 w-4" }) }) }),
4460
4587
  children
4461
4588
  ]
4462
4589
  }
@@ -4472,7 +4599,7 @@ var DropdownMenuRadioItem = React10.forwardRef(({ className, children, ...props
4472
4599
  ),
4473
4600
  ...props,
4474
4601
  children: [
4475
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacticons.DotFilledIcon, { className: "size-4 fill-current" }) }) }),
4602
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacticons.DotFilledIcon, { className: "h-2 w-2 fill-current" }) }) }),
4476
4603
  children
4477
4604
  ]
4478
4605
  }
@@ -4539,11 +4666,11 @@ var DisplayHeader = ({
4539
4666
  type,
4540
4667
  content
4541
4668
  }) => {
4542
- const { setSelectedListItem } = useDatabrowserStore();
4669
+ const { setSelectedListItem } = useTab();
4543
4670
  const handleAddItem = () => {
4544
4671
  setSelectedListItem({ key: type === "stream" ? "*" : "", isNew: true });
4545
4672
  };
4546
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
4673
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "rounded-lg bg-zinc-100", children: [
4547
4674
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex min-h-10 items-center justify-between gap-4", children: [
4548
4675
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: "grow truncate text-base", children: dataKey.trim() === "" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "ml-1 text-zinc-500", children: "(Empty Key)" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "font-semibold", children: dataKey }) }),
4549
4676
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-1", children: [
@@ -4600,7 +4727,7 @@ var useFetchHashFieldExpires = ({
4600
4727
  dataKey,
4601
4728
  fields
4602
4729
  }) => {
4603
- const { redis } = useDatabrowser();
4730
+ const { redis } = useRedis();
4604
4731
  return _reactquery.useQuery.call(void 0, {
4605
4732
  queryKey: [FETCH_HASH_FIELD_TTLS_QUERY_KEY, dataKey, fields],
4606
4733
  queryFn: async () => {
@@ -4631,7 +4758,7 @@ var useFetchHashFieldExpires = ({
4631
4758
  // src/components/databrowser/hooks/use-set-hash-ttl.ts
4632
4759
 
4633
4760
  var useSetHashTTL = () => {
4634
- const { redis } = useDatabrowser();
4761
+ const { redis } = useRedis();
4635
4762
  return _reactquery.useMutation.call(void 0, {
4636
4763
  mutationFn: async ({
4637
4764
  dataKey,
@@ -4653,7 +4780,7 @@ var useSetHashTTL = () => {
4653
4780
  var HashFieldTTLBadge = ({ dataKey, field }) => {
4654
4781
  const { data } = useFetchHashFieldExpires({ dataKey, fields: [field] });
4655
4782
  const { mutate: setTTL, isPending } = useSetHashTTL();
4656
- const expireAt = _optionalChain([data, 'optionalAccess', _25 => _25[field]]);
4783
+ const expireAt = _optionalChain([data, 'optionalAccess', _35 => _35[field]]);
4657
4784
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
4658
4785
  TTLBadge,
4659
4786
  {
@@ -4742,7 +4869,7 @@ var CustomEditor = ({
4742
4869
  if (!monaco || !editorRef.current) {
4743
4870
  return;
4744
4871
  }
4745
- _optionalChain([monaco, 'optionalAccess', _26 => _26.editor, 'access', _27 => _27.setModelLanguage, 'call', _28 => _28(editorRef.current.getModel(), language)]);
4872
+ _optionalChain([monaco, 'optionalAccess', _36 => _36.editor, 'access', _37 => _37.setModelLanguage, 'call', _38 => _38(editorRef.current.getModel(), language)]);
4746
4873
  }, [monaco, language]);
4747
4874
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
4748
4875
  "div",
@@ -4877,7 +5004,7 @@ var ListEditDisplay = ({
4877
5004
  type,
4878
5005
  item
4879
5006
  }) => {
4880
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "grow rounded-md bg-zinc-100 p-3", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListEditForm, { item, type, dataKey }, item.key) });
5007
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "grow rounded-md bg-zinc-100", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListEditForm, { item, type, dataKey }, item.key) });
4881
5008
  };
4882
5009
  var ListEditForm = ({
4883
5010
  type,
@@ -4889,7 +5016,7 @@ var ListEditForm = ({
4889
5016
  dataKey
4890
5017
  });
4891
5018
  const findValue = () => {
4892
- for (const page of _nullishCoalesce(_optionalChain([query, 'access', _29 => _29.data, 'optionalAccess', _30 => _30.pages]), () => ( []))) {
5019
+ for (const page of _nullishCoalesce(_optionalChain([query, 'access', _39 => _39.data, 'optionalAccess', _40 => _40.pages]), () => ( []))) {
4893
5020
  const item = page.keys.find((item2) => item2.key === itemKey);
4894
5021
  if (item && "value" in item) return item.value;
4895
5022
  }
@@ -4903,7 +5030,7 @@ var ListEditForm = ({
4903
5030
  }
4904
5031
  });
4905
5032
  const { mutateAsync: editItem, isPending } = useEditListItem();
4906
- const { setSelectedListItem } = useDatabrowserStore();
5033
+ const { setSelectedListItem } = useTab();
4907
5034
  const [keyLabel, valueLabel] = headerLabels[type];
4908
5035
  const onSubmit = form.handleSubmit(async ({ key, value }) => {
4909
5036
  await editItem({
@@ -5035,7 +5162,7 @@ var HashFieldTTLInfo = ({
5035
5162
  fields
5036
5163
  }) => {
5037
5164
  const { data } = useFetchHashFieldExpires({ dataKey, fields });
5038
- const expireAt = _optionalChain([data, 'optionalAccess', _31 => _31[field]]);
5165
+ const expireAt = _optionalChain([data, 'optionalAccess', _41 => _41[field]]);
5039
5166
  const [ttl, setTTL] = _react.useState.call(void 0, () => calculateTTL(expireAt));
5040
5167
  _react.useEffect.call(void 0, () => {
5041
5168
  setTTL(calculateTTL(expireAt));
@@ -5058,12 +5185,12 @@ var headerLabels = {
5058
5185
  set: ["Value", ""]
5059
5186
  };
5060
5187
  var ListDisplay = ({ dataKey, type }) => {
5061
- const { selectedListItem } = useDatabrowserStore();
5188
+ const { selectedListItem } = useTab();
5062
5189
  const query = useFetchListItems({ dataKey, type });
5063
5190
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full flex-col gap-2", children: [
5064
5191
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DisplayHeader, { dataKey, type }),
5065
5192
  selectedListItem && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListEditDisplay, { dataKey, type, item: selectedListItem }),
5066
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: cn("min-h-0 grow", selectedListItem && "hidden"), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfiniteScroll, { query, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "pr-3", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "table", { className: "w-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ItemContextMenu, { dataKey, type, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "tbody", { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListItems, { dataKey, type, query }) }) }) }) }) }) })
5193
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: cn("min-h-0 grow", selectedListItem && "hidden"), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfiniteScroll, { query, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "table", { className: "w-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ItemContextMenu, { dataKey, type, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "tbody", { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListItems, { dataKey, type, query }) }) }) }) }) })
5067
5194
  ] });
5068
5195
  };
5069
5196
  var ListItems = ({
@@ -5071,8 +5198,8 @@ var ListItems = ({
5071
5198
  type,
5072
5199
  dataKey
5073
5200
  }) => {
5074
- const { setSelectedListItem } = useDatabrowserStore();
5075
- const keys = _react.useMemo.call(void 0, () => _nullishCoalesce(_optionalChain([query, 'access', _32 => _32.data, 'optionalAccess', _33 => _33.pages, 'access', _34 => _34.flatMap, 'call', _35 => _35((page) => page.keys)]), () => ( [])), [query.data]);
5201
+ const { setSelectedListItem } = useTab();
5202
+ const keys = _react.useMemo.call(void 0, () => _nullishCoalesce(_optionalChain([query, 'access', _42 => _42.data, 'optionalAccess', _43 => _43.pages, 'access', _44 => _44.flatMap, 'call', _45 => _45((page) => page.keys)]), () => ( [])), [query.data]);
5076
5203
  const fields = _react.useMemo.call(void 0, () => keys.map((key) => key.key), [keys]);
5077
5204
  const { mutate: editItem } = useEditListItem();
5078
5205
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: keys.map(({ key, value }, i) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
@@ -5083,7 +5210,7 @@ var ListItems = ({
5083
5210
  onClick: () => {
5084
5211
  setSelectedListItem({ key });
5085
5212
  },
5086
- className: cn("h-10 border-b border-b-zinc-100 hover:bg-zinc-100 "),
5213
+ className: cn("h-10 border-b border-b-zinc-100 transition-colors hover:bg-zinc-100"),
5087
5214
  children: [
5088
5215
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5089
5216
  "td",
@@ -5154,13 +5281,7 @@ var EditorDisplay = ({ dataKey, type }) => {
5154
5281
  const { data } = useFetchSimpleKey(dataKey, type);
5155
5282
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full w-full flex-col gap-2", children: [
5156
5283
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DisplayHeader, { dataKey, type, content: _nullishCoalesce(data, () => ( void 0)) }),
5157
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5158
- "div",
5159
- {
5160
- className: "flex h-full grow flex-col gap-2\n rounded-md bg-zinc-100 p-3",
5161
- children: data === void 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Spinner, { isLoadingText: "", isLoading: true }) : data === null ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditorDisplayForm, { dataKey, type, data }, dataKey)
5162
- }
5163
- )
5284
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-full grow flex-col gap-2 rounded-md bg-zinc-100", children: data === void 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Spinner, { isLoadingText: "", isLoading: true }) : data === null ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditorDisplayForm, { dataKey, type, data }, dataKey) })
5164
5285
  ] });
5165
5286
  };
5166
5287
  var EditorDisplayForm = ({
@@ -5204,10 +5325,10 @@ var EditorDisplayForm = ({
5204
5325
  // src/components/databrowser/components/display/index.tsx
5205
5326
 
5206
5327
  var DataDisplay = () => {
5207
- const { selectedKey } = useDatabrowserStore();
5328
+ const { selectedKey } = useTab();
5208
5329
  const { query } = useKeys();
5209
5330
  const type = useKeyType(selectedKey);
5210
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full rounded-xl border bg-white p-1", children: !selectedKey ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", {}) : !type ? query.isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: type === "string" || type === "json" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListDisplay, { dataKey: selectedKey, type }) }) });
5331
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full p-4", children: !selectedKey ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", {}) : !type ? query.isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: type === "string" || type === "json" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListDisplay, { dataKey: selectedKey, type }) }) });
5211
5332
  };
5212
5333
 
5213
5334
  // src/components/databrowser/components/sidebar/index.tsx
@@ -5323,7 +5444,7 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
5323
5444
  // src/components/databrowser/components/add-key-modal.tsx
5324
5445
 
5325
5446
  function AddKeyModal() {
5326
- const { setSelectedKey } = useDatabrowserStore();
5447
+ const { setSelectedKey } = useTab();
5327
5448
  const [open, setOpen] = _react.useState.call(void 0, false);
5328
5449
  const { mutateAsync: addKey, isPending } = useAddKey();
5329
5450
  const { control, handleSubmit, formState, reset } = _reacthookform.useForm.call(void 0, {
@@ -5337,7 +5458,7 @@ function AddKeyModal() {
5337
5458
  setSelectedKey(key);
5338
5459
  setOpen(false);
5339
5460
  setTimeout(() => {
5340
- _optionalChain([window, 'access', _36 => _36.document, 'access', _37 => _37.querySelector, 'call', _38 => _38(`[data-key="${key}"]`), 'optionalAccess', _39 => _39.scrollIntoView, 'call', _40 => _40({
5461
+ _optionalChain([window, 'access', _46 => _46.document, 'access', _47 => _47.querySelector, 'call', _48 => _48(`[data-key="${key}"]`), 'optionalAccess', _49 => _49.scrollIntoView, 'call', _50 => _50({
5341
5462
  behavior: "smooth",
5342
5463
  block: "start",
5343
5464
  inline: "nearest"
@@ -5382,7 +5503,7 @@ function AddKeyModal() {
5382
5503
  }
5383
5504
  )
5384
5505
  ] }),
5385
- formState.errors.key && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "mb-3 mt-2 text-xs text-red-500", children: _optionalChain([formState, 'access', _41 => _41.errors, 'access', _42 => _42.key, 'optionalAccess', _43 => _43.message]) }),
5506
+ formState.errors.key && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "mb-3 mt-2 text-xs text-red-500", children: _optionalChain([formState, 'access', _51 => _51.errors, 'access', _52 => _52.key, 'optionalAccess', _53 => _53.message]) }),
5386
5507
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "mt-2 text-xs text-zinc-500", children: "After creating the key, you can edit the value" }),
5387
5508
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "mt-6 flex justify-end gap-2", children: [
5388
5509
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -5476,7 +5597,7 @@ var SidebarContextMenu = ({ children }) => {
5476
5597
 
5477
5598
  var KeysList = () => {
5478
5599
  const { keys } = useKeys();
5479
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "pr-3", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, SidebarContextMenu, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: keys.map((data, i) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KeyItem, { nextKey: _nullishCoalesce(_optionalChain([keys, 'access', _44 => _44.at, 'call', _45 => _45(i + 1), 'optionalAccess', _46 => _46[0]]), () => ( "")), data }, data[0])) }) }) });
5600
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, SidebarContextMenu, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: keys.map((data, i) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KeyItem, { nextKey: _nullishCoalesce(_optionalChain([keys, 'access', _54 => _54.at, 'call', _55 => _55(i + 1), 'optionalAccess', _56 => _56[0]]), () => ( "")), data }, data[0])) }) });
5480
5601
  };
5481
5602
  var keyStyles = {
5482
5603
  string: "border-sky-400 !bg-sky-50 text-sky-900",
@@ -5488,7 +5609,7 @@ var keyStyles = {
5488
5609
  stream: "border-green-400 !bg-green-50 text-green-900"
5489
5610
  };
5490
5611
  var KeyItem = ({ data, nextKey }) => {
5491
- const { selectedKey, setSelectedKey } = useDatabrowserStore();
5612
+ const { selectedKey, setSelectedKey } = useTab();
5492
5613
  const [dataKey, dataType] = data;
5493
5614
  const isKeySelected = selectedKey === dataKey;
5494
5615
  const isNextKeySelected = selectedKey === nextKey;
@@ -5498,7 +5619,7 @@ var KeyItem = ({ data, nextKey }) => {
5498
5619
  "data-key": dataKey,
5499
5620
  variant: isKeySelected ? "default" : "ghost",
5500
5621
  className: cn(
5501
- "relative flex h-10 w-full items-center justify-start gap-2 px-3 py-0 ",
5622
+ "relative flex h-10 w-full items-center justify-start gap-2 px-3 py-0 !ring-0 focus-visible:bg-zinc-50",
5502
5623
  "select-none border border-transparent text-left",
5503
5624
  isKeySelected && "shadow-sm",
5504
5625
  isKeySelected && keyStyles[dataType]
@@ -5517,30 +5638,106 @@ var KeyItem = ({ data, nextKey }) => {
5517
5638
 
5518
5639
 
5519
5640
 
5641
+ var dedupeSearchHistory = (history) => {
5642
+ const seen = /* @__PURE__ */ new Set();
5643
+ return history.filter((item) => {
5644
+ if (!item || seen.has(item)) return false;
5645
+ seen.add(item);
5646
+ return true;
5647
+ });
5648
+ };
5520
5649
  var SearchInput = () => {
5521
- const { setSearchKey, search } = useDatabrowserStore();
5650
+ const { setSearchKey, search } = useTab();
5651
+ const { searchHistory, addSearchHistory } = useDatabrowserStore();
5522
5652
  const [state, setState] = _react.useState.call(void 0, search.key);
5523
- const submit = (value) => {
5653
+ const [isFocus, setIsFocus] = _react.useState.call(void 0, false);
5654
+ const [focusedIndex, setFocusedIndex] = _react.useState.call(void 0, -1);
5655
+ const inputRef = _react.useRef.call(void 0, null);
5656
+ const historyItemRefs = _react.useRef.call(void 0, []);
5657
+ const handleSubmit = (value) => {
5524
5658
  if (value.trim() !== "" && !value.includes("*")) value = `${value}*`;
5659
+ addSearchHistory(value);
5525
5660
  setSearchKey(value);
5526
5661
  setState(value);
5527
5662
  };
5528
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "relative grow", children: [
5529
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5530
- Input,
5531
- {
5532
- placeholder: "Search",
5533
- className: "rounded-l-none border-zinc-300 font-normal",
5534
- onKeyDown: (e) => {
5535
- if (e.key === "Enter") submit(e.currentTarget.value);
5536
- },
5537
- onChange: (e) => {
5538
- setState(e.currentTarget.value);
5539
- if (e.currentTarget.value.trim() === "") submit("");
5540
- },
5541
- value: state
5663
+ const filteredHistory = dedupeSearchHistory(
5664
+ searchHistory.filter((item) => item.includes(state) && item !== state)
5665
+ ).slice(0, 5).map((item) => item.endsWith("*") ? item.slice(0, -1) : item);
5666
+ _react.useEffect.call(void 0, () => {
5667
+ setFocusedIndex(-1);
5668
+ }, [filteredHistory.length]);
5669
+ const handleKeyDown = (e) => {
5670
+ if (e.key === "Enter") {
5671
+ const text = focusedIndex >= 0 && focusedIndex < filteredHistory.length ? filteredHistory[focusedIndex] : e.currentTarget.value;
5672
+ handleSubmit(text);
5673
+ } else if (e.key === "Escape") {
5674
+ setState("");
5675
+ setFocusedIndex(-1);
5676
+ _optionalChain([inputRef, 'access', _57 => _57.current, 'optionalAccess', _58 => _58.blur, 'call', _59 => _59()]);
5677
+ } else if (e.key === "ArrowDown" || e.key === "Tab" && !e.shiftKey) {
5678
+ e.preventDefault();
5679
+ if (focusedIndex < filteredHistory.length - 1) {
5680
+ setFocusedIndex(focusedIndex + 1);
5681
+ } else if (filteredHistory.length > 0) {
5682
+ setFocusedIndex(0);
5542
5683
  }
5543
- ),
5684
+ } else if (e.key === "ArrowUp" || e.key === "Tab" && e.shiftKey) {
5685
+ e.preventDefault();
5686
+ if (focusedIndex > 0) {
5687
+ setFocusedIndex(focusedIndex - 1);
5688
+ } else if (filteredHistory.length > 0 && focusedIndex === 0) {
5689
+ setFocusedIndex(-1);
5690
+ _optionalChain([inputRef, 'access', _60 => _60.current, 'optionalAccess', _61 => _61.focus, 'call', _62 => _62()]);
5691
+ } else if (filteredHistory.length > 0) {
5692
+ setFocusedIndex(filteredHistory.length - 1);
5693
+ }
5694
+ }
5695
+ };
5696
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "relative grow", children: [
5697
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Popover, { open: isFocus && filteredHistory.length > 0, children: [
5698
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PopoverTrigger, { asChild: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5699
+ Input,
5700
+ {
5701
+ ref: inputRef,
5702
+ placeholder: "Search",
5703
+ className: "rounded-l-none border-zinc-300 font-normal",
5704
+ onKeyDown: handleKeyDown,
5705
+ onChange: (e) => {
5706
+ setState(e.currentTarget.value);
5707
+ if (e.currentTarget.value.trim() === "") handleSubmit("");
5708
+ },
5709
+ value: state,
5710
+ onFocus: () => {
5711
+ setIsFocus(true);
5712
+ setFocusedIndex(-1);
5713
+ },
5714
+ onBlur: () => setIsFocus(false)
5715
+ }
5716
+ ) }) }),
5717
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5718
+ PopoverContent,
5719
+ {
5720
+ className: "w-[--radix-popover-trigger-width] divide-y px-3 py-2 text-[13px] text-zinc-900",
5721
+ autoFocus: false,
5722
+ onOpenAutoFocus: (e) => {
5723
+ e.preventDefault();
5724
+ e.stopPropagation();
5725
+ },
5726
+ children: filteredHistory.map((item, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full py-[3px]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5727
+ "button",
5728
+ {
5729
+ ref: (el) => {
5730
+ historyItemRefs.current[index] = el;
5731
+ },
5732
+ onClick: () => handleSubmit(item),
5733
+ onMouseEnter: () => setFocusedIndex(index),
5734
+ className: `block w-full rounded-sm p-1 text-left transition-colors ${focusedIndex === index ? "bg-zinc-100" : "hover:bg-zinc-100"}`,
5735
+ children: item
5736
+ }
5737
+ ) }, item))
5738
+ }
5739
+ )
5740
+ ] }),
5544
5741
  state && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
5545
5742
  Button,
5546
5743
  {
@@ -5557,7 +5754,8 @@ var SearchInput = () => {
5557
5754
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "sr-only", children: "Clear" })
5558
5755
  ]
5559
5756
  }
5560
- )
5757
+ ),
5758
+ " "
5561
5759
  ] });
5562
5760
  };
5563
5761
 
@@ -5573,7 +5771,7 @@ var LoadingSkeleton = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div",
5573
5771
 
5574
5772
  var ALL_TYPES_KEY = "all";
5575
5773
  function DataTypeSelector() {
5576
- const { search, setSearchType } = useDatabrowserStore();
5774
+ const { search, setSearchType } = useTab();
5577
5775
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
5578
5776
  Select,
5579
5777
  {
@@ -5599,8 +5797,8 @@ function DataTypeSelector() {
5599
5797
 
5600
5798
  function Sidebar() {
5601
5799
  const { keys, query } = useKeys();
5602
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full flex-col gap-2 rounded-xl border bg-white p-1", children: [
5603
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
5800
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full flex-col gap-2 p-4", children: [
5801
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "rounded-lg bg-zinc-100", children: [
5604
5802
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-10 items-center justify-between pl-1", children: [
5605
5803
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DisplayDbSize, {}),
5606
5804
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex gap-1", children: [
@@ -5638,19 +5836,15 @@ function Sidebar() {
5638
5836
  ] }),
5639
5837
  query.isLoading && keys.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, LoadingSkeleton, {}) : keys.length > 0 ? (
5640
5838
  // Infinite scroll already has a loader at the bottom
5641
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfiniteScroll, { query, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KeysList, {}) })
5839
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfiniteScroll, { query, roundedInherit: false, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KeysList, {}) })
5642
5840
  ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Empty, {})
5643
5841
  ] });
5644
5842
  }
5645
5843
 
5646
- // src/components/databrowser/index.tsx
5844
+ // src/components/databrowser/components/databrowser-instance.tsx
5647
5845
 
5648
- var RedisBrowser = ({ token, url }) => {
5649
- const credentials = _react.useMemo.call(void 0, () => ({ token, url }), [token, url]);
5650
- _react.useEffect.call(void 0, () => {
5651
- queryClient.resetQueries();
5652
- }, [credentials.url]);
5653
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactquery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacttooltip.TooltipProvider, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserProvider, { redisCredentials: credentials, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KeysProvider, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "ups-db", style: { height: "100%" }, children: [
5846
+ var DatabrowserInstance = ({ hidden }) => {
5847
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KeysProvider, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: cn("h-full rounded-md bg-zinc-100", hidden && "hidden"), children: [
5654
5848
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
5655
5849
  _reactresizablepanels.PanelGroup,
5656
5850
  {
@@ -5659,21 +5853,96 @@ var RedisBrowser = ({ token, url }) => {
5659
5853
  className: "h-full w-full gap-0.5 text-sm antialiased",
5660
5854
  children: [
5661
5855
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactresizablepanels.Panel, { defaultSize: 30, minSize: 30, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Sidebar, {}) }),
5662
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactresizablepanels.PanelResizeHandle, { className: "h-fullm flex w-1.5 items-center justify-center rounded-full hover:bg-zinc-300/20", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5663
- _iconsreact.IconDotsVertical,
5664
- {
5665
- size: 16,
5666
- stroke: 1,
5667
- className: "pointer-events-none shrink-0 opacity-20"
5668
- }
5669
- ) }),
5856
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactresizablepanels.PanelResizeHandle, { className: "group flex h-full w-1.5 justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full border-r border-dashed border-zinc-200 transition-colors group-hover:border-zinc-300" }) }),
5670
5857
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactresizablepanels.Panel, { minSize: 40, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DataDisplay, {}) })
5671
5858
  ]
5672
5859
  }
5673
5860
  ),
5674
5861
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Toaster, {})
5862
+ ] }) });
5863
+ };
5864
+
5865
+ // src/components/databrowser/components/databrowser-tabs.tsx
5866
+
5867
+
5868
+ // src/components/databrowser/components/tab-type-icon.tsx
5869
+
5870
+ function TabTypeIcon({ selectedKey }) {
5871
+ const { data: keyType, isLoading } = useFetchKeyType(selectedKey);
5872
+ if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Skeleton, { className: "h-5 w-5 rounded" });
5873
+ if (!keyType || keyType === "none") return;
5874
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TypeTag, { variant: keyType, type: "icon" });
5875
+ }
5876
+
5877
+ // src/components/databrowser/components/databrowser-tabs.tsx
5878
+
5879
+ var Tab = ({ id }) => {
5880
+ const { selectTab, selectedTab, tabs, removeTab } = useDatabrowserStore();
5881
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
5882
+ "div",
5883
+ {
5884
+ onClick: () => selectTab(id),
5885
+ className: cn(
5886
+ "flex h-9 translate-y-[1px] cursor-pointer items-center gap-2 rounded-t-lg border border-zinc-200 px-3 text-[13px] transition-colors",
5887
+ id === selectedTab ? "border-b-white bg-white text-zinc-900" : "bg-zinc-100 hover:bg-zinc-50"
5888
+ ),
5889
+ children: [
5890
+ tabs[id].selectedKey ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
5891
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TabTypeIcon, { selectedKey: tabs[id].selectedKey }),
5892
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "max-w-32 truncate", children: tabs[id].selectedKey })
5893
+ ] }) : "New Tab",
5894
+ Object.keys(tabs).length > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5895
+ "button",
5896
+ {
5897
+ onClick: (e) => {
5898
+ e.stopPropagation();
5899
+ removeTab(id);
5900
+ },
5901
+ className: "p-1 text-zinc-300 transition-colors hover:text-zinc-500",
5902
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _iconsreact.IconX, { size: 16 })
5903
+ }
5904
+ )
5905
+ ]
5906
+ }
5907
+ );
5908
+ };
5909
+ var DatabrowserTabs = () => {
5910
+ const { tabs, addTab } = useDatabrowserStore();
5911
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "mb-2 flex items-center gap-1 border-b border-zinc-200", children: [
5912
+ Object.keys(tabs).map((id) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Tab, { id }, id)),
5913
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
5914
+ Button,
5915
+ {
5916
+ variant: "secondary",
5917
+ size: "icon-sm",
5918
+ onClick: addTab,
5919
+ className: "mr-1 flex-shrink-0",
5920
+ title: "Add new tab",
5921
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _iconsreact.IconPlus, { className: "text-zinc-500", size: 16 })
5922
+ }
5923
+ )
5924
+ ] });
5925
+ };
5926
+
5927
+ // src/components/databrowser/index.tsx
5928
+
5929
+ var RedisBrowser = ({ token, url }) => {
5930
+ const credentials = _react.useMemo.call(void 0, () => ({ token, url }), [token, url]);
5931
+ _react.useEffect.call(void 0, () => {
5932
+ queryClient.resetQueries();
5933
+ }, [credentials.url]);
5934
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactquery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, RedisProvider, { redisCredentials: credentials, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserProvider, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacttooltip.TooltipProvider, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "ups-db", style: { height: "100%" }, children: [
5935
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserTabs, {}),
5936
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserInstances, {})
5675
5937
  ] }) }) }) }) });
5676
5938
  };
5939
+ var DatabrowserInstances = () => {
5940
+ const { tabs, selectedTab, addTab } = useDatabrowserStore();
5941
+ _react.useEffect.call(void 0, () => {
5942
+ if (Object.keys(tabs).length === 0) addTab();
5943
+ }, [tabs]);
5944
+ return Object.entries(tabs).map(([id]) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TabIdProvider, { value: id, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserInstance, { hidden: id !== selectedTab }) }, id));
5945
+ };
5677
5946
 
5678
5947
 
5679
5948
  exports.RedisBrowser = RedisBrowser;