Skip to content

Commit f70172b

Browse files
committed
chore: finish prototype
1 parent 100d77b commit f70172b

File tree

11 files changed

+1655
-61
lines changed

11 files changed

+1655
-61
lines changed

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shamefully-hoist=true

app.vue

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<template>
2-
<div>
3-
<NuxtRouteAnnouncer />
4-
<NuxtWelcome />
5-
</div>
2+
<UApp>
3+
<NuxtLayout>
4+
<NuxtPage />
5+
</NuxtLayout>
6+
</UApp>
67
</template>

app/assets/css/main.css

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@import "tailwindcss";
2+
@import "@nuxt/ui-pro";

app/composables/template.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import AppFile from '../template/App.vue?raw'
2+
3+
export function useTemplate() {
4+
const template = ref({
5+
welcomeSFC: AppFile,
6+
})
7+
8+
return {
9+
template,
10+
}
11+
}

app/layouts/default.vue

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<script setup lang="ts">
2+
import { getVersionsBatch } from 'fast-npm-meta'
3+
import semver from 'semver'
4+
5+
// todo: type
6+
const versions = useSessionStorage<any>('versions', [])
7+
8+
const loadingVersions = shallowRef(false)
9+
10+
async function fetchVersions() {
11+
loadingVersions.value = true
12+
versions.value = await getVersionsBatch(['@vueuse/core', 'vue'])
13+
loadingVersions.value = false
14+
}
15+
16+
if (!versions.value.length) {
17+
fetchVersions()
18+
}
19+
20+
const vueVersion = shallowRef()
21+
22+
const vueVersions = computed(() => {
23+
const vue = versions.value.find(p => p.name === 'vue')
24+
if (vue?.error)
25+
return []
26+
return vue?.versions ?? []
27+
})
28+
29+
const vueVersionsSorted = useSorted(vueVersions, (a, b) => semver.compare(b, a))
30+
31+
const vueUseVersion = useRouteQuery('vueuse', 'latest')
32+
33+
const vueUseVersions = computed(() => {
34+
const vueuse = versions.value.find(p => p.name === '@vueuse/core')
35+
if (vueuse?.error)
36+
return []
37+
return vueuse?.versions ?? []
38+
})
39+
40+
const vueUseVersionsSorted = useSorted(vueUseVersions, (a, b) => semver.compare(b, a))
41+
42+
provide('vueVersion', vueVersion)
43+
</script>
44+
45+
<template>
46+
<UApp>
47+
<UHeader>
48+
<template #title>
49+
<UIcon name="i-logos-vueuse" class="size-8" />VueUse Playground
50+
</template>
51+
<template #right>
52+
<USelectMenu v-model="vueUseVersion" :items="vueUseVersionsSorted" class="w-32" icon="i-logos-vueuse" :loading="loadingVersions" />
53+
<USelectMenu v-model="vueVersion" :items="vueVersionsSorted" class="w-32" icon="i-logos-vue" :loading="loadingVersions" />
54+
<UButton icon="i-lucide-refresh-ccw" size="md" color="primary" variant="soft" @click="fetchVersions" />
55+
<UColorModeButton />
56+
57+
<UTooltip text="Open on GitHub" :kbds="['meta', 'G']">
58+
<UButton
59+
color="neutral"
60+
variant="ghost"
61+
to="https://github.com/vueuse"
62+
target="_blank"
63+
icon="i-simple-icons-github"
64+
aria-label="GitHub"
65+
/>
66+
</UTooltip>
67+
</template>
68+
</UHeader>
69+
70+
<UMain class="h-[calc(100vh-var(--ui-header-height))]">
71+
<NuxtPage />
72+
</UMain>
73+
</UApp>
74+
</template>

app/pages/index.vue

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<script setup lang="ts">
2+
import type { OutputModes } from '@vue/repl'
3+
import type { ShallowRef } from 'vue'
4+
import { mergeImportMap, Repl, useStore, useVueImportMap } from '@vue/repl'
5+
import CodeMirror from '@vue/repl/codemirror-editor'
6+
7+
const showOutput = useRouteQuery<string, boolean>('showOutput', 'false', { transform: {
8+
get(value) {
9+
if (value === 'false' || value === '0')
10+
return false
11+
else return Boolean(value)
12+
},
13+
set(value) {
14+
return String(value)
15+
},
16+
} })
17+
18+
const outputMode = useRouteQuery<OutputModes, OutputModes>('outputMode', 'preview')
19+
20+
const hash = useRouteHash(undefined, { mode: 'replace' })
21+
22+
const {
23+
importMap: builtinImportMap,
24+
vueVersion,
25+
productionMode,
26+
} = useVueImportMap()
27+
28+
const injectedVueVersion = inject<ShallowRef<string>>('vueVersion', shallowRef<string>('latest'))
29+
30+
const vueuseVersion = useRouteQuery('vueuse', 'latest')
31+
32+
const vueUsePackages = [
33+
'@vueuse/metadata',
34+
'@vueuse/shared',
35+
'@vueuse/core',
36+
'@vueuse/integrations',
37+
'@vueuse/math',
38+
'@vueuse/rxjs',
39+
]
40+
41+
function generateVueUseImportCDNs() {
42+
return vueUsePackages.map((p) => {
43+
return [p, `https://cdn.jsdelivr.net/npm/${p}@${vueuseVersion.value}/index.mjs`]
44+
})
45+
}
46+
47+
const importMap = computed(() => {
48+
return mergeImportMap(builtinImportMap.value, {
49+
imports: Object.fromEntries([...generateVueUseImportCDNs(), ['vue-demi', 'https://cdn.jsdelivr.net/npm/[email protected]/lib/index.mjs']]),
50+
})
51+
})
52+
53+
const { template } = useTemplate()
54+
55+
const store = useStore(
56+
{
57+
// pre-set import map
58+
builtinImportMap: importMap,
59+
vueVersion,
60+
// starts on the output pane (mobile only) if the URL has a showOutput query
61+
showOutput,
62+
// starts on a different tab on the output pane if the URL has a outputMode query
63+
// and default to the "preview" tab
64+
outputMode,
65+
template,
66+
},
67+
// initialize repl with previously serialized state
68+
hash.value ?? undefined,
69+
)
70+
71+
injectedVueVersion.value = vueVersion.value ?? 'latest'
72+
73+
// persist state to URL hash
74+
watchEffect(() => hash.value = store.serialize())
75+
76+
// production mode is enabled
77+
productionMode.value = true
78+
79+
watch(() => injectedVueVersion.value, (newVersion) => {
80+
vueVersion.value = newVersion
81+
})
82+
</script>
83+
84+
<template>
85+
<Repl :store="store" :editor="CodeMirror" :show-compile-output="true" />
86+
</template>

app/template/App.vue

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script setup lang="ts">
2+
import { timestamp, useLastChanged, useTimeAgo } from '@vueuse/core'
3+
import { shallowRef } from 'vue'
4+
5+
const input = shallowRef('')
6+
const ms = useLastChanged(input, { initialValue: timestamp() - 1000 * 60 * 5 })
7+
const timeago = useTimeAgo(ms)
8+
</script>
9+
10+
<template>
11+
<div>
12+
<input v-model="input" type="text" placeholder="Type anything...">
13+
<div>Last changed: <span class="text-primary">{{ timeago }}</span> <span class="opacity-50 font-mono">({{ ms }})</span></div>
14+
</div>
15+
</template>

nuxt.config.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
// https://nuxt.com/docs/api/configuration/nuxt-config
22
export default defineNuxtConfig({
3+
ssr: false,
34
compatibilityDate: '2024-11-01',
45
devtools: { enabled: true },
5-
modules: [
6-
'@nuxt/eslint',
7-
],
6+
future: {
7+
compatibilityVersion: 4,
8+
},
9+
modules: ['@nuxt/ui-pro', '@nuxt/eslint', '@vueuse/nuxt'],
810
eslint: {
911
config: {
1012
standalone: false,
1113
},
1214
},
15+
vite: {
16+
optimizeDeps: {
17+
exclude: ['@vue/repl'],
18+
},
19+
},
20+
css: ['~/assets/css/main.css'],
1321
})

package.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,20 @@
1414
},
1515
"devDependencies": {
1616
"@antfu/eslint-config": "^4.11.0",
17+
"@iconify-json/logos": "^1.2.4",
1718
"@nuxt/eslint": "^1.3.0",
19+
"@nuxt/ui-pro": "^3.0.2",
20+
"@types/semver": "^7.7.0",
1821
"@vue/repl": "^4.5.1",
22+
"@vueuse/nuxt": "13.0.0",
23+
"@vueuse/router": "^13.0.0",
1924
"eslint": "^9.23.0",
25+
"fast-npm-meta": "^0.4.0",
2026
"nuxt": "^3.16.2",
27+
"semver": "^7.7.1",
2128
"typescript": "^5.8.2",
2229
"vue": "^3.5.13",
23-
"vue-router": "^4.5.0"
30+
"vue-router": "^4.5.0",
31+
"vue-tsc": "^2.2.8"
2432
}
2533
}

0 commit comments

Comments
 (0)