style: run linter

This commit is contained in:
Sebin Nyshkim 2023-04-03 00:55:28 +02:00
parent de3862bf77
commit 4853e1ec63
26 changed files with 502 additions and 631 deletions

View file

@ -1,15 +1,15 @@
/* eslint-env node */ /* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution"); require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = { module.exports = {
root: true, root: true,
extends: [ extends: [
"plugin:vue/vue3-essential", 'plugin:vue/vue3-essential',
"eslint:recommended", 'eslint:recommended',
"@vue/eslint-config-typescript", '@vue/eslint-config-typescript',
"@vue/eslint-config-prettier", '@vue/eslint-config-prettier'
], ],
parserOptions: { parserOptions: {
ecmaVersion: "latest", ecmaVersion: 'latest'
}, }
}; }

View file

@ -1,41 +1,41 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, provide } from "vue"; import { ref, provide } from 'vue'
import { RouterView } from "vue-router"; import { RouterView } from 'vue-router'
import { modalResultKey, nsfwKey, showModalKey } from "@/keys"; import { modalResultKey, nsfwKey, showModalKey } from '@/keys'
import { version } from "../package.json"; import { version } from '../package.json'
import RefModal from "@/components/RefModal.vue"; import RefModal from '@/components/RefModal.vue'
import ButtonGroup from "@/components/ButtonGroup.vue"; import ButtonGroup from '@/components/ButtonGroup.vue'
import Button from "@/components/RefButton.vue"; import Button from '@/components/RefButton.vue'
import SiteHeader from "@/components/SiteHeader.vue"; import SiteHeader from '@/components/SiteHeader.vue'
import SiteFooter from "@/components/SiteFooter.vue"; import SiteFooter from '@/components/SiteFooter.vue'
import SiteNavigation from "@/components/SiteNavigation.vue"; import SiteNavigation from '@/components/SiteNavigation.vue'
const isNsfw = ref(false); const isNsfw = ref(false)
const isConfirmedHorny = ref(false); const isConfirmedHorny = ref(false)
const nsfwmodal = ref<InstanceType<typeof RefModal>>(); const nsfwmodal = ref<InstanceType<typeof RefModal>>()
const showModal = (): void => { const showModal = (): void => {
if (!isConfirmedHorny.value) { if (!isConfirmedHorny.value) {
nsfwmodal.value?.showModal(); nsfwmodal.value?.showModal()
setTimeout(() => { setTimeout(() => {
isNsfw.value = false; isNsfw.value = false
}, 1); }, 1)
} else { } else {
isNsfw.value = !isNsfw.value; isNsfw.value = !isNsfw.value
} }
}; }
const modalResult = (value: boolean): void => { const modalResult = (value: boolean): void => {
isNsfw.value = value; isNsfw.value = value
isConfirmedHorny.value = value; isConfirmedHorny.value = value
nsfwmodal.value?.close(); nsfwmodal.value?.close()
}; }
provide(modalResultKey, modalResult); provide(modalResultKey, modalResult)
provide(nsfwKey, isNsfw); provide(nsfwKey, isNsfw)
provide(showModalKey, showModal); provide(showModalKey, showModal)
</script> </script>
<template> <template>
@ -46,18 +46,13 @@ provide(showModalKey, showModal);
</template> </template>
<template #message> <template #message>
By enabling NSFW mode you confirm that you are of legal age to view adult By enabling NSFW mode you confirm that you are of legal age to view adult content.
content.
</template> </template>
<template #buttons> <template #buttons>
<ButtonGroup col> <ButtonGroup col>
<Button positive @click.prevent="modalResult(true)"> <Button positive @click.prevent="modalResult(true)"> Yes, show me the goods 👀 </Button>
Yes, show me the goods 👀 <Button negative @click.prevent="modalResult(false)"> NO, STAHP 😱 </Button>
</Button>
<Button negative @click.prevent="modalResult(false)">
NO, STAHP 😱
</Button>
</ButtonGroup> </ButtonGroup>
</template> </template>
</RefModal> </RefModal>
@ -81,9 +76,7 @@ provide(showModalKey, showModal);
/> />
<img <img
class="nav-logo" class="nav-logo"
srcset=" srcset="@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&png&srcset"
@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&png&srcset
"
sizes="(min-width: 120em) 56px, (min-width: 80em) 48px, (min-width: 35em) 40px, 36px" sizes="(min-width: 120em) 56px, (min-width: 80em) 48px, (min-width: 35em) 40px, 36px"
alt="Sebin Avatar" alt="Sebin Avatar"
/> />

View file

@ -13,7 +13,6 @@
width: 100%; width: 100%;
max-width: var(--attack-list-max-width); max-width: var(--attack-list-max-width);
margin: auto; margin: auto;
padding: 0 var(--container-spacing-right-safe) 0 padding: 0 var(--container-spacing-right-safe) 0 var(--container-spacing-left-safe);
var(--container-spacing-left-safe);
} }
</style> </style>

View file

@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
col?: boolean; col?: boolean
grid?: boolean; grid?: boolean
} }
defineProps<Props>(); defineProps<Props>()
</script> </script>
<template> <template>

View file

@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColorDict } from "@/interfaces"; import type { ColorDict } from '@/interfaces'
interface Props { interface Props {
colors: ColorDict[]; colors: ColorDict[]
} }
defineProps<Props>(); defineProps<Props>()
</script> </script>
<template> <template>
@ -21,10 +21,7 @@ defineProps<Props>();
<tr class="color-table__row" v-for="(color, idx) in colors" :key="idx"> <tr class="color-table__row" v-for="(color, idx) in colors" :key="idx">
<td class="color-table__cell name">{{ color.name }}</td> <td class="color-table__cell name">{{ color.name }}</td>
<td class="color-table__cell value">{{ color.value }}</td> <td class="color-table__cell value">{{ color.value }}</td>
<td <td class="color-table__cell color" :style="{ 'background-color': color.value }"></td>
class="color-table__cell color"
:style="{ 'background-color': color.value }"
></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -1,21 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
headings: string[]; headings: string[]
data: string[][]; data: string[][]
} }
defineProps<Props>(); defineProps<Props>()
</script> </script>
<template> <template>
<table class="data-table"> <table class="data-table">
<thead class="data-table__head"> <thead class="data-table__head">
<tr class="data-table__row"> <tr class="data-table__row">
<th <th class="data-table__heading" v-for="(heading, idx) in headings" :key="idx">
class="data-table__heading"
v-for="(heading, idx) in headings"
:key="idx"
>
{{ heading }} {{ heading }}
</th> </th>
</tr> </tr>

View file

@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
cols?: number; cols?: number
} }
defineProps<Props>(); defineProps<Props>()
</script> </script>
<template> <template>
@ -32,7 +32,7 @@ defineProps<Props>();
&:before { &:before {
display: block; display: block;
content: ""; content: '';
position: absolute; position: absolute;
top: 1.375rem; top: 1.375rem;
right: 1.375rem; right: 1.375rem;

View file

@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
positive?: boolean; positive?: boolean
negative?: boolean; negative?: boolean
href?: string; href?: string
download?: boolean | any; download?: boolean | any
} }
defineProps<Props>(); defineProps<Props>()
</script> </script>
<template> <template>

View file

@ -1,32 +1,26 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from "vue"; import { inject } from 'vue'
import { nsfwKey, showModalKey } from "@/keys"; import { nsfwKey, showModalKey } from '@/keys'
import RefToggle from "@/components/RefToggle.vue"; import RefToggle from '@/components/RefToggle.vue'
interface Props { interface Props {
polaroidBorder?: boolean; polaroidBorder?: boolean
dropshadow?: boolean; dropshadow?: boolean
nsfw?: boolean; nsfw?: boolean
id: string; id: string
} }
const isNsfw = inject<boolean>(nsfwKey, false); const isNsfw = inject<boolean>(nsfwKey, false)
const showModal = inject<Function>(showModalKey, Function); const showModal = inject<Function>(showModalKey, Function)
defineProps<Props>(); defineProps<Props>()
</script> </script>
<template> <template>
<figure class="figure"> <figure class="figure">
<div <div class="figure__border" :class="{ 'figure__border--polaroid': polaroidBorder }">
class="figure__border"
:class="{ 'figure__border--polaroid': polaroidBorder }"
>
<template v-if="!nsfw || isNsfw"> <template v-if="!nsfw || isNsfw">
<div <div class="figure__image" :class="{ 'figure__image--dropshadow': dropshadow }">
class="figure__image"
:class="{ 'figure__image--dropshadow': dropshadow }"
>
<slot name="img"></slot> <slot name="img"></slot>
</div> </div>
</template> </template>
@ -79,8 +73,7 @@ defineProps<Props>();
text-align: center; text-align: center;
margin: 1em auto; margin: 1em auto;
padding: 0 var(--container-spacing-right-safe) 0 padding: 0 var(--container-spacing-right-safe) 0 var(--container-spacing-left-safe);
var(--container-spacing-left-safe);
&__border { &__border {
display: flex; display: flex;
@ -101,7 +94,7 @@ defineProps<Props>();
} }
&__border--polaroid &__meta { &__border--polaroid &__meta {
font-family: "Permanent Marker", fantasy; font-family: 'Permanent Marker', fantasy;
color: var(--color-figure-polaroid-text); color: var(--color-figure-polaroid-text);
max-width: 35rem; max-width: 35rem;

View file

@ -1,78 +1,68 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from 'vue'
import { debounce } from "@/helpers"; import { debounce } from '@/helpers'
const activeImage = ref(0); const activeImage = ref(0)
const images = ref<Element[]>([]); const images = ref<Element[]>([])
const element = document.createElement("div"); const element = document.createElement('div')
const galleryViewport = ref<HTMLElement>(element); const galleryViewport = ref<HTMLElement>(element)
const galleryItemWidth = ref<number>(1); const galleryItemWidth = ref<number>(1)
const resizeObserverCallback = (entries: ResizeObserverEntry[]): void => { const resizeObserverCallback = (entries: ResizeObserverEntry[]): void => {
for (const entry of entries) { for (const entry of entries) {
galleryItemWidth.value = entry.contentRect.width; galleryItemWidth.value = entry.contentRect.width
} }
}; }
const resizeObserver = new ResizeObserver( const resizeObserver = new ResizeObserver(debounce(resizeObserverCallback, 1000))
debounce(resizeObserverCallback, 1000)
);
const setActiveImage = (index: number): void => { const setActiveImage = (index: number): void => {
activeImage.value = index; activeImage.value = index
galleryViewport.value.scrollTo({ galleryViewport.value.scrollTo({
left: galleryItemWidth.value * index, left: galleryItemWidth.value * index,
behavior: "smooth", behavior: 'smooth'
}); })
}; }
const getActiveImage = (gallery: HTMLElement, itemWidth: number): number => { const getActiveImage = (gallery: HTMLElement, itemWidth: number): number => {
return gallery.scrollLeft / itemWidth; return gallery.scrollLeft / itemWidth
}; }
const prev = (): void => { const prev = (): void => {
if (activeImage.value > 0) { if (activeImage.value > 0) {
galleryViewport.value.scrollBy({ galleryViewport.value.scrollBy({
left: galleryItemWidth.value * -1, left: galleryItemWidth.value * -1,
behavior: "smooth", behavior: 'smooth'
}); })
} }
}; }
const next = (): void => { const next = (): void => {
if (activeImage.value < images.value.length - 1) { if (activeImage.value < images.value.length - 1) {
galleryViewport.value.scrollBy({ galleryViewport.value.scrollBy({
left: galleryItemWidth.value, left: galleryItemWidth.value,
behavior: "smooth", behavior: 'smooth'
}); })
} }
}; }
const onScroll = (): void => { const onScroll = (): void => {
const newImg = Math.floor( const newImg = Math.floor(getActiveImage(galleryViewport.value, galleryItemWidth.value))
getActiveImage(galleryViewport.value, galleryItemWidth.value) setActiveImage(newImg)
); }
setActiveImage(newImg);
};
onMounted(() => { onMounted(() => {
resizeObserver.observe(galleryViewport.value); resizeObserver.observe(galleryViewport.value)
images.value = Array.from(galleryViewport.value.children); images.value = Array.from(galleryViewport.value.children)
galleryItemWidth.value = galleryItemWidth.value = galleryViewport.value.scrollWidth / images.value.length
galleryViewport.value.scrollWidth / images.value.length; galleryViewport.value.addEventListener('scroll', debounce(onScroll, 500))
galleryViewport.value.addEventListener("scroll", debounce(onScroll, 500)); })
});
</script> </script>
<template> <template>
<div class="gallery"> <div class="gallery">
<div class="gallery__images"> <div class="gallery__images">
<a <a href="#" class="gallery__prev" @click.prevent="prev()" v-show="activeImage > 0"></a>
href="#"
class="gallery__prev"
@click.prevent="prev()"
v-show="activeImage > 0"
></a>
<a <a
href="#" href="#"
class="gallery__next" class="gallery__next"

View file

@ -1,27 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from 'vue'
interface Props { interface Props {
id: string; id: string
} }
defineProps<Props>(); defineProps<Props>()
const modal = ref<HTMLDialogElement>(); const modal = ref<HTMLDialogElement>()
const showModal = () => { const showModal = () => {
modal.value?.showModal(); modal.value?.showModal()
document.body.inert = true; document.body.inert = true
document.body.classList.add("scroll-lock"); document.body.classList.add('scroll-lock')
}; }
const close = () => { const close = () => {
modal.value?.close(); modal.value?.close()
document.body.inert = false; document.body.inert = false
document.body.classList.remove("scroll-lock"); document.body.classList.remove('scroll-lock')
}; }
defineExpose({ showModal, close }); defineExpose({ showModal, close })
</script> </script>
<template> <template>

View file

@ -1,23 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "vue"; import { computed } from 'vue'
interface Props { interface Props {
modelValue: boolean; modelValue: boolean
id: string; id: string
name?: string; name?: string
} }
const props = defineProps<Props>(); const props = defineProps<Props>()
const emit = defineEmits(["update:modelValue"]); const emit = defineEmits(['update:modelValue'])
const checked = computed({ const checked = computed({
get() { get() {
return props.modelValue; return props.modelValue
}, },
set(value) { set(value) {
emit("update:modelValue", value); emit('update:modelValue', value)
}, }
}); })
</script> </script>
<template> <template>
@ -37,9 +37,8 @@ const checked = computed({
<style lang="scss"> <style lang="scss">
.toggle { .toggle {
font-family: "apple color emoji", "noto color emoji", "segoe ui emoji", font-family: 'apple color emoji', 'noto color emoji', 'segoe ui emoji', 'android emoji',
"android emoji", "emojisymbols", "emojione mozilla", "twemoji mozilla", 'emojisymbols', 'emojione mozilla', 'twemoji mozilla', 'segoe ui symbol';
"segoe ui symbol";
padding: 0.5em; padding: 0.5em;
@ -84,7 +83,7 @@ const checked = computed({
&:before, &:before,
&:after { &:after {
content: ""; content: '';
position: absolute; position: absolute;
left: 0; left: 0;
height: 1.1em; height: 1.1em;

View file

@ -1,16 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { RouterLink } from "vue-router"; import { RouterLink } from 'vue-router'
import router from "@/router"; import router from '@/router'
</script> </script>
<template> <template>
<nav class="nav"> <nav class="nav">
<ul class="nav__list"> <ul class="nav__list">
<li <li class="nav__item" v-for="(route, idx) in router.options.routes" :key="idx">
class="nav__item"
v-for="(route, idx) in router.options.routes"
:key="idx"
>
<RouterLink class="nav__link" :to="route.path"> <RouterLink class="nav__link" :to="route.path">
{{ route.name }} {{ route.name }}
</RouterLink> </RouterLink>
@ -75,7 +71,7 @@ import router from "@/router";
&:before, &:before,
&:after { &:after {
content: ""; content: '';
position: absolute; position: absolute;
bottom: 0; bottom: 0;

View file

@ -68,7 +68,7 @@
&__mainline, &__mainline,
&__subline { &__subline {
font-family: "Exo", sans-serif; font-family: 'Exo', sans-serif;
text-align: center; text-align: center;
font-style: italic; font-style: italic;
margin: 0; margin: 0;

View file

@ -1,76 +1,67 @@
const debounce = (fn: Function, delay: number = 300): any => { const debounce = (fn: Function, delay: number = 300): any => {
let timer = 0; let timer = 0
const debounced = (...args: any[]): void => { const debounced = (...args: any[]): void => {
if (!args) args = []; if (!args) args = []
clearTimeout(timer); clearTimeout(timer)
timer = setTimeout(() => { timer = setTimeout(() => {
fn.apply(fn, args); fn.apply(fn, args)
}, delay); }, delay)
}; }
return debounced; return debounced
}; }
const getClientLocale = (): string => { const getClientLocale = (): string => {
return navigator.languages.length > 0 ? navigator.languages[0] : "en-US"; return navigator.languages.length > 0 ? navigator.languages[0] : 'en-US'
}; }
const getAge = (dateOfBirth: Date): number => { const getAge = (dateOfBirth: Date): number => {
const today = new Date(); const today = new Date()
const thisYear = today.getFullYear(); const thisYear = today.getFullYear()
const thisMonth = today.getMonth(); const thisMonth = today.getMonth()
const thisDay = today.getDate(); const thisDay = today.getDate()
const dobYear = dateOfBirth.getFullYear(); const dobYear = dateOfBirth.getFullYear()
const dobMonth = dateOfBirth.getMonth(); const dobMonth = dateOfBirth.getMonth()
const dobDay = dateOfBirth.getDate(); const dobDay = dateOfBirth.getDate()
let age = thisYear - dobYear; let age = thisYear - dobYear
if (thisMonth < dobMonth) age--; if (thisMonth < dobMonth) age--
if (thisMonth === dobMonth && thisDay < dobDay) age--; if (thisMonth === dobMonth && thisDay < dobDay) age--
return age; return age
}; }
const toImperial = (cm: number): string => { const toImperial = (cm: number): string => {
const realFeet = (cm * 0.3937) / 12; const realFeet = (cm * 0.3937) / 12
const feet = Math.floor(realFeet); const feet = Math.floor(realFeet)
const inches = Math.round((realFeet - feet) * 12); const inches = Math.round((realFeet - feet) * 12)
return `${feet}'${inches}"`; return `${feet}'${inches}"`
}; }
const toInch = (cm: number): string => { const toInch = (cm: number): string => {
return `${Math.round(cm / 2.45)} in`; return `${Math.round(cm / 2.45)} in`
}; }
const toLbs = (kg: number): number => { const toLbs = (kg: number): number => {
const nearExact = kg / 0.45359237; const nearExact = kg / 0.45359237
const lbs = Math.floor(nearExact); const lbs = Math.floor(nearExact)
return lbs; return lbs
}; }
const toFahrenheit = (celsius: number): number => { const toFahrenheit = (celsius: number): number => {
return celsius * 1.8 + 32; return celsius * 1.8 + 32
}; }
const dateFormat = new Intl.DateTimeFormat(getClientLocale(), { const dateFormat = new Intl.DateTimeFormat(getClientLocale(), {
year: "numeric", year: 'numeric',
month: "long", month: 'long',
day: "2-digit", day: '2-digit'
}); })
export { export { debounce, getClientLocale, getAge, toImperial, toInch, toLbs, toFahrenheit, dateFormat }
debounce,
getClientLocale,
getAge,
toImperial,
toInch,
toLbs,
toFahrenheit,
dateFormat,
};

View file

@ -1,12 +1,12 @@
interface ColorDict { interface ColorDict {
name: string; name: string
value: string; value: string
} }
interface Kink { interface Kink {
name: string; name: string
receive: boolean; receive: boolean
give: boolean; give: boolean
} }
export type { ColorDict, Kink }; export type { ColorDict, Kink }

View file

@ -1,5 +1,5 @@
import type { InjectionKey, Ref } from "vue"; import type { InjectionKey, Ref } from 'vue'
export const nsfwKey: InjectionKey<Ref<boolean>> = Symbol("nsfwKey"); export const nsfwKey: InjectionKey<Ref<boolean>> = Symbol('nsfwKey')
export const showModalKey: InjectionKey<Function> = Symbol("showModalKey"); export const showModalKey: InjectionKey<Function> = Symbol('showModalKey')
export const modalResultKey: InjectionKey<Function> = Symbol("modalResultKey"); export const modalResultKey: InjectionKey<Function> = Symbol('modalResultKey')

View file

@ -1,12 +1,12 @@
import { createApp } from "vue"; import { createApp } from 'vue'
import App from "./App.vue"; import App from './App.vue'
import router from "./router"; import router from './router'
import "normalize.css"; import 'normalize.css'
import "@/scss/main.scss"; import '@/scss/main.scss'
const app = createApp(App); const app = createApp(App)
app.use(router); app.use(router)
app.mount("body"); app.mount('body')

View file

@ -1,43 +1,43 @@
import { createRouter, createWebHistory } from "vue-router"; import { createRouter, createWebHistory } from 'vue-router'
import HomeView from "@/views/HomeView.vue"; import HomeView from '@/views/HomeView.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
scrollBehavior() { scrollBehavior() {
return { top: 0, behavior: "smooth" }; return { top: 0, behavior: 'smooth' }
}, },
routes: [ routes: [
{ {
path: "/", path: '/',
name: "home", name: 'home',
component: HomeView, component: HomeView
}, },
{ {
path: "/general", path: '/general',
name: "general", name: 'general',
component: () => import("@/views/GeneralView.vue"), component: () => import('@/views/GeneralView.vue')
}, },
{ {
path: "/anatomy", path: '/anatomy',
name: "anatomy", name: 'anatomy',
component: () => import("@/views/AnatomyView.vue"), component: () => import('@/views/AnatomyView.vue')
}, },
{ {
path: "/clothing", path: '/clothing',
name: "clothing", name: 'clothing',
component: () => import("@/views/ClothingView.vue"), component: () => import('@/views/ClothingView.vue')
}, },
{ {
path: "/abilities", path: '/abilities',
name: "abilities", name: 'abilities',
component: () => import("@/views/AbilitiesView.vue"), component: () => import('@/views/AbilitiesView.vue')
}, },
{ {
path: "/overdrive", path: '/overdrive',
name: "overdrive", name: 'overdrive',
component: () => import("@/views/OverdriveView.vue"), component: () => import('@/views/OverdriveView.vue')
}, }
], ]
}); })
export default router; export default router

View file

@ -1,51 +1,51 @@
export const firstName = "Sebin", export const firstName = 'Sebin',
middleName = "Antario", middleName = 'Antario',
lastName = "Nyshkim", lastName = 'Nyshkim',
dateOfBirth = new Date("1993-10-17"), dateOfBirth = new Date('1993-10-17'),
gender = "male ♂️", gender = 'male ♂️',
orientation = "gay", orientation = 'gay',
position = "vers, prefers top", position = 'vers, prefers top',
height = 210, // cm height = 210, // cm
weight = 124, // kg weight = 124, // kg
tailLength = 104, // cm tailLength = 104, // cm
wingspan = 417, // cm wingspan = 417, // cm
colors = { colors = {
hairPrimary: "#4b608f", hairPrimary: '#4b608f',
hairSecondary: "#6684c0", hairSecondary: '#6684c0',
eyes: "#31c215", eyes: '#31c215',
scalesPrimary: "#c64c35", scalesPrimary: '#c64c35',
scalesSecondary: "#eda958", scalesSecondary: '#eda958',
eyebrows: "#eda958", eyebrows: '#eda958',
tailspikes: "#7f4539", tailspikes: '#7f4539',
horns: "#413a3a", horns: '#413a3a',
claws: "#413a3a", claws: '#413a3a',
nipples: "#413a3a", nipples: '#413a3a',
penis: "#413a3a", penis: '#413a3a'
}, },
hobbies = ["working out", "travels", "camping", "video games", "tech"], hobbies = ['working out', 'travels', 'camping', 'video games', 'tech'],
penis = { penis = {
shape: "humanoid", shape: 'humanoid',
type: "grower", type: 'grower',
special: "ridged, no foreskin", special: 'ridged, no foreskin',
size: 20, // cm size: 20, // cm
girth: 5, // cm girth: 5 // cm
}, },
kinks = [ kinks = [
{ name: "Oral", receive: true, give: true }, { name: 'Oral', receive: true, give: true },
{ name: "Anal", receive: true, give: true }, { name: 'Anal', receive: true, give: true },
{ name: "Facial", receive: true, give: true }, { name: 'Facial', receive: true, give: true },
{ name: "Creampie", receive: true, give: true }, { name: 'Creampie', receive: true, give: true },
{ name: "Bukkake", receive: true, give: true }, { name: 'Bukkake', receive: true, give: true },
{ name: "Biting", receive: true, give: true }, { name: 'Biting', receive: true, give: true },
{ name: "Nipple Play", receive: true, give: true }, { name: 'Nipple Play', receive: true, give: true },
{ name: "Rough", receive: true, give: true }, { name: 'Rough', receive: true, give: true },
{ name: "Toys", receive: true, give: true }, { name: 'Toys', receive: true, give: true },
{ name: "Frotting", receive: true, give: true }, { name: 'Frotting', receive: true, give: true },
{ name: "Muscle Worship", receive: true, give: true }, { name: 'Muscle Worship', receive: true, give: true },
{ name: "Filled Condoms", receive: true, give: true }, { name: 'Filled Condoms', receive: true, give: true },
{ name: "Growth/Macro", receive: true, give: true }, { name: 'Growth/Macro', receive: true, give: true },
{ name: "Size Difference", receive: true, give: true }, { name: 'Size Difference', receive: true, give: true },
{ name: "Underwear", receive: true, give: true }, { name: 'Underwear', receive: true, give: true },
{ name: "Chubbies", receive: true, give: true }, { name: 'Chubbies', receive: true, give: true }
]; ]

View file

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import AttackItem from "@/components/AttackItem.vue"; import AttackItem from '@/components/AttackItem.vue'
import AttackList from "@/components/AttackList.vue"; import AttackList from '@/components/AttackList.vue'
</script> </script>
<template> <template>
@ -8,8 +8,8 @@ import AttackList from "@/components/AttackList.vue";
<h1>{{ $route.name }}</h1> <h1>{{ $route.name }}</h1>
<p> <p>
Since Sebin is a fire dragon there's a myriad of abilities he has at his Since Sebin is a fire dragon there's a myriad of abilities he has at his disposal to defend
disposal to defend himself. himself.
</p> </p>
</section> </section>
@ -48,12 +48,10 @@ import AttackList from "@/components/AttackList.vue";
</template> </template>
<template #name>Fire Breath</template> <template #name>Fire Breath</template>
<template #desc> <template #desc>
Like most fire dragons, Sebin can breathe fire. In order to do this, he Like most fire dragons, Sebin can breathe fire. In order to do this, he takes a deep breath
takes a deep breath to enrich the oxygen in his lungs with gases, which, to enrich the oxygen in his lungs with gases, which, together with special glands in his
together with special glands in his mouth, produce a combustible mouth, produce a combustible mixture. The resulting jet of fire, reaching several hundred
mixture. The resulting jet of fire, reaching several hundred degrees degrees Celsius, spreads out on its way to its target, scorching everything in its path.
Celsius, spreads out on its way to its target, scorching everything in
its path.
</template> </template>
</AttackItem> </AttackItem>
@ -87,11 +85,10 @@ import AttackList from "@/components/AttackList.vue";
</template> </template>
<template #name>Flame Toss</template> <template #name>Flame Toss</template>
<template #desc> <template #desc>
By spitting fire into his hands, Sebin can form it into a ball and use By spitting fire into his hands, Sebin can form it into a ball and use it as a projectile.
it as a projectile. His scales are fireproof and can withstand the high His scales are fireproof and can withstand the high temperatures. Due to their high
temperatures. Due to their high concentration, the projectiles explode concentration, the projectiles explode upon impact. By combining two fireballs the explosion
upon impact. By combining two fireballs the explosion radius increases radius increases dramatically.
dramatically.
</template> </template>
</AttackItem> </AttackItem>
@ -125,10 +122,9 @@ import AttackList from "@/components/AttackList.vue";
</template> </template>
<template #name>Kindled Fist</template> <template #name>Kindled Fist</template>
<template #desc> <template #desc>
Apart from throwing projectiles, Sebin can also use the fireballs to Apart from throwing projectiles, Sebin can also use the fireballs to wrap his fists in fire.
wrap his fists in fire. This allows him to inflict severe burns on his This allows him to inflict severe burns on his opponent with each blow. In addition, he can
opponent with each blow. In addition, he can release the fire from his release the fire from his fists with aimed blows and hurl it at his opponents.
fists with aimed blows and hurl it at his opponents.
</template> </template>
</AttackItem> </AttackItem>
@ -162,9 +158,8 @@ import AttackList from "@/components/AttackList.vue";
</template> </template>
<template #name>Burning Twister</template> <template #name>Burning Twister</template>
<template #desc> <template #desc>
A technique used in aerial combat, Sebin uses his fire breath to engulf A technique used in aerial combat, Sebin uses his fire breath to engulf his body in flames
his body in flames while spinning to become a fire tornado that singes while spinning to become a fire tornado that singes opponents.
opponents.
</template> </template>
</AttackItem> </AttackItem>
</AttackList> </AttackList>

View file

@ -1,39 +1,39 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from "vue"; import { inject } from 'vue'
import { nsfwKey, showModalKey } from "@/keys"; import { nsfwKey, showModalKey } from '@/keys'
import type { ColorDict } from "@/interfaces"; import type { ColorDict } from '@/interfaces'
import { tailLength, wingspan, penis, colors } from "@/sebin"; import { tailLength, wingspan, penis, colors } from '@/sebin'
import { toImperial, toFahrenheit } from "@/helpers"; import { toImperial, toFahrenheit } from '@/helpers'
import RefToggle from "@/components/RefToggle.vue"; import RefToggle from '@/components/RefToggle.vue'
import RefGallery from "@/components/RefGallery.vue"; import RefGallery from '@/components/RefGallery.vue'
import RefFigure from "@/components/RefFigure.vue"; import RefFigure from '@/components/RefFigure.vue'
import ColorTable from "@/components/ColorTable.vue"; import ColorTable from '@/components/ColorTable.vue'
import DataTable from "@/components/DataTable.vue"; import DataTable from '@/components/DataTable.vue'
import QuickFacts from "@/components/QuickFacts.vue"; import QuickFacts from '@/components/QuickFacts.vue'
const sebinColors: ColorDict[] = [ const sebinColors: ColorDict[] = [
{ name: "Scales", value: colors.scalesPrimary }, { name: 'Scales', value: colors.scalesPrimary },
{ name: "Chest, Wings (front)", value: colors.scalesSecondary }, { name: 'Chest, Wings (front)', value: colors.scalesSecondary },
{ name: "Hair", value: colors.hairPrimary }, { name: 'Hair', value: colors.hairPrimary },
{ name: "Hair Streaks (optional)", value: colors.hairSecondary }, { name: 'Hair Streaks (optional)', value: colors.hairSecondary },
{ name: "Eyes", value: colors.eyes }, { name: 'Eyes', value: colors.eyes },
{ name: "Facial Spikes", value: colors.eyebrows }, { name: 'Facial Spikes', value: colors.eyebrows },
{ name: "Horns/Claws/Nipples", value: colors.horns }, { name: 'Horns/Claws/Nipples', value: colors.horns },
{ name: "Tail Spikes", value: colors.tailspikes }, { name: 'Tail Spikes', value: colors.tailspikes }
]; ]
const sebinPenisHeadings = ["Key", "Value"]; const sebinPenisHeadings = ['Key', 'Value']
const sebinPenisData = [ const sebinPenisData = [
["Shape", penis.shape], ['Shape', penis.shape],
["Type", penis.type], ['Type', penis.type],
["Special Traits", penis.special], ['Special Traits', penis.special],
["Color", colors.penis], ['Color', colors.penis],
["Length", `${penis.size} cm (${toImperial(penis.size)})`], ['Length', `${penis.size} cm (${toImperial(penis.size)})`],
["Girth", `${penis.girth} cm (${toImperial(penis.girth)})`], ['Girth', `${penis.girth} cm (${toImperial(penis.girth)})`]
]; ]
const isNsfw = inject<boolean>(nsfwKey, false); const isNsfw = inject<boolean>(nsfwKey, false)
const showModal = inject<Function>(showModalKey, Function); const showModal = inject<Function>(showModalKey, Function)
</script> </script>
<template> <template>
@ -137,11 +137,7 @@ const showModal = inject<Function>(showModalKey, Function);
</template> </template>
<template #caption> <template #caption>
<RefToggle <RefToggle id="sebin-fullbody-ref" v-model="isNsfw" @click.prevent="showModal()">
id="sebin-fullbody-ref"
v-model="isNsfw"
@click.prevent="showModal()"
>
<template #off>😇</template> <template #off>😇</template>
<template #on>😈</template> <template #on>😈</template>
</RefToggle> </RefToggle>
@ -165,24 +161,19 @@ const showModal = inject<Function>(showModalKey, Function);
<li>Athletic to body builder physique</li> <li>Athletic to body builder physique</li>
<li>Sharp, black claws on fingers and toes</li> <li>Sharp, black claws on fingers and toes</li>
<li>Brown blunt spikes running over back and top-side of tail</li> <li>Brown blunt spikes running over back and top-side of tail</li>
<li> <li>Tail about {{ tailLength / 100 }} meter ({{ toImperial(tailLength) }}) in length</li>
Tail about {{ tailLength / 100 }} meter ({{ toImperial(tailLength) }})
in length
</li>
</ul> </ul>
</QuickFacts> </QuickFacts>
<p> <p>
As the offspring of a union between human and dragon, Sebin is a bipedal As the offspring of a union between human and dragon, Sebin is a bipedal plantigrade. The
plantigrade. The majority of his body is covered in red scales. A strip of majority of his body is covered in red scales. A strip of yellow scales runs from the
yellow scales runs from the underside of his jaw, extending down his chest underside of his jaw, extending down his chest through his legs to the underside of the tip of
through his legs to the underside of the tip of his tail. The scales on his tail. The scales on his chest are characterized by a special toughness to better protect
his chest are characterized by a special toughness to better protect vital vital organs. He also has an athletic to muscular physique that he continuously tends to keep
organs. He also has an athletic to muscular physique that he continuously in shape. Finger and toe tips are armed with sharp, black claws, which serve him equally as
tends to keep in shape. Finger and toe tips are armed with sharp, black tools and weapons. Brown spines run from head to spine to tip of tail, though they are too
claws, which serve him equally as tools and weapons. Brown spines run from blunt to pose a risk of injury. His tail is about
head to spine to tip of tail, though they are too blunt to pose a risk of
injury. His tail is about
{{ tailLength / 100 }} meters ({{ toImperial(tailLength) }}) in length. {{ tailLength / 100 }} meters ({{ toImperial(tailLength) }}) in length.
</p> </p>
</section> </section>
@ -192,9 +183,7 @@ const showModal = inject<Function>(showModalKey, Function);
<QuickFacts :cols="2"> <QuickFacts :cols="2">
<ul> <ul>
<li> <li>Wingspan {{ wingspan / 100 }} meters ({{ toImperial(wingspan) }})</li>
Wingspan {{ wingspan / 100 }} meters ({{ toImperial(wingspan) }})
</li>
<li>Closed when on the ground</li> <li>Closed when on the ground</li>
<li>Function like a second pair of arms</li> <li>Function like a second pair of arms</li>
<li>Pointy talon on end of "hand"</li> <li>Pointy talon on end of "hand"</li>
@ -202,17 +191,14 @@ const showModal = inject<Function>(showModalKey, Function);
</QuickFacts> </QuickFacts>
<p> <p>
Sebin's wings have a span of about {{ wingspan / 100 }} meters ({{ Sebin's wings have a span of about {{ wingspan / 100 }} meters ({{ toImperial(wingspan) }}).
toImperial(wingspan) They function very much like a second pair of arms: two interconnected limbs that form a
}}). They function very much like a second pair of arms: two bendable arm, at the end of which sits a hand-like structure, adorned with a talon in exchange
interconnected limbs that form a bendable arm, at the end of which sits a for a thumb, with a sturdy membrane stretched between its long fingers. On solid ground, he
hand-like structure, adorned with a talon in exchange for a thumb, with a keeps his wings closed so as not to accidentally bump into anything with them. Sebin also pays
sturdy membrane stretched between its long fingers. On solid ground, he a lot of attention to his wings when working out, so that they can keep carrying him reliably
keeps his wings closed so as not to accidentally bump into anything with through the air. Though, he only travels short to medium distances through the air before he
them. Sebin also pays a lot of attention to his wings when working out, so has to take a rest - as long as the airspace in the area has been declared open for wing
that they can keep carrying him reliably through the air. Though, he only
travels short to medium distances through the air before he has to take a
rest - as long as the airspace in the area has been declared open for wing
bearers. bearers.
</p> </p>
</section> </section>
@ -291,20 +277,17 @@ const showModal = inject<Function>(showModalKey, Function);
</QuickFacts> </QuickFacts>
<p> <p>
Despite what one might expect with reptilians, Sebin's pupils are rounded Despite what one might expect with reptilians, Sebin's pupils are rounded instead of
instead of slit-shaped. Yellow spikes running above his eyelids serve as slit-shaped. Yellow spikes running above his eyelids serve as eyebrows. These spikes are also
eyebrows. These spikes are also found along his jaw bone. Two pointed, found along his jaw bone. Two pointed, slightly curved, black horns protrude from his head. He
slightly curved, black horns protrude from his head. He usually wears his usually wears his medium-length, blue hair loose. His long, pointed ears are very flexible,
medium-length, blue hair loose. His long, pointed ears are very flexible, allowing him to hear sounds around him without having to turn his head. They are also used for
allowing him to hear sounds around him without having to turn his head. non-verbal communication, to express emotions through body language. His hearing perceives a
They are also used for non-verbal communication, to express emotions wider range of frequencies, making it superior to that of a human. His jaws are equipped with
through body language. His hearing perceives a wider range of frequencies, razor-sharp teeth that effortlessly sink into anything he manages to bite, be it nourishment
making it superior to that of a human. His jaws are equipped with or adversaries. Embedded between them lies his tongue, which is typically pointed for
razor-sharp teeth that effortlessly sink into anything he manages to bite, reptilians. Glands in his throat produce a mixture which he uses to spit fire, which can reach
be it nourishment or adversaries. Embedded between them lies his tongue, up to around 100 °C ({{ toFahrenheit(100) }} °F).
which is typically pointed for reptilians. Glands in his throat produce a
mixture which he uses to spit fire, which can reach up to around 100 °C
({{ toFahrenheit(100) }} °F).
</p> </p>
</section> </section>
@ -318,18 +301,17 @@ const showModal = inject<Function>(showModalKey, Function);
<li>Defined abs</li> <li>Defined abs</li>
<li>Black nipples</li> <li>Black nipples</li>
<li> <li>
Any muscle mass from athletic to body builder is fine, with a Any muscle mass from athletic to body builder is fine, with a preference towards body
preference towards body builder builder
</li> </li>
</ul> </ul>
</QuickFacts> </QuickFacts>
<p> <p>
At the age of 17, Sebin gradually began to keep his body in shape on a At the age of 17, Sebin gradually began to keep his body in shape on a regular basis. His
regular basis. His favorite exercises include weightlifting (with both favorite exercises include weightlifting (with both dumbbells and barbells), lat pulldown, leg
dumbbells and barbells), lat pulldown, leg press and cable curls. He works press and cable curls. He works out three days a week with one day off between training days,
out three days a week with one day off between training days, rotating the rotating the body regions he trains each day.
body regions he trains each day.
</p> </p>
</section> </section>
@ -369,10 +351,9 @@ const showModal = inject<Function>(showModalKey, Function);
<section> <section>
<p> <p>
Sebin is very concerned with an even distribution of muscle mass, but pays Sebin is very concerned with an even distribution of muscle mass, but pays particular
particular attention to his back, chest and arms. A strong chest with attention to his back, chest and arms. A strong chest with strong arms helps to throw fire
strong arms helps to throw fire projectiles as far as possible. A strong projectiles as far as possible. A strong back guarantees a longer stay in the air.
back guarantees a longer stay in the air.
</p> </p>
</section> </section>
@ -510,9 +491,7 @@ const showModal = inject<Function>(showModalKey, Function);
</template> </template>
<template #caption>Sebin flexing</template> <template #caption>Sebin flexing</template>
<template #copyright> <template #copyright>
<a href="https://www.furaffinity.net/user/Marsel-Defender"> <a href="https://www.furaffinity.net/user/Marsel-Defender"> Marsel-Defender </a>
Marsel-Defender
</a>
</template> </template>
</RefFigure> </RefFigure>
<RefFigure id="sebin-muscle-ref4" polaroidBorder nsfw> <RefFigure id="sebin-muscle-ref4" polaroidBorder nsfw>
@ -791,16 +770,12 @@ const showModal = inject<Function>(showModalKey, Function);
<template #img> <template #img>
<picture> <picture>
<source <source
srcset=" srcset="@/assets/refs/sebin-ref-penis.png?w=480;720;0&avif&quality=75&srcset"
@/assets/refs/sebin-ref-penis.png?w=480;720;0&avif&quality=75&srcset
"
sizes="(min-width: 45em) 1155px, (min-width: 30em) 720px, 480px" sizes="(min-width: 45em) 1155px, (min-width: 30em) 720px, 480px"
type="image/avif" type="image/avif"
/> />
<source <source
srcset=" srcset="@/assets/refs/sebin-ref-penis.png?w=480;720;0&webp&quality=100&srcset"
@/assets/refs/sebin-ref-penis.png?w=480;720;0&webp&quality=100&srcset
"
sizes="(min-width: 45em) 1155px, (min-width: 30em) 720px, 480px" sizes="(min-width: 45em) 1155px, (min-width: 30em) 720px, 480px"
type="image/webp" type="image/webp"
/> />
@ -827,24 +802,20 @@ const showModal = inject<Function>(showModalKey, Function);
<ul> <ul>
<li>Human-shaped with ridges</li> <li>Human-shaped with ridges</li>
<li>Ring-like sheath surrounding shaft</li> <li>Ring-like sheath surrounding shaft</li>
<li> <li>Extends from sheath when erect, lives inside sheath when not erect</li>
Extends from sheath when erect, lives inside sheath when not erect
</li>
<li>External balls</li> <li>External balls</li>
</ul> </ul>
</QuickFacts> </QuickFacts>
<p> <p>
Despite his reptilian appearance, Sebin has nipples, a feature of the Despite his reptilian appearance, Sebin has nipples, a feature of the human side of his
human side of his family. Furthermore, his external testicles represent family. Furthermore, his external testicles represent another humanized feature. Where
another humanized feature. Where relatives of his ilk possess a slit in relatives of his ilk possess a slit in which the penis lies protectively, Sebin possesses a
which the penis lies protectively, Sebin possesses a pouch-like sheath pouch-like sheath from which the tip of the penis protrudes slightly. The shape of his shaft
from which the tip of the penis protrudes slightly. The shape of his is predominantly humanoid, but it is surrounded by ridges and has no equivalent of a
shaft is predominantly humanoid, but it is surrounded by ridges and has foreskin. When aroused, the coal-black shaft swells and pushes out of the sheath until fully
no equivalent of a foreskin. When aroused, the coal-black shaft swells erect, the sheath wrapping around the root of the shaft like a ring. However, he can also
and pushes out of the sheath until fully erect, the sheath wrapping push it out in a flaccid state, e.g. when needing to pass water.
around the root of the shaft like a ring. However, he can also push it
out in a flaccid state, e.g. when needing to pass water.
</p> </p>
</section> </section>
</template> </template>
@ -854,16 +825,12 @@ const showModal = inject<Function>(showModalKey, Function);
<h2>But, wait! There's more...</h2> <h2>But, wait! There's more...</h2>
<p> <p>
Sebin like you haven't seen him yet! Flip the switch to reveal his Sebin like you haven't seen him yet! Flip the switch to reveal his naughty secrets. If you
naughty secrets. If you dare...! Don't say I didn't warn you!! dare...! Don't say I didn't warn you!!
</p> </p>
<p> <p>
<RefToggle <RefToggle id="sebin-manly-bits" v-model="isNsfw" @click.prevent="showModal()">
id="sebin-manly-bits"
v-model="isNsfw"
@click.prevent="showModal()"
>
<template #off>😇</template> <template #off>😇</template>
<template #on>😈</template> <template #on>😈</template>
</RefToggle> </RefToggle>

View file

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import RefFigure from "@/components/RefFigure.vue"; import RefFigure from '@/components/RefFigure.vue'
import RefGallery from "@/components/RefGallery.vue"; import RefGallery from '@/components/RefGallery.vue'
</script> </script>
<template> <template>
@ -40,9 +40,7 @@ import RefGallery from "@/components/RefGallery.vue";
<template #caption> <template #caption>
<p>Sebin in his casual outfit</p> <p>Sebin in his casual outfit</p>
<p> <p>Black tank top, flannell shirt, shorts (w/ dangling bands), sneakers</p>
Black tank top, flannell shirt, shorts (w/ dangling bands), sneakers
</p>
</template> </template>
<template #copyright> <template #copyright>
@ -117,8 +115,7 @@ import RefGallery from "@/components/RefGallery.vue";
<template #caption> <template #caption>
<p>Sebin in his workout outfit</p> <p>Sebin in his workout outfit</p>
<p> <p>
<strong>Full body:</strong> Snapback hat, tank top, fingerless gloves, <strong>Full body:</strong> Snapback hat, tank top, fingerless gloves, shorts, sneakers
shorts, sneakers
</p> </p>
</template> </template>
@ -157,8 +154,8 @@ import RefGallery from "@/components/RefGallery.vue";
<template #caption> <template #caption>
<p>Sebin in his workout outfit</p> <p>Sebin in his workout outfit</p>
<p> <p>
<strong>Close-up:</strong> Snapback hat, headphones, tank top, <strong>Close-up:</strong> Snapback hat, headphones, tank top, fingerless gloves, shorts,
fingerless gloves, shorts, smartwatch smartwatch
</p> </p>
</template> </template>
@ -199,9 +196,8 @@ import RefGallery from "@/components/RefGallery.vue";
<template #caption> <template #caption>
<p>Tracksuit pants</p> <p>Tracksuit pants</p>
<p> <p>
Sebin likes to wear comfortable clothes at home when he doesn't need Sebin likes to wear comfortable clothes at home when he doesn't need to leave the house or
to leave the house or is enjoying some leisure time after work or on is enjoying some leisure time after work or on weekends.
weekends.
</p> </p>
</template> </template>
@ -213,23 +209,17 @@ import RefGallery from "@/components/RefGallery.vue";
<template #img> <template #img>
<picture> <picture>
<source <source
srcset=" srcset="@/assets/refs/clothes/lazy/undies.jpg?w=0&avif&withoutEnlargement&srcset"
@/assets/refs/clothes/lazy/undies.jpg?w=0&avif&withoutEnlargement&srcset
"
sizes="333px" sizes="333px"
type="image/avif" type="image/avif"
/> />
<source <source
srcset=" srcset="@/assets/refs/clothes/lazy/undies.jpg?w=0&webp&withoutEnlargement&srcset"
@/assets/refs/clothes/lazy/undies.jpg?w=0&webp&withoutEnlargement&srcset
"
sizes="333px" sizes="333px"
type="image/webp" type="image/webp"
/> />
<img <img
srcset=" srcset="@/assets/refs/clothes/lazy/undies.jpg?w=0&withoutEnlargement&srcset"
@/assets/refs/clothes/lazy/undies.jpg?w=0&withoutEnlargement&srcset
"
sizes="333px" sizes="333px"
alt="Shorts" alt="Shorts"
loading="lazy" loading="lazy"
@ -240,8 +230,8 @@ import RefGallery from "@/components/RefGallery.vue";
<template #caption> <template #caption>
<p>Topless w/ jockstrap/boxer briefs</p> <p>Topless w/ jockstrap/boxer briefs</p>
<p> <p>
For even more comfort, Sebin tends to forgoe pants completely and save For even more comfort, Sebin tends to forgoe pants completely and save on laundry by only
on laundry by only wearing the absolute necessary. wearing the absolute necessary.
</p> </p>
</template> </template>

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from "vue"; import { inject } from 'vue'
import { import {
firstName, firstName,
middleName, middleName,
@ -13,45 +13,42 @@ import {
tailLength, tailLength,
wingspan, wingspan,
hobbies, hobbies,
kinks, kinks
} from "@/sebin"; } from '@/sebin'
import { dateFormat, getAge, toImperial, toLbs } from "@/helpers"; import { dateFormat, getAge, toImperial, toLbs } from '@/helpers'
import { nsfwKey, showModalKey } from "@/keys"; import { nsfwKey, showModalKey } from '@/keys'
import type { Kink } from "@/interfaces"; import type { Kink } from '@/interfaces'
import DataTable from "@/components/DataTable.vue"; import DataTable from '@/components/DataTable.vue'
import QuickFacts from "@/components/QuickFacts.vue"; import QuickFacts from '@/components/QuickFacts.vue'
import RefToggle from "@/components/RefToggle.vue"; import RefToggle from '@/components/RefToggle.vue'
const generalHeadings = ["Key", "Value"]; const generalHeadings = ['Key', 'Value']
const generalData = [ const generalData = [
["Full Name", `${firstName} ${middleName} ${lastName} `], ['Full Name', `${firstName} ${middleName} ${lastName} `],
[ ['Date of Birth', `${dateFormat.format(dateOfBirth)} (${getAge(dateOfBirth)})`],
"Date of Birth", ['Sex/Gender', gender],
`${dateFormat.format(dateOfBirth)} (${getAge(dateOfBirth)})`, ['Height', `${height} cm (${toImperial(height)})`],
], ['Weight', `${weight} kg (${toLbs(weight)} lbs)`],
["Sex/Gender", gender], ['Tail Length', `${tailLength / 100} m (${toImperial(tailLength)})`],
["Height", `${height} cm (${toImperial(height)})`], ['Wingspan', `${wingspan / 100} m (${toImperial(wingspan)})`]
["Weight", `${weight} kg (${toLbs(weight)} lbs)`], ]
["Tail Length", `${tailLength / 100} m (${toImperial(tailLength)})`],
["Wingspan", `${wingspan / 100} m (${toImperial(wingspan)})`],
];
const nsfwHeadings = ["Key", "Value"]; const nsfwHeadings = ['Key', 'Value']
const nsfwData = [ const nsfwData = [
["Orientation", orientation], ['Orientation', orientation],
["Position", position], ['Position', position]
]; ]
const kinksHeadings = ["Kink", "Receive", "Give"]; const kinksHeadings = ['Kink', 'Receive', 'Give']
const kinksData = kinks.map((kink: Kink): string[] => { const kinksData = kinks.map((kink: Kink): string[] => {
const receive = kink.receive ? "✅" : "🚫"; const receive = kink.receive ? '✅' : '🚫'
const give = kink.give ? "✅" : "🚫"; const give = kink.give ? '✅' : '🚫'
return [kink.name, receive, give]; return [kink.name, receive, give]
}); })
const isNsfw = inject<boolean>(nsfwKey, false); const isNsfw = inject<boolean>(nsfwKey, false)
const showModal = inject<Function>(showModalKey, Function); const showModal = inject<Function>(showModalKey, Function)
</script> </script>
<template> <template>
@ -78,38 +75,34 @@ const showModal = inject<Function>(showModalKey, Function);
</QuickFacts> </QuickFacts>
<p> <p>
Sebin is a warm-hearted guy who cares a lot about the well-being of his Sebin is a warm-hearted guy who cares a lot about the well-being of his loved ones. Bad vibes
loved ones. Bad vibes rarely escape him and he offers his help without rarely escape him and he offers his help without hesitation. He also won't avoid difficult
hesitation. He also won't avoid difficult conversations in the process. conversations in the process. Not being able to help a friend in need is synonymous with
Not being able to help a friend in need is synonymous with failing them, a failing them, a realization that can leave him feeling uneasy long after the fact.
realization that can leave him feeling uneasy long after the fact.
</p> </p>
<p> <p>
At the same time, he is very open and honest about his feelings. He does At the same time, he is very open and honest about his feelings. He does not mince words and
not mince words and finds clear words when speaking his mind. finds clear words when speaking his mind. Unfortunately, Sebin sometimes forgets his good
Unfortunately, Sebin sometimes forgets his good manners in the heat of the manners in the heat of the moment, once he gets invested into a quarrel especially when it
moment, once he gets invested into a quarrel especially when it comes to comes to topics that are near and dear to his heart. Anyone looking to have a bad time can try
topics that are near and dear to his heart. Anyone looking to have a bad their luck at pissing him off even once. This includes an equally vulgar vocabulary. It is not
time can try their luck at pissing him off even once. This includes an uncommon to hear him swear.
equally vulgar vocabulary. It is not uncommon to hear him swear.
</p> </p>
<p> <p>
Nevertheless, Sebin strives to put his best foot forward at all times. He Nevertheless, Sebin strives to put his best foot forward at all times. He is of the sociable
is of the sociable type and likes to laugh a lot, as he is easily amused. type and likes to laugh a lot, as he is easily amused. Sometimes to a degree where it can
Sometimes to a degree where it can become very childish and immature very become very childish and immature very quickly.
quickly.
</p> </p>
<p> <p>
Physical strength is not the only thing that plays a big role for Sebin. Physical strength is not the only thing that plays a big role for Sebin. He is of a firm
He is of a firm believe that a healthy body must also have a healthy mind believe that a healthy body must also have a healthy mind in order to find a balance. But he
in order to find a balance. But he only came to this realization at the only came to this realization at the end of a rocky road. While a setback in the past could
end of a rocky road. While a setback in the past could easily throw him easily throw him off track, today he stands much more firmly in life. Not only for his own
off track, today he stands much more firmly in life. Not only for his own sake, but also to be a kind of anchor for others. He always keeps his cool, so he can be a
sake, but also to be a kind of anchor for others. He always keeps his tower of strenth for others.
cool, so he can be a tower of strenth for others.
</p> </p>
</section> </section>
@ -125,18 +118,16 @@ const showModal = inject<Function>(showModalKey, Function);
</QuickFacts> </QuickFacts>
<p> <p>
Sebin is passionate about his hobbies. If he notices even the smallest Sebin is passionate about his hobbies. If he notices even the smallest spark of interest in
spark of interest in his hobbies you should bring a lot of time, as he his hobbies you should bring a lot of time, as he will chew your ear off first. Patience is
will chew your ear off first. Patience is known to be a virtue one known to be a virtue one unknown to this dragon.
unknown to this dragon.
</p> </p>
<p> <p>
When he indulges in his hobbies, he does so with devotion. Every move has When he indulges in his hobbies, he does so with devotion. Every move has to be right and
to be right and everything has to be in perfect harmony. Once he is in his everything has to be in perfect harmony. Once he is in his flow, he must not be disturbed,
flow, he must not be disturbed, otherwise he can sometimes become quite otherwise he can sometimes become quite eccentric in expressing his dismay of being disrupted,
eccentric in expressing his dismay of being disrupted, possibly losing a possibly losing a very important train of thought.
very important train of thought.
</p> </p>
</section> </section>
@ -161,21 +152,19 @@ const showModal = inject<Function>(showModalKey, Function);
</QuickFacts> </QuickFacts>
<p> <p>
Sebin's day starts with a strong cup of black coffee and sandwiches. He's Sebin's day starts with a strong cup of black coffee and sandwiches. He's also a massive sweet
also a massive sweet tooth which sounds like a big detriment to his tooth which sounds like a big detriment to his fitness routine. That's because it is and it's
fitness routine. That's because it is and it's often very hard for him to often very hard for him to resist.
resist.
</p> </p>
<p> <p>
Besides snacking, Sebin also likes to eat hearty and savory things. He Besides snacking, Sebin also likes to eat hearty and savory things. He doesn't disdain a
doesn't disdain a cheese platter with a wide selection, nor a medium-rare cheese platter with a wide selection, nor a medium-rare steak.
steak.
</p> </p>
<p> <p>
Sebin rarely says no to a good beer with friends, just as he rarely says Sebin rarely says no to a good beer with friends, just as he rarely says no to a bar tour to
no to a bar tour to try new and interesting cocktails. try new and interesting cocktails.
</p> </p>
</section> </section>
@ -198,17 +187,14 @@ const showModal = inject<Function>(showModalKey, Function);
</QuickFacts> </QuickFacts>
<p> <p>
But above all Sebin is a very naughty hornball. He knows what he's got But above all Sebin is a very naughty hornball. He knows what he's got and he's not afraid
and he's not afraid to flaunt it. He is a generally dominant lover who to flaunt it. He is a generally dominant lover who likes to have it rough. But he is not
likes to have it rough. But he is not lacking in tenderness. He lacking in tenderness. He considers himself somewhat of a "service top", who doesn't only
considers himself somewhat of a "service top", who doesn't only have his have his own fun in mind. His job is only done if he's benn able to satisfy. However, that
own fun in mind. His job is only done if he's benn able to satisfy. doesn't mean that he doesn't let others have their fun with him too from time to time. It
However, that doesn't mean that he doesn't let others have their fun always depends on his playmates, which makes him effectively a switch. He loves to wear
with him too from time to time. It always depends on his playmates, bottomless jockstraps and boxer briefs to direct the attention of onlookers exactly where he
which makes him effectively a switch. He loves to wear bottomless wants it. After all he is well endowed enough to peddle it around.
jockstraps and boxer briefs to direct the attention of onlookers exactly
where he wants it. After all he is well endowed enough to peddle it
around.
</p> </p>
<DataTable class="kinks" :headings="kinksHeadings" :data="kinksData" /> <DataTable class="kinks" :headings="kinksHeadings" :data="kinksData" />

View file

@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from "vue"; import { inject } from 'vue'
import { nsfwKey, showModalKey } from "@/keys"; import { nsfwKey, showModalKey } from '@/keys'
import RefToggle from "@/components/RefToggle.vue"; import RefToggle from '@/components/RefToggle.vue'
import WelcomeHeader from "@/components/WelcomeHeader.vue"; import WelcomeHeader from '@/components/WelcomeHeader.vue'
import ButtonGroup from "@/components/ButtonGroup.vue"; import ButtonGroup from '@/components/ButtonGroup.vue'
import Button from "@/components/RefButton.vue"; import Button from '@/components/RefButton.vue'
const isNsfw = inject<boolean>(nsfwKey, false); const isNsfw = inject<boolean>(nsfwKey, false)
const showModal = inject<Function>(showModalKey, Function); const showModal = inject<Function>(showModalKey, Function)
</script> </script>
<template> <template>
@ -18,44 +18,33 @@ const showModal = inject<Function>(showModalKey, Function);
<section> <section>
<h3>Welcome to Sebin's Ref Page</h3> <h3>Welcome to Sebin's Ref Page</h3>
<p> <p>On this page your can learn all about Sebin, your friendly neighborhood dragon!</p>
On this page your can learn all about Sebin, your friendly neighborhood
dragon!
</p>
<p> <p>
I started this page to have a single point of truth with all the info I started this page to have a single point of truth with all the info artists I commission can
artists I commission can possibly need. If you're missing some crucial possibly need. If you're missing some crucial info, or you would like to give me some general
info, or you would like to give me some general feedback about this page, feedback about this page, feel free to reach out!
feel free to reach out!
</p> </p>
<ButtonGroup grid class="social"> <ButtonGroup grid class="social">
<Button href="https://twitter.com/SebinNyshkim" class="twitter"> <Button href="https://twitter.com/SebinNyshkim" class="twitter"> Twitter </Button>
Twitter <Button href="https://meow.social/@SebinNyshkim" class="mastodon" rel="me"> Mastodon </Button>
</Button> <Button href="https://t.me/SebinNyshkim" class="telegram"> Telegram </Button>
<Button href="https://meow.social/@SebinNyshkim" class="mastodon" rel="me" >
Mastodon
</Button>
<Button href="https://t.me/SebinNyshkim" class="telegram">
Telegram
</Button>
<Button href="https://www.furaffinity.net/user/sonofdragons" class="furaffinity"> <Button href="https://www.furaffinity.net/user/sonofdragons" class="furaffinity">
Fur Affinity Fur Affinity
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<blockquote> <blockquote>
<strong>Note:</strong> This page is still under heavy construction, as <strong>Note:</strong> This page is still under heavy construction, as indicated by the 0.x.x
indicated by the 0.x.x version number in the footer. I'm continously version number in the footer. I'm continously updating the site and adding details and
updating the site and adding details and information to it. In case you information to it. In case you run into something weird, definitely let me know!
run into something weird, definitely let me know!
</blockquote> </blockquote>
<h3>Complete Ref Sheet</h3> <h3>Complete Ref Sheet</h3>
<p> <p>
Just here to fetch the ref sheet? Click the button with the ref sheet you Just here to fetch the ref sheet? Click the button with the ref sheet you need and get
need and get started! started!
</p> </p>
<ButtonGroup> <ButtonGroup>

View file

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
// import AttackItem from "@/components/AttackItem.vue"; // import AttackItem from "@/components/AttackItem.vue";
// import AttackList from "@/components/AttackList.vue"; // import AttackList from "@/components/AttackList.vue";
import RefFigure from "@/components/RefFigure.vue"; import RefFigure from '@/components/RefFigure.vue'
</script> </script>
<template> <template>
@ -45,34 +45,30 @@ import RefFigure from "@/components/RefFigure.vue";
<section> <section>
<p> <p>
Sebin can enter an Overdrive Form which greatly increases his strength and Sebin can enter an Overdrive Form which greatly increases his strength and abilities but it
abilities but it comes at a cost. comes at a cost.
</p> </p>
<p> <p>
He enters Overdrive by engulfing himself in a pillar of flames which He enters Overdrive by engulfing himself in a pillar of flames which transforms his exterior
transforms his exterior physique. During Overdrive transformation his hair physique. During Overdrive transformation his hair ignites and flickers with blue flames. The
ignites and flickers with blue flames. The corners of his eyes flicker corners of his eyes flicker with long green flames. His arms and legs become part carbon black
with long green flames. His arms and legs become part carbon black and are and are crossed by several glowing veins which pulsate like flowing lava. Fire in this form
crossed by several glowing veins which pulsate like flowing lava. Fire in burns several degrees hotter than usual because his body becomes a living blast furnace, which
this form burns several degrees hotter than usual because his body becomes is why his limbs have to be of more fire-proof material to withstand the increased heat.
a living blast furnace, which is why his limbs have to be of more
fire-proof material to withstand the increased heat.
</p> </p>
<p> <p>
To complete the transformation he inhales the flames from the pillar To complete the transformation he inhales the flames from the pillar surrounding him which
surrounding him which heats up his body from within. Even if Sebin is a heats up his body from within. Even if Sebin is a fire dragon who can sustain high degrees of
fire dragon who can sustain high degrees of heat he is essentially heat he is essentially overheating himself from the inside. Because of this he can't maintain
overheating himself from the inside. Because of this he can't maintain this form for more than a few hours before he does permanent damage to his own body.
this form for more than a few hours before he does permanent damage to his
own body.
</p> </p>
<p> <p>
Direct body contact with Sebin during overdrive causes 3rd degree burns as Direct body contact with Sebin during overdrive causes 3rd degree burns as he emits an extreme
he emits an extreme temperature, although less than he keeps inside his temperature, although less than he keeps inside his body. His immediate surroundings are
body. His immediate surroundings are likely to burn or melt. likely to burn or melt.
</p> </p>
<h2>Attacks</h2> <h2>Attacks</h2>
@ -88,75 +84,69 @@ import RefFigure from "@/components/RefFigure.vue";
<tr> <tr>
<td>Fire Breath (improved)</td> <td>Fire Breath (improved)</td>
<td> <td>
The reach of Sebin's Fire Breath increases as well as the frequency The reach of Sebin's Fire Breath increases as well as the frequency at which he can fire
at which he can fire shots from his mouth. shots from his mouth.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Flame Toss (improved)</td> <td>Flame Toss (improved)</td>
<td> <td>
Overdrive Form eliminates the need for Sebin to spit fire into his Overdrive Form eliminates the need for Sebin to spit fire into his palms. It instead
palms. It instead enables him to fire the shots directly from the enables him to fire the shots directly from the palm palm of his hands, as the firey
palm palm of his hands, as the firey veins crossing his arms act as veins crossing his arms act as an orifice to do so. The explosion radius of the burning
an orifice to do so. The explosion radius of the burning projectiles projectiles that explode on impact is greatly increased.
that explode on impact is greatly increased.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Kindled Fist (improved)</td> <td>Kindled Fist (improved)</td>
<td> <td>
As his arms and legs are infused with fire his punches and kicks As his arms and legs are infused with fire his punches and kicks exert trails of flames
exert trails of flames while doing so. Landing a punch or kick sears while doing so. Landing a punch or kick sears enemies.
enemies.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Searing Discus</td> <td>Searing Discus</td>
<td> <td>
Overdrive allows Sebin to form rings of fire by igniting flames from Overdrive allows Sebin to form rings of fire by igniting flames from his fingertips and
his fingertips and swirling them in a circle motion. He can use them swirling them in a circle motion. He can use them for both close quarters or ranged
for both close quarters or ranged combat. combat.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Combustion Flare</td> <td>Combustion Flare</td>
<td> <td>
Clinking both of his wrists against each other like flints unleashes Clinking both of his wrists against each other like flints unleashes a devestating fire
a devestating fire blast from both of his fire-infused hands. A blast from both of his fire-infused hands. A secure foothold is needed to prevent Sebin
secure foothold is needed to prevent Sebin from being thrown back by from being thrown back by the recoil of the attack. Using this technique in the air is
the recoil of the attack. Using this technique in the air is
therefore highly risky. therefore highly risky.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Blazing Pandemonium</td> <td>Blazing Pandemonium</td>
<td> <td>
A heavy impact into the ground from a great height with both fists, A heavy impact into the ground from a great height with both fists, tearing deep cracks
tearing deep cracks in the ground around the impact crater. Combined in the ground around the impact crater. Combined with
with
<strong><em>Kindled Fist</em></strong> <strong><em>Kindled Fist</em></strong>
the heat in Sebin's arms are forced through the newly created the heat in Sebin's arms are forced through the newly created furrows, transforming the
furrows, transforming the scene into an inferno. scene into an inferno.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Scorching Edge</td> <td>Scorching Edge</td>
<td> <td>
A fiery blade towering several meters into the air that Sebin sends A fiery blade towering several meters into the air that Sebin sends careening towards
careening towards his enemies from his fire-infused legs with a his enemies from his fire-infused legs with a backflip kick, leaving a swath of
backflip kick, leaving a swath of destruction in its wake. Upon destruction in its wake. Upon impact the force of the attack is distributed sideways.
impact the force of the attack is distributed sideways.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Circling Fire Shield</td> <td>Circling Fire Shield</td>
<td> <td>
A rather defensive technique. By spinning around with stretched out A rather defensive technique. By spinning around with stretched out arms Sebin creates
arms Sebin creates fire balls, which he usually hurls towards fire balls, which he usually hurls towards enemies, that circle around his body
enemies, that circle around his body diagonally. They act as a diagonally. They act as a shield while he can still move his arms relatively freely.
shield while he can still move his arms relatively freely. Enemies Enemies would be well advised to keep their distance to this spinning shield, as the
would be well advised to keep their distance to this spinning fire balls will still explode on contact.
shield, as the fire balls will still explode on contact.
</td> </td>
</tr> </tr>
</tbody> </tbody>