Initial commit. Files are uploaded to the filesystem.

This commit is contained in:
ThePendulum
2025-09-25 06:19:37 +02:00
commit 745b00dcdc
64 changed files with 9572 additions and 0 deletions

27
components/Link.vue Normal file
View File

@@ -0,0 +1,27 @@
<template>
<a :class="{ active: isActive }">
<slot />
</a>
</template>
<script lang="ts" setup>
import { useAttrs, computed } from 'vue'
import { usePageContext } from '../renderer/usePageContext'
const pageContext = usePageContext()
const { href } = useAttrs() as { href: string }
const isActive = computed(() => {
const { urlPathname } = pageContext.value
return href === '/' ? urlPathname === href : urlPathname.startsWith(href)
})
</script>
<style scoped>
a {
padding: 2px 10px;
margin-left: -10px;
}
a.active {
background-color: #eee;
}
</style>

178
components/upload/drop.vue Normal file
View File

@@ -0,0 +1,178 @@
<template>
<form
class="selector"
:class="{ dragging }"
@submit.prevent="upload"
>
<div
ref="dropzone"
class="dropzone"
@drop.prevent="dropFile"
@dragover="dragging = true"
@dragleave="dragging = false"
>
<input
type="file"
@change="selectFile"
>
Drop or paste your files here
</div>
<ul class="uploads nolist">
<li
v-for="upload in thumbs"
class="upload"
>
<img
v-if="upload.file.type.indexOf('image/') === 0"
:src="upload.thumb"
class="upload-thumb"
>
<video
v-if="upload.file.type.indexOf('video/') === 0"
class="upload-thumb"
autoplay
muted
loop
>
<source :src="upload.thumb">
</video>
<span class="upload-name ellipsis">{{ upload.file.name }}</span>
</li>
</ul>
<button
class="button button-primary submit"
>Upload</button>
<label>
<input
v-model="addToAlbum"
type="checkbox"
>Add to album
</label>
</form>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { post } from '#src/api.ts';
const dropzone = ref(null);
const dragging = ref(false);
const uploads = ref([]);
const thumbs = ref([]);
const addToAlbum = ref(true);
async function upload() {
console.log('UPLOAD!', uploads.value);
const form = new FormData();
uploads.value.forEach((file) => {
form.append('files', file);
});
form.append('options', JSON.stringify({
addToAlbum: addToAlbum.value,
}));
const res = await post('/files', form);
console.log(res);
}
function addFiles(newFiles) {
console.log('NEW FILES', newFiles);
uploads.value = uploads.value.concat(newFiles);
thumbs.value = thumbs.value.concat(newFiles.map((file) => ({
file,
thumb: URL.createObjectURL(file),
})));
}
function dropFile(event) {
dragging.value = false;
const newFiles = Array.from(event.dataTransfer.items)
.filter((item) => item.kind === 'file')
.map((item) => item.getAsFile());
addFiles(newFiles);
}
function selectFile(event) {
const newFiles = Array.from(event.target.files);
addFiles(newFiles);
event.target.value = null;
}
onMounted(() => {
window.addEventListener('dragover', (event) => event.preventDefault());
window.addEventListener('drop', (event) => event.preventDefault());
});
</script>
<style scoped>
.selector {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1rem;
}
.dropzone {
width: 100%;
height: 15rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1rem;
padding: 1rem;
border: solid 1px var(--glass-weak-20);
&.dragging {
background: var(--primary);
}
}
.uploads {
width: 100%;
display: grid;
align-items: center;
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
gap: .5rem;
}
.upload {
display: inline-flex;
flex-direction: column;
}
.upload-thumb {
width: 8rem;
height: 6rem;
object-fit: cover;
margin-bottom: .25rem;
}
.upload-name {
font-size: .8rem;
}
.submit {
font-size: 1.2rem;
padding: .75rem 1rem;
}
</style>