diff --git a/main.go b/main.go
index 0a77802..c9d535e 100644
--- a/main.go
+++ b/main.go
@@ -2,6 +2,8 @@ package main
import (
"net/http"
+ "path/filepath"
+ "strings"
"github.com/coalaura/logger"
adapter "github.com/coalaura/logger/http"
@@ -23,7 +25,7 @@ func main() {
r.Use(adapter.Middleware(log))
fs := http.FileServer(http.Dir("./static"))
- r.Handle("/*", http.StripPrefix("/", fs))
+ r.Handle("/*", cache(http.StripPrefix("/", fs)))
r.Get("/-/models", func(w http.ResponseWriter, r *http.Request) {
RespondJson(w, http.StatusOK, models)
@@ -34,3 +36,16 @@ func main() {
log.Debug("Listening at http://localhost:3443/")
http.ListenAndServe(":3443", r)
}
+
+func cache(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ path := strings.ToLower(r.URL.Path)
+ ext := filepath.Ext(path)
+
+ if ext == ".svg" || ext == ".ttf" || strings.HasSuffix(path, ".min.js") {
+ w.Header().Set("Cache-Control", "public, max-age=3024000, immutable")
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/models.go b/models.go
index 493dcbb..738454d 100644
--- a/models.go
+++ b/models.go
@@ -4,13 +4,15 @@ import (
"context"
"sort"
"strings"
+
+ "github.com/revrost/go-openrouter"
)
type Model struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Description string `json:"description"`
- SupportedParameters []string `json:"supported_parameters,omitempty"`
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Tags []string `json:"tags,omitempty"`
}
var ModelMap = make(map[string]*Model)
@@ -37,10 +39,10 @@ func LoadModels() ([]*Model, error) {
}
m := &Model{
- ID: model.ID,
- Name: name,
- Description: model.Description,
- SupportedParameters: model.SupportedParameters,
+ ID: model.ID,
+ Name: name,
+ Description: model.Description,
+ Tags: GetModelTags(model),
}
models[index] = m
@@ -50,3 +52,17 @@ func LoadModels() ([]*Model, error) {
return models, nil
}
+
+func GetModelTags(model openrouter.Model) []string {
+ var tags []string
+
+ for _, parameter := range model.SupportedParameters {
+ if parameter == "reasoning" || parameter == "tools" {
+ tags = append(tags, parameter)
+ }
+ }
+
+ sort.Strings(tags)
+
+ return tags
+}
diff --git a/static/css/chat.css b/static/css/chat.css
index cb221d8..1c34461 100644
--- a/static/css/chat.css
+++ b/static/css/chat.css
@@ -34,13 +34,13 @@
}
::-webkit-scrollbar-track {
- background: #181926;
+ background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: #cad3f5;
+ border: none;
border-radius: 4px;
- border: 1px solid #181926;
}
::-webkit-scrollbar-thumb:hover {
@@ -49,7 +49,7 @@
* {
scrollbar-width: thin;
- scrollbar-color: #cad3f5 #181926;
+ scrollbar-color: #cad3f5 transparent;
}
html,
diff --git a/static/css/dropdown.css b/static/css/dropdown.css
index 8db5fd4..bf65a25 100644
--- a/static/css/dropdown.css
+++ b/static/css/dropdown.css
@@ -48,6 +48,14 @@
overflow-y: auto;
}
+.dropdown .selected,
+.dropdown .opt {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 5px;
+}
+
.dropdown .opt {
padding: 4px 6px;
cursor: pointer;
@@ -66,6 +74,34 @@
display: none;
}
+.dropdown .label {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ max-width: 300px;
+}
+
+.dropdown .tags {
+ display: flex;
+ gap: 2px;
+}
+
+.dropdown .tags .tag {
+ width: 18px;
+ height: 18px;
+ background-position: center;
+ background-size: contain;
+ background-repeat: no-repeat;
+}
+
+.tags .tag.reasoning {
+ background-image: url(icons/tags/reasoning.svg)
+}
+
+.tags .tag.tools {
+ background-image: url(icons/tags/tools.svg)
+}
+
.dropdown .search {
background: #2a2e41;
border-top: 2px solid #494d64;
diff --git a/static/css/icons/tags/reasoning.svg b/static/css/icons/tags/reasoning.svg
new file mode 100644
index 0000000..fb5d3e6
--- /dev/null
+++ b/static/css/icons/tags/reasoning.svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/static/css/icons/tags/tools.svg b/static/css/icons/tags/tools.svg
new file mode 100644
index 0000000..e8d00b5
--- /dev/null
+++ b/static/css/icons/tags/tools.svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/static/js/chat.js b/static/js/chat.js
index 2c138d2..b793d3a 100644
--- a/static/js/chat.js
+++ b/static/js/chat.js
@@ -448,8 +448,11 @@
const el = document.createElement("option");
el.value = model.id;
+ el.title = model.description;
el.textContent = model.name;
+ el.dataset.tags = (model.tags || []).join(",");
+
$model.appendChild(el);
}
diff --git a/static/js/dropdown.js b/static/js/dropdown.js
index 08bb763..99c6d21 100644
--- a/static/js/dropdown.js
+++ b/static/js/dropdown.js
@@ -15,9 +15,15 @@
this.#search = "searchable" in el.dataset;
this.#_select.querySelectorAll("option").forEach((option) => {
+ const tags = option.dataset.tags?.trim();
+
this.#options.push({
value: option.value,
label: option.textContent,
+
+ title: option.title || false,
+ tags: tags ? tags.split(",") : [],
+
search: searchable(option.textContent),
});
});
@@ -75,9 +81,10 @@
// options
for (const option of this.#options) {
+ // option wrapper
const _opt = make("div", "opt");
- _opt.textContent = option.label;
+ _opt.title = option.title || "";
_opt.addEventListener("click", () => {
this.#_select.value = option.value;
@@ -85,6 +92,30 @@
this.#_dropdown.classList.remove("open");
});
+ // option label
+ const _label = make("div", "label");
+
+ _label.title = option.label;
+ _label.textContent = option.label;
+
+ _opt.appendChild(_label);
+
+ // option tags (optional)
+ if (option.tags?.length) {
+ const _tags = make("div", "tags");
+
+ for (const tag of option.tags) {
+ const _tag = make("div", "tag", tag);
+
+ _tag.title = tag;
+
+ _tags.appendChild(_tag);
+ }
+
+ _opt.appendChild(_tags);
+ }
+
+ // add to options
_options.appendChild(_opt);
option.el = _opt;
@@ -128,14 +159,14 @@
#render() {
if (this.#selected === false) {
- this.#_selected.textContent = "";
+ this.#_selected.innerHTML = "";
return;
}
const selection = this.#options[this.#selected];
- this.#_selected.textContent = selection.label;
+ this.#_selected.innerHTML = selection.el.innerHTML;
}
#filter() {
diff --git a/static/js/lib.js b/static/js/lib.js
index 2aaf6a4..2c1f222 100644
--- a/static/js/lib.js
+++ b/static/js/lib.js
@@ -45,7 +45,9 @@ function uid() {
function make(tag, ...classes) {
const el = document.createElement(tag);
- el.classList.add(...classes);
+ if (classes.length) {
+ el.classList.add(...classes);
+ }
return el;
}