Compare commits

9 Commits

Author SHA1 Message Date
techit 6caedc84dd fix experimentalNoScripts for treee
Deploy to GitHub Pages / build (push) Has been cancelled
Deploy to GitHub Pages / deploy (push) Has been cancelled
2025-11-09 17:57:19 +07:00
techit 3f28105ce2 Fix canonical link mapping
Deploy to GitHub Pages / build (push) Has been cancelled
Deploy to GitHub Pages / deploy (push) Has been cancelled
2025-11-09 17:54:34 +07:00
techit a677ffa219 seo fix 2025-11-09 17:48:47 +07:00
techit f1378c6775 tree styles polishing 2025-11-09 17:30:29 +07:00
techit c7b9e08ec8 add legal stuff 2025-11-09 10:11:34 +07:00
techit 588605f21e Add get your own tree button and chnage example link 2025-11-09 09:40:32 +07:00
techit e98010af26 Make a proper /tree page
Deploy to GitHub Pages / build (push) Has been cancelled
Deploy to GitHub Pages / deploy (push) Has been cancelled
2025-11-08 20:43:04 +07:00
techit 5940dbc4fc Add icon support for tree links entry 2025-11-08 20:42:38 +07:00
techit 28946028e8 Add tree support, to v16.1.4 2025-11-08 18:22:47 +07:00
11 changed files with 603 additions and 9 deletions
+85 -1
View File
@@ -3,11 +3,13 @@
--ui-primary: var(--color-deep-dark);
--ui-container: 64rem;
--ui-container-narrow: 48rem;
--ui-container-narrow-very: 36rem;
--ui-header-logo-inverted: 1;
--ui-header-height: calc(var(--ui-spacing)*14);
--ui-header-height-collapsed: calc(var(--ui-spacing)*12);
--color-surface-container: #f9f9f9;
--color-surface-container-glass: #f9f9f9de;
--color-on-surface-container: #111111;
--color-inverse-surface-container: #111111;
--color-on-inverse-surface-container: #f9f9f9;
@@ -40,6 +42,7 @@
@media screen and (prefers-color-scheme: dark) {
:root {
--color-surface-container: #111111;
--color-surface-container-glass: #111111de;
--color-on-surface-container: #eeeeee;
--color-inverse-surface-container: #eeeeee;
--color-on-inverse-surface-container: #111111;
@@ -341,6 +344,10 @@ p {
background: var(--color-inverse-surface-container);
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 {
text-decoration-style: solid;
}
@@ -363,6 +370,9 @@ p {
.web-section.web-section-narrow {
max-width: var(--ui-container-narrow);
}
.web-section.web-section-narrow-very {
max-width: var(--ui-container-narrow-very);
}
.about-me-photo-grid {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
@@ -713,7 +723,7 @@ a.btn {
flex: 3;
}
.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 h1,h2,h3,h4,h5,h6 {
font-family: var(--font-serif);
@@ -794,4 +804,78 @@ a.btn {
padding: 0 calc(var(--ui-spacing)*4);
margin: 0 auto;
justify-content: space-between;
}
.tree-link-card {
display: flex;
align-items: center;
height: calc(var(--ui-spacing)*14);
padding: 0 calc(var(--ui-spacing)*6);
padding-left: calc(var(--ui-spacing)*14);
border-radius: calc(var(--ui-spacing)*2);
background: var(--color-primary);
color: var(--color-on-primary);
margin-bottom: calc(var(--ui-spacing)*1);
}
.tree-link-card:has(.tree-link-card-img),
.tree-link-card:has(.material-symbols-outlined) {
padding-left: calc(var(--ui-spacing)*2);
}
.tree-link-card:hover,
.tree-link-card:focus-visible {
background: var(--color-inverse);
color: var(--color-on-inverse);
}
.tree-link-card:active {
opacity: .8;
}
.tree-link-card-img,
.tree-link-card .material-symbols-outlined {
display: flex;
align-items: center;
justify-content: center;
width: calc(var(--ui-spacing)*10);
height: calc(var(--ui-spacing)*10);
border-radius: 50%;
margin-right: calc(var(--ui-spacing)*2);
}
.tree-link-card:hover .material-symbols-outlined,
.tree-link-card:focus-visible .material-symbols-outlined {
background: var(--color-container);
font-variation-settings:
'FILL' 1
}
.tree-link-card-content {
display: flex;
flex-direction: column;
}
.tree-link-card-content-desc {
font-size: 12px;
color: var(--color-outline-very-intense);
}
.tree-link-card:hover .tree-link-card-content-desc,
.tree-link-card:focus-visible .tree-link-card-content-desc {
color: var(--color-primary-strong);
}
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);
}
+14 -5
View File
@@ -1,11 +1,18 @@
<script setup lang="ts">
const currentYear = new Date().getFullYear();
const props = defineProps<{
isCompact?: boolean;
isTransparent?: boolean;
title?: string;
noTitle?: boolean;
narrowVery?: boolean;
}>();
</script>
<template>
<footer>
<div class="article article-footer">
<section class="web-section">
<div :class="{'article': true, 'article-footer': true, 'article-footer-no-bg': props.isTransparent}">
<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" style="margin-right: 4rem;">
<h3 class="web-footer-link-title">Site</h3>
@@ -22,20 +29,22 @@ const currentYear = new Date().getFullYear();
<h3 class="web-footer-link-title">More</h3>
<ul class="web-footer-links">
<li class="web-footer-link"><NuxtLink href="/services">Services</NuxtLink></li>
<li class="web-footer-link"><NuxtLink href="/tree">Tree</NuxtLink></li>
</ul>
</div>
</div>
</section>
<section class="web-section">
<section :class="{'web-section': true, 'web-section-narrow-very': props.narrowVery}">
<div>
<p class="web-footer-notice">
Techit Thawiang's Website
<p v-if="!props.noTitle" class="web-footer-notice">
{{props.title || "Techit Thawiang's Website"}}
</p>
<p class="web-footer-notice">
Copyright &copy; Techit Thawiang {{ currentYear }} ({{ currentYear + 543 }}). All rights reserved.<br>
PGP/GPG Key: <code style="font-size: 12px;"><a href="https://files.techit.win/files/Techit Thawiang_0xE649CED321557334_public.asc">4116 33BE 1B4A 19D4 8D77 9ADE E649 CED3 2155 7334</a></code><br>
Powered by <a class="link" href="https://dailitation.xyz">dailitation.xyz</a>; Website is available under <a class="link" href="https://gitskette.dailitation.xyz/techit/web">GPL-2.0</a>.
</p>
<p class="web-footer-notice"><NuxtLink href="/legal/privacy-policy">Privacy Policy</NuxtLink> | <NuxtLink href="/legal/cookies-policy">Cookies Policy</NuxtLink> | <NuxtLink href="/legal/terms-of-services">Terms of Services</NuxtLink></p>
</div>
</section>
</div>
+5
View File
@@ -0,0 +1,5 @@
<template>
<div>
<slot/>
</div>
</template>
+37
View File
@@ -0,0 +1,37 @@
<template>
<main>
<article class="article">
<section class="web-section" aria-labelledby="hero" aria-describedby="hero-desc">
<h1 id="hero" class="font-hero">{{TITLE}}</h1>
<p id="hero-desc" class="font-hero-desc">{{ DESC }}</p>
</section>
<section class="web-section">
<p>{{ config.public.siteName }} (<NuxtLink class="link" href="/">https://techit.win</NuxtLink>) does <strong>not</strong> use cookies or any similar tracking technologies.</p>
<p>We do not store any cookies on your device, and we do not track your browsing activity. Your privacy is completely respected and your data is never shared or sold.</p>
<p>If you have any questions about this Cookies Policy, you can contact me here:</p>
<ul>
<li><NuxtLink class="link" href="/contact">/contact</NuxtLink></li>
</ul>
</section>
</article>
</main>
</template>
<script setup>
const config = useRuntimeConfig();
const baseUrl = config.public.baseUrl
const TITLE = "Cookies Policy"
const DESC = "Last updated: November 9, 2025"
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>
+95
View File
@@ -0,0 +1,95 @@
<template>
<main>
<article class="article">
<section class="web-section" aria-labelledby="hero" aria-describedby="hero-desc">
<h1 id="hero" class="font-hero">{{TITLE}}</h1>
<p id="hero-desc" class="font-hero-desc">{{ DESC }}</p>
</section>
<section class="web-section">
<p>This Privacy Policy describes how I, Techit Thawiang, as an individual, handle and protect the personal information of anyone using my Service. It explains what data I may collect, how it is used, and your privacy rights.</p>
<p>By using the Service, you agree to the practices described in this Privacy Policy.</p>
<h2>Interpretation and Definitions</h2>
<h3>Definitions</h3>
<ul>
<li><strong>Account</strong>: A unique account created for you to access the Service or parts of it.</li>
<li><strong>Cookies</strong>: I do <strong>not</strong> use cookies or similar tracking technologies.</li>
<li><strong>Device</strong>: Any device that can access the Service, such as a computer, phone, or tablet.</li>
<li><strong>Personal Data</strong>: Any information that can identify you.</li>
<li><strong>Service</strong>: Refers to the Website.</li>
<li><strong>Usage Data</strong>: Data collected automatically when using the Service, such as device type, browser info, IP address, pages visited, and duration of visits. Usage Data is <strong>only accessible by me and my team on our server</strong>.</li>
<li><strong>Website</strong>: Refers to Techit Thawiang's Site, accessible at <NuxtLink class="link" href="/">{{ config.public.baseUrl }}</NuxtLink>.</li>
<li><strong>You</strong>: The individual using the Service.</li>
</ul>
<h2>Collection and Use of Your Personal Data</h2>
<h3>Types of Data Collected</h3>
<ul>
<li><strong>Personal Data:</strong> May include your first and last name or other necessary information to provide the Service.</li>
<li><strong>Usage Data:</strong> Collected automatically to operate and improve the Service. This data is <strong>never shared, sold, or tracked externally</strong>.</li>
</ul>
<h3>Tracking and Cookies</h3>
<p>I do <strong>not</strong> use cookies or any external tracking technologies. Your activity is <strong>private and will not be monitored by third parties</strong>.</p>
<h3>Use of Your Data</h3>
<p>Your personal data and usage data are used <strong>only to provide and maintain the Service</strong> and improve your experience. I will <strong>never sell your data</strong>. Usage data is accessible only by me and my trusted team on my own servers. No third-party companies have access.</p>
<h2>Data Sharing and Disclosure</h2>
<p>I do <strong>not</strong> share your data with third parties, affiliates, or businesses. Data may only be shared within my team to operate the Service.</p>
<p>Data may be disclosed <strong>only if required by law</strong> or to protect your rights or the safety of others.</p>
<h2>Retention of Data</h2>
<p>I retain your personal and usage data only as long as necessary to provide the Service. You can request deletion of your data at any time. I will respect your request unless retention is required by law.</p>
<h2>Security</h2>
<p>I take the security of your data seriously and store it securely on my own servers. However, no method of storage or transmission over the internet is 100% secure.</p>
<h2>Childrens Privacy</h2>
<p>The Service is <strong>not intended for anyone under the age of 13</strong>. I do not knowingly collect data from children under 13. If you are a parent and believe your child has provided data, please contact me to delete it.</p>
<h2>External Links</h2>
<p>The Service may contain links to external websites. I am <strong>not responsible</strong> for the content, privacy policies, or practices of third-party sites.</p>
<h2>Changes to This Privacy Policy</h2>
<p>I may update this Privacy Policy occasionally. Updates will be posted on this page, and the "Last Updated" date will reflect changes.</p>
<h2>Contact</h2>
<p>For any questions or requests regarding your personal data, you can contact me here:</p>
<ul>
<li><NuxtLink class="link" href="/contact">/contact</NuxtLink></li>
</ul>
<h2>Key Commitments</h2>
<ul>
<li>I do <strong>not sell your data</strong>.</li>
<li>I use <strong>no cookies or tracking</strong>.</li>
<li>Usage data is <strong>private</strong>, accessible only by me and my team on my server.</li>
<li>I am an <strong>individual</strong>, not a company.</li>
<li>Your privacy is <strong>100% protected</strong>.</li>
</ul>
</section>
</article>
</main>
</template>
<script setup>
const config = useRuntimeConfig();
const baseUrl = config.public.baseUrl
const TITLE = "Privacy Policy"
const DESC = "Last updated: November 9, 2025"
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>
+74
View File
@@ -0,0 +1,74 @@
<template>
<main>
<article class="article">
<section class="web-section" aria-labelledby="hero" aria-describedby="hero-desc">
<h1 id="hero" class="font-hero">{{TITLE}}</h1>
<p id="hero-desc" class="font-hero-desc">{{ DESC }}</p>
</section>
<section class="web-section">
<p>Welcome to {{ config.public.siteName }} (<NuxtLink class="link" href="/">https://techit.win</NuxtLink>). By accessing or using this website (the "Service"), you agree to these Terms of Service. Please read them carefully.</p>
<h2>1. Use of the Service</h2>
<p>You may use the Service only for lawful purposes and in accordance with these Terms. You agree not to misuse the Service, interfere with its operation, or attempt to access data or functionality that you are not authorized to access.</p>
<h2>2. No Account or Registration Requirement</h2>
<p>Our Service does not require you to create an account. If you do provide any personal information, it is collected solely to operate and improve the Service and will never be sold or shared with third parties.</p>
<h2>3. Privacy and Data</h2>
<p>Your privacy is important. I do not use cookies, tracking, or any third-party data collection. Usage data is stored securely on my server and is only accessible by me and my trusted team. No company or external entity will have access to your data.</p>
<h2>4. Intellectual Property</h2>
<p>All content on this website, including text, graphics, and other materials, is owned by me unless otherwise stated. You may not copy, reproduce, or distribute content without permission.</p>
<h2>5. Disclaimer</h2>
<p>The Service is provided "as is" and "as available." While I strive to ensure the Service is secure and accurate, I do not guarantee that it will always be error-free, uninterrupted, or free of harmful content. Use the Service at your own risk.</p>
<h2>6. Limitation of Liability</h2>
<p>To the fullest extent permitted by law, I am not liable for any damages arising from the use of the Service. This includes direct, indirect, incidental, or consequential damages.</p>
<h2>7. Changes to the Terms</h2>
<p>I may update these Terms from time to time. Changes will be posted on this page, and the "Last Updated" date will reflect the latest revision. Your continued use of the Service constitutes acceptance of the updated Terms.</p>
<h2>8. Governing Law</h2>
<p>These Terms are governed by the laws of Thailand. Any disputes arising from these Terms or your use of the Service will be resolved under Thai law.</p>
<h2>9. Contact</h2>
<p>If you have any questions about these Terms, you can contact me here:</p>
<ul>
<li><NuxtLink class="link" href="/contact">/contact</NuxtLink></li>
</ul>
</section>
<section class="web-section">
<h2 id="vaultwarden">Vaultwarden Service</h2>
<p>I provides a Vaultwarden instance at <a class="link" href="https://vw.techit.win" target="_blank">https://vw.techit.win</a> for anyone to use for free. By using this service, you acknowledge and agree to the following:</p>
<ul>
<li>The service is provided <strong>as-is</strong> with <strong>no guarantee of data safety or security</strong>. Use at your own risk.</li>
<li>I, the Vaultwarden administrator, also use this instance personally. It is a personal choice to maintain and operate this service.</li>
<li>Data stored on the Vaultwarden instance is backed up once every week to help prevent permanent loss. However, backups are not a guarantee, and I am not responsible for any loss of data.</li>
<li>By using this service, you agree that you are responsible for the confidentiality and security of your own credentials and data.</li>
</ul>
<p>Please ensure that you understand these conditions before using the Vaultwarden service.</p>
</section>
</article>
</main>
</template>
<script setup>
const config = useRuntimeConfig();
const baseUrl = config.public.baseUrl
const TITLE = "Terms of Services"
const DESC = "Last updated: November 9, 2025"
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>
+157
View File
@@ -0,0 +1,157 @@
<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>
<img v-else-if="link.img" width="40" height="40" class="tree-link-card-img" :src="link.img">
<div v-if="link.name && link.desc" class="tree-link-card-content">
<span>{{ link.name }}</span>
<span class="tree-link-card-content-desc">{{ link.desc }}</span>
</div>
<span v-else-if="link.name">{{ link.name }}</span>
</a>
<hr v-else-if="link.separate">
</div>
</section>
<section class="web-section web-section-narrow-very">
<a class="btn btn-sm" href="/tree">Get your own Tree</a>
<p class="web-footer-notice"><a class="link" href="/legal/privacy-policy">Privacy Policy</a> | <a class="link" href="/legal/cookies-policy">Cookies Policy</a> | <a class="link" href="/legal/terms-of-services">Terms of Services</a></p>
</section>
</article>
</main>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'tree'
})
interface TreeUserData {
id: string
index: boolean
twitterCreator: string
enabled: boolean
name: string
desc: string
cover: string
background: string
profile: string
links: Record<string, TreeUserDataLink>
};
interface TreeUserDataLink {
id: string
ico: string
img: string
enabled: boolean
separate: boolean
name: string
desc: string
url: string
}
const config = useRuntimeConfig();
const route = useRoute();
const JSON_URL = config.public.treeJsonUrl
const treeUserData = JSON_URL + route.params.slug + '.json'
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}'s Link`,
ogDescription: tree.value.desc,
ogSiteName: `${tree.value.name}'s Link`,
twitterCard: 'summary_large_image',
twitterTitle: tree.value.name,
twitterDescription: tree.value.desc,
twitterSite: config.public.twitterUsername,
ogImage: tree.value.cover,
twitterImage: tree.value.cover,
ogImageType: 'image/jpeg',
ogLocale: 'en_US',
ogType: 'profile',
twitterCreator: tree.value.twitterCreator || null,
author: tree.value.name || null,
keywords: `${tree.value.name}, links, ${tree.value.desc.replace(/\s+/g, ', ')}`,
})
useHead({
link: [
{
rel: "canonical",
href: config.public.baseUrl + '/tree/' + route.params.slug
},
{
rel: 'icon',
type: 'image/png',
href: tree.value.profile || '/favicon.ico',
}
]
})
if (tree.value.index) {
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
"@context": "https://schema.org",
"@type": "Person",
"name": tree.value.name,
"description": tree.value.desc,
"url": config.public.baseUrl + '/' + route.params.slug,
"image": tree.value.profile || tree.value.cover,
"sameAs": Object.values(tree.value.links || {}).map(l => l.url),
}),
},
],
})
}
}
if (tree.value?.index) {
useSeoMeta({
robots: "index,follow",
})
} else {
useSeoMeta({
robots: "noindex,nofollow",
})
}
onMounted(() => {
if (tree.value?.background) {
document.body.style.backgroundImage = `url('${tree.value.background}')`
}
})
</script>
<style>
body {
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
}
</style>
+115
View File
@@ -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 <a class="link" href="/tree/techitwinner">https://techit.win/tree/techitwinner</a>, 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&lt;string, TreeUserDataLink&gt;
};
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>
+4 -1
View File
@@ -11,7 +11,8 @@ export default defineNuxtConfig({
siteName: process?.env?.NUXT_PUBLIC_SITE_NAME,
twitterUsername: process?.env?.NUXT_PUBLIC_TWITTER_USERNAME,
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'],
@@ -102,5 +103,7 @@ export default defineNuxtConfig({
"*": { experimentalNoScripts: true }, // one level deep, render all pages statically
"posts/*": { experimentalNoScripts: true }, // one level deep, render all post pages statically
"fonts": { experimentalNoScripts: false }, // except /fonts
"tree": { experimentalNoScripts: false }, // except /tree
"tree/*": { experimentalNoScripts: false }, // except /tree/*
}
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@techitwinner/web",
"version": "16.1.3",
"version": "16.1.4",
"private": true,
"type": "module",
"scripts": {
+16 -1
View File
@@ -1,4 +1,19 @@
{
// 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"
]
}