new style added

This commit is contained in:
ed barz 2023-07-24 09:56:00 +02:00
parent ea56388fbf
commit 51d2459460
42 changed files with 1884 additions and 135 deletions

869
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,13 @@
"type": "module",
"dependencies": {
"cookie": "^0.5.0",
"dotenv": "^16.0.3",
"dotenv": "^16.1.4",
"langchain": "^0.0.95",
"normalize.css": "^8.0.1",
"openai": "^3.3.0",
"pg": "^8.11.0",
"pg-promise": "^11.5.0",
"svelte-cookie": "^1.0.1",
"svelte-navigator": "^3.2.2"
}
}

View File

@ -4,7 +4,8 @@
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="stylesheet" href="https://file.brz9.dev/cdn/fonts/base/inter.css">
<link rel="stylesheet" href="https://file.brz9.dev/cdn/fonts/brz9-v2.2/brz9-icon-v2.2.css">
<link rel="stylesheet" href="https://file.brz9.dev/cdn/fonts/base/mononoki.css">
<link rel="stylesheet" href="https://file.brz9.dev/cdn/fonts/brz9-v2.3/brz9-v2.3.css">
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>

View File

@ -1,5 +1,5 @@
<!-- Footer.svelte -->
<footer class="page-footer">
<p>Copyright © 2023 My Website</p>
<p>Made with ♥ by <a href="https://ed.brz9.dev/" target="_blank">edbrz9</a> - <a href="/about" target="_blank">About</a></p>
</footer>

View File

@ -3,6 +3,23 @@
export let isLoggedIn = false;
export let showUserNav = true;
export let showHome = true;
export let breadcrumb = [];
import { t } from "$lib/i18n";
import { setCookie, deleteCookie } from 'svelte-cookie';
import { locale, locales } from "$lib/i18n";
let selectedLanguage = "";
function handleLanguageSelection() {
deleteCookie('lang'); // Delete the existing 'lang' cookie
setCookie('lang', selectedLanguage); // Set the new 'lang' cookie
location.reload();
}
$: {
selectedLanguage = $locale;
}
</script>
<header>
@ -15,6 +32,18 @@
{:else}
<span></span>
{/if}
{#if breadcrumb.length > 0}
{#each breadcrumb as item}
<span class="breadslash">/</span>
{#if item.href}
<a href={item.href}>
{item.name}
</a>
{:else}
<span>{item.name}</span>
{/if}
{/each}
{/if}
</div>
<div class="nav-right">
{#if showUserNav}
@ -27,7 +56,9 @@
</a>
{:else}
<a href="/login">
<span class="brz9-icon-enter"></span>
<button class="btn-fly">
{$t("user.login")}
</button>
</a>
{/if}
@ -36,6 +67,13 @@
<span></span>
{/if}
<select class="lang-pick" bind:value={selectedLanguage} on:change={handleLanguageSelection}>
<option value="en">EN</option>
<option value="fr">FR</option>
<option value="es">ES</option>
<option value="ru">РУ</option>
</select>
</div>
</nav>

View File

@ -0,0 +1,62 @@
<script>
import { onMount, onDestroy } from 'svelte';
export let text = '';
let lines = [''];
let index = 0;
let delay = 100;
let intervalId;
text = text.normalize(); // normalize the text
text = text + ' ';
function update() {
if (index + 1 < text.length) {
if (text[index] === '\\' && text[index+1] === 'n') {
lines = [...lines, ''];
index += 1;
} else {
lines[lines.length - 1] += text[index];
}
index++;
}
if (index < text.length) {
delay = Math.max(10, delay - 1); // lower limit of delay
intervalId = setTimeout(update, delay + Math.random() * 5);
}
}
onMount(() => {
intervalId = setTimeout(update, delay);
});
onDestroy(() => {
if (intervalId) {
clearTimeout(intervalId);
}
});
</script>
<style lang="scss">
@import '../styles/variables.scss';
.cursor {
border-right: 3px solid $fg-color;
animation: blink 1s steps(1) infinite;
}
span.line {
display: inline-block;
width: 100%;
}
@keyframes blink {
50% { opacity: 0; }
}
</style>
<div>
{#each lines as line, i (i)}
<span class="line">{line}{#if i === lines.length - 1}<span class="cursor">&nbsp;</span>{/if}</span>{#if i < lines.length - 1}<br>{/if}
{/each}
</div>

View File

@ -0,0 +1,41 @@
<!-- src/components/NoteForm.svelte -->
<script>
export let sessionID; // receive sessionID as a prop
let title = '';
let body = '';
async function handleSubmit() {
const apiUrl = import.meta.env.VITE_API_URL;
const res = await fetch(apiUrl + '/userland/notes', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + sessionID,
'Content-Type': 'application/json'
},
body: JSON.stringify({ title, body })
});
if (res.ok) {
// If the request was successful, clear the form
title = '';
body = '';
// You might also want to refresh the list of notes
} else {
// Handle error
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<label>
Title:
<input type="text" bind:value={title} required />
</label>
<label>
Body:
<textarea bind:value={body} required></textarea>
</label>
<button type="submit">Create Note</button>
</form>

View File

@ -1,6 +1,8 @@
export async function signup(email, password) {
const apiUrl = import.meta.env.VITE_API_URL;
console.log("API URL: ", apiUrl)
const response = await fetch(apiUrl + '/signup', {
method: 'POST',
headers: {

50
src/lib/i18n.js Normal file
View File

@ -0,0 +1,50 @@
import { derived, writable } from "svelte/store";
import translations from "$lib/translations";
import { getCookie, setCookie } from "svelte-cookie";
// Function to retrieve the initial language
function getInitialLanguage() {
if (typeof window === "undefined") {
// Server-side context
return "en"; // Set the default language for server-side rendering
} else {
// Client-side context
return getCookie("lang") || "en"; // Get the language from the cookie or default to "en"
}
}
export const locale = writable(getInitialLanguage());
export const locales = Object.keys(translations);
function translate(locale, key, vars) {
// Let's throw some errors if we're trying to use keys/locales that don't exist.
// We could improve this by using Typescript and/or fallback values.
if (!key) throw new Error("no key provided to $t()");
if (!locale) throw new Error(`no translation for key "${key}"`);
// Grab the translation from the translations object.
let text = translations[locale][key];
if (!text) throw new Error(`no translation found for ${locale}.${key}`);
// Replace any passed in variables in the translation string.
Object.keys(vars).map((k) => {
const regex = new RegExp(`{{${k}}}`, "g");
text = text.replace(regex, vars[k]);
});
return text;
}
export const t = derived(locale, ($locale) => (key, vars = {}) =>
translate($locale, key, vars)
);
// Function to update the language and set the cookie
export function setLanguage(lang) {
if (typeof window !== "undefined") {
// Client-side context
setCookie("lang", lang); // Set the new 'lang' cookie
}
locale.set(lang); // Update the language in the store
}

55
src/lib/pgm.js Normal file
View File

@ -0,0 +1,55 @@
import pgPromise from 'pg-promise';
const pgp = pgPromise();
class PGM {
constructor(config) {
this.db = pgp(config);
}
async newChatSesh(userID, chatseshname) {
const chat_sesh = await this.db.one('INSERT INTO chat_sesh(user_id, name) VALUES($1, $2) RETURNING *', [userID, chatseshname]);
return new ChatSesh(this.db, chat_sesh);
}
async openChatSesh(userID, chatseshname) {
const chat_sesh = await this.db.one('SELECT * FROM chat_sesh WHERE user_id = $1 AND name = $2', [userID, chatseshname]);
return new ChatSesh(this.db, chat_sesh);
}
async getUserSeshs(userID) {
const seshs = await this.db.any('SELECT id, name FROM chat_sesh WHERE user_id = $1', [userID]);
return seshs;
}
async checkConnection() {
try {
await this.db.query('SELECT NOW()');
return 'Connection to database successful!';
} catch (error) {
return 'Failed to connect to database: ' + error.message;
}
}
}
class ChatSesh {
constructor(db, sesh) {
this.db = db;
this.sesh = sesh;
}
async addUserMessage(message) {
return this.db.none('INSERT INTO chat_history(sesh_id, user, message) VALUES($1, $2, $3)', [this.sesh.id, 'user', message]);
}
async addBotMessage(message) {
return this.db.none('INSERT INTO chat_history(sesh_id, user, message) VALUES($1, $2, $3)', [this.sesh.id, 'bot', message]);
}
async getHistory() {
return this.db.any('SELECT user, message FROM chat_history WHERE sesh_id = $1 ORDER BY id', [this.sesh.id]);
}
}
export { PGM };

27
src/lib/translations.js Normal file
View File

@ -0,0 +1,27 @@
export default {
en: {
"homepage.title": "Welcome on brz9's AI playground",
"homepage.time": "The current time is: {{time}}",
"homepage.welcome": "This is still a work in progress.\n\nSign up is currently closed but leave us your email and we'll let you know when we open it up!",
"user.login": "Login",
"user.logout": "Logout",
},
es: {
"homepage.title": "Bienvenido al patio de recreo de la IA de brz9",
"user.login": "Iniciar sesión",
"user.logout": "Cerrar sesión",
},
fr: {
"homepage.title": "Bienvenue sur l'IA de brz9",
"homepage.welcome": "Ceci est toujours en cours de développement.\n\nL'inscription est actuellement fermée mais laissez-nous votre email et nous vous informerons quand nous l'ouvrirons !",
"user.login": "Se connecter",
"user.logout": "Se déconnecter",
},
ru: {
"homepage.title": "Добро пожаловать на площадку искусственного интеллекта brz9",
"homepage.welcome" : "Это все еще находится в процессе разработки.\n\nРегистрация в настоящее время закрыта, но оставьте нам свой адрес электронной почты, и мы сообщим вам, когда откроем ее!",
"user.login": "Авторизоваться",
"user.logout": "Выйти",
},
};

View File

@ -1,23 +1,8 @@
console.log("Hello from layout.server.js");
import { locale } from "$lib/i18n";
export const load = async (event) => {
const sessionID = event.cookies.get("sessionID");
console.log("SessionID: ", sessionID);
const apiUrl = import.meta.env.VITE_API_URL;
const res = await fetch(apiUrl + '/edible', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionID
}
});
if (res.ok) {
const data = await res.json();
const userID = data.userID;
console.log("UserID: ", userID); // Here's the userID
} else {
console.log("Invalid session or other error.");
}
};
const lang = event.cookies.get("lang");
locale.set(lang || "en");
};

View File

@ -1,6 +1,14 @@
<script>
import "../styles/master.scss";
import 'normalize.css';
import Footer from '$components/Footer.svelte';
import Header from '$components/Header.svelte';
let isLoggedIn = false;
</script>
<slot></slot>
<slot></slot>
<Footer />

View File

@ -0,0 +1,20 @@
export const load = async (event) => {
const sessionID = event.cookies.get('sessionID');
if (sessionID) {
console.log("is logged in")
return {
post: {
isLoggedIn: true,
}
};
} else {
console.log("is not logged in")
return {
post: {
isLoggedIn: false,
}
};
}
};

View File

@ -4,76 +4,23 @@
<script>
import Header from '$components/Header.svelte';
import Footer from '$components/Footer.svelte';
import { t } from "$lib/i18n";
import TypeWriter from '$components/TypeWriter.svelte';
let text = '';
const apiUrl = import.meta.env.VITE_API_URL;
async function createNote() {
const response = await fetch(apiUrl + '/notes', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ text }),
});
if (response.ok) {
// The note was successfully created
text = '';
alert('Note created successfully');
} else {
// There was an error creating the note
alert('Failed to create note');
}
}
let notes = [];
async function fetchNotes() {
const response = await fetch(apiUrl + '/getnotes');
if (response.ok) {
notes = await response.json();
} else {
console.error('Failed to fetch notes');
}
}
// Fetch notes when the component is first rendered
fetchNotes();
export let showHome = false;
export let data;
//export let isLoggedIn;
export let isLoggedIn = data.post.isLoggedIn;
console.log(data);
console.log(isLoggedIn);
</script>
<Header {showHome}/>
<Header { showHome } { isLoggedIn }/>
<main>
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<ul>
<li><a href="/about">About</a></li>
<li><a href="/login">Login</a></li>
<li><a href="/signup">Signup</a></li>
</ul>
<h1>{$t("homepage.title")}!</h1>
<input bind:value={text} type="text" placeholder="Enter note text" />
<button on:click={createNote}>Create Note</button>
<br>
<button class="blob-button" on:click={fetchNotes}>Refresh Notes</button>
<TypeWriter text={$t("homepage.welcome")}/>
<ul>
{#each notes as note}
<li>{note.Text}</li>
{/each}
</ul>
<h4>Just to check, the current API endpoint is : {apiUrl}</h4>
</main>
<Footer/>
<style>
ul li {
max-width: 300px;
line-break: anywhere;
}
</style>
</main>

View File

@ -1,5 +1,14 @@
<script>
import Header from "../../components/Header.svelte";
</script>
<svelte:head>
<title>About</title>
</svelte:head>
<h1>About</h1>
<Header/>
<main>
<h1>About</h1>
</main>

View File

@ -2,11 +2,11 @@ import { redirect } from "@sveltejs/kit";
export const load = async (event) => {
console.log("hello from backend")
//const sessionID = event.cookies.get('sessionID');
const sessionID = event.cookies.get('sessionID');
//if (sessionID) {
// throw redirect('/profile');
//}
if (sessionID) {
throw redirect(301,'/profile');
}
};
export const actions = {

View File

@ -5,7 +5,6 @@
<script>
import Modal from '$components/Modal.svelte';
import Header from '$components/Header.svelte';
import Footer from '$components/Footer.svelte';
import { onMount } from 'svelte';
let errorMessage = '';
@ -51,5 +50,4 @@
</Modal>
{/if}
</main>
<Footer />
</main>

View File

@ -0,0 +1,81 @@
<svelte:head>
<title>Home</title>
</svelte:head>
<script>
import Header from '$components/Header.svelte';
import Footer from '$components/Footer.svelte';
import TypeWriter from '$components/TypeWriter.svelte';
let text = '';
const apiUrl = import.meta.env.VITE_API_URL;
async function createNote() {
const response = await fetch(apiUrl + '/notes', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ text }),
});
if (response.ok) {
// The note was successfully created
text = '';
alert('Note created successfully');
} else {
// There was an error creating the note
alert('Failed to create note');
}
}
let notes = [];
async function fetchNotes() {
const response = await fetch(apiUrl + '/getnotes');
if (response.ok) {
notes = await response.json();
} else {
console.error('Failed to fetch notes');
}
}
// Fetch notes when the component is first rendered
fetchNotes();
export let showHome = false;
</script>
<Header {showHome}/>
<main>
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<ul>
<li><a href="/about">About</a></li>
<li><a href="/login">Login</a></li>
<li><a href="/signup">Signup</a></li>
</ul>
<input bind:value={text} type="text" placeholder="Enter note text" />
<button on:click={createNote}>Create Note</button>
<br>
<button class="blob-button" on:click={fetchNotes}>Refresh Notes</button>
<ul>
{#each notes as note}
<li>{note.Text}</li>
{/each}
</ul>
<TypeWriter text="This is a test for the typewriter component"/>
</main>
<Footer/>
<style>
ul li {
max-width: 300px;
line-break: anywhere;
}
</style>

View File

@ -1,23 +1,28 @@
import { redirect } from "@sveltejs/kit";
/** @type {import('./$types').PageLoad} */
export const load = async (event) => {
const sessionID = event.cookies.get("sessionID");
console.log("SessionID: ", sessionID);
console.log("SessionID???: ", sessionID);
const apiUrl = import.meta.env.VITE_API_URL;
const res = await fetch(apiUrl + '/edible', {
const res = await fetch(apiUrl + '/userland', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionID
}
});
const data = await res.json();
if (res.ok) {
const responseBody = await res.json();
console.log(responseBody); // log the entire response body
const { userID } = responseBody;
console.log(userID);
console.log(data)
return {
post: {
user_mail: data.user
}
};
} else {
throw redirect(301,'/login?error='+ encodeURIComponent("You must be logged in to view this page."));
}
};
};

View File

@ -1,22 +1,21 @@
<script>
/** @type {import('./$types').PageData} */
import Header from '$components/Header.svelte';
import Footer from '$components/Footer.svelte';
export let data;
export let isLoggedIn = true
</script>
<Header />
<Header { isLoggedIn }/>
<main>
<h2>Profile</h2>
<h4><a href="/profile/notes">Go to notes</a></h4>
<h4><a href="/profile/spaces">My AI Spaces</a></h4>
{#if data}
<p>User Email: {data.post.user_mail}</p>
{:else}
<p>Loading...</p>
{/if}
</main>
<Footer />
<!-- Path: src/routes/profile/+page.svelte
{#if user}
<h1>Welcome, {user.name}!</h1>
<p>Your email is {user.email}.</p>
{:else}
<p>Loading...</p>
{/if}
-->

View File

@ -0,0 +1,37 @@
/** @type {import('./$types').Actions} */
import { OpenAI } from "langchain/llms";
import * as dotenv from 'dotenv';
dotenv.config();
if (process.env.NODE_ENV == 'production') {
OPENAI_API_KEY = process.env.OPENAI_API_KEY;
}
export async function load(event) {
return {
test : "123",
}
}
export const actions = {
generate: async ({request}) => {
const form = await request.formData();
const prompt = form.get('prompt');
const model = new OpenAI({ temperature: 0.9 });
const res = await model.call(prompt);
//const res = "Just pretend I'm a real AI. I'm not, that's just for testing and styling purposes.";
//console.log(prompt);
//const res = "Was that your prompt? " + prompt;
return {
prompt: prompt,
generated: res
}
}
};

View File

@ -0,0 +1,45 @@
<script>
import Header from '$components/Header.svelte';
import Footer from '$components/Footer.svelte';
export let form;
export let data;
</script>
<Header {data}></Header>
<main>
<p>{data.test}</p>
<p>{data.lang}</p>
{#if form?.generated != undefined}
<section class="aichat">
<div class="user">
<aside>
<span class="brz9-icon-user"></span>
</aside>
<div class="msg">
<p>{form?.prompt}</p>
</div>
</div>
<div class="gpt">
<aside>
<span class="brz9-icon-openai"></span>
</aside>
<div class="msg">
<p>{form?.generated}</p>
</div>
</div>
</section>
{/if}
<form id="promptForm" action="?/generate" class="colonel" method="POST">
<textarea class="prompt-box" id="prompt" name="prompt"></textarea>
<button formaction="?/generate" class="btn-fly">Send</button>
</form>
</main>
<Footer></Footer>

View File

@ -0,0 +1,27 @@
import { redirect } from "@sveltejs/kit";
export const load = async (event) => {
const sessionID = event.cookies.get("sessionID");
console.log("SessionID???: ", sessionID);
const apiUrl = import.meta.env.VITE_API_URL;
const res = await fetch(apiUrl + '/userland/notes', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionID
}
});
const data = await res.json();
if (res.ok) {
console.log(data)
return {
post: {
notes: data.notes,
sesh_id: sessionID
}
};
} else {
throw redirect(301,'/login?error='+ encodeURIComponent("You must be logged in to view this page."));
}
};

View File

@ -0,0 +1,28 @@
<!-- src/routes/profile/notes/+page.svelte -->
<script>
import Header from '$components/Header.svelte';
import Footer from '$components/Footer.svelte';
import UserNoteForm from '$components/UserNoteForm.svelte';
export let data;
</script>
<Header />
<main>
<h2>Your Notes</h2>
{#each data.post.notes as note}
<h3>{note.Title}</h3>
<p>{note.Body}</p>
<p>{note.ID}</p>
{/each}
<p>Session ID: {data.post.sesh_id}</p>
<h2>Create a Note</h2>
<UserNoteForm sessionID={data.post.sesh_id} />
</main>
<Footer />

View File

@ -0,0 +1,27 @@
import { redirect } from "@sveltejs/kit";
export const load = async (event) => {
const sessionID = event.cookies.get("sessionID");
console.log("SessionID???: ", sessionID);
const apiUrl = import.meta.env.VITE_API_URL;
const res = await fetch(apiUrl + '/userland/spaces', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionID
}
});
const data = await res.json();
if (res.ok) {
console.log(data)
return {
post: {
spaces: data.spaces,
sesh_id: sessionID
}
};
} else {
throw redirect(301,'/login?error='+ encodeURIComponent("You must be logged in to view this page."));
}
};

View File

@ -0,0 +1,19 @@
<script>
export let data;
</script>
<main>
<h1>this is the chat section</h1>
<h4>here we will list the spaces</h4>
{#each data.post.spaces as space}
<h3>{space.Name}</h3>
<p>ID: {space.ID}</p>
<p>Slug: {space.Slug}</p>
<a href="./spaces/{space.ID}">Go to space {space.Name}</a>
{/each}
</main>

View File

@ -0,0 +1,99 @@
//get id from slug
import { redirect } from "@sveltejs/kit";
let sesh_id = "";
//export async function load ()
export const load = async (event) => {
const sessionID = event.cookies.get("sessionID");
sesh_id = sessionID;
console.log("SessionID???: ", sessionID);
const slug = event.params.space_slug;
//const slug = params.space_slug;
console.log("slug: ", slug);
const apiUrl = import.meta.env.VITE_API_URL;
const res = await fetch(apiUrl + '/userland/spaces/' + slug, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionID
}
});
const data = await res.json();
if (res.ok) {
console.log(data)
return {
post: {
space: data.space,
sesh_id: sessionID,
slug: slug,
}
};
} else {
throw redirect(301,'/login?error='+ encodeURIComponent("You must be logged in to view this page."));
}
};
// I alredy have this code in my +page.server.js file:
export const actions = {
complete: async ({request}) => {
const form = await request.formData();
const prompt = form.get('prompt');
const spaceId = form.get('spaceId');
const chatSessionId = form.get('chatSessionId');
const sessionID = sesh_id;
console.log("slug: ", spaceId);
console.log("sessionID: ", sessionID);
console.log("prompt: ", prompt);
const apiUrl = import.meta.env.VITE_API_URL;
const fullUrl = apiUrl + '/userland/spaces/' + spaceId + '/' + chatSessionId;
console.log("fullUrl: ", fullUrl);
const res = await fetch(fullUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + sessionID
},
body: JSON.stringify({
prompt: prompt
})
});
if (!res.ok) {
// handle error
console.log('Error status:', res.status);
const text = await res.text();
console.log('Error message:', text);
return;
}
const data = await res.json();
console.log('Success:', data);
}
};
/*
Now, when I send the prompt, I get this error:
SyntaxError: Unexpected token C in JSON at position 0
at JSON.parse (<anonymous>)
at parseJSONFromBytes (/Users/ed/Code/brz9front/node_modules/undici/lib/fetch/body.js:580:15)
at successSteps (/Users/ed/Code/brz9front/node_modules/undici/lib/fetch/body.js:520:23)
at /Users/ed/Code/brz9front/node_modules/undici/lib/fetch/util.js:821:56
at node:internal/process/task_queues:140:7
at AsyncResource.runInAsyncScope (node:async_hooks:203:9)
at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
*/

View File

@ -0,0 +1,61 @@
<script>
import Header from '$components/Header.svelte';
export let data;
export let isLoggedIn = true
let activeChatSession = null;
export let breadcrumb = [
{ name: 'Spaces', href: '/profile/spaces' },
{ name: data.post.space.Name, href: '' }
]
$: if (data?.post?.space?.ChatSessions?.length > 0 && !activeChatSession) {
activeChatSession = data.post.space.ChatSessions[0];
}
</script>
<Header { isLoggedIn } { breadcrumb }/>
<main>
<aside class="space-chat-left">
<!-- Here I want a list of the Chat Sessions in the Space, when I click on a ChatSession name, I want it to load the Chat Session messages in the section class="chat" and the Chat Session Data in the <aside class="space-chat-right"> -->
{#each data.post.space.ChatSessions as chatSesh}
<button
class="chat-session-button"
on:click={() => activeChatSession = chatSesh}>
{chatSesh.Name}
</button>
{/each}
</aside>
<div class="space-center">
<section class="chat">
<!-- This is where the Chat messages from the current Chat Session will appear, the bot and user messages
{#each activeChatSession?.ChatMessages as message}
<h3>Message</h3>
Display message
{/each}
-->
</section>
<section class="prompt">
<!-- This is the prompt section, I need it to be at the bottom of the page, also, I need a way to pass the SpaceID and ChatSessionID to the backend to send the message to the proper ChatSession -->
<form id="promptForm" action="?/complete" method="POST">
<input type="hidden" name="spaceId" value={data.post.space.ID} />
<input type="hidden" name="chatSessionId" value={activeChatSession.ID} />
<textarea class="prompt-box" id="prompt" name="prompt"></textarea>
<button formaction="?/complete" class="btn-fly">Send</button>
</form>
</section>
</div>
<aside class="space-chat-right">
<!-- Here we'll have Chat Session Data options and control, but we'll see that later -->
</aside>
</main>

View File

@ -0,0 +1,5 @@
// get cookie
// send get request to /chat/space/space_slug/chatseshid
// server returns a json object with the chat session and space data
// we should also have a post method to send chat to server
// on endpoint /chat/space/space_slug/chatseshid and server returns a json object with the chat session and space data

View File

@ -0,0 +1,75 @@
import { redirect } from "@sveltejs/kit";
let sesh_id = "";
export const load = async (event) => {
const sessionID = event.cookies.get("sessionID");
sesh_id = sessionID;
console.log("SessionID???: ", sessionID);
const apiUrl = import.meta.env.VITE_API_URL;
const res = await fetch(apiUrl + '/userland', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionID
}
});
const data = await res.json();
if (res.ok) {
console.log(data)
return {
post: {
user_mail: data.user
}
};
} else {
throw redirect(301,'/login?error='+ encodeURIComponent("You must be logged in to view this page."));
}
};
export const actions = {
summarize: async ({request}) => {
const form = await request.formData();
const yturl = form.get('yturl');
const sessionID = sesh_id;
console.log("sessionID: ", sessionID);
const apiUrl = import.meta.env.VITE_API_URL;
const fullUrl = apiUrl + '/userland/tldw';
console.log("fullUrl: ", fullUrl);
const res = await fetch(fullUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + sessionID
},
body: JSON.stringify({
url: yturl
})
});
if (!res.ok) {
console.log('Error status:', res.status);
const text = await res.text();
console.log('Error message:', text);
return;
}
const data = await res.json();
console.log('Success:', data);
console.log("creator: ", data.body.creator);
return {
creator: data.body.creator,
title: data.body.title,
yturl: data.body.url,
summary: data.body.summary.split("\n"),
};
}
};

View File

@ -0,0 +1,43 @@
<script>
import Header from '$components/Header.svelte';
import { add_attribute } from 'svelte/internal';
export let data;
export let form;
export let isLoggedIn = true
export let breadcrumb = [
{ name: 'Spaces', href: '/profile/spaces' }
]
console.log(data)
</script>
<Header { isLoggedIn } { breadcrumb }/>
<main>
<div class="space-center">
<section class="prompt">
{#if form?.creator != undefined}
<p>{form.creator} - {form.title}</p>
<ul>
{#each form?.summary as point}
<li>{point}</li>
{/each}
</ul>
{/if}
<form id="promptForm" action="?/summarize" method="POST">
<input type="text" name="yturl" id="yturl">
<button formaction="?/summarize" class="btn-fly">Send</button>
</form>
</section>
</div>
<aside class="space-chat-right">
<!-- Here we'll have Chat Session Data options and control, but we'll see that later -->
</aside>
</main>

View File

@ -4,11 +4,13 @@
<script>
import { signup } from '$lib/api';
import { invalidate } from '$app/navigation';
import { isValidEmail, isValidPassword } from '$lib/utils';
import Modal from '$components/Modal.svelte';
import Header from '$components/Header.svelte';
import Footer from '$components/Footer.svelte';
import { navigate } from 'svelte-navigator';
import { invalid_attribute_name_character } from 'svelte/internal';
let showModal = false;
let modalMessage = 'Test message';
@ -31,6 +33,7 @@
const user = await signup(email, password);
sessionStorage.setItem('formSubmitted', 'true');
navigate('/signup/success');
invalidate('/signup/success');
} catch (err) {
modalMessage = err;
showModal = true;

25
src/styles/chat.scss Normal file
View File

@ -0,0 +1,25 @@
section.aichat div {
padding: 20px;
&.user {
background-color: darken($bg-color, 2%);
}
&.gpt {
background-color: darken($bg-color, 8%);
}
aside {
display: inline-block;
min-width: 50px;
text-align: center;
font-size: 1.2rem;
}
div.msg {
display: inline-block;
}
}

View File

@ -1,5 +1,7 @@
html {
background-color: $bg-color;
body {
background-image:
url('/noise.svg'),
linear-gradient(to bottom right, #2e3551, #0d747d);
color: $fg-color;
}

View File

@ -20,11 +20,11 @@ input[type=text], input[type=password], input[type=email], input[type=number], i
box-sizing: border-box;
border: none;
border-bottom: 5px solid $fg-color;
background-color: $bg-color;
background-color: transparent;
color: $fg-color;
font-size: 1.2rem;
font-weight: 400;
&:focus {
&.to-check:focus {
outline: none;
border-bottom: 5px solid $red;
}
@ -45,4 +45,25 @@ input:autofill {
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px $bg-color inset;
-webkit-text-fill-color: $fg-color;
}
textarea.prompt-box {
width: 100%;
height: 200px;
padding: 12px 0px;
margin: 8px 0;
box-sizing: border-box;
border: none;
background-color: darken($bg-color, 5%);
color: $fg-color;
font-size: 1rem;
font-weight: 400;
//no resize
resize: none;
overflow-y: scroll;
//hide scrollbar
scrollbar-width: none;
padding: 20px;
}

View File

@ -58,4 +58,26 @@ main {
footer.page-footer {
text-align: center;
margin-bottom: 20px;
}
div.nav-right select.lang-pick {
padding: 0px;
margin-top: -5px;
cursor: pointer;
option {
cursor: pointer;
}
}
.nav-left {
.breadslash {
margin-right: 5px;
}
* {
margin-left: 5px;
margin-right: 5px !important;
padding: 0px;
}
}

View File

@ -5,3 +5,4 @@
@import 'responsive';
@import 'forms';
@import 'blob';
@import 'chat';

View File

@ -1,4 +1,5 @@
$bg-color : #1C3137;
$fg-color : #F2DFD0;
$fg-color : #e9d2d2;
$red: #C62F00;
$green: #0A9C25;
$green: #0A9C25;

15
static/noise.svg Normal file
View File

@ -0,0 +1,15 @@
<svg
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
width='300' height='300'>
<filter id='n' x='0' y='0'>
<feTurbulence
type='fractalNoise'
baseFrequency='0.52'
stitchTiles='stitch'/>
</filter>
<rect width='300' height='300' fill='#fff0'/>
<rect width='300' height='300' filter="url(#n)" opacity='0.20'/>
</svg>

After

Width:  |  Height:  |  Size: 421 B

View File

@ -2,5 +2,14 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
plugins: [sveltekit()],
server: {
port: 5173
},
dev: {
port: 5173
},
preview: {
port: 5173
}
});