Hosting dependencies on server, added logo to welcome page and some fixs

This commit is contained in:
Adrien Bouvais 2024-08-04 21:53:26 +02:00
parent fa1bd44a99
commit f76ebd7491
13 changed files with 1601 additions and 22 deletions

3
static/dependencies/bulma.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
/*!
Theme: Default
Description: Original highlight.js style
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
Maintainer: @highlightjs/core-team
Website: https://highlightjs.org/
License: see project LICENSE
Touched: 2021
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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,24 @@
/* cyrillic */
@font-face {
font-family: 'Russo One';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/russoone/v16/Z9XUDmZRWg6M1LvRYsHOy8mJrrg.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* latin-ext */
@font-face {
font-family: 'Russo One';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/russoone/v16/Z9XUDmZRWg6M1LvRYsHOwcmJrrg.woff2) format('woff2');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Russo One';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/russoone/v16/Z9XUDmZRWg6M1LvRYsHOz8mJ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

File diff suppressed because one or more lines are too long

301
static/dependencies/sse.js Normal file
View File

@ -0,0 +1,301 @@
/*
Server Sent Events Extension
============================
This extension adds support for Server Sent Events to htmx. See /www/extensions/sse.md for usage instructions.
*/
(function() {
/** @type {import("../htmx").HtmxInternalApi} */
var api
htmx.defineExtension('sse', {
/**
* Init saves the provided reference to the internal HTMX API.
*
* @param {import("../htmx").HtmxInternalApi} api
* @returns void
*/
init: function(apiRef) {
// store a reference to the internal API.
api = apiRef
// set a function in the public API for creating new EventSource objects
if (htmx.createEventSource == undefined) {
htmx.createEventSource = createEventSource
}
},
/**
* onEvent handles all events passed to this extension.
*
* @param {string} name
* @param {Event} evt
* @returns void
*/
onEvent: function(name, evt) {
var parent = evt.target || evt.detail.elt
switch (name) {
case 'htmx:beforeCleanupElement':
var internalData = api.getInternalData(parent)
// Try to remove remove an EventSource when elements are removed
if (internalData.sseEventSource) {
internalData.sseEventSource.close()
}
return
// Try to create EventSources when elements are processed
case 'htmx:afterProcessNode':
ensureEventSourceOnElement(parent)
}
}
})
/// ////////////////////////////////////////////
// HELPER FUNCTIONS
/// ////////////////////////////////////////////
/**
* createEventSource is the default method for creating new EventSource objects.
* it is hoisted into htmx.config.createEventSource to be overridden by the user, if needed.
*
* @param {string} url
* @returns EventSource
*/
function createEventSource(url) {
return new EventSource(url, { withCredentials: true })
}
/**
* registerSSE looks for attributes that can contain sse events, right
* now hx-trigger and sse-swap and adds listeners based on these attributes too
* the closest event source
*
* @param {HTMLElement} elt
*/
function registerSSE(elt) {
// Add message handlers for every `sse-swap` attribute
queryAttributeOnThisOrChildren(elt, 'sse-swap').forEach(function(child) {
// Find closest existing event source
var sourceElement = api.getClosestMatch(child, hasEventSource)
if (sourceElement == null) {
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
return null // no eventsource in parentage, orphaned element
}
// Set internalData and source
var internalData = api.getInternalData(sourceElement)
var source = internalData.sseEventSource
var sseSwapAttr = api.getAttributeValue(child, 'sse-swap')
var sseEventNames = sseSwapAttr.split(',')
for (var i = 0; i < sseEventNames.length; i++) {
var sseEventName = sseEventNames[i].trim()
var listener = function(event) {
// If the source is missing then close SSE
if (maybeCloseSSESource(sourceElement)) {
return
}
// If the body no longer contains the element, remove the listener
if (!api.bodyContains(child)) {
source.removeEventListener(sseEventName, listener)
return
}
// swap the response into the DOM and trigger a notification
if (!api.triggerEvent(elt, 'htmx:sseBeforeMessage', event)) {
return
}
swap(child, event.data)
api.triggerEvent(elt, 'htmx:sseMessage', event)
}
// Register the new listener
api.getInternalData(child).sseEventListener = listener
source.addEventListener(sseEventName, listener)
}
})
// Add message handlers for every `hx-trigger="sse:*"` attribute
queryAttributeOnThisOrChildren(elt, 'hx-trigger').forEach(function(child) {
// Find closest existing event source
var sourceElement = api.getClosestMatch(child, hasEventSource)
if (sourceElement == null) {
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
return null // no eventsource in parentage, orphaned element
}
// Set internalData and source
var internalData = api.getInternalData(sourceElement)
var source = internalData.sseEventSource
var sseEventName = api.getAttributeValue(child, 'hx-trigger')
if (sseEventName == null) {
return
}
// Only process hx-triggers for events with the "sse:" prefix
if (sseEventName.slice(0, 4) != 'sse:') {
return
}
var listener = function(event) {
if (maybeCloseSSESource(sourceElement)) {
return
}
if (!api.bodyContains(child)) {
source.removeEventListener(sseEventName, listener)
}
// Trigger events to be handled by the rest of htmx
htmx.trigger(child, sseEventName, event)
htmx.trigger(child, 'htmx:sseMessage', event)
}
// Register the new listener
api.getInternalData(elt).sseEventListener = listener
source.addEventListener(sseEventName.slice(4), listener)
})
}
/**
* ensureEventSourceOnElement creates a new EventSource connection on the provided element.
* If a usable EventSource already exists, then it is returned. If not, then a new EventSource
* is created and stored in the element's internalData.
* @param {HTMLElement} elt
* @param {number} retryCount
* @returns {EventSource | null}
*/
function ensureEventSourceOnElement(elt, retryCount) {
if (elt == null) {
return null
}
// handle extension source creation attribute
queryAttributeOnThisOrChildren(elt, 'sse-connect').forEach(function(child) {
var sseURL = api.getAttributeValue(child, 'sse-connect')
if (sseURL == null) {
return
}
ensureEventSource(child, sseURL, retryCount)
})
registerSSE(elt)
}
function ensureEventSource(elt, url, retryCount) {
var source = htmx.createEventSource(url)
source.onerror = function(err) {
// Log an error event
api.triggerErrorEvent(elt, 'htmx:sseError', { error: err, source })
// If parent no longer exists in the document, then clean up this EventSource
if (maybeCloseSSESource(elt)) {
return
}
// Otherwise, try to reconnect the EventSource
if (source.readyState === EventSource.CLOSED) {
retryCount = retryCount || 0
var timeout = Math.random() * (2 ^ retryCount) * 500
window.setTimeout(function() {
ensureEventSourceOnElement(elt, Math.min(7, retryCount + 1))
}, timeout)
}
}
source.onopen = function(evt) {
api.triggerEvent(elt, 'htmx:sseOpen', { source })
}
api.getInternalData(elt).sseEventSource = source
}
/**
* maybeCloseSSESource confirms that the parent element still exists.
* If not, then any associated SSE source is closed and the function returns true.
*
* @param {HTMLElement} elt
* @returns boolean
*/
function maybeCloseSSESource(elt) {
if (!api.bodyContains(elt)) {
var source = api.getInternalData(elt).sseEventSource
if (source != undefined) {
source.close()
// source = null
return true
}
}
return false
}
/**
* queryAttributeOnThisOrChildren returns all nodes that contain the requested attributeName, INCLUDING THE PROVIDED ROOT ELEMENT.
*
* @param {HTMLElement} elt
* @param {string} attributeName
*/
function queryAttributeOnThisOrChildren(elt, attributeName) {
var result = []
// If the parent element also contains the requested attribute, then add it to the results too.
if (api.hasAttribute(elt, attributeName)) {
result.push(elt)
}
// Search all child nodes that match the requested attribute
elt.querySelectorAll('[' + attributeName + '], [data-' + attributeName + ']').forEach(function(node) {
result.push(node)
})
return result
}
/**
* @param {HTMLElement} elt
* @param {string} content
*/
function swap(elt, content) {
api.withExtensions(elt, function(extension) {
content = extension.transformResponse(content, null, elt)
})
var swapSpec = api.getSwapSpecification(elt)
var target = api.getTarget(elt)
api.swap(target, content, swapSpec)
}
/**
* doSettle mirrors much of the functionality in htmx that
* settles elements after their content has been swapped.
* TODO: this should be published by htmx, and not duplicated here
* @param {import("../htmx").HtmxSettleInfo} settleInfo
* @returns () => void
*/
function doSettle(settleInfo) {
return function() {
settleInfo.tasks.forEach(function(task) {
task.call()
})
settleInfo.elts.forEach(function(elt) {
if (elt.classList) {
elt.classList.remove(htmx.config.settlingClass)
}
api.triggerEvent(elt, 'htmx:afterSettle')
})
}
}
function hasEventSource(node) {
return api.getInternalData(node).sseEventSource != null
}
})()

View File

@ -3,6 +3,25 @@ html {
margin: 0;
}
.logo-container {
width: 100%;
max-width: 500px;
margin: 0 auto;
}
.mylogo {
font-family: 'Russo One', sans-serif;
width: 100%;
height: auto;
display: block;
}
.mylogo text {
text-transform: uppercase;
fill: #fff;
font-size: 450px;
}
/* Stuff for message boxes */
#chat-messages .message-content pre {
overflow-x: auto;

View File

@ -6,28 +6,21 @@
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<title>JADE</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bulma@1.0.0/css/bulma.min.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
<link href="https://fonts.googleapis.com/css?family=Russo+One"
rel="stylesheet">
<!--link rel="stylesheet" href="/animations.css"-->
<link rel="stylesheet" href="dependencies/bulma.css">
<script src="https://kit.fontawesome.com/f515f7ad51.js"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="dependencies/russo.css">
<link rel="stylesheet" href="/style.css">
<script src="https://unpkg.com/htmx.org@2.0.0-beta4/dist/htmx.js"></script>
<script src="https://unpkg.com/htmx-ext-sse@2.0.0/sse.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
<script async src="https://js.stripe.com/v3/pricing-table.js"></script>
<script src="dependencies/htmx.js"></script>
<script src="dependencies/sse.js"></script>
<script src="dependencies/sortable.js"></script>
<script src="dependencies/pricing-table.js"></script>
<!-- highlight.js -->
<link rel="stylesheet"
href="https://unpkg.com/@highlightjs/cdn-assets@11.9.0/styles/default.min.css">
<script src="https://unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js"></script>
<link rel="stylesheet" href="dependencies/default.css">
<script src="dependencies/highlight.js"></script>
<script>hljs.highlightAll();</script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="dependencies/marked.js"></script>
<script src="wasm_exec.js"></script>
<script>

View File

@ -56,7 +56,7 @@
responses to choose the best one.
</li>
<li>
The selected response can be used as the basis for the next message across
The selected response can be used for the next message across
all models. For example, a response from GPT-4 can be used by Claude Haiku
in the next interaction.
</li>

View File

@ -1,4 +1,11 @@
<h1 class="title is-1">JADE: Simple Multi-Model Chatbot</h1>
<div class="logo-container">
<svg class="mylogo" viewBox="0 0 1320 500">
<text x="50%" y="50%" dy=".35em" text-anchor="middle">
JADE
</text>
</svg>
</div>
<h1 class="title is-1">Cheap Multi-Model Chatbot</h1>
<br />
<br />
<p>
@ -21,7 +28,7 @@
<p>For example, a response from GPT-4 Omni can be used by Claude Haiku.</p>
<a class="button is-primary mt-2 mb-2" href="/signin">Try JADE now for free!</a>
<a class="button is-primary mt-2 mb-2" href="/signin">Login</a>
<br />
<br />