2 Commits

Author SHA1 Message Date
ed306ae554 New landing page 2025-11-30 13:27:58 +08:00
2598f6c112 ♻️ Migrated to new UI 2025-11-30 03:10:12 +08:00
15 changed files with 1454 additions and 1367 deletions

View File

@@ -1,8 +1,5 @@
<template> <template>
<n-config-provider <naive-config>
:theme-overrides="themeOverrides"
:theme="theme.value === 'dark' ? darkTheme : lightTheme"
>
<n-global-style /> <n-global-style />
<n-loading-bar-provider> <n-loading-bar-provider>
<n-dialog-provider> <n-dialog-provider>
@@ -13,33 +10,18 @@
</n-message-provider> </n-message-provider>
</n-dialog-provider> </n-dialog-provider>
</n-loading-bar-provider> </n-loading-bar-provider>
</n-config-provider> </naive-config>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { import {
darkTheme,
lightTheme,
NConfigProvider,
NGlobalStyle, NGlobalStyle,
NLoadingBarProvider, NLoadingBarProvider,
NDialogProvider, NDialogProvider,
NMessageProvider, NMessageProvider,
} from "naive-ui"; } from "naive-ui";
import '@fontsource-variable/nunito'; import "@fontsource-variable/nunito";
const themeOverrides = {
common: {
fontFamily: "Nunito Variable, v-sans, ui-system, -apple-system, sans-serif",
primaryColor: "#7D80BAFF",
primaryColorHover: "#9294C5FF",
primaryColorPressed: "#575B9DFF",
primaryColorSuppl: "#6B6FC1FF",
},
};
const theme = useColorMode();
</script> </script>
<style> <style>
@@ -47,7 +29,9 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: Nunito Variable, sans-serif; font-family:
Nunito Variable,
sans-serif;
scroll-behavior: smooth; scroll-behavior: smooth;
} }

29
app/assets/css/main.css Normal file
View File

@@ -0,0 +1,29 @@
@import "tailwindcss";
@plugin "daisyui";
@plugin "@tailwindcss/typography";
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities);
@layer base {
:root {
--font-family: "Nunito Variable", "Helvatica", sans-serif;
}
html,
body {
font-family: var(--font-family);
}
}
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}

13
app/auto-imports.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const useDialog: typeof import('naive-ui').useDialog
const useLoadingBar: typeof import('naive-ui').useLoadingBar
const useMessage: typeof import('naive-ui').useMessage
const useNotification: typeof import('naive-ui').useNotification
}

40
app/components.d.ts vendored Normal file
View File

@@ -0,0 +1,40 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import { GlobalComponents } from 'vue'
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
NAvatar: typeof import('naive-ui')['NAvatar']
NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard']
NDropdown: typeof import('naive-ui')['NDropdown']
NIcon: typeof import('naive-ui')['NIcon']
NMenu: typeof import('naive-ui')['NMenu']
NPopover: typeof import('naive-ui')['NPopover']
NTag: typeof import('naive-ui')['NTag']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
// For TSX support
declare global {
const NAvatar: typeof import('naive-ui')['NAvatar']
const NButton: typeof import('naive-ui')['NButton']
const NCard: typeof import('naive-ui')['NCard']
const NDropdown: typeof import('naive-ui')['NDropdown']
const NIcon: typeof import('naive-ui')['NIcon']
const NMenu: typeof import('naive-ui')['NMenu']
const NPopover: typeof import('naive-ui')['NPopover']
const NTag: typeof import('naive-ui')['NTag']
const RouterLink: typeof import('vue-router')['RouterLink']
const RouterView: typeof import('vue-router')['RouterView']
}

View File

@@ -1,53 +1,163 @@
<template> <template>
<n-layout class="h-screen"> <div class="flex flex-col min-h-screen">
<n-layout-header class="app-bar-blur"> <header
<div class="flex justify-between items-center container mx-auto"> class="navbar bg-transparent shadow-lg fixed top-0 left-0 right-0 backdrop-blur-2xl z-1000 h-[64px]"
<router-link to="/" class="text-lg font-bold"> Solsynth </router-link> >
<div class="flex gap-3"> <div class="container mx-auto flex items-center justify-between px-5">
<router-link to="/terms" class="text-md font-bold"> <nuxt-link to="/">
Legal <nuxt-img src="/favicon.png" alt="Solsynth" class="w-8 h-8" />
</router-link> </nuxt-link>
</div>
</div>
</n-layout-header>
<n-layout-content> <n-menu
v-if="breakpoints.isGreaterOrEqual('md')"
v-model:value="activeKey"
mode="horizontal"
:options="menuOptions"
style="width: auto"
/>
<n-popover v-else trigger="hover">
<template #trigger>
<n-button text>
<template #icon>
<n-icon :component="MenuOutlined" />
</template>
</n-button>
</template>
<n-menu
v-model:value="activeKey"
mode="vertical"
:options="menuOptions"
class="w-64"
style="height: auto"
/>
</n-popover>
<naive-color-mode-switch />
</div>
</header>
<main class="grow mt-[64px]">
<slot /> <slot />
</n-layout-content> </main>
</n-layout>
<footer
class="footer sm:footer-horizontal bg-base-200 text-base-content p-10"
>
<aside>
<nuxt-img src="/favicon.png" alt="Solsynth" class="w-12 h-12" />
<div>
<h3 class="text-lg font-bold">Solsynth</h3>
Making software, hardware and experiences since 2024
</div>
</aside>
<nav>
<h6 class="footer-title">Products</h6>
<a href="https://solian.app" target="_blank" class="link link-hover"
>Solar Network</a
>
<nuxt-link to="/products" class="link link-hover">Catalog</nuxt-link>
</nav>
<nav>
<h6 class="footer-title">Company</h6>
<nuxt-link to="/about" class="link link-hover">About us</nuxt-link>
<a
href="https://github.com/Solsynth"
target="_blank"
class="link link-hover"
>GitHub</a
>
</nav>
<nav>
<h6 class="footer-title">Legal</h6>
<nuxt-link to="/terms/user-agreement" class="link link-hover"
>Terms of Service</nuxt-link
>
<nuxt-link to="/terms/privacy-policy" class="link link-hover"
>Privacy Policy</nuxt-link
>
<nuxt-link to="/terms/refund-policy" class="link link-hover"
>Refund Policy</nuxt-link
>
<nuxt-link to="/terms" class="link link-hover">All Documents</nuxt-link>
</nav>
</footer>
</div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { NLayout, NLayoutHeader, NLayoutContent } from "naive-ui"; import type { MenuOption } from "naive-ui";
import { NIcon, NAvatar, NMenu } from "naive-ui";
import { computed, h } from "vue";
import { useRoute, RouterLink } from "vue-router";
import {
ExploreOutlined,
CategoryOutlined,
MenuOutlined,
} from "@vicons/material";
import { breakpointsTailwind } from "@vueuse/core";
const route = useRoute();
const breakpoints = useBreakpoints(breakpointsTailwind);
const { data: recentProducts } = await useAsyncData("recent-products", () =>
queryCollection("products").order("updatedDate", "DESC").limit(5).all()
);
const activeKey = computed(() => {
// Map route paths to menu keys
if (route.path === "/") return "explore";
if (route.path.startsWith("/products")) return "products";
return null;
});
function renderIcon(icon: any) {
return () => h(NIcon, null, { default: () => h(icon) });
}
function renderLabel(label: string, route: string) {
return () => h(RouterLink, { to: route }, { default: () => label });
}
function renderExternalLabel(label: string, url: string) {
return () =>
h("a", { href: url, target: "_blank" }, { default: () => label });
}
const menuOptions = computed<MenuOption[]>(() => {
const productChildren =
recentProducts.value?.map((product: any) => {
const id = product.stem.split("/").pop();
const hasPage = product.hasPage;
const url = hasPage ? `/products/${id}` : product.url;
return {
label: hasPage
? renderLabel(product.name, url)
: renderExternalLabel(product.name, url),
key: `product-${id}`,
icon: product.icon
? () =>
h(NAvatar, {
src: product.icon,
size: 24,
style: { backgroundColor: "transparent" },
})
: renderIcon(CategoryOutlined), // Fallback icon, ideally use product.icon if possible but requires NAvatar
};
}) || [];
return [
{
label: renderLabel("Explore", "/"),
key: "explore",
icon: renderIcon(ExploreOutlined),
},
{
label: renderLabel("Products", "/products"),
key: "products",
icon: renderIcon(CategoryOutlined),
children: productChildren,
},
];
});
</script> </script>
<style scoped>
.n-layout-header {
padding: 8px 24px;
border-color: var(--n-border-color);
height: 57px; /* Fixed height */
display: flex;
align-items: center;
}
.n-layout-content {
height: calc(100vh - 57px); /* Adjust based on header height */
}
.app-bar-blur {
-webkit-mask-image: linear-gradient(
to bottom,
rgba(0, 0, 0, 1) 40%,
rgba(0, 0, 0, 0.5) 65%,
rgba(0, 0, 0, 0) 100%
);
mask-image: linear-gradient(
to bottom,
rgba(0, 0, 0, 1) 40%,
rgba(0, 0, 0, 0.5) 65%,
rgba(0, 0, 0, 0) 100%
);
mask-repeat: no-repeat;
mask-size: 100%;
}
</style>

127
app/pages/about.vue Normal file
View File

@@ -0,0 +1,127 @@
<template>
<main class="container mx-auto h-full px-8 flex flex-col gap-16 py-16">
<!-- Hero Section -->
<div
class="text-center flex flex-col items-center justify-center animate-fade-in-up"
>
<h1 class="text-5xl font-extrabold mb-6">About Us</h1>
<p class="text-xl max-w-2xl opacity-80">
We are a collective of creators, dreamers, and builders.
</p>
</div>
<!-- Mission Section -->
<section
class="grid grid-cols-1 md:grid-cols-2 gap-12 items-center animate-fade-in-up delay-100"
>
<div>
<h2 class="text-3xl font-bold mb-4">Our Mission</h2>
<p class="text-lg opacity-90 leading-relaxed">
Our aim is not making a profit.
<i class="text-sm opacity-70">At least not yet.</i> <br /><br />
Instead, we hope we can spread the love to the world and make everyone
enjoy the fun of the Internet and technology. We believe in open
source, collaboration, and building things that bring joy.
</p>
</div>
<div class="flex justify-center">
<n-card class="max-w-sm w-full bg-opacity-50 backdrop-blur-sm">
<div class="flex flex-col items-center text-center p-4">
<n-icon size="48" class="mb-4 text-primary">
<heart-outlined />
</n-icon>
<h3 class="text-xl font-bold mb-2">Built with Love</h3>
<p class="opacity-80">
Every line of code is written with passion and care.
</p>
</div>
</n-card>
</div>
</section>
<!-- Team Section -->
<section class="animate-fade-in-up delay-200">
<h2 class="text-3xl font-bold mb-8 text-center">Meet the Team</h2>
<div class="flex flex-wrap justify-center gap-8">
<!-- Team Member Card -->
<n-card
class="max-w-xs w-full hover:shadow-lg transition-shadow duration-300"
>
<div class="flex flex-col items-center text-center">
<n-avatar
src="https://fs.solian.app/api/files/200ee92546244ed1a6a02202f5ca9cc9"
:size="120"
class="mb-4 shadow-md"
/>
<h3 class="text-xl font-bold">LittleSheep</h3>
<div class="flex items-center gap-2 mt-1 mb-3">
<n-tag size="small" type="primary" round>Founder</n-tag>
<n-button
text
tag="a"
size="small"
href="https://id.solian.app/@littlesheep"
target="_blank"
>
<n-icon size="18">
<info-outlined />
</n-icon>
</n-button>
</div>
<p class="text-sm opacity-75 mb-4">
Founder, CEO, CTO, Senior Developer, Marketing Engineer, Customer
Service Engineer, DevOps, Database Administrator, Product Manager,
UI/UX Designer, QA Engineer, Mobile Developer, Security Engineer,
Technical Writer, Project Manager, Community Manager, Software
Architect
</p>
<div class="flex gap-3">
<n-button
circle
size="small"
tag="a"
href="https://github.com/LittleSheep"
target="_blank"
>
<template #icon
><n-icon><logo-github /></n-icon
></template>
</n-button>
</div>
</div>
</n-card>
</div>
</section>
</main>
</template>
<script setup lang="ts">
import { InfoOutlined } from "@vicons/material";
useHead({
title: "About Us - Solsynth",
});
</script>
<style scoped>
.animate-fade-in-up {
animation: fadeInUp 0.8s ease-out forwards;
opacity: 0;
transform: translateY(20px);
}
.delay-100 {
animation-delay: 0.1s;
}
.delay-200 {
animation-delay: 0.2s;
}
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
</style>

View File

@@ -1,219 +1,260 @@
<template> <template>
<main class="container mx-auto h-full px-8 flex flex-col gap-16"> <main class="container mx-auto h-full px-8 flex flex-col gap-24 pb-24">
<div class="text-center py-56 flex flex-col items-center justify-center"> <!-- Hero Section -->
<nuxt-img src="/favicon.png" class="w-28 h-28 mb-4" /> <div
<h1 class="text-5xl font-extrabold mb-3">We <span id="who-are-we" /></h1> class="text-center min-h-[80vh] flex flex-col items-center justify-center relative"
<p class="text-xl mb-8"> >
<!-- Background decoration -->
<div
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-primary/20 rounded-full blur-[120px] -z-10 pointer-events-none"
></div>
<nuxt-img src="/favicon.png" class="w-32 h-32 mb-8 animate-float" />
<h1 class="text-6xl font-extrabold mb-6 tracking-tight">
We <span ref="typedElement" class="text-primary" />
</h1>
<p class="text-2xl mb-10 opacity-80 max-w-2xl">
We are a group of friends that make software, hardware and any stuff We are a group of friends that make software, hardware and any stuff
that interesting. that is interesting.
</p> </p>
<n-space justify="center"> <n-space justify="center" size="large">
<n-button type="primary" size="large" round tag="a" href="#products" <n-button
>Explore around</n-button type="primary"
> size="large"
<n-button type="default" size="large" round tag="a" href="#about" round
>About us</n-button tag="a"
href="#products"
class="px-8 text-lg font-bold shadow-lg shadow-primary/30 hover:shadow-primary/50 transition-shadow"
> >
Explore Products
</n-button>
<n-button size="large" round tag="a" href="/about" class="px-8 text-lg">
About Us
</n-button>
</n-space> </n-space>
<div class="absolute bottom-10 animate-bounce">
<n-icon
size="32"
class="opacity-50"
:component="ArrowDownwardOutlined"
/>
</div>
</div> </div>
<div id="products" class="pb-56">
<client-only> <!-- Products Section -->
<n-grid cols="1 m:2 l:2" responsive="screen" x-gap="32" y-gap="16"> <div id="products" class="scroll-mt-24">
<n-gi> <div class="text-center mb-16">
<div class="flex items-center justify-center"> <h2 class="text-4xl font-bold mb-4">Our Creations</h2>
<n-carousel <p class="text-xl opacity-70 max-w-2xl mx-auto">
show-arrow From social networks to cloud drives, we build tools that empower and
autoplay connect.
dot-type="line" </p>
class="rounded-xl w-full max-h-[360px] aspect-video flex-shrink-1" </div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<n-card
v-for="product in products"
:key="product.path"
class="product-card"
content-style="padding: 0"
>
<div class="relative aspect-video overflow-hidden group">
<img
:src="product.background"
class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div
class="absolute inset-0 bg-linear-to-t from-black/80 via-black/20 to-transparent opacity-90"
></div>
<div
class="absolute top-4 right-4 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
>
<n-button
v-if="product.repo"
circle
color="white"
size="small"
tag="a"
:href="product.repo"
target="_blank"
class="text-black"
> >
<n-carousel-item <n-icon><code-round /></n-icon>
v-for="product in products" </n-button>
:key="product.path" <n-button
class="rounded-xl w-full max-h-[360px] aspect-video relative" v-if="product.url"
:style="`background-color: ${product.background ? 'transparent' : themeVar.baseColor}`" circle
> color="white"
<img size="small"
:src="product.background" tag="a"
class="absolute left-0 right-0 top-0 bottom-0 object-cover aspect-video" :href="product.url"
style="z-index: -1" target="_blank"
/> class="text-black"
<div >
style=" <n-icon><launch-round /></n-icon>
background: linear-gradient( </n-button>
to top,
rgba(0, 0, 0, 0.7),
transparent
);
z-index: 1;
"
class="absolute left-0 right-0 top-1/2 bottom-0"
/>
<div
class="absolute left-0 right-0 top-0 px-4 pt-4 flex justify-end gap-2.5"
>
<n-button
v-if="product.repo"
circle
color="white"
size="small"
tag="a"
:href="product.repo"
target="_blank"
>
<n-icon color="black"><code-round /></n-icon>
</n-button>
<n-button
v-if="product.url"
circle
color="white"
size="small"
tag="a"
:href="product.url"
target="_blank"
>
<n-icon color="black"><launch-round /></n-icon>
</n-button>
</div>
<div
class="absolute bottom-0 px-6 py-8 w-full"
style="z-index: 2"
>
<nuxt-img :src="product.icon" class="w-12 h-12" />
<p class="text-lg text-white line-height-1">
{{ product.name }}
</p>
<p class="text-white line-height-1">
{{ product.description }}
</p>
</div>
</n-carousel-item>
</n-carousel>
</div> </div>
</n-gi>
<n-gi> <div class="absolute bottom-0 left-0 right-0 p-6">
<div <div class="flex items-center gap-3 mb-2">
class="flex justify-center text-right h-full py-8 px-4 flex-col" <nuxt-img
> :src="product.icon"
<h2 class="text-3xl font-bold mb-3">Our products</h2> class="w-10 h-10 rounded-lg shadow-sm"
<p class="text-lg mb-1"> />
The made various of software, from social network to cloud <h3 class="text-xl font-bold text-white">
drive. {{ product.name }}
</p> </h3>
<p class="text-lg">
Take a look of them on the left on your own
<code>(&gt;&lt;)</code>
</p>
</div>
</n-gi>
</n-grid>
</client-only>
</div>
<div id="about" class="pb-56">
<client-only>
<n-grid cols="1 m:2 l:2" responsive="screen" x-gap="32" y-gap="16">
<n-gi>
<div
class="flex justify-center text-left h-full py-8 px-4 flex flex-col"
>
<h2 class="text-3xl font-bold mb-3">About us</h2>
<p class="text-lg mb-1">
Our aim is not making a profit.
<i class="text-xs">At least not yet.</i>
</p>
<p class="text-lg">
Instead we hope we can spread the love to the world and make
everyone enjoy the fun of the Internet and the world.
</p>
</div>
</n-gi>
<n-gi>
<div class="flex h-full justify-center flex-col text-right">
<h2 class="text-3xl font-bold mb-3">Team members</h2>
<p class="text-lg">
Say hi to our lovely members... uh, seems there is only me.
</p>
<div class="flex justify-end gap-4 my-4">
<div class="flex flex-col items-end text-right">
<n-avatar
src="https://fs.solian.app/api/files/200ee92546244ed1a6a02202f5ca9cc9"
:size="100"
class="mb-2"
/>
<div class="flex gap-1">
<p>LittleSheep</p>
<n-button
text
tag="a"
size="small"
href="https://id.solian.app/@littlesheep"
target="_blank"
class="mt-0.5"
>
<n-icon>
<info-outlined />
</n-icon>
</n-button>
</div>
<p class="text-xs opacity-75 max-w-sm">
Founder, CEO, CTO, Senior Developer, Marketing Engineer,
Customer Service Engineer, DevOps, Database Administrator,
Product Manager, UI/UX Designer, QA Engineer, Mobile
Developer, Security Engineer, Technical Writer, Project
Manager, Community Manager, Software Architect
</p>
</div>
</div> </div>
<p class="text-gray-200 text-sm line-clamp-2">
{{ product.description }}
</p>
</div> </div>
</n-gi> </div>
</n-grid> </n-card>
</client-only> </div>
</div>
<!-- About Teaser Section -->
<div class="py-16">
<n-card
class="bg-linear-to-r from-primary/10 to-secondary/10 border-0 overflow-hidden relative"
>
<div
class="absolute top-0 right-0 w-64 h-64 bg-primary/20 rounded-full blur-[80px] -translate-y-1/2 translate-x-1/2"
></div>
<div
class="flex flex-col md:flex-row items-center gap-12 relative z-10 p-8"
>
<div class="flex-1">
<h2 class="text-3xl font-bold mb-4">More Than Just Code</h2>
<p class="text-lg opacity-80 mb-6 leading-relaxed">
We are a community-driven team focused on creating meaningful
experiences. Our mission goes beyond softwareit's about
connection, innovation, and fun.
</p>
<n-button type="primary" ghost size="large" tag="a" href="/about">
Read Our Story
<template #icon>
<n-icon :component="ChevronRightOutlined"></n-icon>
</template>
</n-button>
</div>
<div class="flex-1 flex justify-center">
<!-- Abstract representation or team collage could go here -->
<div class="grid grid-cols-2 gap-4 opacity-80">
<div
class="w-32 h-32 rounded-2xl bg-primary/20 animate-pulse"
></div>
<div
class="w-32 h-32 rounded-2xl bg-secondary/20 animate-pulse delay-75"
></div>
<div
class="w-32 h-32 rounded-2xl bg-info/20 animate-pulse delay-150"
></div>
<div
class="w-32 h-32 rounded-2xl bg-success/20 animate-pulse delay-300"
></div>
</div>
</div>
</div>
</n-card>
</div> </div>
</main> </main>
</template> </template>
<script setup> <script setup>
import { NSpace, NButton, NIcon, NCard } from "naive-ui";
import { import {
NSpace, LaunchRound,
NButton, CodeRound,
NGrid, ChevronRightOutlined,
NGi, ArrowDownwardOutlined,
NCarousel, } from "@vicons/material";
NCarouselItem,
NIcon,
NAvatar,
useThemeVars,
} from "naive-ui";
import { LaunchRound, CodeRound, InfoOutlined } from "@vicons/material";
import Typed from "typed.js"; import Typed from "typed.js";
const route = useRoute(); const route = useRoute();
const themeVar = useThemeVars(); const typedElement = ref(null);
const typed = shallowRef(null);
useHead({
title: "Solsynth - Creating Experiences",
});
onMounted(() => {
if (typedElement.value) {
if (typed.value) {
typed.value.destroy();
}
typed.value = new Typed(typedElement.value, {
strings: [
"make software",
"make hardware",
"craft experiences",
"write stories",
"are Solsynth",
],
typeSpeed: 50,
backDelay: 1500,
backSpeed: 30,
smartBackspace: true,
loop: true,
showCursor: true,
cursorChar: "|",
autoInsertCss: true,
});
}
});
onUnmounted(() => {
if (typed.value) {
typed.value.destroy();
typed.value = null;
}
});
const { data: products } = await useAsyncData(route.path, () => { const { data: products } = await useAsyncData(route.path, () => {
return queryCollection("products").all(); return queryCollection("products").all();
}); });
useHead({
title: "Solsynth",
});
onMounted(() => {
new Typed("#who-are-we", {
strings: [
"make software",
"make hardware",
"make experience",
"write stories",
"are Solsynth^3000",
],
typeSpeed: 40,
backDelay: 1000,
backSpeed: 40,
smartBackspace: true,
loop: true,
showCursor: false,
autoInsertCss: false,
});
});
</script> </script>
<style scoped>
.animate-float {
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0% {
transform: translateY(0px);
}
50% {
transform: translateY(-20px);
}
100% {
transform: translateY(0px);
}
}
.product-card {
height: 100%;
transform: translateY(0);
transition: all 0.3s ease-in-out;
overflow: hidden;
border: none;
background-color: rgba(
255,
255,
255,
0.5
); /* Example, adjust base color as needed */
backdrop-filter: blur(4px); /* For backdrop-blur-sm */
&:hover {
transform: translateY(-0.5rem); /* For -translate-y-2 */
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 10px 10px -5px rgba(0, 0, 0, 0.04); /* For shadow-xl */
}
}
</style>

View File

@@ -0,0 +1,54 @@
<template>
<div class="container max-w-xl mx-auto my-16 px-8">
<h1 class="text-4xl font-bold mb-8">Our Products</h1>
<nuxt-link
v-for="product in products"
:key="product.url"
:to="product.url"
class="no-underline block mb-4"
>
<n-card hoverable>
<template #cover>
<div class="h-48 overflow-hidden relative">
<img :src="product.background" class="w-full h-full object-cover" />
<div
class="absolute bottom-0 left-0 p-4 bg-linear-to-t from-black/80 to-transparent w-full flex items-center gap-3"
>
<img
:src="product.icon"
class="aspect-square h-10 rounded-full bg-white/10 p-1"
style="width: auto"
/>
<span class="text-white font-bold text-lg grow">{{
product.name
}}</span>
</div>
</div>
</template>
<p class="line-clamp-3 pt-4">{{ product.description }}</p>
<template #footer>
<div class="flex justify-between items-center text-xs opacity-75">
<span>v{{ product.version }}</span>
<span>
Released:
{{ new Date(product.releasedDate ?? "").toLocaleDateString() }}
</span>
</div>
</template>
</n-card>
</nuxt-link>
</div>
</template>
<script setup lang="ts">
import { NCard } from "naive-ui";
const { data: products } = await useAsyncData("products", () =>
queryCollection("products").all()
);
useHead({
title: "Products",
});
</script>

View File

@@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

1754
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ export default defineContentConfig({
releasedDate: z.date().optional(), releasedDate: z.date().optional(),
version: z.string().optional(), version: z.string().optional(),
updatedDate: z.date().optional(), updatedDate: z.date().optional(),
hasPage: z.boolean().optional(),
}), }),
}), }),
terms: defineCollection({ terms: defineCollection({

View File

@@ -7,5 +7,6 @@
"repo": "https://github.com/littlesheep2code/groovy-box", "repo": "https://github.com/littlesheep2code/groovy-box",
"releasedDate": "2025-08-14T08:00:00.000Z", "releasedDate": "2025-08-14T08:00:00.000Z",
"version": "1.0.0.0", "version": "1.0.0.0",
"updatedDate": "2025-08-14T08:00:00.000Z" "updatedDate": "2025-08-14T08:00:00.000Z",
"hasPage": true
} }

View File

@@ -7,5 +7,6 @@
"repo": "https://github.com/Solsynth/Solian", "repo": "https://github.com/Solsynth/Solian",
"releasedDate": "2024-01-27T08:00:00.000Z", "releasedDate": "2024-01-27T08:00:00.000Z",
"version": "3.1.0", "version": "3.1.0",
"updatedDate": "2025-07-28T08:00:00.000Z" "updatedDate": "2025-07-28T08:00:00.000Z",
"hasPage": true
} }

View File

@@ -1,52 +1,86 @@
import tailwindcss from "@tailwindcss/vite";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { NaiveUiResolver } from "unplugin-vue-components/resolvers";
import { generateTailwindColorThemes } from "@bg-dev/nuxt-naiveui/utils";
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: "2025-07-15", compatibilityDate: "2025-07-15",
devtools: { enabled: true }, devtools: { enabled: true },
ssr: false,
css: ["~/assets/css/main.css"],
app: { app: {
pageTransition: { name: "page", mode: "out-in" },
head: { head: {
title: "Solsynth", title: "Solsynth",
titleTemplate: "%s - Solsynth", titleTemplate: "%s - Solsynth",
link: [{ rel: "icon", type: "image/png", href: "/favicon.png" }], link: [{ rel: "icon", type: "image/png", href: "/favicon.png" }],
}, },
}, },
scripts: { nitro: {},
globals: {
umami: {
src: "https://cloud.umami.is/script.js",
"data-website-id": "eef151fb-07e2-461b-8b7f-2547aab735d4",
defer: true,
},
},
},
nitro: {
preset: "cloudflare_module",
cloudflare: {
deployConfig: true,
wrangler: {
d1_databases: [
{
binding: "DB",
database_name: "capital-content",
database_id: "73d65123-3c42-4dc9-b540-8e89678962a2",
},
],
},
},
},
content: {
database: {
type: "d1",
bindingName: "DB",
},
},
modules: [ modules: [
"@nuxt/content", "@nuxt/content",
"@nuxt/eslint", "@nuxt/eslint",
"@nuxt/image", "@nuxt/image",
"@nuxt/scripts", "@nuxt/scripts",
"@nuxtjs/tailwindcss",
"nuxtjs-naive-ui",
"@vueuse/nuxt", "@vueuse/nuxt",
"@eschricht/nuxt-color-mode", "@bg-dev/nuxt-naiveui",
], ],
vite: {
plugins: [
tailwindcss(),
AutoImport({
imports: [
{
"naive-ui": [
"useDialog",
"useMessage",
"useNotification",
"useLoadingBar",
],
},
],
}),
Components({
resolvers: [NaiveUiResolver()],
}),
],
},
naiveui: {
colorModePreference: "system",
colorModePreferenceCookieName: "fi-ColorMode",
themeConfig: {
...generateTailwindColorThemes(),
shared: {
common: {
fontFamily:
"Nunito Variable, v-sans, ui-system, -apple-system, sans-serif",
primaryColor: "#3F51B5FF",
primaryColorHover: "#5767C1FF",
primaryColorPressed: "#3546A4FF",
primaryColorSuppl: "#4C5EC5FF",
borderRadius: "16px",
borderRadiusSmall: "8px",
},
Input: {
borderRadius: "8px",
},
Select: {
borderRadius: "8px",
},
Dropdown: {
borderRadius: "8px",
},
Button: {
borderRadius: "8px",
borderRadiusLarge: "12px",
borderRadiusMedium: "8px",
borderRadiusSmall: "4px",
},
},
light: {},
dark: {},
},
},
}); });

View File

@@ -10,28 +10,31 @@
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"@eschricht/nuxt-color-mode": "1.2.0", "@bg-dev/nuxt-naiveui": "^2.0.0",
"@fontsource-variable/nunito": "^5.2.6", "@fontsource-variable/nunito": "^5.2.7",
"@nuxt/content": "3.6.3", "@nuxt/content": "3.6.3",
"@nuxt/eslint": "1.7.1", "@nuxt/eslint": "1.7.1",
"@nuxt/image": "1.10.0", "@nuxt/image": "1.10.0",
"@nuxt/scripts": "0.11.10", "@nuxt/scripts": "0.11.10",
"@nuxtjs/tailwindcss": "6.14.0", "@octokit/rest": "^22.0.1",
"@octokit/rest": "^22.0.0", "@unhead/vue": "^2.0.19",
"@unhead/vue": "^2.0.3", "@vueuse/core": "^13.9.0",
"@vueuse/core": "^13.6.0",
"@vueuse/nuxt": "13.6.0", "@vueuse/nuxt": "13.6.0",
"better-sqlite3": "^12.2.0", "better-sqlite3": "^12.5.0",
"eslint": "^9.0.0", "eslint": "^9.39.1",
"nuxt": "^4.0.1", "nuxt": "^4.2.1",
"nuxtjs-naive-ui": "1.0.2", "nuxtjs-naive-ui": "1.0.2",
"typed.js": "^2.1.0", "typed.js": "^2.1.0",
"vue": "^3.5.18", "vue": "^3.5.25",
"vue-router": "^4.5.1", "vue-router": "^4.6.3",
"wrangler": "^4.26.1" "wrangler": "^4.51.0"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.19",
"@vicons/material": "^0.13.0" "@vicons/material": "^0.13.0",
"daisyui": "^5.5.5",
"naive-ui": "^2.43.2",
"unplugin-auto-import": "^20.3.0",
"unplugin-vue-components": "^30.0.0"
} }
} }