mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-23 07:41:02 +08:00
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:
parent
34a0868b26
commit
5d77b2770d
27
.env.example
27
.env.example
@ -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=""
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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.",
|
||||
|
@ -37,7 +37,7 @@
|
||||
"find": "尋找",
|
||||
"interface": "使用者介面",
|
||||
"documentation": "教學"
|
||||
},
|
||||
},
|
||||
"description": {
|
||||
"find": "你可以輕鬆地從不同的新聞來源找到相同的主題。",
|
||||
"interface": "這個網站使用類似 Xfce / MacOS / DSM 的介面,讓你可以簡單使用這個網站。",
|
||||
|
15
layouts/docs.vue
Normal file
15
layouts/docs.vue
Normal 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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
6
public/geterrorassets/noImageLogo.svg
Normal file
6
public/geterrorassets/noImageLogo.svg
Normal 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 |
3
public/logos/GitHub-dark.svg
Normal file
3
public/logos/GitHub-dark.svg
Normal 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 |
@ -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",
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user