Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add storage tab #100

Merged
merged 19 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dist
.build-*
.env
.netlify
.data

# Env
.env
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools-ui-kit/playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const radio = ref('a')

<template>
<div class="relative p-10 n-bg-base">
<div class="w-full flex container mx-auto flex-col gap-4">
<div class="w-full flex gap-4 container mx-auto flex-col">
<NTip n="hover:yellow-600 dark:hover:yellow-500">
This library is heavily working in progress. Breaking changes may not follow semver. Pin the version if used.
</NTip>
Expand Down
1 change: 1 addition & 0 deletions packages/devtools/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default defineBuildConfig({
// Type only
'vue',
'vue-router',
'unstorage',
],
rollup: {
inlineDependencies: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/client/components/StateEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async function refresh() {
</div>
</template>

<style>
<style scoped>
.json-editor-vue {
--jse-theme-color: #383e42 !important;
--jse-theme-color-highlight: #687177 !important;
Expand Down
6 changes: 6 additions & 0 deletions packages/devtools/client/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export default defineNuxtConfig({
output: {
publicDir: resolve(__dirname, '../dist/client'),
},
devStorage: {
test: {
driver: 'fs',
base: resolve(__dirname, './.data/test'),
},
},
},
appConfig: {
fixture2: 'from nuxt.config.ts',
Expand Down
136 changes: 136 additions & 0 deletions packages/devtools/client/pages/modules/storage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<script setup lang="ts">
import JsonEditorVue from 'json-editor-vue'
import 'vanilla-jsoneditor/themes/jse-theme-dark.css'

definePageMeta({
icon: 'carbon-data-base',
title: 'Storage',
})

const router = useRouter()
const searchString = ref('')
const newKey = ref('')
const current = ref()
const fileKey = computed(() => useRoute().query?.key as string | undefined)

const { data: keys, refresh } = await useAsyncData('storage', () => rpc.getStorageKeys())
atinux marked this conversation as resolved.
Show resolved Hide resolved

watchEffect(async () => {
if (!fileKey.value) {
current.value = null
return
}
fetchItem(fileKey.value)
})

const filteredKeys = computed(() => {
if (!keys.value)
return []
return keys.value.filter(key => key.includes(searchString.value))
})

async function fetchItem(key: string) {
const content = await rpc.getStorageItem(key)
current.value = {
key: fileKey.value,
content,
updatedContent: content,
}
}

async function saveNewItem() {
if (!newKey.value)
return
// If does not exists
if (!keys.value?.includes(newKey.value))
await rpc.setStorageItem(newKey.value, '')

await refresh()
router.push({ query: { key: newKey.value } })
newKey.value = ''
}
async function saveCurrentItem() {
if (!current.value)
return
await rpc.setStorageItem(current.value.key, current.value.updatedContent)
await fetchItem(current.value.key)
}
async function removeCurrentItem() {
if (!current.value)
return
await rpc.removeStorageItem(current.value.key)
current.value = null
await refresh()
}
</script>

<template>
<div grid="~ cols-[auto_1fr]" h-full of-hidden class="virtual-files">
<div border="r base" of-auto w="300px">
<div class="flex items-center px-3 h-15">
<NTextInput
v-model="searchString"
icon="carbon-search"
placeholder="Search..."
n="primary"
class="w-full"
/>
</div>
<NuxtLink
v-for="key of filteredKeys" :key="key"
border="b base" px2 py1 text-sm font-mono block truncate
:to="{ query: { key } }"
:class="key === current?.key ? 'bg-truegray:20 text-base' : 'text-truegray'"
>
{{ key }}
</NuxtLink>
<NTextInput
v-model="newKey"
icon="carbon-add"
placeholder="new:key"
n="sm"
class="w-full outline-none border-0 border-b rounded-none"
@keyup.enter="saveNewItem"
/>
</div>
<div v-if="current?.key" h-full of-hidden flex="~ col">
<div border="b base" class="text-sm op75 flex items-center h-15 px-4 justify-between">
<div class="flex items-center gap-4">
<code>{{ current.key }}</code>
<NButton n="green xs" :disabled="current.content === current.updatedContent" :class="{ 'border-green': current.content !== current.updatedContent }" @click="saveCurrentItem">
Save
</NButton>
</div>
<div>
<NButton n="red xs" @click="removeCurrentItem">
Delete
</NButton>
</div>
</div>
<textarea v-if="typeof current.content === 'string'" v-model="current.updatedContent" class="of-auto h-full text-sm outline-none p-4" @keyup.ctrl.enter="saveCurrentItem" />
<JsonEditorVue v-else v-model="current.updatedContent" :class="[$colorMode.value === 'dark' ? 'jse-theme-dark' : 'light']" class="json-editor-vue of-auto h-full text-sm outline-none" v-bind="$attrs" mode="text" :navigation-bar="false" :indentation="2" :tab-size="2" />
</div>
<div v-else flex items-center justify-center op50 text-center>
<p>
Select one key to start.<br>Learn more about <NLink href="https://nitro.unjs.io/guide/introduction/storage" n="orange" target="_blank">
Nitro storage
</NLink>
</p>
</div>
</div>
</template>

<style scoped>
.json-editor-vue.light {
--jse-theme-color: #ebebeb !important;
--jse-theme-color-highlight: #bbb !important;
--jse-background-color: #8881 !important;
--jse-menu-color: #333 !important;
}
.json-editor-vue.jse-theme-dark {
--jse-theme-color: #1d1d1d !important;
--jse-theme-color-highlight: #333 !important;
--jse-background-color: #8881 !important;
--jse-menu-color: #aaa !important;
}
</style>
28 changes: 28 additions & 0 deletions packages/devtools/src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { WebSocket } from 'ws'
import { createBirpcGroup } from 'birpc'
import type { ChannelOptions } from 'birpc'
import c from 'picocolors'
import type { Storage, StorageValue } from 'unstorage'

import { parse, stringify } from 'flatted'
import type { Component, Nuxt, NuxtApp, NuxtPage } from 'nuxt/schema'
Expand All @@ -28,6 +29,7 @@ export function setupRPC(nuxt: Nuxt, _options: ModuleOptions) {
const iframeTabs: ModuleCustomTab[] = []
const customTabs: ModuleCustomTab[] = []
const serverHooks: Record<string, HookInfo> = setupHooksDebug(nuxt.hooks)
let storage: Storage | undefined
let unimport: Unimport | undefined
let app: NuxtApp | undefined

Expand All @@ -42,7 +44,33 @@ export function setupRPC(nuxt: Nuxt, _options: ModuleOptions) {
birpc.broadcast.refresh.asEvent(event)
}

nuxt.hook('nitro:init', (nitro) => {
storage = nitro.storage
})

Object.assign(serverFunctions, {
async getStorageKeys() {
if (!storage)
return []
const keys = await storage.getKeys()

return keys.filter(key => !['root', 'build', 'src'].includes(key.split(':')[0]))
},
async getStorageItem(key: string) {
if (!storage)
return null
return await storage.getItem(key)
},
async setStorageItem(key: string, value: StorageValue) {
if (!storage)
return
return await storage.setItem(key, value)
},
async removeStorageItem(key: string) {
if (!storage)
return
return await storage.removeItem(key)
},
getServerConfig() {
return nuxt.options
},
Expand Down
5 changes: 5 additions & 0 deletions packages/devtools/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { VueInspectorClient } from 'vite-plugin-vue-inspector'
import type { Hookable } from 'hookable'
import type { BirpcReturn } from 'birpc'
import type { VNode } from 'vue'
import type { StorageValue } from 'unstorage'
import type { WizardActions, WizardArgs } from './wizard'
import type {} from '@nuxt/schema'

Expand All @@ -31,6 +32,10 @@ export interface ModuleGlobalOptions {
}

export interface ServerFunctions {
getStorageKeys(): Promise<string[]>
getStorageItem(key: string): Promise<StorageValue>
setStorageItem(key: string, value: StorageValue): Promise<void>
removeStorageItem(key: string): Promise<void>
getServerConfig(): NuxtOptions
getComponents(): Component[]
getComponentsRelationships(): Promise<ComponentRelationship[]>
Expand Down