Skip to content

Commit 5be73c2

Browse files
committed
feat(devtools): new cache tab
1 parent 571f7cd commit 5be73c2

File tree

10 files changed

+3043
-474
lines changed

10 files changed

+3043
-474
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"pnpm": {
4444
"onlyBuiltDependencies": [
4545
"@parcel/watcher",
46+
"@tailwindcss/oxide",
4647
"esbuild",
4748
"sharp",
4849
"simple-git-hooks",
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<script lang="ts">
2+
const itemSearchContent = ref('')
3+
</script>
4+
5+
<script lang="ts" setup>
6+
const store = useNonNullRstore()
7+
const cache = useStoreCache()
8+
9+
const showRawCache = useLocalStorage('rstore-devtools-show-raw-cache', false)
10+
const selectedModel = useLocalStorage<string | null>('rstore-devtools-selected-cache-model', null)
11+
const cacheModelSearch = useLocalStorage('rstore-devtools-cache-model-search', '')
12+
const itemSearchKey = useLocalStorage('rstore-devtools-cache-item-search-key', '')
13+
14+
const filteredModels = computed(() => {
15+
if (!cacheModelSearch.value) {
16+
return store.value.$models
17+
}
18+
return store.value.$models.filter(m => m.name.toLowerCase().includes(cacheModelSearch.value.toLowerCase()))
19+
})
20+
21+
const modelSearchEl = useTemplateRef('modelSearchEl')
22+
23+
// Selected cache
24+
25+
const selectedCache = computed(() => cache.value[selectedModel.value as keyof typeof cache.value] as Record<string, any>)
26+
27+
const filteredCache = computed(() => {
28+
function filteredByKey(cache: Record<string, any> | undefined) {
29+
if (!itemSearchKey.value || !cache) {
30+
return cache
31+
}
32+
const result: Record<string, string> = {}
33+
for (const key in cache) {
34+
if (key.includes(itemSearchKey.value)) {
35+
result[key] = cache[key]
36+
}
37+
}
38+
return result
39+
}
40+
41+
function filteredByContent(cache: Record<string, any> | undefined) {
42+
if (!itemSearchContent.value || !cache) {
43+
return cache
44+
}
45+
try {
46+
// eslint-disable-next-line no-eval
47+
const filter = eval(`(item) => {
48+
return ${itemSearchContent.value}
49+
}`)
50+
const result: Record<string, string> = {}
51+
for (const key in cache) {
52+
if (filter(cache[key])) {
53+
result[key] = cache[key]
54+
}
55+
}
56+
return result
57+
}
58+
catch (error) {
59+
console.error(error)
60+
return cache
61+
}
62+
}
63+
64+
let result = filteredByKey(selectedCache.value)
65+
result = filteredByContent(result)
66+
return result
67+
})
68+
</script>
69+
70+
<template>
71+
<div class="h-full">
72+
<CodeSnippet
73+
v-if="showRawCache"
74+
:code="cache"
75+
class="text-xs p-2"
76+
/>
77+
78+
<div
79+
v-else
80+
class="flex items-stretch h-full"
81+
>
82+
<!-- Models -->
83+
<div class="flex flex-col w-1/4 max-w-60">
84+
<div class="p-1">
85+
<UInput
86+
ref="modelSearchEl"
87+
v-model="cacheModelSearch"
88+
placeholder="Search models..."
89+
icon="lucide:search"
90+
size="xs"
91+
class="w-full"
92+
>
93+
<template v-if="cacheModelSearch" #trailing>
94+
<UButton
95+
icon="lucide:x"
96+
size="xs"
97+
variant="link"
98+
color="neutral"
99+
@click="cacheModelSearch = '';modelSearchEl?.inputRef?.focus()"
100+
/>
101+
</template>
102+
</UInput>
103+
</div>
104+
<div class="flex flex-col flex-1 overflow-auto p-1 gap-px">
105+
<DevtoolsCacheModelItem
106+
v-for="model in filteredModels"
107+
:key="model.name"
108+
:model
109+
:selected="selectedModel === model.name"
110+
@click="selectedModel = model.name"
111+
/>
112+
113+
<div v-if="!filteredModels.length" class="p-2 text-xs italic opacity-50 text-center">
114+
No models found.
115+
</div>
116+
</div>
117+
</div>
118+
119+
<!-- Items -->
120+
<div v-if="selectedModel" class="overflow-auto flex-1">
121+
<Empty
122+
v-if="!filteredCache || !Object.keys(selectedCache).length"
123+
icon="lucide:database"
124+
title="No items for this model"
125+
class="h-full"
126+
/>
127+
<Empty
128+
v-else-if="!Object.keys(filteredCache).length"
129+
icon="lucide:search"
130+
title="No items match the search"
131+
class="h-full"
132+
/>
133+
<div v-else class="p-1 gap-1 flex flex-col">
134+
<div
135+
v-for="(value, key) in filteredCache"
136+
:key
137+
class="border border-default rounded-lg group/cache-item hover:border-muted"
138+
>
139+
<div class="font-mono text-xs sticky top-px h-[25px]">
140+
<div class="bg-gradient-to-b from-white via-white to-transparent dark:from-[rgb(21,21,21)] dark:via-[rgb(21,21,21)] dark:to-[rgba(21,21,21,0)] via-75% absolute -top-px -left-px -right-px">
141+
<div class="p-2 border-t border-l border-r border-default group-hover/cache-item:border-muted rounded-t-lg">
142+
{{ key }}
143+
</div>
144+
</div>
145+
</div>
146+
<CodeSnippet :code="value" class="text-xs p-2" />
147+
</div>
148+
</div>
149+
</div>
150+
</div>
151+
152+
<Teleport to="#devtools-toolbar" defer>
153+
<UPopover
154+
v-if="!showRawCache"
155+
arrow
156+
>
157+
<UButton
158+
icon="lucide:search"
159+
size="xs"
160+
:variant="itemSearchKey || itemSearchContent ? 'solid' : 'soft'"
161+
/>
162+
163+
<template #content>
164+
<div class="p-2 w-80 flex flex-col gap-2">
165+
<UFormField label="Search item by Key">
166+
<UInput
167+
v-model="itemSearchKey"
168+
placeholder="Item Key"
169+
icon="lucide:id-card"
170+
autofocus
171+
class="w-full"
172+
/>
173+
</UFormField>
174+
<UFormField label="Filter items by data">
175+
<UTextarea
176+
v-model="itemSearchContent"
177+
placeholder="item.isActive && item.age > 18"
178+
class="w-full font-mono"
179+
:rows="3"
180+
:ui="{
181+
base: 'resize-none',
182+
}"
183+
/>
184+
</UFormField>
185+
</div>
186+
</template>
187+
</UPopover>
188+
189+
<USwitch
190+
v-model="showRawCache"
191+
size="xs"
192+
label="Raw"
193+
class="text-xs"
194+
/>
195+
</Teleport>
196+
</div>
197+
</template>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script lang="ts" setup>
2+
import type { Model } from '@rstore/shared'
3+
4+
const props = defineProps<{
5+
model: Model
6+
selected?: boolean
7+
}>()
8+
9+
const cache = useStoreCache()
10+
11+
const cacheCount = computed(() => Object.keys((cache.value as any)[props.model.name] ?? {}).length)
12+
</script>
13+
14+
<template>
15+
<UButton
16+
v-bind="selected ? {
17+
color: 'primary',
18+
variant: 'subtle',
19+
} : {
20+
color: 'neutral',
21+
variant: 'ghost',
22+
}"
23+
size="sm"
24+
class="w-full"
25+
>
26+
<span class="text-start flex-1 truncate min-w-0">
27+
{{ model.name }}
28+
</span>
29+
30+
<UBadge
31+
v-if="cacheCount"
32+
:label="cacheCount || '0'"
33+
:color="selected ? 'primary' : 'neutral'"
34+
variant="soft"
35+
size="sm"
36+
:class="{
37+
'font-bold': cacheCount > 0,
38+
}"
39+
/>
40+
</UButton>
41+
</template>

packages/nuxt/client/components/devtools/Devtools.vue

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,6 @@ const tabs: Array<TabsItem> = [
3737
const tab = useLocalStorage('rstore-devtools-tab', '0')
3838
3939
const currentTab = computed(() => tabs[Number.parseInt(tab.value)] ?? tabs[0])
40-
41-
const cache = useStoreCache()
42-
43-
const stats = useStoreStats()
44-
45-
const showCacheOps = useLocalStorage('rstore-devtools-show-cache-ops', false)
4640
</script>
4741

4842
<template>
@@ -61,51 +55,16 @@ const showCacheOps = useLocalStorage('rstore-devtools-show-cache-ops', false)
6155
}"
6256
/>
6357

64-
<div class="flex-1 flex justify-end items-center gap-2">
65-
<template v-if="currentTab.slot === 'history'">
66-
<USwitch
67-
v-model="showCacheOps"
68-
size="xs"
69-
label="Cache"
70-
class="text-xs"
71-
/>
72-
73-
<UTooltip text="Clear history">
74-
<UButton
75-
icon="lucide:trash"
76-
size="xs"
77-
variant="soft"
78-
color="error"
79-
@click="clearStoreStats()"
80-
/>
81-
</UTooltip>
82-
</template>
83-
</div>
58+
<div id="devtools-toolbar" class="flex-1 flex justify-end items-center gap-2 pr-2" />
8459
</div>
8560

8661
<div class="overflow-auto flex-1">
8762
<template v-if="currentTab.slot === 'cache'">
88-
<CodeSnippet
89-
:code="cache"
90-
class="text-xs p-2"
91-
/>
63+
<DevtoolsCache />
9264
</template>
9365

9466
<template v-if="currentTab.slot === 'history'">
95-
<Empty
96-
v-if="!stats.history.length"
97-
icon="lucide:history"
98-
title="No operations yet"
99-
class="h-full"
100-
/>
101-
102-
<div v-else class="flex flex-col-reverse p-1 gap-1">
103-
<DevtoolsHistoryItem
104-
v-for="(item, index) in showCacheOps ? stats.history : stats.history.filter((item) => !item.operation.startsWith('cache'))"
105-
:key="index"
106-
:item
107-
/>
108-
</div>
67+
<DevtoolsHistory />
10968
</template>
11069

11170
<template v-if="currentTab.slot === 'model'">
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script lang="ts" setup>
2+
const stats = useStoreStats()
3+
4+
const showCacheOps = useLocalStorage('rstore-devtools-show-cache-ops', false)
5+
</script>
6+
7+
<template>
8+
<div>
9+
<Empty
10+
v-if="!stats.history.length"
11+
icon="lucide:history"
12+
title="No operations yet"
13+
class="h-full"
14+
/>
15+
16+
<div v-else class="flex flex-col-reverse p-1 gap-1">
17+
<DevtoolsHistoryItem
18+
v-for="(item, index) in showCacheOps ? stats.history : stats.history.filter((item) => !item.operation.startsWith('cache'))"
19+
:key="index"
20+
:item
21+
/>
22+
</div>
23+
24+
<Teleport to="#devtools-toolbar" defer>
25+
<USwitch
26+
v-model="showCacheOps"
27+
size="xs"
28+
label="Cache"
29+
class="text-xs"
30+
/>
31+
32+
<UTooltip text="Clear history">
33+
<UButton
34+
icon="lucide:trash"
35+
size="xs"
36+
variant="soft"
37+
color="error"
38+
@click="clearStoreStats()"
39+
/>
40+
</UTooltip>
41+
</teleport>
42+
</div>
43+
</template>

packages/nuxt/client/components/devtools/Model.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ const filteredTypes = computed(() => {
2828
icon="lucide:search"
2929
placeholder="Search"
3030
size="xs"
31-
variant="soft"
3231
autofocus
3332
class="w-full"
3433
/>

packages/nuxt/client/components/devtools/Modules.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ client.value?.host.nuxt.$rstoreModulesUpdated?.on(() => {
4242
icon="lucide:search"
4343
placeholder="Search"
4444
size="xs"
45-
variant="soft"
4645
autofocus
4746
class="w-full"
4847
/>

packages/nuxt/client/components/devtools/Plugins.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ const filteredPlugins = computed(() => {
2424
icon="lucide:search"
2525
placeholder="Search"
2626
size="xs"
27-
variant="soft"
2827
autofocus
2928
class="w-full"
3029
/>

packages/nuxt/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"@nuxt/module-builder": "^0.8.4",
5555
"@nuxt/schema": "^3.15.4",
5656
"@nuxt/test-utils": "^3.15.4",
57-
"@nuxt/ui": "^3.0.0",
57+
"@nuxt/ui": "^3.3.2",
5858
"@types/node": "latest",
5959
"@vueuse/core": "^12.7.0",
6060
"@vueuse/nuxt": "^12.7.0",

0 commit comments

Comments
 (0)