Compare commits
3 Commits
b428649aa0
...
e98010af26
| Author | SHA1 | Date | |
|---|---|---|---|
| e98010af26 | |||
| 5940dbc4fc | |||
| 28946028e8 |
@@ -3,11 +3,13 @@
|
|||||||
--ui-primary: var(--color-deep-dark);
|
--ui-primary: var(--color-deep-dark);
|
||||||
--ui-container: 64rem;
|
--ui-container: 64rem;
|
||||||
--ui-container-narrow: 48rem;
|
--ui-container-narrow: 48rem;
|
||||||
|
--ui-container-narrow-very: 36rem;
|
||||||
--ui-header-logo-inverted: 1;
|
--ui-header-logo-inverted: 1;
|
||||||
--ui-header-height: calc(var(--ui-spacing)*14);
|
--ui-header-height: calc(var(--ui-spacing)*14);
|
||||||
--ui-header-height-collapsed: calc(var(--ui-spacing)*12);
|
--ui-header-height-collapsed: calc(var(--ui-spacing)*12);
|
||||||
|
|
||||||
--color-surface-container: #f9f9f9;
|
--color-surface-container: #f9f9f9;
|
||||||
|
--color-surface-container-glass: #f9f9f9de;
|
||||||
--color-on-surface-container: #111111;
|
--color-on-surface-container: #111111;
|
||||||
--color-inverse-surface-container: #111111;
|
--color-inverse-surface-container: #111111;
|
||||||
--color-on-inverse-surface-container: #f9f9f9;
|
--color-on-inverse-surface-container: #f9f9f9;
|
||||||
@@ -40,6 +42,7 @@
|
|||||||
@media screen and (prefers-color-scheme: dark) {
|
@media screen and (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--color-surface-container: #111111;
|
--color-surface-container: #111111;
|
||||||
|
--color-surface-container-glass: #111111de;
|
||||||
--color-on-surface-container: #eeeeee;
|
--color-on-surface-container: #eeeeee;
|
||||||
--color-inverse-surface-container: #eeeeee;
|
--color-inverse-surface-container: #eeeeee;
|
||||||
--color-on-inverse-surface-container: #111111;
|
--color-on-inverse-surface-container: #111111;
|
||||||
@@ -341,6 +344,10 @@ p {
|
|||||||
background: var(--color-inverse-surface-container);
|
background: var(--color-inverse-surface-container);
|
||||||
padding-block: calc(var(--ui-spacing)*12);
|
padding-block: calc(var(--ui-spacing)*12);
|
||||||
}
|
}
|
||||||
|
.article.article-footer.article-footer-no-bg {
|
||||||
|
color: inherit;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
.article.article-footer .web-footer-link > a:active {
|
.article.article-footer .web-footer-link > a:active {
|
||||||
text-decoration-style: solid;
|
text-decoration-style: solid;
|
||||||
}
|
}
|
||||||
@@ -363,6 +370,9 @@ p {
|
|||||||
.web-section.web-section-narrow {
|
.web-section.web-section-narrow {
|
||||||
max-width: var(--ui-container-narrow);
|
max-width: var(--ui-container-narrow);
|
||||||
}
|
}
|
||||||
|
.web-section.web-section-narrow-very {
|
||||||
|
max-width: var(--ui-container-narrow-very);
|
||||||
|
}
|
||||||
.about-me-photo-grid {
|
.about-me-photo-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||||
@@ -713,7 +723,7 @@ a.btn {
|
|||||||
flex: 3;
|
flex: 3;
|
||||||
}
|
}
|
||||||
.prose p, li { line-height: 1.5; }
|
.prose p, li { line-height: 1.5; }
|
||||||
.prose hr { margin-block: 1em; border: 1px solid var(--color-outline-intense); }
|
hr, .prose hr { height: 1px; margin-block: 1em; border: none; border-bottom: 1px solid var(--color-outline-intense); }
|
||||||
.prose li { margin-block: 1em; }
|
.prose li { margin-block: 1em; }
|
||||||
.prose h1,h2,h3,h4,h5,h6 {
|
.prose h1,h2,h3,h4,h5,h6 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
@@ -794,4 +804,51 @@ a.btn {
|
|||||||
padding: 0 calc(var(--ui-spacing)*4);
|
padding: 0 calc(var(--ui-spacing)*4);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link-card {
|
||||||
|
display: block;
|
||||||
|
padding: calc(var(--ui-spacing)*4) calc(var(--ui-spacing)*6);
|
||||||
|
border-radius: 100px;
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-on-primary);
|
||||||
|
margin-bottom: calc(var(--ui-spacing)*1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.tree-link-card .material-symbols-outlined {
|
||||||
|
margin-right: calc(var(--ui-spacing)*1);
|
||||||
|
}
|
||||||
|
.tree-link-card:hover {
|
||||||
|
background: var(--color-inverse);
|
||||||
|
color: var(--color-on-inverse);
|
||||||
|
}
|
||||||
|
.tree-link-card:hover .material-symbols-outlined {
|
||||||
|
font-variation-settings:
|
||||||
|
'FILL' 1
|
||||||
|
}
|
||||||
|
.tree-link-card:active {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
a.tree-link-card {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.frosted-glass-backdrop-dynamic {
|
||||||
|
background: var(--color-surface-container-glass);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
position: fixed;
|
||||||
|
z-index: -10;
|
||||||
|
backdrop-filter: blur(36px);
|
||||||
|
-webkit-backdrop-filter: (36px);
|
||||||
|
}
|
||||||
|
pre.codebox {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 12px;
|
||||||
|
padding: calc(var(--ui-spacing)*3);
|
||||||
|
border-radius: calc(var(--ui-spacing)*2);
|
||||||
|
background: var(--color-inverse-surface-container);
|
||||||
|
color: var(--color-on-inverse-surface-container);
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
|
const props = defineProps<{
|
||||||
|
isCompact?: boolean;
|
||||||
|
isTransparent?: boolean;
|
||||||
|
title?: string;
|
||||||
|
noTitle?: boolean;
|
||||||
|
narrowVery?: boolean;
|
||||||
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<footer>
|
<footer>
|
||||||
<div class="article article-footer">
|
<div :class="{'article': true, 'article-footer': true, 'article-footer-no-bg': props.isTransparent}">
|
||||||
<section class="web-section">
|
<section v-if="!props.isCompact" :class="{'web-section': true, 'web-section-narrow-very': props.narrowVery}">
|
||||||
<div class="web-footer-links-container-container">
|
<div class="web-footer-links-container-container">
|
||||||
<div class="web-footer-links-container" style="margin-right: 4rem;">
|
<div class="web-footer-links-container" style="margin-right: 4rem;">
|
||||||
<h3 class="web-footer-link-title">Site</h3>
|
<h3 class="web-footer-link-title">Site</h3>
|
||||||
@@ -26,10 +33,10 @@ const currentYear = new Date().getFullYear();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="web-section">
|
<section :class="{'web-section': true, 'web-section-narrow-very': props.narrowVery}">
|
||||||
<div>
|
<div>
|
||||||
<p class="web-footer-notice">
|
<p v-if="!props.noTitle" class="web-footer-notice">
|
||||||
Techit Thawiang's Website
|
{{props.title || "Techit Thawiang's Website"}}
|
||||||
</p>
|
</p>
|
||||||
<p class="web-footer-notice">
|
<p class="web-footer-notice">
|
||||||
Copyright © Techit Thawiang {{ currentYear }} ({{ currentYear + 543 }}). All rights reserved.<br>
|
Copyright © Techit Thawiang {{ currentYear }} ({{ currentYear + 543 }}). All rights reserved.<br>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<slot/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<main style="background: transparent;">
|
||||||
|
<div class="frosted-glass-backdrop-dynamic"></div>
|
||||||
|
<article class="article article-no-bg" v-if="tree">
|
||||||
|
<section class="web-section web-section-narrow-very">
|
||||||
|
<img :width="128" :height="128" :src="tree.profile">
|
||||||
|
<h1 v-if="tree.name">{{ tree.name }}</h1>
|
||||||
|
<p v-if="tree.desc">{{ tree.desc }}</p>
|
||||||
|
</section>
|
||||||
|
<section class="web-section web-section-narrow-very">
|
||||||
|
<div v-for="link in tree.links">
|
||||||
|
<a class="tree-link-card" :href="link.url" v-if="link.enabled && !link.separate">
|
||||||
|
<span v-if="link.ico" class="material-symbols-outlined">{{ link.ico }}</span>{{ link.name }}
|
||||||
|
</a>
|
||||||
|
<hr v-else-if="link.separate">
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const route = useRoute();
|
||||||
|
const JSON_URL = config.public.treeJsonUrl
|
||||||
|
const treeUserData = JSON_URL + route.params.slug + '.json'
|
||||||
|
|
||||||
|
interface TreeUserData {
|
||||||
|
id: string
|
||||||
|
enabled: boolean
|
||||||
|
name: string
|
||||||
|
desc: string
|
||||||
|
cover: string
|
||||||
|
background: string
|
||||||
|
profile: string
|
||||||
|
links: Record<string, TreeUserDataLink>
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TreeUserDataLink {
|
||||||
|
id: string
|
||||||
|
ico: string
|
||||||
|
enabled: boolean
|
||||||
|
separate: boolean
|
||||||
|
name: string
|
||||||
|
desc: string
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: tree, error } = await useAsyncData('treeUserData', () =>
|
||||||
|
$fetch<TreeUserData>(treeUserData)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error('User tree fetch failed:', error.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.value?.statusCode === 404) {
|
||||||
|
await navigateTo('/tree')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree.value) {
|
||||||
|
useSeoMeta({
|
||||||
|
titleTemplate: "%s",
|
||||||
|
title: `${tree.value.name}'s Link`,
|
||||||
|
description: tree.value.desc,
|
||||||
|
ogTitle: tree.value.name,
|
||||||
|
ogDescription: tree.value.desc,
|
||||||
|
ogSiteName: config.public.siteName,
|
||||||
|
twitterCard: 'summary_large_image',
|
||||||
|
twitterTitle: tree.value.name,
|
||||||
|
twitterDescription: tree.value.desc,
|
||||||
|
twitterSite: config.public.twitterUsername
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (tree.value?.background) {
|
||||||
|
document.body.style.backgroundImage = `url('${tree.value.background}')`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
layout: 'tree'
|
||||||
|
})
|
||||||
|
|
||||||
|
useSeoMeta({
|
||||||
|
robots: "noindex",
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<article class="article">
|
||||||
|
<section class="web-section" aria-labelledby="hero" aria-describedby="what-does-he-do?">
|
||||||
|
<img style="margin-bottom:calc(var(--ui-spacing)*4);" src="https://files.techit.win/files/assets/tree-example.png">
|
||||||
|
<h1 id="hero" class="font-hero">Tree</h1>
|
||||||
|
<p id="what-does-he-do?" class="font-hero-desc">A SaaS from me to list all your <i>publicly-available</i> link and stuff.</p>
|
||||||
|
</section>
|
||||||
|
<section class="web-section" aria-labelledby="about-me" aria-describedby="about-me-paragraph-1">
|
||||||
|
<h2 class="web-title" id="about-me">How to use?</h2>
|
||||||
|
<p id="about-me-paragraph-1">Currently I don't have an easy way to make it, but you can <NuxtLink class="link" href="/contact">contact me</NuxtLink> to get one for yourself.</p>
|
||||||
|
</section>
|
||||||
|
<section class="web-section" aria-labelledby="manual" aria-describedby="manual-paragraph-1">
|
||||||
|
<h2 class="web-title" id="manual">Manual</h2>
|
||||||
|
<p id="manual-paragraph-1">This thing is kind of tricky to use at the time, so to work with me without headache, please take a look below.</p>
|
||||||
|
<h3>Features</h3>
|
||||||
|
<p>Currently there aren't much features, to put it easy I only offer custom background image, custom profile image and custom open graph cover image <small>(this can leave blank but will results in no cover image for social media embeds or cards)</small> for now.</p>
|
||||||
|
<h3>Slug</h3>
|
||||||
|
<p>This is obvious and simplest thing in the system, it is named after what you'd like, think of a username you would like to use. For example I want <NuxtLink class="link" href="/tree/techit">https://techit.win/tree/techit</NuxtLink>, just tell me you want <code>techit</code> as your slug.</p>
|
||||||
|
<img src="https://files.techit.win/files/assets/sdfasdf.png">
|
||||||
|
<h3>JSON structure</h3>
|
||||||
|
<p>This is very messed up because I'm very lazy to make a proper API and database than to make a simple JSON static file.</p>
|
||||||
|
<p>Oh! Note that "ico" in the links children needs to be an icon name that are from <a href="https://fonts.google.com/icons">https://fonts.google.com/icons</a> because it's easy to do so, but it does not have branded icon.</p>
|
||||||
|
<p>Also I don't think "id" is necessary so not putting one will not make it broken, it's just there because I designed it to do it.</p>
|
||||||
|
<pre class="codebox">
|
||||||
|
{
|
||||||
|
"id": "techit",
|
||||||
|
"enabled": "true",
|
||||||
|
"name": "Techit Thawiang",
|
||||||
|
"desc": "A 10th grader who passionate in computer engineering.",
|
||||||
|
"background": "https://files.techit.win/files/jason-leung-OrKLsfQYkRA-unsplash.jpg",
|
||||||
|
"profile": "https://files.techit.win/files/assets/profile/techit/1758801557516.jpeg",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"id": "web",
|
||||||
|
"ico": "web",
|
||||||
|
"enabled": true,
|
||||||
|
"separator": false,
|
||||||
|
"name": "เว็บไซต์ส่วนตัว",
|
||||||
|
"url": "https://techit.win/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "print",
|
||||||
|
"ico": "print",
|
||||||
|
"enabled": true,
|
||||||
|
"separator": false,
|
||||||
|
"name": "พิมพ์งานกับเตชิต",
|
||||||
|
"url": "https://techit.win/print"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sep1",
|
||||||
|
"enabled": true,
|
||||||
|
"separate": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fb",
|
||||||
|
"enabled": true,
|
||||||
|
"separate": false,
|
||||||
|
"name": "Facebook: Techit Thawiang",
|
||||||
|
"url": "https://facebook.com/techitwinner/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "web",
|
||||||
|
"enabled": true,
|
||||||
|
"separate": false,
|
||||||
|
"name": "Instagram: techitwinner",
|
||||||
|
"url": "https://instagram.com/techitwinner/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}</pre>
|
||||||
|
<h3>TypeScript Interface</h3>
|
||||||
|
<pre class="codebox">
|
||||||
|
interface TreeUserData {
|
||||||
|
id: string
|
||||||
|
enabled: boolean
|
||||||
|
name: string
|
||||||
|
desc: string
|
||||||
|
cover: string
|
||||||
|
background: string
|
||||||
|
profile: string
|
||||||
|
links: Record<string, TreeUserDataLink>
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TreeUserDataLink {
|
||||||
|
id: string
|
||||||
|
ico: string
|
||||||
|
enabled: boolean
|
||||||
|
separate: boolean
|
||||||
|
name: string
|
||||||
|
desc: string
|
||||||
|
url: string
|
||||||
|
}</pre>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const baseUrl = config.public.baseUrl
|
||||||
|
const TITLE = "Tree"
|
||||||
|
const DESC = "A SaaS from me to list all your publicly-available link and stuff."
|
||||||
|
|
||||||
|
useSeoMeta({
|
||||||
|
title: TITLE,
|
||||||
|
description: DESC,
|
||||||
|
ogTitle: TITLE + ' / ' + config.public.siteName,
|
||||||
|
ogDescription: DESC,
|
||||||
|
ogSiteName: config.public.siteName,
|
||||||
|
twitterCard: 'summary_large_image',
|
||||||
|
twitterTitle: TITLE + ' / ' + config.public.siteName,
|
||||||
|
twitterDescription: DESC,
|
||||||
|
twitterSite: config.public.twitterUsername
|
||||||
|
})
|
||||||
|
</script>
|
||||||
+3
-1
@@ -11,7 +11,8 @@ export default defineNuxtConfig({
|
|||||||
siteName: process?.env?.NUXT_PUBLIC_SITE_NAME,
|
siteName: process?.env?.NUXT_PUBLIC_SITE_NAME,
|
||||||
twitterUsername: process?.env?.NUXT_PUBLIC_TWITTER_USERNAME,
|
twitterUsername: process?.env?.NUXT_PUBLIC_TWITTER_USERNAME,
|
||||||
fontUrl: process?.env?.NUXT_PUBLIC_FONTS_URL,
|
fontUrl: process?.env?.NUXT_PUBLIC_FONTS_URL,
|
||||||
webBannerDataUrl: process?.env?.NUXT_PUBLIC_BANNER_DATA_URL
|
webBannerDataUrl: process?.env?.NUXT_PUBLIC_BANNER_DATA_URL,
|
||||||
|
treeJsonUrl: process?.env?.NUXT_PUBLIC_TREE_JSON_URL
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modules: ['@nuxt/content', '@nuxtjs/sitemap', '@nuxt/image', '@nuxt/icon'],
|
modules: ['@nuxt/content', '@nuxtjs/sitemap', '@nuxt/image', '@nuxt/icon'],
|
||||||
@@ -102,5 +103,6 @@ export default defineNuxtConfig({
|
|||||||
"*": { experimentalNoScripts: true }, // one level deep, render all pages statically
|
"*": { experimentalNoScripts: true }, // one level deep, render all pages statically
|
||||||
"posts/*": { experimentalNoScripts: true }, // one level deep, render all post pages statically
|
"posts/*": { experimentalNoScripts: true }, // one level deep, render all post pages statically
|
||||||
"fonts": { experimentalNoScripts: false }, // except /fonts
|
"fonts": { experimentalNoScripts: false }, // except /fonts
|
||||||
|
"tree/*": { experimentalNoScripts: false }, // except /tree
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@techitwinner/web",
|
"name": "@techitwinner/web",
|
||||||
"version": "16.1.3",
|
"version": "16.1.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+16
-1
@@ -1,4 +1,19 @@
|
|||||||
{
|
{
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
"extends": "./.nuxt/tsconfig.json"
|
"extends": "./.nuxt/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
// "types": ["@types/node"],
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./*"],
|
||||||
|
"@/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
".nuxt/*",
|
||||||
|
"nuxt.config.ts",
|
||||||
|
"app/**/*.ts",
|
||||||
|
"app/**/*.vue",
|
||||||
|
"app/types/**/*.d.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user