@powersync/nuxt 0.0.0-dev-20260128023420

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.
Files changed (64) hide show
  1. package/LICENSE +201 -0
  2. package/README +374 -0
  3. package/dist/module.d.mts +39 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +160 -0
  6. package/dist/runtime/assets/powersync-icon.svg +14 -0
  7. package/dist/runtime/components/BucketsInspectorTab.d.vue.ts +3 -0
  8. package/dist/runtime/components/BucketsInspectorTab.vue +646 -0
  9. package/dist/runtime/components/BucketsInspectorTab.vue.d.ts +3 -0
  10. package/dist/runtime/components/ConfigInspectorTab.d.vue.ts +3 -0
  11. package/dist/runtime/components/ConfigInspectorTab.vue +121 -0
  12. package/dist/runtime/components/ConfigInspectorTab.vue.d.ts +3 -0
  13. package/dist/runtime/components/DataInspectorTab.d.vue.ts +3 -0
  14. package/dist/runtime/components/DataInspectorTab.vue +678 -0
  15. package/dist/runtime/components/DataInspectorTab.vue.d.ts +3 -0
  16. package/dist/runtime/components/LoadingSpinner.d.vue.ts +3 -0
  17. package/dist/runtime/components/LoadingSpinner.vue +12 -0
  18. package/dist/runtime/components/LoadingSpinner.vue.d.ts +3 -0
  19. package/dist/runtime/components/LogsTab.d.vue.ts +3 -0
  20. package/dist/runtime/components/LogsTab.vue +325 -0
  21. package/dist/runtime/components/LogsTab.vue.d.ts +3 -0
  22. package/dist/runtime/components/PowerSyncInstanceTab.d.vue.ts +3 -0
  23. package/dist/runtime/components/PowerSyncInstanceTab.vue +9 -0
  24. package/dist/runtime/components/PowerSyncInstanceTab.vue.d.ts +3 -0
  25. package/dist/runtime/components/SyncStatusTab.d.vue.ts +3 -0
  26. package/dist/runtime/components/SyncStatusTab.vue +272 -0
  27. package/dist/runtime/components/SyncStatusTab.vue.d.ts +3 -0
  28. package/dist/runtime/composables/useDiagnosticsLogger.d.ts +27 -0
  29. package/dist/runtime/composables/useDiagnosticsLogger.js +41 -0
  30. package/dist/runtime/composables/usePowerSyncInspector.d.ts +42 -0
  31. package/dist/runtime/composables/usePowerSyncInspector.js +19 -0
  32. package/dist/runtime/composables/usePowerSyncInspectorDiagnostics.d.ts +153 -0
  33. package/dist/runtime/composables/usePowerSyncInspectorDiagnostics.js +254 -0
  34. package/dist/runtime/composables/usePowerSyncKysely.d.ts +23 -0
  35. package/dist/runtime/composables/usePowerSyncKysely.js +7 -0
  36. package/dist/runtime/index.d.ts +9 -0
  37. package/dist/runtime/layouts/powersync-inspector-layout.d.vue.ts +13 -0
  38. package/dist/runtime/layouts/powersync-inspector-layout.vue +90 -0
  39. package/dist/runtime/layouts/powersync-inspector-layout.vue.d.ts +13 -0
  40. package/dist/runtime/pages/__powersync-inspector.d.vue.ts +3 -0
  41. package/dist/runtime/pages/__powersync-inspector.vue +153 -0
  42. package/dist/runtime/pages/__powersync-inspector.vue.d.ts +3 -0
  43. package/dist/runtime/plugin.client.d.ts +2 -0
  44. package/dist/runtime/plugin.client.js +11 -0
  45. package/dist/runtime/utils/AppSchema.d.ts +27 -0
  46. package/dist/runtime/utils/AppSchema.js +23 -0
  47. package/dist/runtime/utils/DynamicSchemaManager.d.ts +15 -0
  48. package/dist/runtime/utils/DynamicSchemaManager.js +91 -0
  49. package/dist/runtime/utils/JsSchemaGenerator.d.ts +8 -0
  50. package/dist/runtime/utils/JsSchemaGenerator.js +28 -0
  51. package/dist/runtime/utils/NuxtPowerSyncDatabase.d.ts +40 -0
  52. package/dist/runtime/utils/NuxtPowerSyncDatabase.js +117 -0
  53. package/dist/runtime/utils/RecordingStorageAdapter.d.ts +13 -0
  54. package/dist/runtime/utils/RecordingStorageAdapter.js +76 -0
  55. package/dist/runtime/utils/RustClientInterceptor.d.ts +24 -0
  56. package/dist/runtime/utils/RustClientInterceptor.js +102 -0
  57. package/dist/runtime/utils/TokenConnector.d.ts +14 -0
  58. package/dist/runtime/utils/TokenConnector.js +62 -0
  59. package/dist/runtime/utils/addImportsFrom.d.ts +1 -0
  60. package/dist/runtime/utils/addImportsFrom.js +4 -0
  61. package/dist/runtime/utils/index.d.ts +1 -0
  62. package/dist/runtime/utils/index.js +1 -0
  63. package/dist/types.d.mts +9 -0
  64. package/package.json +90 -0
@@ -0,0 +1,646 @@
1
+ <template>
2
+ <TooltipProvider :delay-duration="100">
3
+ <div
4
+ border="t"
5
+ border-color="gray-100"
6
+ relative
7
+ n-bg="base"
8
+ flex="~ col"
9
+ h="screen"
10
+ overflow="hidden"
11
+ >
12
+ <SplitterGroup
13
+ id="buckets-splitter-group"
14
+ class="h-full"
15
+ direction="horizontal"
16
+ >
17
+ <!-- Buckets List Panel -->
18
+ <SplitterPanel
19
+ id="buckets-list-panel"
20
+ :min-size="30"
21
+ :default-size="40"
22
+ class="border-r border-gray-200 flex flex-col overflow-hidden"
23
+ >
24
+ <div class="flex-1 flex flex-col overflow-hidden">
25
+ <!-- Header -->
26
+ <div class="p-3 border-b border-gray-200 dark:border-neutral-700">
27
+ <div class="flex items-center justify-between mb-2">
28
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
29
+ Buckets
30
+ </h2>
31
+ <NBadge n="slate xs">
32
+ {{ filteredBuckets.length }}
33
+ </NBadge>
34
+ </div>
35
+
36
+ <!-- Search Bar -->
37
+ <NTextInput
38
+ v-model="searchQuery"
39
+ n="xs"
40
+ class="w-full"
41
+ placeholder="Search buckets..."
42
+ icon="carbon:search"
43
+ @input="onSearchInput"
44
+ />
45
+ </div>
46
+
47
+ <!-- Buckets List -->
48
+ <div class="flex-1 overflow-y-auto">
49
+ <div
50
+ v-if="filteredBuckets.length === 0 && !isLoading"
51
+ class="flex-1 flex items-center justify-center text-gray-500 dark:text-gray-400 text-sm mt-3"
52
+ >
53
+ <div class="text-center">
54
+ <NIcon
55
+ icon="carbon:data-error"
56
+ class="text-2xl mb-2"
57
+ />
58
+ <div>No buckets found</div>
59
+ </div>
60
+ </div>
61
+
62
+ <div
63
+ v-else-if="isLoading"
64
+ class="flex-1 flex items-center justify-center"
65
+ >
66
+ <NLoading />
67
+ </div>
68
+
69
+ <div
70
+ v-else
71
+ class="p-2 space-y-1"
72
+ >
73
+ <div
74
+ v-for="bucket in filteredBuckets"
75
+ :key="bucket.name"
76
+ class="bucket-item p-3 rounded-lg border border-gray-200 dark:border-neutral-700 cursor-pointer transition-all hover:bg-gray-50 dark:hover:bg-neutral-800"
77
+ :class="{
78
+ 'bg-gray-100 dark:bg-neutral-400/20 border-gray-300 dark:border-blue-700': selectedBucket?.name === bucket.name
79
+ }"
80
+ @click="selectBucket(bucket)"
81
+ >
82
+ <div class="flex items-start justify-between">
83
+ <div class="flex-1 min-w-0 flex flex-col gap-1">
84
+ <div class="flex items-center gap-2 mb-1">
85
+ <NIcon
86
+ icon="carbon:db2-database"
87
+ class="text-blue-500 flex-shrink-0"
88
+ />
89
+ <span class="font-medium text-gray-900 dark:text-gray-100 truncate">
90
+ {{ bucket.name }}
91
+ </span>
92
+ </div>
93
+
94
+ <!-- Bucket Details - Two Lines -->
95
+ <div class="grid grid-cols-4 gap-2 text-xs text-gray-600 dark:text-gray-400">
96
+ <!-- Data Size -->
97
+ <TooltipRoot>
98
+ <TooltipTrigger as-child>
99
+ <div class="flex items-center gap-1">
100
+ <NIcon
101
+ icon="carbon:data-volume"
102
+ class="text-xs"
103
+ />
104
+ <span>{{ formatBytes(bucket.data_size || 0) }}</span>
105
+ </div>
106
+ </TooltipTrigger>
107
+ <TooltipContent
108
+ side="bottom"
109
+ class="text-xs bg-white dark:bg-neutral-800 border border-gray-200 dark:border-neutral-700 rounded-md p-2"
110
+ >
111
+ Data Size: {{ formatBytes(bucket.data_size || 0) }}
112
+ </TooltipContent>
113
+ </TooltipRoot>
114
+
115
+ <!-- Row Count -->
116
+ <TooltipRoot>
117
+ <TooltipTrigger as-child>
118
+ <div class="flex items-center gap-1">
119
+ <NIcon
120
+ icon="carbon:data-2"
121
+ class="text-xs"
122
+ />
123
+ <span>{{ bucket.row_count || 0 }} rows</span>
124
+ </div>
125
+ </TooltipTrigger>
126
+ <TooltipContent
127
+ side="bottom"
128
+ class="text-xs bg-white dark:bg-neutral-800 border border-gray-200 dark:border-neutral-700 rounded-md p-2"
129
+ >
130
+ Row Count: {{ bucket.row_count || 0 }} rows
131
+ </TooltipContent>
132
+ </TooltipRoot>
133
+
134
+ <!-- Metadata Size -->
135
+ <TooltipRoot>
136
+ <TooltipTrigger as-child>
137
+ <div class="flex items-center gap-1">
138
+ <NIcon
139
+ icon="carbon:array-objects"
140
+ class="text-xs"
141
+ />
142
+ <span>{{ formatBytes(bucket.metadata_size || 0) }}</span>
143
+ </div>
144
+ </TooltipTrigger>
145
+ <TooltipContent
146
+ side="bottom"
147
+ class="text-xs bg-white dark:bg-neutral-800 border border-gray-200 dark:border-neutral-700 rounded-md p-2"
148
+ >
149
+ Metadata Size: {{ formatBytes(bucket.metadata_size || 0) }}
150
+ </TooltipContent>
151
+ </TooltipRoot>
152
+
153
+ <!-- Download Size -->
154
+ <TooltipRoot>
155
+ <TooltipTrigger as-child>
156
+ <div class="flex items-center gap-1">
157
+ <NIcon
158
+ icon="carbon:download"
159
+ class="text-xs"
160
+ />
161
+ <span>{{ formatBytes(bucket.download_size || 0) }}</span>
162
+ </div>
163
+ </TooltipTrigger>
164
+ <TooltipContent
165
+ side="bottom"
166
+ class="text-xs bg-white dark:bg-neutral-800 border border-gray-200 dark:border-neutral-700 rounded-md p-2"
167
+ >
168
+ Download Size: {{ formatBytes(bucket.download_size || 0) }}
169
+ </TooltipContent>
170
+ </TooltipRoot>
171
+ </div>
172
+
173
+ <!-- Status Line -->
174
+ <div class="flex items-center justify-between mt-1">
175
+ <div class="flex items-center gap-1 flex-wrap">
176
+ <span class="text-xs text-gray-500 dark:text-gray-400">Tables:</span>
177
+ <template v-if="JSON.parse(bucket.tables) && JSON.parse(bucket.tables).length > 0">
178
+ <template
179
+ v-for="tableName in JSON.parse(bucket.tables).slice(0, 3)"
180
+ :key="tableName"
181
+ >
182
+ <NBadge
183
+ n="gray xs"
184
+ class="text-xs"
185
+ >
186
+ {{ tableName }}
187
+ </NBadge>
188
+ </template>
189
+ <TooltipRoot v-if="JSON.parse(bucket.tables).length > 3">
190
+ <TooltipTrigger as-child>
191
+ <NBadge
192
+ n="gray xs"
193
+ class="text-xs"
194
+ >
195
+ +{{ JSON.parse(bucket.tables).length - 3 }}
196
+ </NBadge>
197
+ </TooltipTrigger>
198
+ <TooltipContent
199
+ side="bottom"
200
+ class="text-xs bg-white dark:bg-neutral-800 border border-gray-200 dark:border-neutral-700 rounded-md p-2"
201
+ >
202
+ All tables: {{ JSON.parse(bucket.tables).join(", ") }}
203
+ </TooltipContent>
204
+ </TooltipRoot>
205
+ </template>
206
+ <span
207
+ v-else
208
+ class="text-xs text-gray-500 dark:text-gray-400"
209
+ >
210
+ No tables
211
+ </span>
212
+ </div>
213
+ <NBadge
214
+ :n="bucket.downloading ? 'blue' : 'gray'"
215
+ :icon="bucket.downloading ? 'carbon:arrow-down' : 'carbon:pause'"
216
+ class="text-xs"
217
+ >
218
+ {{ bucket.downloading ? "Downloading" : "Idle" }}
219
+ </NBadge>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </SplitterPanel>
228
+
229
+ <SplitterResizeHandle
230
+ id="buckets-resize-handle"
231
+ class="w-[2px] bg-gray-200 hover:bg-indigo-300 dark:hover:bg-indigo-700"
232
+ />
233
+
234
+ <!-- Bucket Content Panel -->
235
+ <SplitterPanel
236
+ id="bucket-content-panel"
237
+ :min-size="30"
238
+ class="flex flex-col overflow-hidden"
239
+ >
240
+ <div
241
+ v-if="!selectedBucket"
242
+ class="flex w-full h-full justify-center items-center"
243
+ >
244
+ <div
245
+ text="sm gray-500"
246
+ flex="~ gap-2 items-center"
247
+ >
248
+ <NIcon icon="carbon:data-base" />
249
+ <span>Select a bucket to view its content</span>
250
+ </div>
251
+ </div>
252
+
253
+ <div
254
+ v-else
255
+ class="flex-1 flex flex-col overflow-hidden"
256
+ >
257
+ <!-- Bucket Header -->
258
+ <div class="p-3 border-b border-gray-200 dark:border-neutral-700 bg-gray-50 dark:bg-neutral-800">
259
+ <div class="flex items-center justify-between">
260
+ <div class="flex items-center gap-2">
261
+ <NIcon
262
+ icon="carbon:data-base"
263
+ class="text-blue-500"
264
+ />
265
+ <h3 class="font-semibold text-gray-900 dark:text-gray-100">
266
+ {{ selectedBucket.name }}
267
+ </h3>
268
+ <NBadge n="blue xs">
269
+ {{ bucketContentRows?.length || 0 }} items
270
+ </NBadge>
271
+ </div>
272
+
273
+ <div class="flex items-center gap-2">
274
+ <NButton
275
+ n="xs"
276
+ icon="carbon:refresh"
277
+ @click="refreshBucketContent"
278
+ >
279
+ Refresh
280
+ </NButton>
281
+ </div>
282
+ </div>
283
+ </div>
284
+
285
+ <!-- Bucket Content Table -->
286
+ <div class="flex-1 overflow-hidden">
287
+ <div
288
+ v-if="isLoadingContent"
289
+ class="flex justify-center items-center h-full"
290
+ >
291
+ <NLoading />
292
+ </div>
293
+
294
+ <div
295
+ v-else-if="bucketContentError"
296
+ class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded m-4"
297
+ >
298
+ <div class="text-red-800 dark:text-red-200 font-medium">
299
+ Error loading bucket content:
300
+ </div>
301
+ <div class="text-red-700 dark:text-red-300 text-sm mt-1">
302
+ {{ bucketContentError }}
303
+ </div>
304
+ </div>
305
+
306
+ <div
307
+ v-else-if="bucketContentRows && bucketContentRows.length > 0"
308
+ class="flex-1 flex flex-col overflow-hidden"
309
+ >
310
+ <!-- Table Controls -->
311
+ <div class="px-3 py-2 flex justify-between items-center bg-gray-50 dark:bg-neutral-800 border-b border-gray-200 dark:border-neutral-700 flex-shrink-0">
312
+ <div class="flex items-center gap-3">
313
+ <div class="text-xs text-gray-600 dark:text-gray-400">
314
+ {{ bucketContentRows.length }} row{{
315
+ bucketContentRows.length !== 1 ? "s" : ""
316
+ }}
317
+ </div>
318
+ </div>
319
+
320
+ <div class="flex items-center gap-1">
321
+ <NButton
322
+ n="xs"
323
+ icon="carbon:chevron-left"
324
+ :disabled="!table.getCanPreviousPage()"
325
+ @click="table.previousPage()"
326
+ />
327
+
328
+ <!-- Page Jump Input -->
329
+ <div class="flex items-center gap-1">
330
+ <span class="text-xs text-gray-600 dark:text-gray-400">Page</span>
331
+ <NTextInput
332
+ v-model="currentPageInput"
333
+ n="xs"
334
+ class="w-12 text-center"
335
+ type="number"
336
+ min="1"
337
+ :max="table.getPageCount()"
338
+ @blur="jumpToPage"
339
+ @keydown.enter="jumpToPage"
340
+ />
341
+ <span class="text-xs text-gray-600 dark:text-gray-400">of {{ table.getPageCount() }}</span>
342
+ </div>
343
+
344
+ <NButton
345
+ n="xs"
346
+ icon="carbon:chevron-right"
347
+ :disabled="!table.getCanNextPage()"
348
+ @click="table.nextPage()"
349
+ />
350
+ </div>
351
+
352
+ <!-- Page Size Control -->
353
+ <div class="flex items-center gap-1">
354
+ <span class="text-xs text-gray-600 dark:text-gray-400">Show:</span>
355
+ <NTextInput
356
+ v-model="pageSizeInput"
357
+ n="xs"
358
+ class="w-16"
359
+ type="number"
360
+ min="1"
361
+ max="1000"
362
+ @blur="updatePageSize"
363
+ @keydown.enter="updatePageSize"
364
+ />
365
+ <span class="text-xs text-gray-600 dark:text-gray-400">per page</span>
366
+ </div>
367
+ </div>
368
+
369
+ <!-- Data Table -->
370
+ <div class="flex-1 border border-gray-200 dark:border-neutral-700 rounded-b-lg overflow-auto">
371
+ <table class="w-full min-w-max">
372
+ <thead class="bg-gray-50 dark:bg-neutral-800 sticky top-0">
373
+ <tr>
374
+ <th
375
+ v-for="header in table.getFlatHeaders()"
376
+ :key="header.id"
377
+ class="px-2 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 tracking-wider border-r border-gray-200 dark:border-neutral-700 last:border-r-0 relative overflow-hidden"
378
+ :class="
379
+ header.column.getCanSort() ? 'cursor-pointer hover:bg-gray-100 dark:hover:bg-neutral-700' : ''
380
+ "
381
+ :style="{
382
+ width: `${header.getSize()}px`,
383
+ maxWidth: `${header.getSize()}px`,
384
+ minWidth: `${header.getSize()}px`
385
+ }"
386
+ @click="
387
+ header.column.getToggleSortingHandler()?.($event)
388
+ "
389
+ >
390
+ <div class="flex items-center gap-1 min-w-0">
391
+ <div class="truncate flex-1 min-w-0">
392
+ <FlexRender
393
+ :render="header.column.columnDef.header"
394
+ :props="header.getContext()"
395
+ />
396
+ </div>
397
+ <span
398
+ v-if="header.column.getIsSorted()"
399
+ class="text-xs flex-shrink-0"
400
+ >
401
+ {{
402
+ header.column.getIsSorted() === "asc" ? "\u25B2" : "\u25BC"
403
+ }}
404
+ </span>
405
+ </div>
406
+ <!-- Column Resize Handle -->
407
+ <div
408
+ v-if="header.column.getCanResize()"
409
+ class="absolute top-0 right-0 h-full w-1 cursor-col-resize bg-transparent hover:bg-gray-500 hover:bg-opacity-50 group"
410
+ @mousedown="header.getResizeHandler()?.($event)"
411
+ @touchstart="header.getResizeHandler()?.($event)"
412
+ @click.stop
413
+ >
414
+ <div
415
+ class="w-full h-full group-hover:bg-gray-500 transition-colors duration-150"
416
+ />
417
+ </div>
418
+ </th>
419
+ </tr>
420
+ </thead>
421
+ <tbody
422
+ class="bg-white dark:bg-neutral-900 border-t border-gray-200 dark:border-neutral-700"
423
+ >
424
+ <tr
425
+ v-for="row in table.getRowModel().rows"
426
+ :key="row.id"
427
+ class="hover:bg-gray-50 dark:hover:bg-neutral-800 border-b border-gray-200 dark:border-neutral-700"
428
+ >
429
+ <td
430
+ v-for="cell in row.getVisibleCells()"
431
+ :key="cell.id"
432
+ class="px-2 py-3 text-sm text-gray-900 dark:text-gray-100 border-r border-gray-200 dark:border-neutral-700 last:border-r-0 overflow-hidden"
433
+ :style="{
434
+ width: `${cell.column.getSize()}px`,
435
+ maxWidth: `${cell.column.getSize()}px`,
436
+ minWidth: `${cell.column.getSize()}px`
437
+ }"
438
+ >
439
+ <div
440
+ class="truncate w-full"
441
+ :title="String(cell.getValue())"
442
+ >
443
+ <FlexRender
444
+ :render="cell.column.columnDef.cell"
445
+ :props="cell.getContext()"
446
+ />
447
+ </div>
448
+ </td>
449
+ </tr>
450
+ </tbody>
451
+ </table>
452
+ </div>
453
+ </div>
454
+
455
+ <div
456
+ v-else
457
+ class="text-center py-8"
458
+ >
459
+ <div class="text-gray-500 dark:text-gray-400">
460
+ No content found in this bucket
461
+ </div>
462
+ </div>
463
+ </div>
464
+ </div>
465
+ </SplitterPanel>
466
+ </SplitterGroup>
467
+ </div>
468
+ </TooltipProvider>
469
+ </template>
470
+
471
+ <script setup>
472
+ import { ref, computed, watch, onMounted } from "vue";
473
+ import { usePowerSyncInspectorDiagnostics } from "#imports";
474
+ import {
475
+ SplitterGroup,
476
+ SplitterPanel,
477
+ SplitterResizeHandle,
478
+ TooltipContent,
479
+ TooltipProvider,
480
+ TooltipRoot,
481
+ TooltipTrigger
482
+ } from "reka-ui";
483
+ import {
484
+ FlexRender,
485
+ getCoreRowModel,
486
+ useVueTable,
487
+ getSortedRowModel,
488
+ getPaginationRowModel
489
+ } from "@tanstack/vue-table";
490
+ import Fuse from "fuse.js";
491
+ const { db, bucketRows, formatBytes } = usePowerSyncInspectorDiagnostics();
492
+ const searchQuery = ref("");
493
+ const selectedBucket = ref(null);
494
+ const isLoading = ref(false);
495
+ const fuse = ref();
496
+ const bucketContentRows = ref(null);
497
+ const isLoadingContent = ref(false);
498
+ const bucketContentError = ref(null);
499
+ const currentPageInput = ref("1");
500
+ const pageSizeInput = ref("50");
501
+ const initializeFuse = () => {
502
+ if (!bucketRows.value) return;
503
+ const fuseOptions = {
504
+ keys: ["name"],
505
+ threshold: 0.3,
506
+ includeScore: true,
507
+ includeMatches: true
508
+ };
509
+ fuse.value = new Fuse(bucketRows.value, fuseOptions);
510
+ };
511
+ const filteredBuckets = computed(() => {
512
+ if (!bucketRows.value) return [];
513
+ if (!searchQuery.value || !fuse.value) {
514
+ return bucketRows.value;
515
+ }
516
+ const searchResults = fuse.value.search(searchQuery.value);
517
+ return searchResults.map((result) => result.item);
518
+ });
519
+ const onSearchInput = () => {
520
+ };
521
+ const selectBucket = async (bucket) => {
522
+ selectedBucket.value = bucket;
523
+ await loadBucketContent(bucket);
524
+ };
525
+ const loadBucketContent = async (bucket) => {
526
+ if (!db.value) return;
527
+ isLoadingContent.value = true;
528
+ bucketContentError.value = null;
529
+ try {
530
+ const query = `
531
+ SELECT * FROM ps_oplog WHERE bucket = ?
532
+ ORDER BY op_id DESC
533
+ `;
534
+ const result = await db.value.getAll(query, [bucket.id]);
535
+ bucketContentRows.value = result;
536
+ } catch (error) {
537
+ bucketContentError.value = error instanceof Error ? error.message : "Unknown error occurred";
538
+ bucketContentRows.value = null;
539
+ } finally {
540
+ isLoadingContent.value = false;
541
+ }
542
+ };
543
+ const refreshBucketContent = async () => {
544
+ if (selectedBucket.value) {
545
+ await loadBucketContent(selectedBucket.value);
546
+ }
547
+ };
548
+ const columns = computed(() => {
549
+ if (!bucketContentRows.value || bucketContentRows.value.length === 0) return [];
550
+ const firstRow = bucketContentRows.value[0];
551
+ const dataColumns = Object.keys(firstRow);
552
+ const columnsArray = [
553
+ // Row number column
554
+ {
555
+ id: "rowNumber",
556
+ header: "",
557
+ cell: ({ row }) => row.index + 1,
558
+ size: 60,
559
+ enableSorting: false,
560
+ enableResizing: false
561
+ },
562
+ // Data columns
563
+ ...dataColumns.map((key) => ({
564
+ accessorKey: key,
565
+ header: key,
566
+ size: 150,
567
+ minSize: 20,
568
+ maxSize: 800,
569
+ enableResizing: true,
570
+ cell: ({ getValue }) => {
571
+ const value = getValue();
572
+ if (value === null) return "NULL";
573
+ if (value === void 0) return "UNDEFINED";
574
+ if (typeof value === "object") return JSON.stringify(value);
575
+ return String(value);
576
+ }
577
+ }))
578
+ ];
579
+ return columnsArray;
580
+ });
581
+ const table = useVueTable({
582
+ get data() {
583
+ return bucketContentRows.value || [];
584
+ },
585
+ get columns() {
586
+ return columns.value;
587
+ },
588
+ getCoreRowModel: getCoreRowModel(),
589
+ getSortedRowModel: getSortedRowModel(),
590
+ getPaginationRowModel: getPaginationRowModel(),
591
+ enableColumnResizing: true,
592
+ columnResizeMode: "onChange",
593
+ defaultColumn: {
594
+ size: 150,
595
+ minSize: 20,
596
+ maxSize: 800
597
+ },
598
+ initialState: {
599
+ pagination: {
600
+ pageSize: 50
601
+ }
602
+ }
603
+ });
604
+ const jumpToPage = () => {
605
+ const pageNumber = Number.parseInt(currentPageInput.value, 10);
606
+ if (pageNumber >= 1 && pageNumber <= table.getPageCount()) {
607
+ table.setPageIndex(pageNumber - 1);
608
+ } else {
609
+ currentPageInput.value = String(table.getState().pagination.pageIndex + 1);
610
+ }
611
+ };
612
+ const updatePageSize = () => {
613
+ const pageSize = Number.parseInt(pageSizeInput.value, 10);
614
+ if (pageSize >= 1 && pageSize <= 1e3) {
615
+ table.setPageSize(pageSize);
616
+ } else {
617
+ pageSizeInput.value = String(table.getState().pagination.pageSize);
618
+ }
619
+ };
620
+ watch(
621
+ () => table.getState().pagination.pageIndex,
622
+ (newPageIndex) => {
623
+ currentPageInput.value = String(newPageIndex + 1);
624
+ }
625
+ );
626
+ watch(
627
+ () => table.getState().pagination.pageSize,
628
+ (newPageSize) => {
629
+ pageSizeInput.value = String(newPageSize);
630
+ }
631
+ );
632
+ watch(bucketRows, () => {
633
+ if (bucketRows.value) {
634
+ initializeFuse();
635
+ }
636
+ }, { immediate: true });
637
+ onMounted(async () => {
638
+ if (bucketRows.value) {
639
+ initializeFuse();
640
+ }
641
+ });
642
+ </script>
643
+
644
+ <style scoped>
645
+ .bucket-item{transition:all .2s ease}.bucket-item:hover{box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(-1px)}
646
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;