Rewrite to TailwindCSS, enhance the DOM, uninstall Vuetify, improve styles
This commit is contained in:
parent
9339416c1b
commit
e72a61b317
|
@ -1,5 +0,0 @@
|
||||||
> 1%
|
|
||||||
last 5 major versions
|
|
||||||
not dead
|
|
||||||
not ie <= 11
|
|
||||||
not op_mini all
|
|
|
@ -1,33 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'@nuxtjs/eslint-config-typescript',
|
|
||||||
'plugin:nuxt/recommended',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
],
|
|
||||||
plugins: [],
|
|
||||||
rules: {
|
|
||||||
lintOnStart: 0,
|
|
||||||
indent: ['error', 2, { SwitchCase: 1 }],
|
|
||||||
quotes: [2, 'single', { avoidEscape: true }],
|
|
||||||
'no-console': 'off',
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
argsIgnorePattern: '^_',
|
|
||||||
varsIgnorePattern: '^_',
|
|
||||||
caughtErrorsIgnorePattern: '^_',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'vue/no-multiple-template-root': 'off',
|
|
||||||
'vue/multi-word-component-names': 'off',
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
'import/ignore': ['vue-fontawesome'],
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -9,6 +9,9 @@ dist
|
||||||
# Node dependencies
|
# Node dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
|
# Cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
###
|
|
||||||
# Place your Prettier ignore content here
|
|
||||||
|
|
||||||
###
|
|
||||||
# .gitignore content is duplicated here due to https://github.com/prettier/prettier/issues/8506
|
|
||||||
|
|
||||||
# Created by .ignore support plugin (hsz.mobi)
|
|
||||||
### Node template
|
|
||||||
# Logs
|
|
||||||
/logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# nuxt.js build output
|
|
||||||
.nuxt
|
|
||||||
|
|
||||||
# Nuxt generate
|
|
||||||
dist
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless
|
|
||||||
|
|
||||||
# IDE / Editor
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Service worker
|
|
||||||
sw.*
|
|
||||||
|
|
||||||
# macOS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Vim swap files
|
|
||||||
*.swp
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"semi": false,
|
|
||||||
"singleQuote": true,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"trailingComma": "all"
|
|
||||||
}
|
|
|
@ -1 +1 @@
|
||||||
[{"D:\\Software\\Development\\Websites\\enderman.ch\\index\\assets\\styles\\transitions.scss":"1","D:\\Software\\Development\\Websites\\enderman.ch\\index\\app.vue":"2","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\EMail.vue":"3","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\about.vue":"4","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\index.vue":"5","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\projects.vue":"6","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\social.vue":"7","D:\\Software\\Development\\Websites\\enderman.ch\\index\\layouts\\Card.vue":"8","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Options.vue":"9","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Flooter.vue":"10","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\SwipeControls.vue":"11","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Logo.vue":"12","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Route.vue":"13","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\animations\\Portal.vue":"14","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\ui\\Icon.vue":"15","D:\\Software\\Development\\Websites\\enderman.ch\\index\\assets\\styles\\vuetify.scss":"16","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\[...slug].vue":"17","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\blog\\index.vue":"18","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\blog\\[slug].vue":"19","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\content\\Card.vue":"20","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\content\\OgImage.vue":"21","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\animations\\NotFound.vue":"22"},{"size":165,"mtime":1700688268778,"hashOfConfig":"23"},{"size":1550,"mtime":1700850776284,"hashOfConfig":"23"},{"size":804,"mtime":1700688952880,"hashOfConfig":"23"},{"size":2135,"mtime":1700689265787,"hashOfConfig":"23"},{"size":3117,"mtime":1700689271383,"hashOfConfig":"23"},{"size":2304,"mtime":1700689265769,"hashOfConfig":"23"},{"size":3232,"mtime":1700689265781,"hashOfConfig":"23"},{"size":1623,"mtime":1700748530633,"hashOfConfig":"23"},{"size":3855,"mtime":1708455917401,"hashOfConfig":"24"},{"size":1132,"mtime":1708254584095,"hashOfConfig":"24"},{"size":1269,"mtime":1708255068679,"hashOfConfig":"24"},{"size":1005,"mtime":1718446378238,"hashOfConfig":"24"},{"size":961,"mtime":1718445804903,"hashOfConfig":"24"},{"size":17150,"mtime":1708455647910,"hashOfConfig":"24"},{"size":935,"mtime":1706445231843,"hashOfConfig":"24"},{"size":120,"mtime":1706789613657,"hashOfConfig":"25"},{"size":2084,"mtime":1708424343371,"hashOfConfig":"26"},{"size":2560,"mtime":1708202402381,"hashOfConfig":"26"},{"size":983,"mtime":1708206997656,"hashOfConfig":"26"},{"size":1895,"mtime":1718444102273,"hashOfConfig":"24"},{"size":976,"mtime":1708431988520,"hashOfConfig":"26"},{"size":1673,"mtime":1718386852848,"hashOfConfig":"24"},"5tgxr3","lsooul","1lk8nat","1nljphs"]
|
[{"D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\animations\\NotFound.vue":"1","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Route.vue":"2","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Logo.vue":"3","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Settings.vue":"4","D:\\Software\\Development\\Websites\\enderman.ch\\index\\app.vue":"5","D:\\Software\\Development\\Websites\\enderman.ch\\index\\layouts\\Card.vue":"6"},{"size":1679,"mtime":1718453058852,"hashOfConfig":"7"},{"size":1065,"mtime":1718555359979,"hashOfConfig":"7"},{"size":985,"mtime":1718555386128,"hashOfConfig":"7"},{"size":3590,"mtime":1718563207348,"hashOfConfig":"7"},{"size":1874,"mtime":1718548398710,"hashOfConfig":"8"},{"size":1807,"mtime":1718548507733,"hashOfConfig":"8"},"2j789g","1lkkhgi"]
|
|
@ -1,2 +0,0 @@
|
||||||
node_modules/
|
|
||||||
assets/styles/**/*.css
|
|
|
@ -1,11 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
defaultSeverity: 'warning',
|
|
||||||
extends: [
|
|
||||||
'stylelint-config-standard-scss',
|
|
||||||
'stylelint-config-recommended-vue',
|
|
||||||
],
|
|
||||||
plugins: [],
|
|
||||||
rules: {
|
|
||||||
'declaration-empty-line-before': null,
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -2,13 +2,4 @@ import config from './config'
|
||||||
|
|
||||||
export default defineAppConfig({
|
export default defineAppConfig({
|
||||||
...config,
|
...config,
|
||||||
nuxtIcon: {
|
|
||||||
size: '1em',
|
|
||||||
class: 'icon',
|
|
||||||
aliases: {},
|
|
||||||
iconifyApiOptions: {
|
|
||||||
url: 'https://api.iconify.design',
|
|
||||||
publicApiFallback: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
63
app.vue
63
app.vue
|
@ -1,17 +1,18 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import 'iconify-icon'
|
||||||
|
|
||||||
const config = useAppConfig()
|
const config = useAppConfig()
|
||||||
const theme = useVThemeSSR()
|
|
||||||
const vdisp = ref(useVDisplay())
|
|
||||||
const { reader } = storeToRefs(usePageStore())
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { reader, animate } = storeToRefs(usePageStore())
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
titleTemplate: (chunk?) => {
|
titleTemplate: (chunk?) => {
|
||||||
if (route.fullPath === '/') return config.title.full
|
if (route.fullPath === '/') return config.title.full
|
||||||
if (route.fullPath.split('/').length === 2)
|
if (route.fullPath.split('/').length === 2)
|
||||||
switch (route.fullPath.split('/')[1]) {
|
switch (route.fullPath.split('/')[1]) {
|
||||||
case 'blog':
|
case 'blog':
|
||||||
return 'The Enderchest'
|
return 'The Ender Chest'
|
||||||
default:
|
default:
|
||||||
return `${chunk} – ${config.title.short}`
|
return `${chunk} – ${config.title.short}`
|
||||||
}
|
}
|
||||||
|
@ -22,47 +23,39 @@ useHead({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VThemeProvider
|
|
||||||
:theme="theme.dark.value ? config.theme.dark : config.theme.light"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
id="ender-layout"
|
id="ender-layout"
|
||||||
class="p-animated flex flex-col items-center h-full"
|
class="flex flex-col lm:justify-center lt:justify-center items-center h-full"
|
||||||
:class="{ 'sm:pt-8': !reader }"
|
|
||||||
>
|
>
|
||||||
<AClientOnly appear mode="out-in">
|
<ClientOnly>
|
||||||
<SwipeControls v-if="vdisp.xs" />
|
<LazySwipeControls class="block sm:hidden" />
|
||||||
</AClientOnly>
|
</ClientOnly>
|
||||||
|
|
||||||
<NuxtLayout
|
<NuxtLayout name="card">
|
||||||
name="card"
|
|
||||||
class="animate__animated-sm animate__delay-1-5s animate__fadeInDown"
|
|
||||||
>
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<AClientOnly
|
<footer
|
||||||
appear
|
v-if="!reader"
|
||||||
mode="in-out"
|
class="lm:static sm:fixed sm:bottom-0 sm:left-0 sm:right-0 mx-auto w-auto lm:mb-0 sm:mb-2 pass-through text-center"
|
||||||
enter="animate__animated animate__delay-1s animate__fadeIn"
|
|
||||||
leave="animate__animated animate__fadeOut"
|
|
||||||
>
|
>
|
||||||
<Flooter v-if="vdisp.xs" opaque />
|
<small
|
||||||
</AClientOnly>
|
class="lm:text-inherit sm:accent-text sm:drop-shadow-lg sm:transition-all lm:opacity-100 sm:opacity-25 sm:hover:opacity-100"
|
||||||
|
>
|
||||||
|
© 2018-{{ new Date().getFullYear() }},
|
||||||
|
<a class="sm:link-dark" :href="config.url">Enderman</a>. All rights
|
||||||
|
reserved.
|
||||||
|
<wbr />
|
||||||
|
<sub class="whitespace-nowrap">
|
||||||
|
β{{ config.build.version ? config.build.version : '?.?.?' }} ({{
|
||||||
|
config.build.date ? config.build.date : '1970-01-01'
|
||||||
|
}})
|
||||||
|
</sub>
|
||||||
|
</small>
|
||||||
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AClientOnly
|
|
||||||
appear
|
|
||||||
mode="out-in"
|
|
||||||
enter="animate__animated animate__delay-1s animate__fadeInUpBig animate__slow"
|
|
||||||
leave="animate__animated animate__fadeOutDown"
|
|
||||||
>
|
|
||||||
<Flooter v-if="vdisp.smAndUp && !reader" class="floaty mb-4 px-2" />
|
|
||||||
</AClientOnly>
|
|
||||||
|
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<template #fallback> </template>
|
<LazyPortal v-model="animate" layout="#ender-layout" randomize fade />
|
||||||
<LazyPortal layout="#ender-layout" animate randomize fade />
|
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</VThemeProvider>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 536 KiB |
|
@ -1 +0,0 @@
|
||||||
@layer base, vuetify, overrides
|
|
|
@ -31,5 +31,9 @@
|
||||||
|
|
||||||
&-faq {
|
&-faq {
|
||||||
list-style-type: faq;
|
list-style-type: faq;
|
||||||
|
|
||||||
|
> li:nth-child(2n):not(:last-child) {
|
||||||
|
@apply mb-2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,14 @@
|
||||||
// Modules.
|
// Modules.
|
||||||
@use 'transitions';
|
@use 'transitions';
|
||||||
@use 'lists';
|
@use 'lists';
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: Enchant;
|
||||||
|
src:
|
||||||
|
url("~/assets/fonts/enchant/enchant.woff") format('woff'),
|
||||||
|
url("~/assets/fonts/enchant/enchant.woff2") format('woff2')
|
||||||
|
}
|
||||||
|
|
||||||
// CSS Variables.
|
// CSS Variables.
|
||||||
:root {
|
:root {
|
||||||
|
@ -11,10 +19,47 @@
|
||||||
--animate-repeat: 1;
|
--animate-repeat: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
html {
|
||||||
|
min-height: 128px;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgb(255 255 255 / 15%) rgb(0 0 0 / 20%);
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
scrollbar-color: rgb(0 0 0 / 15%) rgb(255 255 255 / 20%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The HTML page.
|
// The HTML page.
|
||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: rgb(255 255 255 / 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: rgb(255 255 255 / 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: rgb(0 0 0 / 30%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The body element.
|
// The body element.
|
||||||
|
@ -69,8 +114,12 @@ h6 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@apply text-inherit no-underline transition-all;
|
@apply no-underline transition-all;
|
||||||
|
|
||||||
color: cornflowerblue;
|
color: cornflowerblue;
|
||||||
|
|
||||||
|
@ -79,34 +128,41 @@ a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
center {
|
||||||
padding-left: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry point (Vue mount).
|
|
||||||
#ender-app {
|
|
||||||
flex: 1 1 0;
|
|
||||||
image-rendering: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper classes that don't exist in Bootstrap.
|
|
||||||
.inline {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nobr {
|
:not(nav) > ul {
|
||||||
white-space: nowrap;
|
list-style-type: disc;
|
||||||
|
padding-left: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clickable {
|
:is(section, article, aside).page {
|
||||||
cursor: pointer;
|
:is(div, p):not(:last-child) {
|
||||||
|
@apply mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(h1, h2, h3, h4, h5, h6):not(:last-child) {
|
||||||
|
@apply mb-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(ul, ol):not(:last-child) {
|
||||||
|
@apply mb-4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iconify-icon {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry point (Vue mount).
|
||||||
|
#ender-app {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
image-rendering: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper classes that don't exist in Bootstrap.
|
||||||
.overlay {
|
.overlay {
|
||||||
grid-area: 1 / 1;
|
grid-area: 1 / 1;
|
||||||
}
|
}
|
||||||
|
@ -119,57 +175,59 @@ ul {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-decoration {
|
ruby {
|
||||||
text-decoration: none;
|
ruby-position: under;
|
||||||
color: inherit;
|
|
||||||
|
ruby {
|
||||||
|
ruby-position: over;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-align-center {
|
rt {
|
||||||
text-align: center;
|
ruby-align: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.font-small {
|
.font-small {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-cols-3 {
|
.transition-ease {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
@extend %transition;
|
||||||
}
|
|
||||||
|
|
||||||
.display-sm {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alex {
|
|
||||||
font-family: Alexandria, sans-serif;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pre-wrap {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: keep-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-animated {
|
|
||||||
transition: max-height 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-animated {
|
|
||||||
transition: padding 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query-overridable classes.
|
// Query-overridable classes.
|
||||||
.background {
|
|
||||||
background-color: rgb(0 0 0 / 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dimensions {
|
.dimensions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.accent {
|
.accent-background {
|
||||||
|
background-color: rgb(0 0 0 / 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-overlay-background {
|
||||||
|
background-color: rgb(0 0 0 / 90%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@responsive {
|
||||||
|
.accent-text {
|
||||||
color: white;
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-dark {
|
||||||
|
@apply text-inherit no-underline transition-all;
|
||||||
|
|
||||||
|
color: cornflowerblue;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: royalblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-mask {
|
||||||
|
mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0%), rgb(0 0 0 / 100%) 5%, rgb(0 0 0 / 100%) 95%, rgb(0 0 0 / 0%));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
|
@ -212,16 +270,8 @@ ul {
|
||||||
transition: 0.3s ease;
|
transition: 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.parallax {
|
||||||
&-hi-force-dark {
|
transition: all 0.6942s ease-in;
|
||||||
@apply text-inherit no-underline transition-all;
|
|
||||||
|
|
||||||
color: cornflowerblue;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: royalblue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.accent-gradient {
|
.accent-gradient {
|
||||||
|
@ -233,53 +283,26 @@ ul {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar {
|
.accent-text-shadow {
|
||||||
min-height: 128px;
|
text-shadow: black 2px 3px 4px;
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: rgb(255 255 255 / 15%) rgb(0 0 0 / 20%);
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: rgb(0 0 0 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: rgb(255 255 255 / 15%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-button {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
:where(html.light) {
|
||||||
body {
|
body {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar {
|
.accent-background {
|
||||||
scrollbar-color: rgb(0 0 0 / 20%) rgb(255 255 255 / 20%);
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background-color: rgb(255 255 255 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: rgb(0 0 0 / 20%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
background-color: rgb(255 255 255 / 80%);
|
background-color: rgb(255 255 255 / 80%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accent-overlay-background {
|
||||||
|
background-color: rgb(255 255 255 / 90%);
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@apply no-underline transition-all;
|
||||||
|
|
||||||
color: royalblue;
|
color: royalblue;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -287,7 +310,7 @@ ul {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.accent {
|
.accent-text {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,20 +322,14 @@ ul {
|
||||||
rgb(0 0 0 / 0%)
|
rgb(0 0 0 / 0%)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.floaty {
|
.accent-text-shadow {
|
||||||
position: fixed;
|
text-shadow: white 1px 2px 3px;
|
||||||
bottom: 0;
|
}
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: fit-content;
|
|
||||||
height: fit-content;
|
|
||||||
margin: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
scroll-snap-type: both mandatory;
|
scroll-snap-type: y mandatory;
|
||||||
scroll-snap-stop: normal;
|
scroll-snap-stop: normal;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
@ -337,19 +354,10 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
&-pocket {
|
&-pocket {
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
text-transform: uppercase;
|
|
||||||
background-color: rgb(153 153 255 / 10%);
|
background-color: rgb(153 153 255 / 10%);
|
||||||
|
|
||||||
border-left: 1px solid rgb(153 153 255 / 60%);
|
border-left: 1px solid rgb(153 153 255 / 60%);
|
||||||
border-top: 1px solid rgb(153 153 255 / 60%);
|
border-top: 1px solid rgb(153 153 255 / 60%);
|
||||||
|
|
||||||
border-top-left-radius: var(--bs-border-radius-xl);
|
|
||||||
border-bottom-right-radius: var(--bs-border-radius-xl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-thumb {
|
&-thumb {
|
||||||
|
@ -377,45 +385,16 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
&-preamble {
|
&-preamble {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
min-height: 400px;
|
|
||||||
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
|
|
||||||
border-radius: 1em;
|
|
||||||
|
|
||||||
scroll-snap-align: end;
|
scroll-snap-align: end;
|
||||||
|
|
||||||
text-shadow: black 1px 1px 7px;
|
|
||||||
|
|
||||||
iconify-icon {
|
iconify-icon {
|
||||||
filter: drop-shadow(1px 2px 3px rgb(0 0 0 / 100%));
|
filter: drop-shadow(1px 2px 3px rgb(0 0 0 / 100%));
|
||||||
}
|
}
|
||||||
|
|
||||||
&-control {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-share {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-content {
|
&-content {
|
||||||
|
@ -550,6 +529,13 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic classes.
|
// Dynamic classes.
|
||||||
|
@screen lm {
|
||||||
|
.dimensions {
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@screen sm {
|
@screen sm {
|
||||||
html {
|
html {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -557,9 +543,7 @@ ul {
|
||||||
|
|
||||||
#ender-app {
|
#ender-app {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.scrollbar {
|
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,18 +553,6 @@ ul {
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-mask-sm {
|
|
||||||
mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0%), rgb(0 0 0 / 100%) 5%, rgb(0 0 0 / 100%) 95%, rgb(0 0 0 / 0%));
|
|
||||||
}
|
|
||||||
|
|
||||||
.rounded-1-sm {
|
|
||||||
border-radius: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.display-sm {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate__animated-sm {
|
.animate__animated-sm {
|
||||||
animation-duration: var(--animate-duration);
|
animation-duration: var(--animate-duration);
|
||||||
animation-fill-mode: both;
|
animation-fill-mode: both;
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
@forward 'vuetify/settings' with (
|
|
||||||
$utilities: false,
|
|
||||||
$color-pack: false,
|
|
||||||
$reset: false,
|
|
||||||
$layers: true,
|
|
||||||
);
|
|
|
@ -47,9 +47,10 @@ onMounted(() => setTimeout(type, 1500))
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<section>
|
||||||
<h3 class="alex">Page not found!</h3>
|
<h3>Page not found!</h3>
|
||||||
<pre
|
<pre class="whitespace-pre-wrap break-keep font-small">{{
|
||||||
class="whitespace-pre-wrap break-keep">{{ buffer }}<span class="blinker">▊</span></pre>
|
buffer
|
||||||
|
}}<span class="blinker">▊</span></pre>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
import sky from '~/assets/images/textures/sky.png'
|
import sky from '~/assets/images/textures/sky.png'
|
||||||
import particles from '~/assets/images/textures/particles.png'
|
import particles from '~/assets/images/textures/particles.png'
|
||||||
|
|
||||||
|
const { $local } = useNuxtApp()
|
||||||
const config = useAppConfig()
|
const config = useAppConfig()
|
||||||
const fqdn = config.url.split('//')[1]
|
const fqdn = config.url.split('//').at(1)
|
||||||
|
|
||||||
const resources: string[] = [sky, particles]
|
const resources: string[] = [sky, particles]
|
||||||
|
const animated = defineModel<boolean>({
|
||||||
const pages = storeToRefs(usePageStore())
|
required: true,
|
||||||
|
})
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
layout: {
|
layout: {
|
||||||
|
@ -138,7 +140,7 @@ class Portal {
|
||||||
constructor(
|
constructor(
|
||||||
directory: string = '/',
|
directory: string = '/',
|
||||||
create: boolean = true,
|
create: boolean = true,
|
||||||
animate: boolean = true,
|
animate: boolean = false,
|
||||||
randomize: boolean = false,
|
randomize: boolean = false,
|
||||||
fade: boolean = false,
|
fade: boolean = false,
|
||||||
speed: number = 1,
|
speed: number = 1,
|
||||||
|
@ -563,9 +565,6 @@ class Portal {
|
||||||
// Run the scene.
|
// Run the scene.
|
||||||
this.scene()
|
this.scene()
|
||||||
|
|
||||||
// TODO: Make it into a composable? The toggle will not work. Hardcoded.
|
|
||||||
if (!pages.animate.value) this.pause()
|
|
||||||
|
|
||||||
// Request the next animation frame if animation is enabled.
|
// Request the next animation frame if animation is enabled.
|
||||||
if (this.animate) requestAnimationFrame(this.render.bind(this))
|
if (this.animate) requestAnimationFrame(this.render.bind(this))
|
||||||
}
|
}
|
||||||
|
@ -573,10 +572,6 @@ class Portal {
|
||||||
resize() {
|
resize() {
|
||||||
if (!this.canvas.full()) this.canvas.fill()
|
if (!this.canvas.full()) this.canvas.fill()
|
||||||
|
|
||||||
// Pause the animation if the viewport becomes too small.
|
|
||||||
if (window.innerWidth <= 600) this.pause()
|
|
||||||
else this.continue()
|
|
||||||
|
|
||||||
// If we have animation disabled, we still have to re-render the scene once on resize.
|
// If we have animation disabled, we still have to re-render the scene once on resize.
|
||||||
if (!this.animate) requestAnimationFrame(this.render.bind(this))
|
if (!this.animate) requestAnimationFrame(this.render.bind(this))
|
||||||
}
|
}
|
||||||
|
@ -585,8 +580,8 @@ class Portal {
|
||||||
this.clickTime = Date.now()
|
this.clickTime = Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
continue() {
|
play() {
|
||||||
if (this.pauseTime === 0) return
|
if (this.pauseTime === 0) this.pauseTime = this.currentTime
|
||||||
|
|
||||||
// Add the time that has passed since the pause to the current time.
|
// Add the time that has passed since the pause to the current time.
|
||||||
this.currentTime += Date.now() - this.pauseTime
|
this.currentTime += Date.now() - this.pauseTime
|
||||||
|
@ -619,47 +614,50 @@ class Portal {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// If the localStorage value is null, trigger the setup.
|
||||||
|
// If the viewport is too small, disable by default.
|
||||||
|
const ecmaportal: string | null | undefined = $local.getItem('ecmaportal')
|
||||||
|
|
||||||
|
if (ecmaportal === null) {
|
||||||
|
const notMobile = window.innerWidth > 600
|
||||||
|
|
||||||
|
$local.setItem('ecmaportal', notMobile)
|
||||||
|
animated.value = notMobile
|
||||||
|
} else animated.value = ecmaportal === 'true'
|
||||||
|
|
||||||
const layout = document.querySelector(props.layout)
|
const layout = document.querySelector(props.layout)
|
||||||
const portal = new Portal(
|
const portal = new Portal(
|
||||||
props.directory,
|
props.directory,
|
||||||
props.create,
|
props.create,
|
||||||
props.animate && window.innerWidth > 600,
|
animated.value,
|
||||||
props.randomize,
|
props.randomize,
|
||||||
props.fade,
|
props.fade,
|
||||||
props.speed,
|
props.speed,
|
||||||
)
|
)
|
||||||
|
|
||||||
layout!.addEventListener('mousedown', ((e: UIEvent) => {
|
const click: EventListener = (e: Event) => {
|
||||||
if (e.target === e.currentTarget && e.detail >= 2) {
|
if (e.target === e.currentTarget) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
portal.click()
|
portal.click()
|
||||||
}
|
}
|
||||||
}) as EventListener)
|
}
|
||||||
|
|
||||||
|
for (const event of ['dblclick', 'touchstart'])
|
||||||
|
layout!.addEventListener(event, click)
|
||||||
|
|
||||||
|
// Save the new value to local storage.
|
||||||
|
watch(animated, (newAnimate) => {
|
||||||
|
$local.setItem('ecmaportal', newAnimate)
|
||||||
|
newAnimate ? portal.play() : portal.pause()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<canvas id="ecmaportal" class="parallax">
|
<canvas id="ecmaportal" class="fixed top-0 left-0 opacity-0 parallax -z-10">
|
||||||
<span>
|
<span>
|
||||||
Your browser does not support the <canvas /> element, which is
|
Your browser does not support the <canvas /> element, which is
|
||||||
required for parallax animation.
|
required for parallax animation.
|
||||||
</span>
|
</span>
|
||||||
</canvas>
|
</canvas>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.parallax {
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.6942s ease-in;
|
|
||||||
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<hr class="accent-text accent-gradient border-0 h-px my-4" />
|
||||||
|
<p>
|
||||||
|
<strong>🚧 This page is currently under construction.</strong> Expect a
|
||||||
|
lot more content to be added as time passes.
|
||||||
|
<em>Please report all bugs you encounter via the link in the footer</em>,
|
||||||
|
I will make sure to sand them down.
|
||||||
|
</p>
|
||||||
|
<hr class="accent-text accent-gradient border-0 h-px my-4" />
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,59 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
const props = defineProps({
|
|
||||||
opaque: Boolean,
|
|
||||||
})
|
|
||||||
|
|
||||||
const config = useAppConfig()
|
|
||||||
const currentYear = new Date().getFullYear()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<footer
|
|
||||||
class="user-select-none pass-through text-align-center line-height-1-5"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="font-small"
|
|
||||||
:class="{
|
|
||||||
'footer-transparent': !props.opaque,
|
|
||||||
'text-shadow': !props.opaque,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
© 2018-{{ currentYear }},
|
|
||||||
<a
|
|
||||||
:class="{
|
|
||||||
'link-hi-force-dark': !props.opaque,
|
|
||||||
'link-hi': props.opaque,
|
|
||||||
}"
|
|
||||||
:href="config.url"
|
|
||||||
>Enderman</a
|
|
||||||
>. All rights reserved.
|
|
||||||
<wbr />
|
|
||||||
<sub class="nobr">
|
|
||||||
β{{ config.build.version ? config.build.version : '?.?.?' }} ({{
|
|
||||||
config.build.date ? config.build.date : '1970-01-01'
|
|
||||||
}})
|
|
||||||
</sub>
|
|
||||||
</span>
|
|
||||||
</footer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.footer-transparent {
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
opacity: 0.25;
|
|
||||||
transition: ease 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-height-1-5 {
|
|
||||||
line-height: 14px * 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-shadow {
|
|
||||||
text-shadow: black 0 2px 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -29,10 +29,11 @@ const props = defineProps({
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
class="flex flex-row items-center text-inherit hover:text-inherit select-none sm:m-auto lg:m-0"
|
class="flex flex-row items-center text-inherit hover:text-inherit select-none sm:m-auto lm:m-0 lt:m-0 lg:m-0"
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
|
class="transition-all"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
:width="props.width"
|
:width="props.width"
|
||||||
:height="props.height"
|
:height="props.height"
|
||||||
|
@ -41,22 +42,14 @@ const props = defineProps({
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h2>{{ props.title }}</h2>
|
<h2>{{ props.title }}</h2>
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
<hr class="accent-text accent-gradient border-0 h-px" />
|
||||||
<p>{{ props.description }}</p>
|
<p>{{ props.description }}</p>
|
||||||
</div>
|
</div>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
a {
|
a:hover > img {
|
||||||
> img {
|
|
||||||
transition: ease 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
> img {
|
|
||||||
transform: scale(105%);
|
transform: scale(105%);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -12,7 +12,7 @@ const links = toRaw(pages.value).filter((page) => page.path !== '/')
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav
|
<nav
|
||||||
class="flex flex-row flex-wrap sm:flex-col lg:flex-row justify-around sm:justify-start lg:justify-between gap-2 lg:gap-3"
|
class="flex flex-row flex-wrap lm:flex-row lt:flex-row sm:flex-col lg:flex-row justify-around sm:justify-start lm:justify-between lt:justify-between lg:justify-between gap-2 lg:gap-4"
|
||||||
>
|
>
|
||||||
<Logo
|
<Logo
|
||||||
:src="logo"
|
:src="logo"
|
||||||
|
@ -21,7 +21,7 @@ const links = toRaw(pages.value).filter((page) => page.path !== '/')
|
||||||
description="official website"
|
description="official website"
|
||||||
/>
|
/>
|
||||||
<ul
|
<ul
|
||||||
class="flex flex-row flex-wrap items-center sm:items-start lg:items-center justify-center gap-3 m-2 sm:m-0 lg:mx-2 sm:my-2 lg:my-0"
|
class="flex flex-row flex-wrap items-center sm:items-start lm:items-center lt:items-center lg:items-center justify-center gap-4 m-2 sm:m-0 lm:mx-2 lt:mx-2 lg:mx-2 sm:my-2 lg:my-0"
|
||||||
>
|
>
|
||||||
<li v-for="(page, index) in links" :key="index" class="nav-item">
|
<li v-for="(page, index) in links" :key="index" class="nav-item">
|
||||||
<Route
|
<Route
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import cogIcon from '~/assets/images/icons/cog.png'
|
|
||||||
import pearlIcon from '~/assets/images/icons/pearl.gif'
|
|
||||||
|
|
||||||
const theme = useVThemeSSR()
|
|
||||||
const config = useAppConfig()
|
|
||||||
const fqdn = config.url.split('//')[1]
|
|
||||||
|
|
||||||
const mailTemplate = `I've just found a bug on ${config.url} and would like to report it.%0D%0A%0D%0AWebsite version: ${
|
|
||||||
config.build.version
|
|
||||||
}%0D%0ABuild date: ${
|
|
||||||
config.build.date
|
|
||||||
}%0D%0ATimestamp: ${new Date().toISOString()}%0D%0A%0D%0A%0D%0ASteps to reproduce:%0D%0A{ Explain in detail what happened here, or attach a video/screenshot }%0D%0A%0D%0AAdditional information:%0D%0A{ Helpful information, such as developer console output / Leave empty if none }%0D%0A%0D%0A%0D%0A// Keep in mind that it's just a template, you can change it as you wish! :)`
|
|
||||||
|
|
||||||
const pages = storeToRefs(usePageStore())
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<VDialog width="auto">
|
|
||||||
<template #activator="{ props }">
|
|
||||||
<div v-bind="props" class="options clickable">
|
|
||||||
<img
|
|
||||||
draggable="false"
|
|
||||||
:src="cogIcon"
|
|
||||||
alt="Options"
|
|
||||||
class="logo user-select-none"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #default="{ isActive }">
|
|
||||||
<VCard>
|
|
||||||
<img
|
|
||||||
draggable="false"
|
|
||||||
:src="pearlIcon"
|
|
||||||
alt="Pearl"
|
|
||||||
class="icon-badge user-select-none"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template #title>
|
|
||||||
<h3 class="alex text-align-center">Site options</h3>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="overlay background"></div>
|
|
||||||
<template #text>
|
|
||||||
<p class="center mb-3">
|
|
||||||
This tab is experimental and isn't ready for production.
|
|
||||||
<em>I was too lazy to exclude it from the build.</em><br />
|
|
||||||
<strong>Features provided here may or may not work.</strong>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<VRow>
|
|
||||||
<VCol>
|
|
||||||
<strong>Theme</strong>
|
|
||||||
<p>Available in all flavors, vanilla and chocolate.</p>
|
|
||||||
<VBtn variant="flat" color="secondary" @click="theme.toggle()">
|
|
||||||
Toggle theme
|
|
||||||
</VBtn>
|
|
||||||
</VCol>
|
|
||||||
<VCol>
|
|
||||||
<strong>Animation</strong>
|
|
||||||
<p>
|
|
||||||
Some computers may have issues rendering that gorgeous
|
|
||||||
background animation. Disabling it substantially decreases
|
|
||||||
power consumption, but also makes the website less cool.
|
|
||||||
</p>
|
|
||||||
<VBtn
|
|
||||||
variant="flat"
|
|
||||||
color="primary"
|
|
||||||
@click="pages.animate.value = !pages.animate.value"
|
|
||||||
>
|
|
||||||
Stop animation
|
|
||||||
</VBtn>
|
|
||||||
</VCol>
|
|
||||||
</VRow>
|
|
||||||
</div>
|
|
||||||
<span class="mt-3">
|
|
||||||
Since you're so curious and thus here, why not report a bug from the
|
|
||||||
production environment behind this tab?<br />
|
|
||||||
It will greatly help me improve the website!
|
|
||||||
<EMail
|
|
||||||
class="link-hi"
|
|
||||||
:address="`contact@${fqdn}`"
|
|
||||||
:cc="`admin@${fqdn}`"
|
|
||||||
:subject="`Bug report: ${fqdn}`"
|
|
||||||
:body="mailTemplate"
|
|
||||||
>
|
|
||||||
<strong>Report a bug</strong>
|
|
||||||
</EMail>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #actions>
|
|
||||||
<VSpacer />
|
|
||||||
<VBtn
|
|
||||||
variant="outlined"
|
|
||||||
prepend-icon="close"
|
|
||||||
@click="isActive.value = false"
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</VBtn>
|
|
||||||
</template>
|
|
||||||
</VCard>
|
|
||||||
</template>
|
|
||||||
</VDialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.options {
|
|
||||||
display: flex;
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-badge {
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
top: -48px;
|
|
||||||
left: -32px;
|
|
||||||
|
|
||||||
transform: rotate(-15deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -17,23 +17,32 @@ const props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Link',
|
default: 'Link',
|
||||||
},
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 32,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 32,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
class="flex flex-row items-center gap-2 select-none no-decoration"
|
class="flex flex-row items-center gap-2 select-none text-inherit no-underline"
|
||||||
active-class="icon-active sm:px-3"
|
active-class="active"
|
||||||
:to="!props.external ? props.path : undefined"
|
:to="!props.external ? props.path : undefined"
|
||||||
:href="props.external ? props.path : undefined"
|
:href="props.external ? props.path : undefined"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="icon-image"
|
|
||||||
draggable="false"
|
draggable="false"
|
||||||
:src="props.icon"
|
:src="props.icon"
|
||||||
:alt="props.alt"
|
:alt="props.alt"
|
||||||
|
:width="props.width"
|
||||||
|
:height="props.height"
|
||||||
/>
|
/>
|
||||||
<span class="display-sm">
|
<span class="hidden lm:hidden lt:hidden sm:block">
|
||||||
<strong>
|
<strong>
|
||||||
{{ props.name }}
|
{{ props.name }}
|
||||||
</strong>
|
</strong>
|
||||||
|
@ -42,14 +51,8 @@ const props = defineProps({
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.icon {
|
.active {
|
||||||
&-active {
|
@apply px-4;
|
||||||
transform: translate(2px, 2px) rotate3d(1, 1, 1, 5deg) scale(1.25);
|
transform: translate(2px, 2px) rotate3d(1, 1, 1, 5deg) scale(1.25);
|
||||||
}
|
|
||||||
|
|
||||||
&-image {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import cogIcon from '~/assets/images/icons/cog.png'
|
||||||
|
import pearlIcon from '~/assets/images/icons/pearl.gif'
|
||||||
|
|
||||||
|
const config = useAppConfig()
|
||||||
|
const { animate } = storeToRefs(usePageStore())
|
||||||
|
|
||||||
|
const fqdn = config.url.split('//').at(1)
|
||||||
|
const mailTemplate = `I've just found a bug on ${config.url} and would like to report it.%0D%0A%0D%0AWebsite version: ${
|
||||||
|
config.build.version
|
||||||
|
}%0D%0ABuild date: ${
|
||||||
|
config.build.date
|
||||||
|
}%0D%0ATimestamp: ${new Date().toISOString()}%0D%0A%0D%0A%0D%0ASteps to reproduce:%0D%0A{ Explain in detail what happened here, or attach a video/screenshot }%0D%0A%0D%0AAdditional information:%0D%0A{ Helpful information, such as developer console output / Leave empty if none }%0D%0A%0D%0A%0D%0A// Keep in mind that it's just a template, you can change it as you wish! :)`
|
||||||
|
|
||||||
|
const active = ref(false)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="absolute top-0 right-0 flex flex-col cursor-pointer select-none p-2"
|
||||||
|
@click="active = true"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
draggable="false"
|
||||||
|
:src="cogIcon"
|
||||||
|
alt="Options"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Transition
|
||||||
|
enter-active-class="animate__animated animate__fadeIn"
|
||||||
|
leave-active-class="animate__animated animate__fadeOut"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="active"
|
||||||
|
class="fixed top-0 left-0 flex flex-row justify-center items-center w-full h-full z-20"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="relative flex flex-col gap-4 accent-overlay-background rounded-xl max-w-[800px] mx-4 p-4"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
draggable="false"
|
||||||
|
:src="pearlIcon"
|
||||||
|
alt="Pearl"
|
||||||
|
class="absolute -top-8 -left-6 badge select-none w-16 lg:w-auto"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h3 class="text-center">Site settings</h3>
|
||||||
|
|
||||||
|
<div class="overlay accent-overlay-background" />
|
||||||
|
<div class="overlay">
|
||||||
|
<p class="mb-3">
|
||||||
|
The site settings are experimental. Suggest what you want to see
|
||||||
|
here next!
|
||||||
|
<strong>Please report any bugs that may occur.</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="flex flex-col md:flex-row gap-4">
|
||||||
|
<div class="md:basis-0 flex-grow">
|
||||||
|
<strong>Theme</strong>
|
||||||
|
<p>Available in vanilla and chocolate flavors.</p>
|
||||||
|
|
||||||
|
<select
|
||||||
|
v-model="$colorMode.preference"
|
||||||
|
class="accent accent-overlay-background"
|
||||||
|
>
|
||||||
|
<option value="system">System</option>
|
||||||
|
<option value="light">Light</option>
|
||||||
|
<option value="dark">Dark</option>
|
||||||
|
<option value="sepia">Sepia</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="md:basis-0 flex-grow">
|
||||||
|
<strong>Animation</strong>
|
||||||
|
<p>
|
||||||
|
Some computers may have issues rendering that gorgeous
|
||||||
|
background animation. Disabling it substantially decreases power
|
||||||
|
consumption, but also makes the website less cool.
|
||||||
|
</p>
|
||||||
|
<input v-model="animate" type="checkbox" color="primary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="mt-3">
|
||||||
|
<EMail
|
||||||
|
:address="`contact@${fqdn}`"
|
||||||
|
:cc="`admin@${fqdn}`"
|
||||||
|
:subject="`Bug report: ${fqdn}`"
|
||||||
|
:body="mailTemplate"
|
||||||
|
>
|
||||||
|
<strong>Report a bug</strong>
|
||||||
|
</EMail>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button @click="active = false">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.badge {
|
||||||
|
transform: rotate(-15deg);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,9 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia'
|
const { pages } = storeToRefs(usePageStore())
|
||||||
import { usePageStore } from '~/stores/pages'
|
|
||||||
|
|
||||||
const pageStore = usePageStore()
|
|
||||||
const { pages } = storeToRefs(pageStore)
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const state = computed(() => {
|
const state = computed(() => {
|
||||||
|
@ -24,39 +20,18 @@ const state = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="control-layout pass-through">
|
<div
|
||||||
<NuxtLink class="control-icon no-decoration" :to="state.prev">
|
class="flex fixed flex-row justify-between items-center w-full h-full pass-through"
|
||||||
<Icon name="iconoir:nav-arrow-left" />
|
>
|
||||||
|
<NuxtLink
|
||||||
|
class="opacity-25 hover:opacity-100 text-inherit no-underline transition-all"
|
||||||
|
:to="state.prev"
|
||||||
|
>
|
||||||
|
<iconify-icon icon="iconoir:nav-arrow-left" inline />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink class="control-icon no-decoration" :to="state.next">
|
<NuxtLink class="control-icon text-inherit no-underline" :to="state.next">
|
||||||
<Icon name="iconoir:nav-arrow-right" />
|
<iconify-icon icon="iconoir:nav-arrow-right" inline />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.control {
|
|
||||||
&-layout {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-icon {
|
|
||||||
opacity: 0.25;
|
|
||||||
transition: 0.15s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ switch (props.icon) {
|
||||||
:alt="`Download`"
|
:alt="`Download`"
|
||||||
class="icon-image"
|
class="icon-image"
|
||||||
/>
|
/>
|
||||||
<h3 class="mb-0 nobr">{{ props.title }}</h3>
|
<h3 class="whitespace-nowrap mb-0">{{ props.title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box p-4 rounded-xl mb-4">
|
<div class="box p-4 rounded-xl mb-4">
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -30,7 +30,7 @@ const props = defineProps({
|
||||||
})
|
})
|
||||||
|
|
||||||
const sentence = computed(() => {
|
const sentence = computed(() => {
|
||||||
return props.description.split('.')[0] + '...'
|
return props.description.split('.').at(0) + '...'
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -50,9 +50,9 @@ const sentence = computed(() => {
|
||||||
|
|
||||||
<div class="mb-5 px-5" style="text-shadow: black 1px 1px 10px">
|
<div class="mb-5 px-5" style="text-shadow: black 1px 1px 10px">
|
||||||
<h1 class="m-0 text-[60px]">{{ title }}</h1>
|
<h1 class="m-0 text-[60px]">{{ title }}</h1>
|
||||||
<strong class="min-h-[90px] m-0 text-[24px]" style="font-family: Lato">{{
|
<strong class="min-h-[90px] m-0 text-[24px]" style="font-family: Lato">
|
||||||
sentence
|
{{ sentence }}
|
||||||
}}</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import { useDark, useToggle } from '@vueuse/core'
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const { $vuetify } = useNuxtApp()
|
|
||||||
const config = useAppConfig()
|
|
||||||
|
|
||||||
const dark = useDark({
|
|
||||||
valueLight: config.theme.light,
|
|
||||||
valueDark: config.theme.dark,
|
|
||||||
onChanged: (dark: boolean) => {
|
|
||||||
$vuetify.theme.global.name.value = dark
|
|
||||||
? config.theme.dark
|
|
||||||
: config.theme.light
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const toggle = useToggle(dark)
|
|
||||||
|
|
||||||
return { dark, toggle }
|
|
||||||
}
|
|
50
config.ts
50
config.ts
|
@ -1,4 +1,3 @@
|
||||||
import type { ThemeDefinition } from 'vuetify'
|
|
||||||
import packageJSON from './package.json'
|
import packageJSON from './package.json'
|
||||||
|
|
||||||
interface TitleConfig {
|
interface TitleConfig {
|
||||||
|
@ -11,15 +10,6 @@ interface BuildConfig {
|
||||||
version: string
|
version: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ThemeConfig {
|
|
||||||
file: string
|
|
||||||
cookie: string
|
|
||||||
default: string
|
|
||||||
light: string
|
|
||||||
dark: string
|
|
||||||
themes: Record<string, ThemeDefinition>
|
|
||||||
}
|
|
||||||
|
|
||||||
type config = {
|
type config = {
|
||||||
url: string
|
url: string
|
||||||
shortener: string
|
shortener: string
|
||||||
|
@ -28,7 +18,6 @@ type config = {
|
||||||
description: string
|
description: string
|
||||||
locale: string
|
locale: string
|
||||||
build: BuildConfig
|
build: BuildConfig
|
||||||
theme: ThemeConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -46,43 +35,4 @@ export default {
|
||||||
date: new Date().toISOString().split('T')[0],
|
date: new Date().toISOString().split('T')[0],
|
||||||
version: packageJSON.version || '0.0.0',
|
version: packageJSON.version || '0.0.0',
|
||||||
},
|
},
|
||||||
theme: {
|
|
||||||
file: './assets/styles/vuetify.scss',
|
|
||||||
cookie: 'color-scheme',
|
|
||||||
default: 'chocolate',
|
|
||||||
light: 'vanilla',
|
|
||||||
dark: 'chocolate',
|
|
||||||
themes: {
|
|
||||||
vanilla: {
|
|
||||||
dark: false,
|
|
||||||
colors: {
|
|
||||||
background: '#FFFFFF',
|
|
||||||
surface: '#FFFFFF',
|
|
||||||
primary: '#6200EE',
|
|
||||||
'primary-darken-1': '#3700B3',
|
|
||||||
secondary: '#03DAC6',
|
|
||||||
'secondary-darken-1': '#018786',
|
|
||||||
error: '#B00020',
|
|
||||||
info: '#2196F3',
|
|
||||||
success: '#4CAF50',
|
|
||||||
warning: '#FB8C00',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
chocolate: {
|
|
||||||
dark: true,
|
|
||||||
colors: {
|
|
||||||
background: '#000',
|
|
||||||
surface: '#000',
|
|
||||||
primary: '#795548',
|
|
||||||
'primary-darken-1': '#5D4037',
|
|
||||||
secondary: '#FF9800',
|
|
||||||
'secondary-darken-1': '#F57C00',
|
|
||||||
error: '#B00020',
|
|
||||||
info: '#2196F3',
|
|
||||||
success: '#4CAF50',
|
|
||||||
warning: '#FB8C00',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies config as config
|
} satisfies config as config
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// @ts-check
|
||||||
|
import withNuxt from './.nuxt/eslint.config.mjs'
|
||||||
|
|
||||||
|
export default withNuxt({
|
||||||
|
rules: {
|
||||||
|
'no-unused-vars': 'warn',
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/max-attributes-per-line': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
singleline: 3,
|
||||||
|
multiline: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'space-in-parens': 'off',
|
||||||
|
'computed-property-spacing': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,65 +0,0 @@
|
||||||
import 'iconify-icon'
|
|
||||||
import type { IconAliases, IconSet } from 'vuetify'
|
|
||||||
|
|
||||||
const aliases = <IconAliases>{
|
|
||||||
/* custom ones */
|
|
||||||
email: 'mdi:email',
|
|
||||||
/* vuetify aliases */
|
|
||||||
collapse: 'mdi:chevron-up',
|
|
||||||
complete: 'mdi:check',
|
|
||||||
cancel: 'mdi:close-circle',
|
|
||||||
close: 'mdi:close',
|
|
||||||
delete: 'mdi:close-circle',
|
|
||||||
// delete (e.g. v-chip close)
|
|
||||||
clear: 'mdi:close-circle',
|
|
||||||
success: 'mdi:check-circle',
|
|
||||||
info: 'mdi:information',
|
|
||||||
warning: 'mdi:alert-circle',
|
|
||||||
error: 'mdi:close-circle',
|
|
||||||
prev: 'mdi:chevron-left',
|
|
||||||
next: 'mdi:chevron-right',
|
|
||||||
checkboxOn: 'mdi:checkbox-marked',
|
|
||||||
checkboxOff: 'mdi:checkbox-blank-outline',
|
|
||||||
checkboxIndeterminate: 'mdi:minus-box',
|
|
||||||
delimiter: 'mdi:circle',
|
|
||||||
// for carousel
|
|
||||||
sortAsc: 'mdi:arrow-up',
|
|
||||||
sortDesc: 'mdi:arrow-down',
|
|
||||||
expand: 'mdi:chevron-down',
|
|
||||||
menu: 'mdi:menu',
|
|
||||||
subgroup: 'mdi:menu-down',
|
|
||||||
dropdown: 'mdi:menu-down',
|
|
||||||
radioOn: 'mdi:radiobox-marked',
|
|
||||||
radioOff: 'mdi:radiobox-blank',
|
|
||||||
edit: 'mdi:pencil',
|
|
||||||
ratingEmpty: 'mdi:star-outline',
|
|
||||||
ratingFull: 'mdi:star',
|
|
||||||
ratingHalf: 'mdi:star-half-full',
|
|
||||||
loading: 'mdi:cached',
|
|
||||||
first: 'mdi:page-first',
|
|
||||||
last: 'mdi:page-last',
|
|
||||||
unfold: 'mdi:unfold-more-horizontal',
|
|
||||||
file: 'mdi:paperclip',
|
|
||||||
plus: 'mdi:plus',
|
|
||||||
minus: 'mdi:minus',
|
|
||||||
calendar: 'mdi:calendar',
|
|
||||||
$checkboxOn: 'mdi:checkbox-marked',
|
|
||||||
$checkboxOff: 'mdi:checkbox-blank-outline',
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconify = <IconSet>{
|
|
||||||
component: (props: any) => {
|
|
||||||
const { icon, tag, ...rest } = props
|
|
||||||
const strIcon = icon as string
|
|
||||||
|
|
||||||
return h(tag, rest, [
|
|
||||||
h('iconify-icon', {
|
|
||||||
key: strIcon,
|
|
||||||
icon: aliases[strIcon] ?? icon,
|
|
||||||
...rest,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export { aliases, iconify }
|
|
|
@ -1,8 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { UseSwipeDirection } from '@vueuse/core'
|
import type { UseSwipeDirection } from '@vueuse/core'
|
||||||
import { useSwipe } from '@vueuse/core'
|
import { useSwipe } from '@vueuse/core'
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { usePageStore } from '~/stores/pages'
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
@ -36,23 +34,34 @@ const swipe = useSwipe(card, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const animationComplete = ref(false)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (card.value)
|
||||||
|
card.value.addEventListener('animationend', () => {
|
||||||
|
animationComplete.value = true
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main
|
<main
|
||||||
ref="card"
|
ref="card"
|
||||||
class="dimensions background h-animated overflow-auto flex flex-col gap-3 sm:gap-2 px-4 pt-4 pb-3"
|
class="dimensions accent-background transition-ease overflow-auto flex flex-col gap-4 lm:gap-0 sm:gap-2 py-2 px-6 sm:p-4"
|
||||||
:class="{
|
:class="{
|
||||||
'sm:rounded-xl': !reader,
|
'animate__animated-sm animate__delay-1-5s animate__fadeInDown':
|
||||||
|
!animationComplete,
|
||||||
|
'lm:rounded-none sm:rounded-xl sm:mt-8 lm:mt-0 lt:mt-0': !reader,
|
||||||
'!max-h-full h-full': reader,
|
'!max-h-full h-full': reader,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Options />
|
<Settings v-if="animationComplete" />
|
||||||
<Navigation />
|
<Navigation />
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
<NuxtPage
|
<NuxtPage
|
||||||
class="scrollbar fade-mask-sm flex-grow overflow-x-hidden overflow-y-auto min-h-full sm:py-4 sm:pe-3"
|
class="sm:fade-mask flex-grow overflow-y-auto h-full sm:py-4 sm:pe-4"
|
||||||
/>
|
/>
|
||||||
<slot name="footer" />
|
<slot v-if="animationComplete" name="footer" />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
176
nuxt.config.ts
176
nuxt.config.ts
|
@ -1,35 +1,35 @@
|
||||||
import config from './config'
|
import config from "./config";
|
||||||
|
|
||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
app: {
|
app: {
|
||||||
head: {
|
head: {
|
||||||
htmlAttrs: {
|
htmlAttrs: {
|
||||||
lang: config.locale || 'en',
|
lang: config.locale || "en",
|
||||||
},
|
},
|
||||||
link: [
|
link: [
|
||||||
{
|
{
|
||||||
rel: 'icon',
|
rel: "icon",
|
||||||
type: 'image/x-icon',
|
type: "image/x-icon",
|
||||||
href: '/favicon.ico',
|
href: "/favicon.ico",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
charset: 'utf-8',
|
charset: "utf-8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'viewport',
|
name: "viewport",
|
||||||
content: 'width=device-width, initial-scale=1',
|
content: "width=device-width, initial-scale=1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'http-equiv': 'X-UA-Compatible',
|
"http-equiv": "X-UA-Compatible",
|
||||||
content: 'ie=edge',
|
content: "ie=edge",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
pageTransition: { name: 'page', mode: 'out-in' },
|
pageTransition: { name: "page", mode: "out-in" },
|
||||||
rootId: 'ender-app',
|
rootId: "ender-app",
|
||||||
},
|
},
|
||||||
devtools: {
|
devtools: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -43,56 +43,64 @@ export default defineNuxtConfig({
|
||||||
components: {
|
components: {
|
||||||
dirs: [
|
dirs: [
|
||||||
{
|
{
|
||||||
path: '~/components',
|
path: "~/components",
|
||||||
pathPrefix: false,
|
pathPrefix: false,
|
||||||
extensions: ['.vue'],
|
extensions: [".vue"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
css: ['~/assets/styles/main.scss'],
|
css: ["~/assets/styles/main.scss"],
|
||||||
plugins: [],
|
plugins: [],
|
||||||
modules: [
|
modules: [
|
||||||
['@nuxtjs/eslint-module', { failOnError: false, lintOnStart: false }],
|
"@pinia/nuxt",
|
||||||
['@nuxtjs/stylelint-module', { failOnError: true, lintOnStart: false }],
|
"@nuxt/content",
|
||||||
'@pinia/nuxt',
|
"@nuxtjs/seo",
|
||||||
'@nuxt/content',
|
"@nuxtjs/google-fonts",
|
||||||
'vuetify-nuxt-module',
|
"@nuxtjs/tailwindcss",
|
||||||
'@nuxtjs/seo',
|
"@nuxtjs/color-mode",
|
||||||
'@nuxtjs/google-fonts',
|
"@nuxt/eslint",
|
||||||
'@nuxtjs/tailwindcss',
|
["@nuxtjs/stylelint-module", { failOnError: true, lintOnStart: false }],
|
||||||
],
|
],
|
||||||
|
colorMode: {
|
||||||
|
preference: "system",
|
||||||
|
fallback: "dark",
|
||||||
|
classPrefix: "",
|
||||||
|
classSuffix: "",
|
||||||
|
componentName: "NuxtTheme",
|
||||||
|
storageKey: "ecmatheme",
|
||||||
|
},
|
||||||
content: {
|
content: {
|
||||||
markdown: {
|
markdown: {
|
||||||
remarkPlugins: ['remark-reading-time'],
|
remarkPlugins: ["remark-reading-time"],
|
||||||
},
|
},
|
||||||
highlight: {
|
highlight: {
|
||||||
theme: 'github-dark',
|
theme: "github-dark",
|
||||||
langs: [
|
langs: [
|
||||||
'shell',
|
"shell",
|
||||||
'batch',
|
"batch",
|
||||||
'vb',
|
"vb",
|
||||||
'ini',
|
"ini",
|
||||||
'asm',
|
"asm",
|
||||||
'c',
|
"c",
|
||||||
'cpp',
|
"cpp",
|
||||||
'java',
|
"java",
|
||||||
'python',
|
"python",
|
||||||
'csv',
|
"csv",
|
||||||
'xml',
|
"xml",
|
||||||
'json',
|
"json",
|
||||||
'yaml',
|
"yaml",
|
||||||
'html',
|
"html",
|
||||||
'css',
|
"css",
|
||||||
'sass',
|
"sass",
|
||||||
'php',
|
"php",
|
||||||
'js',
|
"js",
|
||||||
'ts',
|
"ts",
|
||||||
'vue',
|
"vue",
|
||||||
'md',
|
"md",
|
||||||
'mdc',
|
"mdc",
|
||||||
'pascal',
|
"pascal",
|
||||||
'lisp',
|
"lisp",
|
||||||
'sql',
|
"sql",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -101,53 +109,7 @@ export default defineNuxtConfig({
|
||||||
crawlLinks: true,
|
crawlLinks: true,
|
||||||
autoSubfolderIndex: true,
|
autoSubfolderIndex: true,
|
||||||
failOnError: true,
|
failOnError: true,
|
||||||
routes: ['/robots.txt', '/sitemap.xml'],
|
routes: ["/robots.txt", "/sitemap.xml"],
|
||||||
},
|
|
||||||
},
|
|
||||||
vuetify: {
|
|
||||||
moduleOptions: {
|
|
||||||
importComposables: true,
|
|
||||||
prefixComposables: true,
|
|
||||||
styles: {
|
|
||||||
configFile: config.theme.file,
|
|
||||||
},
|
|
||||||
includeTransformAssetsUrls: true,
|
|
||||||
ssrClientHints: {
|
|
||||||
reloadOnFirstRequest: false,
|
|
||||||
viewportSize: true,
|
|
||||||
prefersColorScheme: true,
|
|
||||||
prefersColorSchemeOptions: {
|
|
||||||
cookieName: config.theme.cookie,
|
|
||||||
lightThemeName: config.theme.light,
|
|
||||||
darkThemeName: config.theme.dark,
|
|
||||||
useBrowserThemeOnly: true,
|
|
||||||
},
|
|
||||||
prefersReducedMotion: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vuetifyOptions: {
|
|
||||||
ssr: {
|
|
||||||
clientWidth: 0,
|
|
||||||
clientHeight: 0,
|
|
||||||
},
|
|
||||||
components: false,
|
|
||||||
labComponents: true,
|
|
||||||
directives: false,
|
|
||||||
date: {
|
|
||||||
adapter: 'vuetify',
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
defaultTheme: config.theme.default,
|
|
||||||
themes: config.theme.themes,
|
|
||||||
},
|
|
||||||
icons: {
|
|
||||||
defaultSet: 'custom',
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
VBtn: {
|
|
||||||
style: 'text-transform: none; letter-spacing: normal;',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
googleFonts: {
|
googleFonts: {
|
||||||
|
@ -198,22 +160,22 @@ export default defineNuxtConfig({
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
sitemap: {
|
sitemap: {
|
||||||
sources: ['/api/__sitemap__/content'],
|
sources: ["/api/__sitemap__/content"],
|
||||||
cacheMaxAgeSeconds: 360,
|
cacheMaxAgeSeconds: 360,
|
||||||
exclude: [],
|
exclude: [],
|
||||||
credits: false,
|
credits: false,
|
||||||
xslColumns: [
|
xslColumns: [
|
||||||
{ label: 'URL', width: '50%' },
|
{ label: "URL", width: "50%" },
|
||||||
{ label: 'Last Modified', select: 'sitemap:lastmod', width: '25%' },
|
{ label: "Last Modified", select: "sitemap:lastmod", width: "25%" },
|
||||||
{ label: 'Priority', select: 'sitemap:priority', width: '12.5%' },
|
{ label: "Priority", select: "sitemap:priority", width: "12.5%" },
|
||||||
{
|
{
|
||||||
label: 'Change Frequency',
|
label: "Change Frequency",
|
||||||
select: 'sitemap:changefreq',
|
select: "sitemap:changefreq",
|
||||||
width: '12.5%',
|
width: "12.5%",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
defaults: {
|
defaults: {
|
||||||
changefreq: 'yearly',
|
changefreq: "yearly",
|
||||||
priority: 0.7,
|
priority: 0.7,
|
||||||
lastmod: config.build.date,
|
lastmod: config.build.date,
|
||||||
},
|
},
|
||||||
|
@ -232,7 +194,7 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
vue: {
|
vue: {
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
isCustomElement: (tag) => tag === 'iconify-icon',
|
isCustomElement: (tag) => tag === "iconify-icon",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "enderapp",
|
"name": "enderapp",
|
||||||
"version": "0.2.1",
|
"version": "0.2.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -17,41 +17,43 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/content": "^2.12.1",
|
"@nuxt/content": "^2.12.1",
|
||||||
"@nuxt/devtools": "^1.3.3",
|
"@nuxt/devtools": "^1.3.3",
|
||||||
|
"@nuxt/eslint": "^0.3.13",
|
||||||
"@nuxt/types": "^2.17.3",
|
"@nuxt/types": "^2.17.3",
|
||||||
"@nuxtjs/eslint-config-typescript": "^12.1.0",
|
"@nuxtjs/color-mode": "^3.4.1",
|
||||||
"@nuxtjs/eslint-module": "^4.1.0",
|
|
||||||
"@nuxtjs/google-fonts": "^3.2.0",
|
"@nuxtjs/google-fonts": "^3.2.0",
|
||||||
"@nuxtjs/seo": "^2.0.0-rc.10",
|
"@nuxtjs/seo": "^2.0.0-rc.10",
|
||||||
"@nuxtjs/stylelint-module": "^5.2.0",
|
"@nuxtjs/stylelint-module": "^5.2.0",
|
||||||
"@nuxtjs/tailwindcss": "^6.12.0",
|
"@nuxtjs/tailwindcss": "^6.12.0",
|
||||||
"@pinia/nuxt": "^0.5.1",
|
"@pinia/nuxt": "^0.5.1",
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.13.0",
|
|
||||||
"@typescript-eslint/parser": "^7.13.0",
|
|
||||||
"animate.css": "latest",
|
"animate.css": "latest",
|
||||||
"caniuse-lite": "^1.0.30001634",
|
"caniuse-lite": "^1.0.30001634",
|
||||||
"eslint": "^8.56.0",
|
|
||||||
"eslint-config-prettier": "^9.1.0",
|
|
||||||
"eslint-plugin-nuxt": "^4.0.0",
|
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
|
||||||
"iconify-icon": "^1.0.8",
|
|
||||||
"nuxt": "^3.12.1",
|
"nuxt": "^3.12.1",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.2",
|
||||||
"sass": "^1.77.5",
|
"sass": "^1.77.5",
|
||||||
"stylelint": "^16.6.1",
|
"stylelint": "^16.6.1",
|
||||||
|
"stylelint-config-recommended-scss": "^14.0.0",
|
||||||
"stylelint-config-recommended-vue": "^1.5.0",
|
"stylelint-config-recommended-vue": "^1.5.0",
|
||||||
"stylelint-config-standard-scss": "^13.1.0",
|
"stylelint-config-standard-scss": "^13.1.0",
|
||||||
|
"stylelint-config-tailwindcss": "^0.0.7",
|
||||||
|
"stylelint-scss": "^6.3.1",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"vue": "^3.4.28",
|
"vue": "^3.4.28",
|
||||||
"vue-router": "^4.3.3",
|
"vue-router": "^4.3.3",
|
||||||
"vue-tsc": "^1.8.22",
|
"vue-tsc": "^1.8.22"
|
||||||
"vuetify": "^3.6.9",
|
|
||||||
"vuetify-nuxt-module": "^0.14.1"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@date-io/date-fns": "^3.0.0",
|
"@date-io/date-fns": "^3.0.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
"iconify-icon": "^2.1.0",
|
||||||
"remark-reading-time": "^2.0.1"
|
"remark-reading-time": "^2.0.1"
|
||||||
}
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.3%",
|
||||||
|
"not dead",
|
||||||
|
"defaults",
|
||||||
|
"fully supports es6-module",
|
||||||
|
"maintained node versions"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,27 +41,21 @@ useHead({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<section class="page">
|
||||||
<h4>About me</h4>
|
<h3>About me</h3>
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
<Construction />
|
||||||
<p>
|
|
||||||
<strong>🚧 This page is currently under construction.</strong> Expect a
|
|
||||||
lot more content to be added as time passes.
|
|
||||||
<em>Please report all bugs you encounter via the link in the footer</em>,
|
|
||||||
I will make sure to sand them down.
|
|
||||||
</p>
|
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
|
||||||
<p>
|
<p>
|
||||||
Nice to meet you! I'm Andrew, a {{ age }}-year-old guy from Kaluga,
|
Nice to meet you! I'm Andrew, a {{ age }}-year-old guy from Kaluga,
|
||||||
Russia. I have been developing software since I was 10, and I have always
|
Russia. I have been developing software since I was 10, and I have always
|
||||||
been interested in technology. I'm a middle-senior C/++ developer and a
|
been interested in technology. I'm a middle-senior C/++ developer and a
|
||||||
junior full-stack engineer.
|
junior full-stack engineer.
|
||||||
<sup>1</sup>
|
<sup>1</sup>
|
||||||
</p>
|
<br />
|
||||||
<p class="font-small">
|
<small>
|
||||||
<sup>1</sup>
|
<sup>1</sup>
|
||||||
All titles are based on knowledge and confidence, not on official work
|
All titles are based on knowledge and confidence, not on official work
|
||||||
experience.
|
experience.
|
||||||
|
</small>
|
||||||
</p>
|
</p>
|
||||||
<p><strong>Here's a little more about myself:</strong></p>
|
<p><strong>Here's a little more about myself:</strong></p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -70,11 +64,11 @@ useHead({
|
||||||
<li>My favorite field in mathematics is Algebraic Geometry.</li>
|
<li>My favorite field in mathematics is Algebraic Geometry.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
I work on all sorts of projects, most of the times simultaneously, which
|
I work on all sorts of projects, most of the time simultaneously, which
|
||||||
doesn't help with adhering to deadlines at all. However, you can always
|
doesn't help with adhering to deadlines at all. However, you can always
|
||||||
check them out!
|
check them out!
|
||||||
</p>
|
</p>
|
||||||
<h5 class="alex">FAQ</h5>
|
<h5>FAQ</h5>
|
||||||
<ul class="list-style-type-faq">
|
<ul class="list-style-type-faq">
|
||||||
<li><strong>Why are you called Endermanch?</strong></li>
|
<li><strong>Why are you called Endermanch?</strong></li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -84,10 +78,9 @@ useHead({
|
||||||
end sounded pretty good. I was 14 at the time of making that decision.
|
end sounded pretty good. I was 14 at the time of making that decision.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong
|
<strong>
|
||||||
>When will you make a new video? What happened to your
|
When will you make a new video? What happened to your schedule?
|
||||||
schedule?</strong
|
</strong>
|
||||||
>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
One day. Haven't been particularly motivated lately, but life's busy. 🙁
|
One day. Haven't been particularly motivated lately, but life's busy. 🙁
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatDate } from 'date-fns'
|
import { formatDate } from 'date-fns'
|
||||||
import { useAsyncData } from '#app'
|
import chestAnimation from '~/assets/images/chest.webp'
|
||||||
|
|
||||||
const config = useAppConfig()
|
const config = useAppConfig()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
let thumbnail: string | null = null
|
let thumbnail: string | null = null
|
||||||
let slug = useRoute().params.slug
|
let slug = route.params.slug
|
||||||
|
|
||||||
if (Array.isArray(slug)) slug = slug.join('/')
|
if (Array.isArray(slug)) slug = slug.join('/')
|
||||||
|
|
||||||
const { data } = await useAsyncData('home', () =>
|
const { data, status } = await useAsyncData('home', () =>
|
||||||
queryContent(`/blog/${slug}`).findOne(),
|
queryContent(`/blog/${slug}`).findOne(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,55 +59,55 @@ if (data.value) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hydrate the rendered items.
|
// Hydrate the rendered items.
|
||||||
onMounted(() => {
|
// onMounted(() => {
|
||||||
document.querySelectorAll('pre').forEach((pre) => {
|
// document.querySelectorAll('pre').forEach((pre) => {
|
||||||
const icon = document.createElement('iconify-icon')
|
// const icon = document.createElement('iconify-icon')
|
||||||
|
//
|
||||||
icon.setAttribute('icon', 'mdi:content-copy')
|
// icon.setAttribute('icon', 'mdi:content-copy')
|
||||||
icon.setAttribute('inline', 'true')
|
// icon.setAttribute('inline', 'true')
|
||||||
|
//
|
||||||
icon.classList.add('button')
|
// icon.classList.add('button')
|
||||||
|
//
|
||||||
pre.appendChild(icon)
|
// pre.appendChild(icon)
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
document
|
// document
|
||||||
.querySelectorAll('code:not(pre *), pre > iconify-icon.button')
|
// .querySelectorAll('code:not(pre *), pre > iconify-icon.button')
|
||||||
.forEach((code) => {
|
// .forEach((code) => {
|
||||||
if (code instanceof HTMLElement) {
|
// if (code instanceof HTMLElement) {
|
||||||
code.onclick = () => {
|
// code.onclick = () => {
|
||||||
const area = document.createElement('textarea')
|
// const area = document.createElement('textarea')
|
||||||
|
//
|
||||||
area.textContent =
|
// area.textContent =
|
||||||
code.nodeName && code.nodeName.toLowerCase() === 'code'
|
// code.nodeName && code.nodeName.toLowerCase() === 'code'
|
||||||
? code.textContent
|
// ? code.textContent
|
||||||
: code.parentElement!.textContent
|
// : code.parentElement!.textContent
|
||||||
|
//
|
||||||
// It's necessary to create the textarea element every time you copy to get access to the select() method.
|
// // It's necessary to create the textarea element every time you copy to get access to the select() method.
|
||||||
area.setSelectionRange(0, 99999) // An iOS gotcha.
|
// area.setSelectionRange(0, 99999) // An iOS gotcha.
|
||||||
area.select()
|
// area.select()
|
||||||
|
//
|
||||||
// Copy the text inside the textarea.
|
// // Copy the text inside the textarea.
|
||||||
navigator.clipboard.writeText(area.value)
|
// navigator.clipboard.writeText(area.value)
|
||||||
|
//
|
||||||
// TODO: Alert the user text has been successfully copied.
|
// // TODO: Alert the user text has been successfully copied.
|
||||||
|
//
|
||||||
// Remove the textarea element.
|
// // Remove the textarea element.
|
||||||
area.remove()
|
// area.remove()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
onUnmounted(() => {
|
// onUnmounted(() => {
|
||||||
document.querySelectorAll('code').forEach((code) => {
|
// document.querySelectorAll('code').forEach((code) => {
|
||||||
code.onclick = null
|
// code.onclick = null
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
document.querySelectorAll('pre').forEach((pre) => {
|
// document.querySelectorAll('pre').forEach((pre) => {
|
||||||
pre.querySelector('.clipboard')?.remove()
|
// pre.querySelector('.clipboard')?.remove()
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
|
@ -125,44 +126,68 @@ useHead({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="status === 'pending'"
|
||||||
|
class="flex flex-col items-center justify-center gap-4 w-full select-none text-center"
|
||||||
|
>
|
||||||
|
<img draggable="false" :src="chestAnimation" alt="The Ender Chest" />
|
||||||
|
<div>
|
||||||
|
<h1 class="font-enchant">Loading document...</h1>
|
||||||
|
<span class="opacity-0 hover:opacity-100 transition-ease">
|
||||||
|
<em>Loading document...</em>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<article
|
<article
|
||||||
v-if="data"
|
v-else-if="status === 'success' && data"
|
||||||
class="post scrollbar fade-mask-sm flex flex-col gap-3 flex-grow-1 overflow-x-hidden overflow-y-auto sm:py-4 sm:pe-3"
|
class="flex-grow post fade-mask-sm flex flex-col gap-4 overflow-x-hidden overflow-y-auto sm:py-4 sm:pe-4"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="post-preamble"
|
class="relative flex flex-col justify-end items-start w-full min-h-[400px] rounded-xl accent-text-shadow post-preamble"
|
||||||
:style="{ backgroundImage: 'url(' + thumbnail + ')' }"
|
:style="{ backgroundImage: 'url(' + thumbnail + ')' }"
|
||||||
>
|
>
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<h3 class="alex">{{ data.title }}</h3>
|
<h3>{{ data.title }}</h3>
|
||||||
<div class="flex flex-row gap-x-0 gap-y-2 flex-wrap">
|
<div class="flex flex-row flex-wrap gap-x-2 gap-y-0">
|
||||||
<div class="flex flex-row items-center gap-1">
|
<div class="flex flex-row items-center gap-2">
|
||||||
<iconify-icon icon="mdi:calendar" />
|
<iconify-icon icon="mdi:calendar" />
|
||||||
<strong class="nobr font-small">
|
<small class="whitespace-nowrap">
|
||||||
|
<strong>
|
||||||
{{ formatDate(data.created, 'LLLL do, y – HH:mm') }}
|
{{ formatDate(data.created, 'LLLL do, y – HH:mm') }}
|
||||||
</strong>
|
</strong>
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row items-center gap-1">
|
<div class="flex flex-row items-center gap-2">
|
||||||
<iconify-icon icon="mdi:clock-outline" />
|
<iconify-icon icon="mdi:clock-outline" />
|
||||||
<strong class="nobr font-small">
|
<small class="whitespace-nowrap">
|
||||||
|
<strong>
|
||||||
{{ data!.readingTime.text.split(' ')[0] + ' minutes to read' }}
|
{{ data!.readingTime.text.split(' ')[0] + ' minutes to read' }}
|
||||||
</strong>
|
</strong>
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<NuxtLink
|
||||||
|
to="/blog"
|
||||||
|
class="absolute top-0 left-0 p-2 text-inherit no-underline"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="icon-park-solid:back"
|
||||||
|
width="2em"
|
||||||
|
height="2em"
|
||||||
|
style="color: lavender"
|
||||||
|
/>
|
||||||
|
</NuxtLink>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:href="`https://twitter.com/share?url=${config.url}/blog/${slug}&text=${data.title}&hashtags=${data.tags.slice(0, 3).join(',').replace(/ /g, '')}`"
|
:href="`https://twitter.com/share?url=${config.url}/blog/${slug}&text=${data.title}&hashtags=${data.tags.slice(0, 3).join(',').replace(/ /g, '')}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="link post-preamble-share px-2 py-1"
|
class="absolute top-0 right-0 p-2"
|
||||||
>
|
>
|
||||||
<iconify-icon icon="mdi:twitter"></iconify-icon>
|
<iconify-icon icon="logos:twitter" width="2em" height="2em" />
|
||||||
</NuxtLink>
|
|
||||||
<NuxtLink to="/blog" class="link post-preamble-control px-2 py-1">
|
|
||||||
<iconify-icon icon="icon-park-solid:back"></iconify-icon>
|
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="post-content">
|
<div class="post-content">
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
<hr class="accent-text accent-gradient border-0 h-px" />
|
||||||
<ContentRenderer :value="data" />
|
<ContentRenderer :value="data" />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { formatDate } from 'date-fns'
|
||||||
|
|
||||||
const config = useAppConfig()
|
const config = useAppConfig()
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'The Enderchest',
|
title: 'The Ender Chest',
|
||||||
description:
|
description:
|
||||||
'A blog about software development, scientific research and Windows quirks.',
|
'A blog about software development, scientific research and Windows quirks.',
|
||||||
image: `${config.url}/images/chest.png`,
|
image: `${config.url}/images/chest.png`,
|
||||||
|
@ -25,7 +25,7 @@ useSeoMeta({
|
||||||
})
|
})
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: 'The Enderchest',
|
title: 'The Ender Chest',
|
||||||
htmlAttrs: {
|
htmlAttrs: {
|
||||||
lang: config.locale || 'en',
|
lang: config.locale || 'en',
|
||||||
},
|
},
|
||||||
|
@ -41,12 +41,12 @@ useHead({
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<section>
|
||||||
<h3 class="alex">The Enderchest</h3>
|
<h3 class="mb-2">The Ender Chest</h3>
|
||||||
<p>
|
<p class="mb-4">
|
||||||
You're browsing the enderchest — a blog about software development,
|
You're browsing the Ender Chest — a blog about software development,
|
||||||
scientific research and Windows quirks.
|
scientific research and Windows quirks.
|
||||||
</p>
|
</p>
|
||||||
<h4 class="alex">Recent posts</h4>
|
<h4 class="mb-2">Recent posts</h4>
|
||||||
<ContentList
|
<ContentList
|
||||||
:query="{
|
:query="{
|
||||||
path: '/blog',
|
path: '/blog',
|
||||||
|
@ -56,15 +56,15 @@ useHead({
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #default="{ list }">
|
<template #default="{ list }">
|
||||||
<div class="grid gap-3">
|
<div class="grid gap-4">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-for="post in list"
|
v-for="post in list"
|
||||||
:key="post._path"
|
:key="post._path"
|
||||||
:to="post._path"
|
:to="post._path"
|
||||||
class="no-decoration"
|
class="text-inherit hover:text-inherit no-underline"
|
||||||
>
|
>
|
||||||
<article
|
<article
|
||||||
class="post-box flex flex-col md:flex-row gap-3 rounded-xl h-full"
|
class="post-box flex flex-col md:flex-row gap-4 rounded-xl h-full"
|
||||||
>
|
>
|
||||||
<div class="post-thumb">
|
<div class="post-thumb">
|
||||||
<img
|
<img
|
||||||
|
@ -78,46 +78,48 @@ useHead({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<h3 class="post-title alex">{{ post.title }}</h3>
|
<h3 class="post-title">{{ post.title }}</h3>
|
||||||
<p class="post-description mb-0">{{ post.description }}</p>
|
<p class="post-description mb-0">{{ post.description }}</p>
|
||||||
|
|
||||||
<div class="post-tags flex flex-row flex-wrap gap-2 py-2">
|
<div class="post-tags flex flex-row flex-wrap gap-2 py-2">
|
||||||
<span
|
<span
|
||||||
v-for="(tag, index) in post.tags.slice(0, 3)"
|
v-for="(tag, index) in post.tags.slice(0, 3)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="nobr"
|
class="whitespace-nowrap"
|
||||||
>
|
>
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="post.tags.length > 3" class="nobr">
|
<span v-if="post.tags.length > 3" class="whitespace-nowrap">
|
||||||
{{ post.tags.length - 3 }} more
|
{{ post.tags.length - 3 }} more
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
<hr class="accent-text accent-gradient border-0 h-px" />
|
||||||
|
|
||||||
<div class="post-details py-2">
|
<div class="post-details py-2">
|
||||||
<div class="flex flex-row items-center gap-1">
|
<div class="flex flex-row items-center gap-2">
|
||||||
<iconify-icon icon="mdi:calendar" />
|
<iconify-icon icon="mdi:calendar" />
|
||||||
<small class="nobr">
|
<small class="whitespace-nowrap">
|
||||||
{{ formatDate(post.created, 'LLLL do, y – HH:mm') }}
|
{{ formatDate(post.created, 'LLLL do, y – HH:mm') }}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="post.updated"
|
v-if="post.updated"
|
||||||
class="flex flex-row items-center gap-1"
|
class="flex flex-row items-center gap-2"
|
||||||
>
|
>
|
||||||
<iconify-icon icon="mdi:pencil" />
|
<iconify-icon icon="mdi:pencil" />
|
||||||
<small class="nobr">
|
<small class="whitespace-nowrap">
|
||||||
{{ formatDate(post.updated, 'LLLL do, y – HH:mm') }}
|
{{ formatDate(post.updated, 'LLLL do, y – HH:mm') }}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="post-pocket flex flex-row items-center gap-1 p-2">
|
<div
|
||||||
|
class="post-pocket absolute bottom-0 right-0 uppercase rounded-tl-xl flex flex-row items-center gap-2 p-2"
|
||||||
|
>
|
||||||
<iconify-icon icon="mdi:clock-outline" />
|
<iconify-icon icon="mdi:clock-outline" />
|
||||||
<small class="nobr font-monospace font-small">
|
<small class="whitespace-nowrap font-mono font-small">
|
||||||
{{ post.readingTime.text }}
|
{{ post.readingTime.text }}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
@ -127,13 +129,11 @@ useHead({
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #not-found>
|
<template #not-found>
|
||||||
<div class="flex flex-col justify-center items-center gap-3">
|
<div class="flex flex-col justify-center items-center gap-4">
|
||||||
<span>No posts found 🙁</span>
|
<span>No posts found 🙁</span>
|
||||||
<NuxtLink to="/" class="link-hi">Back to index</NuxtLink>
|
<NuxtLink to="/">Back to index</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ContentList>
|
</ContentList>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
|
|
|
@ -46,18 +46,17 @@ defineRouteRules({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<section class="page">
|
||||||
<h3>Welcome 👋</h3>
|
<h3>Welcome 👋</h3>
|
||||||
<p class="mb-4">
|
<p>
|
||||||
I'm <strong>Enderman</strong> – a software engineer, a malware
|
I'm <strong>Enderman</strong> – a software engineer, a malware
|
||||||
enthusiast and most importantly, a weird tall creature. I have over 300K
|
enthusiast and most importantly, a weird tall creature. I have over 300K
|
||||||
subscribers on <a :href="`${config.shortener}/youtube`">YouTube</a> and
|
subscribers on <a :href="`${config.shortener}/youtube`">YouTube</a> and
|
||||||
over 25K followers on
|
over 25K followers on
|
||||||
<a class="text-inherit no-underline" :href="`${config.shortener}/twitter`"
|
<a :href="`${config.shortener}/twitter`">Twitter</a>. Sometimes I wish
|
||||||
>Twitter</a
|
there were 48 hours in a day.
|
||||||
>. Sometimes I wish there were 48 hours in a day.
|
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col md:flex-row gap-3 mb-4">
|
<div class="flex flex-col md:flex-row gap-4">
|
||||||
<div class="md:basis-0 md:flex-grow-[2]">
|
<div class="md:basis-0 md:flex-grow-[2]">
|
||||||
<h6>What I do</h6>
|
<h6>What I do</h6>
|
||||||
<ul class="list-style-type-do">
|
<ul class="list-style-type-do">
|
||||||
|
@ -66,7 +65,7 @@ defineRouteRules({
|
||||||
<strong>C/C++</strong> for desktop applications and any common
|
<strong>C/C++</strong> for desktop applications and any common
|
||||||
backend stack with framework-loaded TypeScript for web. You can take
|
backend stack with framework-loaded TypeScript for web. You can take
|
||||||
a look at my code on
|
a look at my code on
|
||||||
<a class="link-hi" :href="`${config.shortener}/github`">GitHub</a>.
|
<a :href="`${config.shortener}/github`">GitHub</a>.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
I have the most unnecessary, yet fascinating knowledge about
|
I have the most unnecessary, yet fascinating knowledge about
|
||||||
|
@ -79,8 +78,7 @@ defineRouteRules({
|
||||||
<li>
|
<li>
|
||||||
I research and analyze modern malware, educate computer users about
|
I research and analyze modern malware, educate computer users about
|
||||||
it and preserve history. The repository can be found
|
it and preserve history. The repository can be found
|
||||||
<a class="link-hi" :href="`${config.shortener}/repository`">here</a
|
<a :href="`${config.shortener}/repository`">here</a>.
|
||||||
>.
|
|
||||||
</li>
|
</li>
|
||||||
<li>I make videos for you to enjoy!</li>
|
<li>I make videos for you to enjoy!</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -95,7 +93,7 @@ defineRouteRules({
|
||||||
<li>Philosophy</li>
|
<li>Philosophy</li>
|
||||||
<li>Geopolitics</li>
|
<li>Geopolitics</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="link-hi" :href="`${config.shortener}/chess`">Chess</a>
|
<a :href="`${config.shortener}/chess`">Chess</a>
|
||||||
</li>
|
</li>
|
||||||
<li>Solitude</li>
|
<li>Solitude</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -67,20 +67,13 @@ useHead({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<section class="page">
|
||||||
<h3 class="alex">Projects</h3>
|
<h3>Projects</h3>
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
<Construction />
|
||||||
<p>
|
|
||||||
<strong>🚧 This page is currently under construction.</strong> Expect a
|
|
||||||
lot more content to be added as time passes.
|
|
||||||
<em>Please report all bugs you encounter via the link in the footer</em>,
|
|
||||||
I will make sure to sand them down.
|
|
||||||
</p>
|
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
|
||||||
<p><strong>My current projects are:</strong></p>
|
<p><strong>My current projects are:</strong></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(item, index) in projects" :key="index">
|
<li v-for="(item, index) in projects" :key="index">
|
||||||
<a class="link-hi" :href="item.url">
|
<a :href="item.url">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -115,39 +115,34 @@ useHead({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<section class="page">
|
||||||
<h3 class="alex">Online presence</h3>
|
<h3>Online presence</h3>
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
<Construction />
|
||||||
<p>
|
<div class="flex flex-col md:flex-row gap-4">
|
||||||
<strong>🚧 This page is currently under construction.</strong> Expect a
|
<div class="md:basis-0 md:flex-grow-[1]">
|
||||||
lot more content to be added as time passes.
|
<h5>Social media</h5>
|
||||||
<em>Please report all bugs you encounter via the link in the footer</em>,
|
|
||||||
I will make sure to sand them down.
|
|
||||||
</p>
|
|
||||||
<hr class="accent accent-gradient border-0 h-px" />
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-md-3">
|
|
||||||
<h5 class="alex">Social media</h5>
|
|
||||||
<ul class="list-style-type-none p-0">
|
<ul class="list-style-type-none p-0">
|
||||||
<li v-for="(page, index) in socials" :key="index">
|
<li v-for="(page, index) in socials" :key="index">
|
||||||
<a class="link-hi" target="_blank" rel="noopener" :href="page.url">
|
<NuxtLink target="_blank" rel="noopener" :href="page.url">
|
||||||
<Icon :name="page.icon.name" :color="page.icon.color" inline />
|
<iconify-icon
|
||||||
|
:icon="page.icon.name"
|
||||||
|
:style="{ color: page.icon.color }"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
inline
|
||||||
|
/>
|
||||||
<span class="mx-2">{{ page.name }}</span>
|
<span class="mx-2">{{ page.name }}</span>
|
||||||
</a>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="md:basis-0 md:flex-grow-[3]">
|
||||||
<h5 class="alex">Contact me</h5>
|
<h5>Contact me</h5>
|
||||||
<p>
|
<p>
|
||||||
Personal:
|
Personal:
|
||||||
<EMail
|
<EMail :address="`contact@${fqdn}`" subject="Hey Enderman!" /><br />
|
||||||
class="link-hi"
|
Manager: <EMail :address="`manager@${fqdn}`" /><br />
|
||||||
:address="`contact@${fqdn}`"
|
Abuse: <EMail :address="`abuse@${fqdn}`" />
|
||||||
subject="Hey Enderman!"
|
|
||||||
/><br />
|
|
||||||
Manager: <EMail class="link-hi" :address="`manager@${fqdn}`" /><br />
|
|
||||||
Abuse: <EMail class="link-hi" :address="`abuse@${fqdn}`" />
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
export default defineNuxtPlugin(() => {
|
||||||
|
return {
|
||||||
|
provide: {
|
||||||
|
local: {
|
||||||
|
getItem(item: string) {
|
||||||
|
if (import.meta.client) {
|
||||||
|
return localStorage.getItem(item)
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setItem(item: string, value: any) {
|
||||||
|
if (import.meta.client) {
|
||||||
|
return localStorage.setItem(item, value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,11 +0,0 @@
|
||||||
import { aliases, iconify } from '~/iconify'
|
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
|
||||||
nuxtApp.hook('vuetify:before-create', ({ vuetifyOptions }) => {
|
|
||||||
vuetifyOptions.icons = {
|
|
||||||
defaultSet: 'iconify',
|
|
||||||
aliases,
|
|
||||||
sets: { iconify },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/**
|
||||||
|
* @see https://prettier.io/docs/en/configuration.html
|
||||||
|
* @type {import("prettier").Config}
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
semi: false,
|
||||||
|
quoteProps: 'as-needed',
|
||||||
|
singleQuote: true,
|
||||||
|
useTabs: false,
|
||||||
|
tabWidth: 2,
|
||||||
|
trailingComma: 'all',
|
||||||
|
bracketSpacing: true,
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
import aboutIcon from '~/assets/images/icons/accent/info.png'
|
import aboutIcon from '~/assets/images/icons/accent/info.png'
|
||||||
import projectIcon from '~/assets/images/icons/defrag.png'
|
import projectIcon from '~/assets/images/icons/defrag.png'
|
||||||
import socialIcon from '~/assets/images/icons/user.png'
|
import socialIcon from '~/assets/images/icons/user.png'
|
||||||
|
@ -50,8 +48,9 @@ export const usePageStore = defineStore('page', () => {
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const reader = ref(false)
|
const reader = ref<boolean>(false)
|
||||||
const animate = ref(true)
|
const animate = ref<boolean>(false)
|
||||||
|
const dark = ref<boolean>(false)
|
||||||
|
|
||||||
function _autoFetchPages() {
|
function _autoFetchPages() {
|
||||||
while (pages.value.length) pages.value.pop()
|
while (pages.value.length) pages.value.pop()
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/** @type {import("stylelint").Config} */
|
||||||
|
export default {
|
||||||
|
defaultSeverity: 'warning',
|
||||||
|
formatter: 'compact',
|
||||||
|
cache: false,
|
||||||
|
fix: true,
|
||||||
|
extends: [
|
||||||
|
'stylelint-config-standard-scss',
|
||||||
|
'stylelint-config-recommended-scss',
|
||||||
|
'stylelint-config-tailwindcss/scss',
|
||||||
|
'stylelint-config-recommended-vue/scss',
|
||||||
|
],
|
||||||
|
plugins: ['stylelint-scss'],
|
||||||
|
rules: {
|
||||||
|
'at-rule-no-unknown': null,
|
||||||
|
'scss/at-rule-no-unknown': [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
ignoreAtRules: ['tailwind', 'responsive', 'screen'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'declaration-empty-line-before': null,
|
||||||
|
},
|
||||||
|
}
|
|
@ -11,6 +11,14 @@ export default <Partial<Config>>{
|
||||||
'2xl': '2560px',
|
'2xl': '2560px',
|
||||||
|
|
||||||
desktop: '960px',
|
desktop: '960px',
|
||||||
|
// Landscape Mobile (LM)
|
||||||
|
lm: {
|
||||||
|
raw: '(max-height: 600px)',
|
||||||
|
},
|
||||||
|
// Landscape Tablet (LT)
|
||||||
|
lt: {
|
||||||
|
raw: '(max-height: 996px) and (min-width: 601px) and (max-width: 1280px)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: [
|
sans: [
|
||||||
|
@ -38,6 +46,7 @@ export default <Partial<Config>>{
|
||||||
'monospace',
|
'monospace',
|
||||||
],
|
],
|
||||||
alex: ['Alexandria', 'serif'],
|
alex: ['Alexandria', 'serif'],
|
||||||
|
enchant: ['Enchant', 'serif'],
|
||||||
lato: ['Lato', 'sans-serif'],
|
lato: ['Lato', 'sans-serif'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue