Initial commit

This commit is contained in:
Silas 2023-04-04 00:00:04 -04:00
commit bfc698c49c
Signed by: silentsilas
GPG Key ID: 4199EFB7DAA34349
80 changed files with 72665 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
.npmrc Normal file
View File

@ -0,0 +1 @@
engine-strict=true

13
.prettierignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

10
.prettierrc Normal file
View File

@ -0,0 +1,10 @@
{
"useTabs": false,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

View File

@ -0,0 +1,27 @@
{
"version": 3,
"routes": [
{
"src": "/_app/immutable/.+",
"headers": {
"cache-control": "public, immutable, max-age=31536000"
}
},
{
"handle": "filesystem"
},
{
"src": "^/?(?:/__data.json)?$",
"dest": "/fn-0"
},
{
"src": "^/api/chat/?(?:/__data.json)?$",
"dest": "/fn-1"
},
{
"src": "/.*",
"dest": "/fn"
}
],
"overrides": {}
}

View File

@ -0,0 +1,78 @@
let HttpError = class HttpError2 {
/**
* @param {number} status
* @param {{message: string} extends App.Error ? (App.Error | string | undefined) : App.Error} body
*/
constructor(status, body) {
this.status = status;
if (typeof body === "string") {
this.body = { message: body };
} else if (body) {
this.body = body;
} else {
this.body = { message: `Error: ${status}` };
}
}
toString() {
return JSON.stringify(this.body);
}
};
let Redirect = class Redirect2 {
/**
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
* @param {string} location
*/
constructor(status, location) {
this.status = status;
this.location = location;
}
};
let ActionFailure = class ActionFailure2 {
/**
* @param {number} status
* @param {T} [data]
*/
constructor(status, data) {
this.status = status;
this.data = data;
}
};
function error(status, message) {
if (isNaN(status) || status < 400 || status > 599) {
throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);
}
return new HttpError(status, message);
}
function json(data, init) {
const body = JSON.stringify(data);
const headers = new Headers(init?.headers);
if (!headers.has("content-length")) {
headers.set("content-length", encoder.encode(body).byteLength.toString());
}
if (!headers.has("content-type")) {
headers.set("content-type", "application/json");
}
return new Response(body, {
...init,
headers
});
}
const encoder = new TextEncoder();
function text(body, init) {
const headers = new Headers(init?.headers);
if (!headers.has("content-length")) {
headers.set("content-length", encoder.encode(body).byteLength.toString());
}
return new Response(body, {
...init,
headers
});
}
export {
ActionFailure as A,
HttpError as H,
Redirect as R,
error as e,
json as j,
text as t
};

View File

@ -0,0 +1,92 @@
import { n as noop, a as subscribe, r as run_all, o as safe_not_equal, p as is_function } from "./index3.js";
const subscriber_queue = [];
function readable(value, start) {
return {
subscribe: writable(value, start).subscribe
};
}
function writable(value, start = noop) {
let stop;
const subscribers = /* @__PURE__ */ new Set();
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) {
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe2(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0 && stop) {
stop();
stop = null;
}
};
}
return { set, update, subscribe: subscribe2 };
}
function derived(stores, fn, initial_value) {
const single = !Array.isArray(stores);
const stores_array = single ? [stores] : stores;
const auto = fn.length < 2;
return readable(initial_value, (set) => {
let started = false;
const values = [];
let pending = 0;
let cleanup = noop;
const sync = () => {
if (pending) {
return;
}
cleanup();
const result = fn(single ? values[0] : values, set);
if (auto) {
set(result);
} else {
cleanup = is_function(result) ? result : noop;
}
};
const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => {
values[i] = value;
pending &= ~(1 << i);
if (started) {
sync();
}
}, () => {
pending |= 1 << i;
}));
started = true;
sync();
return function stop() {
run_all(unsubscribers);
cleanup();
started = false;
};
});
}
export {
derived as d,
readable as r,
writable as w
};

View File

@ -0,0 +1,254 @@
function noop() {
}
function run(fn) {
return fn();
}
function blank_object() {
return /* @__PURE__ */ Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === "function";
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || (a && typeof a === "object" || typeof a === "function");
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function get_store_value(store) {
let value;
subscribe(store, (_) => value = _)();
return value;
}
function compute_rest_props(props, keys) {
const rest = {};
keys = new Set(keys);
for (const k in props)
if (!keys.has(k) && k[0] !== "$")
rest[k] = props[k];
return rest;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error("Function called outside component initialization");
return current_component;
}
function setContext(key, context) {
get_current_component().$$.context.set(key, context);
return context;
}
function getContext(key) {
return get_current_component().$$.context.get(key);
}
const _boolean_attributes = [
"allowfullscreen",
"allowpaymentrequest",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"defer",
"disabled",
"formnovalidate",
"hidden",
"inert",
"ismap",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected"
];
const boolean_attributes = /* @__PURE__ */ new Set([..._boolean_attributes]);
const void_element_names = /^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;
function is_void(name) {
return void_element_names.test(name) || name.toLowerCase() === "!doctype";
}
const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u;
function spread(args, attrs_to_add) {
const attributes = Object.assign({}, ...args);
if (attrs_to_add) {
const classes_to_add = attrs_to_add.classes;
const styles_to_add = attrs_to_add.styles;
if (classes_to_add) {
if (attributes.class == null) {
attributes.class = classes_to_add;
} else {
attributes.class += " " + classes_to_add;
}
}
if (styles_to_add) {
if (attributes.style == null) {
attributes.style = style_object_to_string(styles_to_add);
} else {
attributes.style = style_object_to_string(merge_ssr_styles(attributes.style, styles_to_add));
}
}
}
let str = "";
Object.keys(attributes).forEach((name) => {
if (invalid_attribute_name_character.test(name))
return;
const value = attributes[name];
if (value === true)
str += " " + name;
else if (boolean_attributes.has(name.toLowerCase())) {
if (value)
str += " " + name;
} else if (value != null) {
str += ` ${name}="${value}"`;
}
});
return str;
}
function merge_ssr_styles(style_attribute, style_directive) {
const style_object = {};
for (const individual_style of style_attribute.split(";")) {
const colon_index = individual_style.indexOf(":");
const name = individual_style.slice(0, colon_index).trim();
const value = individual_style.slice(colon_index + 1).trim();
if (!name)
continue;
style_object[name] = value;
}
for (const name in style_directive) {
const value = style_directive[name];
if (value) {
style_object[name] = value;
} else {
delete style_object[name];
}
}
return style_object;
}
const ATTR_REGEX = /[&"]/g;
const CONTENT_REGEX = /[&<]/g;
function escape(value, is_attr = false) {
const str = String(value);
const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX;
pattern.lastIndex = 0;
let escaped = "";
let last = 0;
while (pattern.test(str)) {
const i = pattern.lastIndex - 1;
const ch = str[i];
escaped += str.substring(last, i) + (ch === "&" ? "&amp;" : ch === '"' ? "&quot;" : "&lt;");
last = i + 1;
}
return escaped + str.substring(last);
}
function escape_attribute_value(value) {
const should_escape = typeof value === "string" || value && typeof value === "object";
return should_escape ? escape(value, true) : value;
}
function escape_object(obj) {
const result = {};
for (const key in obj) {
result[key] = escape_attribute_value(obj[key]);
}
return result;
}
function each(items, fn) {
let str = "";
for (let i = 0; i < items.length; i += 1) {
str += fn(items[i], i);
}
return str;
}
const missing_component = {
$$render: () => ""
};
function validate_component(component, name) {
if (!component || !component.$$render) {
if (name === "svelte:component")
name += " this={...}";
throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <${name}>.`);
}
return component;
}
let on_destroy;
function create_ssr_component(fn) {
function $$render(result, props, bindings, slots, context) {
const parent_component = current_component;
const $$ = {
on_destroy,
context: new Map(context || (parent_component ? parent_component.$$.context : [])),
// these will be immediately discarded
on_mount: [],
before_update: [],
after_update: [],
callbacks: blank_object()
};
set_current_component({ $$ });
const html = fn(result, props, bindings, slots);
set_current_component(parent_component);
return html;
}
return {
render: (props = {}, { $$slots = {}, context = /* @__PURE__ */ new Map() } = {}) => {
on_destroy = [];
const result = { title: "", head: "", css: /* @__PURE__ */ new Set() };
const html = $$render(result, props, {}, $$slots, context);
run_all(on_destroy);
return {
html,
css: {
code: Array.from(result.css).map((css) => css.code).join("\n"),
map: null
// TODO
},
head: result.title + result.head
};
},
$$render
};
}
function add_attribute(name, value, boolean) {
if (value == null || boolean && !value)
return "";
const assignment = boolean && value === true ? "" : `="${escape(value, true)}"`;
return ` ${name}${assignment}`;
}
function style_object_to_string(style_object) {
return Object.keys(style_object).filter((key) => style_object[key]).map((key) => `${key}: ${escape_attribute_value(style_object[key])};`).join(" ");
}
export {
subscribe as a,
get_store_value as b,
create_ssr_component as c,
each as d,
escape as e,
spread as f,
getContext as g,
escape_object as h,
is_void as i,
add_attribute as j,
compute_rest_props as k,
escape_attribute_value as l,
missing_component as m,
noop as n,
safe_not_equal as o,
is_function as p,
run_all as r,
setContext as s,
validate_component as v
};

View File

@ -0,0 +1,187 @@
import { c as create_ssr_component, s as setContext, v as validate_component, m as missing_component } from "./index3.js";
let base = "";
let assets = base;
const initial = { base, assets };
function reset() {
base = initial.base;
assets = initial.assets;
}
function set_assets(path) {
assets = initial.assets = path;
}
let public_env = {};
function set_private_env(environment) {
}
function set_public_env(environment) {
public_env = environment;
}
function afterUpdate() {
}
function set_building() {
}
const Root = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let { stores } = $$props;
let { page } = $$props;
let { constructors } = $$props;
let { components = [] } = $$props;
let { form } = $$props;
let { data_0 = null } = $$props;
let { data_1 = null } = $$props;
{
setContext("__svelte__", stores);
}
afterUpdate(stores.page.notify);
if ($$props.stores === void 0 && $$bindings.stores && stores !== void 0)
$$bindings.stores(stores);
if ($$props.page === void 0 && $$bindings.page && page !== void 0)
$$bindings.page(page);
if ($$props.constructors === void 0 && $$bindings.constructors && constructors !== void 0)
$$bindings.constructors(constructors);
if ($$props.components === void 0 && $$bindings.components && components !== void 0)
$$bindings.components(components);
if ($$props.form === void 0 && $$bindings.form && form !== void 0)
$$bindings.form(form);
if ($$props.data_0 === void 0 && $$bindings.data_0 && data_0 !== void 0)
$$bindings.data_0(data_0);
if ($$props.data_1 === void 0 && $$bindings.data_1 && data_1 !== void 0)
$$bindings.data_1(data_1);
let $$settled;
let $$rendered;
do {
$$settled = true;
{
stores.page.set(page);
}
$$rendered = `
${constructors[1] ? `${validate_component(constructors[0] || missing_component, "svelte:component").$$render(
$$result,
{ data: data_0, this: components[0] },
{
this: ($$value) => {
components[0] = $$value;
$$settled = false;
}
},
{
default: () => {
return `${validate_component(constructors[1] || missing_component, "svelte:component").$$render(
$$result,
{ data: data_1, form, this: components[1] },
{
this: ($$value) => {
components[1] = $$value;
$$settled = false;
}
},
{}
)}`;
}
}
)}` : `${validate_component(constructors[0] || missing_component, "svelte:component").$$render(
$$result,
{ data: data_0, form, this: components[0] },
{
this: ($$value) => {
components[0] = $$value;
$$settled = false;
}
},
{}
)}`}
${``}`;
} while (!$$settled);
return $$rendered;
});
const options = {
app_template_contains_nonce: false,
csp: { "mode": "auto", "directives": { "upgrade-insecure-requests": false, "block-all-mixed-content": false }, "reportOnly": { "upgrade-insecure-requests": false, "block-all-mixed-content": false } },
csrf_check_origin: true,
embedded: false,
env_public_prefix: "PUBLIC_",
hooks: null,
// added lazily, via `get_hooks`
preload_strategy: "modulepreload",
root: Root,
service_worker: false,
templates: {
app: ({ head, body, assets: assets2, nonce, env }) => '<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <link rel="icon" href="' + assets2 + '/favicon.png" />\n <meta name="viewport" content="width=device-width" />\n ' + head + '\n </head>\n <body data-sveltekit-preload-data="hover">\n <div style="display: contents">' + body + "</div>\n </body>\n</html>\n",
error: ({ status, message }) => '<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <title>' + message + `</title>
<style>
body {
--bg: white;
--fg: #222;
--divider: #ccc;
background: var(--bg);
color: var(--fg);
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.error {
display: flex;
align-items: center;
max-width: 32rem;
margin: 0 1rem;
}
.status {
font-weight: 200;
font-size: 3rem;
line-height: 1;
position: relative;
top: -0.05rem;
}
.message {
border-left: 1px solid var(--divider);
padding: 0 0 0 1rem;
margin: 0 0 0 1rem;
min-height: 2.5rem;
display: flex;
align-items: center;
}
.message h1 {
font-weight: 400;
font-size: 1em;
margin: 0;
}
@media (prefers-color-scheme: dark) {
body {
--bg: #222;
--fg: #ddd;
--divider: #666;
}
}
</style>
</head>
<body>
<div class="error">
<span class="status">` + status + '</span>\n <div class="message">\n <h1>' + message + "</h1>\n </div>\n </div>\n </body>\n</html>\n"
},
version_hash: "10ptubg"
};
function get_hooks() {
return {};
}
export {
assets as a,
base as b,
set_assets as c,
set_building as d,
set_private_env as e,
get_hooks as g,
options as o,
public_env as p,
reset as r,
set_public_env as s
};

View File

@ -0,0 +1,30 @@
import { g as getContext, c as create_ssr_component, a as subscribe, e as escape } from "../../chunks/index3.js";
const getStores = () => {
const stores = getContext("__svelte__");
return {
page: {
subscribe: stores.page.subscribe
},
navigating: {
subscribe: stores.navigating.subscribe
},
updated: stores.updated
};
};
const page = {
/** @param {(value: any) => void} fn */
subscribe(fn) {
const store = getStores().page;
return store.subscribe(fn);
}
};
const Error$1 = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let $page, $$unsubscribe_page;
$$unsubscribe_page = subscribe(page, (value) => $page = value);
$$unsubscribe_page();
return `<h1>${escape($page.status)}</h1>
<p>${escape($page.error?.message)}</p>`;
});
export {
Error$1 as default
};

View File

@ -0,0 +1,8 @@
import { c as create_ssr_component } from "../../chunks/index3.js";
const app = "";
const Layout = create_ssr_component(($$result, $$props, $$bindings, slots) => {
return `<div class="flex flex-col items-center py-14">${slots.default ? slots.default({}) : ``}</div>`;
});
export {
Layout as default
};

View File

@ -0,0 +1,402 @@
import { c as create_ssr_component, b as get_store_value, a as subscribe$1, v as validate_component, d as each, e as escape, g as getContext, m as missing_component, f as spread, h as escape_object, i as is_void, j as add_attribute, k as compute_rest_props, l as escape_attribute_value } from "../../chunks/index3.js";
import { SSE } from "sse.js";
import { w as writable, d as derived } from "../../chunks/index2.js";
import { marked } from "marked";
import DOMPurify from "isomorphic-dompurify";
const Chat = create_ssr_component(($$result, $$props, $$bindings, slots) => {
return `<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>`;
});
const Pencil = create_ssr_component(($$result, $$props, $$bindings, slots) => {
return `<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path></svg>`;
});
const Plus = create_ssr_component(($$result, $$props, $$bindings, slots) => {
return `<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>`;
});
const Trash = create_ssr_component(($$result, $$props, $$bindings, slots) => {
return `<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>`;
});
const { subscribe, update, ...store } = writable({
messages: [
{ role: "assistant", content: "Welcome! Please introduce yourself to your AI competitor." }
],
chatState: "idle"
});
const set = async (query) => {
updateMessages(query, "user", "loading");
request();
};
const request = async (query) => {
const eventSource = new SSE("/api/chat", {
headers: {
"Content-Type": "application/json"
},
payload: JSON.stringify({ messages: get_store_value(chatMessages).messages })
});
eventSource.addEventListener("error", handleError);
eventSource.addEventListener("message", streamMessage);
eventSource.stream();
};
const replace = (messages) => {
store.set(messages);
};
const reset = () => store.set({
messages: [
{ role: "assistant", content: "Welcome! Please introduce yourself to your AI competitor." }
],
chatState: "idle"
});
const updateMessages = (content, role, state) => {
chatMessages.update((messages) => {
return { messages: [...messages.messages, { role, content }], chatState: state };
});
};
const handleError = (err) => {
updateMessages(err, "system", "error");
console.error(err);
};
const streamMessage = (e) => {
try {
if (e.data === "[DONE]") {
updateMessages(get_store_value(answer), "assistant", "idle");
return answer.set("");
}
if (get_store_value(answer) === "...")
answer.set("");
const completionResponse = JSON.parse(e.data);
const [{ delta }] = completionResponse.choices;
if (delta.content) {
answer.update((_a) => _a + delta.content);
}
} catch (err) {
handleError(err);
}
};
const chatMessages = { subscribe, set, update, reset, replace };
const answer = writable("");
const chatHistory = derived(chatMessages, ($chatMessages) => {
return null;
});
const ChatHistory = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let $$unsubscribe_chatHistory;
$$unsubscribe_chatHistory = subscribe$1(chatHistory, (value) => value);
let chatHistoryKeys = [];
$$unsubscribe_chatHistory();
return `<div class="h-[700px] w-[350px] bg-black bg-opacity-20 rounded-md py-4 px-2 overflow-y-auto flex flex-col gap-2"><button class="flex py-3 px-3 items-center gap-3 rounded-md hover:bg-gray-500/10 transition-colors duration-200 text-white cursor-pointer text-sm mb-2 flex-shrink-0 border border-white/20">${validate_component(Plus, "Plus").$$render($$result, {}, {}, {})} New Game
</button>
${chatHistoryKeys.length > 0 ? `${each(chatHistoryKeys, (message) => {
return `
<div class="flex py-3 px-3 items-center gap-3 relative rounded-md cursor-pointer break-all pr-14 bg-opacity-40 hover:bg-white/5 bg-black group animate-flash text-sm">${validate_component(Chat, "Chat").$$render($$result, {}, {}, {})}
<div class="flex-1 text-ellipsis max-h-5 overflow-hidden break-all relative">${escape(message)}</div>
<div class="absolute flex right-1 z-10 text-gray-300 visible"><button class="p-1 hover:text-white">${validate_component(Pencil, "Pencil").$$render($$result, {}, {}, {})}</button>
<button class="p-1 hover:text-white">${validate_component(Trash, "Trash").$$render($$result, {}, {}, {})}
</button></div>
</div>`;
})}` : ``}</div>`;
});
const componentsContextKey = {};
const getComponentsContext = () => getContext(componentsContextKey);
const Renderer = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let $components, $$unsubscribe_components;
let { astNode } = $$props;
let { __index = 0 } = $$props;
let { type = void 0 } = $$props;
let { position = void 0 } = $$props;
const components = getComponentsContext();
$$unsubscribe_components = subscribe$1(components, (value) => $components = value);
if ($$props.astNode === void 0 && $$bindings.astNode && astNode !== void 0)
$$bindings.astNode(astNode);
if ($$props.__index === void 0 && $$bindings.__index && __index !== void 0)
$$bindings.__index(__index);
if ($$props.type === void 0 && $$bindings.type && type !== void 0)
$$bindings.type(type);
if ($$props.position === void 0 && $$bindings.position && position !== void 0)
$$bindings.position(position);
$$unsubscribe_components();
return `${astNode.type === "root" ? `${validate_component(Children, "Children").$$render($$result, Object.assign({}, astNode), {}, {})}` : `${astNode.type === "element" ? `${validate_component($components[astNode.tagName] || missing_component, "svelte:component").$$render($$result, Object.assign({}, astNode, { __index }), {}, {})}` : `${$components[astNode.type] !== void 0 ? `${validate_component($components[astNode.type] || missing_component, "svelte:component").$$render($$result, Object.assign({}, astNode, { __index }), {}, {})}` : ``}`}`}`;
});
const Children = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let { children } = $$props;
let { type = void 0 } = $$props;
let { position = void 0 } = $$props;
let { __index = void 0 } = $$props;
if ($$props.children === void 0 && $$bindings.children && children !== void 0)
$$bindings.children(children);
if ($$props.type === void 0 && $$bindings.type && type !== void 0)
$$bindings.type(type);
if ($$props.position === void 0 && $$bindings.position && position !== void 0)
$$bindings.position(position);
if ($$props.__index === void 0 && $$bindings.__index && __index !== void 0)
$$bindings.__index(__index);
return `${each(children, (child, __index2) => {
return `${validate_component(Renderer, "Renderer").$$render($$result, { astNode: child, __index: __index2 }, {}, {})}`;
})}`;
});
const Default = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let { tagName } = $$props;
let { children } = $$props;
let { properties } = $$props;
let { type = void 0 } = $$props;
let { position = void 0 } = $$props;
let { __index = void 0 } = $$props;
if ($$props.tagName === void 0 && $$bindings.tagName && tagName !== void 0)
$$bindings.tagName(tagName);
if ($$props.children === void 0 && $$bindings.children && children !== void 0)
$$bindings.children(children);
if ($$props.properties === void 0 && $$bindings.properties && properties !== void 0)
$$bindings.properties(properties);
if ($$props.type === void 0 && $$bindings.type && type !== void 0)
$$bindings.type(type);
if ($$props.position === void 0 && $$bindings.position && position !== void 0)
$$bindings.position(position);
if ($$props.__index === void 0 && $$bindings.__index && __index !== void 0)
$$bindings.__index(__index);
return `${Array.isArray(children) && children.length !== 0 ? `
${((tag) => {
return tag ? `<${tagName}${spread([escape_object(properties)], {})}>${is_void(tag) ? "" : `${validate_component(Children, "Children").$$render($$result, { children }, {}, {})}`}${is_void(tag) ? "" : `</${tag}>`}` : "";
})(tagName)}` : `
${((tag) => {
return tag ? `<${tagName}${spread([escape_object(properties)], {})}>${is_void(tag) ? "" : ``}${is_void(tag) ? "" : `</${tag}>`}` : "";
})(tagName)}`}`;
});
const defaultTags = [
// Content sectioning
"address",
"article",
"aside",
"footer",
"header",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"main",
"nav",
"section",
// Text content
"blockquote",
"dd",
"div",
"dl",
"dt",
"figcaption",
"figure",
"hr",
"li",
"menu",
"ol",
"p",
"pre",
"ul",
// Inline text semantics
"a",
"abbr",
"b",
"bdi",
"bdo",
"br",
"cite",
"code",
"data",
"dfn",
"em",
"i",
"kbd",
"mark",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"small",
"span",
"strong",
"sub",
"sup",
"time",
"u",
"var",
"wbr",
// Image and multimedia
"area",
"audio",
"img",
"map",
"track",
"video",
// Embedded content
"embed",
"iframe",
"object",
"param",
"picture",
"portal",
"source",
// SVG and MathML
"svg",
"math",
// Demarcating edits
"del",
"ins",
// Table content
"caption",
"col",
"colgroup",
"table",
"tbody",
"td",
"tfoot",
"th",
"thead",
"tr",
// Forms
"button",
"datalist",
"fieldset",
"form",
"input",
"label",
"legend",
"meter",
"optgroup",
"option",
"output",
"progress",
"select",
"textarea",
// Interactive elements
"details",
"dialog",
"summary"
];
const htmlComponents = defaultTags.reduce((acc, tag) => ({ ...acc, [tag]: Default }), {});
const Text = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let { value = "" } = $$props;
let { type = void 0 } = $$props;
let { position = void 0 } = $$props;
let { __index = void 0 } = $$props;
if ($$props.value === void 0 && $$bindings.value && value !== void 0)
$$bindings.value(value);
if ($$props.type === void 0 && $$bindings.type && type !== void 0)
$$bindings.type(type);
if ($$props.position === void 0 && $$bindings.position && position !== void 0)
$$bindings.position(position);
if ($$props.__index === void 0 && $$bindings.__index && __index !== void 0)
$$bindings.__index(__index);
return `${escape(value)}`;
});
({
...htmlComponents,
text: Text,
raw: Text
});
const ChatMessage = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let { type } = $$props;
let { message = "" } = $$props;
let { class: classes = "" } = $$props;
let scrollToDiv;
const classSet = {
user: "justify-end text-rose-700",
assistant: "justify-start text-teal-400",
system: "justify-center text-gray-400"
};
if ($$props.type === void 0 && $$bindings.type && type !== void 0)
$$bindings.type(type);
if ($$props.message === void 0 && $$bindings.message && message !== void 0)
$$bindings.message(message);
if ($$props.class === void 0 && $$bindings.class && classes !== void 0)
$$bindings.class(classes);
return `<div class="${"flex items-center " + escape(classSet[type], true)}"><p class="text-xs px-2">${escape(type === "user" ? "Me" : "Bot")}</p></div>
<div class="${"flex " + escape(classSet[type], true)}"><div class="${"bg-black py-0.5 px-4 max-w-2xl rounded leading-loose " + escape(classes, true) + " " + escape(classSet[type], true)}"><!-- HTML_TAG_START -->${DOMPurify.sanitize(marked.parse(message))}<!-- HTML_TAG_END --></div>
<div${add_attribute("this", scrollToDiv, 0)}></div></div>`;
});
const Input = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let $$restProps = compute_rest_props($$props, ["value", "placeholder", "name", "type", "label", "class"]);
let { value } = $$props;
let { placeholder = "" } = $$props;
let { name = "" } = $$props;
let { type = "text" } = $$props;
let { label = "" } = $$props;
let { class: classes = "" } = $$props;
if ($$props.value === void 0 && $$bindings.value && value !== void 0)
$$bindings.value(value);
if ($$props.placeholder === void 0 && $$bindings.placeholder && placeholder !== void 0)
$$bindings.placeholder(placeholder);
if ($$props.name === void 0 && $$bindings.name && name !== void 0)
$$bindings.name(name);
if ($$props.type === void 0 && $$bindings.type && type !== void 0)
$$bindings.type(type);
if ($$props.label === void 0 && $$bindings.label && label !== void 0)
$$bindings.label(label);
if ($$props.class === void 0 && $$bindings.class && classes !== void 0)
$$bindings.class(classes);
return `<div class="${"flex flex-col min-w-xl " + escape(classes, true)}"><label class="text-xs"${add_attribute("for", name, 0)}>${escape(label)}</label>
<input${spread(
[
{ name: escape_attribute_value(name) },
{
placeholder: escape_attribute_value(placeholder)
},
{
class: "shadow bg-white/10 rounded-md px-4 py-1.5 min-w-xl text-teal-300 border border-transparent focus:outline-none focus:ring-1 focus:ring-teal-300 focus:border-transparent"
},
escape_object($$restProps)
],
{}
)}${add_attribute("value", value, 0)}></div>`;
});
const Page = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let $chatMessages, $$unsubscribe_chatMessages;
let $answer, $$unsubscribe_answer;
$$unsubscribe_chatMessages = subscribe$1(chatMessages, (value) => $chatMessages = value);
$$unsubscribe_answer = subscribe$1(answer, (value) => $answer = value);
let query = "";
let $$settled;
let $$rendered;
do {
$$settled = true;
$$rendered = `<section class="flex max-w-6xl w-full pt-4 justify-center"><div class="flex flex-col gap-2">${validate_component(ChatHistory, "ChatHistory").$$render($$result, {}, {}, {})}</div>
<div class="flex flex-col w-full px-8 items-center gap-2"><div class="h-[700px] w-full bg-black bg-opacity-20 rounded-md p-4 overflow-y-auto flex flex-col gap-4"><div class="flex flex-col gap-2">${each($chatMessages.messages, (message) => {
return `${validate_component(ChatMessage, "ChatMessage").$$render(
$$result,
{
type: message.role,
message: message.content
},
{},
{}
)}`;
})}
${$answer ? `${validate_component(ChatMessage, "ChatMessage").$$render($$result, { type: "assistant", message: $answer }, {}, {})}` : ``}</div></div>
<form class="flex w-full rounded-md gap-4 bg-black bg-opacity-20 p-2">${validate_component(Input, "Input").$$render(
$$result,
{
type: "text",
class: "w-full",
value: query
},
{
value: ($$value) => {
query = $$value;
$$settled = false;
}
},
{}
)}
<button type="submit" class="bg-black bg-opacity-40 hover:bg-white/5 px-8 py-1.5 border border-black/40 ml-[-0.5rem] rounded-md text-teal-300">Send
</button></form></div></section>`;
} while (!$$settled);
$$unsubscribe_chatMessages();
$$unsubscribe_answer();
return $$rendered;
});
export {
Page as default
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
export const index = 0;
export const component = async () => (await import('../entries/pages/_layout.svelte.js')).default;
export const file = '_app/immutable/entry/_layout.svelte.62a505d3.js';
export const imports = ["_app/immutable/entry/_layout.svelte.62a505d3.js","_app/immutable/chunks/index.3641fafa.js"];
export const stylesheets = ["_app/immutable/assets/_layout.b9bcc4f4.css"];
export const fonts = [];

View File

@ -0,0 +1,8 @@
export const index = 1;
export const component = async () => (await import('../entries/fallbacks/error.svelte.js')).default;
export const file = '_app/immutable/entry/error.svelte.a6bd0229.js';
export const imports = ["_app/immutable/entry/error.svelte.a6bd0229.js","_app/immutable/chunks/index.3641fafa.js","_app/immutable/chunks/singletons.8077db6c.js","_app/immutable/chunks/index.fa397836.js"];
export const stylesheets = [];
export const fonts = [];

View File

@ -0,0 +1,8 @@
export const index = 2;
export const component = async () => (await import('../entries/pages/_page.svelte.js')).default;
export const file = '_app/immutable/entry/_page.svelte.2f89fa28.js';
export const imports = ["_app/immutable/entry/_page.svelte.2f89fa28.js","_app/immutable/chunks/index.3641fafa.js","_app/immutable/chunks/index.fa397836.js"];
export const stylesheets = [];
export const fonts = [];

View File

@ -0,0 +1,51 @@
import { installPolyfills } from '@sveltejs/kit/node/polyfills';
import { getRequest, setResponse } from '@sveltejs/kit/node';
import { Server } from '../output/server/index.js';
import { manifest } from './manifest.js';
installPolyfills();
const server = new Server(manifest);
await server.init({
env: /** @type {Record<string, string>} */ (process.env)
});
const DATA_SUFFIX = '/__data.json';
/**
* @param {import('http').IncomingMessage} req
* @param {import('http').ServerResponse} res
*/
export default async (req, res) => {
if (req.url) {
const [path, search] = req.url.split('?');
const params = new URLSearchParams(search);
const pathname = params.get('__pathname');
if (pathname) {
params.delete('__pathname');
req.url = `${pathname}${path.endsWith(DATA_SUFFIX) ? DATA_SUFFIX : ''}?${params}`;
}
}
/** @type {Request} */
let request;
try {
request = await getRequest({ base: `https://${req.headers.host}`, request: req });
} catch (err) {
res.statusCode = /** @type {any} */ (err).status || 400;
return res.end('Invalid request body');
}
setResponse(
res,
await server.respond(request, {
getClientAddress() {
return /** @type {string} */ (request.headers.get('x-forwarded-for'));
}
})
);
};

View File

@ -0,0 +1,27 @@
export const manifest = {
appDir: "_app",
appPath: "_app",
assets: new Set(["favicon.png"]),
mimeTypes: {".png":"image/png"},
_: {
client: {"start":{"file":"_app/immutable/entry/start.c62adf58.js","imports":["_app/immutable/entry/start.c62adf58.js","_app/immutable/chunks/index.3641fafa.js","_app/immutable/chunks/singletons.8077db6c.js","_app/immutable/chunks/index.fa397836.js"],"stylesheets":[],"fonts":[]},"app":{"file":"_app/immutable/entry/app.c59518f6.js","imports":["_app/immutable/entry/app.c59518f6.js","_app/immutable/chunks/index.3641fafa.js"],"stylesheets":[],"fonts":[]}},
nodes: [
() => import('../output/server/nodes/0.js'),
() => import('../output/server/nodes/1.js'),
() => import('../output/server/nodes/2.js')
],
routes: [
{
id: "/",
pattern: /^\/$/,
params: [],
page: { layouts: [0], errors: [1], leaf: 2 },
endpoint: null
}
],
matchers: async () => {
return { };
}
}
};

View File

@ -0,0 +1,6 @@
{
"runtime": "nodejs18.x",
"handler": ".svelte-kit/vercel-tmp/index.js",
"launcherType": "Nodejs",
"experimentalResponseStreaming": true
}

View File

@ -0,0 +1 @@
{"type":"module"}

View File

@ -0,0 +1,5 @@
{
"runtime": "edge",
"envVarsInUse": [],
"entrypoint": "index.js"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,78 @@
let HttpError = class HttpError2 {
/**
* @param {number} status
* @param {{message: string} extends App.Error ? (App.Error | string | undefined) : App.Error} body
*/
constructor(status, body) {
this.status = status;
if (typeof body === "string") {
this.body = { message: body };
} else if (body) {
this.body = body;
} else {
this.body = { message: `Error: ${status}` };
}
}
toString() {
return JSON.stringify(this.body);
}
};
let Redirect = class Redirect2 {
/**
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
* @param {string} location
*/
constructor(status, location) {
this.status = status;
this.location = location;
}
};
let ActionFailure = class ActionFailure2 {
/**
* @param {number} status
* @param {T} [data]
*/
constructor(status, data) {
this.status = status;
this.data = data;
}
};
function error(status, message) {
if (isNaN(status) || status < 400 || status > 599) {
throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);
}
return new HttpError(status, message);
}
function json(data, init) {
const body = JSON.stringify(data);
const headers = new Headers(init?.headers);
if (!headers.has("content-length")) {
headers.set("content-length", encoder.encode(body).byteLength.toString());
}
if (!headers.has("content-type")) {
headers.set("content-type", "application/json");
}
return new Response(body, {
...init,
headers
});
}
const encoder = new TextEncoder();
function text(body, init) {
const headers = new Headers(init?.headers);
if (!headers.has("content-length")) {
headers.set("content-length", encoder.encode(body).byteLength.toString());
}
return new Response(body, {
...init,
headers
});
}
export {
ActionFailure as A,
HttpError as H,
Redirect as R,
error as e,
json as j,
text as t
};

View File

@ -0,0 +1,92 @@
import { n as noop, a as subscribe, r as run_all, o as safe_not_equal, p as is_function } from "./index3.js";
const subscriber_queue = [];
function readable(value, start) {
return {
subscribe: writable(value, start).subscribe
};
}
function writable(value, start = noop) {
let stop;
const subscribers = /* @__PURE__ */ new Set();
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) {
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe2(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0 && stop) {
stop();
stop = null;
}
};
}
return { set, update, subscribe: subscribe2 };
}
function derived(stores, fn, initial_value) {
const single = !Array.isArray(stores);
const stores_array = single ? [stores] : stores;
const auto = fn.length < 2;
return readable(initial_value, (set) => {
let started = false;
const values = [];
let pending = 0;
let cleanup = noop;
const sync = () => {
if (pending) {
return;
}
cleanup();
const result = fn(single ? values[0] : values, set);
if (auto) {
set(result);
} else {
cleanup = is_function(result) ? result : noop;
}
};
const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => {
values[i] = value;
pending &= ~(1 << i);
if (started) {
sync();
}
}, () => {
pending |= 1 << i;
}));
started = true;
sync();
return function stop() {
run_all(unsubscribers);
cleanup();
started = false;
};
});
}
export {
derived as d,
readable as r,
writable as w
};

View File

@ -0,0 +1,254 @@
function noop() {
}
function run(fn) {
return fn();
}
function blank_object() {
return /* @__PURE__ */ Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === "function";
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || (a && typeof a === "object" || typeof a === "function");
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function get_store_value(store) {
let value;
subscribe(store, (_) => value = _)();
return value;
}
function compute_rest_props(props, keys) {
const rest = {};
keys = new Set(keys);
for (const k in props)
if (!keys.has(k) && k[0] !== "$")
rest[k] = props[k];
return rest;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error("Function called outside component initialization");
return current_component;
}
function setContext(key, context) {
get_current_component().$$.context.set(key, context);
return context;
}
function getContext(key) {
return get_current_component().$$.context.get(key);
}
const _boolean_attributes = [
"allowfullscreen",
"allowpaymentrequest",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"defer",
"disabled",
"formnovalidate",
"hidden",
"inert",
"ismap",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected"
];
const boolean_attributes = /* @__PURE__ */ new Set([..._boolean_attributes]);
const void_element_names = /^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;
function is_void(name) {
return void_element_names.test(name) || name.toLowerCase() === "!doctype";
}
const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u;
function spread(args, attrs_to_add) {
const attributes = Object.assign({}, ...args);
if (attrs_to_add) {
const classes_to_add = attrs_to_add.classes;
const styles_to_add = attrs_to_add.styles;
if (classes_to_add) {
if (attributes.class == null) {
attributes.class = classes_to_add;
} else {
attributes.class += " " + classes_to_add;
}
}
if (styles_to_add) {
if (attributes.style == null) {
attributes.style = style_object_to_string(styles_to_add);
} else {
attributes.style = style_object_to_string(merge_ssr_styles(attributes.style, styles_to_add));
}
}
}
let str = "";
Object.keys(attributes).forEach((name) => {
if (invalid_attribute_name_character.test(name))
return;
const value = attributes[name];
if (value === true)
str += " " + name;
else if (boolean_attributes.has(name.toLowerCase())) {
if (value)
str += " " + name;
} else if (value != null) {
str += ` ${name}="${value}"`;
}
});
return str;
}
function merge_ssr_styles(style_attribute, style_directive) {
const style_object = {};
for (const individual_style of style_attribute.split(";")) {
const colon_index = individual_style.indexOf(":");
const name = individual_style.slice(0, colon_index).trim();
const value = individual_style.slice(colon_index + 1).trim();
if (!name)
continue;
style_object[name] = value;
}
for (const name in style_directive) {
const value = style_directive[name];
if (value) {
style_object[name] = value;
} else {
delete style_object[name];
}
}
return style_object;
}
const ATTR_REGEX = /[&"]/g;
const CONTENT_REGEX = /[&<]/g;
function escape(value, is_attr = false) {
const str = String(value);
const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX;
pattern.lastIndex = 0;
let escaped = "";
let last = 0;
while (pattern.test(str)) {
const i = pattern.lastIndex - 1;
const ch = str[i];
escaped += str.substring(last, i) + (ch === "&" ? "&amp;" : ch === '"' ? "&quot;" : "&lt;");
last = i + 1;
}
return escaped + str.substring(last);
}
function escape_attribute_value(value) {
const should_escape = typeof value === "string" || value && typeof value === "object";
return should_escape ? escape(value, true) : value;
}
function escape_object(obj) {
const result = {};
for (const key in obj) {
result[key] = escape_attribute_value(obj[key]);
}
return result;
}
function each(items, fn) {
let str = "";
for (let i = 0; i < items.length; i += 1) {
str += fn(items[i], i);
}
return str;
}
const missing_component = {
$$render: () => ""
};
function validate_component(component, name) {
if (!component || !component.$$render) {
if (name === "svelte:component")
name += " this={...}";
throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <${name}>.`);
}
return component;
}
let on_destroy;
function create_ssr_component(fn) {
function $$render(result, props, bindings, slots, context) {
const parent_component = current_component;
const $$ = {
on_destroy,
context: new Map(context || (parent_component ? parent_component.$$.context : [])),
// these will be immediately discarded
on_mount: [],
before_update: [],
after_update: [],
callbacks: blank_object()
};
set_current_component({ $$ });
const html = fn(result, props, bindings, slots);
set_current_component(parent_component);
return html;
}
return {
render: (props = {}, { $$slots = {}, context = /* @__PURE__ */ new Map() } = {}) => {
on_destroy = [];
const result = { title: "", head: "", css: /* @__PURE__ */ new Set() };
const html = $$render(result, props, {}, $$slots, context);
run_all(on_destroy);
return {
html,
css: {
code: Array.from(result.css).map((css) => css.code).join("\n"),
map: null
// TODO
},
head: result.title + result.head
};
},
$$render
};
}
function add_attribute(name, value, boolean) {
if (value == null || boolean && !value)
return "";
const assignment = boolean && value === true ? "" : `="${escape(value, true)}"`;
return ` ${name}${assignment}`;
}
function style_object_to_string(style_object) {
return Object.keys(style_object).filter((key) => style_object[key]).map((key) => `${key}: ${escape_attribute_value(style_object[key])};`).join(" ");
}
export {
subscribe as a,
get_store_value as b,
create_ssr_component as c,
each as d,
escape as e,
spread as f,
getContext as g,
escape_object as h,
is_void as i,
add_attribute as j,
compute_rest_props as k,
escape_attribute_value as l,
missing_component as m,
noop as n,
safe_not_equal as o,
is_function as p,
run_all as r,
setContext as s,
validate_component as v
};

View File

@ -0,0 +1,187 @@
import { c as create_ssr_component, s as setContext, v as validate_component, m as missing_component } from "./index3.js";
let base = "";
let assets = base;
const initial = { base, assets };
function reset() {
base = initial.base;
assets = initial.assets;
}
function set_assets(path) {
assets = initial.assets = path;
}
let public_env = {};
function set_private_env(environment) {
}
function set_public_env(environment) {
public_env = environment;
}
function afterUpdate() {
}
function set_building() {
}
const Root = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let { stores } = $$props;
let { page } = $$props;
let { constructors } = $$props;
let { components = [] } = $$props;
let { form } = $$props;
let { data_0 = null } = $$props;
let { data_1 = null } = $$props;
{
setContext("__svelte__", stores);
}
afterUpdate(stores.page.notify);
if ($$props.stores === void 0 && $$bindings.stores && stores !== void 0)
$$bindings.stores(stores);
if ($$props.page === void 0 && $$bindings.page && page !== void 0)
$$bindings.page(page);
if ($$props.constructors === void 0 && $$bindings.constructors && constructors !== void 0)
$$bindings.constructors(constructors);
if ($$props.components === void 0 && $$bindings.components && components !== void 0)
$$bindings.components(components);
if ($$props.form === void 0 && $$bindings.form && form !== void 0)
$$bindings.form(form);
if ($$props.data_0 === void 0 && $$bindings.data_0 && data_0 !== void 0)
$$bindings.data_0(data_0);
if ($$props.data_1 === void 0 && $$bindings.data_1 && data_1 !== void 0)
$$bindings.data_1(data_1);
let $$settled;
let $$rendered;
do {
$$settled = true;
{
stores.page.set(page);
}
$$rendered = `
${constructors[1] ? `${validate_component(constructors[0] || missing_component, "svelte:component").$$render(
$$result,
{ data: data_0, this: components[0] },
{
this: ($$value) => {
components[0] = $$value;
$$settled = false;
}
},
{
default: () => {
return `${validate_component(constructors[1] || missing_component, "svelte:component").$$render(
$$result,
{ data: data_1, form, this: components[1] },
{
this: ($$value) => {
components[1] = $$value;
$$settled = false;
}
},
{}
)}`;
}
}
)}` : `${validate_component(constructors[0] || missing_component, "svelte:component").$$render(
$$result,
{ data: data_0, form, this: components[0] },
{
this: ($$value) => {
components[0] = $$value;
$$settled = false;
}
},
{}
)}`}
${``}`;
} while (!$$settled);
return $$rendered;
});
const options = {
app_template_contains_nonce: false,
csp: { "mode": "auto", "directives": { "upgrade-insecure-requests": false, "block-all-mixed-content": false }, "reportOnly": { "upgrade-insecure-requests": false, "block-all-mixed-content": false } },
csrf_check_origin: true,
embedded: false,
env_public_prefix: "PUBLIC_",
hooks: null,
// added lazily, via `get_hooks`
preload_strategy: "modulepreload",
root: Root,
service_worker: false,
templates: {
app: ({ head, body, assets: assets2, nonce, env }) => '<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <link rel="icon" href="' + assets2 + '/favicon.png" />\n <meta name="viewport" content="width=device-width" />\n ' + head + '\n </head>\n <body data-sveltekit-preload-data="hover">\n <div style="display: contents">' + body + "</div>\n </body>\n</html>\n",
error: ({ status, message }) => '<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <title>' + message + `</title>
<style>
body {
--bg: white;
--fg: #222;
--divider: #ccc;
background: var(--bg);
color: var(--fg);
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.error {
display: flex;
align-items: center;
max-width: 32rem;
margin: 0 1rem;
}
.status {
font-weight: 200;
font-size: 3rem;
line-height: 1;
position: relative;
top: -0.05rem;
}
.message {
border-left: 1px solid var(--divider);
padding: 0 0 0 1rem;
margin: 0 0 0 1rem;
min-height: 2.5rem;
display: flex;
align-items: center;
}
.message h1 {
font-weight: 400;
font-size: 1em;
margin: 0;
}
@media (prefers-color-scheme: dark) {
body {
--bg: #222;
--fg: #ddd;
--divider: #666;
}
}
</style>
</head>
<body>
<div class="error">
<span class="status">` + status + '</span>\n <div class="message">\n <h1>' + message + "</h1>\n </div>\n </div>\n </body>\n</html>\n"
},
version_hash: "10ptubg"
};
function get_hooks() {
return {};
}
export {
assets as a,
base as b,
set_assets as c,
set_building as d,
set_private_env as e,
get_hooks as g,
options as o,
public_env as p,
reset as r,
set_public_env as s
};

View File

@ -0,0 +1,30 @@
import { g as getContext, c as create_ssr_component, a as subscribe, e as escape } from "../../chunks/index3.js";
const getStores = () => {
const stores = getContext("__svelte__");
return {
page: {
subscribe: stores.page.subscribe
},
navigating: {
subscribe: stores.navigating.subscribe
},
updated: stores.updated
};
};
const page = {
/** @param {(value: any) => void} fn */
subscribe(fn) {
const store = getStores().page;
return store.subscribe(fn);
}
};
const Error$1 = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let $page, $$unsubscribe_page;
$$unsubscribe_page = subscribe(page, (value) => $page = value);
$$unsubscribe_page();
return `<h1>${escape($page.status)}</h1>
<p>${escape($page.error?.message)}</p>`;
});
export {
Error$1 as default
};

View File

@ -0,0 +1,8 @@
import { c as create_ssr_component } from "../../chunks/index3.js";
const app = "";
const Layout = create_ssr_component(($$result, $$props, $$bindings, slots) => {
return `<div class="flex flex-col items-center py-14">${slots.default ? slots.default({}) : ``}</div>`;
});
export {
Layout as default
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
export const index = 0;
export const component = async () => (await import('../entries/pages/_layout.svelte.js')).default;
export const file = '_app/immutable/entry/_layout.svelte.62a505d3.js';
export const imports = ["_app/immutable/entry/_layout.svelte.62a505d3.js","_app/immutable/chunks/index.3641fafa.js"];
export const stylesheets = ["_app/immutable/assets/_layout.b9bcc4f4.css"];
export const fonts = [];

View File

@ -0,0 +1,8 @@
export const index = 1;
export const component = async () => (await import('../entries/fallbacks/error.svelte.js')).default;
export const file = '_app/immutable/entry/error.svelte.a6bd0229.js';
export const imports = ["_app/immutable/entry/error.svelte.a6bd0229.js","_app/immutable/chunks/index.3641fafa.js","_app/immutable/chunks/singletons.8077db6c.js","_app/immutable/chunks/index.fa397836.js"];
export const stylesheets = [];
export const fonts = [];

View File

@ -0,0 +1,51 @@
import { installPolyfills } from '@sveltejs/kit/node/polyfills';
import { getRequest, setResponse } from '@sveltejs/kit/node';
import { Server } from '../output/server/index.js';
import { manifest } from './manifest.js';
installPolyfills();
const server = new Server(manifest);
await server.init({
env: /** @type {Record<string, string>} */ (process.env)
});
const DATA_SUFFIX = '/__data.json';
/**
* @param {import('http').IncomingMessage} req
* @param {import('http').ServerResponse} res
*/
export default async (req, res) => {
if (req.url) {
const [path, search] = req.url.split('?');
const params = new URLSearchParams(search);
const pathname = params.get('__pathname');
if (pathname) {
params.delete('__pathname');
req.url = `${pathname}${path.endsWith(DATA_SUFFIX) ? DATA_SUFFIX : ''}?${params}`;
}
}
/** @type {Request} */
let request;
try {
request = await getRequest({ base: `https://${req.headers.host}`, request: req });
} catch (err) {
res.statusCode = /** @type {any} */ (err).status || 400;
return res.end('Invalid request body');
}
setResponse(
res,
await server.respond(request, {
getClientAddress() {
return /** @type {string} */ (request.headers.get('x-forwarded-for'));
}
})
);
};

View File

@ -0,0 +1,20 @@
export const manifest = {
appDir: "_app",
appPath: "_app",
assets: new Set(["favicon.png"]),
mimeTypes: {".png":"image/png"},
_: {
client: {"start":{"file":"_app/immutable/entry/start.c62adf58.js","imports":["_app/immutable/entry/start.c62adf58.js","_app/immutable/chunks/index.3641fafa.js","_app/immutable/chunks/singletons.8077db6c.js","_app/immutable/chunks/index.fa397836.js"],"stylesheets":[],"fonts":[]},"app":{"file":"_app/immutable/entry/app.c59518f6.js","imports":["_app/immutable/entry/app.c59518f6.js","_app/immutable/chunks/index.3641fafa.js"],"stylesheets":[],"fonts":[]}},
nodes: [
() => import('../output/server/nodes/0.js'),
() => import('../output/server/nodes/1.js')
],
routes: [
],
matchers: async () => {
return { };
}
}
};

View File

@ -0,0 +1,6 @@
{
"runtime": "nodejs18.x",
"handler": ".svelte-kit/vercel-tmp/index.js",
"launcherType": "Nodejs",
"experimentalResponseStreaming": true
}

View File

@ -0,0 +1 @@
{"type":"module"}

View File

@ -0,0 +1,711 @@
/* Write your global styles here, in PostCSS syntax */
/* ! tailwindcss v3.3.1 | MIT License | https://tailwindcss.com */
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
border-width: 0; /* 2 */
border-style: solid; /* 2 */
border-color: #e5e7eb; /* 2 */
}
::before,
::after {
--tw-content: '';
}
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
*/
html {
line-height: 1.5; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-moz-tab-size: 4; /* 3 */
-o-tab-size: 4;
tab-size: 4; /* 3 */
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */
font-feature-settings: normal; /* 5 */
font-variation-settings: normal; /* 6 */
}
/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/
body {
margin: 0; /* 1 */
line-height: inherit; /* 2 */
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0; /* 1 */
color: inherit; /* 2 */
border-top-width: 1px; /* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */
font-size: 1em; /* 2 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
border-collapse: collapse; /* 3 */
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
font-weight: inherit; /* 1 */
line-height: inherit; /* 1 */
color: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 3 */
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button; /* 1 */
background-color: transparent; /* 2 */
background-image: none; /* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: auto;
}
/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::-moz-placeholder, textarea::-moz-placeholder {
opacity: 1; /* 1 */
color: #9ca3af; /* 2 */
}
input::placeholder,
textarea::placeholder {
opacity: 1; /* 1 */
color: #9ca3af; /* 2 */
}
/*
Set the default cursor for buttons.
*/
button,
[role="button"] {
cursor: pointer;
}
/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
cursor: default;
}
/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block; /* 1 */
vertical-align: middle; /* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
/* Make elements with the HTML hidden attribute stay hidden by default */
[hidden] {
display: none;
}
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
.visible {
visibility: visible;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.right-1 {
right: 0.25rem;
}
.z-10 {
z-index: 10;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.ml-\[-0\.5rem\] {
margin-left: -0.5rem;
}
.block {
display: block;
}
.inline {
display: inline;
}
.flex {
display: flex;
}
.contents {
display: contents;
}
.h-4 {
height: 1rem;
}
.h-\[700px\] {
height: 700px;
}
.max-h-5 {
max-height: 1.25rem;
}
.w-4 {
width: 1rem;
}
.w-\[350px\] {
width: 350px;
}
.w-full {
width: 100%;
}
.max-w-2xl {
max-width: 42rem;
}
.max-w-6xl {
max-width: 72rem;
}
.flex-1 {
flex: 1 1 0%;
}
.flex-shrink-0 {
flex-shrink: 0;
}
.cursor-pointer {
cursor: pointer;
}
.flex-col {
flex-direction: column;
}
.items-center {
align-items: center;
}
.justify-start {
justify-content: flex-start;
}
.justify-end {
justify-content: flex-end;
}
.justify-center {
justify-content: center;
}
.gap-2 {
gap: 0.5rem;
}
.gap-3 {
gap: 0.75rem;
}
.gap-4 {
gap: 1rem;
}
.overflow-hidden {
overflow: hidden;
}
.overflow-y-auto {
overflow-y: auto;
}
.text-ellipsis {
text-overflow: ellipsis;
}
.break-all {
word-break: break-all;
}
.rounded {
border-radius: 0.25rem;
}
.rounded-md {
border-radius: 0.375rem;
}
.border {
border-width: 1px;
}
.border-black\/40 {
border-color: rgb(0 0 0 / 0.4);
}
.border-transparent {
border-color: transparent;
}
.border-white\/20 {
border-color: rgb(255 255 255 / 0.2);
}
.bg-black {
--tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
}
.bg-white\/10 {
background-color: rgb(255 255 255 / 0.1);
}
.bg-opacity-20 {
--tw-bg-opacity: 0.2;
}
.bg-opacity-40 {
--tw-bg-opacity: 0.4;
}
.p-1 {
padding: 0.25rem;
}
.p-2 {
padding: 0.5rem;
}
.p-4 {
padding: 1rem;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.px-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.px-8 {
padding-left: 2rem;
padding-right: 2rem;
}
.py-0 {
padding-top: 0px;
padding-bottom: 0px;
}
.py-0\.5 {
padding-top: 0.125rem;
padding-bottom: 0.125rem;
}
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-1\.5 {
padding-top: 0.375rem;
padding-bottom: 0.375rem;
}
.py-14 {
padding-top: 3.5rem;
padding-bottom: 3.5rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.pr-14 {
padding-right: 3.5rem;
}
.pt-4 {
padding-top: 1rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.leading-loose {
line-height: 2;
}
.text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity));
}
.text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
}
.text-rose-700 {
--tw-text-opacity: 1;
color: rgb(190 18 60 / var(--tw-text-opacity));
}
.text-teal-300 {
--tw-text-opacity: 1;
color: rgb(94 234 212 / var(--tw-text-opacity));
}
.text-teal-400 {
--tw-text-opacity: 1;
color: rgb(45 212 191 / var(--tw-text-opacity));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.transition-colors {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-200 {
transition-duration: 200ms;
}
:root {
--cyan-100: 183 90% 90%;
--cyan-200: 183 90% 80%;
--cyan-300: 183 90% 70%;
--cyan-400: 183 90% 60%;
--cyan-500: 183 90% 50%;
--cyan-600: 183 90% 40%;
--cyan-700: 183 90% 30%;
--cyan-800: 183 90% 20%;
--cyan-900: 183 90% 10%;
}
html,
body {
height: 100%;
font-family: Helvetica, sans-serif;
}
pre {
display: inline-flex;
padding: 0.5rem;
width: 100%;
border-radius: 0.25rem
}
code {
background-color: rgba(44, 43, 43, 0.6);
color: rgb(190 18 60 / var(--tw-text-opacity));
border-radius: 0.25rem;
padding: 0 0.5rem;
}
body {
color: white;
background-color: #131416;
}
ol {
list-style: number;
padding: 1rem;
}
ol > li {
margin: 1rem 0;
}
.hover\:bg-gray-500\/10:hover {
background-color: rgb(107 114 128 / 0.1);
}
.hover\:bg-white\/5:hover {
background-color: rgb(255 255 255 / 0.05);
}
.hover\:text-white:hover {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.focus\:border-transparent:focus {
border-color: transparent;
}
.focus\:outline-none:focus {
outline: 2px solid transparent;
outline-offset: 2px;
}
.focus\:ring-1:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.focus\:ring-teal-300:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(94 234 212 / var(--tw-ring-opacity));
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{default as t}from"../entry/_layout.svelte.62a505d3.js";export{t as component};

View File

@ -0,0 +1 @@
import{default as t}from"../entry/error.svelte.a6bd0229.js";export{t as component};

View File

@ -0,0 +1 @@
import{default as t}from"../entry/_page.svelte.2f89fa28.js";export{t as component};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{H as f,a4 as y,P as m,s as q,Z as w}from"./index.3641fafa.js";const o=[];function z(e,u){return{subscribe:A(e,u).subscribe}}function A(e,u=f){let r;const n=new Set;function a(t){if(q(e,t)&&(e=t,r)){const i=!o.length;for(const s of n)s[1](),o.push(s,e);if(i){for(let s=0;s<o.length;s+=2)o[s][0](o[s+1]);o.length=0}}}function l(t){a(t(e))}function b(t,i=f){const s=[t,i];return n.add(s),n.size===1&&(r=u(a)||f),t(e),()=>{n.delete(s),n.size===0&&r&&(r(),r=null)}}return{set:a,update:l,subscribe:b}}function H(e,u,r){const n=!Array.isArray(e),a=n?[e]:e,l=u.length<2;return z(r,b=>{let t=!1;const i=[];let s=0,d=f;const g=()=>{if(s)return;d();const c=u(n?i[0]:i,b);l?b(c):d=w(c)?c:f},_=a.map((c,p)=>y(c,h=>{i[p]=h,s&=~(1<<p),t&&g()},()=>{s|=1<<p}));return t=!0,g(),function(){m(_),d(),t=!1}})}export{H as d,A as w};

View File

@ -0,0 +1 @@
import{w as u}from"./index.fa397836.js";var _;const g=((_=globalThis.__sveltekit_10ptubg)==null?void 0:_.base)??"";var h;const v=((h=globalThis.__sveltekit_10ptubg)==null?void 0:h.assets)??g,k="1680580103854",A="sveltekit:snapshot",R="sveltekit:scroll",T="sveltekit:index",f={tap:1,hover:2,viewport:3,eager:4,off:-1};function I(e){let t=e.baseURI;if(!t){const n=e.getElementsByTagName("base");t=n.length?n[0].href:e.URL}return t}function S(){return{x:pageXOffset,y:pageYOffset}}function i(e,t){return e.getAttribute(`data-sveltekit-${t}`)}const d={...f,"":f.hover};function b(e){let t=e.assignedSlot??e.parentNode;return(t==null?void 0:t.nodeType)===11&&(t=t.host),t}function y(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=b(e)}}function x(e,t){let n;try{n=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI)}catch{}const l=e instanceof SVGAElement?e.target.baseVal:e.target,a=!n||!!l||m(n,t)||(e.getAttribute("rel")||"").split(/\s+/).includes("external")||e.hasAttribute("download");return{url:n,external:a,target:l}}function O(e){let t=null,n=null,l=null,a=null,r=null,o=null,s=e;for(;s&&s!==document.documentElement;)l===null&&(l=i(s,"preload-code")),a===null&&(a=i(s,"preload-data")),t===null&&(t=i(s,"keepfocus")),n===null&&(n=i(s,"noscroll")),r===null&&(r=i(s,"reload")),o===null&&(o=i(s,"replacestate")),s=b(s);return{preload_code:d[l??"off"],preload_data:d[a??"off"],keep_focus:t==="off"?!1:t===""?!0:null,noscroll:n==="off"?!1:n===""?!0:null,reload:r==="off"?!1:r===""?!0:null,replace_state:o==="off"?!1:o===""?!0:null}}function p(e){const t=u(e);let n=!0;function l(){n=!0,t.update(o=>o)}function a(o){n=!1,t.set(o)}function r(o){let s;return t.subscribe(c=>{(s===void 0||n&&c!==s)&&o(s=c)})}return{notify:l,set:a,subscribe:r}}function E(){const{set:e,subscribe:t}=u(!1);let n;async function l(){clearTimeout(n);const a=await fetch(`${v}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(a.ok){const o=(await a.json()).version!==k;return o&&(e(!0),clearTimeout(n)),o}else throw new Error(`Version check failed: ${a.status}`)}return{subscribe:t,check:l}}function m(e,t){return e.origin!==location.origin||!e.pathname.startsWith(t)}function U(e){e.client}const L={url:p({}),page:p({}),navigating:u(null),updated:E()};export{T as I,f as P,R as S,A as a,x as b,O as c,S as d,g as e,y as f,I as g,U as h,m as i,L as s};

View File

@ -0,0 +1 @@
import{S as r,i as f,s as u,C as c,k as _,l as d,m,h as o,n as p,b as h,D as $,E as v,F as g,g as y,d as b}from"../chunks/index.3641fafa.js";function C(n){let s,a;const i=n[1].default,e=c(i,n,n[0],null);return{c(){s=_("div"),e&&e.c(),this.h()},l(t){s=d(t,"DIV",{class:!0});var l=m(s);e&&e.l(l),l.forEach(o),this.h()},h(){p(s,"class","flex flex-col items-center py-14")},m(t,l){h(t,s,l),e&&e.m(s,null),a=!0},p(t,[l]){e&&e.p&&(!a||l&1)&&$(e,i,t,t[0],a?g(i,t[0],l,null):v(t[0]),null)},i(t){a||(y(e,t),a=!0)},o(t){b(e,t),a=!1},d(t){t&&o(s),e&&e.d(t)}}}function D(n,s,a){let{$$slots:i={},$$scope:e}=s;return n.$$set=t=>{"$$scope"in t&&a(0,e=t.$$scope)},[e,i]}class S extends r{constructor(s){super(),f(this,s,D,C,u,{})}}export{S as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{S,i as q,s as x,k as f,q as _,a as H,l as d,m as g,r as h,h as u,c as k,b as m,G as v,u as $,H as E,I as y}from"../chunks/index.3641fafa.js";import{s as C}from"../chunks/singletons.8077db6c.js";const G=()=>{const s=C;return{page:{subscribe:s.page.subscribe},navigating:{subscribe:s.navigating.subscribe},updated:s.updated}},I={subscribe(s){return G().page.subscribe(s)}};function P(s){var b;let t,r=s[0].status+"",o,n,i,c=((b=s[0].error)==null?void 0:b.message)+"",l;return{c(){t=f("h1"),o=_(r),n=H(),i=f("p"),l=_(c)},l(e){t=d(e,"H1",{});var a=g(t);o=h(a,r),a.forEach(u),n=k(e),i=d(e,"P",{});var p=g(i);l=h(p,c),p.forEach(u)},m(e,a){m(e,t,a),v(t,o),m(e,n,a),m(e,i,a),v(i,l)},p(e,[a]){var p;a&1&&r!==(r=e[0].status+"")&&$(o,r),a&1&&c!==(c=((p=e[0].error)==null?void 0:p.message)+"")&&$(l,c)},i:E,o:E,d(e){e&&u(t),e&&u(n),e&&u(i)}}}function j(s,t,r){let o;return y(s,I,n=>r(0,o=n)),[o]}let A=class extends S{constructor(t){super(),q(this,t,j,P,x,{})}};export{A as default};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":"1680580103854"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

42
README.md Normal file
View File

@ -0,0 +1,42 @@
# Another SvelteKit ChatGPT UI
Video Link: https://ivanprojects.s3.us-west-1.amazonaws.com/Screen+Recording+2023-03-10+at+10.11.18+AM.mov
![](https://i.imgur.com/gKu0MbX.png)
## Features
- Formatted Markdown Responses
- Persisted Chat History (Setup for local storage but able to be adapted)
- Auto Scrolling/Message streaming
## Using the App
1. pnpm Install
2. Add an OPENAI_KEY to an .env file with your open ai key
3. Enjoy
## Stores
- Chat Messages
- Chat Messages (Derived)
- Answer
- Chat History
### Chat Messages
The chat messages store stores the transcript for the current active conversation.
When a question is submitted it posts to the api endpoint and if it gets a 200 starts to stream the answer.
**Answer Store**
The answer store is what holds answer being streamed. While the AI is typing the answer store is being updated, and when the response is 'DONE' the answer is appended to the active transcript and reset to a blank string.
The use of two separate stores allows for a single update to the entire transcript, error checking prior to updating and for there to be no visible difference in the UI when streaming the answer.
### Chat Messages (Derived)
The derived Chat Messages store gets the active conversation starting with the first query and checks to see if there is a 'chatHistory' key in local storage. This store acts as the bridge between the history store and the chat messages store. If there isn't any history in local storage, it creates a key and updates it if there is a query. Once the answer store finishes streaming the answer and updates the original Chat messages store, the derived store updates the key with updated transcript
### Chat history
The Chat history store holds the references to all the various conversations held in local storage. On clicking one it'll update the chat view to display the active transcript.
It also handles deleting any of the keys. The Chat history store is set by the derived store, since it is dependant on the existance of messages, the change in those messages, and the existance of a history in local storage.

9201
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

45
package.json Normal file
View File

@ -0,0 +1,45 @@
{
"name": "chatbot",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:unit": "vitest",
"lint": "prettier --plugin-search-dir . --check .",
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-vercel": "^2.3.0",
"@sveltejs/kit": "^1.5.0",
"@types/common-tags": "^1.8.1",
"@types/dompurify": "^2.4.0",
"@types/marked": "^4.0.8",
"autoprefixer": "^10.4.13",
"dompurify": "^3.0.1",
"gpt3-tokenizer": "^1.1.5",
"isomorphic-dompurify": "^1.1.0",
"marked": "^4.2.12",
"openai": "^3.2.1",
"postcss": "^8.4.21",
"postcss-load-config": "^4.0.1",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.8.1",
"sse.js": "^0.6.1",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"svelte-exmarkdown": "^2.0.0",
"svelte-preprocess": "^4.10.7",
"svelte-preprocess-markdown": "^2.7.3",
"tailwindcss": "^3.2.7",
"tslib": "^2.4.1",
"typescript": "^4.9.3",
"vite": "^4.0.0",
"vitest": "^0.25.3"
},
"type": "module"
}

13
postcss.config.cjs Normal file
View File

@ -0,0 +1,13 @@
const tailwindcss = require("tailwindcss");
const autoprefixer = require("autoprefixer");
const config = {
plugins: [
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer,
],
};
module.exports = config;

39
src/app.css Normal file
View File

@ -0,0 +1,39 @@
/* Write your global styles here, in PostCSS syntax */
@import './styles/tailwind.css';
@import './styles/vars.css';
html,
body {
height: 100%;
font-family: Helvetica, sans-serif;
}
pre {
display: inline-flex;
padding: 0.5rem;
width: 100%;
border-radius: 0.25rem
}
code {
background-color: rgba(44, 43, 43, 0.6);
color: rgb(190 18 60 / var(--tw-text-opacity));
border-radius: 0.25rem;
padding: 0 0.5rem;
}
body {
color: white;
background-color: #131416;
}
ol {
list-style: number;
padding: 1rem;
}
ol > li {
margin: 1rem 0;
}

12
src/app.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

12
src/app.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

7
src/index.test.ts Normal file
View File

@ -0,0 +1,7 @@
import { describe, it, expect } from 'vitest';
describe('sum test', () => {
it('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
});

View File

@ -0,0 +1,61 @@
<script lang="ts">
import { onMount } from 'svelte';
import Chat from './Icons/Chat.svelte';
import Pencil from './Icons/Pencil.svelte';
import Plus from './Icons/Plus.svelte';
import Trash from './Icons/Trash.svelte';
import { chatMessages } from '$lib/stores/chat-messages';
import {
chatHistory,
filterHistory,
chatHistorySubscription,
loadMessages
} from '../stores/chat-history';
let chatHistoryKeys: any = [];
onMount(() => {
chatHistorySubscription.set($chatHistory);
chatHistorySubscription.subscribe((value: any) => {
chatHistoryKeys = Object.keys(value);
});
});
</script>
<div
class="h-[700px] w-[350px] bg-black bg-opacity-20 rounded-md py-4 px-2 overflow-y-auto flex flex-col gap-2"
>
<button
on:click={chatMessages.reset}
class="flex py-3 px-3 items-center gap-3 rounded-md hover:bg-gray-500/10 transition-colors duration-200 text-white cursor-pointer text-sm mb-2 flex-shrink-0 border border-white/20"
>
<Plus /> New Game
</button>
{#if chatHistoryKeys.length > 0}
{#each chatHistoryKeys as message (message)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
on:click={() => loadMessages(message)}
class="flex py-3 px-3 items-center gap-3 relative rounded-md cursor-pointer break-all pr-14 bg-opacity-40 hover:bg-white/5 bg-black group animate-flash text-sm"
>
<Chat />
<div class="flex-1 text-ellipsis max-h-5 overflow-hidden break-all relative">{message}</div>
<div class="absolute flex right-1 z-10 text-gray-300 visible">
<button on:click={() => loadMessages(message)} class="p-1 hover:text-white">
<Pencil />
</button>
<button
on:click|preventDefault={() => filterHistory(message)}
class="p-1 hover:text-white"
>
<Trash />
</button>
</div>
</div>
{/each}
{/if}
</div>

View File

@ -0,0 +1,46 @@
<script lang="ts">
import Markdown from 'svelte-exmarkdown';
import { marked } from 'marked';
import DOMPurify from 'isomorphic-dompurify';
import type { ChatCompletionRequestMessageRoleEnum } from 'openai';
import { onMount } from 'svelte';
export let type: ChatCompletionRequestMessageRoleEnum;
export let message: string = '';
export { classes as class };
let classes = '';
let scrollToDiv: HTMLDivElement;
const classSet = {
user: 'justify-end text-rose-700',
assistant: 'justify-start text-teal-400',
system: 'justify-center text-gray-400'
};
const typeEffect = (node: HTMLDivElement, message: string) => {
return {
update(message: string) {
scrollToDiv.scrollIntoView({ behavior: 'auto', block: 'end', inline: 'end' });
}
};
};
onMount(() => {
scrollToDiv.scrollIntoView({ behavior: 'auto', block: 'end', inline: 'end' });
});
</script>
<div class="flex items-center {classSet[type]} ">
<p class="text-xs px-2">{type === 'user' ? 'Me' : 'Bot'}</p>
</div>
<div class="flex {classSet[type]}">
<div
use:typeEffect={message}
class="bg-black py-0.5 px-4 max-w-2xl rounded leading-loose {classes} {classSet[type]}"
>
{@html DOMPurify.sanitize(marked.parse(message))}
</div>
<div bind:this={scrollToDiv} />
</div>

View File

@ -0,0 +1,13 @@
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /></svg
>

After

Width:  |  Height:  |  Size: 293 B

View File

@ -0,0 +1,13 @@
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
><path d="M12 20h9" /><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" /></svg
>

After

Width:  |  Height:  |  Size: 308 B

View File

@ -0,0 +1,13 @@
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
><line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" /></svg
>

After

Width:  |  Height:  |  Size: 297 B

View File

@ -0,0 +1,20 @@
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
><polyline points="3 6 5 6 21 6" /><path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
/><line x1="10" y1="11" x2="10" y2="17" /><line
x1="14"
y1="11"
x2="14"
y2="17"
/></svg
>

After

Width:  |  Height:  |  Size: 422 B

View File

@ -0,0 +1,25 @@
<script lang="ts">
export let value: any;
export let placeholder = '';
export let name = '';
export let type = 'text';
export let label = '';
let classes = '';
export { classes as class };
const typeAction = (node: HTMLInputElement) => {
node.type = type;
};
</script>
<div class="flex flex-col min-w-xl {classes}">
<label class="text-xs" for={name}>{label}</label>
<input
{name}
{placeholder}
use:typeAction
bind:value
class="shadow bg-white/10 rounded-md px-4 py-1.5 min-w-xl text-teal-300 border border-transparent focus:outline-none focus:ring-1 focus:ring-teal-300 focus:border-transparent"
{...$$restProps}
/>
</div>

View File

@ -0,0 +1,53 @@
import { derived, get, writable } from 'svelte/store';
import { chatMessages, type ChatTranscript } from './chat-messages';
import { browser } from '$app/environment';
export const chatHistorySubscription = writable();
const setLocalHistory = <T>(history: T) =>
localStorage.setItem('chatHistory', JSON.stringify(history));
const getLocalHistory = () => JSON.parse(localStorage.getItem('chatHistory') || '{}');
export const chatHistory = derived(chatMessages, ($chatMessages) => {
if (!browser) return null;
let history = localStorage.getItem('chatHistory');
if (!history && $chatMessages.messages.length === 1) return null;
if (history && $chatMessages.messages.length === 1) return JSON.parse(history);
const key = $chatMessages.messages[1].content; //The second message is the query
const value = $chatMessages.messages;
const obj = { [key]: value };
if (!history) setLocalHistory(obj);
const chatHistory = getLocalHistory();
if (chatHistory) {
chatHistory[key] = value;
setLocalHistory(chatHistory);
chatHistorySubscription.set(chatHistory);
return chatHistory;
}
return null;
});
export const filterHistory = (key: string) => {
const history = getLocalHistory();
delete history[key];
setLocalHistory(history);
chatHistorySubscription.set(history);
};
const getHistory = (key: string) => getLocalHistory()[key]; //Returns the history for a given key
export const loadMessages = (query: string) => {
if (get(chatMessages).chatState !== 'idle') return; //Prevents switching between messages while loading
if (!query) return;
const newMessages = getHistory(query);
chatMessages.replace({ messages: newMessages, chatState: 'idle' });
};

View File

@ -0,0 +1,80 @@
import type { ChatCompletionRequestMessage } from 'openai';
import { SSE } from 'sse.js';
import { get, writable } from 'svelte/store';
export interface ChatTranscript {
messages: ChatCompletionRequestMessage[];
chatState: 'idle' | 'loading' | 'error' | 'message';
}
const { subscribe, update, ...store } = writable<ChatTranscript>({
messages: [
{ role: 'assistant', content: 'Welcome! Please introduce yourself to your AI competitor.'}
],
chatState: 'idle'
});
const set = async (query: string) => {
updateMessages(query, 'user', 'loading');
request(query);
};
const request = async (query: string) => {
const eventSource = new SSE('/api/chat', {
headers: {
'Content-Type': 'application/json'
},
payload: JSON.stringify({ messages: get(chatMessages).messages })
});
eventSource.addEventListener('error', handleError);
eventSource.addEventListener('message', streamMessage);
eventSource.stream();
}
const replace = (messages: ChatTranscript) => {
store.set(messages);
};
const reset = () =>
store.set({
messages: [
{ role: 'assistant', content: 'Welcome! Please introduce yourself to your AI competitor.' }
],
chatState: 'idle'
});
const updateMessages = (content: any, role: any, state: any) => {
chatMessages.update((messages: ChatTranscript) => {
return { messages: [...messages.messages, { role: role, content: content }], chatState: state };
});
};
const handleError = <T>(err: T) => {
updateMessages(err, 'system', 'error');
console.error(err);
};
const streamMessage = (e: MessageEvent) => {
try {
if (e.data === '[DONE]') {
updateMessages(get(answer), 'assistant', 'idle');
return answer.set('');
}
if (get(answer) === '...') answer.set('');
const completionResponse = JSON.parse(e.data);
const [{ delta }] = completionResponse.choices;
if (delta.content) {
answer.update((_a) => _a + delta.content);
}
} catch (err) {
handleError(err);
}
};
export const chatMessages = { subscribe, set, update, reset, replace };
export const answer = writable<string>('');

View File

@ -0,0 +1,13 @@
import GPT3TokenizerImport from 'gpt3-tokenizer';
const GPT3Tokenizer: typeof GPT3TokenizerImport =
typeof GPT3TokenizerImport === 'function'
? GPT3TokenizerImport
: (GPT3TokenizerImport as any).default;
const tokenizer = new GPT3Tokenizer({ type: 'gpt3' });
export function getTokens(input: string): number {
const tokens = tokenizer.encode(input);
return tokens.text.length;
}

View File

@ -0,0 +1,7 @@
<script>
import '../app.css';
</script>
<div class="flex flex-col items-center py-14">
<slot />
</div>

69
src/routes/+page.svelte Normal file
View File

@ -0,0 +1,69 @@
<script lang="ts">
import ChatHistory from '$lib/components/ChatHistory.svelte';
import ChatMessage from '$lib/components/ChatMessage.svelte';
import Input from '$lib/components/Input.svelte';
import { chatMessages, answer } from '$lib/stores/chat-messages';
import { onMount } from 'svelte';
let query = '';
const INITIAL_QUERY = `You will erroneously define a randomly chosen English word with the definition of an English word that sounds similar. You will then ask them to guess the correct word for the errornous definition.
Here is an example of what you would output:
Today we'll be playing a word game. Here's how it works: I will define a word. But the definition I give it will actually be for a different but similarly-sounding word. You'll have to guess the correct word that is being defined.
Ready to begin? Here's the first one:
"A photon is a legendary bird which according to one account lived 500 years, burned itself to ashes on a pyre, and rose alive from the ashes to live another period. What word am I actually describing?"
---
To further explain the output, I chose two words that sound similar, photon and phoenix. I am then using the definition of a phoenix to describe a photon, and the user will have to guess that the real word I'm describing is a phoenix.
Repeat this process until the user incorrectly guesses 3 times.`;
// onMount(async () => {
// await chatMessages.set(INITIAL_QUERY);
// });
const handleSubmit = async () => {
answer.set('...');
await chatMessages.set(query);
query = '';
};
</script>
<section class="flex max-w-6xl w-full pt-4 justify-center">
<div class="flex flex-col gap-2">
<ChatHistory />
</div>
<div class="flex flex-col w-full px-8 items-center gap-2">
<div
class="h-[700px] w-full bg-black bg-opacity-20 rounded-md p-4 overflow-y-auto flex flex-col gap-4"
>
<div class="flex flex-col gap-2">
{#each $chatMessages.messages as message}
<ChatMessage type={message.role} message={message.content} />
{/each}
{#if $answer}
<ChatMessage type="assistant" message={$answer} />
{/if}
</div>
</div>
<form
class="flex w-full rounded-md gap-4 bg-black bg-opacity-20 p-2"
on:submit|preventDefault={handleSubmit}
>
<Input type="text" bind:value={query} class="w-full" />
<button
type="submit"
class="bg-black bg-opacity-40 hover:bg-white/5 px-8 py-1.5 border border-black/40 ml-[-0.5rem] rounded-md text-teal-300"
>
Send
</button>
</form>
</div>
</section>

View File

@ -0,0 +1,115 @@
import { OPENAI_KEY } from '$env/static/private';
import type { CreateChatCompletionRequest, ChatCompletionRequestMessage } from 'openai';
import type { RequestHandler } from './$types';
import { getTokens } from '$lib/utils/tokenizer';
import { json } from '@sveltejs/kit';
import type { Config } from '@sveltejs/adapter-vercel';
export const config: Config = {
runtime: 'edge'
};
const INITIAL_QUERY = `You are playing a game with the user. You will erroneously define a randomly chosen English word with the definition of an English word that sounds similar. You will then ask them to guess the correct word for the errornous definition.
Here is an example of what you would output.
---
Nice to meet you. Today we'll be playing a word game. Here's how it works: I will define a word. But the definition I give it will actually be for a different but similarly-sounding word. You'll have to guess the correct word that is being defined.
Ready to begin? Here's the first one:
"A photon is a legendary bird which according to one account lived 500 years, burned itself to ashes on a pyre, and rose alive from the ashes to live another period. What word am I actually describing?"
---
To further explain the output, I chose two words that sound similar, photon and phoenix. I am then using the definition of a phoenix to describe a photon, and the user will have to guess that the real word I'm describing is a phoenix.
Repeat this process until the user incorrectly guesses 3 times.`;
export const POST: RequestHandler = async ({ request }) => {
try {
if (!OPENAI_KEY) {
throw new Error('OPENAI_KEY env variable not set');
}
const requestData = await request.json();
if (!requestData) {
throw new Error('No request data');
}
const reqMessages: ChatCompletionRequestMessage[] = requestData.messages;
if (!reqMessages) {
throw new Error('no messages provided');
}
let tokenCount = 0;
reqMessages.forEach((msg) => {
const tokens = getTokens(msg.content);
tokenCount += tokens;
});
const moderationRes = await fetch('https://api.openai.com/v1/moderations', {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${OPENAI_KEY}`
},
method: 'POST',
body: JSON.stringify({
input: reqMessages[reqMessages.length - 1].content
})
});
const moderationData = await moderationRes.json();
const [results] = moderationData.results;
if (results.flagged) {
throw new Error('Query flagged by openai');
}
const prompt = INITIAL_QUERY;
tokenCount += getTokens(prompt);
if (tokenCount >= 4000) {
throw new Error('Query too large');
}
const messages: ChatCompletionRequestMessage[] = [
{ role: 'system', content: prompt },
...reqMessages
];
const chatRequestOpts: CreateChatCompletionRequest = {
model: 'gpt-3.5-turbo',
messages,
temperature: 0.5,
stream: true
};
const chatResponse = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
Authorization: `Bearer ${OPENAI_KEY}`,
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(chatRequestOpts)
});
if (!chatResponse.ok) {
const err = await chatResponse.json();
throw new Error(err);
}
return new Response(chatResponse.body, {
headers: {
'Content-Type': 'text/event-stream'
}
});
} catch (err) {
console.error(err);
return json({ error: 'There was an error processing your request' }, { status: 500 });
}
};

3
src/styles/tailwind.css Normal file
View File

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

11
src/styles/vars.css Normal file
View File

@ -0,0 +1,11 @@
:root {
--cyan-100: 183 90% 90%;
--cyan-200: 183 90% 80%;
--cyan-300: 183 90% 70%;
--cyan-400: 183 90% 60%;
--cyan-500: 183 90% 50%;
--cyan-600: 183 90% 40%;
--cyan-700: 183 90% 30%;
--cyan-800: 183 90% 20%;
--cyan-900: 183 90% 10%;
}

12
src/types/sse.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
declare module 'sse.js' {
export type SSEOptions = EventSourceInit & {
headers?: Record<string, string>
payload?: string
method?: string
}
export class SSE extends EventSource {
constructor(url: string | URL, sseOptions?: SSEOptions)
stream(): void
}
}

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

23
svelte.config.js Normal file
View File

@ -0,0 +1,23 @@
import preprocess from "svelte-preprocess";
import adapter from '@sveltejs/adapter-vercel';
import { vitePreprocess } from '@sveltejs/kit/vite';
import {markdown} from 'svelte-preprocess-markdown';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [vitePreprocess(), preprocess({
postcss: true
}),
markdown()
],
kit: {
adapter: adapter({
runtime: 'nodejs18.x'
})
}
};
export default config;

9
tailwind.config.cjs Normal file
View File

@ -0,0 +1,9 @@
const defaultTheme = require('tailwindcss/defaultTheme')
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {},
plugins: [],
}

18
tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"ignoreDeprecations": "5.0"
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

10
vite.config.ts Normal file
View File

@ -0,0 +1,10 @@
import { sveltekit } from '@sveltejs/kit/vite';
import adapter from '@sveltejs/adapter-vercel';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [sveltekit()],
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
}
});