first commit

main
Gabe Farrell 1 year ago
commit df94d26ad6

@ -0,0 +1,6 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

30
.gitignore vendored

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo

@ -0,0 +1,7 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}

@ -0,0 +1,9 @@
{
"recommendations": [
"Vue.volar",
"vitest.explorer",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode"
]
}

@ -0,0 +1,45 @@
# featherfin
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
bun install
```
### Compile and Hot-Reload for Development
```sh
bun dev
```
### Type-Check, Compile and Minify for Production
```sh
bun run build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
bun test:unit
```
### Lint with [ESLint](https://eslint.org/)
```sh
bun lint
```

Binary file not shown.

1
env.d.ts vendored

@ -0,0 +1 @@
/// <reference types="vite/client" />

@ -0,0 +1,25 @@
import pluginVue from 'eslint-plugin-vue'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
import pluginVitest from '@vitest/eslint-plugin'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
export default [
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},
...pluginVue.configs['flat/essential'],
...vueTsEslintConfig(),
{
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*'],
},
skipFormatting,
]

@ -0,0 +1,19 @@
<!doctype html>
<html lang="">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&display=swap"
rel="stylesheet"
/>
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

@ -0,0 +1,48 @@
{
"name": "featherfin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint": "eslint . --fix",
"format": "prettier --write src/"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.7.1",
"@fortawesome/fontawesome-svg-core": "^6.7.1",
"@fortawesome/free-brands-svg-icons": "^6.7.1",
"@fortawesome/free-regular-svg-icons": "^6.7.1",
"@fortawesome/free-solid-svg-icons": "^6.7.1",
"@fortawesome/vue-fontawesome": "^3.0.8",
"pinia": "^2.2.6",
"vue": "^3.5.13",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.9.3",
"@vitejs/plugin-vue": "^5.2.1",
"@vitest/eslint-plugin": "1.1.10",
"@vue/eslint-config-prettier": "^10.1.0",
"@vue/eslint-config-typescript": "^14.1.3",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.14.0",
"eslint-plugin-vue": "^9.30.0",
"jsdom": "^25.0.1",
"npm-run-all2": "^7.0.1",
"prettier": "^3.3.3",
"typescript": "~5.6.3",
"vite": "^6.0.1",
"vite-plugin-vue-devtools": "^7.6.5",
"vitest": "^2.1.5",
"vue-tsc": "^2.1.10"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,16 @@
<script setup lang="ts">
import { RouterView } from 'vue-router'
</script>
<template>
<div class="wrapper">
<RouterView />
</div>
</template>
<style scoped>
.wrapper {
max-width: 90%;
margin-left: auto;
}
</style>

@ -0,0 +1,81 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: #03161b;
--color-background-darker: #070601;
--color-background-darkest: #000;
--color-background-grey: #131313;
/* --color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute); */
--color-accent-primary: #00e5ff;
--color-accent-yellow: #ffbb00;
/* --color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1); */
--color-heading: var(--color-text);
--color-text: #f1f1f1;
--color-text-faded: #a3a3a3;
--color-text-dark: #121212;
--section-gap: 160px;
}
/* @media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
} */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: 400;
}
body {
min-height: 100vh;
color: var(--color-text);
background: linear-gradient(var(--color-background), var(--color-background-darker) 50%);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.5;
font-family: 'Quicksand', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans',
'Droid Sans', 'Helvetica Neue', sans-serif;
font-size: 16px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

@ -0,0 +1,99 @@
@import './base.css';
#app {
/* max-width: 1280px; */
/* margin: 0 auto; */
/* padding: 2rem; */
font-weight: normal;
}
@media (min-width: 1024px) {
/* body {
display: flex;
place-items: center;
} */
/* #app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
} */
}
button.primary-button {
padding: 0.6em 1.5em;
background-color: var(--color-text);
outline: none;
border: none;
border-radius: 15px;
color: var(--color-text-dark);
font-family: inherit;
font-weight: 500;
font-size: 16px;
user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
}
button.primary-button:hover {
background-color: var(--color-text-faded);
cursor: pointer;
}
button.primary-button svg {
padding-right: 0.5em;
}
span.age-rating {
font-size: 14px;
padding: 0.1em 0.4em;
border: 1px solid white;
}
span.age-rating.sm {
font-size: 10px;
padding: 0.1em 0.4em;
border: 1px solid white;
}
svg.star-rating {
color: var(--color-accent-yellow);
}
svg.clickable:hover {
cursor: pointer;
color: var(--color-text-faded);
}
svg.active.clickable:hover {
cursor: pointer;
}
.clamp-2 {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2; /* number of lines to show */
line-clamp: 2;
-webkit-box-orient: vertical;
}
.clamp {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1; /* number of lines to show */
line-clamp: 1;
-webkit-box-orient: vertical;
}
.clamp-6 {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 6; /* number of lines to show */
line-clamp: 6;
-webkit-box-orient: vertical;
}
.no-select {
user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
}

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80L0 432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/><filter id="myblurfilter" width="110%" height="100%">
<feGaussianBlur stdDeviation="2" result="blur" />
</filter></svg>

After

Width:  |  Height:  |  Size: 497 B

@ -0,0 +1,41 @@
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>

@ -0,0 +1,65 @@
<script setup lang="ts">
import ScrollerItemPortrait from './ScrollerItemPortrait.vue'
import ScrollerItemLandscape from './ScrollerItemLandscape.vue'
const props = defineProps({
title: {
type: String,
required: true,
},
landscape: Boolean,
items: {
type: [Object],
required: true,
},
})
</script>
<template>
<div class="wrapper">
<div class="scroller-title">{{ props.title }}</div>
<div class="scroller-content" :style="{ height: `${props.landscape ? 210 : 240}px` }">
<div class="scroller-wrapper" v-if="landscape">
<ScrollerItemLandscape
v-for="item in props.items"
:title="item.title"
:subtext="item.subtext"
:image="item.image"
:info-subtext="item.infoSubtext"
:date="item.date"
:runtime="item.runtime"
:summary="item.summary"
:-m-p-a-a="item.MPAA"
:imdb-rating="item.imdbRating"
:key="item.title"
/>
</div>
<div class="scroller-wrapper" v-else>
<ScrollerItemPortrait
v-for="item in props.items"
:title="item.title"
:subtext="item.subtext"
:image="item.image"
:info-subtext="item.infoSubtext"
:date="item.date"
:runtime="item.runtime"
:summary="item.summary"
:-m-p-a-a="item.MPAA"
:imdb-rating="item.imdbRating"
:key="item.title"
/>
</div>
</div>
</div>
</template>
<style scoped>
.scroller-title {
margin-bottom: 1em;
}
.scroller-wrapper {
display: flex;
gap: 1em;
font-size: 12px;
}
</style>

@ -0,0 +1,120 @@
<script setup lang="ts"></script>
<template>
<div class="media-showcase-hero">
<div class="spacer"></div>
<div class="media-showcase-info">
<div class="media-showcase-logo">
<img src="@/assets/images/vvitch_logo.png" alt="" class="no-select" />
</div>
<div class="media-showcase-info-misc">
<div class="media-showcase-misc-info-year">2019</div>
<div class="media-showcase-misc-info-runtime">2h 12m</div>
<div class="media-showcase-misc-info imdb-rating">
<font-awesome-icon icon="fa-solid fa-star" size="xs" class="star-rating" /> 7.6
</div>
<div class="media-showcase-misc-info-age-rating"><span class="age-rating">R</span></div>
</div>
<div class="media-showcase-summary">
In 1630, a farmer relocates his family to a remote plot of land on the edge of a forest
where strange, unsettling things happen. With suspicion and paranoia mounting, each family
member's faith, loyalty and love are tested in shocking ways.
</div>
<div class="media-showcase-detail-buttons">
<div class="media-showcase-play-button">
<button class="primary-button">
<font-awesome-icon icon="fa-solid fa-play" size="md" />Play Now
</button>
<div class="media-showcase-end-time">Ends at 6:23pm</div>
</div>
<div class="media-showcase-details">
<div class="media-showcase-watched">
<font-awesome-icon icon="fa-solid fa-check" size="xl" class="clickable" />
</div>
<div class="media-showcase-favorited">
<font-awesome-icon icon="fa-solid fa-heart" size="xl" class="clickable" />
</div>
</div>
</div>
<div class="media-showcase-carousel">
<font-awesome-icon icon="fa-solid fa-circle" size="xs" class="active" />
<font-awesome-icon icon="fa-solid fa-circle" size="xs" />
<font-awesome-icon icon="fa-solid fa-circle" size="xs" />
<font-awesome-icon icon="fa-solid fa-circle" size="xs" />
<font-awesome-icon icon="fa-solid fa-circle" size="xs" />
<font-awesome-icon icon="fa-solid fa-circle" size="xs" />
</div>
</div>
<div class="media-showcase-image">
<img src="@/assets/images/vvitch_bg.jpg" alt="" class="no-select" />
</div>
</div>
</template>
<style scoped>
.media-showcase-hero {
display: flex;
justify-content: space-between;
align-items: center;
}
.media-showcase-logo img {
max-height: 90px;
}
.media-showcase-image img {
max-height: 475px;
-webkit-mask-image: -webkit-linear-gradient(right, rgba(0, 0, 0, 1) 50%, rgba(0, 0, 0, 0));
mask-image: linear-gradient(to left, rgba(0, 0, 0, 1) 50%, rgba(0, 0, 0, 0));
}
.media-showcase-info {
text-align: center;
padding: 3em 5em;
padding-right: 13em;
display: flex;
flex-direction: column;
align-items: center;
gap: 1.3em;
max-height: 475px;
max-width: 854px;
}
.media-showcase-info-misc {
display: flex;
justify-content: space-around;
min-width: 20em;
}
.media-showcase-summary {
text-align: left;
}
.media-showcase-detail-buttons {
padding-top: 1.5em;
display: flex;
min-width: 100%;
justify-content: space-around;
}
.media-showcase-play-button {
display: flex;
align-items: center;
justify-content: space-around;
width: 50%;
}
.media-showcase-details {
display: flex;
align-items: center;
justify-content: space-around;
width: 25%;
}
.media-showcase-carousel {
display: flex;
gap: 1.25em;
padding-top: 1em;
}
.media-showcase-carousel svg {
opacity: 25%;
height: 8px;
}
.media-showcase-carousel svg:hover {
cursor: pointer;
}
.media-showcase-carousel svg.active {
opacity: 100%;
}
</style>

@ -0,0 +1,201 @@
<script setup lang="ts">
const props = defineProps({
title: {
type: String,
required: true,
},
subtext: {
type: String,
required: true,
},
image: {
type: String,
required: true,
},
summary: {
type: String,
// required: true,
},
infoSubtext: {
type: String,
},
date: {
type: String,
// required: true,
},
runtime: {
type: String,
// required: true,
},
imdbRating: {
type: String,
// required: true,
},
MPAA: {
type: String,
},
})
</script>
<template>
<div class="scroller-item">
<div class="scroller-item-img">
<font-awesome-icon icon="fa-solid fa-play" class="play-icon" />
<img
:src="'/src/assets/images/' + props.image"
alt=""
:style="{
width: `${240}px`,
}"
/>
<div class="info-box">
<div class="info-box-content">
<div class="info-box-header">
<div class="info-box-title">{{ props.title }}</div>
<div class="info-box-subtext">{{ props.infoSubtext }}</div>
</div>
<div class="info-box-summary clamp-6">{{ props.summary }}</div>
<div class="info-box-misc-info">
<div class="date">{{ props.date }}</div>
<div class="runtime">{{ props.runtime }}</div>
<div class="imdb">
<font-awesome-icon icon="fa-solid fa-star" size="xs" class="star-rating" />
{{ props.imdbRating }}
</div>
<div class="mpaa">
<span class="age-rating sm">{{ props.MPAA }}</span>
</div>
<div class="ends-at">Ends at 6:23pm</div>
</div>
</div>
</div>
</div>
<div class="scroller-item-title clamp-2" :style="{ maxWidth: `${240 - 5}px` }">
{{ props.title }}
</div>
<div class="scroller-item-subtext clamp" :style="{ maxWidth: `${240 - 5}px` }">
{{ props.subtext }}
</div>
</div>
</template>
<style scoped>
.scroller-item {
display: flex;
flex-direction: column;
align-items: center;
}
/* .scroller-item:hover {
cursor: pointer;
} */
.play-icon {
position: absolute;
top: 45px;
left: 95px;
width: 35px;
height: 35px;
cursor: pointer;
z-index: 3;
/* visibility: hidden; */
opacity: 0%;
padding: 0.75em;
/* From https://css.glass */
background: rgba(255, 255, 255, 0.2);
color: rgba(255, 255, 255, 0.5);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(6.6px);
-webkit-backdrop-filter: blur(6.6px);
border: 1px solid rgba(255, 255, 255, 0.3);
transition: background 10ms linear 0ms;
}
.play-icon:hover {
background: rgba(122, 122, 122, 0.2);
}
.scroller-item-img:hover .play-icon {
top: 65px;
left: 135px;
opacity: 100%;
transition:
all 0.25s ease 0.5s,
z-index 1ms,
background 10ms linear 0ms;
z-index: 4;
visibility: visible;
}
.scroller-item-img {
position: relative;
}
.scroller-item-img img {
position: relative;
background-size: cover;
z-index: 1;
transition-duration: 0.3s;
}
.scroller-item-img:hover img {
transform: scale(1.35) translateX(32px) translateY(17px);
cursor: pointer;
transition:
all 0.25s ease 0.5s,
z-index 1ms;
position: relative;
z-index: 3;
}
.scroller-item-title {
text-align: center;
}
.scroller-item-subtext {
color: var(--color-text-faded);
max-width: 245px;
text-align: center;
}
.info-box {
position: absolute;
top: 0;
left: 0;
z-index: 0;
width: 240px;
height: 135px;
visibility: hidden;
background-color: var(--color-background-grey);
transition-duration: 0.3s;
}
.scroller-item-img:hover .info-box {
visibility: visible;
transition-delay: 0.5s;
transform: translateX(324px);
width: 340px;
height: 183px;
z-index: 2;
transition-duration: 0.3s;
}
.info-box-content {
width: 90%;
margin: 0 auto;
padding: 1em;
max-height: 135px;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: baseline;
gap: 2em;
min-height: 80%;
}
.scroller-item:hover .info-box-content {
max-height: 183px;
transition-delay: 0.05s;
}
.info-box-title {
font-weight: 600;
font-size: 18px;
}
.info-box-subtext {
color: var(--color-text-faded);
}
.info-box-misc-info {
display: flex;
justify-content: space-between;
min-width: 90%;
}
</style>

@ -0,0 +1,202 @@
<script setup lang="ts">
const props = defineProps({
title: {
type: String,
required: true,
},
subtext: {
type: String,
required: true,
},
image: {
type: String,
required: true,
},
summary: {
type: String,
// required: true,
},
infoSubtext: {
type: String,
},
date: {
type: String,
// required: true,
},
runtime: {
type: String,
// required: true,
},
imdbRating: {
type: String,
// required: true,
},
MPAA: {
type: String,
},
})
</script>
<template>
<div class="scroller-item">
<div class="scroller-item-img">
<!-- <div class="scroller-item-poster-wrapper"> -->
<font-awesome-icon icon="fa-solid fa-play" class="play-icon" />
<img
:src="'/src/assets/images/' + props.image"
alt=""
:style="{
width: `${120}px`,
height: `${120 * (3 / 2)}px`,
}"
/>
<!-- </div> -->
<div class="info-box">
<div class="info-box-content">
<div class="info-box-header">
<div class="info-box-title">{{ props.title }}</div>
<div class="info-box-subtext">{{ props.infoSubtext }}</div>
</div>
<div class="info-box-summary clamp-6">{{ props.summary }}</div>
<div class="info-box-misc-info">
<div class="date">{{ props.date }}</div>
<div class="runtime">{{ props.runtime }}</div>
<div class="imdb">
<font-awesome-icon icon="fa-solid fa-star" size="xs" class="star-rating" />
{{ props.imdbRating }}
</div>
<div class="mpaa">
<span class="age-rating sm">{{ props.MPAA }}</span>
</div>
<div class="ends-at">Ends at 6:23pm</div>
</div>
</div>
</div>
</div>
<div class="scroller-item-title clamp-2" :style="{ maxWidth: `${120 - 5}px` }">
{{ props.title }}
</div>
<div class="scroller-item-subtext clamp" :style="{ maxWidth: `${120 - 5}px` }">
{{ props.subtext }}
</div>
</div>
</template>
<style scoped>
.scroller-item {
display: flex;
flex-direction: column;
align-items: center;
}
/* .scroller-item:hover {
cursor: pointer;
} */
.play-icon {
position: absolute;
top: 75px;
left: 35px;
width: 35px;
height: 35px;
cursor: pointer;
z-index: 1;
opacity: 0%;
padding: 0.75em;
background: rgba(255, 255, 255, 0.2);
color: rgba(255, 255, 255, 0.5);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(6.6px);
-webkit-backdrop-filter: blur(6.6px);
border: 1px solid rgba(255, 255, 255, 0.3);
transition: background 10ms linear 0ms;
}
.play-icon:hover {
background: rgba(122, 122, 122, 0.2);
}
.scroller-item-img:hover .play-icon {
top: 100px;
left: 55px;
opacity: 100%;
transition:
all 0.25s ease 0.5s,
z-index 1ms,
background 10ms linear 0ms;
z-index: 4;
visibility: visible;
}
.scroller-item-img {
position: relative;
}
.scroller-item-img img {
position: relative;
background-size: cover;
z-index: 1;
transition-duration: 0.3s;
}
.scroller-item-img:hover img {
transform: scale(1.35) translateX(16px) translateY(23px);
cursor: pointer;
transition:
all 0.25s ease,
z-index 1ms;
transition-delay: 0.5s;
position: relative;
z-index: 3;
}
.scroller-item-title {
text-align: center;
}
.scroller-item-subtext {
color: var(--color-text-faded);
max-width: 245px;
text-align: center;
}
.info-box {
position: absolute;
top: 0;
left: 0;
z-index: 0;
width: 120px;
height: 180px;
visibility: hidden;
background-color: var(--color-background-grey);
transition-duration: 0.3s;
}
.scroller-item-img:hover .info-box {
visibility: visible;
transition-delay: 0.5s;
transform: translateX(162px);
width: 360px;
height: 243px;
z-index: 2;
transition-duration: 0.3s;
}
.info-box-content {
width: 90%;
margin: 0 auto;
padding: 1em;
max-height: 180px;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: baseline;
gap: 2em;
min-height: 80%;
}
.scroller-item:hover .info-box-content {
max-height: 240px;
transition-delay: 0.05s;
}
.info-box-title {
font-weight: 600;
font-size: 18px;
}
.info-box-subtext {
color: var(--color-text-faded);
}
.info-box-misc-info {
display: flex;
justify-content: space-between;
min-width: 90%;
}
</style>

@ -0,0 +1,90 @@
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vite.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a>
+
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
you need to test your components and web pages, check out
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a>
and
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
>Cypress Component Testing</a
>.
<br />
More instructions are available in <code>README.md</code>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
Discord server, or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also subscribe to
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a>
and follow the official
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
twitter account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>

@ -0,0 +1,87 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>

@ -0,0 +1,11 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import HelloWorld from '../HelloWorld.vue'
describe('HelloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
expect(wrapper.text()).toContain('Hello Vitest')
})
})

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>

@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>

@ -0,0 +1,150 @@
const ContinueWatching = [
{
title: 'Bloom Into You',
subtext: 'S1:E7 - Secrets Galore / Sparks',
image: 'biy_ep.jpg',
},
{
title: 'The Bear',
subtext: 'S3:E9 - Apologies',
image: 'bear_ep.jpg',
},
{
title: 'BOCCHI THE ROCK!',
subtext: 'S1:E12 - Morning Light Falls on You',
image: 'bocchi_ep.jpg',
},
{
title: 'Twenty Five Twenty One',
subtext: 'S1:E5 - Episode 5',
image: '2521_ep.jpg',
summary: `Hee-do takes one giant leap closer to her dream. She's eager to tell Yi-jin the good news — but realizes he's disappeared without a trace.`,
infoSubtext: 'S1:E5 - Episode 5',
imdbRating: '8.7',
runtime: '1h 16m',
date: '2022',
MPAA: 'TV-14',
},
]
const LatestMovies = [
{
title: 'Alien',
subtext: '1979',
image: 'alien.jpg',
},
{
title: 'Your Name',
subtext: '2017',
image: 'yourname.jpg',
},
{
title: 'SoulMate',
subtext: '2016',
image: 'soulmate.jpg',
summary:
'The films spans two decades as the story unfolds in a series of flashbacks that begin when Qiyue and Ansheng were just thirteen. The two became inseparable until they met a boy who ended up tearing their lives apart.',
infoSubtext: '七月与安生',
imdbRating: '7.3',
runtime: '1h 50m',
date: '2016',
MPAA: 'PG-13',
},
{
title: 'Burning',
subtext: '2018',
image: 'burning.jpg',
},
{
title: 'Better Days',
subtext: '2019',
image: 'betterdays.jpg',
},
{
title: 'House of Hummingbird',
subtext: '2019',
image: 'hummingbird.jpg',
},
{
title: 'Where We Belong',
subtext: '2019',
image: 'wwb.jpg',
summary: `Will there be someone like you where i'm going?\n
A teenager creates a checklist to complete before she studies abroad and realizes that her toughest task is leaving behind her best friend.`,
infoSubtext: 'ที่ตรงนั้น มีฉันหรือเปล่า',
imdbRating: '7.6',
runtime: '2h 12m',
date: '2019',
MPAA: 'R',
},
{
title: '1917',
subtext: '2019',
image: '1917.jpg',
},
{
title: 'Life of Brian',
subtext: '1979',
image: 'lob.jpg',
},
{
title: 'Shoplifters',
subtext: '2018',
image: 'shoplifters.jpg',
},
{
title: 'The Lobster',
subtext: '2016',
image: 'lobster.jpg',
},
{
title: 'Her',
subtext: '2013',
image: 'her.jpg',
},
]
const LatestAnime = [
{
title: 'Re:Zero - Starting Life in Another World',
subtext: 'S3:E6 - Conditions of the Life We Live In',
image: 'rezero.jpg',
},
{
title: 'Serial Experiments Lain',
subtext: '1998',
image: 'lain.jpg',
},
{
title: 'Monster',
subtext: '2004-2005',
image: 'monster.jpg',
},
{
title: 'Bloom Into You',
subtext: '2018',
image: 'biy.jpg',
},
{
title: 'Attack on Titan',
subtext: '2013-2022',
image: 'aot.jpg',
},
{
title: "Frieren: Beyond Journey's End",
subtext: '2023-2024',
image: 'frieren.jpg',
},
{
title: 'Lycoris Recoil',
subtext: '2022',
image: 'lycoris.jpg',
},
{
title: 'Tengoku Daimakyo',
subtext: '2023 - Present',
image: 'tengoku.jpg',
},
]
export { ContinueWatching, LatestMovies, LatestAnime }

@ -0,0 +1,39 @@
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
/* import the fontawesome core */
import { library } from '@fortawesome/fontawesome-svg-core'
/* import font awesome icon component */
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
fetch('http://192.168.0.63:30013/Branding/Configuration')
.then((r) => r.json())
.then((r) => console.log(r))
/* import specific icons */
import {
faCheck,
faHeart,
faPlay,
faBars,
faEllipsis,
faStar,
faCircle,
} from '@fortawesome/free-solid-svg-icons'
/* add icons to the library */
library.add(faCheck, faHeart, faPlay, faBars, faEllipsis, faStar, faCircle)
const app = createApp(App)
app.component('font-awesome-icon', FontAwesomeIcon)
app.use(createPinia())
app.use(router)
app.mount('#app')

@ -0,0 +1,23 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
],
})
export default router

@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

@ -0,0 +1,15 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

@ -0,0 +1,46 @@
<script setup lang="ts">
import MediaShowcase from '@/components/MediaShowcase.vue'
import MediaScroller from '@/components/MediaScroller.vue'
import { ContinueWatching, LatestAnime, LatestMovies } from '@/feed'
</script>
<template>
<main>
<MediaShowcase />
<div class="home-nav">
<div class="home-nav-item active">Home</div>
<div class="home-nav-item">4k Movies</div>
<div class="home-nav-item">Anime</div>
<div class="home-nav-item">Movies</div>
<div class="home-nav-item">Shows</div>
</div>
<MediaScroller title="Continue Watching" landscape :items="ContinueWatching" />
<MediaScroller title="Latest Movies" :items="LatestMovies" />
<MediaScroller title="Latest Anime" :items="LatestAnime" />
</main>
</template>
<style scoped>
.home-nav {
display: flex;
justify-content: center;
margin-right: 11.5%;
margin-top: 1.5em;
gap: 3em;
align-items: center;
}
.home-nav-item:hover {
cursor: pointer;
color: var(--color-text-faded);
}
.home-nav-item.active {
padding: 0.4em 1.5em;
background-color: var(--color-text);
border-radius: 15px;
color: var(--color-text-dark);
font-weight: 600;
}
.home-nav-item.active:hover {
background-color: var(--color-text-faded);
}
</style>

@ -0,0 +1,13 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
}

@ -0,0 +1,14 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.vitest.json"
}
]
}

@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

@ -0,0 +1,11 @@
{
"extends": "./tsconfig.app.json",
"exclude": [],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
"lib": [],
"types": ["node", "jsdom"]
}
}

@ -0,0 +1,18 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})

@ -0,0 +1,14 @@
import { fileURLToPath } from 'node:url'
import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(
viteConfig,
defineConfig({
test: {
environment: 'jsdom',
exclude: [...configDefaults.exclude, 'e2e/**'],
root: fileURLToPath(new URL('./', import.meta.url)),
},
}),
)
Loading…
Cancel
Save