mirror of
https://github.com/mudler/LocalAI.git
synced 2025-05-27 14:05:00 +00:00
fix(ux): fix small glitches (#2265)
also drop duplicates for displaying in-progress model ops Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
parent
6559ac11b1
commit
5ff5f0b393
6 changed files with 104 additions and 90 deletions
|
@ -12,17 +12,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NoImage = "https://upload.wikimedia.org/wikipedia/commons/6/65/No-Image-Placeholder.svg"
|
noImage = "https://upload.wikimedia.org/wikipedia/commons/6/65/No-Image-Placeholder.svg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DoneProgress(galleryID, text string, showDelete bool) string {
|
func DoneProgress(galleryID, text string, showDelete bool) string {
|
||||||
|
var modelName = galleryID
|
||||||
// Split by @ and grab the name
|
// Split by @ and grab the name
|
||||||
if strings.Contains(galleryID, "@") {
|
if strings.Contains(galleryID, "@") {
|
||||||
galleryID = strings.Split(galleryID, "@")[1]
|
modelName = strings.Split(galleryID, "@")[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return elem.Div(
|
return elem.Div(
|
||||||
attrs.Props{},
|
attrs.Props{
|
||||||
|
"id": "action-div-" + dropBadChars(galleryID),
|
||||||
|
},
|
||||||
elem.H3(
|
elem.H3(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
"role": "status",
|
"role": "status",
|
||||||
|
@ -32,7 +35,7 @@ func DoneProgress(galleryID, text string, showDelete bool) string {
|
||||||
},
|
},
|
||||||
elem.Text(text),
|
elem.Text(text),
|
||||||
),
|
),
|
||||||
elem.If(showDelete, deleteButton(galleryID), reInstallButton(galleryID)),
|
elem.If(showDelete, deleteButton(galleryID, modelName), reInstallButton(galleryID)),
|
||||||
).Render()
|
).Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +80,7 @@ func StartProgressBar(uid, progress, text string) string {
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
"hx-trigger": "done",
|
"hx-trigger": "done",
|
||||||
"hx-get": "/browse/job/" + uid,
|
"hx-get": "/browse/job/" + uid,
|
||||||
"hx-swap": "innerHTML",
|
"hx-swap": "outerHTML",
|
||||||
"hx-target": "this",
|
"hx-target": "this",
|
||||||
},
|
},
|
||||||
elem.H3(
|
elem.H3(
|
||||||
|
@ -88,7 +91,6 @@ func StartProgressBar(uid, progress, text string) string {
|
||||||
"autofocus": "",
|
"autofocus": "",
|
||||||
},
|
},
|
||||||
elem.Text(text),
|
elem.Text(text),
|
||||||
// This is a simple example of how to use the HTMLX library to create a progress bar that updates every 600ms.
|
|
||||||
elem.Div(attrs.Props{
|
elem.Div(attrs.Props{
|
||||||
"hx-get": "/browse/job/progress/" + uid,
|
"hx-get": "/browse/job/progress/" + uid,
|
||||||
"hx-trigger": "every 600ms",
|
"hx-trigger": "every 600ms",
|
||||||
|
@ -192,6 +194,7 @@ func reInstallButton(galleryName string) elem.Node {
|
||||||
"data-twe-ripple-init": "",
|
"data-twe-ripple-init": "",
|
||||||
"data-twe-ripple-color": "light",
|
"data-twe-ripple-color": "light",
|
||||||
"class": "float-right inline-block rounded bg-primary ml-2 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong",
|
"class": "float-right inline-block rounded bg-primary ml-2 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong",
|
||||||
|
"hx-target": "#action-div-" + dropBadChars(galleryName),
|
||||||
"hx-swap": "outerHTML",
|
"hx-swap": "outerHTML",
|
||||||
// post the Model ID as param
|
// post the Model ID as param
|
||||||
"hx-post": "/browse/install/model/" + galleryName,
|
"hx-post": "/browse/install/model/" + galleryName,
|
||||||
|
@ -205,16 +208,17 @@ func reInstallButton(galleryName string) elem.Node {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteButton(modelName string) elem.Node {
|
func deleteButton(galleryID, modelName string) elem.Node {
|
||||||
return elem.Button(
|
return elem.Button(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
"data-twe-ripple-init": "",
|
"data-twe-ripple-init": "",
|
||||||
"data-twe-ripple-color": "light",
|
"data-twe-ripple-color": "light",
|
||||||
"hx-confirm": "Are you sure you wish to delete the model?",
|
"hx-confirm": "Are you sure you wish to delete the model?",
|
||||||
"class": "float-right inline-block rounded bg-red-800 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-red-accent-300 hover:shadow-red-2 focus:bg-red-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-red-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong",
|
"class": "float-right inline-block rounded bg-red-800 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-red-accent-300 hover:shadow-red-2 focus:bg-red-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-red-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong",
|
||||||
|
"hx-target": "#action-div-" + dropBadChars(galleryID),
|
||||||
"hx-swap": "outerHTML",
|
"hx-swap": "outerHTML",
|
||||||
// post the Model ID as param
|
// post the Model ID as param
|
||||||
"hx-post": "/browse/delete/model/" + modelName,
|
"hx-post": "/browse/delete/model/" + galleryID,
|
||||||
},
|
},
|
||||||
elem.I(
|
elem.I(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
|
@ -225,20 +229,14 @@ func deleteButton(modelName string) elem.Node {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Javascript/HTMX doesn't like weird IDs
|
||||||
|
func dropBadChars(s string) string {
|
||||||
|
return strings.ReplaceAll(s, "@", "__")
|
||||||
|
}
|
||||||
|
|
||||||
func ListModels(models []*gallery.GalleryModel, processing *xsync.SyncedMap[string, string], galleryService *services.GalleryService) string {
|
func ListModels(models []*gallery.GalleryModel, processing *xsync.SyncedMap[string, string], galleryService *services.GalleryService) string {
|
||||||
//StartProgressBar(uid, "0")
|
|
||||||
modelsElements := []elem.Node{}
|
modelsElements := []elem.Node{}
|
||||||
// span := func(s string) elem.Node {
|
|
||||||
// return elem.Span(
|
|
||||||
// attrs.Props{
|
|
||||||
// "class": "float-right inline-block bg-green-500 text-white py-1 px-3 rounded-full text-xs",
|
|
||||||
// },
|
|
||||||
// elem.Text(s),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
descriptionDiv := func(m *gallery.GalleryModel) elem.Node {
|
descriptionDiv := func(m *gallery.GalleryModel) elem.Node {
|
||||||
|
|
||||||
return elem.Div(
|
return elem.Div(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
"class": "p-6 text-surface dark:text-white",
|
"class": "p-6 text-surface dark:text-white",
|
||||||
|
@ -261,13 +259,16 @@ func ListModels(models []*gallery.GalleryModel, processing *xsync.SyncedMap[stri
|
||||||
actionDiv := func(m *gallery.GalleryModel) elem.Node {
|
actionDiv := func(m *gallery.GalleryModel) elem.Node {
|
||||||
galleryID := fmt.Sprintf("%s@%s", m.Gallery.Name, m.Name)
|
galleryID := fmt.Sprintf("%s@%s", m.Gallery.Name, m.Name)
|
||||||
currentlyProcessing := processing.Exists(galleryID)
|
currentlyProcessing := processing.Exists(galleryID)
|
||||||
|
jobID := ""
|
||||||
isDeletionOp := false
|
isDeletionOp := false
|
||||||
if currentlyProcessing {
|
if currentlyProcessing {
|
||||||
status := galleryService.GetStatus(galleryID)
|
status := galleryService.GetStatus(galleryID)
|
||||||
if status != nil && status.Deletion {
|
if status != nil && status.Deletion {
|
||||||
isDeletionOp = true
|
isDeletionOp = true
|
||||||
}
|
}
|
||||||
// if status == nil : "Waiting"
|
jobID = processing.Get(galleryID)
|
||||||
|
// TODO:
|
||||||
|
// case not handled, if status == nil : "Waiting"
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := []elem.Node{
|
nodes := []elem.Node{
|
||||||
|
@ -317,29 +318,33 @@ func ListModels(models []*gallery.GalleryModel, processing *xsync.SyncedMap[stri
|
||||||
},
|
},
|
||||||
nodes...,
|
nodes...,
|
||||||
),
|
),
|
||||||
elem.If(
|
elem.Div(
|
||||||
currentlyProcessing,
|
attrs.Props{
|
||||||
elem.Node( // If currently installing, show progress bar
|
"id": "action-div-" + dropBadChars(galleryID),
|
||||||
elem.Raw(StartProgressBar(processing.Get(galleryID), "0", progressMessage)),
|
},
|
||||||
), // Otherwise, show install button (if not installed) or display "Installed"
|
elem.If(
|
||||||
elem.If(m.Installed,
|
currentlyProcessing,
|
||||||
elem.Node(elem.Div(
|
elem.Node( // If currently installing, show progress bar
|
||||||
attrs.Props{},
|
elem.Raw(StartProgressBar(jobID, "0", progressMessage)),
|
||||||
reInstallButton(m.ID()),
|
), // Otherwise, show install button (if not installed) or display "Installed"
|
||||||
deleteButton(m.Name),
|
elem.If(m.Installed,
|
||||||
)),
|
elem.Node(elem.Div(
|
||||||
installButton(m.ID()),
|
attrs.Props{},
|
||||||
|
reInstallButton(m.ID()),
|
||||||
|
deleteButton(m.ID(), m.Name),
|
||||||
|
)),
|
||||||
|
installButton(m.ID()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range models {
|
for _, m := range models {
|
||||||
|
|
||||||
elems := []elem.Node{}
|
elems := []elem.Node{}
|
||||||
|
|
||||||
if m.Icon == "" {
|
if m.Icon == "" {
|
||||||
m.Icon = NoImage
|
m.Icon = noImage
|
||||||
}
|
}
|
||||||
|
|
||||||
divProperties := attrs.Props{
|
divProperties := attrs.Props{
|
||||||
|
@ -347,7 +352,6 @@ func ListModels(models []*gallery.GalleryModel, processing *xsync.SyncedMap[stri
|
||||||
}
|
}
|
||||||
|
|
||||||
elems = append(elems,
|
elems = append(elems,
|
||||||
|
|
||||||
elem.Div(divProperties,
|
elem.Div(divProperties,
|
||||||
elem.A(attrs.Props{
|
elem.A(attrs.Props{
|
||||||
"href": "#!",
|
"href": "#!",
|
||||||
|
@ -359,8 +363,11 @@ func ListModels(models []*gallery.GalleryModel, processing *xsync.SyncedMap[stri
|
||||||
"src": m.Icon,
|
"src": m.Icon,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Special/corner case: if a model sets Trust Remote Code as required, show a warning
|
||||||
|
// TODO: handle this more generically later
|
||||||
_, trustRemoteCodeExists := m.Overrides["trust_remote_code"]
|
_, trustRemoteCodeExists := m.Overrides["trust_remote_code"]
|
||||||
if trustRemoteCodeExists {
|
if trustRemoteCodeExists {
|
||||||
elems = append(elems, elem.Div(
|
elems = append(elems, elem.Div(
|
||||||
|
@ -392,7 +399,6 @@ func ListModels(models []*gallery.GalleryModel, processing *xsync.SyncedMap[stri
|
||||||
|
|
||||||
wrapper := elem.Div(attrs.Props{
|
wrapper := elem.Div(attrs.Props{
|
||||||
"class": "dark grid grid-cols-1 grid-rows-1 md:grid-cols-3 block rounded-lg shadow-secondary-1 dark:bg-surface-dark",
|
"class": "dark grid grid-cols-1 grid-rows-1 md:grid-cols-3 block rounded-lg shadow-secondary-1 dark:bg-surface-dark",
|
||||||
//"class": "block rounded-lg bg-white shadow-secondary-1 dark:bg-surface-dark",
|
|
||||||
}, modelsElements...)
|
}, modelsElements...)
|
||||||
|
|
||||||
return wrapper.Render()
|
return wrapper.Render()
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/go-skynet/LocalAI/pkg/gallery"
|
"github.com/go-skynet/LocalAI/pkg/gallery"
|
||||||
"github.com/go-skynet/LocalAI/pkg/model"
|
"github.com/go-skynet/LocalAI/pkg/model"
|
||||||
"github.com/go-skynet/LocalAI/pkg/xsync"
|
"github.com/go-skynet/LocalAI/pkg/xsync"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -117,6 +118,7 @@ func RegisterUIRoutes(app *fiber.App,
|
||||||
// https://htmx.org/examples/progress-bar/
|
// https://htmx.org/examples/progress-bar/
|
||||||
app.Post("/browse/install/model/:id", auth, func(c *fiber.Ctx) error {
|
app.Post("/browse/install/model/:id", auth, func(c *fiber.Ctx) error {
|
||||||
galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests!
|
galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests!
|
||||||
|
log.Debug().Msgf("UI job submitted to install : %+v\n", galleryID)
|
||||||
|
|
||||||
id, err := uuid.NewUUID()
|
id, err := uuid.NewUUID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -143,6 +145,14 @@ func RegisterUIRoutes(app *fiber.App,
|
||||||
// https://htmx.org/examples/progress-bar/
|
// https://htmx.org/examples/progress-bar/
|
||||||
app.Post("/browse/delete/model/:id", auth, func(c *fiber.Ctx) error {
|
app.Post("/browse/delete/model/:id", auth, func(c *fiber.Ctx) error {
|
||||||
galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests!
|
galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests!
|
||||||
|
log.Debug().Msgf("UI job submitted to delete : %+v\n", galleryID)
|
||||||
|
var galleryName = galleryID
|
||||||
|
if strings.Contains(galleryID, "@") {
|
||||||
|
// if the galleryID contains a @ it means that it's a model from a gallery
|
||||||
|
// but we want to delete it from the local models which does not need
|
||||||
|
// a repository ID
|
||||||
|
galleryName = strings.Split(galleryID, "@")[1]
|
||||||
|
}
|
||||||
|
|
||||||
id, err := uuid.NewUUID()
|
id, err := uuid.NewUUID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -151,16 +161,20 @@ func RegisterUIRoutes(app *fiber.App,
|
||||||
|
|
||||||
uid := id.String()
|
uid := id.String()
|
||||||
|
|
||||||
|
// Track the deletion job by galleryID and galleryName
|
||||||
|
// The GalleryID contains information about the repository,
|
||||||
|
// while the GalleryName is ONLY the name of the model
|
||||||
|
processingModels.Set(galleryName, uid)
|
||||||
processingModels.Set(galleryID, uid)
|
processingModels.Set(galleryID, uid)
|
||||||
|
|
||||||
op := gallery.GalleryOp{
|
op := gallery.GalleryOp{
|
||||||
Id: uid,
|
Id: uid,
|
||||||
Delete: true,
|
Delete: true,
|
||||||
GalleryModelName: galleryID,
|
GalleryModelName: galleryName,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
galleryService.C <- op
|
galleryService.C <- op
|
||||||
cl.RemoveBackendConfig(galleryID)
|
cl.RemoveBackendConfig(galleryName)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return c.SendString(elements.StartProgressBar(uid, "0", "Deletion"))
|
return c.SendString(elements.StartProgressBar(uid, "0", "Deletion"))
|
||||||
|
@ -170,7 +184,7 @@ func RegisterUIRoutes(app *fiber.App,
|
||||||
// If the job is done, we trigger the /browse/job/:uid route
|
// If the job is done, we trigger the /browse/job/:uid route
|
||||||
// https://htmx.org/examples/progress-bar/
|
// https://htmx.org/examples/progress-bar/
|
||||||
app.Get("/browse/job/progress/:uid", auth, func(c *fiber.Ctx) error {
|
app.Get("/browse/job/progress/:uid", auth, func(c *fiber.Ctx) error {
|
||||||
jobUID := c.Params("uid")
|
jobUID := strings.Clone(c.Params("uid")) // note: strings.Clone is required for multiple requests!
|
||||||
|
|
||||||
status := galleryService.GetStatus(jobUID)
|
status := galleryService.GetStatus(jobUID)
|
||||||
if status == nil {
|
if status == nil {
|
||||||
|
@ -192,17 +206,22 @@ func RegisterUIRoutes(app *fiber.App,
|
||||||
// this route is hit when the job is done, and we display the
|
// this route is hit when the job is done, and we display the
|
||||||
// final state (for now just displays "Installation completed")
|
// final state (for now just displays "Installation completed")
|
||||||
app.Get("/browse/job/:uid", auth, func(c *fiber.Ctx) error {
|
app.Get("/browse/job/:uid", auth, func(c *fiber.Ctx) error {
|
||||||
|
jobUID := strings.Clone(c.Params("uid")) // note: strings.Clone is required for multiple requests!
|
||||||
|
|
||||||
status := galleryService.GetStatus(c.Params("uid"))
|
status := galleryService.GetStatus(jobUID)
|
||||||
|
|
||||||
galleryID := ""
|
galleryID := ""
|
||||||
for _, k := range processingModels.Keys() {
|
for _, k := range processingModels.Keys() {
|
||||||
if processingModels.Get(k) == c.Params("uid") {
|
if processingModels.Get(k) == jobUID {
|
||||||
galleryID = k
|
galleryID = k
|
||||||
processingModels.Delete(k)
|
processingModels.Delete(k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if galleryID == "" {
|
||||||
|
log.Debug().Msgf("no processing model found for job : %+v\n", jobUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msgf("JOB finished : %+v\n", status)
|
||||||
showDelete := true
|
showDelete := true
|
||||||
displayText := "Installation completed"
|
displayText := "Installation completed"
|
||||||
if status.Deletion {
|
if status.Deletion {
|
||||||
|
|
|
@ -113,7 +113,8 @@ SOFTWARE.
|
||||||
|
|
||||||
<div class="chat-messages p-4" id="chat" x-data="{history: $store.chat.history}">
|
<div class="chat-messages p-4" id="chat" x-data="{history: $store.chat.history}">
|
||||||
<p id="usage" x-show="history.length === 0">
|
<p id="usage" x-show="history.length === 0">
|
||||||
Start chatting with the AI by typing a prompt in the input field below.
|
Start chatting with the AI by typing a prompt in the input field below and pressing Enter.
|
||||||
|
For models that support images, you can upload an image by clicking the paperclip <i class="fa-solid fa-paperclip"></i> icon.
|
||||||
</p>
|
</p>
|
||||||
<div id="messages">
|
<div id="messages">
|
||||||
<template x-for="message in history">
|
<template x-for="message in history">
|
||||||
|
|
|
@ -18,29 +18,7 @@
|
||||||
|
|
||||||
<div class="models mt-4">
|
<div class="models mt-4">
|
||||||
|
|
||||||
<!-- Show in progress operations-->
|
{{template "views/partials/inprogress" .}}
|
||||||
{{ if .ProcessingModels }}
|
|
||||||
<h3
|
|
||||||
class="mt-4 mb-4 text-center text-3xl font-semibold text-gray-100">Operations in progress</h2>
|
|
||||||
{{end}}
|
|
||||||
{{$taskType:=.TaskTypes}}
|
|
||||||
{{ range $key,$value:=.ProcessingModels }}
|
|
||||||
{{ $op := index $taskType $key}}
|
|
||||||
{{$parts := split "@" $key}}
|
|
||||||
<div class="flex items-center justify-between bg-slate-600 p-2 mb-2 rounded-md">
|
|
||||||
<div class="flex items center">
|
|
||||||
<span class="text-gray-300"><a href="/browse?term={{$parts._1}}"
|
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
|
||||||
>{{$parts._1}}</a> (from the '{{$parts._0}}' repository)</span>
|
|
||||||
</div>
|
|
||||||
<div hx-get="/browse/job/{{$value}}" hx-swap="innerHTML" hx-target="this" hx-trigger="done">
|
|
||||||
<h3 role="status" id="pblabel" >{{$op}}
|
|
||||||
<div hx-get="/browse/job/progress/{{$value}}" hx-trigger="every 600ms" hx-target="this"
|
|
||||||
hx-swap= "innerHTML" ></div></h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
|
||||||
<!-- END Show in progress operations-->
|
|
||||||
|
|
||||||
{{ if eq (len .ModelsConfig) 0 }}
|
{{ if eq (len .ModelsConfig) 0 }}
|
||||||
<h2 class="text-center text-3xl font-semibold text-gray-100"> <i class="text-yellow-200 ml-2 fa-solid fa-triangle-exclamation animate-pulse"></i> Ouch! seems you don't have any models installed!</h2>
|
<h2 class="text-center text-3xl font-semibold text-gray-100"> <i class="text-yellow-200 ml-2 fa-solid fa-triangle-exclamation animate-pulse"></i> Ouch! seems you don't have any models installed!</h2>
|
||||||
|
@ -92,7 +70,7 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="px-4 py-3">
|
<td class="px-4 py-3">
|
||||||
<button
|
<button
|
||||||
class="float-right inline-block rounded bg-red-800 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-red-accent-300 hover:shadow-red-2 focus:bg-red-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-red-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
class="float-right inline-block rounded bg-red-800 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-red-accent-300 hover:shadow-red-2 focus:bg-red-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-red-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
||||||
data-twe-ripple-color="light" data-twe-ripple-init="" hx-confirm="Are you sure you wish to delete the model?" hx-post="/browse/delete/model/{{.Name}}" hx-swap="outerHTML"><i class="fa-solid fa-cancel pr-2"></i>Delete</button>
|
data-twe-ripple-color="light" data-twe-ripple-init="" hx-confirm="Are you sure you wish to delete the model?" hx-post="/browse/delete/model/{{.Name}}" hx-swap="outerHTML"><i class="fa-solid fa-cancel pr-2"></i>Delete</button>
|
||||||
|
|
|
@ -65,29 +65,7 @@
|
||||||
|
|
||||||
|
|
||||||
<span class="htmx-indicator loader"></span>
|
<span class="htmx-indicator loader"></span>
|
||||||
<!-- Show in progress operations-->
|
{{template "views/partials/inprogress" .}}
|
||||||
{{ if .ProcessingModels }}
|
|
||||||
<h2
|
|
||||||
class="mt-4 mb-4 text-center text-3xl font-semibold text-gray-100">Operations in progress</h2>
|
|
||||||
{{end}}
|
|
||||||
{{$taskType:=.TaskTypes}}
|
|
||||||
{{ range $key,$value:=.ProcessingModels }}
|
|
||||||
{{ $op := index $taskType $key}}
|
|
||||||
{{$parts := split "@" $key}}
|
|
||||||
<div class="flex items-center justify-between bg-slate-600 p-2 mb-2 rounded-md">
|
|
||||||
<div class="flex items center">
|
|
||||||
<span class="text-gray-300"><a href="/browse?term={{$parts._1}}"
|
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
|
||||||
>{{$parts._1}}</a> (from the '{{$parts._0}}' repository)</span>
|
|
||||||
</div>
|
|
||||||
<div hx-get="/browse/job/{{$value}}" hx-swap="innerHTML" hx-target="this" hx-trigger="done">
|
|
||||||
<h3 role="status" id="pblabel" >{{$op}}
|
|
||||||
<div hx-get="/browse/job/progress/{{$value}}" hx-trigger="every 600ms" hx-target="this"
|
|
||||||
hx-swap= "innerHTML" ></div></h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
|
||||||
<!-- END Show in progress operations-->
|
|
||||||
|
|
||||||
<input class="form-control appearance-none block w-full mt-5 px-3 py-2 text-base font-normal text-gray-300 pb-2 mb-5 bg-gray-800 bg-clip-padding border border-solid border-gray-600 rounded transition ease-in-out m-0 focus:text-gray-300 focus:bg-gray-900 focus:border-blue-500 focus:outline-none" type="search"
|
<input class="form-control appearance-none block w-full mt-5 px-3 py-2 text-base font-normal text-gray-300 pb-2 mb-5 bg-gray-800 bg-clip-padding border border-solid border-gray-600 rounded transition ease-in-out m-0 focus:text-gray-300 focus:bg-gray-900 focus:border-blue-500 focus:outline-none" type="search"
|
||||||
name="search" placeholder="Begin Typing To Search models..."
|
name="search" placeholder="Begin Typing To Search models..."
|
||||||
|
|
32
core/http/views/partials/inprogress.html
Normal file
32
core/http/views/partials/inprogress.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<!-- Show in progress operations-->
|
||||||
|
{{ if .ProcessingModels }}
|
||||||
|
<h2
|
||||||
|
class="mt-4 mb-4 text-center text-3xl font-semibold text-gray-100">Operations in progress</h2>
|
||||||
|
{{end}}
|
||||||
|
{{$taskType:=.TaskTypes}}
|
||||||
|
|
||||||
|
{{ range $key,$value:=.ProcessingModels }}
|
||||||
|
{{ $op := index $taskType $key}}
|
||||||
|
{{$parts := split "@" $key}}
|
||||||
|
{{$modelName:=$parts._1}}
|
||||||
|
{{$repository:=$parts._0}}
|
||||||
|
{{ if not (contains "@" $key)}}
|
||||||
|
{{$repository = ""}}
|
||||||
|
{{$modelName = $key}}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between bg-slate-600 p-2 mb-2 rounded-md">
|
||||||
|
<div class="flex items center">
|
||||||
|
<span class="text-gray-300"><a href="/browse?term={{$parts._1}}"
|
||||||
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
|
>{{$modelName}}</a> {{if $repository}} (from the '{{$repository}}' repository) {{end}}</span>
|
||||||
|
</div>
|
||||||
|
<div hx-get="/browse/job/{{$value}}" hx-swap="outerHTML" hx-target="this" hx-trigger="done">
|
||||||
|
<h3 role="status" id="pblabel" >{{$op}}
|
||||||
|
<div hx-get="/browse/job/progress/{{$value}}" hx-trigger="every 600ms"
|
||||||
|
hx-target="this"
|
||||||
|
hx-swap="innerHTML" ></div></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
<!-- END Show in progress operations-->
|
Loading…
Add table
Add a link
Reference in a new issue