@keepkit/ui 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,12 +6,297 @@ import {
6
6
  createKeepKit as createCoreKeepKit
7
7
  } from "@keepkit/core/react";
8
8
 
9
+ // src/KeepBackup.tsx
10
+ import { useKeepContext } from "@keepkit/core/react";
11
+ import { useRef, useState } from "react";
12
+
13
+ // src/ui-context.tsx
14
+ import { createContext, useContext, useMemo } from "react";
15
+ import { jsx } from "react/jsx-runtime";
16
+ var DEFAULT_LABELS = {
17
+ save: "Save",
18
+ saved: "Saved",
19
+ remove: "Remove",
20
+ loading: "Loading\u2026",
21
+ saving: "Saving\u2026",
22
+ syncing: "Syncing\u2026",
23
+ error: "Something went wrong.",
24
+ allTags: "All",
25
+ tags: "Tags",
26
+ filterTags: "Filter saved items by tag",
27
+ note: "Note",
28
+ saveNote: "Save note",
29
+ noItems: "No saved items.",
30
+ loadingItems: "Loading saved items\u2026",
31
+ errorItems: "Could not load saved items.",
32
+ search: "Search saved items",
33
+ sort: "Sort saved items",
34
+ newest: "Newest first",
35
+ oldest: "Oldest first",
36
+ savedNewest: "Saved newest first",
37
+ savedOldest: "Saved oldest first",
38
+ updatedNewest: "Updated newest first",
39
+ updatedOldest: "Updated oldest first",
40
+ previousPage: "Previous page",
41
+ nextPage: "Next page",
42
+ pagination: "Pagination",
43
+ page: "Page",
44
+ selectItems: "Select saved items",
45
+ selectedCount: "selected",
46
+ deleteSelected: "Delete selected",
47
+ tagsToApply: "Tags to apply",
48
+ applyTags: "Apply tags",
49
+ savedMessage: "Item saved.",
50
+ removedMessage: "Item removed.",
51
+ undo: "Undo",
52
+ undoAvailable: "Item removed. Undo?",
53
+ selectionScope: "Selection scope",
54
+ currentPage: "Current page",
55
+ searchResults: "Current search results",
56
+ allItems: "All saved items",
57
+ statusAvailable: "Available",
58
+ noteSavedMessage: "Note saved.",
59
+ exportData: "Export JSON",
60
+ importData: "Import JSON",
61
+ importMode: "Import mode",
62
+ merge: "Merge",
63
+ replace: "Replace",
64
+ importedCount: "items imported",
65
+ failedCount: "items failed",
66
+ storageQuotaError: "Storage is full. Free space and try again.",
67
+ statusExpired: "Expired",
68
+ statusRemoved: "Removed",
69
+ statusDeleted: "Deleted",
70
+ statusPrivate: "Private",
71
+ statusUnknown: "Unavailable"
72
+ };
73
+ var KEEP_LOCALE_LABELS = {
74
+ ja: {
75
+ save: "\u4FDD\u5B58",
76
+ saved: "\u4FDD\u5B58\u6E08\u307F",
77
+ remove: "\u524A\u9664",
78
+ loading: "\u8AAD\u307F\u8FBC\u307F\u4E2D\u2026",
79
+ saving: "\u4FDD\u5B58\u4E2D\u2026",
80
+ syncing: "\u540C\u671F\u4E2D\u2026",
81
+ allTags: "\u3059\u3079\u3066",
82
+ filterTags: "\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0\u3092\u30BF\u30B0\u3067\u7D5E\u308A\u8FBC\u3080",
83
+ note: "\u30E1\u30E2",
84
+ saveNote: "\u30E1\u30E2\u3092\u4FDD\u5B58",
85
+ loadingItems: "\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D\u2026",
86
+ errorItems: "\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
87
+ noItems: "\u4FDD\u5B58\u3055\u308C\u305F\u30A2\u30A4\u30C6\u30E0\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
88
+ search: "\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0\u3092\u691C\u7D22",
89
+ sort: "\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0\u3092\u4E26\u3079\u66FF\u3048",
90
+ newest: "\u65B0\u3057\u3044\u9806",
91
+ oldest: "\u53E4\u3044\u9806",
92
+ savedNewest: "\u4FDD\u5B58\u65E5\u6642\u306E\u65B0\u3057\u3044\u9806",
93
+ savedOldest: "\u4FDD\u5B58\u65E5\u6642\u306E\u53E4\u3044\u9806",
94
+ updatedNewest: "\u66F4\u65B0\u65E5\u6642\u306E\u65B0\u3057\u3044\u9806",
95
+ updatedOldest: "\u66F4\u65B0\u65E5\u6642\u306E\u53E4\u3044\u9806",
96
+ previousPage: "\u524D\u306E\u30DA\u30FC\u30B8",
97
+ nextPage: "\u6B21\u306E\u30DA\u30FC\u30B8",
98
+ pagination: "\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3",
99
+ page: "\u30DA\u30FC\u30B8",
100
+ selectItems: "\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0\u3092\u9078\u629E",
101
+ selectedCount: "\u4EF6\u3092\u9078\u629E\u4E2D",
102
+ deleteSelected: "\u9078\u629E\u9805\u76EE\u3092\u524A\u9664",
103
+ undo: "\u5143\u306B\u623B\u3059",
104
+ undoAvailable: "\u30A2\u30A4\u30C6\u30E0\u3092\u524A\u9664\u3057\u307E\u3057\u305F\u3002\u5143\u306B\u623B\u3057\u307E\u3059\u304B\uFF1F",
105
+ selectionScope: "\u9078\u629E\u7BC4\u56F2",
106
+ currentPage: "\u8868\u793A\u4E2D\u306E\u30DA\u30FC\u30B8",
107
+ searchResults: "\u73FE\u5728\u306E\u691C\u7D22\u7D50\u679C\u5168\u4F53",
108
+ allItems: "\u3059\u3079\u3066\u306E\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0",
109
+ tags: "\u30BF\u30B0",
110
+ statusAvailable: "\u5229\u7528\u53EF\u80FD",
111
+ tagsToApply: "\u9069\u7528\u3059\u308B\u30BF\u30B0",
112
+ applyTags: "\u30BF\u30B0\u3092\u9069\u7528"
113
+ },
114
+ ko: {
115
+ save: "\uC800\uC7A5",
116
+ saved: "\uC800\uC7A5\uB428",
117
+ remove: "\uC0AD\uC81C",
118
+ loading: "\uB85C\uB4DC \uC911\u2026",
119
+ saving: "\uC800\uC7A5 \uC911\u2026",
120
+ syncing: "\uB3D9\uAE30\uD654 \uC911\u2026",
121
+ noItems: "\uC800\uC7A5\uB41C \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
122
+ search: "\uC800\uC7A5\uB41C \uD56D\uBAA9 \uAC80\uC0C9",
123
+ sort: "\uC800\uC7A5\uB41C \uD56D\uBAA9 \uC815\uB82C",
124
+ previousPage: "\uC774\uC804 \uD398\uC774\uC9C0",
125
+ nextPage: "\uB2E4\uC74C \uD398\uC774\uC9C0",
126
+ pagination: "\uD398\uC774\uC9C0 \uB9E4\uAE40",
127
+ page: "\uD398\uC774\uC9C0",
128
+ undo: "\uC2E4\uD589 \uCDE8\uC18C",
129
+ undoAvailable: "\uD56D\uBAA9\uC744 \uC0AD\uC81C\uD588\uC2B5\uB2C8\uB2E4. \uC2E4\uD589 \uCDE8\uC18C\uD560\uAE4C\uC694?",
130
+ selectionScope: "\uC120\uD0DD \uBC94\uC704",
131
+ currentPage: "\uD604\uC7AC \uD398\uC774\uC9C0",
132
+ searchResults: "\uD604\uC7AC \uAC80\uC0C9 \uACB0\uACFC \uC804\uCCB4",
133
+ allItems: "\uBAA8\uB4E0 \uC800\uC7A5 \uD56D\uBAA9",
134
+ tags: "\uD0DC\uADF8"
135
+ },
136
+ "zh-CN": {
137
+ save: "\u4FDD\u5B58",
138
+ saved: "\u5DF2\u4FDD\u5B58",
139
+ remove: "\u5220\u9664",
140
+ loading: "\u52A0\u8F7D\u4E2D\u2026",
141
+ saving: "\u4FDD\u5B58\u4E2D\u2026",
142
+ syncing: "\u540C\u6B65\u4E2D\u2026",
143
+ noItems: "\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u9879\u76EE\u3002",
144
+ search: "\u641C\u7D22\u5DF2\u4FDD\u5B58\u9879\u76EE",
145
+ sort: "\u6392\u5E8F\u5DF2\u4FDD\u5B58\u9879\u76EE",
146
+ previousPage: "\u4E0A\u4E00\u9875",
147
+ nextPage: "\u4E0B\u4E00\u9875",
148
+ pagination: "\u5206\u9875",
149
+ page: "\u9875",
150
+ undo: "\u64A4\u9500",
151
+ undoAvailable: "\u9879\u76EE\u5DF2\u5220\u9664\u3002\u8981\u64A4\u9500\u5417\uFF1F",
152
+ selectionScope: "\u9009\u62E9\u8303\u56F4",
153
+ currentPage: "\u5F53\u524D\u9875",
154
+ searchResults: "\u5F53\u524D\u641C\u7D22\u7ED3\u679C",
155
+ allItems: "\u5168\u90E8\u5DF2\u4FDD\u5B58\u9879\u76EE",
156
+ tags: "\u6807\u7B7E"
157
+ },
158
+ "zh-TW": {
159
+ save: "\u5132\u5B58",
160
+ saved: "\u5DF2\u5132\u5B58",
161
+ remove: "\u522A\u9664",
162
+ loading: "\u8F09\u5165\u4E2D\u2026",
163
+ saving: "\u5132\u5B58\u4E2D\u2026",
164
+ syncing: "\u540C\u6B65\u4E2D\u2026",
165
+ noItems: "\u6C92\u6709\u5DF2\u5132\u5B58\u7684\u9805\u76EE\u3002",
166
+ search: "\u641C\u5C0B\u5DF2\u5132\u5B58\u9805\u76EE",
167
+ sort: "\u6392\u5E8F\u5DF2\u5132\u5B58\u9805\u76EE",
168
+ previousPage: "\u4E0A\u4E00\u9801",
169
+ nextPage: "\u4E0B\u4E00\u9801",
170
+ undo: "\u5FA9\u539F",
171
+ undoAvailable: "\u9805\u76EE\u5DF2\u522A\u9664\u3002\u8981\u5FA9\u539F\u55CE\uFF1F",
172
+ selectionScope: "\u9078\u53D6\u7BC4\u570D",
173
+ currentPage: "\u76EE\u524D\u9801\u9762",
174
+ searchResults: "\u76EE\u524D\u641C\u5C0B\u7D50\u679C",
175
+ allItems: "\u6240\u6709\u5DF2\u5132\u5B58\u9805\u76EE",
176
+ tags: "\u6A19\u7C64"
177
+ }
178
+ };
179
+ KEEP_LOCALE_LABELS.zh = KEEP_LOCALE_LABELS["zh-CN"];
180
+ var KeepUiLabelsContext = createContext({ labels: DEFAULT_LABELS });
181
+ function KeepUiProvider({ labels, locale, labelResolver, children }) {
182
+ const value = useMemo(() => {
183
+ const localeKey = locale?.toLowerCase() ?? "en";
184
+ const dictionary = KEEP_LOCALE_LABELS[localeKey] ?? KEEP_LOCALE_LABELS[localeKey.split("-")[0]] ?? {};
185
+ const resolved = { ...DEFAULT_LABELS, ...dictionary, ...labels };
186
+ return { locale, labels: resolved, customLabels: labels, labelResolver };
187
+ }, [labelResolver, labels, locale]);
188
+ return /* @__PURE__ */ jsx(KeepUiLabelsContext.Provider, { value, children });
189
+ }
190
+ function useKeepUiLabels() {
191
+ return useContext(KeepUiLabelsContext);
192
+ }
193
+ function useUiLabel(key, override) {
194
+ const context = useKeepUiLabels();
195
+ return override ?? context.customLabels?.[key] ?? context.labelResolver?.(key, { locale: context.locale }) ?? context.labels[key];
196
+ }
197
+ function getKeepLocaleLabels(locale) {
198
+ const key = locale?.toLowerCase() ?? "en";
199
+ return { ...KEEP_LOCALE_LABELS[key] ?? KEEP_LOCALE_LABELS[key.split("-")[0]] ?? {} };
200
+ }
201
+
202
+ // src/KeepBackup.tsx
203
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
204
+ function KeepBackup({
205
+ filename = "keepkit-backup.json",
206
+ onExport,
207
+ onImported,
208
+ ...props
209
+ }) {
210
+ const context = useKeepContext();
211
+ const exportLabel = useUiLabel("exportData");
212
+ const importLabel = useUiLabel("importData");
213
+ const importModeLabel = useUiLabel("importMode");
214
+ const mergeLabel = useUiLabel("merge");
215
+ const replaceLabel = useUiLabel("replace");
216
+ const importedCountLabel = useUiLabel("importedCount");
217
+ const failedCountLabel = useUiLabel("failedCount");
218
+ const quotaErrorLabel = useUiLabel("storageQuotaError");
219
+ const inputRef = useRef(null);
220
+ const [mode, setMode] = useState("merge");
221
+ const [result, setResult] = useState();
222
+ const [error, setError] = useState();
223
+ async function handleExport() {
224
+ setError(void 0);
225
+ try {
226
+ const data = await context.exportBackup();
227
+ onExport?.(data);
228
+ if (typeof document === "undefined") return;
229
+ const url = URL.createObjectURL(new Blob([data], { type: "application/json" }));
230
+ const anchor = document.createElement("a");
231
+ anchor.href = url;
232
+ anchor.download = filename;
233
+ anchor.click();
234
+ URL.revokeObjectURL(url);
235
+ } catch (cause) {
236
+ setError(cause);
237
+ }
238
+ }
239
+ async function handleImport(event) {
240
+ const file = event.currentTarget.files?.[0];
241
+ event.currentTarget.value = "";
242
+ if (!file) return;
243
+ setError(void 0);
244
+ setResult(void 0);
245
+ try {
246
+ const imported = await context.importBackup(await file.text(), { mode });
247
+ setResult(imported);
248
+ onImported?.(imported);
249
+ } catch (cause) {
250
+ setError(cause);
251
+ }
252
+ }
253
+ return /* @__PURE__ */ jsxs("section", { ...props, "data-state": error ? "error" : result ? "complete" : "idle", children: [
254
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void handleExport(), disabled: context.isMutating, children: exportLabel }),
255
+ /* @__PURE__ */ jsxs("label", { children: [
256
+ importModeLabel,
257
+ /* @__PURE__ */ jsxs("select", { value: mode, onChange: (event) => setMode(event.currentTarget.value), children: [
258
+ /* @__PURE__ */ jsx2("option", { value: "merge", children: mergeLabel }),
259
+ /* @__PURE__ */ jsx2("option", { value: "replace", children: replaceLabel })
260
+ ] })
261
+ ] }),
262
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => inputRef.current?.click(), disabled: context.isMutating, children: importLabel }),
263
+ /* @__PURE__ */ jsx2(
264
+ "input",
265
+ {
266
+ ref: inputRef,
267
+ type: "file",
268
+ accept: "application/json,.json",
269
+ "aria-label": importLabel,
270
+ onChange: (event) => void handleImport(event)
271
+ }
272
+ ),
273
+ result ? /* @__PURE__ */ jsxs("p", { role: "status", children: [
274
+ result.imported,
275
+ " ",
276
+ importedCountLabel,
277
+ "; ",
278
+ result.failed,
279
+ " ",
280
+ failedCountLabel
281
+ ] }) : null,
282
+ error ? /* @__PURE__ */ jsx2("p", { role: "alert", children: isQuotaError(error) ? quotaErrorLabel : getErrorMessage(error) }) : null
283
+ ] });
284
+ }
285
+ function isQuotaError(error) {
286
+ if (error instanceof Error && error.name === "KeepStorageQuotaError") return true;
287
+ if (error && typeof error === "object" && "cause" in error) return isQuotaError(error.cause);
288
+ return false;
289
+ }
290
+ function getErrorMessage(error) {
291
+ return error instanceof Error ? error.message : "Something went wrong.";
292
+ }
293
+
9
294
  // src/KeepBulkActions.tsx
10
295
  import { useKeepList } from "@keepkit/core/react";
11
- import { useState } from "react";
296
+ import { useState as useState2 } from "react";
12
297
 
13
298
  // src/KeepItemCheckbox.tsx
14
- import { jsx } from "react/jsx-runtime";
299
+ import { jsx as jsx3 } from "react/jsx-runtime";
15
300
  function KeepItemCheckbox({
16
301
  item,
17
302
  checked = false,
@@ -22,7 +307,7 @@ function KeepItemCheckbox({
22
307
  }) {
23
308
  const itemLabel = getItemLabel(item) ?? item.id;
24
309
  const accessibleLabel = ariaLabel ?? (typeof label === "string" ? label : itemLabel);
25
- return /* @__PURE__ */ jsx(
310
+ return /* @__PURE__ */ jsx3(
26
311
  "input",
27
312
  {
28
313
  ...props,
@@ -46,7 +331,7 @@ import {
46
331
  cloneElement,
47
332
  isValidElement
48
333
  } from "react";
49
- import { jsx as jsx2 } from "react/jsx-runtime";
334
+ import { jsx as jsx4 } from "react/jsx-runtime";
50
335
  function toKeepButtonItem(item) {
51
336
  return {
52
337
  id: item.id,
@@ -75,66 +360,11 @@ function renderRoot(asChild, child, props, body, componentName) {
75
360
  if (!isValidElement(child)) throw new Error(`${componentName} with asChild requires a single React element child.`);
76
361
  return cloneElement(child, { ...props, children: body });
77
362
  }
78
- return /* @__PURE__ */ jsx2("div", { ...props, children: body });
79
- }
80
-
81
- // src/ui-context.tsx
82
- import { createContext, useContext, useMemo } from "react";
83
- import { jsx as jsx3 } from "react/jsx-runtime";
84
- var DEFAULT_LABELS = {
85
- save: "Save",
86
- saved: "Saved",
87
- remove: "Remove",
88
- loading: "Loading\u2026",
89
- saving: "Saving\u2026",
90
- syncing: "Syncing\u2026",
91
- error: "Something went wrong.",
92
- allTags: "All",
93
- filterTags: "Filter saved items by tag",
94
- note: "Note",
95
- saveNote: "Save note",
96
- noItems: "No saved items.",
97
- loadingItems: "Loading saved items\u2026",
98
- errorItems: "Could not load saved items.",
99
- search: "Search saved items",
100
- sort: "Sort saved items",
101
- newest: "Newest first",
102
- oldest: "Oldest first",
103
- savedNewest: "Saved newest first",
104
- savedOldest: "Saved oldest first",
105
- updatedNewest: "Updated newest first",
106
- updatedOldest: "Updated oldest first",
107
- previousPage: "Previous page",
108
- nextPage: "Next page",
109
- pagination: "Pagination",
110
- page: "Page",
111
- selectItems: "Select saved items",
112
- selectedCount: "selected",
113
- deleteSelected: "Delete selected",
114
- tagsToApply: "Tags to apply",
115
- applyTags: "Apply tags",
116
- savedMessage: "Item saved.",
117
- removedMessage: "Item removed.",
118
- noteSavedMessage: "Note saved."
119
- };
120
- var KeepUiLabelsContext = createContext({ labels: DEFAULT_LABELS });
121
- function KeepUiProvider({ labels, locale, labelResolver, children }) {
122
- const value = useMemo(() => {
123
- const resolved = { ...DEFAULT_LABELS, ...labels };
124
- return { locale, labels: resolved, labelResolver };
125
- }, [labelResolver, labels, locale]);
126
- return /* @__PURE__ */ jsx3(KeepUiLabelsContext.Provider, { value, children });
127
- }
128
- function useKeepUiLabels() {
129
- return useContext(KeepUiLabelsContext);
130
- }
131
- function useUiLabel(key, override) {
132
- const context = useKeepUiLabels();
133
- return override ?? context.labelResolver?.(key, { locale: context.locale }) ?? context.labels[key];
363
+ return /* @__PURE__ */ jsx4("div", { ...props, children: body });
134
364
  }
135
365
 
136
366
  // src/KeepBulkActions.tsx
137
- import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
367
+ import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
138
368
  function isAllSelected(items, selectedIds) {
139
369
  if (items.length === 0) return false;
140
370
  const selected = new Set(selectedIds);
@@ -156,6 +386,8 @@ function KeepBulkActions({
156
386
  onSelectedIdsChange,
157
387
  renderItem,
158
388
  onCompleted,
389
+ selectionScope: controlledScope,
390
+ onSelectionScopeChange,
159
391
  render,
160
392
  children,
161
393
  ...props
@@ -165,21 +397,34 @@ function KeepBulkActions({
165
397
  const deleteSelectedLabel = useUiLabel("deleteSelected");
166
398
  const tagsLabel = useUiLabel("tagsToApply");
167
399
  const applyTagsLabel = useUiLabel("applyTags");
400
+ const selectionScopeLabel = useUiLabel("selectionScope");
401
+ const currentPageLabel = useUiLabel("currentPage");
402
+ const searchResultsLabel = useUiLabel("searchResults");
403
+ const allItemsLabel = useUiLabel("allItems");
168
404
  const list = useKeepList(query);
169
- const [uncontrolledSelectedIds, setUncontrolledSelectedIds] = useState(defaultSelectedIds);
405
+ const queryList = useKeepList(query ? { ...query, pagination: void 0 } : { pagination: void 0 });
406
+ const allList = useKeepList({ pagination: void 0 });
407
+ const [uncontrolledScope, setUncontrolledScope] = useState2(controlledScope ?? "page");
408
+ const selectionScope = controlledScope ?? uncontrolledScope;
409
+ const setSelectionScope = (scope) => {
410
+ if (controlledScope === void 0) setUncontrolledScope(scope);
411
+ onSelectionScopeChange?.(scope);
412
+ };
413
+ const targetItems = selectionScope === "page" ? list.items : selectionScope === "query" ? queryList.items : allList.items;
414
+ const [uncontrolledSelectedIds, setUncontrolledSelectedIds] = useState2(defaultSelectedIds);
170
415
  const selectedIds = controlledSelectedIds ?? uncontrolledSelectedIds;
171
416
  const selected = new Set(selectedIds);
172
- const [tagsInput, setTagsInput] = useState("");
417
+ const [tagsInput, setTagsInput] = useState2("");
173
418
  const setSelectedIds = (ids) => {
174
419
  if (controlledSelectedIds === void 0) setUncontrolledSelectedIds(ids);
175
420
  onSelectedIdsChange?.(ids);
176
421
  };
177
422
  const toggle = (id) => setSelectedIds(selected.has(id) ? selectedIds.filter((current) => current !== id) : [...selectedIds, id]);
178
- const allSelected = isAllSelected(list.items, selectedIds);
179
- const toggleAll = () => setSelectedIds(toggleSelectAll(list.items, selectedIds));
423
+ const allSelected = isAllSelected(targetItems, selectedIds);
424
+ const toggleAll = () => setSelectedIds(toggleSelectAll(targetItems, selectedIds));
180
425
  const remove = async () => {
181
426
  const ids = [...selectedIds];
182
- await list.removeBatch(ids);
427
+ await list.removeBatchWithUndo(ids);
183
428
  onCompleted?.("remove", ids);
184
429
  setSelectedIds([]);
185
430
  };
@@ -190,7 +435,7 @@ function KeepBulkActions({
190
435
  setSelectedIds([]);
191
436
  };
192
437
  const state = {
193
- items: list.items,
438
+ items: targetItems,
194
439
  selectedIds,
195
440
  selectedCount: selectedIds.length,
196
441
  isAllSelected: allSelected,
@@ -202,19 +447,36 @@ function KeepBulkActions({
202
447
  toggleAll,
203
448
  remove,
204
449
  updateTags,
205
- isMutating: list.isMutating
450
+ isMutating: list.isMutating,
451
+ selectionScope,
452
+ setSelectionScope
206
453
  };
207
- const body = render ? render(state) : typeof children === "function" ? children(state) : children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
208
- /* @__PURE__ */ jsxs("fieldset", { children: [
209
- /* @__PURE__ */ jsx4("legend", { children: selectItemsLabel }),
210
- /* @__PURE__ */ jsxs("label", { children: [
211
- /* @__PURE__ */ jsx4("input", { type: "checkbox", checked: allSelected, onChange: toggleAll, "aria-label": selectItemsLabel }),
454
+ const body = render ? render(state) : typeof children === "function" ? children(state) : children ?? /* @__PURE__ */ jsxs2(Fragment, { children: [
455
+ /* @__PURE__ */ jsxs2("fieldset", { children: [
456
+ /* @__PURE__ */ jsx5("legend", { children: selectItemsLabel }),
457
+ /* @__PURE__ */ jsxs2("label", { children: [
458
+ selectionScopeLabel,
459
+ /* @__PURE__ */ jsxs2(
460
+ "select",
461
+ {
462
+ value: selectionScope,
463
+ onChange: (event) => setSelectionScope(event.currentTarget.value),
464
+ children: [
465
+ /* @__PURE__ */ jsx5("option", { value: "page", children: currentPageLabel }),
466
+ /* @__PURE__ */ jsx5("option", { value: "query", children: searchResultsLabel }),
467
+ /* @__PURE__ */ jsx5("option", { value: "all", children: allItemsLabel })
468
+ ]
469
+ }
470
+ )
471
+ ] }),
472
+ /* @__PURE__ */ jsxs2("label", { children: [
473
+ /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: allSelected, onChange: toggleAll, "aria-label": selectItemsLabel }),
212
474
  selectedIds.length,
213
475
  " ",
214
476
  selectedCountLabel
215
477
  ] }),
216
- list.items.map((item) => /* @__PURE__ */ jsxs("span", { children: [
217
- /* @__PURE__ */ jsx4(
478
+ targetItems.map((item) => /* @__PURE__ */ jsxs2("span", { children: [
479
+ /* @__PURE__ */ jsx5(
218
480
  KeepItemCheckbox,
219
481
  {
220
482
  item,
@@ -225,12 +487,12 @@ function KeepBulkActions({
225
487
  renderItem ? renderItem(item, selected.has(item.id)) : getMetaTitle(item.meta) ?? item.id
226
488
  ] }, item.id))
227
489
  ] }),
228
- /* @__PURE__ */ jsx4("button", { type: "button", onClick: () => void remove(), disabled: selectedIds.length === 0 || list.isMutating, children: deleteSelectedLabel }),
229
- /* @__PURE__ */ jsxs("label", { children: [
490
+ /* @__PURE__ */ jsx5("button", { type: "button", onClick: () => void remove(), disabled: selectedIds.length === 0 || list.isMutating, children: deleteSelectedLabel }),
491
+ /* @__PURE__ */ jsxs2("label", { children: [
230
492
  tagsLabel,
231
- /* @__PURE__ */ jsx4("input", { value: tagsInput, onChange: (event) => setTagsInput(event.currentTarget.value) })
493
+ /* @__PURE__ */ jsx5("input", { value: tagsInput, onChange: (event) => setTagsInput(event.currentTarget.value) })
232
494
  ] }),
233
- /* @__PURE__ */ jsx4(
495
+ /* @__PURE__ */ jsx5(
234
496
  "button",
235
497
  {
236
498
  type: "button",
@@ -240,7 +502,7 @@ function KeepBulkActions({
240
502
  }
241
503
  )
242
504
  ] });
243
- return /* @__PURE__ */ jsx4(
505
+ return /* @__PURE__ */ jsx5(
244
506
  "section",
245
507
  {
246
508
  ...props,
@@ -257,7 +519,7 @@ import {
257
519
  KeepButton as CoreKeepButton,
258
520
  useKeepItem
259
521
  } from "@keepkit/core/react";
260
- import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
522
+ import { Fragment as Fragment2, jsx as jsx6 } from "react/jsx-runtime";
261
523
  function KeepButton({ labels, ...props }) {
262
524
  const saveLabel = useUiLabel("save", typeof labels?.unsaved === "string" ? labels.unsaved : void 0);
263
525
  const savedLabel = useUiLabel("saved", typeof labels?.saved === "string" ? labels.saved : void 0);
@@ -280,13 +542,13 @@ function KeepButton({ labels, ...props }) {
280
542
  savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,
281
543
  unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel
282
544
  };
283
- if (!customStateLabel) return /* @__PURE__ */ jsx5(CoreKeepButton, { ...sharedProps });
284
- return /* @__PURE__ */ jsx5(CoreKeepButton, { ...sharedProps, children: (state) => /* @__PURE__ */ jsx5(Fragment2, { children: getStateContent(state) }) });
545
+ if (!customStateLabel) return /* @__PURE__ */ jsx6(CoreKeepButton, { ...sharedProps });
546
+ return /* @__PURE__ */ jsx6(CoreKeepButton, { ...sharedProps, children: (state) => /* @__PURE__ */ jsx6(Fragment2, { children: getStateContent(state) }) });
285
547
  }
286
548
 
287
549
  // src/KeepCollection.tsx
288
550
  import { KeepErrorBoundary as KeepErrorBoundary2, useKeepList as useKeepList4 } from "@keepkit/core/react";
289
- import { useMemo as useMemo3, useState as useState4 } from "react";
551
+ import { useMemo as useMemo3, useState as useState5 } from "react";
290
552
 
291
553
  // src/KeepList.tsx
292
554
  import {
@@ -297,8 +559,10 @@ import { isValidElement as isValidElement3 } from "react";
297
559
 
298
560
  // src/KeepItemCard.tsx
299
561
  import { useKeepItem as useKeepItem2 } from "@keepkit/core/react";
300
- import { isValidElement as isValidElement2 } from "react";
301
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
562
+ import {
563
+ isValidElement as isValidElement2
564
+ } from "react";
565
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
302
566
  function KeepItemCard({
303
567
  item,
304
568
  title,
@@ -306,6 +570,8 @@ function KeepItemCard({
306
570
  getImageProps,
307
571
  imageComponent: ImageComponent,
308
572
  renderImage,
573
+ renderTags,
574
+ showTags = true,
309
575
  imageAlt,
310
576
  render,
311
577
  children,
@@ -315,6 +581,12 @@ function KeepItemCard({
315
581
  showSaveButton = true,
316
582
  saveButtonLabels,
317
583
  asChild = false,
584
+ href: hrefOption,
585
+ onOpen,
586
+ linkTarget = "title",
587
+ linkComponent: LinkComponent,
588
+ linkTargetAttribute,
589
+ linkRel,
318
590
  className,
319
591
  ...rootProps
320
592
  }) {
@@ -327,10 +599,28 @@ function KeepItemCard({
327
599
  isSaved: itemState.isSaved,
328
600
  isMutating: itemState.isMutating,
329
601
  error: itemState.error,
330
- remove: itemState.remove
602
+ remove: itemState.remove,
603
+ status: item.status
331
604
  };
332
605
  const resolvedTitle = typeof title === "function" ? title(item) : title ?? getTitle?.(item) ?? getMetaTitle(item.meta) ?? item.id;
333
606
  const imageProps = getImageProps?.(item, resolvedTitle);
607
+ const href = typeof hrefOption === "function" ? hrefOption(item) : hrefOption;
608
+ const isAvailable = item.status === void 0 || item.status === "available";
609
+ const statusLabelKey = item.status && item.status !== "available" ? getStatusLabelKey(item.status) : "statusUnknown";
610
+ const unavailableLabel = useUiLabel(statusLabelKey);
611
+ const tagsLabel = useUiLabel("tags");
612
+ const statusLabel = item.status && item.status !== "available" ? unavailableLabel : void 0;
613
+ function renderLink(content) {
614
+ if (!href || !isAvailable) return content;
615
+ const linkProps = {
616
+ href,
617
+ target: linkTargetAttribute,
618
+ rel: linkRel,
619
+ onClick: (event) => onOpen?.(item, event),
620
+ children: content
621
+ };
622
+ return LinkComponent ? /* @__PURE__ */ jsx7(LinkComponent, { ...linkProps }) : /* @__PURE__ */ jsx7("a", { ...linkProps });
623
+ }
334
624
  async function handleRemove() {
335
625
  try {
336
626
  await itemState.remove();
@@ -339,10 +629,12 @@ function KeepItemCard({
339
629
  onRemoveError?.(error);
340
630
  }
341
631
  }
342
- const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
343
- imageProps ? renderImage?.({ ...imageProps, alt: imageAlt ?? imageProps.alt }, item) ?? (ImageComponent ? /* @__PURE__ */ jsx6(ImageComponent, { ...imageProps, alt: imageAlt ?? imageProps.alt }) : /* @__PURE__ */ jsx6("img", { ...imageProps, alt: imageAlt ?? imageProps.alt })) : null,
344
- /* @__PURE__ */ jsx6("h3", { children: resolvedTitle }),
345
- showSaveButton ? /* @__PURE__ */ jsx6(
632
+ const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs3(Fragment3, { children: [
633
+ imageProps ? renderImage?.({ ...imageProps, alt: imageAlt ?? imageProps.alt }, item) ?? (ImageComponent ? /* @__PURE__ */ jsx7(ImageComponent, { ...imageProps, alt: imageAlt ?? imageProps.alt }) : /* @__PURE__ */ jsx7("img", { ...imageProps, alt: imageAlt ?? imageProps.alt })) : null,
634
+ /* @__PURE__ */ jsx7("h3", { children: linkTarget === "title" ? renderLink(resolvedTitle) : resolvedTitle }),
635
+ showTags && (item.tags?.length ?? 0) > 0 ? renderTags ? renderTags(item.tags ?? [], item) : /* @__PURE__ */ jsx7("ul", { "aria-label": tagsLabel, children: item.tags?.map((tag) => /* @__PURE__ */ jsx7("li", { children: tag }, tag)) }) : null,
636
+ statusLabel ? /* @__PURE__ */ jsx7("p", { "data-status": item.status, children: statusLabel }) : null,
637
+ showSaveButton ? /* @__PURE__ */ jsx7(
346
638
  KeepButton,
347
639
  {
348
640
  item: toKeepButtonItem(item),
@@ -350,8 +642,9 @@ function KeepItemCard({
350
642
  getAriaLabel: (buttonState) => `${buttonState.isSaved ? removeActionLabel : saveActionLabel} ${String(resolvedTitle)}`
351
643
  }
352
644
  ) : null,
353
- /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => void handleRemove(), disabled: itemState.isMutating, children: removeLabel ?? removeActionLabel })
645
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => void handleRemove(), disabled: itemState.isMutating, children: removeLabel ?? removeActionLabel })
354
646
  ] });
647
+ const linkedBody = linkTarget === "card" ? renderLink(body) : body;
355
648
  return renderRoot(
356
649
  asChild,
357
650
  isValidElement2(children) ? children : void 0,
@@ -360,20 +653,35 @@ function KeepItemCard({
360
653
  className,
361
654
  "aria-busy": itemState.isMutating || rootProps["aria-busy"],
362
655
  "data-state": itemState.isSaved ? "saved" : "unsaved",
656
+ "data-status": item.status ?? "available",
363
657
  "data-loading": itemState.isMutating ? "true" : void 0
364
658
  },
365
- body,
659
+ linkedBody,
366
660
  "KeepItemCard"
367
661
  );
368
662
  }
663
+ function getStatusLabelKey(status) {
664
+ switch (status) {
665
+ case "expired":
666
+ return "statusExpired";
667
+ case "removed":
668
+ return "statusRemoved";
669
+ case "deleted":
670
+ return "statusDeleted";
671
+ case "private":
672
+ return "statusPrivate";
673
+ default:
674
+ return "statusUnknown";
675
+ }
676
+ }
369
677
 
370
678
  // src/KeepList.tsx
371
- import { jsx as jsx7 } from "react/jsx-runtime";
679
+ import { jsx as jsx8 } from "react/jsx-runtime";
372
680
  function KeepList(props) {
373
681
  const { fallback, onBoundaryError, boundaryResetKey, ...listProps } = props;
374
- const content = /* @__PURE__ */ jsx7(KeepListContent, { ...listProps });
682
+ const content = /* @__PURE__ */ jsx8(KeepListContent, { ...listProps });
375
683
  if (fallback === void 0 && onBoundaryError === void 0) return content;
376
- return /* @__PURE__ */ jsx7(KeepErrorBoundary, { fallback, onError: onBoundaryError, resetKey: boundaryResetKey, children: content });
684
+ return /* @__PURE__ */ jsx8(KeepErrorBoundary, { fallback, onError: onBoundaryError, resetKey: boundaryResetKey, children: content });
377
685
  }
378
686
  function KeepListContent({
379
687
  query,
@@ -383,6 +691,7 @@ function KeepListContent({
383
691
  empty,
384
692
  error: errorContent,
385
693
  itemCardProps,
694
+ layout = "list",
386
695
  asChild = false,
387
696
  className,
388
697
  ...rootProps
@@ -397,7 +706,8 @@ function KeepListContent({
397
706
  loading: loading ?? defaultLoading,
398
707
  empty: empty ?? defaultEmpty,
399
708
  error: errorContent ?? defaultError,
400
- itemCardProps
709
+ itemCardProps,
710
+ layout
401
711
  });
402
712
  return renderRoot(
403
713
  asChild,
@@ -425,15 +735,22 @@ function getListBody(state, options) {
425
735
  if (state.isHydrated && state.items.length === 0) return resolveContent(options.empty, state);
426
736
  if (typeof options.children === "function") return options.children(state);
427
737
  if (options.children !== void 0 && !isValidElement3(options.children)) return options.children;
428
- return /* @__PURE__ */ jsx7("ul", { children: state.items.map(
429
- (item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(KeepItemCard, { item, ...options.itemCardProps }) }, item.id)
430
- ) });
738
+ return /* @__PURE__ */ jsx8(
739
+ "ul",
740
+ {
741
+ "data-layout": options.layout,
742
+ style: options.layout === "grid" ? { display: "grid", gap: "1rem", gridTemplateColumns: "repeat(auto-fit, minmax(16rem, 1fr))" } : options.layout === "compact" ? { display: "grid", gap: "0.5rem", gridTemplateColumns: "repeat(auto-fit, minmax(12rem, 1fr))" } : { display: "flex", flexDirection: "column", gap: "1rem" },
743
+ children: state.items.map(
744
+ (item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */ jsx8("li", { children: /* @__PURE__ */ jsx8(KeepItemCard, { item, ...options.itemCardProps }) }, item.id)
745
+ )
746
+ }
747
+ );
431
748
  }
432
749
 
433
750
  // src/KeepTagFilter.tsx
434
751
  import { useKeepList as useKeepList3 } from "@keepkit/core/react";
435
- import { isValidElement as isValidElement4, useCallback, useMemo as useMemo2, useState as useState2 } from "react";
436
- import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
752
+ import { isValidElement as isValidElement4, useCallback, useMemo as useMemo2, useState as useState3 } from "react";
753
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
437
754
  function KeepTagFilter({
438
755
  query,
439
756
  value: controlledValue,
@@ -451,7 +768,7 @@ function KeepTagFilter({
451
768
  }) {
452
769
  const uiAllLabel = useUiLabel("allTags");
453
770
  const uiAriaLabel = useUiLabel("filterTags");
454
- const [uncontrolledValue, setUncontrolledValue] = useState2(defaultValue);
771
+ const [uncontrolledValue, setUncontrolledValue] = useState3(defaultValue);
455
772
  const resolvedValue = controlledValue ?? uncontrolledValue;
456
773
  const list = useKeepList3({
457
774
  ...query,
@@ -470,12 +787,12 @@ function KeepTagFilter({
470
787
  [list.tagCounts, list.tags, resolvedValue, select]
471
788
  );
472
789
  const contentChildren = asChild && isValidElement4(children) ? void 0 : children;
473
- const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs3("fieldset", { children: [
474
- /* @__PURE__ */ jsx8("legend", { children: ariaLabel ?? uiAriaLabel }),
475
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-pressed": resolvedValue === void 0, onClick: () => select(), children: allLabel ?? uiAllLabel }),
476
- list.tags.map((tag) => /* @__PURE__ */ jsxs3("button", { type: "button", "aria-pressed": resolvedValue === tag, onClick: () => select(tag), children: [
790
+ const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs4("fieldset", { children: [
791
+ /* @__PURE__ */ jsx9("legend", { children: ariaLabel ?? uiAriaLabel }),
792
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": resolvedValue === void 0, onClick: () => select(), children: allLabel ?? uiAllLabel }),
793
+ list.tags.map((tag) => /* @__PURE__ */ jsxs4("button", { type: "button", "aria-pressed": resolvedValue === tag, onClick: () => select(tag), children: [
477
794
  renderTag ? renderTag(tag, list.tagCounts[tag] ?? 0, resolvedValue === tag) : tag,
478
- /* @__PURE__ */ jsxs3("span", { children: [
795
+ /* @__PURE__ */ jsxs4("span", { children: [
479
796
  " (",
480
797
  list.tagCounts[tag] ?? 0,
481
798
  ")"
@@ -497,8 +814,8 @@ function KeepTagFilter({
497
814
  }
498
815
 
499
816
  // src/query-controls.tsx
500
- import { useEffect, useState as useState3 } from "react";
501
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
817
+ import { useEffect, useState as useState4 } from "react";
818
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
502
819
  function KeepSearchInput({
503
820
  value: controlledValue,
504
821
  defaultValue = "",
@@ -509,7 +826,7 @@ function KeepSearchInput({
509
826
  ...props
510
827
  }) {
511
828
  const label = useUiLabel("search");
512
- const [uncontrolledValue, setUncontrolledValue] = useState3(defaultValue);
829
+ const [uncontrolledValue, setUncontrolledValue] = useState4(defaultValue);
513
830
  const value = controlledValue ?? uncontrolledValue;
514
831
  useEffect(() => {
515
832
  if (!onValueChange) return;
@@ -520,7 +837,7 @@ function KeepSearchInput({
520
837
  const timer = window.setTimeout(() => onValueChange(value), debounceMs);
521
838
  return () => window.clearTimeout(timer);
522
839
  }, [debounceMs, onValueChange, value]);
523
- return /* @__PURE__ */ jsx9(
840
+ return /* @__PURE__ */ jsx10(
524
841
  "input",
525
842
  {
526
843
  ...props,
@@ -550,15 +867,15 @@ function KeepSortSelect({
550
867
  const updatedOldestLabel = useUiLabel("updatedOldest");
551
868
  const savedNewestLabel = useUiLabel("savedNewest");
552
869
  const savedOldestLabel = useUiLabel("savedOldest");
553
- const [uncontrolledValue, setUncontrolledValue] = useState3(defaultValue);
870
+ const [uncontrolledValue, setUncontrolledValue] = useState4(defaultValue);
554
871
  const value = controlledValue ?? uncontrolledValue;
555
- const options = children ?? /* @__PURE__ */ jsxs4(Fragment4, { children: [
556
- /* @__PURE__ */ jsx9("option", { value: "updatedAt:desc", children: updatedNewestLabel }),
557
- /* @__PURE__ */ jsx9("option", { value: "updatedAt:asc", children: updatedOldestLabel }),
558
- /* @__PURE__ */ jsx9("option", { value: "savedAt:desc", children: savedNewestLabel }),
559
- /* @__PURE__ */ jsx9("option", { value: "savedAt:asc", children: savedOldestLabel })
872
+ const options = children ?? /* @__PURE__ */ jsxs5(Fragment4, { children: [
873
+ /* @__PURE__ */ jsx10("option", { value: "updatedAt:desc", children: updatedNewestLabel }),
874
+ /* @__PURE__ */ jsx10("option", { value: "updatedAt:asc", children: updatedOldestLabel }),
875
+ /* @__PURE__ */ jsx10("option", { value: "savedAt:desc", children: savedNewestLabel }),
876
+ /* @__PURE__ */ jsx10("option", { value: "savedAt:asc", children: savedOldestLabel })
560
877
  ] });
561
- return /* @__PURE__ */ jsx9(
878
+ return /* @__PURE__ */ jsx10(
562
879
  "select",
563
880
  {
564
881
  ...props,
@@ -600,11 +917,11 @@ function KeepPagination({
600
917
  "aria-label": props["aria-label"] ?? paginationLabel,
601
918
  "data-state": pageCount > 1 ? "active" : "idle"
602
919
  };
603
- if (render) return /* @__PURE__ */ jsx9("nav", { ...navProps, children: render({ page: currentPage, pageCount, goToPage }) });
920
+ if (render) return /* @__PURE__ */ jsx10("nav", { ...navProps, children: render({ page: currentPage, pageCount, goToPage }) });
604
921
  const visiblePages = getVisiblePages(currentPage, pageCount, Math.max(1, maxPageButtons));
605
- return /* @__PURE__ */ jsxs4("nav", { ...navProps, children: [
606
- /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => goToPage(currentPage - 1), disabled: currentPage <= 1, children: previousPageLabel }),
607
- visiblePages.map((nextPage) => /* @__PURE__ */ jsx9(
922
+ return /* @__PURE__ */ jsxs5("nav", { ...navProps, children: [
923
+ /* @__PURE__ */ jsx10("button", { type: "button", onClick: () => goToPage(currentPage - 1), disabled: currentPage <= 1, children: previousPageLabel }),
924
+ visiblePages.map((nextPage) => /* @__PURE__ */ jsx10(
608
925
  "button",
609
926
  {
610
927
  type: "button",
@@ -615,7 +932,7 @@ function KeepPagination({
615
932
  },
616
933
  nextPage
617
934
  )),
618
- /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => goToPage(currentPage + 1), disabled: currentPage >= pageCount, children: nextPageLabel })
935
+ /* @__PURE__ */ jsx10("button", { type: "button", onClick: () => goToPage(currentPage + 1), disabled: currentPage >= pageCount, children: nextPageLabel })
619
936
  ] });
620
937
  }
621
938
  function getVisiblePages(currentPage, pageCount, maxPageButtons) {
@@ -625,17 +942,102 @@ function getVisiblePages(currentPage, pageCount, maxPageButtons) {
625
942
  return Array.from({ length: maxPageButtons }, (_, index) => start + index);
626
943
  }
627
944
 
945
+ // src/url-sync.tsx
946
+ import {
947
+ DEFAULT_KEEP_URL_PARAMS,
948
+ decodeKeepListQuery,
949
+ encodeKeepListQuery
950
+ } from "@keepkit/core/core";
951
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
952
+ function createNextPagesRouterAdapter(router) {
953
+ const getUrl = () => router.asPath ?? (typeof window === "undefined" ? "/" : window.location.href);
954
+ return {
955
+ getUrl,
956
+ subscribe: router.events ? (listener) => {
957
+ router.events?.on("routeChangeComplete", listener);
958
+ return () => router.events?.off("routeChangeComplete", listener);
959
+ } : void 0,
960
+ navigate: (url, mode) => {
961
+ void router[mode](url, void 0, { shallow: true });
962
+ }
963
+ };
964
+ }
965
+ function useKeepUrlSync({
966
+ enabled = true,
967
+ query,
968
+ onQueryChange,
969
+ options = {},
970
+ adapter: providedAdapter
971
+ }) {
972
+ const browserAdapterRef = useRef2(getBrowserAdapter());
973
+ const adapter = providedAdapter ?? browserAdapterRef.current;
974
+ const onQueryChangeRef = useRef2(onQueryChange);
975
+ onQueryChangeRef.current = onQueryChange;
976
+ const skipWriteRef = useRef2(true);
977
+ const params = options.params;
978
+ useEffect2(() => {
979
+ if (!enabled) return;
980
+ const read = () => {
981
+ const url = adapter.getUrl();
982
+ const decoded = decodeKeepListQuery(url, { params });
983
+ skipWriteRef.current = true;
984
+ onQueryChangeRef.current((previousQuery) => ({
985
+ ...previousQuery,
986
+ ...decoded.search ? { search: decoded.search } : { search: void 0 },
987
+ ...decoded.tags ? { tags: decoded.tags } : { tags: void 0 },
988
+ ...decoded.sort ? { sort: decoded.sort } : {},
989
+ ...decoded.pagination ? { pagination: { ...previousQuery.pagination, ...decoded.pagination } } : {}
990
+ }));
991
+ };
992
+ read();
993
+ return adapter.subscribe?.(read);
994
+ }, [adapter, enabled, params]);
995
+ useEffect2(() => {
996
+ if (!enabled) return;
997
+ if (skipWriteRef.current) {
998
+ skipWriteRef.current = false;
999
+ return;
1000
+ }
1001
+ const currentUrl = new URL(adapter.getUrl(), "http://keepkit.invalid");
1002
+ const urlParams = { ...DEFAULT_KEEP_URL_PARAMS, ...params };
1003
+ for (const key of Object.values(urlParams)) currentUrl.searchParams.delete(key);
1004
+ const nextParams = encodeKeepListQuery(query, { params });
1005
+ nextParams.forEach((value, key) => {
1006
+ currentUrl.searchParams.append(key, value);
1007
+ });
1008
+ const nextUrl = `${currentUrl.pathname}${currentUrl.search}${currentUrl.hash}`;
1009
+ adapter.navigate(nextUrl, options.history ?? "push");
1010
+ }, [adapter, enabled, options.history, params, query]);
1011
+ }
1012
+ function getBrowserAdapter() {
1013
+ return {
1014
+ getUrl: () => typeof window === "undefined" ? "/" : window.location.href,
1015
+ subscribe: (listener) => {
1016
+ if (typeof window === "undefined") return () => void 0;
1017
+ window.addEventListener("popstate", listener);
1018
+ return () => window.removeEventListener("popstate", listener);
1019
+ },
1020
+ navigate: (url, mode) => {
1021
+ if (typeof window === "undefined") return;
1022
+ window.history[mode === "push" ? "pushState" : "replaceState"]({}, "", url);
1023
+ }
1024
+ };
1025
+ }
1026
+
628
1027
  // src/KeepCollection.tsx
629
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
1028
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
630
1029
  function KeepCollection(props) {
631
1030
  const { fallback, onBoundaryError, boundaryResetKey, ...collectionProps } = props;
632
- const content = /* @__PURE__ */ jsx10(KeepCollectionContent, { ...collectionProps });
1031
+ const content = /* @__PURE__ */ jsx11(KeepCollectionContent, { ...collectionProps });
633
1032
  if (fallback === void 0 && onBoundaryError === void 0) return content;
634
- return /* @__PURE__ */ jsx10(KeepErrorBoundary2, { fallback, onError: onBoundaryError, resetKey: boundaryResetKey, children: content });
1033
+ return /* @__PURE__ */ jsx11(KeepErrorBoundary2, { fallback, onError: onBoundaryError, resetKey: boundaryResetKey, children: content });
635
1034
  }
636
1035
  function KeepCollectionContent({
637
1036
  query = {},
638
1037
  pageSize = 20,
1038
+ layout = "list",
1039
+ urlSync = false,
1040
+ urlAdapter,
639
1041
  features,
640
1042
  renderItem,
641
1043
  itemCardProps,
@@ -653,10 +1055,10 @@ function KeepCollectionContent({
653
1055
  bulkActions: false,
654
1056
  ...features
655
1057
  };
656
- const [searchValue, setSearchValue] = useState4(query.search?.query ?? "");
657
- const [sort, setSort] = useState4(query.sort ?? { by: "updatedAt", direction: "desc" });
658
- const [tag, setTag] = useState4(query.tags?.[0]);
659
- const [page, setPage] = useState4(query.pagination?.page ?? 1);
1058
+ const [searchValue, setSearchValue] = useState5(query.search?.query ?? "");
1059
+ const [sort, setSort] = useState5(query.sort ?? { by: "updatedAt", direction: "desc" });
1060
+ const [tag, setTag] = useState5(query.tags?.[0]);
1061
+ const [page, setPage] = useState5(query.pagination?.page ?? 1);
660
1062
  const resolvedPageSize = query.pagination?.pageSize ?? pageSize;
661
1063
  const resolvedQuery = useMemo3(
662
1064
  () => ({
@@ -668,8 +1070,21 @@ function KeepCollectionContent({
668
1070
  }),
669
1071
  [enabled.pagination, enabled.search, enabled.sort, page, query, resolvedPageSize, searchValue, sort, tag]
670
1072
  );
1073
+ useKeepUrlSync({
1074
+ enabled: Boolean(urlSync),
1075
+ query: resolvedQuery,
1076
+ onQueryChange: (nextOrUpdater) => {
1077
+ const next = typeof nextOrUpdater === "function" ? nextOrUpdater(resolvedQuery) : nextOrUpdater;
1078
+ setSearchValue(next.search?.query ?? "");
1079
+ setSort(next.sort ?? { by: "updatedAt", direction: "desc" });
1080
+ setTag(next.tags?.[0]);
1081
+ setPage(next.pagination?.page ?? 1);
1082
+ },
1083
+ options: typeof urlSync === "object" ? urlSync : {},
1084
+ adapter: urlAdapter
1085
+ });
671
1086
  const list = useKeepList4(resolvedQuery);
672
- return /* @__PURE__ */ jsxs5(
1087
+ return /* @__PURE__ */ jsxs6(
673
1088
  "section",
674
1089
  {
675
1090
  ...rootProps,
@@ -678,8 +1093,8 @@ function KeepCollectionContent({
678
1093
  "data-state": getCollectionState(list),
679
1094
  "data-loading": list.isLoading || list.isMutating ? "true" : void 0,
680
1095
  children: [
681
- /* @__PURE__ */ jsxs5("div", { children: [
682
- enabled.search ? /* @__PURE__ */ jsx10(
1096
+ /* @__PURE__ */ jsxs6("div", { children: [
1097
+ enabled.search ? /* @__PURE__ */ jsx11(
683
1098
  KeepSearchInput,
684
1099
  {
685
1100
  value: searchValue,
@@ -689,7 +1104,7 @@ function KeepCollectionContent({
689
1104
  }
690
1105
  }
691
1106
  ) : null,
692
- enabled.sort ? /* @__PURE__ */ jsx10(
1107
+ enabled.sort ? /* @__PURE__ */ jsx11(
693
1108
  KeepSortSelect,
694
1109
  {
695
1110
  value: sortToValue(sort),
@@ -699,7 +1114,7 @@ function KeepCollectionContent({
699
1114
  }
700
1115
  }
701
1116
  ) : null,
702
- enabled.tagFilter ? /* @__PURE__ */ jsx10(
1117
+ enabled.tagFilter ? /* @__PURE__ */ jsx11(
703
1118
  KeepTagFilter,
704
1119
  {
705
1120
  query,
@@ -711,18 +1126,19 @@ function KeepCollectionContent({
711
1126
  }
712
1127
  ) : null
713
1128
  ] }),
714
- /* @__PURE__ */ jsx10(
1129
+ /* @__PURE__ */ jsx11(
715
1130
  KeepList,
716
1131
  {
717
1132
  query: resolvedQuery,
718
1133
  renderItem,
719
1134
  itemCardProps,
1135
+ layout,
720
1136
  loading,
721
1137
  empty,
722
1138
  error
723
1139
  }
724
1140
  ),
725
- enabled.pagination ? /* @__PURE__ */ jsx10(
1141
+ enabled.pagination ? /* @__PURE__ */ jsx11(
726
1142
  KeepPagination,
727
1143
  {
728
1144
  totalCount: list.totalCount,
@@ -731,7 +1147,7 @@ function KeepCollectionContent({
731
1147
  onPageChange: (nextPage) => setPage(nextPage)
732
1148
  }
733
1149
  ) : null,
734
- enabled.bulkActions ? /* @__PURE__ */ jsx10(KeepBulkActions, { query: resolvedQuery }) : null
1150
+ enabled.bulkActions ? /* @__PURE__ */ jsx11(KeepBulkActions, { query: resolvedQuery }) : null
735
1151
  ]
736
1152
  }
737
1153
  );
@@ -743,16 +1159,23 @@ function getCollectionState(list) {
743
1159
  return "ready";
744
1160
  }
745
1161
 
1162
+ // src/KeepLayout.tsx
1163
+ import { jsx as jsx12 } from "react/jsx-runtime";
1164
+ function KeepLayout({ layout = "list", children, style, ...props }) {
1165
+ const layoutStyle = layout === "grid" ? { display: "grid", gap: "1rem", gridTemplateColumns: "repeat(auto-fit, minmax(16rem, 1fr))" } : layout === "compact" ? { display: "grid", gap: "0.5rem", gridTemplateColumns: "repeat(auto-fit, minmax(12rem, 1fr))" } : { display: "flex", flexDirection: "column", gap: "1rem" };
1166
+ return /* @__PURE__ */ jsx12("div", { ...props, style: { ...layoutStyle, ...style }, "data-layout": layout, children });
1167
+ }
1168
+
746
1169
  // src/KeepNoteEditor.tsx
747
1170
  import { useKeepItem as useKeepItem3 } from "@keepkit/core/react";
748
1171
  import {
749
1172
  isValidElement as isValidElement5,
750
1173
  useCallback as useCallback2,
751
- useEffect as useEffect2,
752
- useRef,
753
- useState as useState5
1174
+ useEffect as useEffect3,
1175
+ useRef as useRef3,
1176
+ useState as useState6
754
1177
  } from "react";
755
- import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
1178
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
756
1179
  function KeepNoteEditor({
757
1180
  item,
758
1181
  label,
@@ -772,11 +1195,11 @@ function KeepNoteEditor({
772
1195
  const itemState = useKeepItem3(item);
773
1196
  const { error, isMutating, item: savedItem, updateNote } = itemState;
774
1197
  const contentChildren = asChild && isValidElement5(children) ? void 0 : children;
775
- const [note, setNote] = useState5(item.note ?? "");
1198
+ const [note, setNote] = useState6(item.note ?? "");
776
1199
  const baselineNote = savedItem?.note ?? item.note ?? "";
777
1200
  const isDirty = note !== baselineNote;
778
- const lastSavedNoteRef = useRef(void 0);
779
- useEffect2(() => setNote(baselineNote), [baselineNote]);
1201
+ const lastSavedNoteRef = useRef3(void 0);
1202
+ useEffect3(() => setNote(baselineNote), [baselineNote]);
780
1203
  const save = useCallback2(async () => {
781
1204
  const nextNote = note.trim() || void 0;
782
1205
  try {
@@ -788,7 +1211,7 @@ function KeepNoteEditor({
788
1211
  throw error2;
789
1212
  }
790
1213
  }, [note, onSaveError, onSaved, updateNote]);
791
- useEffect2(() => {
1214
+ useEffect3(() => {
792
1215
  if (!isDirty || debounceMs <= 0 || lastSavedNoteRef.current === note) return;
793
1216
  const timer = window.setTimeout(() => void save().catch(() => void 0), debounceMs);
794
1217
  return () => window.clearTimeout(timer);
@@ -802,10 +1225,10 @@ function KeepNoteEditor({
802
1225
  error,
803
1226
  save
804
1227
  };
805
- const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs6(Fragment5, { children: [
806
- /* @__PURE__ */ jsxs6("label", { children: [
1228
+ const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs7(Fragment5, { children: [
1229
+ /* @__PURE__ */ jsxs7("label", { children: [
807
1230
  label ?? defaultLabel,
808
- /* @__PURE__ */ jsx11(
1231
+ /* @__PURE__ */ jsx13(
809
1232
  "textarea",
810
1233
  {
811
1234
  value: note,
@@ -821,14 +1244,14 @@ function KeepNoteEditor({
821
1244
  }
822
1245
  )
823
1246
  ] }),
824
- /* @__PURE__ */ jsx11("button", { type: "submit", disabled: isMutating, "aria-busy": isMutating, children: saveLabel ?? defaultSaveLabel })
1247
+ /* @__PURE__ */ jsx13("button", { type: "submit", disabled: isMutating, "aria-busy": isMutating, children: saveLabel ?? defaultSaveLabel })
825
1248
  ] });
826
1249
  const handleSubmit = (event) => {
827
1250
  event.preventDefault();
828
1251
  void save().catch(() => void 0);
829
1252
  };
830
1253
  if (!asChild) {
831
- return /* @__PURE__ */ jsx11(
1254
+ return /* @__PURE__ */ jsx13(
832
1255
  "form",
833
1256
  {
834
1257
  ...formProps,
@@ -861,8 +1284,8 @@ function KeepNoteEditor({
861
1284
 
862
1285
  // src/KeepTagEditor.tsx
863
1286
  import { useKeepItem as useKeepItem4 } from "@keepkit/core/react";
864
- import { useCallback as useCallback3, useEffect as useEffect3, useState as useState6 } from "react";
865
- import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1287
+ import { useCallback as useCallback3, useEffect as useEffect4, useState as useState7 } from "react";
1288
+ import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
866
1289
  function KeepTagEditor({
867
1290
  item,
868
1291
  availableTags = [],
@@ -875,9 +1298,9 @@ function KeepTagEditor({
875
1298
  const removeLabel = useUiLabel("remove");
876
1299
  const applyTagsLabel = useUiLabel("applyTags");
877
1300
  const itemState = useKeepItem4(item);
878
- const [tags, setTags] = useState6(item.tags ?? []);
879
- const [input, setInput] = useState6("");
880
- useEffect3(() => setTags(itemState.item?.tags ?? item.tags ?? []), [item.tags, itemState.item?.tags]);
1301
+ const [tags, setTags] = useState7(item.tags ?? []);
1302
+ const [input, setInput] = useState7("");
1303
+ useEffect4(() => setTags(itemState.item?.tags ?? item.tags ?? []), [item.tags, itemState.item?.tags]);
881
1304
  const save = useCallback3(async () => {
882
1305
  const nextTags = normalizeUiTags(tags);
883
1306
  try {
@@ -893,10 +1316,10 @@ function KeepTagEditor({
893
1316
  setTags(normalizeUiTags([...tags, tag]));
894
1317
  setInput("");
895
1318
  };
896
- const body = render ? render({ tags, setTags, save, isSaving: itemState.isMutating }) : /* @__PURE__ */ jsxs7(Fragment6, { children: [
897
- /* @__PURE__ */ jsxs7("label", { children: [
1319
+ const body = render ? render({ tags, setTags, save, isSaving: itemState.isMutating }) : /* @__PURE__ */ jsxs8(Fragment6, { children: [
1320
+ /* @__PURE__ */ jsxs8("label", { children: [
898
1321
  tagsLabel,
899
- /* @__PURE__ */ jsx12(
1322
+ /* @__PURE__ */ jsx14(
900
1323
  "input",
901
1324
  {
902
1325
  value: input,
@@ -915,14 +1338,14 @@ function KeepTagEditor({
915
1338
  }
916
1339
  )
917
1340
  ] }),
918
- availableTags.length > 0 ? /* @__PURE__ */ jsx12("datalist", { id: `keep-tags-${item.id}`, children: availableTags.map((tag) => /* @__PURE__ */ jsx12("option", { value: tag }, tag)) }) : null,
919
- /* @__PURE__ */ jsx12("ul", { "aria-label": tagsLabel, children: tags.map((tag) => /* @__PURE__ */ jsxs7("li", { children: [
1341
+ availableTags.length > 0 ? /* @__PURE__ */ jsx14("datalist", { id: `keep-tags-${item.id}`, children: availableTags.map((tag) => /* @__PURE__ */ jsx14("option", { value: tag }, tag)) }) : null,
1342
+ /* @__PURE__ */ jsx14("ul", { "aria-label": tagsLabel, children: tags.map((tag) => /* @__PURE__ */ jsxs8("li", { children: [
920
1343
  tag,
921
- /* @__PURE__ */ jsx12("button", { type: "button", onClick: () => setTags(tags.filter((current) => current !== tag)), children: removeLabel })
1344
+ /* @__PURE__ */ jsx14("button", { type: "button", onClick: () => setTags(tags.filter((current) => current !== tag)), children: removeLabel })
922
1345
  ] }, tag)) }),
923
- /* @__PURE__ */ jsx12("button", { type: "submit", disabled: itemState.isMutating, "aria-busy": itemState.isMutating, children: applyTagsLabel })
1346
+ /* @__PURE__ */ jsx14("button", { type: "submit", disabled: itemState.isMutating, "aria-busy": itemState.isMutating, children: applyTagsLabel })
924
1347
  ] });
925
- return /* @__PURE__ */ jsx12(
1348
+ return /* @__PURE__ */ jsx14(
926
1349
  "form",
927
1350
  {
928
1351
  ...props,
@@ -939,10 +1362,24 @@ function KeepTagEditor({
939
1362
  );
940
1363
  }
941
1364
 
1365
+ // src/KeepUndo.tsx
1366
+ import { useKeepContext as useKeepContext2 } from "@keepkit/core/react";
1367
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1368
+ function KeepUndo({ children, label, ...props }) {
1369
+ const context = useKeepContext2();
1370
+ const message = useUiLabel("undoAvailable");
1371
+ const undoLabel = useUiLabel("undo");
1372
+ if (!context.undo.canUndo) return null;
1373
+ return /* @__PURE__ */ jsxs9("div", { ...props, role: "status", "aria-live": "polite", "data-state": "available", children: [
1374
+ children ?? message,
1375
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => void context.undoLastRemoval(), children: label ?? undoLabel })
1376
+ ] });
1377
+ }
1378
+
942
1379
  // src/status.tsx
943
- import { useKeepContext } from "@keepkit/core/react";
944
- import { isValidElement as isValidElement6, useEffect as useEffect4, useRef as useRef2, useState as useState7 } from "react";
945
- import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
1380
+ import { useKeepContext as useKeepContext3 } from "@keepkit/core/react";
1381
+ import { isValidElement as isValidElement6, useEffect as useEffect5, useRef as useRef4, useState as useState8 } from "react";
1382
+ import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
946
1383
  function KeepEmptyState({
947
1384
  title,
948
1385
  description,
@@ -954,9 +1391,9 @@ function KeepEmptyState({
954
1391
  }) {
955
1392
  const defaultTitle = useUiLabel("noItems").replace(/\.$/, "");
956
1393
  const contentChildren = asChild && isValidElement6(children) ? void 0 : children;
957
- const body = contentChildren ?? /* @__PURE__ */ jsxs8(Fragment7, { children: [
958
- /* @__PURE__ */ jsx13("h2", { children: title ?? defaultTitle }),
959
- description ? /* @__PURE__ */ jsx13("p", { children: description }) : null,
1394
+ const body = contentChildren ?? /* @__PURE__ */ jsxs10(Fragment7, { children: [
1395
+ /* @__PURE__ */ jsx16("h2", { children: title ?? defaultTitle }),
1396
+ description ? /* @__PURE__ */ jsx16("p", { children: description }) : null,
960
1397
  action
961
1398
  ] });
962
1399
  return renderRoot(asChild, children, { ...rootProps, className, "data-state": "empty" }, body, "KeepEmptyState");
@@ -970,10 +1407,10 @@ function KeepStatus({
970
1407
  className,
971
1408
  ...rootProps
972
1409
  }) {
973
- const context = useKeepContext();
1410
+ const context = useKeepContext3();
974
1411
  const contentChildren = asChild && isValidElement6(children) ? void 0 : children;
975
1412
  const resolvedStatus = status ?? getDerivedStatus(context);
976
- const defaultLabel = useUiLabel(getStatusLabelKey(resolvedStatus));
1413
+ const defaultLabel = useUiLabel(getStatusLabelKey2(resolvedStatus));
977
1414
  const state = {
978
1415
  status: resolvedStatus,
979
1416
  error: context.error,
@@ -998,13 +1435,13 @@ function KeepStatus({
998
1435
  );
999
1436
  }
1000
1437
  function KeepAnnouncements({ messages, ...props }) {
1001
- const context = useKeepContext();
1438
+ const context = useKeepContext3();
1002
1439
  const savedMessage = useUiLabel("savedMessage", messages?.save);
1003
1440
  const removedMessage = useUiLabel("removedMessage", messages?.remove);
1004
1441
  const noteSavedMessage = useUiLabel("noteSavedMessage", messages?.note);
1005
- const [message, setMessage] = useState7("");
1006
- const lastChangeRef = useRef2(void 0);
1007
- useEffect4(() => {
1442
+ const [message, setMessage] = useState8("");
1443
+ const lastChangeRef = useRef4(void 0);
1444
+ useEffect5(() => {
1008
1445
  const change = context.lastChange;
1009
1446
  if (!change || change === lastChangeRef.current) return;
1010
1447
  lastChangeRef.current = change;
@@ -1012,7 +1449,7 @@ function KeepAnnouncements({ messages, ...props }) {
1012
1449
  else if (change.action === "remove" || change.action === "removeBatch") setMessage(removedMessage);
1013
1450
  else if (change.action === "updateNote") setMessage(noteSavedMessage);
1014
1451
  }, [context.lastChange, noteSavedMessage, removedMessage, savedMessage]);
1015
- return /* @__PURE__ */ jsx13(
1452
+ return /* @__PURE__ */ jsx16(
1016
1453
  "div",
1017
1454
  {
1018
1455
  ...props,
@@ -1033,7 +1470,7 @@ function getDerivedStatus(context) {
1033
1470
  if (context.isHydrated && context.items.length === 0) return "empty";
1034
1471
  return "idle";
1035
1472
  }
1036
- function getStatusLabelKey(status) {
1473
+ function getStatusLabelKey2(status) {
1037
1474
  if (status === "empty") return "noItems";
1038
1475
  if (status === "loading") return "loadingItems";
1039
1476
  if (status === "error") return "error";
@@ -1046,7 +1483,7 @@ function getStatusLabelKey(status) {
1046
1483
  import {
1047
1484
  KeepErrorBoundary as KeepErrorBoundary3,
1048
1485
  KeepProvider,
1049
- useKeepContext as useKeepContext2,
1486
+ useKeepContext as useKeepContext4,
1050
1487
  useKeepItem as useKeepItem5,
1051
1488
  useKeepList as useKeepList5,
1052
1489
  useKeepShortcut
@@ -1061,7 +1498,7 @@ import {
1061
1498
  LocalStorageSyncQueueAdapter,
1062
1499
  SyncStorageAdapter
1063
1500
  } from "@keepkit/core/storage";
1064
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
1501
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
1065
1502
  function KeepKitProvider({
1066
1503
  labels,
1067
1504
  locale,
@@ -1069,16 +1506,16 @@ function KeepKitProvider({
1069
1506
  children,
1070
1507
  ...providerProps
1071
1508
  }) {
1072
- return /* @__PURE__ */ jsx14(KeepUiProvider, { labels, locale, labelResolver, children: /* @__PURE__ */ jsxs9(CoreKeepProvider, { ...providerProps, children: [
1509
+ return /* @__PURE__ */ jsx17(KeepUiProvider, { labels, locale, labelResolver, children: /* @__PURE__ */ jsxs11(CoreKeepProvider, { ...providerProps, children: [
1073
1510
  children,
1074
- /* @__PURE__ */ jsx14(KeepAnnouncements, {})
1511
+ /* @__PURE__ */ jsx17(KeepAnnouncements, {})
1075
1512
  ] }) });
1076
1513
  }
1077
1514
  function createKeepKit(options = {}) {
1078
1515
  const { labels, locale, labelResolver, getTitle, getImageProps, ...coreOptions } = options;
1079
1516
  const coreKit = createCoreKeepKit(coreOptions);
1080
1517
  return {
1081
- Provider: (props) => /* @__PURE__ */ jsx14(
1518
+ Provider: (props) => /* @__PURE__ */ jsx17(
1082
1519
  KeepKitProvider,
1083
1520
  {
1084
1521
  ...coreOptions,
@@ -1088,8 +1525,9 @@ function createKeepKit(options = {}) {
1088
1525
  ...props
1089
1526
  }
1090
1527
  ),
1091
- Button: (props) => /* @__PURE__ */ jsx14(KeepButton, { ...props }),
1092
- Collection: (props) => /* @__PURE__ */ jsx14(
1528
+ Button: (props) => /* @__PURE__ */ jsx17(KeepButton, { ...props }),
1529
+ Backup: (props) => /* @__PURE__ */ jsx17(KeepBackup, { ...props }),
1530
+ Collection: (props) => /* @__PURE__ */ jsx17(
1093
1531
  KeepCollection,
1094
1532
  {
1095
1533
  ...props,
@@ -1112,6 +1550,7 @@ export {
1112
1550
  IndexedDBSyncQueueAdapter,
1113
1551
  KeepAnnouncements,
1114
1552
  KeepAnnouncer,
1553
+ KeepBackup,
1115
1554
  KeepBulkActions,
1116
1555
  KeepButton,
1117
1556
  KeepCollection,
@@ -1120,6 +1559,7 @@ export {
1120
1559
  KeepItemCard,
1121
1560
  KeepItemCheckbox,
1122
1561
  KeepKitProvider,
1562
+ KeepLayout,
1123
1563
  KeepList,
1124
1564
  KeepNoteEditor,
1125
1565
  KeepPagination,
@@ -1130,18 +1570,22 @@ export {
1130
1570
  KeepTagEditor,
1131
1571
  KeepTagFilter,
1132
1572
  KeepUiProvider,
1573
+ KeepUndo,
1133
1574
  LocalStorageAdapter,
1134
1575
  LocalStorageSyncQueueAdapter,
1135
1576
  SyncStorageAdapter,
1136
1577
  createBrowserStorageAdapter,
1137
1578
  createKeepKit,
1579
+ createNextPagesRouterAdapter,
1138
1580
  createStorageAdapter,
1581
+ getKeepLocaleLabels,
1139
1582
  isAllSelected,
1140
1583
  toggleSelectAll,
1141
- useKeepContext2 as useKeepContext,
1584
+ useKeepContext4 as useKeepContext,
1142
1585
  useKeepItem5 as useKeepItem,
1143
1586
  useKeepList5 as useKeepList,
1144
1587
  useKeepShortcut,
1145
- useKeepUiLabels
1588
+ useKeepUiLabels,
1589
+ useKeepUrlSync
1146
1590
  };
1147
1591
  //# sourceMappingURL=index.js.map