This commit is contained in:
AaronHux 2024-05-25 15:20:10 +08:00
parent e592459af2
commit 96a117b084
54 changed files with 13904 additions and 0 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# editorconfig.org
root = true
[*]
indent_size = 2
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

4
.env.example Normal file
View File

@ -0,0 +1,4 @@
# Production license for @nuxt/ui-pro, get one at https://ui.nuxt.com/pro/purchase
NUXT_UI_PRO_LICENSE=
# Public URL, used for OG Image when running nuxt generate
NUXT_PUBLIC_SITE_URL=

4
.eslintignore Normal file
View File

@ -0,0 +1,4 @@
dist
node_modules
.output
.nuxt

16
.eslintrc.cjs Normal file
View File

@ -0,0 +1,16 @@
module.exports = {
root: true,
extends: [
'@nuxt/eslint-config'
],
rules: {
// Global
semi: ['error', 'never'],
quotes: ['error', 'single'],
'quote-props': ['error', 'as-needed'],
// Vue
'vue/multi-word-component-names': 0,
'vue/max-attributes-per-line': 'off',
'vue/no-v-html': 0
}
}

27
.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example
# VSC
.history

1
.npmrc Normal file
View File

@ -0,0 +1 @@
shamefully-hoist=true

34
app.config.ts Normal file
View File

@ -0,0 +1,34 @@
export default defineAppConfig({
ui: {
primary: 'amber',
gray: 'stone',
button: {
rounded: 'rounded-full',
default: {
size: 'md'
}
},
input: {
default: {
size: 'md'
}
},
card: {
rounded: 'rounded-xl'
},
footer: {
top: {
wrapper: 'border-t border-gray-200 dark:border-gray-800',
container: 'py-8 lg:py-16'
},
bottom: {
wrapper: 'border-t border-gray-200 dark:border-gray-800'
}
},
page: {
hero: {
wrapper: 'lg:py-24'
}
}
}
})

38
app.vue Normal file
View File

@ -0,0 +1,38 @@
<script setup lang="ts">
const colorMode = useColorMode()
const color = computed(() => colorMode.value === 'dark' ? '#111827' : 'white')
useHead({
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ key: 'theme-color', name: 'theme-color', content: color }
],
link: [
{ rel: 'icon', href: '/favicon.ico' }
],
htmlAttrs: {
lang: 'en'
}
})
useSeoMeta({
titleTemplate: '%s - Nuxt UI Pro - SaaS template',
ogImage: 'https://saas-template.nuxt.dev/social-card.png',
twitterImage: 'https://saas-template.nuxt.dev/social-card.png',
twitterCard: 'summary_large_image'
})
</script>
<template>
<div>
<NuxtLoadingIndicator />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<UNotifications />
</div>
</template>

86
components/Footer.vue Normal file
View File

@ -0,0 +1,86 @@
<script setup lang="ts">
const links = [{
label: 'Resources',
children: [{
label: 'Help center'
}, {
label: 'Docs'
}, {
label: 'Roadmap'
}, {
label: 'Changelog'
}]
}, {
label: 'Features',
children: [{
label: 'Affiliates'
}, {
label: 'Portal'
}, {
label: 'Jobs'
}, {
label: 'Sponsors'
}]
}, {
label: 'Company',
children: [{
label: 'About'
}, {
label: 'Pricing'
}, {
label: 'Careers'
}, {
label: 'Blog'
}]
}]
const toast = useToast()
const email = ref('')
const loading = ref(false)
function onSubmit () {
loading.value = true
setTimeout(() => {
toast.add({
title: 'Subscribed!',
description: 'You\'ve been subscribed to our newsletter.'
})
loading.value = false
}, 1000)
}
</script>
<template>
<UFooter>
<template #top>
<UFooterColumns :links="links">
<template #right>
<form @submit.prevent="onSubmit">
<UFormGroup label="Subscribe to our newsletter" :ui="{ container: 'mt-3' }">
<UInput v-model="email" type="email" placeholder="Enter your email" :ui="{ icon: { trailing: { pointer: '' } } }" required size="xl" autocomplete="off" class="max-w-sm" input-class="rounded-full">
<template #trailing>
<UButton type="submit" size="xs" color="primary" :label="loading ? 'Subscribing' : 'Subscribe'" :loading="loading" />
</template>
</UInput>
</UFormGroup>
</form>
</template>
</UFooterColumns>
</template>
<template #left>
<p class="text-gray-500 dark:text-gray-400 text-sm">
Copyright © {{ new Date().getFullYear() }}. All rights reserved.
</p>
</template>
<template #right>
<UColorModeButton size="sm" />
<UButton to="https://github.com/nuxt-ui-pro/saas" target="_blank" icon="i-simple-icons-github" aria-label="GitHub" color="gray" variant="ghost" />
</template>
</UFooter>
</template>

41
components/Header.vue Normal file
View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import type { NavItem } from '@nuxt/content/dist/runtime/types'
const navigation = inject<Ref<NavItem[]>>('navigation', ref([]))
const { data: page, pending } = await useAsyncData('header', () => queryContent('/header').findOne())
if (!page.value) {
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
}
effect(() => {
console.log(page.value?.body)
})
const links: any = computed(() => page.value?.body ?? [])
</script>
<template>
<div class=" sticky top-[20px] pt-10 z-50 flex items-center justify-between container mx-auto">
<BrandLogo />
<USkeleton v-if="pending" class="h-10 w-[250px] bg-gray-900" />
<div v-else class="w-fit px-10 py-3 rounded-full bg-gray-900/90 z-50 shadow-lg">
<UHeaderLinks :links="links" />
</div>
</div>
<!-- <UHeader :links="links" class="mt-5 ">
<template #logo>
<UBadge label="SaaS" variant="subtle" class="mb-0.5" />
</template>
<template #right>
<UButton label="Sign in" color="gray" to="/login" />
<UButton label="Sign up" icon="i-heroicons-arrow-right-20-solid" trailing color="black" to="/signup" class="hidden lg:flex" />
</template>
<template #panel>
<UNavigationTree :links="mapContentNavigation(navigation)" default-open />
</template>
</UHeader> -->
</template>

View File

@ -0,0 +1,29 @@
<script lang="ts" setup>
defineOptions({
inheritAttrs: false
})
defineProps({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
}
})
</script>
<template>
<div class="w-full h-full flex flex-col justify-center text-center bg-slate-900 p-8">
<div class="relative">
<h1 class="text-8xl mb-4 text-white">
{{ title }}
</h1>
<p class="text-5xl text-gray-200 leading-tight">
{{ description }}
</p>
</div>
</div>
</template>

View File

@ -0,0 +1,21 @@
<template>
<div class="bg-gray-900/5 dark:bg-white/5 ring-1 ring-inset ring-gray-900/10 dark:ring-white/10 rounded-xl lg:-m-4 p-4">
<div class="aspect-w-16 aspect-h-9 rounded-lg relative overflow-hidden border border-dashed border-gray-900/10 dark:border-white/10">
<svg class="absolute inset-0 h-full w-full stroke-gray-900/10 dark:stroke-white/10" fill="none">
<defs>
<pattern
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
x="0"
y="0"
width="10"
height="10"
patternUnits="userSpaceOnUse"
>
<path d="M-3 13 15-5M-5 5l18-18M-1 21 17 3" />
</pattern>
</defs>
<rect stroke="none" fill="url(#pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e)" width="100%" height="100%" />
</svg>
</div>
</div>
</template>

44
components/brand/logo.vue Normal file
View File

@ -0,0 +1,44 @@
<script setup>
const color = useColorMode()
const textColor = computed(() => color.value === 'dark' ? 'white' : 'black')
</script>
<template>
<div :text="textColor" class="py-10">
<svg xmlns="http://www.w3.org/2000/svg" height="25" viewBox="0 0 749.65 91.19">
<path fill="currentColor" d="M80.84,7.07V22.23H48V72.75H32.84V22.23H0V7.07Z" />
<path
fill="currentColor"
d="M106.56,7.07H147q20.21,0,20.21,20.21V52.54q0,20.21-20.21,20.21H106.56q-20.22,0-20.21-20.21V27.28Q86.35,7.07,106.56,7.07ZM147,22.23H106.56q-5.06,0-5.06,5.05V52.54q0,5.06,5.06,5.05H147c3.36,0,5.05-1.68,5.05-5.05V27.28C152,23.91,150.34,22.23,147,22.23Z"
/>
<path
fill="currentColor"
d="M194.12,22.23V72.75H179V7.07h60.63q20.21,0,20.21,20.21T239.59,47.49H204.22V32.33h35.37q5.06,0,5-5t-5-5.05Z"
/>
<path
fill="currentColor" d="M261.11,7.07h16.68l26.77,51.84L331.34,7.07H348L304.56,91.19Z"
/>
<path
fill="currentColor"
d="M369.64,57.59h60.63V72.75H369.64q-20.2,0-20.21-20.21V27.28q0-20.21,20.21-20.21h60.63V22.23H369.64q-5.06,0-5.05,5.05V52.54Q364.59,57.6,369.64,57.59Zm40.42-25.26V47.49H374.69V32.33Z"
/>
<path
fill="currentColor"
d="M457.25,22.23V72.75H442.09V7.07h60.63q20.22,0,20.21,20.21T502.72,47.49h-3.84l25.26,25.26H502.72L462.3,32.33h40.42q5.05,0,5-5t-5-5.05Z"
/>
<path
fill="currentColor"
d="M592.2,72.75H531.57V57.59H592.2q5.06,0,5-5.05t-5-5H551.78q-20.21,0-20.21-20.21T551.78,7.07h60.63V22.23H551.78q-5.06,0-5,5.05t5,5H592.2q20.2,0,20.21,20.21T592.2,72.75Z"
/>
<path
fill="currentColor"
d="M642.73,57.59h60.63V72.75H642.73q-20.22,0-20.21-20.21V27.28q0-20.21,20.21-20.21h60.63V22.23H642.73q-5.06,0-5.06,5.05V52.54Q637.67,57.6,642.73,57.59Zm40.42-25.26V47.49H647.78V32.33Z"
/>
<path
fill="currentColor"
d="M730.5,38.29A18.44,18.44,0,0,1,717,32.68a18.45,18.45,0,0,1-5.62-13.53A18.45,18.45,0,0,1,717,5.61a19.13,19.13,0,0,1,27.07,0,18.45,18.45,0,0,1,5.61,13.54A18.44,18.44,0,0,1,744,32.68,18.48,18.48,0,0,1,730.5,38.29ZM729,8.84q9.72,0,9.72,7.36c0,3.06-1.26,5.18-3.77,6.33l4.83,6,.06-.06a12.76,12.76,0,0,0,3.89-9.36,12.77,12.77,0,0,0-3.89-9.37,13.23,13.23,0,0,0-18.74,0,12.76,12.76,0,0,0-3.88,9.37,12.75,12.75,0,0,0,3.88,9.36,13.26,13.26,0,0,0,17.71.95H734l-4.91-5.9H729v5.9h-5.89V8.84Zm0,5.89v2.94c2.55,0,3.83-.49,3.83-1.47S731.58,14.73,729,14.73Z"
/>
</svg>
</div>
</template>

162
components/galaxy.tsx Normal file

File diff suppressed because one or more lines are too long

145
content/0.index.yml Normal file
View File

@ -0,0 +1,145 @@
title: Nuxt UI Pro - SaaS template
description: Nuxt UI Pro is a collection of premium Vue components built on top of Nuxt UI to create beautiful & responsive Nuxt applications in minutes.
navigation: false
hero:
title: Topverse 下一代元宇宙
description: 世界领先 Web 3.0 领域应用及多现实混合应用技术研发
# headline:
# label: Made with Nuxt UI Pro
# to: https://ui.nuxt.com/pro
# icon: i-heroicons-arrow-top-right-on-square-20-solid
# links:
# - label: Get started
# to: '/signup'
# size: xl
# color: black
# icon: i-heroicons-arrow-right-20-solid
# trailing: true
# - label: Use this template
# icon: i-simple-icons-github
# size: xl
# color: gray
# to: https://github.com/nuxt-ui-pro/saas
# target: _blank
sections:
- title: The power of our service
description: Aliqua labore laboris fugiat. Reprehenderit exercitation eu commodo. Officia nostrud sit et aliqua ea ex sunt minim incididunt sunt.
id: features
align: left
features:
- name: Easy to use
description: Id laborum laboris duis nostrud excepteur ut velit.
icon: i-heroicons-cog
- name: Reliable
description: Magna Lorem ex cillum fugiat ad enim aute irure sit duis minim.
icon: i-heroicons-check
- name: Secure
description: Proident nostrud excepteur sint ut culpa consectetur aute adipisicing.
icon: i-heroicons-lock-closed
links:
- label: Explore components
to: /pro/components
color: gray
icon: i-heroicons-arrow-right-20-solid
trailing: true
size: md
class: ml-8
- title: The speed of our service
description: Cillum sint enim excepteur ut deserunt qui nisi in deserunt in. Deserunt aliquip quis aliquip eu quis ex velit velit nostrud sit.
align: right
features:
- name: Fast
description: Qui reprehenderit nostrud dolore nisi ad fugiat labore eiusmod.
icon: i-heroicons-rocket-launch
- name: Affordable
description: Reprehenderit fugiat elit in do ipsum ut pariatur.
icon: i-heroicons-currency-dollar
- name: Scalable
description: Lorem deserunt et eiusmod. Ea in consectetur minim officia.
icon: i-heroicons-chart-bar
links:
- label: Learn more
to: /pro/guide/content
color: gray
icon: i-heroicons-arrow-right-20-solid
trailing: true
size: md
class: ml-8
features:
title: Why choose our service?
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
items:
- title: Easy to use
description: Id laborum laboris duis nostrud excepteur ut velit nulla magna Lorem proident non.
icon: i-heroicons-cog
- title: Reliable
description: Magna Lorem ex cillum fugiat ad enim aute irure sit duis minim.
icon: i-heroicons-check
- title: Secure
description: Proident nostrud excepteur sint ut culpa consectetur aute adipisicing non anim ullamco.
icon: i-heroicons-lock-closed
- title: Fast
description: Qui reprehenderit nostrud dolore nisi ad fugiat labore eiusmod sint aliquip nisi voluptate.
icon: i-heroicons-rocket-launch
- title: Affordable
description: Reprehenderit fugiat elit in do ipsum ut pariatur.
icon: i-heroicons-currency-dollar
- title: Scalable
description: Lorem deserunt et eiusmod. Ea in consectetur minim officia ullamco enim deserunt est.
icon: i-heroicons-chart-bar
testimonials:
headline: Testimonials
title: What our customers are saying.
description: Proident sunt exercitation minim laborum enim laboris labore esse.
items:
- quote: Nostrud tempor sunt fugiat. Dolor in sint dolore labore non occaecat adipisicing Lorem labore ullamco enim excepteur. In fugiat Lorem sit velit id veniam esse eiusmod non ea voluptate cupidatat reprehenderit ullamco dolore. Mollit laborum occaecat aliquip.
author:
name: Rose Roberson
description: CEO at Company
avatar:
src: https://i.pravatar.cc/120?img=1
loading: lazy
- quote: Eiusmod dolor aute ut nulla pariatur officia consequat aute amet exercitation.
author:
name: Chace Rodgers
description: CEO at Company
avatar:
src: https://i.pravatar.cc/120?img=7
loading: lazy
- quote: Id duis velit enim officia ad nisi incididunt magna ex dolor minim deserunt dolor. Esse incididunt cillum nostrud esse do quis amet labore amet nulla eiusmod amet nulla Lorem. Incididunt ex voluptate irure officia laboris ea proident est qui.
author:
name: Cornelius Sheppard
description: CEO at Company
avatar:
src: https://i.pravatar.cc/120?img=3
loading: lazy
- quote: Velit consectetur in adipisicing culpa eiusmod commodo eu ex dolore. Officia irure nisi dolor dolore velit fugiat. Aliqua sint aliqua aute elit eu sunt.
author:
name: Destinee Woods
description: CEO at Company
avatar:
src: https://i.pravatar.cc/120?img=5
loading: lazy
- quote: Proident quis deserunt qui ex exercitation veniam id Lorem est cupidatat ipsum irure aliquip ad.
author:
name: Kaleb Mccormick
description: CEO at Company
avatar:
src: https://i.pravatar.cc/120?img=8
loading: lazy
- quote: Magna officia quis ea ea in officia non voluptate ipsum culpa do labore sunt. Aute est dolore commodo sint officia ad laboris dolor magna aliquip exercitation tempor commodo.
author:
name: Jazmin Mccall
description: CEO at Company
avatar:
src: https://i.pravatar.cc/120?img=9
loading: lazy
cta:
title: Get started today
description: Nisi mollit id aliquip sunt est laborum sit sit.
links:
- label: Buy now
size: xl
color: black
to: /pricing

View File

@ -0,0 +1,44 @@
---
title: Introduction
description: Welcome to Nuxt UI Pro SaaS template.
---
This template is a ready-to-use SaaS template made with [Nuxt UI Pro](https://ui.nuxt.com/pro), a collection of premium components built on top of [Nuxt UI](https://ui.nuxt.com) to create beautiful & responsive Nuxt applications in minutes.
This template includes a customizable landing page, a pricing page, a documentation, a blog and authentication pages (login, register).
## Features
- Powered by [Nuxt 3](https://nuxt.com)
- Built with [Nuxt UI](https://ui.nuxt.com) and [Nuxt UI Pro](https://ui.nuxt.com/pro)
- Write content with [MDC syntax](https://content.nuxt.com/usage/markdown) thanks to [Nuxt Content](https://content.nuxt.com)
- Beautiful Typography styles
- Full-Text Search out of the box
- Dark mode support
- And more...
## Play online
You can start playing with this template in your browser using our online sandboxes:
::u-button
---
class: mr-4
icon: i-simple-icons-stackblitz
label: Play on StackBlitz
target: _blank
to: https://stackblitz.com/github/nuxt-ui-pro/saas/
---
::
::u-button
---
class: mt-2 sm:mt-0
icon: i-simple-icons-codesandbox
label: Play on CodeSandbox
target: _blank
to: https://codesandbox.io/s/github/nuxt-ui-pro/saas/
---
::
Or open [Nuxt UI playground](https://ui.nuxt.com/playground){target=_blank}.

View File

@ -0,0 +1,21 @@
---
title: Installation
description: Get started with Nuxt UI Pro SaaS template.
---
## Quick Start
You can start a fresh new project with:
```bash [Terminal]
npx nuxi init -t github:nuxt-ui-pro/saas
```
or create a new repository from GitHub:
1. Open <https://github.com/nuxt-ui-pro/saas>
2. Click on `Use this template` button
3. Enter repository name and click on `Create repository from template` button
4. Clone your new repository
5. Install dependencies with your favorite package manager
6. Start development server

View File

@ -0,0 +1 @@
title: Getting Started

1
content/1.docs/_dir.yml Normal file
View File

@ -0,0 +1 @@
navigation.icon: i-heroicons-book-open

85
content/2.pricing.yml Normal file
View File

@ -0,0 +1,85 @@
title: Pricing
description: Choose the plan that's right for you.
navigation.icon: i-heroicons-credit-card
hero:
title: A plan for every need
description: Our plans are designed to meet the requirements of both beginners and players. Get the right plan that suits you.
align: center
plans:
- title: Basic
description: A basic plan for individuals.
price:
month: $9.9
year: $99.9
align: top
button:
label: Get Started
color: white
features:
- 1 GB Storage
- 1 Email Account
- 1 Domain
- 1 Website
- 1 Database
- 1 SSL Certificate
- 1 Support Ticket
- title: Standard
description: A standard plan for small teams.
price:
month: $19.9
year: $199.9
highlight: true
align: top
button:
label: Get Started
color: black
features:
- 10 GB Storage
- 10 Email Accounts
- 10 Domains
- 10 Websites
- 10 Databases
- 10 SSL Certificates
- 10 Support Tickets
- title: Premium
description: A premium plan for large teams.
price:
month: $29.9
year: $299.9
align: top
button:
label: Get Started
color: white
features:
- 100 GB Storage
- 100 Email Accounts
- 100 Domains
- 100 Websites
- 100 Databases
- 100 SSL Certificates
- 100 Support Tickets
logos:
title: Trusted by the world's best
icons:
- i-simple-icons-amazonaws
- i-simple-icons-heroku
- i-simple-icons-netlify
- i-simple-icons-vercel
- i-simple-icons-cloudflare
faq:
title: Frequently asked questions
description: Culpa consectetur dolor pariatur commodo aliqua amet tempor nisi enim deserunt elit cillum.
items:
- label: Is this a secure service?
content: Qui sunt nostrud aliquip reprehenderit enim proident veniam magna aliquip velit occaecat eiusmod nisi deserunt sunt.
defaultOpen: true
- label: How can I cancel my subscription?
content: Consectetur irure Lorem nostrud adipisicing aliqua mollit Lorem sit officia magna eiusmod cupidatat.
- label: How does the free trial work?
content: Quis officia commodo magna eu qui aliquip duis.
- label: Can I switch plans later?
content: Dolore irure ullamco dolore eu occaecat magna eiusmod dolor aliqua culpa labore.
- label: Do you offer refunds?
content: Duis mollit nostrud voluptate mollit Lorem dolore commodo veniam incididunt eiusmod.
- label: Do you offer support?
content: Aliqua sit nisi consequat pariatur Lorem minim irure qui.

3
content/3.blog.yml Normal file
View File

@ -0,0 +1,3 @@
title: Blog
description: Those are examples posts taken from Nuxt.com, they may be outdated and not render properly.
navigation.icon: i-heroicons-newspaper

View File

@ -0,0 +1,288 @@
---
title: 'Exploring the Culinary Wonders of Asia'
description: "Embark on a tantalizing expedition through the diverse and enchanting flavors of Asia "
image:
src: https://picsum.photos/id/490/640/360
authors:
- name: Alexia wong
to: https://twitter.com/benjamincanac
avatar:
src: https://i.pravatar.cc/128?u=0
date: 2023-08-25
badge:
label: Cooking
---
## Introduction to the Enchanting World of Asian Cuisine
Dive into the rich tapestry of Asian cuisine, a journey through centuries-old traditions and a symphony of flavors that tantalize the taste buds. From the bustling street markets of Bangkok to the serene tea houses of Kyoto, each corner of Asia offers a culinary adventure waiting to be explored. In this chapter, we embark on a quest to understand what makes Asian cuisine truly extraordinary.
Asia's culinary landscape is as diverse as its cultures. Chinese, Japanese, Thai, Indian, Vietnamese, Malaysian each region boasts its own unique culinary identity. The use of fresh, locally sourced ingredients, a delicate balance of spices, and a reverence for tradition are the common threads that bind these diverse cuisines.
::div{ .flex .space-x-4 .items-center .justify-between }
::div
![pepper](https://source.unsplash.com/random/400x400/?shushan+pepper){ width="400" height="400" .rounded-lg }
::
::div
![miso](https://source.unsplash.com/random/400x400/?miso+soup){ width="400" height="400" .rounded-lg }
::
::div
![lemongrass](https://source.unsplash.com/random/400x400/?lemongrass){ width="400" height="400" .rounded-lg }
::
::
## Unraveling the Secrets of Asian Flavors and Techniques
The heart of Asian cuisine lies in its distinct flavors and time-honored cooking techniques. Take a journey through the spice-laden streets of Sichuan in China, where the fiery heat of the wok creates mouthwatering masterpieces. Learn the art of sushi making in Japan, where precision and presentation are paramount. Delve into the aromatic world of Thai curries, where the interplay of sweet, sour, salty, and spicy creates a dance of flavors on the palate.
Asian kitchens are a treasure trove of ingredients, each with its own story. Soy sauce, miso, coconut milk, lemongrass, and an array of exotic spices elevate dishes to new heights. Discover the magic of umami, the elusive fifth taste that defines many Asian dishes, leaving a lingering and savory sensation.
::div{ .flex .space-x-4 .items-center .justify-between }
::div
![curry](https://source.unsplash.com/random/400x400/?curry){ width="400" height="400" .rounded-lg }
::
::div
![safran](https://source.unsplash.com/random/400x400/?safran){ width="400" height="400" .rounded-lg }
::
::div
![Garlic](https://source.unsplash.com/random/400x400/?Garlic){ width="400" height="400" .rounded-lg }
::
::div
![coriander](https://source.unsplash.com/random/400x400/?coriander){ width="400" height="400" .rounded-lg }
::
::div
![wasabi](https://source.unsplash.com/random/400x400/?wasabi){ width="400" height="400" .rounded-lg }
::
::div
![Cumin](https://source.unsplash.com/random/400x400/?Cumin){ width="400" height="400" .rounded-lg }
::
::
## Some Cooking Recipe
### Salmon Avocado Sushi Rolls (Japan 🇯🇵)
::div
![sushi](https://source.unsplash.com/random/1200x400/?sushi-rolls){ width="1200" height="400" .rounded-lg }
::
::tabs
::div
---
label: 'Ingredients'
icon: 'i-heroicons-fire'
---
- 2 cups sushi rice, cooked and seasoned with<br/>rice vinegar, sugar, and salt
- 10 sheets nori (seaweed)
- 1/2 lb fresh sushi-grade salmon, thinly sliced
- 1 ripe avocado, sliced
- 1 cucumber, julienned
- Soy sauce, pickled ginger, and wasabi for serving
::
::div
---
label: 'Instructions'
icon: 'i-heroicons-list-bullet-20-solid'
---
- **Prepare Sushi Rice:**
Cook sushi rice according to package instructions. Once cooked, season with a mixture of rice vinegar, sugar, and salt. Allow it to cool.
- **Prepare Ingredients:**
Slice the salmon into thin strips.
Slice the avocado and julienne the cucumber.
- **Assemble Sushi Roll:**
Place a sheet of nori on the bamboo sushi rolling mat, shiny side down.
Wet your hands to prevent sticking and spread a thin layer of sushi rice on the nori, leaving a small border at the top.
Add Filling:
Arrange slices of salmon, avocado, and julienned cucumber in the center of the rice.
- **Roll the Sushi:**
Using the bamboo mat, start rolling the sushi away from you, applying gentle pressure to create a tight roll.
Moisten the top border of the nori with a little water to seal the roll.
- **Slice the Roll:**
With a sharp, wet knife, slice the roll into bite-sized pieces.
- **Serve:**
Arrange the sushi rolls on a plate.
Serve with soy sauce, pickled ginger, and wasabi on the side.
- **Enjoy:**
Pick up a slice of sushi with chopsticks, dip it in soy sauce, and savor the delicious combination of fresh salmon, creamy avocado, and crunchy cucumber.
::
::
### Nems/Cha Gio (Vietnam 🇻🇳)
::div
![Nems](https://source.unsplash.com/random/1200x400/?nems){ width="1200" height="400" .rounded-lg }
::
::tabs
::div{ .flex .space-between .w-full .items-center }
---
label: Ingredients
icon: i-heroicons-fire
---
- 1/2 lb (about 225g) ground pork
- 1 cup finely shredded carrots
- 1 cup bean sprouts
- 1 cup finely chopped wood ear mushrooms (soaked and softened if dried)
- 1 cup thin rice vermicelli noodles (cooked according to package instructions)
- 2 cloves garlic, minced
- 1 shallot, minced
- 1 tablespoon fish sauce
- 1 teaspoon sugar
- 1/2 teaspoon black pepper
- 2 tablespoons vegetable oil for stir-frying
::
::div
---
label: Instructions
icon: i-heroicons-list-bullet-20-solid
---
- **Prepare the Filling:**
In a pan, heat 2 tablespoons of vegetable oil. Add minced garlic and shallots, stir-frying until fragrant.
Add ground pork and cook until browned. Add fish sauce, sugar, and black pepper.
Add shredded carrots, bean sprouts, wood ear mushrooms, and cooked rice vermicelli. Stir-fry until vegetables are slightly softened. Remove from heat and let it cool.
- **Soak Rice Paper:**
Dip one rice paper sheet into warm water for a few seconds until it becomes pliable. Place it on a clean, damp cloth.
- **Roll the Spring Rolls:**
Place a generous spoonful of the filling on the lower third of the rice paper.
Fold the sides of the rice paper over the filling and then roll it up tightly from the bottom to the top.
- **Seal and Repeat:**
Seal the edges by moistening with water. Repeat until all the filling is used.
- **Deep Fry:**
Heat vegetable oil in a deep fryer or a deep pan to 350°F (180°C).
Fry the spring rolls until golden brown and crispy. Drain on paper towels.
- **Prepare Dipping Sauce:**
Mix fish sauce, water, sugar, lime juice, minced garlic, and chopped chili (if using). Stir until sugar is dissolved.
- **Serve:**
Serve the Vietnamese Spring Rolls with the dipping sauce and garnish with shredded carrots.
- **Enjoy:**
Enjoy these crispy and flavorful Vietnamese Spring Rolls as an appetizer or a snack.
::
::
### Bibimbap (South Korean 🇰🇷)
::div
![bibimbap](https://source.unsplash.com/random/1200x400/?bibimbap){ width="1200" height="400" .rounded-lg }
::
::tabs
::div{ .flex .space-between .w-full .items-center }
---
label: Ingredients
icon: i-heroicons-fire
---
- 2 cups cooked short-grain rice
- 1 cup assorted vegetables (such as carrots, spinach, mushrooms, zucchini, bean sprouts)
- 1 cup protein (such as thinly sliced beef, chicken, or tofu)
- 2 tablespoons vegetable oil
- 2 cloves garlic, minced
- Salt, to taste
- Soy sauce, to taste
- Sesame oil, to taste
- Gochujang (Korean red chili paste), to taste
- Toasted sesame seeds, for garnish
- Fried eggs (optional), for topping
::
::div
---
label: Instructions
icon: i-heroicons-list-bullet-20-solid
---
- **Cook the Rice:**
Prepare 2 cups of short-grain rice according to package instructions.
Set aside once cooked.
- **Prepare the Vegetables:**
Slice assorted vegetables thinly (carrots, spinach, mushrooms, zucchini, bean sprouts).
Blanch or stir-fry the vegetables separately or together, seasoning with salt and soy sauce to taste.
Set aside.
- **Cook the Protein (Optional):**
Thinly slice your choice of protein (beef, chicken, or tofu) against the grain.
Heat 1 tablespoon of vegetable oil in a skillet over medium-high heat.
Add minced garlic and cook until fragrant (about 30 seconds).
Add the sliced meat and cook until browned and cooked through.
Season with salt and soy sauce to taste.
Set aside.
- **Assemble the Bibimbap:**
Divide the cooked rice among serving bowls.
Arrange the cooked vegetables and protein on top of the rice in separate sections.
- **Add Flavorings:**
Drizzle each bowl with sesame oil and gochujang, adjusting the amount to taste.
- **Garnish and Serve:**
Sprinkle toasted sesame seeds over the top of each bowl.
Optionally, top each bowl with a fried egg.
- **Enjoy:**
Serve the Bibimbap immediately.
Mix everything together just before eating for the best flavor experience.
::
::
### Cheese Naan (India 🇮🇳)
::div
![Naan](https://source.unsplash.com/random/1200x400/?naan){ width="1200" height="400" .rounded-lg }
::
::tabs
::div
---
label: 'Ingredients'
icon: 'i-heroicons-fire'
---
- 2 1/4 teaspoons (1 packet) active dry yeast
- 1 teaspoon sugar
- 3/4 cup warm water (about 110°F or 43°C)
- 2 cups all-purpose flour, plus extra for rolling
- 1 teaspoon salt
- 1/4 teaspoon baking soda
- 1/4 cup plain yogurt
- 1 tablespoon olive oil or melted ghee
::
::div
---
label: 'Instructions'
icon: 'i-heroicons-list-bullet-20-solid'
---
- **Activate the Yeast:**
In a small bowl, combine the active dry yeast, sugar, and warm water. Let it sit for about 5-10 minutes until it becomes frothy.
- **Prepare the Dough:**
In a large mixing bowl, combine the flour, salt, and baking soda.
Make a well in the center and add the activated yeast mixture, yogurt, and olive oil.
Mix the ingredients until a dough forms.
- **Knead the Dough:**
Transfer the dough to a floured surface and knead for about 5-7 minutes until it becomes smooth and elastic.
- **Let it Rise:**
Place the dough in a lightly oiled bowl, cover it with a damp cloth, and let it rise in a warm place for 1-2 hours or until it has doubled in size.
- **Preheat the Oven:**
Preheat your oven to the highest setting, usually around 500°F (260°C). If you have a pizza stone, place it in the oven to heat.
- **Divide and Shape:**
Divide the dough into equal portions and shape each portion into a ball.
Roll out each ball into an oval or round shape, about 1/4 inch thick.
- **Bake:**
If using a pizza stone, transfer the rolled-out naan directly onto the stone in the preheated oven. Alternatively, place the rolled-out naan on a baking sheet.
Bake for 5-7 minutes or until the naan puffs up and the edges turn golden brown.
- **Optional Toppings:**
Brush the hot naan with melted ghee or butter and sprinkle with chopped fresh cilantro if desired.
- **Serve:**
Serve the naan warm, either as a side to your favorite curry or as a delicious flatbread.
::
::

View File

@ -0,0 +1,93 @@
---
title: 'Discovering the Majestic Peaks'
description: "Embark on an unforgettable odyssey through the Pyrenees, where majestic peaks, pristine valleys, and rich cultural tapestries await in this immersive exploration."
image:
src: https://picsum.photos/id/29/640/360
authors:
- name: Nicolas Maillet
to: https://twitter.com/benjamincanac
avatar:
src: https://i.pravatar.cc/128?u=1
date: 2022-07-08
badge:
label: Nature
---
## Introduction to the Pyrenean Wonderland
Embark on a breathtaking exploration of the Pyrenees, a mountain range that weaves its way between Spain and France, standing as a majestic guardian of natural beauty. This chapter introduces you to the rugged charm and ecological diversity that make the Pyrenees a haven for adventure seekers and nature enthusiasts alike.
The Pyrenees are not merely a geographical boundary; they are a realm of awe-inspiring landscapes, ranging from lush green valleys to snow-capped peaks. The pristine wilderness is home to a diverse array of flora and fauna, creating a captivating tapestry of life that unfolds as you ascend.
![Royal Eagle](https://source.unsplash.com/random/1000x600/?eagle,pyrenees){ width="1000" height="600" .rounded-md }
## Peaks and Valleys - Nature's Masterpiece
Delve into the heart of the Pyrenees, where towering peaks touch the sky, and deep valleys cradle crystal-clear lakes and meandering rivers. From the iconic Pic du Midi to the serene Cirque de Gavarnie, each summit tells a story of geological wonder and natural splendor.
Explore the verdant meadows adorned with wildflowers, witness the dance of marmots in rocky outcrops, and breathe in the crisp mountain air. The Pyrenees offer a sanctuary for biodiversity, where rare species find refuge in the untouched wilderness.
::div{ .flex .space-x-4 .items-center .justify-between }
::div
![valley](https://source.unsplash.com/random/400x400/?pyrenees,valley){ width="400" height="400" .rounded-lg }
::
::div
![mountain](https://source.unsplash.com/random/400x400/?pyrenees,mountain){ width="400" height="400" .rounded-lg }
::
::div
![forest](https://source.unsplash.com/random/400x400/?pyrenees,forest){ width="400" height="400" .rounded-lg }
::
::
## A Tapestry of Culture and History
Beyond its natural wonders, the Pyrenees boast a rich tapestry of human history and cultural heritage. Ancient pilgrimage routes like the Camino de Santiago wind their way through picturesque villages, medieval castles cling to mountain slopes, and traditional mountain festivals celebrate the spirit of the local communities.
Discover the legends that echo through the valleys, from tales of shepherds and their flocks to the myths that shroud hidden caves. The Pyrenees bear witness to centuries of human existence, leaving behind a cultural legacy that adds depth to the mountainous landscape.
::div{ .flex .flex-col .items-center .justify-between }
::div
![valley](https://source.unsplash.com/random/1200x400/?pyrenees,valley){ width="1200" height="400" .rounded-lg .w-full }
::
::div{ .flex .items-center .justify-between .w-full .space-x-4 }
::div
![mountain](https://source.unsplash.com/random/600x400/?pyrenees,mountain){ width="600" height="400" .rounded-lg }
::
::div
![wolf](https://source.unsplash.com/random/600x400/?pyrenees,wolf){ width="600" height="400" .rounded-lg }
::
::div
::
## Outdoor Adventures in the Pyrenean Playground
For the adventure-seekers, the Pyrenees offer an exhilarating playground. Whether it's scaling peaks, hiking through ancient trails, or skiing down powdery slopes, there's no shortage of adrenaline-pumping activities. Traverse the GR10, a long-distance hiking trail that spans the entire length of the Pyrenees, or test your mettle on the challenging rock faces that beckon climbers from around the world.
The Pyrenees cater to all levels of outdoor enthusiasts, making it a haven for both seasoned mountaineers and casual hikers. The variety of landscapes ensures that every outdoor pursuit comes with a stunning backdrop of nature's grandeur.
::div{ .flex .space-x-4 .items-center .justify-between }
::div
![beaver](https://source.unsplash.com/random/200x200/?pyrenees,beaver){ width="200" height="200" .rounded-lg }
::
::div
![mountain](https://source.unsplash.com/random/200x200/?pyrenees,mountain){ width="200" height="200" .rounded-lg }
::
::div
![pyrennes](https://source.unsplash.com/random/200x200/?pyrenees){ width="200" height="200" .rounded-lg }
::
::div
![wolf](https://source.unsplash.com/random/200x200/?pyrenees,wolf){ width="200" height="200" .rounded-lg }
::
::
::div{ .flex .space-x-4 .items-center .justify-between }
::div
![eagle](https://source.unsplash.com/random/600x300/?pyrenees,eagle){ width="600" height="300" .rounded-lg .w-full }
::
::div
![pyrennes](https://source.unsplash.com/random/600x300/?pyrenees){ width="600" height="300" .rounded-lg .w-full }
::
::
## Preserving the Pyrenean Legacy
As we look to the future, conservation efforts and sustainable tourism initiatives are vital for preserving the Pyrenees' unique ecosystem. Balancing the thrill of exploration with the responsibility of preservation ensures that future generations can continue to be captivated by the untamed beauty of these ancient mountains.
In conclusion, 'Discovering the Majestic Peaks: A Journey Through the Pyrenees' invites you to witness the grandeur of a mountain range that transcends geographical boundaries, offering a symphony of nature, culture, and adventure in every chapter.
![snow](https://source.unsplash.com/random/600x300/?pyrenees,snow){ width="600" height="300" .rounded-lg .w-full }

View File

@ -0,0 +1,68 @@
---
title: 'Unveiling the Marvel'
description: "The Journey to Create the James Webb Space Telescope"
image:
src: https://picsum.photos/id/903/640/360
authors:
- name: Josh Bayers
to: https://twitter.com/benjamincanac
avatar:
src: https://i.pravatar.cc/128?u=2
date: 2020-12-12
badge:
label: Science, Astronomie, History
---
## Building the Vision
::div{ .grid .grid-cols-3 .items-center .w-full .gap-x-8 }
![nebula](https://source.unsplash.com/random/400x400/?nebula){ width="400" height="400" .rounded-lg .col-span-1 }
::div{ .col-span-2 }
### Designing the Future
In the bustling halls of NASA's engineering facilities, a team of brilliant minds gathered around blueprints and prototypes. They envisioned a telescope unlike any other, one that would revolutionize our understanding of the cosmos. Led by Dr. Catherine Nguyen, they meticulously crafted the design for what would become the James Webb Space Telescope (JWST). With its massive primary mirror and cutting-edge infrared technology, the JWST promised to unveil the deepest mysteries of the universe.
::
::
::div{ .grid .grid-cols-3 .items-center .w-full .gap-x-8 }
::div{ .col-span-2 }
### Overcoming Challenges
However, the path to launch was fraught with challenges. Engineers faced immense pressure to ensure the JWST's success, navigating technical hurdles and budget constraints. Delays mounted as unforeseen complications arose, testing the team's resolve. Yet, fueled by their passion for exploration, they pressed onward, refining each component with unwavering determination. Through perseverance and ingenuity, they transformed the JWST from a concept into a marvel of modern engineering.
::
![mountain](https://source.unsplash.com/random/400x400/?mountain){ width="400" height="400" .rounded-lg .col-span-1 }
::
## Embarking into the Unknown
::div{ .grid .grid-cols-3 .items-center .w-full .gap-x-8 }
![rocket](https://source.unsplash.com/random/400x400/?rocket){ width="400" height="400" .rounded-lg .col-span-1 }
::div{ .col-span-2 }
### Launching into Space
On a crisp morning at the Guiana Space Centre in French Guiana, anticipation hung in the air as the JWST stood tall atop its Ariane 5 rocket. Millions around the world held their breath as countdown reached its final moments. Then, with a thunderous roar, the rocket ignited, propelling the telescope into the vast expanse of space. As it soared higher and higher, the JWST represented humanity's boundless curiosity and relentless pursuit of knowledge.
::
::
::div{ .grid .grid-cols-3 .items-center .w-full .gap-x-8 }
::div{ .col-span-2 }
### Unfolding the Universe
Months later, nestled in its orbit around the Earth, the JWST embarked on its monumental mission. With its golden mirrors unfurled like petals, it peered into the depths of the cosmos, capturing breathtaking images of distant galaxies and nebulae. Each observation unveiled new wonders, shedding light on the origins of stars, planets, and life itself. From the icy realms of the outer solar system to the fiery cores of distant exoplanets, the JWST's gaze transcended the limits of human imagination.
::
![universe](https://source.unsplash.com/random/400x400/?universe){ .rounded-lg .col-span-1 }
::
## Legacy of Discovery
::div{ .grid .grid-cols-3 .items-center .w-full .gap-x-8 }
![childrens](https://source.unsplash.com/random/400x400/?childrens){ width="400" height="400" .rounded-lg .col-span-1 }
::div{ .col-span-2 }
### Inspiring Future Generations
As the years passed, the JWST's legacy continued to grow, inspiring generations to dream of the stars. Its groundbreaking discoveries sparked scientific revolutions and expanded humanity's collective understanding of the universe. From classrooms to observatories, its images adorned the walls, igniting the spark of curiosity in the minds of countless individuals. The JWST served as a beacon of hope, reminding us of our capacity to explore, discover, and unite in pursuit of a shared destiny among the stars.
::
::
::div{ .grid .grid-cols-3 .items-center .w-full .gap-x-8 }
::div{ .col-span-2 }
### A Journey Without End
Though the JWST's mission eventually came to a close, its impact endured far beyond the boundaries of space and time. Its data continued to fuel scientific inquiry for decades to come, unlocking new realms of knowledge and shaping the course of human history. And as future telescopes followed in its wake, each building upon its pioneering legacy, the spirit of exploration embodied by the James Webb Space Telescope lived on, guiding humanity toward ever greater heights of discovery and understanding.
::
![Royal Eagle](https://source.unsplash.com/random/400x400/?telescope){ width="400" height="400" .rounded-lg .col-span-1 }
::

View File

@ -0,0 +1,61 @@
---
title: 'The Benefits of Meditation'
description: "The Benefits of Meditation and Mindfulness Practices on Mental Health"
image:
src: https://picsum.photos/id/691/640/360
authors:
- name: Rebecca Millers
to: https://twitter.com/benjamincanac
avatar:
src: https://i.pravatar.cc/128?u=3
date: 2021-04-23
badge:
label: Health
---
![yoga](https://source.unsplash.com/random/1200x600/?yoga){ width="1200" height="600" .rounded-lg }
## 🧘🏻 Introduction
In today's fast-paced world, where stress and anxiety seem to be constant companions, the importance of mental well-being cannot be overstated. Fortunately, there are ancient practices like meditation and mindfulness that offer profound benefits for our mental health. Research continues to uncover the transformative effects of these practices, showing how they can help alleviate stress, improve focus, and cultivate a sense of inner peace and resilience.
## 🪷 Understanding Meditation and Mindfulness
Meditation is a practice that involves training the mind to focus and redirect thoughts. It often involves techniques such as deep breathing, visualization, or repeating a mantra. Mindfulness, on the other hand, is about being fully present in the moment, acknowledging and accepting one's thoughts, feelings, and bodily sensations without judgment.
One of the most well-documented benefits of meditation and mindfulness is their ability to reduce stress and anxiety. Studies have shown that regular meditation practice can lower levels of cortisol, the stress hormone, in the body. By quieting the mind and promoting relaxation, meditation helps to interrupt the cycle of anxious thoughts and bodily tension, leading to a greater sense of calm and tranquility.
::div{ .grid .grid-cols-2 .space-x-4 .justify-between .items-center }
::div
![flowers](https://source.unsplash.com/random/600x200/?flowers){ width="600" height="200" .rounded-lg }
::
::div
![lotus](https://source.unsplash.com/random/600x200/?lotus){ width="600" height="200" .rounded-lg }
::
::
## 🧠 Improved Focus and Cognitive Function
In today's digital age, our attention is constantly pulled in multiple directions, leading to reduced focus and cognitive overload. Meditation and mindfulness have been found to enhance cognitive function by increasing attention span, concentration, and memory. By training the mind to stay present and focused, these practices can improve productivity and mental clarity, enabling individuals to perform better in various tasks and activities.
## 😌 Enhanced Emotional Well-being
Another significant benefit of meditation and mindfulness is their positive impact on emotional well-being. By fostering self-awareness and acceptance, these practices help individuals develop a healthier relationship with their emotions. Studies have shown that regular meditation can reduce symptoms of depression and anxiety disorders, while also increasing feelings of happiness and overall life satisfaction.
## 💪🏻 Building Resilience and Coping Skills
![sky](https://source.unsplash.com/random/1200x300/?sky){ width="1200" height="300" .rounded-lg }
Life is filled with ups and downs, challenges, and setbacks. Meditation and mindfulness provide valuable tools for building resilience and coping with adversity. By cultivating a sense of inner strength and equanimity, these practices help individuals navigate difficult situations with greater ease and grace. They teach us to respond to stressors with mindfulness and compassion, rather than reacting impulsively out of fear or frustration.
## ❤️ Cultivating a Sense of Connection and Compassion
Beyond benefiting individual mental health, meditation and mindfulness also promote a greater sense of connection and compassion towards others. By cultivating empathy and understanding, these practices foster harmonious relationships and a sense of interconnectedness with the world around us. They remind us that we are all part of a larger tapestry of humanity, bound together by our shared experiences and aspirations.
## 🫶🏻 Conclusion
In conclusion, the benefits of meditation and mindfulness on mental health are undeniable. From reducing stress and anxiety to improving focus, emotional well-being, and resilience, these ancient practices offer a holistic approach to mental wellness in an increasingly hectic world. By incorporating meditation and mindfulness into our daily lives, we can nurture a sense of inner peace, balance, and contentment that radiates outward, enriching not only our own lives but the lives of those around us as well.
![peace](https://source.unsplash.com/random/1200x600/?peace){ width="1200" height="600" .rounded-lg }

158
content/3.blog/5.animals.md Normal file
View File

@ -0,0 +1,158 @@
---
title: 'The 10 Most Dangerous Creatures on Earth'
description: "From Predators to the Ultimate Threat"
image:
src: https://picsum.photos/id/219/640/360
authors:
- name: Emilio Manuel
to: https://twitter.com/benjamincanac
avatar:
src: https://i.pravatar.cc/128?u=4
date: 2018-05-15
badge:
label: Animals
---
The natural world is teeming with creatures of all shapes and sizes, each with its own unique set of adaptations and behaviors. While many animals pose little threat to humans, there are some that command respect and caution due to their deadly capabilities. From apex predators to venomous insects, let's explore the 10 most dangerous creatures on Earth, culminating in the ultimate threat: humans.
## Saltwater Crocodile
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![crocodile](https://source.unsplash.com/random/800x400/?crocodile){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
As the largest living reptile, the **saltwater crocodile** reigns supreme in its domain. With a powerful bite force and lightning-fast ambush tactics, these apex predators strike fear into the hearts of any creature unfortunate enough to cross their path.
::
::
::
## Box Jellyfish
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![jellyfish](https://source.unsplash.com/random/800x400/?jellyfish){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
Known for its potent venom and translucent appearance, the **box jellyfish** is one of the most venomous creatures in the world. Its sting can cause excruciating pain and, in some cases, even death, making it a formidable predator of the oceans.
::
::
::
## African Elephant
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![elephant](https://source.unsplash.com/random/800x400/?elephant){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
While revered for their intelligence and majesty, **African elephants** can also be incredibly dangerous. With their massive size and formidable tusks, these gentle giants are capable of causing serious injury or death if provoked or threatened.
::
::
::
## Cape Buffalo
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![buffalo](https://source.unsplash.com/random/800x400/?buffalo){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
Often referred to as the "black death" or "widowmaker" of the African savannah, the **Cape buffalo** is responsible for more human fatalities in Africa than any other large animal. Highly unpredictable and fiercely territorial, these bovines will not hesitate to charge at anything they perceive as a threat.
::
::
::
## Great White Shark
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![shark](https://source.unsplash.com/random/800x400/?shark){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
As the apex predator of the ocean, the **great white shark** strikes fear into the hearts of beachgoers and surfers around the world. With its razor-sharp teeth and lightning-fast attacks, this formidable predator is the stuff of nightmares for many.
::
::
::
## Mosquito
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![mosquito](https://source.unsplash.com/random/800x400/?mosquito){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
While tiny in size, the **mosquito** is responsible for more human deaths than any other creature on Earth. As carriers of deadly diseases such as malaria, dengue fever, and Zika virus, these blood-sucking insects pose a significant threat to public health worldwide.
::
::
::
## Golden Poison Frog
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![frog](https://source.unsplash.com/random/800x400/?frog){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
The **Golden Poison Frog**, native to the rainforests of Colombia, is considered one of the most toxic amphibians on the planet. Despite its vibrant golden coloration, this frog secretes potent neurotoxins through its skin, which can cause severe reactions or even death if ingested or handled improperly. Its toxicity serves as a defense mechanism against predators, making it one of the most dangerous frogs in the world.
::
::
::
## King Cobra
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![snake](https://source.unsplash.com/random/800x300/?snake){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
As the largest venomous snake in the world, the **king cobra** commands respect wherever it is found. With its deadly venom and impressive size, this iconic serpent is a top predator in its habitat and a formidable adversary for any would-be threat.
::
::
::
## Hippopotamus
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![hippopotamus](https://source.unsplash.com/random/800x400/?hippopotamus){ width="800" height="400" .rounded-lg }
::
::div{ .w-[50%] }
Despite their seemingly docile appearance, hippos are responsible for more human fatalities in Africa than any other large animal besides mosquitoes. Highly territorial and fiercely protective of their young, these semi-aquatic mammals are capable of inflicting serious injury with their powerful jaws and massive size.
::
::
::
## Humans
::card{ .not-prose }
::div{ .flex .space-x-8 .items-center }
::div{ .w-[55%] }
![Trex](https://source.unsplash.com/random/800x800/?humans,hunting){ width="800" height="800" .rounded-lg }
::
::div{ .w-[50%] }
While not traditionally viewed as a "wild" creature, **humans** have proven to be the most dangerous and destructive force on Earth. From habitat destruction to pollution, overhunting, and warfare, humans have caused irreparable harm to countless species and ecosystems, threatening the very survival of life on our planet.
In conclusion, while the natural world is filled with creatures of all shapes and sizes, it is ultimately humans who pose the greatest threat to our own survival and the delicate balance of life on Earth. As stewards of our planet, it is imperative that we respect and protect the diverse array of species that call it home, lest we face the consequences of our own actions.
::
::
::

View File

@ -0,0 +1,114 @@
---
title: 'The Rise of Cryptocurrencies'
description: "Transforming Finance and Economy"
image:
src: https://picsum.photos/id/1048/640/360
authors:
- name: Emily pasek
to: https://twitter.com/benjamincanac
avatar:
src: https://i.pravatar.cc/128?u=5
date: 2024-02-01
badge:
label: Economy, Information Technology
---
In recent years, cryptocurrencies have emerged as a disruptive force in the world of finance and economics. Born out of the decentralized ethos of blockchain technology, cryptocurrencies have challenged traditional financial systems, offering new avenues for investment, transactions, and economic empowerment. This article explores the impact of cryptocurrencies on the economy and finance, examining their evolution, opportunities, and challenges.
![Cryto](https://source.unsplash.com/random/1000x600/?crypto){ width="1000" height="600" .rounded-lg }
## The Evolution of Cryptocurrencies
Bitcoin, introduced in 2009 by the pseudonymous Satoshi Nakamoto, marked the birth of cryptocurrencies. Initially met with skepticism, Bitcoin gradually gained traction, attracting attention from investors and technologists alike. Its underlying blockchain technology, a distributed ledger system, offered transparency, security, and immutability, laying the foundation for a new financial paradigm.
Since then, thousands of cryptocurrencies, including Ethereum, Ripple, and Litecoin, have proliferated, each with its unique features and use cases. Ethereum introduced smart contracts, enabling programmable transactions, while Ripple focused on facilitating cross-border payments. These diverse offerings have expanded the scope of cryptocurrencies, fueling innovation and experimentation in the financial sector.
## 📈 Opportunities in Cryptocurrencies
Cryptocurrencies present numerous opportunities for individuals, businesses, and economies:
- **Financial Inclusion:** Cryptocurrencies offer financial services to the unbanked and underbanked populations, bypassing traditional banking infrastructure and reducing transaction costs.
- **Decentralized Finance (DeFi):** DeFi platforms leverage blockchain technology to provide decentralized alternatives to traditional financial services, including lending, borrowing, and trading, without intermediaries.
- **Investment Diversification:** Cryptocurrencies serve as a hedge against traditional assets, providing diversification benefits and offering exposure to a nascent asset class with high growth potential.
- **Technological Innovation:** The underlying blockchain technology of cryptocurrencies has applications beyond finance, including supply chain management, healthcare, and voting systems, driving innovation across industries.
## 📉 Challenges and Risks
Despite their potential, cryptocurrencies also face challenges and risks that warrant attention:
- **Volatility:** Cryptocurrency markets are characterized by high volatility, subject to speculative trading, market manipulation, and sudden price fluctuations, posing risks to investors and stability.
- **Regulatory Uncertainty:** Governments and regulatory bodies worldwide are grappling with the regulation of cryptocurrencies, raising concerns about legal compliance, taxation, and investor protection.
- **Security Concerns:** Cryptocurrency exchanges and wallets are vulnerable to cyber attacks, theft, and fraud, necessitating robust security measures and risk management practices.
- **Environmental Impact:** The energy-intensive mining process of cryptocurrencies, particularly Bitcoin, raises environmental concerns due to its carbon footprint and energy consumption.
## Here are the most well-known cryptocurrencies
These cryptocurrencies are among the most recognized and widely used in the cryptocurrency ecosystem, each with its unique features and use cases.
::card-group
::card
---
icon: 'i-simple-icons-bitcoin'
title: Bitcoin (BTC)
to: 'https://bitcoin.org/'
target: '_blank'
---
The first and most famous cryptocurrency, often considered a digital store of value and widely used as a medium of exchange.
::
::card
---
icon: 'i-simple-icons-ethereum'
title: Ethereum (ETH)
to: 'https://ethereum.org'
target: '_blank'
---
A blockchain platform enabling developers to create smart contracts and decentralized applications (DApps).
::
::card
---
icon: 'i-simple-icons-ripple'
title: Ripple (XRP)
to: 'https://ripple.com/'
target: '_blank'
---
Focused on providing fast and inexpensive global payment solutions, especially for interbank transactions and cross-border payments.
::
::card
---
icon: 'i-simple-icons-litecoin'
title: Litecoin (LTC)
to: 'https://litecoin.com//'
target: '_blank'
---
Known for faster transaction times and a more decentralized approach compared to Bitcoin.
::
::card
---
icon: 'i-simple-icons-bitcoincash'
title: Bitcoin Cash (BCH)
to: 'https://bitcoincash.org'
target: '_blank'
---
A fork of Bitcoin aimed at improving scalability and transaction processing capabilities.
::
::card
---
icon: 'i-simple-icons-cardano'
title: Cardano (ADA)
to: 'https://cardano.org/'
target: '_blank'
---
A blockchain platform designed for enhanced security and scalability, supporting smart contract and DApp development.
::
::
## Conclusion
Cryptocurrencies have emerged as a transformative force in the economy and finance, offering opportunities for innovation, inclusion, and investment. However, their adoption and integration into mainstream financial systems require addressing regulatory, security, and scalability challenges. As cryptocurrencies continue to evolve, their impact on the global economy will be shaped by technological advancements, regulatory developments, and market dynamics, paving the way for a decentralized and digitized financial future.

260
content/3.blog/7.nuxt-ui.md Normal file
View File

@ -0,0 +1,260 @@
---
title: 'I tested Nuxt UI'
description: "Nuxt UI is a module that provides a set of Vue components and composables built with Tailwind CSS and Headless UI"
image:
src: https://ui.nuxt.com/social-card.png
authors:
- name: Kevin browski
to: https://twitter.com/benjamincanac
avatar:
src: https://i.pravatar.cc/128?u=6
date: 2023-10-19
badge:
label: Web devlopment, Nuxt
---
## Introduction
Nuxt UI is a module that provides a set of Vue components and composables built with Tailwind CSS and Headless UI to help you build beautiful and accessible user interfaces.
Its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts.
### ✨ Awesome Features
- Built with Headless UI and Tailwind CSS
- HMR support through Nuxt App Config
- Dark mode support
- Support for LTR and RTL languages
- Keyboard shortcuts
- Bundled icons
- Fully typed
- Figma Kit
## 😌 Easy and quick installation
### Setup
1. Install `@nuxt/ui` dependency to your project:
::code-group
```bash [pnpm]
pnpm add @nuxt/ui
```
```bash [yarn]
yarn add @nuxt/ui
```
```bash [npm]
npm install @nuxt/ui
```
```bash [bun]
bun add @nuxt/ui
```
::
2. Add it to your `modules` section in your `nuxt.config`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui']
})
```
That's it! You can now use all the components and composables in your Nuxt app 🤩
### Automatically installed modules
Nuxt UI will automatically install the [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/), [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) and [nuxt-icon](https://github.com/nuxt-modules/icon) modules for you.
::callout{icon="i-heroicons-exclamation-triangle"}
You should remove them from your `modules` and `dependencies` if you've previously installed them.
::
### ...And all in Typescript !
This module is written in TypeScript and provides typings for all the components and composables.
You can use those types in your own components by importing them from `#ui/types`, for example when defining wrapper components:
```vue
<template>
<UBreadcrumb :links="links">
<template #icon="{ link }">
<UIcon :name="link.icon" />
</template>
</UBreadcrumb>
</template>
<script setup lang="ts">
import type { BreadcrumbLink } from '#ui/types'
export interface Props {
links: BreadcrumbLink[]
}
defineProps<Props>()
</script>
```
### The power of IntelliSense
If you're using VSCode, you can install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension to get autocompletion for the classes.
You can read more on how to set it up on the [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/tailwind/editor-support) module documentation.
### Many options
| Key | Default | Description |
|-----------------------|-----------------|-------------------------------------------------------------------------------------------------------------|
| `prefix` | `u` | Define the prefix of the imported components. |
| `global` | `false` | Expose components globally. |
| `icons` | `['heroicons']` | Icon collections to load. |
| `safelistColors` | `['primary']` | Force safelisting of colors to need be purged. |
| `disableGlobalStyles` | `false` | Disable [global CSS styles](https://github.com/nuxt/ui/blob/dev/src/runtime/ui.css) injected by the module. |
Configure options in your `nuxt.config.ts` as such:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
ui: {
global: true,
icons: ['mdi', 'simple-icons']
}
})
```
## 🎨 Theming
### Colors
#### Configuration
Components are based on a `primary` and a `gray` color. You can change them in your `app.config.ts`.
```ts [app.config.ts]
export default defineAppConfig({
ui: {
primary: 'green',
gray: 'cool'
}
})
```
As this module uses Tailwind CSS under the hood, you can use any of the [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference) or your own custom colors. By default, the `primary` color is `green` and the `gray` color is `cool`.
When [using custom colors](https://tailwindcss.com/docs/customizing-colors#using-custom-colors) or [adding additional colors](https://tailwindcss.com/docs/customizing-colors#adding-additional-colors) through the `extend` key in your `tailwind.config.ts`, you'll need to make sure to define all the shades from `50` to `950` as most of them are used in the components config defined in [`ui.config.ts`](https://github.com/nuxt/ui/blob/dev/src/runtime/ui.config.ts). You can [generate your colors](https://tailwindcss.com/docs/customizing-colors#generating-colors) using tools such as https://uicolors.app/ for example.
### Components
#### `app.config.ts`
Components are styled with Tailwind CSS but classes are all defined in the default [ui.config.ts](https://github.com/nuxt/ui/blob/dev/src/runtime/ui.config.ts) file. You can override those in your own `app.config.ts`.
```ts [app.config.ts]
export default defineAppConfig({
ui: {
container: {
constrained: 'max-w-5xl'
}
}
})
```
Thanks to [tailwind-merge](https://github.com/dcastil/tailwind-merge), the `app.config.ts` is smartly merged with the default config. This means you don't have to rewrite everything.
#### `ui` prop
Each component has a `ui` prop that allows you to customize everything specifically.
```vue
<template>
<UContainer :ui="{ constrained: 'max-w-2xl' }">
<slot />
</UContainer>
</template>
```
::callout{icon="i-heroicons-light-bulb"}
You can find the default classes for each component under the `Config` section.
::
### Dark mode
All the components are styled with dark mode in mind.
:color-mode-button
### Icons
You can use any icon (100,000+) from [Iconify](https://iconify.design/).
Some components have an `icon` prop that allows you to add an icon to the component.
```vue
<template>
<UButton icon="i-heroicons-magnifying-glass" />
</template>
```
## Here are some components you can use... but there are many others !
::card-group
::card
---
title: Accordion
to: https://ui.nuxt.com/components/accordion
target: _blank
---
Display togglable accordion panels.
::
::card
---
title: Carousel
to: https://ui.nuxt.com/components/carousel
target: _blank
---
Display images or content in a scrollable area.
::
::card
---
title: Command Palette
to: https://ui.nuxt.com/components/command-palette
target: _blank
---
Add a customizable command palette to your app.
::
::card
---
title: Popover
to: https://ui.nuxt.com/components/popover
target: _blank
---
Display a non-modal dialog that floats around a trigger element.
::
::card
---
title: Range
to: https://ui.nuxt.com/components/range
target: _blank
---
Display a range field
::
::card
---
title: Table
to: https://ui.nuxt.com/components/table
target: _blank
---
Display data in a table.
::
::
## Conclusion
Nuxt UI is the **perfect**, **modular** and **customizable** UI library for creating websites in Nuxt. it allows you to create a beautiful website with incredible components (more than 45!)
In addition, the Pro version allows you to expand the range of components, it's a collection of premium Vue components built on top of Nuxt UI to create beautiful & responsive Nuxt applications in minutes.
It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.

39
content/header.yml Normal file
View File

@ -0,0 +1,39 @@
- label: 首页
to: /
- label: 解决方案
children:
- label: 互联网3.0
description: 区块链及元宇宙板块
to: /solutions/web3
icon: i-tabler-affiliate
- label: 数字孪生
description: 智慧城市及工业元宇宙解决方案
to: /solutions/digital-twin
icon: i-tabler-map-2
- label: 混合现实
description: 虚拟、混合现实内容开发
to: /solutions/mixed-reality
icon: i-tabler-3d-cube-sphere
- label: 生成式人工智能
description: 人工智能及大数据分析
to: /solutions/ai
icon: i-tabler-robot
- label: 最新动态
to: /release
- label: 社区动态
children:
- label: AaaO!
description: 未建成建筑系列
to: /community/aaao
icon: i-tabler-building-skyscraper
- label: TOPO Land
description: TOPO元界岛
to: /community/topo
icon: i-tabler-brand-apple-arcade
- label: VAMX
description: VR/AR/MR/XR 学术生态社区
to: /community/vamx
icon: i-tabler-brand-vimeo
- label: 关于我们
to: /about

49
error.vue Normal file
View File

@ -0,0 +1,49 @@
<script setup lang="ts">
import type { NuxtError } from '#app'
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
useSeoMeta({
title: 'Page not found',
description: 'We are sorry but this page could not be found.'
})
defineProps({
error: {
type: Object as PropType<NuxtError>,
required: true
}
})
useHead({
htmlAttrs: {
lang: 'en'
}
})
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(), { default: () => [] })
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', { default: () => [], server: false })
provide('navigation', navigation)
</script>
<template>
<div>
<Header />
<UMain>
<UContainer>
<UPage>
<UPageError :error="error" />
</UPage>
</UContainer>
</UMain>
<Footer />
<ClientOnly>
<LazyUContentSearch :files="files" :navigation="navigation" />
</ClientOnly>
<UNotifications />
</div>
</template>

23
install.sh Normal file
View File

@ -0,0 +1,23 @@
#!/bin/bash
# nuxt-pro 1.1.0
FIND_FILE="./package.json"
FIND_STR="@nuxt/ui-pro"
# 判断匹配函数匹配函数不为0则包含给定字符
if [ `grep -c "$FIND_STR" $FIND_FILE` -ne '0' ];then
echo "找到UI-PRO开始删除"
# sed -n '/build:before/=' ./node_modules/@nuxt/ui-pro/modules/pro/index.ts
FIND_FILE="./node_modules/@nuxt/ui-pro/modules/pro/index.ts"
FIND_STR="build:before"
# 判断匹配函数匹配函数不为0则包含给定字符
if [ `grep -c "$FIND_STR" $FIND_FILE` -ne '0' ];then
echo "找到build:before开始删除"
sed -i '103,105d' ./node_modules/@nuxt/ui-pro/modules/pro/index.ts
exit 0
fi
exit 0
fi

46
layouts/auth.vue Normal file
View File

@ -0,0 +1,46 @@
<script setup lang="ts">
useHead({
bodyAttrs: {
class: 'dark:bg-gray-950'
}
})
</script>
<template>
<div class="h-screen flex items-center justify-center overlay">
<div class="gradient" />
<UButton icon="i-heroicons-home" label="Home" to="/" color="black" class="absolute top-4" />
<slot />
</div>
</template>
<style scoped>
.gradient {
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(50% 50% at 50% 50%, rgb(var(--color-primary-500) / 0.25) 0, #FFF 100%);
}
.dark {
.gradient {
background: radial-gradient(50% 50% at 50% 50%, rgb(var(--color-primary-400) / 0.1) 0, rgb(var(--color-gray-950)) 100%);
}
}
.overlay {
background-size: 100px 100px;
background-image:
linear-gradient(to right, rgb(var(--color-gray-200)) 0.5px, transparent 0.5px),
linear-gradient(to bottom, rgb(var(--color-gray-200)) 0.5px, transparent 0.5px);
}
.dark {
.overlay {
background-image:
linear-gradient(to right, rgb(var(--color-gray-900)) 0.5px, transparent 0.5px),
linear-gradient(to bottom, rgb(var(--color-gray-900)) 0.5px, transparent 0.5px);
}
}
</style>

48
layouts/default.vue Normal file
View File

@ -0,0 +1,48 @@
<script setup lang="ts">
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
import type { UseScrollReturn } from '@vueuse/core'
import { vScroll } from '@vueuse/components'
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(), { default: () => [] })
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', { default: () => [], server: false })
provide('navigation', navigation)
const targetTimeCoef = ref(1)
const el = ref<HTMLElement | null>(null)
const { arrivedState } = useScroll(el)
const { x, y, isOutside } = useMouseInElement(el)
function onScroll(state: UseScrollReturn) {
if (state.isScrolling.value) {
targetTimeCoef.value = 100
} else {
targetTimeCoef.value = 1
}
}
</script>
<template>
<div ref="el" v-scroll="onScroll" class="h-screen w-screen overflow-y-auto overflow-x-hidden">
<Header />
<UMain>
<slot />
</UMain>
<Footer />
<ClientOnly>
<LazyUContentSearch :files="files" :navigation="navigation" />
</ClientOnly>
<div class="fixed bottom-0 right-0 -z-10 opacity-90">
<ClientOnly>
<Galaxy :target-time-coef="targetTimeCoef" />
</ClientOnly>
</div>
</div>
</template>

32
nuxt.config.ts Normal file
View File

@ -0,0 +1,32 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
extends: [process.env.NUXT_UI_PRO_PATH || '@nuxt/ui-pro'],
modules: [
'@nuxt/content',
'@nuxt/image',
'@nuxt/ui',
'@nuxt/fonts',
'@nuxthq/studio',
'@vueuse/nuxt',
'nuxt-og-image',
],
hooks: {
// Define `@nuxt/ui` components as global to use them in `.md` (feel free to add those you need)
'components:extend': (components) => {
const globals = components.filter((c) => ['UButton'].includes(c.pascalName))
globals.forEach((c) => c.global = true)
}
},
ssr: false,
ui: {
icons: ['heroicons', 'simple-icons', 'tabler']
},
routeRules: {
'/api/search.json': { prerender: true },
'/docs': { redirect: '/docs/getting-started', prerender: false }
},
devtools: {
enabled: true
}
})

78
nuxt.schema.ts Normal file
View File

@ -0,0 +1,78 @@
import { field, group } from '@nuxthq/studio/theme'
export default defineNuxtSchema({
appConfig: {
ui: group({
title: 'UI',
description: 'UI Customization.',
icon: 'i-mdi-palette-outline',
fields: {
icons: group({
title: 'Icons',
description: 'Manage icons used in UI Pro.',
icon: 'i-mdi-application-settings-outline',
fields: {
search: field({
type: 'icon',
title: 'Search Bar',
description: 'Icon to display in the search bar.',
icon: 'i-mdi-magnify',
default: 'i-heroicons-magnifying-glass-20-solid'
}),
dark: field({
type: 'icon',
title: 'Dark mode',
description: 'Icon of color mode button for dark mode.',
icon: 'i-mdi-moon-waning-crescent',
default: 'i-heroicons-moon-20-solid'
}),
light: field({
type: 'icon',
title: 'Light mode',
description: 'Icon of color mode button for light mode.',
icon: 'i-mdi-white-balance-sunny',
default: 'i-heroicons-sun-20-solid'
}),
external: field({
type: 'icon',
title: 'External Link',
description: 'Icon for external link.',
icon: 'i-mdi-arrow-top-right',
default: 'i-heroicons-arrow-up-right-20-solid'
}),
chevron: field({
type: 'icon',
title: 'Chevron',
description: 'Icon for chevron.',
icon: 'i-mdi-chevron-down',
default: 'i-heroicons-chevron-down-20-solid'
}),
hash: field({
type: 'icon',
title: 'Hash',
description: 'Icon for hash anchors.',
icon: 'i-ph-hash',
default: 'i-heroicons-hashtag-20-solid'
})
}
}),
primary: field({
type: 'string',
title: 'Primary',
description: 'Primary color of your UI.',
icon: 'i-mdi-palette-outline',
default: 'green',
required: ['sky', 'mint', 'rose', 'amber', 'violet', 'emerald', 'fuchsia', 'indigo', 'lime', 'orange', 'pink', 'purple', 'red', 'teal', 'yellow', 'green', 'blue', 'cyan', 'gray', 'white', 'black']
}),
gray: field({
type: 'string',
title: 'Gray',
description: 'Gray color of your UI.',
icon: 'i-mdi-palette-outline',
default: 'slate',
required: ['slate', 'cool', 'zinc', 'neutral', 'stone']
})
}
})
}
})

36
package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "nuxt-ui-pro-template-saas",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"lint": "eslint .",
"typecheck": "nuxt typecheck"
},
"dependencies": {
"@iconify-json/heroicons": "^1.1.20",
"@iconify-json/simple-icons": "^1.1.97",
"@iconify-json/tabler": "^1.1.110",
"@nuxt/content": "^2.12.1",
"@nuxt/fonts": "^0.5.1",
"@nuxt/image": "^1.4.0",
"@nuxt/ui-pro": "^1.1.0",
"@types/three": "0.127.0",
"nice-color-palettes": "3.0.0",
"@vueuse/nuxt": "^10.9.0",
"nuxt": "^3.11.1",
"nuxt-og-image": "^2.2.4",
"three": "0.127.0",
"troisjs": "0.3.0-beta.4"
},
"devDependencies": {
"@nuxt/eslint-config": "^0.2.0",
"@nuxthq/studio": "^1.0.13",
"eslint": "^8.57.0",
"vue-tsc": "^2.0.7"
}
}

3
pages/blog.vue Normal file
View File

@ -0,0 +1,3 @@
<template>
<NuxtPage />
</template>

85
pages/blog/[slug].vue Normal file
View File

@ -0,0 +1,85 @@
<script setup lang="ts">
import { withoutTrailingSlash, joinURL } from 'ufo'
import type { BlogPost } from '~/types'
const route = useRoute()
const { data: post } = await useAsyncData(route.path, () => queryContent<BlogPost>(route.path).findOne())
if (!post.value) {
throw createError({ statusCode: 404, statusMessage: 'Post not found', fatal: true })
}
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => queryContent('/blog')
.where({ _extension: 'md' })
.without(['body', 'excerpt'])
.sort({ date: -1 })
.findSurround(withoutTrailingSlash(route.path))
, { default: () => [] })
const title = post.value.head?.title || post.value.title
const description = post.value.head?.description || post.value.description
useSeoMeta({
title,
ogTitle: title,
description,
ogDescription: description,
})
if (post.value.image?.src) {
const site = useSiteConfig()
useSeoMeta({
ogImage: joinURL(site.url, post.value.image.src),
twitterImage: joinURL(site.url, post.value.image.src)
})
} else {
defineOgImage({
component: 'Saas',
title,
description,
headline: 'Blog'
})
}
</script>
<template>
<UContainer v-if="post">
<UPageHeader :title="post.title" :description="post.description">
<template #headline>
<UBadge v-bind="post.badge" variant="subtle" />
<span class="text-gray-500 dark:text-gray-400">&middot;</span>
<time class="text-gray-500 dark:text-gray-400">{{ new Date(post.date).toLocaleDateString('en', { year: 'numeric', month: 'short', day: 'numeric' }) }}</time>
</template>
<div class="flex flex-wrap items-center gap-3 mt-4">
<UButton
v-for="(author, index) in post.authors"
:key="index"
:to="author.to"
color="white"
target="_blank"
size="sm"
>
<UAvatar v-bind="author.avatar" :alt="author.name" size="2xs" />
{{ author.name }}
</UButton>
</div>
</UPageHeader>
<UPage>
<UPageBody prose>
<ContentRenderer v-if="post && post.body" :value="post" />
<hr v-if="surround?.length">
<UContentSurround :surround="surround" />
</UPageBody>
<template #right>
<UContentToc v-if="post.body && post.body.toc" :links="post.body.toc.links" />
</template>
</UPage>
</UContainer>
</template>

53
pages/blog/index.vue Normal file
View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import type { BlogPost } from '~/types'
const { data: page } = await useAsyncData('blog', () => queryContent('/blog').findOne())
if (!page.value) {
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
}
const { data: posts } = await useAsyncData('posts', () => queryContent<BlogPost>('/blog')
.where({ _extension: 'md' })
.sort({ date: -1 })
.find())
useSeoMeta({
title: page.value.title,
ogTitle: page.value.title,
description: page.value.description,
ogDescription: page.value.description
})
defineOgImage({
component: 'Saas',
title: page.value.title,
description: page.value.description
})
</script>
<template>
<UContainer>
<UPageHeader v-bind="page" class="py-[50px]" />
<UPageBody>
<UBlogList>
<UBlogPost
v-for="(post, index) in posts"
:key="index"
:to="post._path"
:title="post.title"
:description="post.description"
:image="post.image"
:date="new Date(post.date).toLocaleDateString('en', { year: 'numeric', month: 'short', day: 'numeric' })"
:authors="post.authors"
:badge="post.badge"
:orientation="index === 0 ? 'horizontal' : 'vertical'"
:class="[index === 0 && 'col-span-full']"
:ui="{
description: 'line-clamp-2'
}"
/>
</UBlogList>
</UPageBody>
</UContainer>
</template>

25
pages/docs.vue Normal file
View File

@ -0,0 +1,25 @@
<script setup lang="ts">
import type { NavItem } from '@nuxt/content/dist/runtime/types'
const navigation = inject<Ref<NavItem[]>>('navigation', ref([]))
const links = computed(() => navigation.value.find((item) => item._path === '/docs')?.children ?? [])
</script>
<template>
<UContainer>
<UPage>
<template #left>
<UAside>
<template #top>
<UContentSearchButton class="rounded-md" size="sm" />
</template>
<UNavigationTree :links="mapContentNavigation(links)" />
</UAside>
</template>
<NuxtPage />
</UPage>
</UContainer>
</template>

49
pages/docs/[...slug].vue Normal file
View File

@ -0,0 +1,49 @@
<script setup lang="ts">
import { withoutTrailingSlash } from 'ufo'
const route = useRoute()
const { data: page } = await useAsyncData(route.path, () => queryContent(route.path).findOne())
if (!page.value) {
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
}
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => queryContent('/docs')
.where({ _extension: 'md', navigation: { $ne: false } })
.only(['title', 'description', '_path'])
.findSurround(withoutTrailingSlash(route.path))
, { default: () => [] })
useSeoMeta({
title: page.value.title,
ogTitle: page.value.title,
description: page.value.description,
ogDescription: page.value.description
})
defineOgImage({
component: 'Saas',
title: page.value.title,
description: page.value.description
})
const headline = computed(() => findPageHeadline(page.value!))
</script>
<template>
<UPage v-if="page">
<UPageHeader :title="page.title" :description="page.description" :links="page.links" :headline="headline" />
<UPageBody prose>
<ContentRenderer v-if="page.body" :value="page" />
<hr v-if="surround?.length">
<UContentSurround :surround="surround" />
</UPageBody>
<template v-if="page.toc !== false" #right>
<UContentToc :links="page.body?.toc?.links" />
</template>
</UPage>
</template>

142
pages/index.vue Normal file
View File

@ -0,0 +1,142 @@
<script setup lang="ts">
const { data: page } = await useAsyncData('index', () => queryContent('/').findOne())
if (!page.value) {
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
}
useSeoMeta({
titleTemplate: '',
title: page.value.title,
ogTitle: page.value.title,
description: page.value.description,
ogDescription: page.value.description
})
const items = [
'https://picsum.photos/1920/1080?random=1',
'https://picsum.photos/1920/1080?random=2',
'https://picsum.photos/1920/1080?random=3',
'https://picsum.photos/1920/1080?random=4',
'https://picsum.photos/1920/1080?random=5',
'https://picsum.photos/1920/1080?random=6'
]
const carouselRef = ref()
onMounted(() => {
setInterval(() => {
if (!carouselRef.value) return
if (carouselRef.value.page === carouselRef.value.pages) {
return carouselRef.value.select(0)
}
carouselRef.value.next()
}, 10000)
})
</script>
<template>
<div v-if="page">
<!-- <Galaxy /> -->
<Transition name="bounce">
<ULandingHero :title="page.hero.title" :description="page.hero.description" :links="page.hero.links">
<!-- <div
class="absolute inset-0 landing-grid z-[-1] [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)]"
/> -->
<template #headline>
<UBadge v-if="page.hero.headline" variant="subtle" size="lg" class="relative rounded-full font-semibold">
<NuxtLink :to="page.hero.headline.to" target="_blank" class="focus:outline-none" tabindex="-1">
<span class="absolute inset-0" aria-hidden="true" />
</NuxtLink>
{{ page.hero.headline.label }}
<UIcon
v-if="page.hero.headline.icon" :name="page.hero.headline.icon"
class="ml-1 w-4 h-4 pointer-events-none"
/>
</UBadge>
</template>
</ULandingHero>
</Transition>
<!-- <ULandingSection class="!pt-0">
<UCarousel
ref="carouselRef" v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }"
class="rounded-lg overflow-hidden" indicators
>
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</ULandingSection> -->
<ULandingSection
v-for="(section, index) in page.sections" :key="index" :title="section.title"
:description="section.description" :align="section.align" :features="section.features"
>
<Placeholder />
</ULandingSection>
<ULandingSection :title="page.features.title" :description="page.features.description">
<UPageGrid>
<ULandingCard v-for="(item, index) in page.features.items" :key="index" v-bind="item" />
</UPageGrid>
</ULandingSection>
<ULandingSection
:headline="page.testimonials.headline" :title="page.testimonials.title"
:description="page.testimonials.description"
>
<UPageColumns class="xl:columns-4">
<div v-for="(testimonial, index) in page.testimonials.items" :key="index" class="break-inside-avoid">
<ULandingTestimonial v-bind="testimonial" class="bg-gray-100/50 dark:bg-gray-800/50" />
</div>
</UPageColumns>
</ULandingSection>
<ULandingSection>
<ULandingCTA v-bind="page.cta" class="bg-gray-100/50 dark:bg-gray-800/50" />
</ULandingSection>
</div>
</template>
<style scoped>
.landing-grid {
background-size: 100px 100px;
background-image:
linear-gradient(to right, rgb(var(--color-gray-200)) 1px, transparent 1px),
linear-gradient(to bottom, rgb(var(--color-gray-200)) 1px, transparent 1px);
}
.dark {
.landing-grid {
background-image:
linear-gradient(to right, rgb(var(--color-gray-800)) 1px, transparent 1px),
linear-gradient(to bottom, rgb(var(--color-gray-800)) 1px, transparent 1px);
}
}
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
</style>

71
pages/login.vue Normal file
View File

@ -0,0 +1,71 @@
<script setup lang="ts">
definePageMeta({
layout: 'auth'
})
useSeoMeta({
title: 'Login'
})
const fields = [{
name: 'email',
type: 'text',
label: 'Email',
placeholder: 'Enter your email'
}, {
name: 'password',
label: 'Password',
type: 'password',
placeholder: 'Enter your password'
}]
const validate = (state: any) => {
const errors = []
if (!state.email) errors.push({ path: 'email', message: 'Email is required' })
if (!state.password) errors.push({ path: 'password', message: 'Password is required' })
return errors
}
const providers = [{
label: 'Continue with GitHub',
icon: 'i-simple-icons-github',
color: 'white' as const,
click: () => {
console.log('Redirect to GitHub')
}
}]
function onSubmit (data: any) {
console.log('Submitted', data)
}
</script>
<!-- eslint-disable vue/multiline-html-element-content-newline -->
<!-- eslint-disable vue/singleline-html-element-content-newline -->
<template>
<UCard class="max-w-sm w-full bg-white/75 dark:bg-white/5 backdrop-blur">
<UAuthForm
:fields="fields"
:validate="validate"
:providers="providers"
title="Welcome back"
align="top"
icon="i-heroicons-lock-closed"
:ui="{ base: 'text-center', footer: 'text-center' }"
:submit-button="{ trailingIcon: 'i-heroicons-arrow-right-20-solid' }"
@submit="onSubmit"
>
<template #description>
Don't have an account? <NuxtLink to="/signup" class="text-primary font-medium">Sign up</NuxtLink>.
</template>
<template #password-hint>
<NuxtLink to="/" class="text-primary font-medium">Forgot password?</NuxtLink>
</template>
<template #footer>
By signing in, you agree to our <NuxtLink to="/" class="text-primary font-medium">Terms of Service</NuxtLink>.
</template>
</UAuthForm>
</UCard>
</template>

47
pages/pricing.vue Normal file
View File

@ -0,0 +1,47 @@
<script setup lang="ts">
const { data: page } = await useAsyncData('pricing', () => queryContent('/pricing').findOne())
if (!page.value) {
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
}
useSeoMeta({
title: page.value.title,
ogTitle: page.value.title,
description: page.value.description,
ogDescription: page.value.description
})
defineOgImage({
component: 'Saas',
title: page.value.title,
description: page.value.description
})
const isYearly = ref(false)
</script>
<template>
<div v-if="page">
<UPageHero v-bind="page.hero">
<template #links>
<UPricingToggle v-model="isYearly" class="w-48" />
</template>
</UPageHero>
<UContainer>
<UPricingGrid>
<UPricingCard v-for="(plan, index) in page.plans" :key="index" v-bind="plan" :price="isYearly ? plan.price.year : plan.price.month" :cycle="isYearly ? '/year' : '/month'" />
</UPricingGrid>
</UContainer>
<ULandingSection>
<ULandingLogos>
<UIcon v-for="icon in page.logos.icons" :key="icon" :name="icon" class="w-12 h-12 flex-shrink-0 text-gray-500 dark:text-gray-400" />
</ULandingLogos>
</ULandingSection>
<ULandingSection :title="page.faq.title" :description="page.faq.description">
<ULandingFAQ :items="page.faq.items" multiple default-open class="max-w-4xl mx-auto" />
</ULandingSection>
</div>
</template>

71
pages/signup.vue Normal file
View File

@ -0,0 +1,71 @@
<script setup lang="ts">
definePageMeta({
layout: 'auth'
})
useSeoMeta({
title: 'Sign up'
})
const fields = [{
name: 'name',
type: 'text',
label: 'Name',
placeholder: 'Enter your name'
}, {
name: 'email',
type: 'text',
label: 'Email',
placeholder: 'Enter your email'
}, {
name: 'password',
label: 'Password',
type: 'password',
placeholder: 'Enter your password'
}]
const validate = (state: any) => {
const errors = []
if (!state.email) errors.push({ path: 'email', message: 'Email is required' })
if (!state.password) errors.push({ path: 'password', message: 'Password is required' })
return errors
}
const providers = [{
label: 'Continue with GitHub',
icon: 'i-simple-icons-github',
color: 'gray' as const,
click: () => {
console.log('Redirect to GitHub')
}
}]
function onSubmit (data: any) {
console.log('Submitted', data)
}
</script>
<!-- eslint-disable vue/multiline-html-element-content-newline -->
<!-- eslint-disable vue/singleline-html-element-content-newline -->
<template>
<UCard class="max-w-sm w-full bg-white/75 dark:bg-white/5 backdrop-blur">
<UAuthForm
:fields="fields"
:validate="validate"
:providers="providers"
align="top"
title="Create an account"
:ui="{ base: 'text-center', footer: 'text-center' }"
:submit-button="{ label: 'Create account' }"
@submit="onSubmit"
>
<template #description>
Already have an account? <NuxtLink to="/login" class="text-primary font-medium">Login</NuxtLink>.
</template>
<template #footer>
By signing up, you agree to our <NuxtLink to="/" class="text-primary font-medium">Terms of Service</NuxtLink>.
</template>
</UAuthForm>
</UCard>
</template>

11102
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/social-card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

5
renovate.json Normal file
View File

@ -0,0 +1,5 @@
{
"extends": [
"github>nuxt/renovate-config-nuxt"
]
}

View File

@ -0,0 +1,5 @@
import { serverQueryContent } from '#content/server'
export default eventHandler(async (event) => {
return serverQueryContent(event).where({ _type: 'markdown', navigation: { $ne: false } }).find()
})

3
server/tsconfig.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

12
tailwind.config.ts Normal file
View File

@ -0,0 +1,12 @@
import type { Config } from 'tailwindcss'
import defaultTheme from 'tailwindcss/defaultTheme'
export default <Partial<Config>>{
theme: {
extend: {
fontFamily: {
sans: ['DM Sans', ...defaultTheme.fontFamily.sans]
}
}
}
}

4
tsconfig.json Normal file
View File

@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}

14
types/index.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
export interface BlogPost extends ParsedContent {
title: string
description: string
date: string
image?: HTMLImageElement
badge?: Badge
authors?: ({
name: string
description?: string
avatar?: Avatar
} & Link)[]
}