Author | SHA1 | Message | Date |
---|---|---|---|
|
0db343ec7e | Some UI stuff... Not really working yet | 4 years ago |
@ -1,27 +1,8 @@ | |||||
<template> | <template> | ||||
<img alt="Vue logo" src="./assets/logo.png" /> | |||||
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" /> | |||||
<router-view></router-view> | |||||
</template> | </template> | ||||
<script lang="ts"> | <script lang="ts"> | ||||
import { defineComponent } from 'vue' | |||||
import HelloWorld from './components/HelloWorld.vue' | |||||
export default defineComponent({ | |||||
name: 'App', | |||||
components: { | |||||
HelloWorld | |||||
} | |||||
}) | |||||
// import { defineComponent } from 'vue' | |||||
// export default defineComponent({}); | |||||
</script> | </script> | ||||
<style> | |||||
#app { | |||||
font-family: Avenir, Helvetica, Arial, sans-serif; | |||||
-webkit-font-smoothing: antialiased; | |||||
-moz-osx-font-smoothing: grayscale; | |||||
text-align: center; | |||||
color: #2c3e50; | |||||
margin-top: 60px; | |||||
} | |||||
</style> |
@ -1,65 +0,0 @@ | |||||
<template> | |||||
<h1>{{ msg }}</h1> | |||||
<p> | |||||
Recommended IDE setup: | |||||
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a> | |||||
+ | |||||
<a | |||||
href="https://marketplace.visualstudio.com/items?itemName=octref.vetur" | |||||
target="_blank" | |||||
>Vetur</a> | |||||
or | |||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a> | |||||
(if using | |||||
<code><script setup></code>) | |||||
</p> | |||||
<p>See <code>README.md</code> for more information.</p> | |||||
<p> | |||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">Vite Docs</a> | | |||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a> | |||||
</p> | |||||
<button @click="count++">count is: {{ count }}</button> | |||||
<p> | |||||
Edit | |||||
<code>components/HelloWorld.vue</code> to test hot module replacement. | |||||
</p> | |||||
</template> | |||||
<script lang="ts"> | |||||
import { ref, defineComponent } from 'vue' | |||||
export default defineComponent({ | |||||
name: 'HelloWorld', | |||||
props: { | |||||
msg: { | |||||
type: String, | |||||
required: true | |||||
} | |||||
}, | |||||
setup: () => { | |||||
const count = ref(0) | |||||
return { count } | |||||
} | |||||
}) | |||||
</script> | |||||
<style scoped> | |||||
a { | |||||
color: #42b983; | |||||
} | |||||
label { | |||||
margin: 0 0.5em; | |||||
font-weight: bold; | |||||
} | |||||
code { | |||||
background-color: #eee; | |||||
padding: 2px 4px; | |||||
border-radius: 4px; | |||||
color: #304455; | |||||
} | |||||
</style> |
@ -0,0 +1,3 @@ | |||||
<template> | |||||
<div></div> | |||||
</template> |
@ -0,0 +1,89 @@ | |||||
<template> | |||||
<div class="box-border relative text-white radial-progress"> | |||||
<svg class="transform -rotate-90" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style="height: 100%; width: 100%"> | |||||
<circle | |||||
class="radial-progress-ring__circle" | |||||
:stroke-width="ringWidth" | |||||
:r="50 - ringWidth/2" | |||||
:cx="50" | |||||
:cy="50" | |||||
ref="ring" | |||||
/> | |||||
</svg> | |||||
<div class="antialiased flex absolute inset-0 items-center justify-center text-xs font-light rounded-full text-white">{{ progressFormatted }}%</div> | |||||
<div class="flex absolute inset-0 items-center justify-center text-sm font-thin rounded-full bg-modal" :class="{ 'show-state': isPaused }" @click="$emit('toggleState')"> | |||||
<i class="fas fa-play" v-if="!isPaused"></i> | |||||
<i class="fas fa-pause" v-else></i> | |||||
</div> | |||||
</div> | |||||
</template> | |||||
<script lang="ts"> | |||||
import { defineComponent } from "vue"; | |||||
export enum RadialProgressState { | |||||
Downloading, | |||||
Paused, | |||||
Halted, | |||||
Error | |||||
} | |||||
export default defineComponent({ | |||||
computed: { | |||||
circumference(): number { | |||||
return 2*Math.PI*(50 - this.ringWidth/2); | |||||
}, | |||||
progressFormatted(): number { | |||||
return Math.floor(100*(<number>this.progress)); | |||||
} | |||||
}, | |||||
methods: { | |||||
updateProgressBar() { | |||||
(<HTMLElement>this.$refs.ring).style.strokeDasharray = `${this.circumference} ${this.circumference}`; | |||||
(<HTMLElement>this.$refs.ring).style.strokeDashoffset = `${this.circumference - this.circumference*this.progress}`; | |||||
} | |||||
}, | |||||
mounted() { | |||||
this.updateProgressBar(); | |||||
}, | |||||
props: { | |||||
progress: { | |||||
type: Number, | |||||
required: true | |||||
}, | |||||
isPaused: { | |||||
type: Boolean, | |||||
default: false | |||||
}, | |||||
ringWidth: { | |||||
type: Number, | |||||
default: 1 | |||||
} | |||||
}, | |||||
watch: { | |||||
progress() { | |||||
this.updateProgressBar(); | |||||
} | |||||
} | |||||
}); | |||||
</script> | |||||
<style lang="css"> | |||||
.radial-progress-ring__circle { | |||||
fill: #1F2937; | |||||
stroke: #34D399; | |||||
transition: stroke-dashoffset 0.25s; | |||||
} | |||||
.bg-modal { | |||||
background: rgba(0, 0, 0, 0.35); | |||||
opacity: 0.0; | |||||
transition: opacity 0.25s; | |||||
} | |||||
.show-state { | |||||
opacity: 1.0; | |||||
} | |||||
.radial-progress:hover .bg-modal { | |||||
opacity: 1.0; | |||||
} | |||||
</style> |
@ -0,0 +1,133 @@ | |||||
<template> | |||||
<div class="bg-gray-800 rounded-md m-4 md:m-8 text-gray-300"> | |||||
<div class="p-5"> | |||||
<input type="text" placeholder="Search..." class="p-2 bg-gray-600 rounded w-full"> | |||||
</div> | |||||
<div class="grid grid-cols-torrent-list"> | |||||
<!-- Header --> | |||||
<div class="header contents col-span-5"> | |||||
<div class="p-5 py-2 bg-gray-700"></div> | |||||
<div class="p-5 py-2 bg-gray-700 col-span-2 md:col-span-1">Name</div> | |||||
<div class="p-5 py-2 bg-gray-700 hidden md:block">Size</div> | |||||
<div class="p-5 py-2 bg-gray-700 hidden md:block">ETA</div> | |||||
<div class="p-5 py-2 bg-gray-700 hidden md:block">Peers</div> | |||||
<div class="p-5 py-2 bg-gray-700 hidden md:block">Actions</div> | |||||
</div> | |||||
<!-- Items --> | |||||
<torrent-list-item v-for="infoHash of infoHashes" :torrent="torrents[infoHash]" @delete="removeTorrent"></torrent-list-item> | |||||
</div> | |||||
<!-- <div v-for="i of order">{{ JSON.stringify(torrents[infoHashes[i]]) }}</div> --> | |||||
</div> | |||||
</template> | |||||
<script lang="ts"> | |||||
import { defineComponent, reactive } from "vue"; | |||||
import TorrentListItem from "./TorrentListItem.vue"; | |||||
import PaginationWidget from "./PaginationWidget.vue"; | |||||
interface ITorrentMap { | |||||
[infoHash: string]: ITorrent | |||||
} | |||||
interface ITorrent { | |||||
name: string, | |||||
progress: number, | |||||
infoHash: string, | |||||
isPaused: boolean | |||||
} | |||||
export default defineComponent({ | |||||
components: { | |||||
TorrentListItem, | |||||
PaginationWidget | |||||
}, | |||||
data() { | |||||
return { | |||||
infoHashes: <string[]>[], | |||||
torrents: <ITorrentMap>{} | |||||
} | |||||
}, | |||||
methods: { | |||||
updateTorrentsList(torrents: ITorrent[]) { | |||||
let toDelete = new Set(this.infoHashes); | |||||
torrents.forEach((torrent, index) => { | |||||
let infoHash = torrent.infoHash; | |||||
if (infoHash in this.torrents) { | |||||
toDelete.delete(infoHash); | |||||
this.torrents[infoHash].progress = torrent.progress; | |||||
this.torrents[infoHash].isPaused = torrent.isPaused; | |||||
} else { | |||||
this.infoHashes.splice(index, 0, infoHash); | |||||
this.torrents[infoHash] = reactive(torrent); | |||||
} | |||||
}); | |||||
toDelete.forEach((infoHash) => { | |||||
delete this.torrents[infoHash]; | |||||
}); | |||||
}, | |||||
removeTorrent(infoHash: string) { | |||||
delete this.torrents[infoHash]; | |||||
this.infoHashes.splice(this.infoHashes.indexOf(infoHash), 1); | |||||
} | |||||
}, | |||||
mounted() { | |||||
this.updateTorrentsList([{ | |||||
name: "Big Buck Bunny", | |||||
progress: 0.5, | |||||
infoHash: "dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c", | |||||
isPaused: false | |||||
}, | |||||
// { | |||||
// name: "Cosmos Laundromat", | |||||
// progress: 0.5, | |||||
// infoHash: "c9e15763f722f23e98a29decdfae341b98d53056", | |||||
// isPaused: false | |||||
// }, { | |||||
// name: "Sintel", | |||||
// progress: 0.5, | |||||
// infoHash: "08ada5a7a6183aae1e09d831df6748d566095a10", | |||||
// isPaused: false | |||||
// }, { | |||||
// name: "Tears of Steel", | |||||
// progress: 0.5, | |||||
// infoHash: "209c8226b299b308beaf2b9cd3fb49212dbd13ec", | |||||
// isPaused: false | |||||
// }, { | |||||
// name: "The WIRED CD - Rip. Sample. Mash. Share", | |||||
// progress: 0.5, | |||||
// infoHash: "a88fda5954e89178c372716a6a78b8180ed4dad3", | |||||
// isPaused: false | |||||
// } | |||||
]); | |||||
setInterval(() => { | |||||
for (let infoHash of this.infoHashes) { | |||||
this.torrents[infoHash].progress = Math.random(); | |||||
} | |||||
}, 3000); | |||||
} | |||||
}); | |||||
</script> | |||||
<style lang="css"> | |||||
.grid-cols-torrent-list { | |||||
grid-template-columns: min-content 1fr min-content; | |||||
} | |||||
@media (min-width: 768px) { | |||||
.grid-cols-torrent-list { | |||||
grid-template-columns: min-content 1fr repeat(3, minmax(0, 0.20fr)) min-content; | |||||
} | |||||
} | |||||
.row > div { | |||||
align-items: center; | |||||
@apply border-b border-gray-700 cursor-pointer; | |||||
} | |||||
.row:last-of-type > div { | |||||
@apply border-b-0; | |||||
} | |||||
.row:hover > div { | |||||
@apply bg-gray-600; | |||||
} | |||||
</style> |
@ -0,0 +1,59 @@ | |||||
<template> | |||||
<div class="row contents"> | |||||
<div class="pl-5 py-3 w-16 h-16 flex justify-center"> | |||||
<progress-ring :ring-width="8" class="h-10" :is-paused="torrent.isPaused" @toggle-state="toggleState" :progress="torrent.progress"/> | |||||
</div> | |||||
<div class="pl-5 py-3 h-16 flex overflow-ellipsis overflow-hidden whitespace-nowrap">{{ torrent.name }}</div> | |||||
<div class="pl-5 py-3 h-16 hidden md:flex">1.21GB</div> | |||||
<div class="pl-5 py-3 h-16 hidden md:flex">5 Minutes</div> | |||||
<div class="pl-5 py-3 h-16 hidden md:flex">29</div> | |||||
<div class="p-5 py-3 h-16 flex"> | |||||
<div class="dropdown" :class="{'active': showMenu}"> | |||||
<button id="actions-menu" aria-expanded="true" aria-haspopup="true">...</button> | |||||
<div class="dropdown-menu origin-top-right absolute right: 0 mt-8 w-56" role="menu" aria-orientation="vertical" aria-labelledby="actions-menu"> | |||||
<div class="py-1" role="none"> | |||||
<a href="#" class="block px-4 py-2 text-sm text-gray-300">Thing</a> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</template> | |||||
<script lang="ts"> | |||||
import { defineComponent } from "vue"; | |||||
import ProgressRing from "./ProgressRing.vue"; | |||||
export default defineComponent({ | |||||
components: { | |||||
ProgressRing | |||||
}, | |||||
data() { | |||||
return { | |||||
showMenu: false | |||||
} | |||||
}, | |||||
methods: { | |||||
toggleState() { | |||||
this.torrent.isPaused = !this.torrent.isPaused; | |||||
}, | |||||
}, | |||||
mounted() { | |||||
}, | |||||
props: { | |||||
torrent: { | |||||
type: Object, | |||||
required: true | |||||
} | |||||
} | |||||
}); | |||||
</script> | |||||
<style class="css"> | |||||
.dropdown .dropdown-menu { | |||||
display: none; | |||||
} | |||||
.dropdown.active .dropdown-menu { | |||||
display: initial; | |||||
} | |||||
</style> |
@ -0,0 +1,82 @@ | |||||
<template> | |||||
<tr class=""> | |||||
<td class="pl-5 py-3 w-1 h-16 handle cursor-move text-gray-500 hover:text-white"> | |||||
<div class="text-center"> | |||||
<i class="fas fa-bars transitions transition-colors"></i> | |||||
</div> | |||||
</td> | |||||
<td class="pl-5 py-3 w-16 h-16"> | |||||
<progress-ring :ring-width="8" class="w-10 h-10" :is-paused="isPaused" @toggle-state="toggleState" :progress="progress"/> | |||||
</td> | |||||
<td class="p-5 py-3 h-16 overflow-ellipsis overflow-hidden whitespace-nowrap"> | |||||
{{ name }} | |||||
</td> | |||||
<td class="p-5 py-3 h-16 w-32 hidden md:table-cell">1.21GB</td> | |||||
<td class="p-5 py-3 h-16 w-32 hidden md:table-cell">5 Minutes</td> | |||||
<td class="p-5 py-3 h-16 w-16 hidden md:table-cell">29</td> | |||||
<td class="p-5 py-3 h-16 w-1 hidden md:table-cell"> | |||||
<div class="dropdown" :class="{'active': showMenu}"> | |||||
<button class="w-9 h-9 rounded-full hover:bg-indigo-500 hover:text-white transitions transition-colors" id="actions-menu" aria-expanded="true" aria-haspopup="true"><i class="fas fa-ellipsis-v"></i></button> | |||||
<div class="dropdown-menu origin-top-right absolute right: 0 mt-8 w-56" role="menu" aria-orientation="vertical" aria-labelledby="actions-menu"> | |||||
<div class="py-1" role="none"> | |||||
<a href="#" class="block px-4 py-2 text-sm text-gray-300">Thing</a> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</td> | |||||
</tr> | |||||
</template> | |||||
<script lang="ts"> | |||||
import { defineComponent } from "vue"; | |||||
import ProgressRing from "./ProgressRing.vue"; | |||||
export default defineComponent({ | |||||
components: { | |||||
ProgressRing | |||||
}, | |||||
data() { | |||||
return { | |||||
showMenu: false | |||||
} | |||||
}, | |||||
methods: { | |||||
toggleState() { | |||||
// this.isPaused = !this.isPaused; | |||||
}, | |||||
}, | |||||
mounted() { | |||||
}, | |||||
props: { | |||||
id: { | |||||
type: Number, | |||||
required: true | |||||
}, | |||||
name: { | |||||
type: String, | |||||
required: true | |||||
}, | |||||
infoHash: { | |||||
type: String, | |||||
required: true, | |||||
}, | |||||
progress: { | |||||
type: Number, | |||||
required: true | |||||
}, | |||||
isPaused: { | |||||
type: Boolean, | |||||
required: true | |||||
} | |||||
} | |||||
}); | |||||
</script> | |||||
<style class="css"> | |||||
.dropdown .dropdown-menu { | |||||
display: none; | |||||
} | |||||
.dropdown.active .dropdown-menu { | |||||
display: initial; | |||||
} | |||||
</style> |
@ -0,0 +1,161 @@ | |||||
<template> | |||||
<div class="bg-gray-800 rounded-md m-4 md:m-8 text-gray-300"> | |||||
<div class="p-5"> | |||||
<input type="text" placeholder="Search..." class="p-2 bg-gray-600 rounded w-full"> | |||||
</div> | |||||
<table class="w-full" cellspacing="0" cellpadding="0"> | |||||
<thead class="w-full text-center"> | |||||
<tr> | |||||
<th class="p-5 py-2 bg-gray-700" colspan="2"></th> | |||||
<th class="p-5 py-2 bg-gray-700 text-left">Name</th> | |||||
<th class="p-5 py-2 bg-gray-700 hidden md:table-cell">Size</th> | |||||
<th class="p-5 py-2 bg-gray-700 hidden md:table-cell">ETA</th> | |||||
<th class="p-5 py-2 bg-gray-700 hidden md:table-cell">Peers</th> | |||||
<th class="p-5 py-2 bg-gray-700 hidden md:table-cell">Actions</th> | |||||
</tr> | |||||
</thead> | |||||
<!-- Items --> | |||||
<draggable v-model="torrents" v-bind="dragOptions" item-key="id" tag="transition-group" :component-data="{tag: 'tbody'}" @start="isDragging=true" @end="isDragging=false" @sort="onSort"> | |||||
<template v-slot:item="{element}"> | |||||
<torrent-list-item-new v-bind="element"></torrent-list-item-new> | |||||
</template> | |||||
</draggable> | |||||
</table> | |||||
</div> | |||||
</template> | |||||
<script lang="ts"> | |||||
import { defineComponent, reactive } from "vue"; | |||||
import Draggable from "vuedraggable"; | |||||
import TorrentListItemNew from "./TorrentListItemNew.vue"; | |||||
import PaginationWidget from "./PaginationWidget.vue"; | |||||
interface ITorrentMap { | |||||
[infoHash: string]: ITorrent | |||||
} | |||||
interface ITorrent { | |||||
id: number, | |||||
name: string, | |||||
progress: number, | |||||
infoHash: string, | |||||
isPaused: boolean | |||||
} | |||||
export default defineComponent({ | |||||
components: { | |||||
Draggable, | |||||
TorrentListItemNew, | |||||
PaginationWidget | |||||
}, | |||||
data() { | |||||
return { | |||||
torrents: <ITorrent[]>[{ | |||||
id: 0, | |||||
name: "Big Buck Bunny", | |||||
progress: 0.5, | |||||
infoHash: "dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c", | |||||
isPaused: false | |||||
}, | |||||
{ | |||||
id: 1, | |||||
name: "Cosmos Laundromat", | |||||
progress: 0.5, | |||||
infoHash: "c9e15763f722f23e98a29decdfae341b98d53056", | |||||
isPaused: false | |||||
}, { | |||||
id: 2, | |||||
name: "Sintel", | |||||
progress: 0.5, | |||||
infoHash: "08ada5a7a6183aae1e09d831df6748d566095a10", | |||||
isPaused: false | |||||
}, { | |||||
id: 3, | |||||
name: "Tears of Steel", | |||||
progress: 0.5, | |||||
infoHash: "209c8226b299b308beaf2b9cd3fb49212dbd13ec", | |||||
isPaused: false | |||||
}, { | |||||
id: 4, | |||||
name: "The WIRED CD - Rip. Sample. Mash. Share", | |||||
progress: 0.5, | |||||
infoHash: "a88fda5954e89178c372716a6a78b8180ed4dad3", | |||||
isPaused: false | |||||
}], | |||||
isDragging: false, | |||||
dragOptions: { | |||||
animation: 200, | |||||
ghostClass: "ghost", | |||||
handle: ".handle" | |||||
} | |||||
} | |||||
}, | |||||
methods: { | |||||
updateTorrentsList(torrents: ITorrent[]) { | |||||
this.torrents = reactive(torrents); | |||||
}, | |||||
onSort() { | |||||
console.log("Sorted"); | |||||
} | |||||
}, | |||||
mounted() { | |||||
// this.updateTorrentsList([{ | |||||
// name: "Big Buck Bunny", | |||||
// progress: 0.5, | |||||
// infoHash: "dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c", | |||||
// isPaused: false | |||||
// }, | |||||
// { | |||||
// name: "Cosmos Laundromat", | |||||
// progress: 0.5, | |||||
// infoHash: "c9e15763f722f23e98a29decdfae341b98d53056", | |||||
// isPaused: false | |||||
// }, { | |||||
// name: "Sintel", | |||||
// progress: 0.5, | |||||
// infoHash: "08ada5a7a6183aae1e09d831df6748d566095a10", | |||||
// isPaused: false | |||||
// }, { | |||||
// name: "Tears of Steel", | |||||
// progress: 0.5, | |||||
// infoHash: "209c8226b299b308beaf2b9cd3fb49212dbd13ec", | |||||
// isPaused: false | |||||
// }, { | |||||
// name: "The WIRED CD - Rip. Sample. Mash. Share", | |||||
// progress: 0.5, | |||||
// infoHash: "a88fda5954e89178c372716a6a78b8180ed4dad3", | |||||
// isPaused: false | |||||
// } | |||||
// ]); | |||||
setInterval(() => { | |||||
for (let torrent of this.torrents) { | |||||
torrent.progress = Math.random(); | |||||
} | |||||
}, 3000); | |||||
} | |||||
}); | |||||
</script> | |||||
<style lang="css"> | |||||
.grid-cols-torrent-list { | |||||
grid-template-columns: min-content 1fr min-content; | |||||
} | |||||
@media (min-width: 768px) { | |||||
.grid-cols-torrent-list { | |||||
grid-template-columns: min-content 1fr repeat(3, minmax(0, 0.20fr)) min-content; | |||||
} | |||||
} | |||||
tr:last-of-type > div { | |||||
@apply border-b-0; | |||||
} | |||||
tr:hover { | |||||
@apply bg-gray-600; | |||||
} | |||||
.ghost { | |||||
opacity: 0.0; | |||||
} | |||||
</style> |
@ -1,5 +1,17 @@ | |||||
import { createApp } from 'vue' | import { createApp } from 'vue' | ||||
import router from "./routes"; | |||||
import App from './App.vue' | import App from './App.vue' | ||||
import "./styles/index.css"; | import "./styles/index.css"; | ||||
createApp(App).mount('#app'); | |||||
let app = createApp(App); | |||||
app.use(router); | |||||
app.mount("#app"); | |||||
/** | |||||
* Dropdown menus | |||||
*/ | |||||
document.addEventListener("click", (event) => { | |||||
let dropdownMenus = document.querySelectorAll(".dropdown.active").forEach((elem) => { | |||||
elem.classList.remove("active"); | |||||
}); | |||||
}); |
@ -0,0 +1,21 @@ | |||||
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"; | |||||
const routes: RouteRecordRaw[] = [ | |||||
{ | |||||
path: "/", | |||||
name: "Home", | |||||
component: () => import("../views/Home.vue") | |||||
}, | |||||
{ | |||||
path: "/login", | |||||
name: "Login", | |||||
component: () => import("../views/Login.vue") | |||||
} | |||||
]; | |||||
const router = createRouter({ | |||||
history: createWebHistory(), | |||||
routes | |||||
}); | |||||
export default router; |
@ -1,3 +1,4 @@ | |||||
@import '@fortawesome/fontawesome-free/css/all.css'; | |||||
@tailwind base; | @tailwind base; | ||||
@tailwind components; | @tailwind components; | ||||
@tailwind utilities; | @tailwind utilities; |
@ -0,0 +1,15 @@ | |||||
<template> | |||||
<torrent-list-new></torrent-list-new> | |||||
<!-- <sortable-list></sortable-list> --> | |||||
</template> | |||||
<script lang="ts"> | |||||
import { defineComponent } from "vue"; | |||||
import TorrentListNew from "../components/TorrentListNew.vue"; | |||||
export default defineComponent({ | |||||
components: { | |||||
TorrentListNew | |||||
} | |||||
}); | |||||
</script> |
@ -0,0 +1,3 @@ | |||||
<template> | |||||
<div>Login</div> | |||||
</template> |