It's so bad that it works... AI: feat: update environment variables in .env.example; enhance chatbot and hotnews components; improve sources layout and image handling; add new logo assets

This commit is contained in:
吳元皓 2025-05-13 15:47:02 +08:00
parent 34a0868b26
commit 5d77b2770d
13 changed files with 168 additions and 47 deletions

View File

@ -3,15 +3,24 @@
# If you are using Windows, run ps1 clone-env.ps1 in the terminal.
# This is the default .env file.
S3_ACCESS_KEY=
S3_SECRET_KEY=
S3_BUCKETNAME=
S3_ENDPOINT=
POSTGRES_URL=
# S3 INFO
S3_ACCESS_KEY=""
S3_SECRET_KEY=""
S3_BUCKETNAME=""
S3_ENDPOINT=""
GROQ_API_KEY=
# GITHUB OAUTH (NOT WORKING 4n)
NUXT_GITHUB_CLIENT_ID=""
NUXT_GITHUB_CLIENT_SECRET=""
# GLOBAL DATABASE
POSTGRES_URL=""
# GROQ API KEY
GROQ_API_KEY=""
# PASSWORD SALT
PASSWORD_HASH_SALT=""
# SCRAPING
@ -19,4 +28,8 @@ POSTGRES_DB=""
POSTGRES_USER=""
POSTGRES_PASSWORD=""
POSTGRES_HOST=""
POSTGRES_PORT=""
POSTGRES_PORT=""
# CF TURNSTILE
NUXT_CF_TURNSTILE_SITE_KEY=""
NUXT_CF_TURNSTILE_SECRET_KEY=""

View File

@ -12,18 +12,20 @@ onMounted(() => {
<template>
<div class="justify-center align-center text-center flex flex-col">
<div class="flex flex-row justify-between">
<div>Chat Name</div>
<div>Chat Name</div>
<div class="flex flex-row justify-center align-center text-center">
<div class="flex flex-row justify-center align-center text-center gap-2">
<button class="p-2 rounded-lg hover:bg-gray-300">
<History class="h-4 w-4" />
</button>
<button class="p-2 rounded-lg hover:bg-gray-300">
<Plus class="h-4 w-4" />
</button>
<div
class="flex flex-row justify-center align-center text-center gap-2"
>
<button class="p-2 rounded-lg hover:bg-gray-300">
<History class="h-4 w-4" />
</button>
<button class="p-2 rounded-lg hover:bg-gray-300">
<Plus class="h-4 w-4" />
</button>
</div>
</div>
</div>
<hr/>
<hr />
</div>
</template>

View File

@ -13,9 +13,7 @@ try {
}
</script>
<template>
<div v-if="!ffeed">
Loading...
</div>
<div v-if="!ffeed">Loading...</div>
<div
v-for="item in ffeed"
class="justify-center align-center text-center p-4 border border-black rounded-lg m-4"

View File

@ -1,6 +1,13 @@
<script setup lang="ts">
import noImageLogo from "~/public/geterrorassets/noImageLogo.svg";
const { t, locale } = useI18n();
const emit = defineEmits(['windowopener'])
const openNewWindow = (windowName: string) => {
emit('windowopener', windowName)
}
const {
data: source,
pending,
@ -14,13 +21,52 @@ const {
lang: locale,
},
});
async function getImageSource(image: string) {
console.log(image);
if (!image || image === "#") {
return noImageLogo;
}
try {
const checkImgWorks = await fetch(image, {
method: "GET",
});
if (checkImgWorks.status === 200) {
return image;
} else {
return noImageLogo;
}
} catch (error) {
return noImageLogo;
}
}
const imageUrls = ref<{ [key: string]: string }>({});
// Load image sources when component mounts
onMounted(async () => {
if (source.value?.data) {
for (const item of source.value.data) {
imageUrls.value[item.id] = await getImageSource(item.logo);
}
}
});
</script>
<template>
<div>
<div v-for="item in source?.data" :key="item.id">
<h1>{{ item.title }}</h1>
<span>{{ item.description }}</span>
<a :href="item.url">{{ item.url }}</a>
<div class="flex flex-row flexw-wrap justify-center gap-2">
<div
class="flex flex-col group bg-gray-900/30 rounded-xl p-3 transition-all duration-500 shadow-lg hover:translate-y-[-2px] ransition-all duration-700"
v-for="item in source?.data"
:key="item.id"
>
<button @click="openNewWindow('chatbot')">
<img
:src="imageUrls[item.id] || noImageLogo"
alt="Organization Logo"
class="w-16 h-16 rounded-xl"
/>
<h1>{{ item.title }}</h1>
<span>{{ item.description }}</span>
</button>
</div>
</div>
</template>

View File

@ -37,7 +37,7 @@
"find": "Find",
"interface": "Interface",
"documentation": "Documentation"
},
},
"description": {
"find": "You can easily find the same topic from different news sources.",
"interface": "Use a desktop like interface, the one that you already got used to.",

View File

@ -37,7 +37,7 @@
"find": "尋找",
"interface": "使用者介面",
"documentation": "教學"
},
},
"description": {
"find": "你可以輕鬆地從不同的新聞來源找到相同的主題。",
"interface": "這個網站使用類似 Xfce / MacOS / DSM 的介面,讓你可以簡單使用這個網站。",

15
layouts/docs.vue Normal file
View File

@ -0,0 +1,15 @@
<script setup lang="ts">
import Navigation from "~/components/navigation.vue";
import Footer from "~/components/footer.vue";
</script>
<template>
<nav>
<Navigation />
</nav>
<main class="pt-[60px]">
<slot />
</main>
<footer>
<Footer />
</footer>
</template>

View File

@ -66,6 +66,7 @@ const openAppNameQuery = ref();
const currentOpenAppId = ref(0);
const progress = ref(0);
const titleAppName = ref("Desktop");
const openingAppViaAnApp = ref(false);
// Key Data
const menuItems = [
@ -100,6 +101,8 @@ const associAppWindow = [
id: "3",
title: t("app.sources"),
component: SourcesWindow,
width: "700px",
height: "500px",
},
{
name: "about",
@ -236,14 +239,16 @@ const findAndOpenWindow = (windowName: string) => {
};
const obtainTopWindowPosition = (windowId: string) => {
const windowIndex = activeWindows.value.findIndex(
if (!openingAppViaAnApp.value) {
const windowIndex = activeWindows.value.findIndex(
(window) => window.id === windowId,
);
console.log(windowIndex);
if (windowIndex !== -1) {
const [window] = activeWindows.value.splice(windowIndex, 1);
titleAppName.value = window.name;
activeWindows.value.push(window);
}
}
};
const closeWindow = (windowId: string) => {
@ -253,6 +258,14 @@ const closeWindow = (windowId: string) => {
console.log("activeWindows.value", activeWindows.value);
};
const openNewWindowViaApp = (windowId: string) => {
openingAppViaAnApp.value = true;
findAndOpenWindow(windowId);
setTimeout(() => {
openingAppViaAnApp.value = false;
},1000);
}
const maxWindow = (windowId: string) => {};
// Title
@ -416,6 +429,7 @@ watchEffect((cleanupFn) => {
<Component
:is="window.component"
@error="console.error('Error:', $event)"
@windowopener="openNewWindowViaApp($event)"
/>
</Suspense>
</DraggableWindow>

View File

@ -1,4 +1,6 @@
<template>
<div class="h-[200px]"></div>
<h1 class="text-4xl text-center align-middle">Documentation is currently not available.</h1>
</template>
<div class="h-[200px]"></div>
<h1 class="text-4xl text-center align-middle">
Documentation is currently not available.
</h1>
</template>

View File

@ -5,7 +5,13 @@ import {
AccordionItem,
AccordionTrigger,
} from "~/components/ui/accordion";
import { ComputerDesktopIcon, CircleStackIcon, MagnifyingGlassIcon, ViewfinderCircleIcon, DocumentDuplicateIcon } from "@heroicons/vue/24/outline";
import {
ComputerDesktopIcon,
CircleStackIcon,
MagnifyingGlassIcon,
ViewfinderCircleIcon,
DocumentDuplicateIcon,
} from "@heroicons/vue/24/outline";
import { gsap } from "gsap";
import { TextPlugin } from "gsap/TextPlugin";
gsap.registerPlugin(TextPlugin);
@ -26,19 +32,19 @@ const cards = [
{
icon: ViewfinderCircleIcon,
title: t("home.cards.title.find"),
description: t("home.cards.description.find"),
description: t("home.cards.description.find"),
},
{
icon: ComputerDesktopIcon,
title: t("home.cards.title.interface"),
title: t("home.cards.title.interface"),
description: t("home.cards.description.interface"),
},
{
{
icon: DocumentDuplicateIcon,
title: t("home.cards.title.documentation"),
description: t("home.cards.description.documentation"),
title: t("home.cards.title.documentation"),
description: t("home.cards.description.documentation"),
},
]
];
const accordionItems = [
{
@ -133,18 +139,31 @@ onMounted(() => {
<div class="h-screen"></div>
<div id="learnmore"></div>
<div class="h-[100px]"></div>
<div class="">
<div class="justify-center align-center gap-2 p-2 w-full flex flex-row flex-wrap" id="cards">
<div class="px-10 bg-gray-900/70 w-[300px] h-[200px] rounded-xl shadow-lg hover:shadow-sky-600/90 backdrop-blur-sm border border-gray-800 hover:border-gray-600/70 transition-all duration-700 justify-center align-middle flex flex-col" v-for="item in cards" :key="item.title">
<component :is="item.icon" class="w-8 h-8" />
<h3 class="text-xl font-bold">{{ item.title }}</h3>
<p class="">{{ item.description }}</p>
<div
class="justify-center align-center gap-2 p-2 w-full flex flex-row flex-wrap relative"
id="cards"
>
<div
class="px-10 bg-gray-900/70 w-[300px] h-[200px] group rounded-xl shadow-lg hover:shadow-sky-600/90 backdrop-blur-sm border border-gray-800 hover:border-gray-600/70 transition-all duration-700 justify-center align-middle flex flex-col"
v-for="item in cards"
:key="item.title"
>
<component
:is="item.icon"
class="w-8 h-8 text-white group-hover:text-sky-500 transition-colors duration-300"
/>
<h3 class="text-xl font-bold">{{ item.title }}</h3>
<p
class="text-gray-400 group-hover:text-gray-300 transition-colors duration-300"
>
{{ item.description }}
</p>
</div>
</div>
<br />
<div class="justify-center align-center text-center w-full flex">
<div
class="bg-gray-900/60 rounded-xl shadow-lg p-8 border border-gray-800 hover:border-sky-700 m-4 w-1/2 align-center justify-center text-center duration-700"
class="bg-gray-900/60 rounded-xl shadow-lg p-8 border border-gray-800 hover:border-sky-700 m-4 w-full md:w-1/2 align-center justify-center text-center duration-700"
>
<span class="text-2xl font-bold">Q/A</span>
<Accordion
@ -157,7 +176,9 @@ onMounted(() => {
:key="item.value"
:value="item.value"
>
<AccordionTrigger>{{ item.title }}</AccordionTrigger>
<AccordionTrigger class="transition-all duration-100">{{
item.title
}}</AccordionTrigger>
<AccordionContent>
{{ item.content }}
</AccordionContent>
@ -166,5 +187,4 @@ onMounted(() => {
</div>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,6 @@
<svg width='16' height='16' xmlns='http://www.w3.org/2000/svg'>
<rect width='100%' height='100%' fill='#333'/>
<text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='#666' font-size='4'>
無圖片
</text>
</svg>

After

Width:  |  Height:  |  Size: 252 B

View File

@ -0,0 +1,3 @@
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" transform="scale(64)" fill="#ffff"/>
</svg>

After

Width:  |  Height:  |  Size: 966 B

View File

@ -11,12 +11,14 @@ export default defineEventHandler(async (event) => {
{
id: 1,
title: "Source 1",
logo: "#",
url: "https://source1.com",
description: "Description for Source 1",
},
{
id: 2,
title: "Source 2",
logo: "#",
url: "https://source2.com",
description: "Description for Source 2",
},