diff --git a/.github/release.yml b/.github/release.yml index 8c2c11f9..eee7f6ec 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -13,6 +13,9 @@ changelog: labels: - bug - regression + - title: "🖧 P2P area" + labels: + - area/p2p - title: Exciting New Features 🎉 labels: - Semver-Minor diff --git a/Makefile b/Makefile index df9b9474..99b16e66 100644 --- a/Makefile +++ b/Makefile @@ -53,8 +53,8 @@ RANDOM := $(shell bash -c 'echo $$RANDOM') VERSION?=$(shell git describe --always --tags || echo "dev" ) # go tool nm ./local-ai | grep Commit LD_FLAGS?= -override LD_FLAGS += -X "github.com/go-skynet/LocalAI/internal.Version=$(VERSION)" -override LD_FLAGS += -X "github.com/go-skynet/LocalAI/internal.Commit=$(shell git rev-parse HEAD)" +override LD_FLAGS += -X "github.com/mudler/LocalAI/internal.Version=$(VERSION)" +override LD_FLAGS += -X "github.com/mudler/LocalAI/internal.Commit=$(shell git rev-parse HEAD)" OPTIONAL_TARGETS?= @@ -147,7 +147,7 @@ endif # glibc-static or glibc-devel-static required ifeq ($(STATIC),true) - LD_FLAGS=-linkmode external -extldflags -static + LD_FLAGS+=-linkmode external -extldflags -static endif ifeq ($(findstring stablediffusion,$(GO_TAGS)),stablediffusion) diff --git a/core/cli/cli.go b/core/cli/cli.go index b88db7b2..0fed33fd 100644 --- a/core/cli/cli.go +++ b/core/cli/cli.go @@ -9,6 +9,7 @@ var CLI struct { cliContext.Context `embed:""` Run RunCMD `cmd:"" help:"Run LocalAI, this the default command if no other command is specified. Run 'local-ai run --help' for more information" default:"withargs"` + Federated FederatedCLI `cmd:"" help:"Run LocalAI in federated mode"` Models ModelsCMD `cmd:"" help:"Manage LocalAI models and definitions"` TTS TTSCMD `cmd:"" help:"Convert text to speech"` Transcript TranscriptCMD `cmd:"" help:"Convert audio to text"` diff --git a/core/cli/federated.go b/core/cli/federated.go new file mode 100644 index 00000000..b99ef4f8 --- /dev/null +++ b/core/cli/federated.go @@ -0,0 +1,130 @@ +package cli + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "time" + + "math/rand/v2" + + cliContext "github.com/mudler/LocalAI/core/cli/context" + "github.com/mudler/LocalAI/core/p2p" + "github.com/mudler/edgevpn/pkg/node" + "github.com/mudler/edgevpn/pkg/protocol" + "github.com/mudler/edgevpn/pkg/types" + "github.com/rs/zerolog/log" +) + +type FederatedCLI struct { + Address string `env:"LOCALAI_ADDRESS,ADDRESS" default:":8080" help:"Bind address for the API server" group:"api"` + Peer2PeerToken string `env:"LOCALAI_P2P_TOKEN,P2P_TOKEN,TOKEN" name:"p2ptoken" help:"Token for P2P mode (optional)" group:"p2p"` +} + +func (f *FederatedCLI) Run(ctx *cliContext.Context) error { + + n, err := p2p.NewNode(f.Peer2PeerToken) + if err != nil { + return fmt.Errorf("creating a new node: %w", err) + } + err = n.Start(context.Background()) + if err != nil { + return fmt.Errorf("creating a new node: %w", err) + } + + if err := p2p.ServiceDiscoverer(context.Background(), n, f.Peer2PeerToken, p2p.FederatedID, nil); err != nil { + return err + } + + return Proxy(context.Background(), n, f.Address, p2p.FederatedID) +} + +func Proxy(ctx context.Context, node *node.Node, listenAddr, service string) error { + + log.Info().Msgf("Allocating service '%s' on: %s", service, listenAddr) + // Open local port for listening + l, err := net.Listen("tcp", listenAddr) + if err != nil { + log.Error().Err(err).Msg("Error listening") + return err + } + // ll.Info("Binding local port on", srcaddr) + + ledger, _ := node.Ledger() + + // Announce ourselves so nodes accepts our connection + ledger.Announce( + ctx, + 10*time.Second, + func() { + // Retrieve current ID for ip in the blockchain + //_, found := ledger.GetKey(protocol.UsersLedgerKey, node.Host().ID().String()) + // If mismatch, update the blockchain + //if !found { + updatedMap := map[string]interface{}{} + updatedMap[node.Host().ID().String()] = &types.User{ + PeerID: node.Host().ID().String(), + Timestamp: time.Now().String(), + } + ledger.Add(protocol.UsersLedgerKey, updatedMap) + // } + }, + ) + + defer l.Close() + for { + select { + case <-ctx.Done(): + return errors.New("context canceled") + default: + log.Debug().Msg("New for connection") + // Listen for an incoming connection. + conn, err := l.Accept() + if err != nil { + fmt.Println("Error accepting: ", err.Error()) + continue + } + + // Handle connections in a new goroutine, forwarding to the p2p service + go func() { + var tunnelAddresses []string + for _, v := range p2p.GetAvailableNodes(p2p.FederatedID) { + if v.IsOnline() { + tunnelAddresses = append(tunnelAddresses, v.TunnelAddress) + } else { + log.Info().Msgf("Node %s is offline", v.ID) + } + } + + // open a TCP stream to one of the tunnels + // chosen randomly + // TODO: optimize this and track usage + tunnelAddr := tunnelAddresses[rand.IntN(len(tunnelAddresses))] + + tunnelConn, err := net.Dial("tcp", tunnelAddr) + if err != nil { + log.Error().Err(err).Msg("Error connecting to tunnel") + return + } + + log.Info().Msgf("Redirecting %s to %s", conn.LocalAddr().String(), tunnelConn.RemoteAddr().String()) + closer := make(chan struct{}, 2) + go copyStream(closer, tunnelConn, conn) + go copyStream(closer, conn, tunnelConn) + <-closer + + tunnelConn.Close() + conn.Close() + // ll.Infof("(service %s) Done handling %s", serviceID, l.Addr().String()) + }() + } + } + +} + +func copyStream(closer chan struct{}, dst io.Writer, src io.Reader) { + defer func() { closer <- struct{}{} }() // connection is closed, send signal to stop proxy + io.Copy(dst, src) +} diff --git a/core/cli/run.go b/core/cli/run.go index 2d19b072..4a313391 100644 --- a/core/cli/run.go +++ b/core/cli/run.go @@ -3,6 +3,8 @@ package cli import ( "context" "fmt" + "net" + "os" "strings" "time" @@ -50,7 +52,7 @@ type RunCMD struct { DisableWebUI bool `env:"LOCALAI_DISABLE_WEBUI,DISABLE_WEBUI" default:"false" help:"Disable webui" group:"api"` OpaqueErrors bool `env:"LOCALAI_OPAQUE_ERRORS" default:"false" help:"If true, all error responses are replaced with blank 500 errors. This is intended only for hardening against information leaks and is normally not recommended." group:"api"` Peer2Peer bool `env:"LOCALAI_P2P,P2P" name:"p2p" default:"false" help:"Enable P2P mode" group:"p2p"` - Peer2PeerToken string `env:"LOCALAI_P2P_TOKEN,P2P_TOKEN" name:"p2ptoken" help:"Token for P2P mode (optional)" group:"p2p"` + Peer2PeerToken string `env:"LOCALAI_P2P_TOKEN,P2P_TOKEN,TOKEN" name:"p2ptoken" help:"Token for P2P mode (optional)" group:"p2p"` ParallelRequests bool `env:"LOCALAI_PARALLEL_REQUESTS,PARALLEL_REQUESTS" help:"Enable backends to handle multiple requests in parallel if they support it (e.g.: llama.cpp or vllm)" group:"backends"` SingleActiveBackend bool `env:"LOCALAI_SINGLE_ACTIVE_BACKEND,SINGLE_ACTIVE_BACKEND" help:"Allow only one backend to be run at a time" group:"backends"` PreloadBackendOnly bool `env:"LOCALAI_PRELOAD_BACKEND_ONLY,PRELOAD_BACKEND_ONLY" default:"false" help:"Do not launch the API services, only the preloaded models / backends are started (useful for multi-node setups)" group:"backends"` @@ -59,6 +61,7 @@ type RunCMD struct { WatchdogIdleTimeout string `env:"LOCALAI_WATCHDOG_IDLE_TIMEOUT,WATCHDOG_IDLE_TIMEOUT" default:"15m" help:"Threshold beyond which an idle backend should be stopped" group:"backends"` EnableWatchdogBusy bool `env:"LOCALAI_WATCHDOG_BUSY,WATCHDOG_BUSY" default:"false" help:"Enable watchdog for stopping backends that are busy longer than the watchdog-busy-timeout" group:"backends"` WatchdogBusyTimeout string `env:"LOCALAI_WATCHDOG_BUSY_TIMEOUT,WATCHDOG_BUSY_TIMEOUT" default:"5m" help:"Threshold beyond which a busy backend should be stopped" group:"backends"` + Federated bool `env:"LOCALAI_FEDERATED,FEDERATED" help:"Enable federated instance" group:"federated"` } func (r *RunCMD) Run(ctx *cliContext.Context) error { @@ -91,9 +94,10 @@ func (r *RunCMD) Run(ctx *cliContext.Context) error { config.WithOpaqueErrors(r.OpaqueErrors), } + token := "" if r.Peer2Peer || r.Peer2PeerToken != "" { log.Info().Msg("P2P mode enabled") - token := r.Peer2PeerToken + token = r.Peer2PeerToken if token == "" { // IF no token is provided, and p2p is enabled, // we generate one and wait for the user to pick up the token (this is for interactive) @@ -104,14 +108,46 @@ func (r *RunCMD) Run(ctx *cliContext.Context) error { log.Info().Msg("To use the token, you can run the following command in another node or terminal:") fmt.Printf("export TOKEN=\"%s\"\nlocal-ai worker p2p-llama-cpp-rpc\n", token) - - // Ask for user confirmation - log.Info().Msg("Press a button to proceed") - var input string - fmt.Scanln(&input) } + opts = append(opts, config.WithP2PToken(token)) + + node, err := p2p.NewNode(token) + if err != nil { + return err + } + log.Info().Msg("Starting P2P server discovery...") - if err := p2p.LLamaCPPRPCServerDiscoverer(context.Background(), token); err != nil { + if err := p2p.ServiceDiscoverer(context.Background(), node, token, "", func() { + var tunnelAddresses []string + for _, v := range p2p.GetAvailableNodes("") { + if v.IsOnline() { + tunnelAddresses = append(tunnelAddresses, v.TunnelAddress) + } else { + log.Info().Msgf("Node %s is offline", v.ID) + } + } + tunnelEnvVar := strings.Join(tunnelAddresses, ",") + + os.Setenv("LLAMACPP_GRPC_SERVERS", tunnelEnvVar) + log.Debug().Msgf("setting LLAMACPP_GRPC_SERVERS to %s", tunnelEnvVar) + }); err != nil { + return err + } + } + + if r.Federated { + _, port, err := net.SplitHostPort(r.Address) + if err != nil { + return err + } + if err := p2p.ExposeService(context.Background(), "localhost", port, token, p2p.FederatedID); err != nil { + return err + } + node, err := p2p.NewNode(token) + if err != nil { + return err + } + if err := p2p.ServiceDiscoverer(context.Background(), node, token, p2p.FederatedID, nil); err != nil { return err } } diff --git a/core/cli/worker/worker_p2p.go b/core/cli/worker/worker_p2p.go index 4651c36e..2eb5cb94 100644 --- a/core/cli/worker/worker_p2p.go +++ b/core/cli/worker/worker_p2p.go @@ -20,7 +20,7 @@ import ( type P2P struct { WorkerFlags `embed:""` - Token string `env:"LOCALAI_TOKEN,TOKEN" help:"JSON list of galleries"` + Token string `env:"LOCALAI_TOKEN,LOCALAI_P2P_TOKEN,TOKEN" help:"P2P token to use"` NoRunner bool `env:"LOCALAI_NO_RUNNER,NO_RUNNER" help:"Do not start the llama-cpp-rpc-server"` RunnerAddress string `env:"LOCALAI_RUNNER_ADDRESS,RUNNER_ADDRESS" help:"Address of the llama-cpp-rpc-server"` RunnerPort string `env:"LOCALAI_RUNNER_PORT,RUNNER_PORT" help:"Port of the llama-cpp-rpc-server"` @@ -59,7 +59,7 @@ func (r *P2P) Run(ctx *cliContext.Context) error { p = r.RunnerPort } - err = p2p.BindLLamaCPPWorker(context.Background(), address, p, r.Token) + err = p2p.ExposeService(context.Background(), address, p, r.Token, "") if err != nil { return err } @@ -99,7 +99,7 @@ func (r *P2P) Run(ctx *cliContext.Context) error { } }() - err = p2p.BindLLamaCPPWorker(context.Background(), address, fmt.Sprint(port), r.Token) + err = p2p.ExposeService(context.Background(), address, fmt.Sprint(port), r.Token, "") if err != nil { return err } diff --git a/core/config/application_config.go b/core/config/application_config.go index 65c716f8..1bac349b 100644 --- a/core/config/application_config.go +++ b/core/config/application_config.go @@ -32,6 +32,7 @@ type ApplicationConfig struct { CORSAllowOrigins string ApiKeys []string OpaqueErrors bool + P2PToken string ModelLibraryURL string @@ -95,6 +96,12 @@ func WithCsrf(b bool) AppOption { } } +func WithP2PToken(s string) AppOption { + return func(o *ApplicationConfig) { + o.P2PToken = s + } +} + func WithModelLibraryURL(url string) AppOption { return func(o *ApplicationConfig) { o.ModelLibraryURL = url diff --git a/core/http/elements/gallery.go b/core/http/elements/gallery.go index 373de038..3b3741d8 100644 --- a/core/http/elements/gallery.go +++ b/core/http/elements/gallery.go @@ -7,6 +7,7 @@ import ( "github.com/chasefleming/elem-go" "github.com/chasefleming/elem-go/attrs" "github.com/mudler/LocalAI/core/gallery" + "github.com/mudler/LocalAI/core/p2p" "github.com/mudler/LocalAI/core/services" "github.com/mudler/LocalAI/pkg/xsync" ) @@ -15,6 +16,14 @@ const ( noImage = "https://upload.wikimedia.org/wikipedia/commons/6/65/No-Image-Placeholder.svg" ) +func renderElements(n []elem.Node) string { + render := "" + for _, r := range n { + render += r.Render() + } + return render +} + func DoneProgress(galleryID, text string, showDelete bool) string { var modelName = galleryID // Split by @ and grab the name @@ -72,6 +81,135 @@ func ProgressBar(progress string) string { ).Render() } +func P2PNodeStats(nodes []p2p.NodeData) string { + /* +
+

Total Workers Detected: {{ len .Nodes }}

+ {{ $online := 0 }} + {{ range .Nodes }} + {{ if .IsOnline }} + {{ $online = add $online 1 }} + {{ end }} + {{ end }} +

Total Online Workers: {{$online}}

+
+ */ + + online := 0 + for _, n := range nodes { + if n.IsOnline() { + online++ + } + } + + class := "text-green-500" + if online == 0 { + class = "text-red-500" + } + /* + + */ + circle := elem.I(attrs.Props{ + "class": "fas fa-circle animate-pulse " + class + " ml-2 mr-1", + }) + nodesElements := []elem.Node{ + elem.Span( + attrs.Props{ + "class": class, + }, + circle, + elem.Text(fmt.Sprintf("%d", online)), + ), + elem.Span( + attrs.Props{ + "class": "text-gray-200", + }, + elem.Text(fmt.Sprintf("/%d", len(nodes))), + ), + } + + return renderElements(nodesElements) +} + +func P2PNodeBoxes(nodes []p2p.NodeData) string { + /* +
+
+ + {{.ID}} +
+

+ Status: + + + {{ if .IsOnline }}Online{{ else }}Offline{{ end }} + +

+
+ */ + + nodesElements := []elem.Node{} + + for _, n := range nodes { + + nodesElements = append(nodesElements, + elem.Div( + attrs.Props{ + "class": "bg-gray-700 p-6 rounded-lg shadow-lg text-left", + }, + elem.P( + attrs.Props{ + "class": "text-sm text-gray-400 mt-2 flex", + }, + elem.I( + attrs.Props{ + "class": "fas fa-desktop text-gray-400 mr-2", + }, + ), + elem.Text("Name: "), + elem.Span( + attrs.Props{ + "class": "text-gray-200 font-semibold ml-2 mr-1", + }, + elem.Text(n.ID), + ), + elem.Text("Status: "), + elem.If( + n.IsOnline(), + elem.I( + attrs.Props{ + "class": "fas fa-circle animate-pulse text-green-500 ml-2 mr-1", + }, + ), + elem.I( + attrs.Props{ + "class": "fas fa-circle animate-pulse text-red-500 ml-2 mr-1", + }, + ), + ), + elem.If( + n.IsOnline(), + elem.Span( + attrs.Props{ + "class": "text-green-400", + }, + + elem.Text("Online"), + ), + elem.Span( + attrs.Props{ + "class": "text-red-400", + }, + elem.Text("Offline"), + ), + ), + ), + )) + } + + return renderElements(nodesElements) +} + func StartProgressBar(uid, progress, text string) string { if progress == "" { progress = "0" diff --git a/core/http/endpoints/localai/welcome.go b/core/http/endpoints/localai/welcome.go index fa00e900..34a2d975 100644 --- a/core/http/endpoints/localai/welcome.go +++ b/core/http/endpoints/localai/welcome.go @@ -4,6 +4,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/mudler/LocalAI/core/config" "github.com/mudler/LocalAI/core/gallery" + "github.com/mudler/LocalAI/core/p2p" "github.com/mudler/LocalAI/internal" "github.com/mudler/LocalAI/pkg/model" ) @@ -33,6 +34,7 @@ func WelcomeEndpoint(appConfig *config.ApplicationConfig, "Models": models, "ModelsConfig": backendConfigs, "GalleryConfig": galleryConfigs, + "IsP2PEnabled": p2p.IsP2PEnabled(), "ApplicationConfig": appConfig, "ProcessingModels": processingModels, "TaskTypes": taskTypes, diff --git a/core/http/routes/localai.go b/core/http/routes/localai.go index 9342182c..cc0b9d49 100644 --- a/core/http/routes/localai.go +++ b/core/http/routes/localai.go @@ -5,6 +5,7 @@ import ( "github.com/gofiber/swagger" "github.com/mudler/LocalAI/core/config" "github.com/mudler/LocalAI/core/http/endpoints/localai" + "github.com/mudler/LocalAI/core/p2p" "github.com/mudler/LocalAI/core/services" "github.com/mudler/LocalAI/internal" "github.com/mudler/LocalAI/pkg/model" @@ -56,6 +57,20 @@ func RegisterLocalAIRoutes(app *fiber.App, app.Get("/backend/monitor", auth, localai.BackendMonitorEndpoint(backendMonitorService)) app.Post("/backend/shutdown", auth, localai.BackendShutdownEndpoint(backendMonitorService)) + // p2p + if p2p.IsP2PEnabled() { + app.Get("/api/p2p", auth, func(c *fiber.Ctx) error { + // Render index + return c.JSON(map[string]interface{}{ + "Nodes": p2p.GetAvailableNodes(""), + "FederatedNodes": p2p.GetAvailableNodes(p2p.FederatedID), + }) + }) + app.Get("/api/p2p/token", auth, func(c *fiber.Ctx) error { + return c.Send([]byte(appConfig.P2PToken)) + }) + } + app.Get("/version", auth, func(c *fiber.Ctx) error { return c.JSON(struct { Version string `json:"version"` diff --git a/core/http/routes/ui.go b/core/http/routes/ui.go index 4618f032..51742b81 100644 --- a/core/http/routes/ui.go +++ b/core/http/routes/ui.go @@ -10,6 +10,7 @@ import ( "github.com/mudler/LocalAI/core/gallery" "github.com/mudler/LocalAI/core/http/elements" "github.com/mudler/LocalAI/core/http/endpoints/localai" + "github.com/mudler/LocalAI/core/p2p" "github.com/mudler/LocalAI/core/services" "github.com/mudler/LocalAI/internal" "github.com/mudler/LocalAI/pkg/model" @@ -53,6 +54,37 @@ func RegisterUIRoutes(app *fiber.App, app.Get("/", auth, localai.WelcomeEndpoint(appConfig, cl, ml, modelStatus)) + if p2p.IsP2PEnabled() { + app.Get("/p2p", auth, func(c *fiber.Ctx) error { + summary := fiber.Map{ + "Title": "LocalAI - P2P dashboard", + "Version": internal.PrintableVersion(), + //"Nodes": p2p.GetAvailableNodes(""), + //"FederatedNodes": p2p.GetAvailableNodes(p2p.FederatedID), + "IsP2PEnabled": p2p.IsP2PEnabled(), + "P2PToken": appConfig.P2PToken, + } + + // Render index + return c.Render("views/p2p", summary) + }) + + /* show nodes live! */ + app.Get("/p2p/ui/workers", auth, func(c *fiber.Ctx) error { + return c.SendString(elements.P2PNodeBoxes(p2p.GetAvailableNodes(""))) + }) + app.Get("/p2p/ui/workers-federation", auth, func(c *fiber.Ctx) error { + return c.SendString(elements.P2PNodeBoxes(p2p.GetAvailableNodes(p2p.FederatedID))) + }) + + app.Get("/p2p/ui/workers-stats", auth, func(c *fiber.Ctx) error { + return c.SendString(elements.P2PNodeStats(p2p.GetAvailableNodes(""))) + }) + app.Get("/p2p/ui/workers-federation-stats", auth, func(c *fiber.Ctx) error { + return c.SendString(elements.P2PNodeStats(p2p.GetAvailableNodes(p2p.FederatedID))) + }) + } + // Show the Models page (all models) app.Get("/browse", auth, func(c *fiber.Ctx) error { term := c.Query("term") @@ -87,7 +119,9 @@ func RegisterUIRoutes(app *fiber.App, "AllTags": tags, "ProcessingModels": processingModelsData, "AvailableModels": len(models), - "TaskTypes": taskTypes, + "IsP2PEnabled": p2p.IsP2PEnabled(), + + "TaskTypes": taskTypes, // "ApplicationConfig": appConfig, } @@ -243,6 +277,7 @@ func RegisterUIRoutes(app *fiber.App, "ModelsConfig": backendConfigs, "Model": c.Params("model"), "Version": internal.PrintableVersion(), + "IsP2PEnabled": p2p.IsP2PEnabled(), } // Render index @@ -261,6 +296,7 @@ func RegisterUIRoutes(app *fiber.App, "Title": "LocalAI - Talk", "ModelsConfig": backendConfigs, "Model": backendConfigs[0].ID, + "IsP2PEnabled": p2p.IsP2PEnabled(), "Version": internal.PrintableVersion(), } @@ -282,6 +318,7 @@ func RegisterUIRoutes(app *fiber.App, "ModelsConfig": backendConfigs, "Model": backendConfigs[0].ID, "Version": internal.PrintableVersion(), + "IsP2PEnabled": p2p.IsP2PEnabled(), } // Render index @@ -296,6 +333,7 @@ func RegisterUIRoutes(app *fiber.App, "ModelsConfig": backendConfigs, "Model": c.Params("model"), "Version": internal.PrintableVersion(), + "IsP2PEnabled": p2p.IsP2PEnabled(), } // Render index @@ -316,6 +354,7 @@ func RegisterUIRoutes(app *fiber.App, "ModelsConfig": backendConfigs, "Model": backendConfigs[0].Name, "Version": internal.PrintableVersion(), + "IsP2PEnabled": p2p.IsP2PEnabled(), } // Render index @@ -330,6 +369,7 @@ func RegisterUIRoutes(app *fiber.App, "ModelsConfig": backendConfigs, "Model": c.Params("model"), "Version": internal.PrintableVersion(), + "IsP2PEnabled": p2p.IsP2PEnabled(), } // Render index @@ -349,6 +389,7 @@ func RegisterUIRoutes(app *fiber.App, "Title": "LocalAI - Generate audio with " + backendConfigs[0].Name, "ModelsConfig": backendConfigs, "Model": backendConfigs[0].Name, + "IsP2PEnabled": p2p.IsP2PEnabled(), "Version": internal.PrintableVersion(), } diff --git a/core/http/static/assets/tw-elements.js b/core/http/static/assets/tw-elements.js new file mode 100644 index 00000000..a82e9a4c --- /dev/null +++ b/core/http/static/assets/tw-elements.js @@ -0,0 +1,29 @@ +(function(L,P){typeof exports=="object"&&typeof module<"u"?P(exports):typeof define=="function"&&define.amd?define(["exports"],P):(L=typeof globalThis<"u"?globalThis:L||self,P(L.twe={}))})(this,function(L){"use strict";var jl=Object.defineProperty;var Ul=(L,P,R)=>P in L?jl(L,P,{enumerable:!0,configurable:!0,writable:!0,value:R}):L[P]=R;var $i=(L,P,R)=>(Ul(L,typeof P!="symbol"?P+"":P,R),R);/*! +* TW Elements +* Version: FREE 2.0.0 +* +* Copyright: Material Design for Bootstrap +* https://mdbootstrap.com/ +* +* Read the license: https://tw-elements.com/license/ +* +* +* Documentation: https://tw-elements.com/ +* +* Support: https://mdbootstrap.com/support/cat/twe +* +* Contact: tailwind@mdbootstrap.com +* +*/const P=(()=>{const i={};let t=1;return{set(e,s,n){typeof e[s]>"u"&&(e[s]={key:s,id:t},t++),i[e[s].id]=n},get(e,s){if(!e||typeof e[s]>"u")return null;const n=e[s];return n.key===s?i[n.id]:null},delete(e,s){if(typeof e[s]>"u")return;const n=e[s];n.key===s&&(delete i[n.id],delete e[s])}}})(),R={setData(i,t,e){P.set(i,t,e)},getData(i,t){return P.get(i,t)},removeData(i,t){P.delete(i,t)}},Bn=1e6,jn=1e3,Ie="transitionend",Un=i=>i==null?`${i}`:{}.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase(),Yn=i=>{do i+=Math.floor(Math.random()*Bn);while(document.getElementById(i));return i},Ii=i=>{let t=i.getAttribute("data-twe-target");if(!t||t==="#"){let e=i.getAttribute("href");if(!e||!e.includes("#")&&!e.startsWith("."))return null;e.includes("#")&&!e.startsWith("#")&&(e=`#${e.split("#")[1]}`),t=e&&e!=="#"?e.trim():null}return t},Re=i=>{const t=Ii(i);return t&&document.querySelector(t)?t:null},st=i=>{const t=Ii(i);return t?document.querySelector(t):null},ke=i=>{if(!i)return 0;let{transitionDuration:t,transitionDelay:e}=window.getComputedStyle(i);const s=Number.parseFloat(t),n=Number.parseFloat(e);return!s&&!n?0:(t=t.split(",")[0],e=e.split(",")[0],(Number.parseFloat(t)+Number.parseFloat(e))*jn)},Ri=i=>{i.dispatchEvent(new Event(Ie))},yt=i=>!i||typeof i!="object"?!1:(typeof i.jquery<"u"&&(i=i[0]),typeof i.nodeType<"u"),nt=i=>yt(i)?i.jquery?i[0]:i:typeof i=="string"&&i.length>0?document.querySelector(i):null,x=(i,t,e)=>{Object.keys(e).forEach(s=>{const n=e[s],r=t[s],o=r&&yt(r)?"element":Un(r);if(!new RegExp(n).test(o))throw new Error(`${i.toUpperCase()}: Option "${s}" provided type "${o}" but expected type "${n}".`)})},Ct=i=>{if(!i)return!1;if(i.style&&i.parentNode&&i.parentNode.style){const t=getComputedStyle(i),e=getComputedStyle(i.parentNode);return t.display!=="none"&&e.display!=="none"&&t.visibility!=="hidden"}return!1},ct=i=>!i||i.nodeType!==Node.ELEMENT_NODE||i.classList.contains("disabled")?!0:typeof i.disabled<"u"?i.disabled:i.hasAttribute("disabled")&&i.getAttribute("disabled")!=="false",ki=i=>{if(!document.documentElement.attachShadow)return null;if(typeof i.getRootNode=="function"){const t=i.getRootNode();return t instanceof ShadowRoot?t:null}return i instanceof ShadowRoot?i:i.parentNode?ki(i.parentNode):null},se=()=>function(){},Ut=i=>{i.offsetHeight},xi=()=>{const{jQuery:i}=window;return i&&!document.body.hasAttribute("data-twe-no-jquery")?i:null},xe=[],Pi=i=>{document.readyState==="loading"?(xe.length||document.addEventListener("DOMContentLoaded",()=>{xe.forEach(t=>t())}),xe.push(i)):i()},W=()=>document.documentElement.dir==="rtl",Yt=i=>document.createElement(i),dt=i=>{typeof i=="function"&&i()},Mi=(i,t,e=!0)=>{if(!e){dt(i);return}const s=5,n=ke(t)+s;let r=!1;const o=({target:a})=>{a===t&&(r=!0,t.removeEventListener(Ie,o),dt(i))};t.addEventListener(Ie,o),setTimeout(()=>{r||Ri(t)},n)},Hi=(i,t,e,s)=>{let n=i.indexOf(t);if(n===-1)return i[!e&&s?i.length-1:0];const r=i.length;return n+=e?1:-1,s&&(n=(n+r)%r),i[Math.max(0,Math.min(n,r-1))]},Kn=/[^.]*(?=\..*)\.|.*/,Fn=/\..*/,zn=/::\d+$/,Pe={};let Vi=1;const qn={mouseenter:"mouseover",mouseleave:"mouseout"},Xn=/^(mouseenter|mouseleave)/i,Wi=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function Bi(i,t){return t&&`${t}::${Vi++}`||i.uidEvent||Vi++}function ji(i){const t=Bi(i);return i.uidEvent=t,Pe[t]=Pe[t]||{},Pe[t]}function Gn(i,t){return function e(s){return s.delegateTarget=i,e.oneOff&&d.off(i,s.type,t),t.apply(i,[s])}}function Qn(i,t,e){return function s(n){const r=i.querySelectorAll(t);for(let{target:o}=n;o&&o!==this;o=o.parentNode)for(let a=r.length;a--;"")if(r[a]===o)return n.delegateTarget=o,s.oneOff&&d.off(i,n.type,e),e.apply(o,[n]);return null}}function Ui(i,t,e=null){const s=Object.keys(i);for(let n=0,r=s.length;nfunction(T){if(!T.relatedTarget||T.relatedTarget!==T.delegateTarget&&!T.delegateTarget.contains(T.relatedTarget))return O.call(this,T)};s?s=g(s):e=g(e)}const[r,o,a]=Yi(t,e,s),l=ji(i),u=l[a]||(l[a]={}),c=Ui(u,o,r?e:null);if(c){c.oneOff=c.oneOff&&n;return}const f=Bi(o,t.replace(Kn,"")),v=r?Qn(i,e,s):Gn(i,e);v.delegationSelector=r?e:null,v.originalHandler=o,v.oneOff=n,v.uidEvent=f,u[f]=v,i.addEventListener(a,v,r)}function Me(i,t,e,s,n){const r=Ui(t[e],s,n);r&&(i.removeEventListener(e,r,!!n),delete t[e][r.uidEvent])}function Zn(i,t,e,s){const n=t[e]||{};Object.keys(n).forEach(r=>{if(r.includes(s)){const o=n[r];Me(i,t,e,o.originalHandler,o.delegationSelector)}})}function Fi(i){return i=i.replace(Fn,""),qn[i]||i}const d={on(i,t,e,s){Ki(i,t,e,s,!1)},one(i,t,e,s){Ki(i,t,e,s,!0)},off(i,t,e,s){if(typeof t!="string"||!i)return;const[n,r,o]=Yi(t,e,s),a=o!==t,l=ji(i),u=t.startsWith(".");if(typeof r<"u"){if(!l||!l[o])return;Me(i,l,o,r,n?e:null);return}u&&Object.keys(l).forEach(f=>{Zn(i,l,f,t.slice(1))});const c=l[o]||{};Object.keys(c).forEach(f=>{const v=f.replace(zn,"");if(!a||t.includes(v)){const g=c[f];Me(i,l,o,g.originalHandler,g.delegationSelector)}})},trigger(i,t,e){if(typeof t!="string"||!i)return null;const s=xi(),n=Fi(t),r=t!==n,o=Wi.has(n);let a,l=!0,u=!0,c=!1,f=null;return r&&s&&(a=s.Event(t,e),s(i).trigger(a),l=!a.isPropagationStopped(),u=!a.isImmediatePropagationStopped(),c=a.isDefaultPrevented()),o?(f=document.createEvent("HTMLEvents"),f.initEvent(n,l,!0)):f=new CustomEvent(t,{bubbles:l,cancelable:!0}),typeof e<"u"&&Object.keys(e).forEach(v=>{Object.defineProperty(f,v,{get(){return e[v]}})}),c&&f.preventDefault(),u&&i.dispatchEvent(f),f.defaultPrevented&&typeof a<"u"&&a.preventDefault(),f}},Jn="5.1.3";class et{constructor(t){t=nt(t),t&&(this._element=t,R.setData(this._element,this.constructor.DATA_KEY,this))}dispose(){R.removeData(this._element,this.constructor.DATA_KEY),d.off(this._element,this.constructor.EVENT_KEY),Object.getOwnPropertyNames(this).forEach(t=>{this[t]=null})}_queueCallback(t,e,s=!0){Mi(t,e,s)}static getInstance(t){return R.getData(nt(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,typeof e=="object"?e:null)}static get VERSION(){return Jn}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}static get DATA_KEY(){return`twe.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}}const tr="button",er="active";class ne extends et{static get NAME(){return tr}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle(er))}static jQueryInterface(t){return this.each(function(){const e=ne.getOrCreateInstance(this);t==="toggle"&&e[t]()})}}var M="top",B="bottom",j="right",H="left",re="auto",Ot=[M,B,j,H],ut="start",Dt="end",zi="clippingParents",He="viewport",Nt="popper",qi="reference",Ve=Ot.reduce(function(i,t){return i.concat([t+"-"+ut,t+"-"+Dt])},[]),We=[].concat(Ot,[re]).reduce(function(i,t){return i.concat([t,t+"-"+ut,t+"-"+Dt])},[]),Xi="beforeRead",Gi="read",Qi="afterRead",Zi="beforeMain",Ji="main",ts="afterMain",es="beforeWrite",is="write",ss="afterWrite",ns=[Xi,Gi,Qi,Zi,Ji,ts,es,is,ss];function Q(i){return i?(i.nodeName||"").toLowerCase():null}function U(i){if(i==null)return window;if(i.toString()!=="[object Window]"){var t=i.ownerDocument;return t&&t.defaultView||window}return i}function ht(i){var t=U(i).Element;return i instanceof t||i instanceof Element}function F(i){var t=U(i).HTMLElement;return i instanceof t||i instanceof HTMLElement}function Be(i){if(typeof ShadowRoot>"u")return!1;var t=U(i).ShadowRoot;return i instanceof t||i instanceof ShadowRoot}function ir(i){var t=i.state;Object.keys(t.elements).forEach(function(e){var s=t.styles[e]||{},n=t.attributes[e]||{},r=t.elements[e];!F(r)||!Q(r)||(Object.assign(r.style,s),Object.keys(n).forEach(function(o){var a=n[o];a===!1?r.removeAttribute(o):r.setAttribute(o,a===!0?"":a)}))})}function sr(i){var t=i.state,e={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,e.popper),t.styles=e,t.elements.arrow&&Object.assign(t.elements.arrow.style,e.arrow),function(){Object.keys(t.elements).forEach(function(s){var n=t.elements[s],r=t.attributes[s]||{},o=Object.keys(t.styles.hasOwnProperty(s)?t.styles[s]:e[s]),a=o.reduce(function(l,u){return l[u]="",l},{});!F(n)||!Q(n)||(Object.assign(n.style,a),Object.keys(r).forEach(function(l){n.removeAttribute(l)}))})}}const je={name:"applyStyles",enabled:!0,phase:"write",fn:ir,effect:sr,requires:["computeStyles"]};function Z(i){return i.split("-")[0]}var ft=Math.max,oe=Math.min,St=Math.round;function Ue(){var i=navigator.userAgentData;return i!=null&&i.brands&&Array.isArray(i.brands)?i.brands.map(function(t){return t.brand+"/"+t.version}).join(" "):navigator.userAgent}function rs(){return!/^((?!chrome|android).)*safari/i.test(Ue())}function Lt(i,t,e){t===void 0&&(t=!1),e===void 0&&(e=!1);var s=i.getBoundingClientRect(),n=1,r=1;t&&F(i)&&(n=i.offsetWidth>0&&St(s.width)/i.offsetWidth||1,r=i.offsetHeight>0&&St(s.height)/i.offsetHeight||1);var o=ht(i)?U(i):window,a=o.visualViewport,l=!rs()&&e,u=(s.left+(l&&a?a.offsetLeft:0))/n,c=(s.top+(l&&a?a.offsetTop:0))/r,f=s.width/n,v=s.height/r;return{width:f,height:v,top:c,right:u+f,bottom:c+v,left:u,x:u,y:c}}function Ye(i){var t=Lt(i),e=i.offsetWidth,s=i.offsetHeight;return Math.abs(t.width-e)<=1&&(e=t.width),Math.abs(t.height-s)<=1&&(s=t.height),{x:i.offsetLeft,y:i.offsetTop,width:e,height:s}}function os(i,t){var e=t.getRootNode&&t.getRootNode();if(i.contains(t))return!0;if(e&&Be(e)){var s=t;do{if(s&&i.isSameNode(s))return!0;s=s.parentNode||s.host}while(s)}return!1}function it(i){return U(i).getComputedStyle(i)}function nr(i){return["table","td","th"].indexOf(Q(i))>=0}function rt(i){return((ht(i)?i.ownerDocument:i.document)||window.document).documentElement}function ae(i){return Q(i)==="html"?i:i.assignedSlot||i.parentNode||(Be(i)?i.host:null)||rt(i)}function as(i){return!F(i)||it(i).position==="fixed"?null:i.offsetParent}function rr(i){var t=/firefox/i.test(Ue()),e=/Trident/i.test(Ue());if(e&&F(i)){var s=it(i);if(s.position==="fixed")return null}var n=ae(i);for(Be(n)&&(n=n.host);F(n)&&["html","body"].indexOf(Q(n))<0;){var r=it(n);if(r.transform!=="none"||r.perspective!=="none"||r.contain==="paint"||["transform","perspective"].indexOf(r.willChange)!==-1||t&&r.willChange==="filter"||t&&r.filter&&r.filter!=="none")return n;n=n.parentNode}return null}function Kt(i){for(var t=U(i),e=as(i);e&&nr(e)&&it(e).position==="static";)e=as(e);return e&&(Q(e)==="html"||Q(e)==="body"&&it(e).position==="static")?t:e||rr(i)||t}function Ke(i){return["top","bottom"].indexOf(i)>=0?"x":"y"}function Ft(i,t,e){return ft(i,oe(t,e))}function or(i,t,e){var s=Ft(i,t,e);return s>e?e:s}function ls(){return{top:0,right:0,bottom:0,left:0}}function cs(i){return Object.assign({},ls(),i)}function ds(i,t){return t.reduce(function(e,s){return e[s]=i,e},{})}var ar=function(t,e){return t=typeof t=="function"?t(Object.assign({},e.rects,{placement:e.placement})):t,cs(typeof t!="number"?t:ds(t,Ot))};function lr(i){var t,e=i.state,s=i.name,n=i.options,r=e.elements.arrow,o=e.modifiersData.popperOffsets,a=Z(e.placement),l=Ke(a),u=[H,j].indexOf(a)>=0,c=u?"height":"width";if(!(!r||!o)){var f=ar(n.padding,e),v=Ye(r),g=l==="y"?M:H,O=l==="y"?B:j,T=e.rects.reference[c]+e.rects.reference[l]-o[l]-e.rects.popper[c],y=o[l]-e.rects.reference[l],D=Kt(r),I=D?l==="y"?D.clientHeight||0:D.clientWidth||0:0,m=T/2-y/2,p=f[g],_=I-v[c]-f[O],b=I/2-v[c]/2+m,w=Ft(p,b,_),C=l;e.modifiersData[s]=(t={},t[C]=w,t.centerOffset=w-b,t)}}function cr(i){var t=i.state,e=i.options,s=e.element,n=s===void 0?"[data-popper-arrow]":s;n!=null&&(typeof n=="string"&&(n=t.elements.popper.querySelector(n),!n)||os(t.elements.popper,n)&&(t.elements.arrow=n))}const us={name:"arrow",enabled:!0,phase:"main",fn:lr,effect:cr,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function $t(i){return i.split("-")[1]}var dr={top:"auto",right:"auto",bottom:"auto",left:"auto"};function ur(i,t){var e=i.x,s=i.y,n=t.devicePixelRatio||1;return{x:St(e*n)/n||0,y:St(s*n)/n||0}}function hs(i){var t,e=i.popper,s=i.popperRect,n=i.placement,r=i.variation,o=i.offsets,a=i.position,l=i.gpuAcceleration,u=i.adaptive,c=i.roundOffsets,f=i.isFixed,v=o.x,g=v===void 0?0:v,O=o.y,T=O===void 0?0:O,y=typeof c=="function"?c({x:g,y:T}):{x:g,y:T};g=y.x,T=y.y;var D=o.hasOwnProperty("x"),I=o.hasOwnProperty("y"),m=H,p=M,_=window;if(u){var b=Kt(e),w="clientHeight",C="clientWidth";if(b===U(e)&&(b=rt(e),it(b).position!=="static"&&a==="absolute"&&(w="scrollHeight",C="scrollWidth")),b=b,n===M||(n===H||n===j)&&r===Dt){p=B;var A=f&&b===_&&_.visualViewport?_.visualViewport.height:b[w];T-=A-s.height,T*=l?1:-1}if(n===H||(n===M||n===B)&&r===Dt){m=j;var N=f&&b===_&&_.visualViewport?_.visualViewport.width:b[C];g-=N-s.width,g*=l?1:-1}}var $=Object.assign({position:a},u&&dr),V=c===!0?ur({x:g,y:T},U(e)):{x:g,y:T};if(g=V.x,T=V.y,l){var k;return Object.assign({},$,(k={},k[p]=I?"0":"",k[m]=D?"0":"",k.transform=(_.devicePixelRatio||1)<=1?"translate("+g+"px, "+T+"px)":"translate3d("+g+"px, "+T+"px, 0)",k))}return Object.assign({},$,(t={},t[p]=I?T+"px":"",t[m]=D?g+"px":"",t.transform="",t))}function hr(i){var t=i.state,e=i.options,s=e.gpuAcceleration,n=s===void 0?!0:s,r=e.adaptive,o=r===void 0?!0:r,a=e.roundOffsets,l=a===void 0?!0:a,u={placement:Z(t.placement),variation:$t(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:n,isFixed:t.options.strategy==="fixed"};t.modifiersData.popperOffsets!=null&&(t.styles.popper=Object.assign({},t.styles.popper,hs(Object.assign({},u,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:o,roundOffsets:l})))),t.modifiersData.arrow!=null&&(t.styles.arrow=Object.assign({},t.styles.arrow,hs(Object.assign({},u,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})}const Fe={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:hr,data:{}};var le={passive:!0};function fr(i){var t=i.state,e=i.instance,s=i.options,n=s.scroll,r=n===void 0?!0:n,o=s.resize,a=o===void 0?!0:o,l=U(t.elements.popper),u=[].concat(t.scrollParents.reference,t.scrollParents.popper);return r&&u.forEach(function(c){c.addEventListener("scroll",e.update,le)}),a&&l.addEventListener("resize",e.update,le),function(){r&&u.forEach(function(c){c.removeEventListener("scroll",e.update,le)}),a&&l.removeEventListener("resize",e.update,le)}}const ze={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:fr,data:{}};var pr={left:"right",right:"left",bottom:"top",top:"bottom"};function ce(i){return i.replace(/left|right|bottom|top/g,function(t){return pr[t]})}var _r={start:"end",end:"start"};function fs(i){return i.replace(/start|end/g,function(t){return _r[t]})}function qe(i){var t=U(i),e=t.pageXOffset,s=t.pageYOffset;return{scrollLeft:e,scrollTop:s}}function Xe(i){return Lt(rt(i)).left+qe(i).scrollLeft}function gr(i,t){var e=U(i),s=rt(i),n=e.visualViewport,r=s.clientWidth,o=s.clientHeight,a=0,l=0;if(n){r=n.width,o=n.height;var u=rs();(u||!u&&t==="fixed")&&(a=n.offsetLeft,l=n.offsetTop)}return{width:r,height:o,x:a+Xe(i),y:l}}function mr(i){var t,e=rt(i),s=qe(i),n=(t=i.ownerDocument)==null?void 0:t.body,r=ft(e.scrollWidth,e.clientWidth,n?n.scrollWidth:0,n?n.clientWidth:0),o=ft(e.scrollHeight,e.clientHeight,n?n.scrollHeight:0,n?n.clientHeight:0),a=-s.scrollLeft+Xe(i),l=-s.scrollTop;return it(n||e).direction==="rtl"&&(a+=ft(e.clientWidth,n?n.clientWidth:0)-r),{width:r,height:o,x:a,y:l}}function Ge(i){var t=it(i),e=t.overflow,s=t.overflowX,n=t.overflowY;return/auto|scroll|overlay|hidden/.test(e+n+s)}function ps(i){return["html","body","#document"].indexOf(Q(i))>=0?i.ownerDocument.body:F(i)&&Ge(i)?i:ps(ae(i))}function zt(i,t){var e;t===void 0&&(t=[]);var s=ps(i),n=s===((e=i.ownerDocument)==null?void 0:e.body),r=U(s),o=n?[r].concat(r.visualViewport||[],Ge(s)?s:[]):s,a=t.concat(o);return n?a:a.concat(zt(ae(o)))}function Qe(i){return Object.assign({},i,{left:i.x,top:i.y,right:i.x+i.width,bottom:i.y+i.height})}function Er(i,t){var e=Lt(i,!1,t==="fixed");return e.top=e.top+i.clientTop,e.left=e.left+i.clientLeft,e.bottom=e.top+i.clientHeight,e.right=e.left+i.clientWidth,e.width=i.clientWidth,e.height=i.clientHeight,e.x=e.left,e.y=e.top,e}function _s(i,t,e){return t===He?Qe(gr(i,e)):ht(t)?Er(t,e):Qe(mr(rt(i)))}function vr(i){var t=zt(ae(i)),e=["absolute","fixed"].indexOf(it(i).position)>=0,s=e&&F(i)?Kt(i):i;return ht(s)?t.filter(function(n){return ht(n)&&os(n,s)&&Q(n)!=="body"}):[]}function br(i,t,e,s){var n=t==="clippingParents"?vr(i):[].concat(t),r=[].concat(n,[e]),o=r[0],a=r.reduce(function(l,u){var c=_s(i,u,s);return l.top=ft(c.top,l.top),l.right=oe(c.right,l.right),l.bottom=oe(c.bottom,l.bottom),l.left=ft(c.left,l.left),l},_s(i,o,s));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}function gs(i){var t=i.reference,e=i.element,s=i.placement,n=s?Z(s):null,r=s?$t(s):null,o=t.x+t.width/2-e.width/2,a=t.y+t.height/2-e.height/2,l;switch(n){case M:l={x:o,y:t.y-e.height};break;case B:l={x:o,y:t.y+t.height};break;case j:l={x:t.x+t.width,y:a};break;case H:l={x:t.x-e.width,y:a};break;default:l={x:t.x,y:t.y}}var u=n?Ke(n):null;if(u!=null){var c=u==="y"?"height":"width";switch(r){case ut:l[u]=l[u]-(t[c]/2-e[c]/2);break;case Dt:l[u]=l[u]+(t[c]/2-e[c]/2);break}}return l}function It(i,t){t===void 0&&(t={});var e=t,s=e.placement,n=s===void 0?i.placement:s,r=e.strategy,o=r===void 0?i.strategy:r,a=e.boundary,l=a===void 0?zi:a,u=e.rootBoundary,c=u===void 0?He:u,f=e.elementContext,v=f===void 0?Nt:f,g=e.altBoundary,O=g===void 0?!1:g,T=e.padding,y=T===void 0?0:T,D=cs(typeof y!="number"?y:ds(y,Ot)),I=v===Nt?qi:Nt,m=i.rects.popper,p=i.elements[O?I:v],_=br(ht(p)?p:p.contextElement||rt(i.elements.popper),l,c,o),b=Lt(i.elements.reference),w=gs({reference:b,element:m,strategy:"absolute",placement:n}),C=Qe(Object.assign({},m,w)),A=v===Nt?C:b,N={top:_.top-A.top+D.top,bottom:A.bottom-_.bottom+D.bottom,left:_.left-A.left+D.left,right:A.right-_.right+D.right},$=i.modifiersData.offset;if(v===Nt&&$){var V=$[n];Object.keys(N).forEach(function(k){var vt=[j,B].indexOf(k)>=0?1:-1,bt=[M,B].indexOf(k)>=0?"y":"x";N[k]+=V[bt]*vt})}return N}function wr(i,t){t===void 0&&(t={});var e=t,s=e.placement,n=e.boundary,r=e.rootBoundary,o=e.padding,a=e.flipVariations,l=e.allowedAutoPlacements,u=l===void 0?We:l,c=$t(s),f=c?a?Ve:Ve.filter(function(O){return $t(O)===c}):Ot,v=f.filter(function(O){return u.indexOf(O)>=0});v.length===0&&(v=f);var g=v.reduce(function(O,T){return O[T]=It(i,{placement:T,boundary:n,rootBoundary:r,padding:o})[Z(T)],O},{});return Object.keys(g).sort(function(O,T){return g[O]-g[T]})}function Tr(i){if(Z(i)===re)return[];var t=ce(i);return[fs(i),t,fs(t)]}function Ar(i){var t=i.state,e=i.options,s=i.name;if(!t.modifiersData[s]._skip){for(var n=e.mainAxis,r=n===void 0?!0:n,o=e.altAxis,a=o===void 0?!0:o,l=e.fallbackPlacements,u=e.padding,c=e.boundary,f=e.rootBoundary,v=e.altBoundary,g=e.flipVariations,O=g===void 0?!0:g,T=e.allowedAutoPlacements,y=t.options.placement,D=Z(y),I=D===y,m=l||(I||!O?[ce(y)]:Tr(y)),p=[y].concat(m).reduce(function(jt,lt){return jt.concat(Z(lt)===re?wr(t,{placement:lt,boundary:c,rootBoundary:f,padding:u,flipVariations:O,allowedAutoPlacements:T}):lt)},[]),_=t.rects.reference,b=t.rects.popper,w=new Map,C=!0,A=p[0],N=0;N=0,bt=vt?"width":"height",K=It(t,{placement:$,boundary:c,rootBoundary:f,altBoundary:v,padding:u}),G=vt?k?j:H:k?B:M;_[bt]>b[bt]&&(G=ce(G));var De=ce(G),wt=[];if(r&&wt.push(K[V]<=0),a&&wt.push(K[G]<=0,K[De]<=0),wt.every(function(jt){return jt})){A=$,C=!1;break}w.set($,wt)}if(C)for(var Ne=O?3:1,Di=function(lt){var ie=p.find(function(Le){var Tt=w.get(Le);if(Tt)return Tt.slice(0,lt).every(function(Ni){return Ni})});if(ie)return A=ie,"break"},ee=Ne;ee>0;ee--){var Se=Di(ee);if(Se==="break")break}t.placement!==A&&(t.modifiersData[s]._skip=!0,t.placement=A,t.reset=!0)}}const ms={name:"flip",enabled:!0,phase:"main",fn:Ar,requiresIfExists:["offset"],data:{_skip:!1}};function Es(i,t,e){return e===void 0&&(e={x:0,y:0}),{top:i.top-t.height-e.y,right:i.right-t.width+e.x,bottom:i.bottom-t.height+e.y,left:i.left-t.width-e.x}}function vs(i){return[M,j,B,H].some(function(t){return i[t]>=0})}function yr(i){var t=i.state,e=i.name,s=t.rects.reference,n=t.rects.popper,r=t.modifiersData.preventOverflow,o=It(t,{elementContext:"reference"}),a=It(t,{altBoundary:!0}),l=Es(o,s),u=Es(a,n,r),c=vs(l),f=vs(u);t.modifiersData[e]={referenceClippingOffsets:l,popperEscapeOffsets:u,isReferenceHidden:c,hasPopperEscaped:f},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":c,"data-popper-escaped":f})}const bs={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:yr};function Cr(i,t,e){var s=Z(i),n=[H,M].indexOf(s)>=0?-1:1,r=typeof e=="function"?e(Object.assign({},t,{placement:i})):e,o=r[0],a=r[1];return o=o||0,a=(a||0)*n,[H,j].indexOf(s)>=0?{x:a,y:o}:{x:o,y:a}}function Or(i){var t=i.state,e=i.options,s=i.name,n=e.offset,r=n===void 0?[0,0]:n,o=We.reduce(function(c,f){return c[f]=Cr(f,t.rects,r),c},{}),a=o[t.placement],l=a.x,u=a.y;t.modifiersData.popperOffsets!=null&&(t.modifiersData.popperOffsets.x+=l,t.modifiersData.popperOffsets.y+=u),t.modifiersData[s]=o}const ws={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:Or};function Dr(i){var t=i.state,e=i.name;t.modifiersData[e]=gs({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})}const Ze={name:"popperOffsets",enabled:!0,phase:"read",fn:Dr,data:{}};function Nr(i){return i==="x"?"y":"x"}function Sr(i){var t=i.state,e=i.options,s=i.name,n=e.mainAxis,r=n===void 0?!0:n,o=e.altAxis,a=o===void 0?!1:o,l=e.boundary,u=e.rootBoundary,c=e.altBoundary,f=e.padding,v=e.tether,g=v===void 0?!0:v,O=e.tetherOffset,T=O===void 0?0:O,y=It(t,{boundary:l,rootBoundary:u,padding:f,altBoundary:c}),D=Z(t.placement),I=$t(t.placement),m=!I,p=Ke(D),_=Nr(p),b=t.modifiersData.popperOffsets,w=t.rects.reference,C=t.rects.popper,A=typeof T=="function"?T(Object.assign({},t.rects,{placement:t.placement})):T,N=typeof A=="number"?{mainAxis:A,altAxis:A}:Object.assign({mainAxis:0,altAxis:0},A),$=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,V={x:0,y:0};if(b){if(r){var k,vt=p==="y"?M:H,bt=p==="y"?B:j,K=p==="y"?"height":"width",G=b[p],De=G+y[vt],wt=G-y[bt],Ne=g?-C[K]/2:0,Di=I===ut?w[K]:C[K],ee=I===ut?-C[K]:-w[K],Se=t.elements.arrow,jt=g&&Se?Ye(Se):{width:0,height:0},lt=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:ls(),ie=lt[vt],Le=lt[bt],Tt=Ft(0,w[K],jt[K]),Ni=m?w[K]/2-Ne-Tt-ie-N.mainAxis:Di-Tt-ie-N.mainAxis,Pl=m?-w[K]/2+Ne+Tt+Le+N.mainAxis:ee+Tt+Le+N.mainAxis,Si=t.elements.arrow&&Kt(t.elements.arrow),Ml=Si?p==="y"?Si.clientTop||0:Si.clientLeft||0:0,In=(k=$==null?void 0:$[p])!=null?k:0,Hl=G+Ni-In-Ml,Vl=G+Pl-In,Rn=Ft(g?oe(De,Hl):De,G,g?ft(wt,Vl):wt);b[p]=Rn,V[p]=Rn-G}if(a){var kn,Wl=p==="x"?M:H,Bl=p==="x"?B:j,At=b[_],$e=_==="y"?"height":"width",xn=At+y[Wl],Pn=At-y[Bl],Li=[M,H].indexOf(D)!==-1,Mn=(kn=$==null?void 0:$[_])!=null?kn:0,Hn=Li?xn:At-w[$e]-C[$e]-Mn+N.altAxis,Vn=Li?At+w[$e]+C[$e]-Mn-N.altAxis:Pn,Wn=g&&Li?or(Hn,At,Vn):Ft(g?Hn:xn,At,g?Vn:Pn);b[_]=Wn,V[_]=Wn-At}t.modifiersData[s]=V}}const Ts={name:"preventOverflow",enabled:!0,phase:"main",fn:Sr,requiresIfExists:["offset"]};function Lr(i){return{scrollLeft:i.scrollLeft,scrollTop:i.scrollTop}}function $r(i){return i===U(i)||!F(i)?qe(i):Lr(i)}function Ir(i){var t=i.getBoundingClientRect(),e=St(t.width)/i.offsetWidth||1,s=St(t.height)/i.offsetHeight||1;return e!==1||s!==1}function Rr(i,t,e){e===void 0&&(e=!1);var s=F(t),n=F(t)&&Ir(t),r=rt(t),o=Lt(i,n,e),a={scrollLeft:0,scrollTop:0},l={x:0,y:0};return(s||!s&&!e)&&((Q(t)!=="body"||Ge(r))&&(a=$r(t)),F(t)?(l=Lt(t,!0),l.x+=t.clientLeft,l.y+=t.clientTop):r&&(l.x=Xe(r))),{x:o.left+a.scrollLeft-l.x,y:o.top+a.scrollTop-l.y,width:o.width,height:o.height}}function kr(i){var t=new Map,e=new Set,s=[];i.forEach(function(r){t.set(r.name,r)});function n(r){e.add(r.name);var o=[].concat(r.requires||[],r.requiresIfExists||[]);o.forEach(function(a){if(!e.has(a)){var l=t.get(a);l&&n(l)}}),s.push(r)}return i.forEach(function(r){e.has(r.name)||n(r)}),s}function xr(i){var t=kr(i);return ns.reduce(function(e,s){return e.concat(t.filter(function(n){return n.phase===s}))},[])}function Pr(i){var t;return function(){return t||(t=new Promise(function(e){Promise.resolve().then(function(){t=void 0,e(i())})})),t}}function Mr(i){var t=i.reduce(function(e,s){var n=e[s.name];return e[s.name]=n?Object.assign({},n,s,{options:Object.assign({},n.options,s.options),data:Object.assign({},n.data,s.data)}):s,e},{});return Object.keys(t).map(function(e){return t[e]})}var As={placement:"bottom",modifiers:[],strategy:"absolute"};function ys(){for(var i=arguments.length,t=new Array(i),e=0;e`-${t.toLowerCase()}`)}const E={setDataAttribute(i,t,e){i.setAttribute(`data-twe-${ei(t)}`,e)},removeDataAttribute(i,t){i.removeAttribute(`data-twe-${ei(t)}`)},getDataAttributes(i){if(!i)return{};const t={};return Object.keys(i.dataset).filter(e=>e.startsWith("twe")).forEach(e=>{if(e.startsWith("tweClass"))return;let s=e.replace(/^twe/,"");s=s.charAt(0).toLowerCase()+s.slice(1,s.length),t[s]=ti(i.dataset[e])}),t},getDataClassAttributes(i){if(!i)return{};const t={...i.dataset};return Object.keys(t).filter(e=>e.startsWith("tweClass")).forEach(e=>{let s=e.replace(/^tweClass/,"");s=s.charAt(0).toLowerCase()+s.slice(1,s.length),t[s]=ti(t[e])}),t},getDataAttribute(i,t){return ti(i.getAttribute(`data-twe-${ei(t)}`))},offset(i){const t=i.getBoundingClientRect();return{top:t.top+document.body.scrollTop,left:t.left+document.body.scrollLeft}},position(i){return{top:i.offsetTop,left:i.offsetLeft}},style(i,t){Object.assign(i.style,t)},toggleClass(i,t){i&&ii(t).forEach(e=>{i.classList.contains(e)?i.classList.remove(e):i.classList.add(e)})},addClass(i,t){ii(t).forEach(e=>!i.classList.contains(e)&&i.classList.add(e))},addStyle(i,t){Object.keys(t).forEach(e=>{i.style[e]=t[e]})},removeClass(i,t){ii(t).forEach(e=>i.classList.contains(e)&&i.classList.remove(e))},hasClass(i,t){return i.classList.contains(t)},maxOffset(i){const t=i.getBoundingClientRect();return{top:t.top+Math.max(document.body.scrollTop,document.documentElement.scrollTop,window.scrollY),left:t.left+Math.max(document.body.scrollLeft,document.documentElement.scrollLeft,window.scrollX)}}};function ii(i){return typeof i=="string"?i.split(" "):Array.isArray(i)?i:!1}const jr=3,h={closest(i,t){return i.closest(t)},matches(i,t){return i.matches(t)},find(i,t=document.documentElement){return[].concat(...Element.prototype.querySelectorAll.call(t,i))},findOne(i,t=document.documentElement){return Element.prototype.querySelector.call(t,i)},children(i,t){return[].concat(...i.children).filter(s=>s.matches(t))},parents(i,t){const e=[];let s=i.parentNode;for(;s&&s.nodeType===Node.ELEMENT_NODE&&s.nodeType!==jr;)this.matches(s,t)&&e.push(s),s=s.parentNode;return e},prev(i,t){let e=i.previousElementSibling;for(;e;){if(e.matches(t))return[e];e=e.previousElementSibling}return[]},next(i,t){let e=i.nextElementSibling;for(;e;){if(this.matches(e,t))return[e];e=e.nextElementSibling}return[]},focusableChildren(i){const t=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(e=>`${e}:not([tabindex^="-"])`).join(", ");return this.find(t,i).filter(e=>!ct(e)&&Ct(e))}},si="dropdown",pt=".twe.dropdown",ni=".data-api",ue="Escape",Os="Space",Ds="Tab",ri="ArrowUp",he="ArrowDown",Ur=2,Yr=new RegExp(`${ri}|${he}|${ue}`),Kr=`hide${pt}`,Fr=`hidden${pt}`,zr=`show${pt}`,qr=`shown${pt}`,Xr=`click${pt}${ni}`,Ns=`keydown${pt}${ni}`,Gr=`keyup${pt}${ni}`,ot="show",Qr="dropup",Zr="dropend",Jr="dropstart",to="[data-twe-navbar-ref]",fe="[data-twe-dropdown-toggle-ref]",oi="[data-twe-dropdown-menu-ref]",eo="[data-twe-navbar-nav-ref]",io="[data-twe-dropdown-menu-ref] [data-twe-dropdown-item-ref]:not(.disabled):not(:disabled)",so=W()?"top-end":"top-start",no=W()?"top-start":"top-end",ro=W()?"bottom-end":"bottom-start",oo=W()?"bottom-start":"bottom-end",ao=W()?"left-start":"right-start",lo=W()?"right-start":"left-start",co=[{opacity:"0"},{opacity:"1"}],uo=[{opacity:"1"},{opacity:"0"}],Ss={iterations:1,easing:"ease",fill:"both"},ho={offset:[0,2],boundary:"clippingParents",reference:"toggle",display:"dynamic",popperConfig:null,autoClose:!0,dropdownAnimation:"on",animationDuration:550},fo={offset:"(array|string|function)",boundary:"(string|element)",reference:"(string|element|object)",display:"string",popperConfig:"(null|object|function)",autoClose:"(boolean|string)",dropdownAnimation:"string",animationDuration:"number"};class z extends et{constructor(t,e){super(t),this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._fadeOutAnimate=null;const s=window.matchMedia("(prefers-reduced-motion: reduce)").matches;this._animationCanPlay=this._config.dropdownAnimation==="on"&&!s,this._didInit=!1,this._init()}static get Default(){return ho}static get DefaultType(){return fo}static get NAME(){return si}toggle(){return this._isShown()?this.hide():this.show()}show(){if(ct(this._element)||this._isShown(this._menu))return;const t={relatedTarget:this._element};if(d.trigger(this._element,zr,t).defaultPrevented)return;const s=z.getParentFromElement(this._element);this._inNavbar?E.setDataAttribute(this._menu,"popper","none"):this._createPopper(s),"ontouchstart"in document.documentElement&&!s.closest(eo)&&[].concat(...document.body.children).forEach(n=>d.on(n,"mouseover",se)),this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.setAttribute(`data-twe-dropdown-${ot}`,""),this._animationCanPlay&&this._menu.animate(co,{...Ss,duration:this._config.animationDuration}),this._element.setAttribute(`data-twe-dropdown-${ot}`,""),setTimeout(()=>{d.trigger(this._element,qr,t)},this._animationCanPlay?this._config.animationDuration:0)}hide(){if(ct(this._element)||!this._isShown(this._menu))return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_init(){this._didInit||(d.on(document,Ns,fe,z.dataApiKeydownHandler),d.on(document,Ns,oi,z.dataApiKeydownHandler),d.on(document,Xr,z.clearMenus),d.on(document,Gr,z.clearMenus),this._didInit=!0)}_completeHide(t){this._fadeOutAnimate&&this._fadeOutAnimate.playState==="running"||d.trigger(this._element,Kr,t).defaultPrevented||("ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach(s=>d.off(s,"mouseover",se)),this._animationCanPlay&&(this._fadeOutAnimate=this._menu.animate(uo,{...Ss,duration:this._config.animationDuration})),setTimeout(()=>{this._popper&&this._popper.destroy(),this._menu.removeAttribute(`data-twe-dropdown-${ot}`),this._element.removeAttribute(`data-twe-dropdown-${ot}`),this._element.setAttribute("aria-expanded","false"),E.removeDataAttribute(this._menu,"popper"),d.trigger(this._element,Fr,t)},this._animationCanPlay?this._config.animationDuration:0))}_getConfig(t){if(t={...this.constructor.Default,...E.getDataAttributes(this._element),...t},x(si,t,this.constructor.DefaultType),typeof t.reference=="object"&&!yt(t.reference)&&typeof t.reference.getBoundingClientRect!="function")throw new TypeError(`${si.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(t){if(typeof Cs>"u")throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;this._config.reference==="parent"?e=t:yt(this._config.reference)?e=nt(this._config.reference):typeof this._config.reference=="object"&&(e=this._config.reference);const s=this._getPopperConfig(),n=s.modifiers.find(r=>r.name==="applyStyles"&&r.enabled===!1);this._popper=Je(e,this._menu,s),n&&E.setDataAttribute(this._menu,"popper","static")}_isShown(t=this._element){return t.dataset[`tweDropdown${ot.charAt(0).toUpperCase()+ot.slice(1)}`]===""}_getMenuElement(){return h.next(this._element,oi)[0]}_getPlacement(){const t=this._element.parentNode;if(t.dataset.tweDropdownPosition===Zr)return ao;if(t.dataset.tweDropdownPosition===Jr)return lo;const e=t.dataset.tweDropdownAlignment==="end";return t.dataset.tweDropdownPosition===Qr?e?no:so:e?oo:ro}_detectNavbar(){return this._element.closest(to)!==null}_getOffset(){const{offset:t}=this._config;return typeof t=="string"?t.split(",").map(e=>Number.parseInt(e,10)):typeof t=="function"?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return this._config.display==="static"&&(t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...typeof this._config.popperConfig=="function"?this._config.popperConfig(t):this._config.popperConfig}}_selectMenuItem({key:t,target:e}){const s=h.find(io,this._menu).filter(Ct);s.length&&Hi(s,e,t===he,!s.includes(e)).focus()}static jQueryInterface(t){return this.each(function(){const e=z.getOrCreateInstance(this,t);if(typeof t=="string"){if(typeof e[t]>"u")throw new TypeError(`No method named "${t}"`);e[t]()}})}static clearMenus(t){if(t&&(t.button===Ur||t.type==="keyup"&&t.key!==Ds))return;const e=h.find(fe);for(let s=0,n=e.length;sc===this._element);l!==null&&u.length&&(this._selector=l,this._triggerArray.push(a))}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return $s}static get NAME(){return ai}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[],e;if(this._config.parent){const c=h.find(Rs,this._config.parent);t=h.find(To,this._config.parent).filter(f=>!c.includes(f))}const s=h.findOne(this._selector);if(t.length){const c=t.find(f=>s!==f);if(e=c?kt.getInstance(c):null,e&&e._isTransitioning)return}if(d.trigger(this._element,_o).defaultPrevented)return;t.forEach(c=>{s!==c&&kt.getOrCreateInstance(c,{toggle:!1}).hide(),e||R.setData(c,Ls,null)});const r=this._getDimension(),o=r==="height"?this._classes.collapsing:this._classes.collapsingHorizontal;E.removeClass(this._element,this._classes.visible),E.removeClass(this._element,this._classes.hidden),E.addClass(this._element,o),this._element.removeAttribute(Rt),this._element.setAttribute(_e,""),this._element.style[r]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const a=()=>{this._isTransitioning=!1,E.removeClass(this._element,this._classes.hidden),E.removeClass(this._element,o),E.addClass(this._element,this._classes.visible),this._element.removeAttribute(_e),this._element.setAttribute(Rt,""),this._element.setAttribute(li,""),this._element.style[r]="",d.trigger(this._element,go)},u=`scroll${r[0].toUpperCase()+r.slice(1)}`;this._queueCallback(a,this._element,!0),this._element.style[r]=`${this._element[u]}px`}hide(){if(this._isTransitioning||!this._isShown()||d.trigger(this._element,mo).defaultPrevented)return;const e=this._getDimension(),s=e==="height"?this._classes.collapsing:this._classes.collapsingHorizontal;this._element.style[e]=`${this._element.getBoundingClientRect()[e]}px`,Ut(this._element),E.addClass(this._element,s),E.removeClass(this._element,this._classes.visible),E.removeClass(this._element,this._classes.hidden),this._element.setAttribute(_e,""),this._element.removeAttribute(Rt),this._element.removeAttribute(li);const n=this._triggerArray.length;for(let o=0;o{this._isTransitioning=!1,E.removeClass(this._element,s),E.addClass(this._element,this._classes.visible),E.addClass(this._element,this._classes.hidden),this._element.removeAttribute(_e),this._element.setAttribute(Rt,""),d.trigger(this._element,Eo)};this._element.style[e]="",this._queueCallback(r,this._element,!0)}_isShown(t=this._element){return t.hasAttribute(li)}_getConfig(t){return t={...$s,...E.getDataAttributes(this._element),...t},t.toggle=!!t.toggle,t.parent=nt(t.parent),x(ai,t,po),t}_getClasses(t){const e=E.getDataClassAttributes(this._element);return t={...Ao,...e,...t},x(ai,t,yo),t}_getDimension(){return this._element.hasAttribute(vo)?bo:wo}_initializeChildren(){if(!this._config.parent)return;const t=h.find(Rs,this._config.parent);h.find(ks,this._config.parent).filter(e=>!t.includes(e)).forEach(e=>{const s=st(e);s&&this._addAriaAndCollapsedClass([e],this._isShown(s))})}_addAriaAndCollapsedClass(t,e){t.length&&t.forEach(s=>{e?s.removeAttribute(Is):s.setAttribute(`${Is}`,""),s.setAttribute("aria-expanded",e)})}static jQueryInterface(t){return this.each(function(){const e={};typeof t=="string"&&/show|hide/.test(t)&&(e.toggle=!1);const s=kt.getOrCreateInstance(this,e);if(typeof t=="string"){if(typeof s[t]>"u")throw new TypeError(`No method named "${t}"`);s[t]()}})}}const xs=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Ps=".sticky-top";class ci{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,"paddingRight",e=>e+t),this._setElementAttributes(xs,"paddingRight",e=>e+t),this._setElementAttributes(Ps,"marginRight",e=>e-t)}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,s){const n=this.getWidth(),r=o=>{if(o!==this._element&&window.innerWidth>o.clientWidth+n)return;this._saveInitialAttribute(o,e);const a=window.getComputedStyle(o)[e];o.style[e]=`${s(Number.parseFloat(a))}px`};this._applyManipulationCallback(t,r)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,"paddingRight"),this._resetElementAttributes(xs,"paddingRight"),this._resetElementAttributes(Ps,"marginRight")}_saveInitialAttribute(t,e){const s=t.style[e];s&&E.setDataAttribute(t,e,s)}_resetElementAttributes(t,e){const s=n=>{const r=E.getDataAttribute(n,e);typeof r>"u"?n.style.removeProperty(e):(E.removeDataAttribute(n,e),n.style[e]=r)};this._applyManipulationCallback(t,s)}_applyManipulationCallback(t,e){yt(t)?e(t):h.find(t,this._element).forEach(e)}isOverflowing(){return this.getWidth()>0}}const Co={isVisible:!0,isAnimated:!1,rootElement:"body",clickCallback:null,backdropClasses:null},Oo={isVisible:"boolean",isAnimated:"boolean",rootElement:"(element|string)",clickCallback:"(function|null)",backdropClasses:"(array|string|null)"},Ms="backdrop",Hs=`mousedown.twe.${Ms}`;class Vs{constructor(t){this._config=this._getConfig(t),this._isAppended=!1,this._element=null}show(t){if(!this._config.isVisible){dt(t);return}this._append(),this._config.isAnimated&&Ut(this._getElement());const e=this._config.backdropClasses||["opacity-50","transition-all","duration-300","ease-in-out","fixed","top-0","left-0","z-[1040]","bg-black","w-screen","h-screen"];E.removeClass(this._getElement(),"opacity-0"),E.addClass(this._getElement(),e),this._element.setAttribute("data-twe-backdrop-show",""),this._emulateAnimation(()=>{dt(t)})}hide(t){if(!this._config.isVisible){dt(t);return}this._element.removeAttribute("data-twe-backdrop-show"),this._getElement().classList.add("opacity-0"),this._getElement().classList.remove("opacity-50"),this._emulateAnimation(()=>{this.dispose(),dt(t)})}update(t={}){this._config=this._getConfig({...this._config,...t})}_getElement(){if(!this._element){const t=document.createElement("div");this._element=t}return this._element}_getConfig(t){return t={...Co,...typeof t=="object"?t:{}},t.rootElement=nt(t.rootElement),x(Ms,t,Oo),t}_append(){this._isAppended||(this._config.rootElement.append(this._getElement()),d.on(this._getElement(),Hs,()=>{dt(this._config.clickCallback)}),this._isAppended=!0)}dispose(){this._isAppended&&(d.off(this._element,Hs),this._element.remove(),this._isAppended=!1)}_emulateAnimation(t){Mi(t,this._getElement(),this._config.isAnimated)}}class Ws{constructor(t,e={},s){this._element=t,this._toggler=s,this._event=e.event||"blur",this._condition=e.condition||(()=>!0),this._selector=e.selector||'button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])',this._onlyVisible=e.onlyVisible||!1,this._focusableElements=[],this._firstElement=null,this._lastElement=null,this.handler=n=>{this._condition(n)&&!n.shiftKey&&n.target===this._lastElement?(n.preventDefault(),this._firstElement.focus()):this._condition(n)&&n.shiftKey&&n.target===this._firstElement&&(n.preventDefault(),this._lastElement.focus())}}trap(){this._setElements(),this._init(),this._setFocusTrap()}disable(){this._focusableElements.forEach(t=>{t.removeEventListener(this._event,this.handler)}),this._toggler&&this._toggler.focus()}update(){this._setElements(),this._setFocusTrap()}_init(){const t=e=>{!this._firstElement||e.key!=="Tab"||this._focusableElements.includes(e.target)||(e.preventDefault(),this._firstElement.focus(),window.removeEventListener("keydown",t))};window.addEventListener("keydown",t)}_filterVisible(t){return t.filter(e=>{if(!Ct(e))return!1;const s=h.parents(e,"*");for(let n=0;n{const s=e.getAttribute("data-twe-disabled")==="true"||e.hasAttribute("disabled");return e.disabled||s?null:e}),this._onlyVisible&&(this._focusableElements=this._filterVisible(this._focusableElements)),this._firstElement=this._focusableElements[0],this._lastElement=this._focusableElements[this._focusableElements.length-1]}_setFocusTrap(){this._focusableElements.forEach((t,e)=>{e===this._focusableElements.length-1||e===0?t.addEventListener(this._event,this.handler):t.removeEventListener(this._event,this.handler)})}}let Bs=[];const js=(i,t="hide")=>{const e=`click.dismiss${i.EVENT_KEY}`,s=i.NAME;Bs.includes(s)||(Bs.push(s),d.on(document,e,`[data-twe-${s}-dismiss]`,function(n){if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),ct(this))return;const r=st(this)||this.closest(`.${s}`)||this.closest(`[data-twe-${s}-init]`);if(!r)return;i.getOrCreateInstance(r)[t]()}))},Do=9,Us="offcanvas",xt=".twe.offcanvas",No=`load${xt}.data-api`,So="Escape",Ys={backdrop:!0,keyboard:!0,scroll:!1},Lo={backdrop:"boolean",keyboard:"boolean",scroll:"boolean"},Ks="show",$o="[data-twe-offcanvas-init][data-twe-offcanvas-show]",Io=`show${xt}`,Ro=`shown${xt}`,ko=`hide${xt}`,xo=`hidden${xt}`,Po=`keydown.dismiss${xt}`;class Pt extends et{constructor(t,e){super(t),this._config=this._getConfig(e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners(),this._didInit=!1,this._init()}static get NAME(){return Us}static get Default(){return Ys}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){if(this._isShown||d.trigger(this._element,Io,{relatedTarget:t}).defaultPrevented)return;this._isShown=!0,this._element.style.visibility="visible",this._backdrop.show(),this._config.scroll||new ci().hide(),this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.setAttribute(`data-twe-offcanvas-${Ks}`,"");const s=()=>{this._config.scroll||this._focustrap.trap(),d.trigger(this._element,Ro,{relatedTarget:t})};this._queueCallback(s,this._element,!0)}hide(){if(!this._isShown||d.trigger(this._element,ko).defaultPrevented)return;this._focustrap.disable(),this._element.blur(),this._isShown=!1,this._element.removeAttribute(`data-twe-offcanvas-${Ks}`),this._backdrop.hide();const e=()=>{this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._element.style.visibility="hidden",this._config.scroll||new ci().reset(),d.trigger(this._element,xo)};this._queueCallback(e,this._element,!0)}dispose(){this._backdrop.dispose(),this._focustrap.disable(),super.dispose()}_init(){this._didInit||(d.on(window,No,()=>h.find($o).forEach(t=>Pt.getOrCreateInstance(t).show())),this._didInit=!0,js(Pt))}_getConfig(t){return t={...Ys,...E.getDataAttributes(this._element),...typeof t=="object"?t:{}},x(Us,t,Lo),t}_initializeBackDrop(){return new Vs({isVisible:this._config.backdrop,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:()=>this.hide()})}_initializeFocusTrap(){return new Ws(this._element,{event:"keydown",condition:t=>t.keyCode===Do,onlyVisible:!0})}_addEventListeners(){d.on(this._element,Po,t=>{this._config.keyboard&&t.key===So&&this.hide()})}static jQueryInterface(t){return this.each(function(){const e=Pt.getOrCreateInstance(this,t);if(typeof t=="string"){if(e[t]===void 0||t.startsWith("_")||t==="constructor")throw new TypeError(`No method named "${t}"`);e[t](this)}})}}const di="carousel",Y=".twe.carousel",Fs=".data-api",Mo="ArrowLeft",Ho="ArrowRight",Vo=500,Wo=40,zs={interval:5e3,keyboard:!0,ride:!1,pause:"hover",wrap:!0,touch:!0},Bo={interval:"(number|boolean)",keyboard:"boolean",ride:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},jo={pointer:"touch-pan-y",block:"!block",visible:"data-[twe-carousel-fade]:opacity-100 data-[twe-carousel-fade]:z-[1]",invisible:"data-[twe-carousel-fade]:z-0 data-[twe-carousel-fade]:opacity-0 data-[twe-carousel-fade]:duration-[600ms] data-[twe-carousel-fade]:delay-600",slideRight:"translate-x-full",slideLeft:"-translate-x-full"},Uo={pointer:"string",block:"string",visible:"string",invisible:"string",slideRight:"string",slideLeft:"string"},_t="next",gt="prev",mt="left",qt="right",Yo={[Mo]:qt,[Ho]:mt},Ko=`slide${Y}`,ui=`slid${Y}`,Fo=`keydown${Y}`,zo=`mouseenter${Y}`,qo=`mouseleave${Y}`,Xo=`touchstart${Y}`,Go=`touchmove${Y}`,Qo=`touchend${Y}`,Zo=`pointerdown${Y}`,Jo=`pointerup${Y}`,ta=`dragstart${Y}`,qs=`load${Y}${Fs}`,Xs=`click${Y}${Fs}`,Xt="data-twe-carousel-active",ea="data-twe-carousel-item-end",hi="data-twe-carousel-item-start",ia="data-twe-carousel-item-next",sa="data-twe-carousel-item-prev",na="data-twe-carousel-pointer-event",ra="[data-twe-carousel-init]",Gs="[data-twe-carousel-active]",fi="[data-twe-carousel-item]",Mt=`${Gs}${fi}`,oa=`${fi} img`,aa="[data-twe-carousel-item-next], [data-twe-carousel-item-prev]",la="[data-twe-carousel-indicators]",ca="[data-twe-target]",Qs="[data-twe-slide], [data-twe-slide-to]",da="touch",ua="pen";class q extends et{constructor(t,e,s){super(t),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._classes=this._getClasses(s),this._indicatorsElement=h.findOne(la,this._element),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=!!window.PointerEvent,this._setActiveElementClass(),this._addEventListeners(),this._didInit=!1,this._init(),this._config.ride==="carousel"&&this.cycle()}static get Default(){return zs}static get NAME(){return di}next(){this._slide(_t)}nextWhenVisible(){!document.hidden&&Ct(this._element)&&this.next()}prev(){this._slide(gt)}pause(t){t||(this._isPaused=!0),h.findOne(aa,this._element)&&(Ri(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null}cycle(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))}to(t){this._activeElement=h.findOne(Mt,this._element);const e=this._getItemIndex(this._activeElement);if(t>this._items.length-1||t<0)return;if(this._isSliding){d.one(this._element,ui,()=>this.to(t));return}if(e===t){this.pause(),this.cycle();return}const s=t>e?_t:gt;this._slide(s,this._items[t])}dispose(){d.off(document,Xs,Qs,q.dataApiClickHandler),d.off(window,qs),super.dispose()}_init(){this._didInit||(d.on(document,Xs,Qs,q.dataApiClickHandler),d.on(window,qs,()=>{const t=h.find(ra);for(let e=0,s=t.length;ethis.cycle());return}this.cycle()}}_applyInitialClasses(){const t=h.findOne(Mt,this._element);t.classList.add(this._classes.block,...this._classes.visible.split(" ")),this._setActiveIndicatorElement(t)}_handleSwipe(){const t=Math.abs(this.touchDeltaX);if(t<=Wo)return;const e=t/this.touchDeltaX;this.touchDeltaX=0,e&&this._slide(e>0?qt:mt)}_setActiveElementClass(){this._activeElement=h.findOne(Mt,this._element),E.addClass(this._activeElement,"hidden")}_addEventListeners(){this._config.keyboard&&d.on(this._element,Fo,t=>this._keydown(t)),this._config.pause==="hover"&&(d.on(this._element,zo,t=>this.pause(t)),d.on(this._element,qo,t=>this._enableCycle(t))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners(),this._applyInitialClasses()}_addTouchEventListeners(){const t=r=>this._pointerEvent&&(r.pointerType===ua||r.pointerType===da),e=r=>{t(r)?this.touchStartX=r.clientX:this._pointerEvent||(this.touchStartX=r.touches[0].clientX)},s=r=>{this.touchDeltaX=r.touches&&r.touches.length>1?0:r.touches[0].clientX-this.touchStartX},n=r=>{t(r)&&(this.touchDeltaX=r.clientX-this.touchStartX),this._handleSwipe(),this._config.pause==="hover"&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(o=>this._enableCycle(o),Vo+this._config.interval))};h.find(oa,this._element).forEach(r=>{d.on(r,ta,o=>o.preventDefault())}),this._pointerEvent?(d.on(this._element,Zo,r=>e(r)),d.on(this._element,Jo,r=>n(r)),this._element.classList.add(this._classes.pointer),this._element.setAttribute(`${na}`,"")):(d.on(this._element,Xo,r=>e(r)),d.on(this._element,Go,r=>s(r)),d.on(this._element,Qo,r=>n(r)))}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Yo[t.key];e&&(t.preventDefault(),this._slide(e))}_getItemIndex(t){return this._items=t&&t.parentNode?h.find(fi,t.parentNode):[],this._items.indexOf(t)}_getItemByOrder(t,e){const s=t===_t;return Hi(this._items,e,s,this._config.wrap)}_triggerSlideEvent(t,e){const s=this._getItemIndex(t),n=this._getItemIndex(h.findOne(Mt,this._element));return d.trigger(this._element,Ko,{relatedTarget:t,direction:e,from:n,to:s})}_setActiveIndicatorElement(t){if(this._indicatorsElement){const e=h.findOne(Gs,this._indicatorsElement);e.removeAttribute(Xt),e.removeAttribute("aria-current"),e.classList.remove("!opacity-100");const s=h.find(ca,this._indicatorsElement);for(let n=0;n{d.trigger(this._element,ui,{relatedTarget:o,direction:v,from:r,to:a})};o.setAttribute(`${f}`,""),o.classList.add(this._classes.block,O),Ut(o),n.setAttribute(`${c}`,""),n.classList.add(g,...this._classes.invisible.split(" ")),n.classList.remove(...this._classes.visible.split(" ")),o.setAttribute(`${c}`,""),o.classList.add(...this._classes.visible.split(" ")),o.classList.remove(this._classes.slideRight,this._classes.slideLeft);const D=()=>{o.removeAttribute(c),o.removeAttribute(f),o.setAttribute(`${Xt}`,""),n.removeAttribute(Xt),n.classList.remove(g,...this._classes.invisible.split(" "),this._classes.block),n.removeAttribute(f),n.removeAttribute(c),this._isSliding=!1,setTimeout(y,0)};this._queueCallback(D,n,!0),(l||this._config.ride===!0)&&this.cycle()}_directionToOrder(t){return[qt,mt].includes(t)?W()?t===mt?gt:_t:t===mt?_t:gt:t}_orderToDirection(t){return[_t,gt].includes(t)?W()?t===gt?mt:qt:t===gt?qt:mt:t}static carouselInterface(t,e){const s=q.getOrCreateInstance(t,e);let{_config:n}=s;typeof e=="object"&&(n={...n,...e});const r=typeof e=="string"?e:e.slide;if(typeof e=="number"){s.to(e);return}if(typeof r=="string"){if(typeof s[r]>"u")throw new TypeError(`No method named "${r}"`);s[r]()}else n.interval&&n.ride===!0&&s.pause()}static jQueryInterface(t){return this.each(function(){let e=q.getOrCreateInstance(this);if(typeof t=="number"){e.to(t);return}if(typeof t=="string"){if(typeof e[t]>"u")throw new TypeError(`No method named "${t}"`);e[t]()}})}static dataApiClickHandler(t){const e=st(this);if(!e)return;const s={...E.getDataAttributes(e),...E.getDataAttributes(this)},n=this.getAttribute("data-twe-slide-to");n&&(s.interval=!1),q.carouselInterface(e,s),n&&q.getInstance(e).to(n),t.preventDefault()}}const ha=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),fa=/^aria-[\w-]*$/i,pa=/^data-twe-[\w-]*$/i,_a=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,ga=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,ma=(i,t)=>{const e=i.nodeName.toLowerCase();if(t.includes(e))return ha.has(e)?!!(_a.test(i.nodeValue)||ga.test(i.nodeValue)):!0;const s=t.filter(n=>n instanceof RegExp);for(let n=0,r=s.length;n{ma(v,f)||l.removeAttribute(v.nodeName)})}return n.body.innerHTML}const Js="tooltip",J=".twe.tooltip",va="te-tooltip",ba=new Set(["sanitize","allowList","sanitizeFn"]),wa={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(array|string|function)",container:"(string|element|boolean)",fallbackPlacements:"array",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",allowList:"object",popperConfig:"(null|object|function)"},Ta={AUTO:"auto",TOP:"top",RIGHT:W()?"left":"right",BOTTOM:"bottom",LEFT:W()?"right":"left"},Aa={animation:!0,template:` + + `,trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:[0,0],container:!1,fallbackPlacements:["top","right","bottom","left"],boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:Ea,popperConfig:{hide:!0}},ya={HIDE:`hide${J}`,HIDDEN:`hidden${J}`,SHOW:`show${J}`,SHOWN:`shown${J}`,INSERTED:`inserted${J}`,CLICK:`click${J}`,FOCUSIN:`focusin${J}`,FOCUSOUT:`focusout${J}`,MOUSEENTER:`mouseenter${J}`,MOUSELEAVE:`mouseleave${J}`},Ca="fade",Oa="modal",pi="show",Gt="show",_i="out",tn="[data-twe-tooltip-inner-ref]",en=`.${Oa}`,sn="hide.twe.modal",Qt="hover",gi="focus",Da="click",Na="manual";class Et extends et{constructor(t,e){if(typeof Cs>"u")throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this._config=this._getConfig(e),this.tip=null,this._setListeners()}static get Default(){return Aa}static get NAME(){return Js}static get Event(){return ya}static get DefaultType(){return wa}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(t){if(this._isEnabled)if(t){const e=this._initializeOnDelegatedTarget(t);e._activeTrigger.click=!e._activeTrigger.click,e._isWithActiveTrigger()?e._enter(null,e):e._leave(null,e)}else{if(this.getTipElement().classList.contains(pi)){this._leave(null,this);return}this._enter(null,this)}}dispose(){clearTimeout(this._timeout),d.off(this._element.closest(en),sn,this._hideModalHandler),this.tip&&this.tip.remove(),this._disposePopper(),super.dispose()}show(){if(this._element.style.display==="none")throw new Error("Please use show on visible elements");if(!(this.isWithContent()&&this._isEnabled))return;const t=d.trigger(this._element,this.constructor.Event.SHOW),e=ki(this._element),s=e===null?this._element.ownerDocument.documentElement.contains(this._element):e.contains(this._element);if(t.defaultPrevented||!s)return;this.constructor.NAME==="tooltip"&&this.tip&&this.getTitle()!==this.tip.querySelector(tn).innerHTML&&(this._disposePopper(),this.tip.remove(),this.tip=null);const n=this.getTipElement(),r=Yn(this.constructor.NAME);n.setAttribute("id",r),this._element.setAttribute("aria-describedby",r),this._config.animation&&setTimeout(()=>{this.tip.classList.add("opacity-100"),this.tip.classList.remove("opacity-0")},100);const o=typeof this._config.placement=="function"?this._config.placement.call(this,n,this._element):this._config.placement,a=this._getAttachment(o);this._addAttachmentClass(a);const{container:l}=this._config;if(R.setData(n,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||(l.append(n),d.trigger(this._element,this.constructor.Event.INSERTED)),this._popper?this._popper.update():this._popper=Je(this._element,n,this._getPopperConfig(a)),n.getAttribute("id").includes("tooltip"))switch(o){case"bottom":n.classList.add("py-[0.4rem]");break;case"left":n.classList.add("px-[0.4rem]");break;case"right":n.classList.add("px-[0.4rem]");break;default:n.classList.add("py-[0.4rem]");break}const c=this._resolvePossibleFunction(this._config.customClass);c&&n.classList.add(...c.split(" ")),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach(g=>{d.on(g,"mouseover",se)});const f=()=>{const g=this._hoverState;this._hoverState=null,d.trigger(this._element,this.constructor.Event.SHOWN),g===_i&&this._leave(null,this)},v=this.tip.classList.contains("transition-opacity");this._queueCallback(f,this.tip,v)}hide(){if(!this._popper)return;const t=this.getTipElement(),e=()=>{this._isWithActiveTrigger()||(this._hoverState!==Gt&&t.remove(),this._cleanTipClass(),this._element.removeAttribute("aria-describedby"),d.trigger(this._element,this.constructor.Event.HIDDEN),this._disposePopper())};if(d.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented)return;t.classList.add("opacity-0"),t.classList.remove("opacity-100"),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach(r=>d.off(r,"mouseover",se)),this._activeTrigger[Da]=!1,this._activeTrigger[gi]=!1,this._activeTrigger[Qt]=!1;const n=this.tip.classList.contains("opacity-0");this._queueCallback(e,this.tip,n),this._hoverState=""}update(){this._popper!==null&&this._popper.update()}isWithContent(){return!!this.getTitle()}getTipElement(){if(this.tip)return this.tip;const t=document.createElement("div");t.innerHTML=this._config.template;const e=t.children[0];return this.setContent(e),e.classList.remove(Ca,pi),this.tip=e,this.tip}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),tn)}_sanitizeAndSetContent(t,e,s){const n=h.findOne(s,t);if(!e&&n){n.remove();return}this.setElementContent(n,e)}setElementContent(t,e){if(t!==null){if(yt(e)){e=nt(e),this._config.html?e.parentNode!==t&&(t.innerHTML="",t.append(e)):t.textContent=e.textContent;return}this._config.html?(this._config.sanitize&&(e=Zs(e,this._config.allowList,this._config.sanitizeFn)),t.innerHTML=e):t.textContent=e}}getTitle(){const t=this._element.getAttribute("data-twe-original-title")||this._config.title;return this._resolvePossibleFunction(t)}updateAttachment(t){return t==="right"?"end":t==="left"?"start":t}_initializeOnDelegatedTarget(t,e){return e||this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_getOffset(){const{offset:t}=this._config;return typeof t=="string"?t.split(",").map(e=>Number.parseInt(e,10)):typeof t=="function"?e=>t(e,this._element):t}_resolvePossibleFunction(t){return typeof t=="function"?t.call(this._element):t}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"onChange",enabled:!0,phase:"afterWrite",fn:s=>this._handlePopperPlacementChange(s)}],onFirstUpdate:s=>{s.options.placement!==s.placement&&this._handlePopperPlacementChange(s)}};return{...e,...typeof this._config.popperConfig=="function"?this._config.popperConfig(e):this._config.popperConfig}}_addAttachmentClass(t){this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(t)}`)}_getAttachment(t){return Ta[t.toUpperCase()]}_setListeners(){this._config.trigger.split(" ").forEach(e=>{if(e==="click")d.on(this._element,this.constructor.Event.CLICK,this._config.selector,s=>this.toggle(s));else if(e!==Na){const s=e===Qt?this.constructor.Event.MOUSEENTER:this.constructor.Event.FOCUSIN,n=e===Qt?this.constructor.Event.MOUSELEAVE:this.constructor.Event.FOCUSOUT;d.on(this._element,s,this._config.selector,r=>this._enter(r)),d.on(this._element,n,this._config.selector,r=>this._leave(r))}}),this._hideModalHandler=()=>{this._element&&this.hide()},d.on(this._element.closest(en),sn,this._hideModalHandler),this._config.selector?this._config={...this._config,trigger:"manual",selector:""}:this._fixTitle()}_fixTitle(){const t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-twe-original-title");(t||e!=="string")&&(this._element.setAttribute("data-twe-original-title",t||""),t&&!this._element.getAttribute("aria-label")&&!this._element.textContent&&this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))}_enter(t,e){if(e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger[t.type==="focusin"?gi:Qt]=!0),e.getTipElement().classList.contains(pi)||e._hoverState===Gt){e._hoverState=Gt;return}if(clearTimeout(e._timeout),e._hoverState=Gt,!e._config.delay||!e._config.delay.show){e.show();return}e._timeout=setTimeout(()=>{e._hoverState===Gt&&e.show()},e._config.delay.show)}_leave(t,e){if(e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger[t.type==="focusout"?gi:Qt]=e._element.contains(t.relatedTarget)),!e._isWithActiveTrigger()){if(clearTimeout(e._timeout),e._hoverState=_i,!e._config.delay||!e._config.delay.hide){e.hide();return}e._timeout=setTimeout(()=>{e._hoverState===_i&&e.hide()},e._config.delay.hide)}}_isWithActiveTrigger(){for(const t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1}_getConfig(t){const e=E.getDataAttributes(this._element);return Object.keys(e).forEach(s=>{ba.has(s)&&delete e[s]}),t={...this.constructor.Default,...e,...typeof t=="object"&&t?t:{}},t.container=t.container===!1?document.body:nt(t.container),typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),typeof t.title=="number"&&(t.title=t.title.toString()),typeof t.content=="number"&&(t.content=t.content.toString()),x(Js,t,this.constructor.DefaultType),t.sanitize&&(t.template=Zs(t.template,t.allowList,t.sanitizeFn)),t}_getDelegateConfig(){const t={};for(const e in this._config)this.constructor.Default[e]!==this._config[e]&&(t[e]=this._config[e]);return t}_cleanTipClass(){const t=this.getTipElement(),e=new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`,"g"),s=t.getAttribute("class").match(e);s!==null&&s.length>0&&s.map(n=>n.trim()).forEach(n=>t.classList.remove(n))}_getBasicClassPrefix(){return va}_handlePopperPlacementChange(t){const{state:e}=t;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null)}static jQueryInterface(t){return this.each(function(){const e=Et.getOrCreateInstance(this,t);if(typeof t=="string"){if(typeof e[t]>"u")throw new TypeError(`No method named "${t}"`);e[t]()}})}}const Sa="popover",tt=".twe.popover",La="te-popover",$a={...Et.Default,placement:"right",offset:[0,8],trigger:"click",content:"",template:` + + `},Ia={...Et.DefaultType,content:"(string|element|function)"},Ra={HIDE:`hide${tt}`,HIDDEN:`hidden${tt}`,SHOW:`show${tt}`,SHOWN:`shown${tt}`,INSERTED:`inserted${tt}`,CLICK:`click${tt}`,FOCUSIN:`focusin${tt}`,FOCUSOUT:`focusout${tt}`,MOUSEENTER:`mouseenter${tt}`,MOUSELEAVE:`mouseleave${tt}`},ka="[data-twe-popover-header-ref]",xa="[data-twe-popover-body-ref]";class ge extends Et{static get Default(){return $a}static get NAME(){return Sa}static get Event(){return Ra}static get DefaultType(){return Ia}isWithContent(){return this.getTitle()||this._getContent()}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),ka),this._sanitizeAndSetContent(t,this._getContent(),xa)}_getContent(){return this._resolvePossibleFunction(this._config.content)}_getBasicClassPrefix(){return La}static jQueryInterface(t){return this.each(function(){const e=ge.getOrCreateInstance(this,t);if(typeof t=="string"){if(typeof e[t]>"u")throw new TypeError(`No method named "${t}"`);e[t]()}})}}const mi="scrollspy",Ei=".twe.scrollspy",nn={offset:10,method:"auto",target:""},Pa={offset:"number",method:"string",target:"(string|element)"},Ma={active:"!text-primary font-semibold border-s-[0.125rem] border-solid border-primary"},Ha={active:"string"},vi=`activate${Ei}`,Va=`scroll${Ei}`,Zt="data-twe-nav-link-active",rn="data-twe-collapsible-scrollspy-ref",on="[data-twe-dropdown-item-ref]",Wa="[data-twe-nav-list-ref]",bi="[data-twe-nav-link-ref]",Ba="[data-twe-nav-item-ref]",an="[data-twe-list-group-item-ref]",wi=`${bi}, ${an}, ${on}`,ja="[data-twe-dropdown-ref]",Ua="[data-twe-dropdown-toggle-ref]",ln=`[${rn}]`,Ya=`[${Zt}]`,Ti="ul",Ka="maxOffset",cn="position";class me extends et{constructor(t,e,s){super(t),this._scrollElement=this._element.tagName==="BODY"?window:this._element,this._config=this._getConfig(e),this._classes=this._getClasses(s),this._offsets=[],this._targets=[],this._collapsibles=[],this._activeTarget=null,this._scrollHeight=0,d.on(this._scrollElement,Va,()=>this._process()),this.refresh(),this._process(),this._bindActivateEvent(),this._getCollapsibles(),this._collapsibles.length!==0&&(this._showSubsection(),this._hideSubsection())}static get Default(){return nn}static get NAME(){return mi}refresh(){const t=this._scrollElement===this._scrollElement.window?Ka:cn,e=this._config.method==="auto"?t:this._config.method,s=e===cn?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),h.find(wi,this._config.target).map(r=>{const o=Re(r),a=o?h.findOne(o):null;if(a){const l=a.getBoundingClientRect();if(l.width||l.height)return[E[e](a).top+s,o]}return null}).filter(r=>r).sort((r,o)=>r[0]-o[0]).forEach(r=>{this._offsets.push(r[0]),this._targets.push(r[1])})}dispose(){d.off(this._scrollElement,Ei),d.off(this._scrollElement,vi),super.dispose()}_getConfig(t){return t={...nn,...E.getDataAttributes(this._element),...typeof t=="object"&&t?t:{}},t.target=nt(t.target)||document.documentElement,x(mi,t,Pa),t}_getClasses(t){const e=E.getDataClassAttributes(this._element);return t={...Ma,...e,...t},x(mi,t,Ha),t}_getScrollTop(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop}_getScrollHeight(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}_getOffsetHeight(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height}_process(){const t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),s=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=s){const n=this._targets[this._targets.length-1];this._activeTarget!==n&&this._activate(n);return}if(this._activeTarget&&t0){this._activeTarget=null,this._clear();return}for(let n=this._offsets.length;n--;)this._activeTarget!==this._targets[n]&&t>=this._offsets[n]&&(typeof this._offsets[n+1]>"u"||t`${n}[data-twe-target="${t}"],${n}[href="${t}"]`),s=h.findOne(e.join(","),this._config.target);s.classList.add(...this._classes.active.split(" ")),s.setAttribute(Zt,""),s.getAttribute(on)?h.findOne(Ua,s.closest(ja)).classList.add(...this._classes.active.split(" ")):h.parents(s,Wa).forEach(n=>{h.prev(n,`${bi}, ${an}`).forEach(r=>{r.classList.add(...this._classes.active.split(" ")),r.setAttribute(Zt,"")}),h.prev(n,Ba).forEach(r=>{h.children(r,bi).forEach(o=>o.classList.add(...this._classes.active.split(" ")))})}),d.trigger(this._scrollElement,vi,{relatedTarget:t})}_clear(){h.find(wi,this._config.target).filter(t=>t.classList.contains(...this._classes.active.split(" "))).forEach(t=>{t.classList.remove(...this._classes.active.split(" ")),t.removeAttribute(Zt)})}_hide(t){const e=h.findOne(Ti,t.parentNode);e.style.overflow="hidden",e.style.height="0px"}_show(t,e){t.style.height=e}_getCollapsibles(){const t=h.find(ln);t&&t.forEach(e=>{const s=e.parentNode,n=h.findOne(Ti,s),r=n.offsetHeight||n.scrollHeight;this._collapsibles.push({element:n,relatedTarget:e.getAttribute("href"),height:`${r}px`})})}_showSubsection(){h.find(Ya).filter(s=>s.hasAttribute(rn)).forEach(s=>{const n=h.findOne(Ti,s.parentNode),r=this._collapsibles.find(o=>o.relatedTarget=s.getAttribute("href")).height;this._show(n,r)})}_hideSubsection(){h.find(ln).filter(e=>e.hasAttribute(Zt)===!1).forEach(e=>{this._hide(e)})}_bindActivateEvent(){d.on(this._element,vi,()=>{this._showSubsection(),this._hideSubsection()})}static jQueryInterface(t){return this.each(function(){const e=me.getOrCreateInstance(this,t);if(typeof t=="string"){if(typeof e[t]>"u")throw new TypeError(`No method named "${t}"`);e[t]()}})}}const dn="tab",Ee=".twe.tab",Fa=`hide${Ee}`,za=`hidden${Ee}`,qa=`show${Ee}`,Xa=`shown${Ee}`,Ga="data-twe-dropdown-menu-ref",Ht="data-twe-tab-active",ve="data-twe-nav-active",Qa="[data-twe-dropdown-ref]",Za="[data-twe-nav-ref]",un=`[${Ht}]`,Ja=`[${ve}]`,hn=":scope > li > .active",tl="[data-twe-dropdown-toggle-ref]",el=":scope > [data-twe-dropdown-menu-ref] [data-twe-dropdown-show]",il={show:"opacity-100",hide:"opacity-0"},sl={show:"string",hide:"string"};class be extends et{constructor(t,e){super(t),this._classes=this._getClasses(e)}static get NAME(){return dn}show(){if(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&this._element.getAttribute(ve)==="")return;let t;const e=st(this._element),s=this._element.closest(Za),n=h.findOne(Ja,s);if(s){const l=s.nodeName==="UL"||s.nodeName==="OL"?hn:un;t=h.find(l,s),t=t[t.length-1]}const r=t?d.trigger(t,Fa,{relatedTarget:this._element}):null;if(d.trigger(this._element,qa,{relatedTarget:t}).defaultPrevented||r!==null&&r.defaultPrevented)return;this._activate(this._element,s,null,n,this._element);const a=()=>{d.trigger(t,za,{relatedTarget:this._element}),d.trigger(this._element,Xa,{relatedTarget:t})};e?this._activate(e,e.parentNode,a,n,this._element):a()}_getClasses(t){const e=E.getDataClassAttributes(this._element);return t={...il,...e,...t},x(dn,t,sl),t}_activate(t,e,s,n,r){const a=(e&&(e.nodeName==="UL"||e.nodeName==="OL")?h.find(hn,e):h.children(e,un))[0],l=s&&a&&a.hasAttribute(Ht),u=()=>this._transitionComplete(t,a,s,n,r);a&&l?(E.removeClass(a,this._classes.show),E.addClass(a,this._classes.hide),this._queueCallback(u,t,!0)):u()}_transitionComplete(t,e,s,n,r){if(e&&n){e.removeAttribute(Ht),n.removeAttribute(ve);const a=h.findOne(el,e.parentNode);a&&a.removeAttribute(Ht),e.getAttribute("role")==="tab"&&e.setAttribute("aria-selected",!1)}t.setAttribute(Ht,""),r.setAttribute(ve,""),t.getAttribute("role")==="tab"&&t.setAttribute("aria-selected",!0),Ut(t),t.classList.contains(this._classes.hide)&&(E.removeClass(t,this._classes.hide),E.addClass(t,this._classes.show));let o=t.parentNode;if(o&&o.nodeName==="LI"&&(o=o.parentNode),o&&o.hasAttribute(Ga)){const a=t.closest(Qa);a&&h.find(tl,a).forEach(l=>l.setAttribute(Ht,"")),t.setAttribute("aria-expanded",!0)}s&&s()}static jQueryInterface(t){return this.each(function(){const e=be.getOrCreateInstance(this);if(typeof t=="string"){if(typeof e[t]>"u")throw new TypeError(`No method named "${t}"`);e[t]()}})}}(()=>{var i={454:(s,n,r)=>{r.d(n,{Z:()=>l});var o=r(645),a=r.n(o)()(function(u){return u[1]});a.push([s.id,"INPUT:-webkit-autofill,SELECT:-webkit-autofill,TEXTAREA:-webkit-autofill{animation-name:onautofillstart}INPUT:not(:-webkit-autofill),SELECT:not(:-webkit-autofill),TEXTAREA:not(:-webkit-autofill){animation-name:onautofillcancel}@keyframes onautofillstart{}@keyframes onautofillcancel{}",""]);const l=a},645:s=>{s.exports=function(n){var r=[];return r.toString=function(){return this.map(function(o){var a=n(o);return o[2]?"@media ".concat(o[2]," {").concat(a,"}"):a}).join("")},r.i=function(o,a,l){typeof o=="string"&&(o=[[null,o,""]]);var u={};if(l)for(var c=0;c{(function(){if(typeof window<"u")try{var s=new window.CustomEvent("test",{cancelable:!0});if(s.preventDefault(),s.defaultPrevented!==!0)throw new Error("Could not prevent default")}catch{var n=function(o,a){var l,u;return(a=a||{}).bubbles=!!a.bubbles,a.cancelable=!!a.cancelable,(l=document.createEvent("CustomEvent")).initCustomEvent(o,a.bubbles,a.cancelable,a.detail),u=l.preventDefault,l.preventDefault=function(){u.call(this);try{Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}})}catch{this.defaultPrevented=!0}},l};n.prototype=window.Event.prototype,window.CustomEvent=n}})()},379:(s,n,r)=>{var o,a=function(){var m={};return function(p){if(m[p]===void 0){var _=document.querySelector(p);if(window.HTMLIFrameElement&&_ instanceof window.HTMLIFrameElement)try{_=_.contentDocument.head}catch{_=null}m[p]=_}return m[p]}}(),l=[];function u(m){for(var p=-1,_=0;_{var n=s&&s.__esModule?()=>s.default:()=>s;return e.d(n,{a:n}),n},e.d=(s,n)=>{for(var r in n)e.o(n,r)&&!e.o(s,r)&&Object.defineProperty(s,r,{enumerable:!0,get:n[r]})},e.o=(s,n)=>Object.prototype.hasOwnProperty.call(s,n),(()=>{var s=e(379),n=e.n(s),r=e(454);function o(l){if(!l.hasAttribute("autocompleted")){l.setAttribute("autocompleted","");var u=new window.CustomEvent("onautocomplete",{bubbles:!0,cancelable:!0,detail:null});l.dispatchEvent(u)||(l.value="")}}function a(l){l.hasAttribute("autocompleted")&&(l.removeAttribute("autocompleted"),l.dispatchEvent(new window.CustomEvent("onautocomplete",{bubbles:!0,cancelable:!1,detail:null})))}n()(r.Z,{insert:"head",singleton:!1}),r.Z.locals,e(810),document.addEventListener("animationstart",function(l){l.animationName==="onautofillstart"?o(l.target):a(l.target)},!0),document.addEventListener("input",function(l){l.inputType!=="insertReplacementText"&&"data"in l?a(l.target):o(l.target)},!0)})()})();const Ai="input",we="twe.input",fn="data-twe-input-wrapper-init",pn="data-twe-input-notch-ref",_n="data-twe-input-notch-leading-ref",gn="data-twe-input-notch-middle-ref",nl="data-twe-input-notch-trailing-ref",rl="data-twe-input-helper-ref",ol="data-twe-input-placeholder-active",at="data-twe-input-state-active",mn="data-twe-input-focused",En="data-twe-input-form-counter",Te=`[${fn}] input`,Ae=`[${fn}] textarea`,Vt=`[${pn}]`,vn=`[${_n}]`,bn=`[${gn}]`,al=`[${rl}]`,ll={inputFormWhite:!1},cl={inputFormWhite:"(boolean)"},dl={notch:"group flex absolute left-0 top-0 w-full max-w-full h-full text-left pointer-events-none",notchLeading:"pointer-events-none border border-solid box-border bg-transparent transition-all duration-200 ease-linear motion-reduce:transition-none left-0 top-0 h-full w-2 border-e-0 rounded-s-[0.25rem] group-data-[twe-input-focused]:border-e-0 group-data-[twe-input-state-active]:border-e-0",notchLeadingNormal:"border-secondary-500 dark:border-neutral-400 group-data-[twe-input-focused]:shadow-notch-1 group-data-[twe-input-focused]:border-primary",notchLeadingWhite:"border-neutral-200 group-data-[twe-input-focused]:shadow-notch-1 group-data-[twe-input-focused]:shadow-white group-data-[twe-input-focused]:border-white",notchMiddle:"pointer-events-none border border-solid box-border bg-transparent transition-all duration-200 ease-linear motion-reduce:transition-none grow-0 shrink-0 basis-auto w-auto max-w-[calc(100%-1rem)] h-full border-e-0 border-s-0 group-data-[twe-input-focused]:border-x-0 group-data-[twe-input-state-active]:border-x-0 group-data-[twe-input-focused]:border-t group-data-[twe-input-state-active]:border-t group-data-[twe-input-focused]:border-solid group-data-[twe-input-state-active]:border-solid group-data-[twe-input-focused]:border-t-transparent group-data-[twe-input-state-active]:border-t-transparent",notchMiddleNormal:"border-secondary-500 dark:border-neutral-400 group-data-[twe-input-focused]:shadow-notch-2 group-data-[twe-input-focused]:border-primary",notchMiddleWhite:"border-neutral-200 group-data-[twe-input-focused]:shadow-notch-2 group-data-[twe-input-focused]:shadow-white group-data-[twe-input-focused]:border-white",notchTrailing:"pointer-events-none border border-solid box-border bg-transparent transition-all duration-200 ease-linear motion-reduce:transition-none grow h-full border-s-0 rounded-e-[0.25rem] group-data-[twe-input-focused]:border-s-0 group-data-[twe-input-state-active]:border-s-0",notchTrailingNormal:"border-secondary-500 dark:border-neutral-400 group-data-[twe-input-focused]:shadow-notch-3 group-data-[twe-input-focused]:border-primary",notchTrailingWhite:"border-neutral-200 group-data-[twe-input-focused]:shadow-notch-3 group-data-[twe-input-focused]:shadow-white group-data-[twe-input-focused]:border-white",counter:"text-right leading-[1.6]"},ul={notch:"string",notchLeading:"string",notchLeadingNormal:"string",notchLeadingWhite:"string",notchMiddle:"string",notchMiddleNormal:"string",notchMiddleWhite:"string",notchTrailing:"string",notchTrailingNormal:"string",notchTrailingWhite:"string",counter:"string"};class S{constructor(t,e,s){this._config=this._getConfig(e,t),this._element=t,this._classes=this._getClasses(s),this._label=null,this._labelWidth=0,this._labelMarginLeft=0,this._notchLeading=null,this._notchMiddle=null,this._notchTrailing=null,this._initiated=!1,this._helper=null,this._counter=!1,this._counterElement=null,this._maxLength=0,this._leadingIcon=null,this._element&&(R.setData(t,we,this),this.init())}static get NAME(){return Ai}get input(){return h.findOne("input",this._element)||h.findOne("textarea",this._element)}init(){this._initiated||(this._getLabelData(),this._applyDivs(),this._applyNotch(),this._activate(),this._getHelper(),this._getCounter(),this._getEvents(),this._initiated=!0)}update(){this._getLabelData(),this._getNotchData(),this._applyNotch(),this._activate(),this._getHelper(),this._getCounter()}forceActive(){this.input.setAttribute(at,""),h.findOne(Vt,this.input.parentNode).setAttribute(at,"")}forceInactive(){this.input.removeAttribute(at),h.findOne(Vt,this.input.parentNode).removeAttribute(at)}dispose(){this._removeBorder(),R.removeData(this._element,we),this._element=null}_getConfig(t,e){return t={...ll,...E.getDataAttributes(e),...typeof t=="object"?t:{}},x(Ai,t,cl),t}_getClasses(t){const e=E.getDataClassAttributes(this._element);return t={...dl,...e,...t},x(Ai,t,ul),t}_getLabelData(){this._label=h.findOne("label",this._element),this._label===null?this._showPlaceholder():(this._getLabelWidth(),this._getLabelPositionInInputGroup(),this._toggleDefaultDatePlaceholder())}_getHelper(){this._helper=h.findOne(al,this._element)}_getCounter(){this._counter=E.getDataAttribute(this.input,"inputShowcounter"),this._counter&&(this._maxLength=this.input.maxLength,this._showCounter())}_getEvents(){d.on(this._element,"focus","input",S.activate(new S)),d.on(this._element,"input","input",S.activate(new S)),d.on(this._element,"blur","input",S.deactivate(new S)),d.on(this._element,"focus","textarea",S.activate(new S)),d.on(this._element,"input","textarea",S.activate(new S)),d.on(this._element,"blur","textarea",S.deactivate(new S)),d.on(window,"shown.twe.modal",t=>{h.find(Te,t.target).forEach(e=>{const s=S.getInstance(e.parentNode);s&&s.update()}),h.find(Ae,t.target).forEach(e=>{const s=S.getInstance(e.parentNode);s&&s.update()})}),d.on(window,"shown.twe.dropdown",t=>{const e=t.target.parentNode.querySelector("[data-twe-dropdown-menu-ref]");e&&(h.find(Te,e).forEach(s=>{const n=S.getInstance(s.parentNode);n&&n.update()}),h.find(Ae,e).forEach(s=>{const n=S.getInstance(s.parentNode);n&&n.update()}))}),d.on(window,"shown.twe.tab",t=>{let e;t.target.href?e=t.target.href.split("#")[1]:e=E.getDataAttribute(t.target,"target").split("#")[1];const s=h.findOne(`#${e}`);h.find(Te,s).forEach(n=>{const r=S.getInstance(n.parentNode);r&&r.update()}),h.find(Ae,s).forEach(n=>{const r=S.getInstance(n.parentNode);r&&r.update()})}),d.on(window,"reset",t=>{h.find(Te,t.target).forEach(e=>{const s=S.getInstance(e.parentNode);s&&s.forceInactive()}),h.find(Ae,t.target).forEach(e=>{const s=S.getInstance(e.parentNode);s&&s.forceInactive()})}),d.on(window,"onautocomplete",t=>{const e=S.getInstance(t.target.parentNode);!e||!t.cancelable||e.forceActive()})}_showCounter(){if(h.find(`[${En}]`,this._element).length>0)return;this._counterElement=document.createElement("div"),E.addClass(this._counterElement,this._classes.counter),this._counterElement.setAttribute(En,"");const e=this.input.value.length;this._counterElement.innerHTML=`${e} / ${this._maxLength}`,this._helper.appendChild(this._counterElement),this._bindCounter()}_bindCounter(){d.on(this.input,"input",()=>{const t=this.input.value.length;this._counterElement.innerHTML=`${t} / ${this._maxLength}`})}_toggleDefaultDatePlaceholder(t=this.input){if(!(t.getAttribute("type")==="date"))return;!(document.activeElement===t)&&!t.value?t.style.opacity=0:t.style.opacity=1}_showPlaceholder(){this.input.setAttribute(ol,"")}_getNotchData(){this._notchMiddle=h.findOne(bn,this._element),this._notchLeading=h.findOne(vn,this._element)}_getLabelWidth(){this._labelWidth=this._label.clientWidth*.8+8}_getLabelPositionInInputGroup(){if(this._labelMarginLeft=0,!this._element.hasAttribute("data-twe-input-group-ref"))return;const t=this.input,e=h.prev(t,"[data-twe-input-group-text-ref]")[0];e===void 0?this._labelMarginLeft=0:this._labelMarginLeft=e.offsetWidth-1}_applyDivs(){const t=this._config.inputFormWhite?this._classes.notchLeadingWhite:this._classes.notchLeadingNormal,e=this._config.inputFormWhite?this._classes.notchMiddleWhite:this._classes.notchMiddleNormal,s=this._config.inputFormWhite?this._classes.notchTrailingWhite:this._classes.notchTrailingNormal,n=h.find(Vt,this._element),r=Yt("div");E.addClass(r,this._classes.notch),r.setAttribute(pn,""),this._notchLeading=Yt("div"),E.addClass(this._notchLeading,`${this._classes.notchLeading} ${t}`),this._notchLeading.setAttribute(_n,""),this._notchMiddle=Yt("div"),E.addClass(this._notchMiddle,`${this._classes.notchMiddle} ${e}`),this._notchMiddle.setAttribute(gn,""),this._notchTrailing=Yt("div"),E.addClass(this._notchTrailing,`${this._classes.notchTrailing} ${s}`),this._notchTrailing.setAttribute(nl,""),!(n.length>=1)&&(r.append(this._notchLeading),r.append(this._notchMiddle),r.append(this._notchTrailing),this._element.append(r),r.dir="ltr")}_applyNotch(){this._notchMiddle.style.width=`${this._labelWidth}px`,this._notchLeading.style.width=`${this._labelMarginLeft+9}px`,this._label!==null&&(this._label.style.marginLeft=`${this._labelMarginLeft}px`)}_removeBorder(){const t=h.findOne(Vt,this._element);t&&t.remove()}_activate(t){Pi(()=>{this._getElements(t);const e=t?t.target:this.input,s=h.findOne(Vt,this._element);t&&t.type==="focus"&&s&&s.setAttribute(mn,""),e.value!==""&&(e.setAttribute(at,""),s&&s.setAttribute(at,"")),this._toggleDefaultDatePlaceholder(e)})}_getElements(t){if(t&&(this._element=t.target.parentNode,this._label=h.findOne("label",this._element)),t&&this._label){const e=this._labelWidth;this._getLabelData(),e!==this._labelWidth&&(this._notchMiddle=h.findOne(bn,t.target.parentNode),this._notchLeading=h.findOne(vn,t.target.parentNode),this._applyNotch())}}_deactivate(t){const e=t?t.target:this.input;if(e.getAttribute("aria-expanded")==="true")return;const s=h.findOne(Vt,e.parentNode);s.removeAttribute(mn),e.value===""&&(e.removeAttribute(at),s.removeAttribute(at)),this._toggleDefaultDatePlaceholder(e)}static activate(t){return function(e){t._activate(e)}}static deactivate(t){return function(e){t._deactivate(e)}}static jQueryInterface(t,e){return this.each(function(){let s=R.getData(this,we);const n=typeof t=="object"&&t;if(!(!s&&/dispose/.test(t))&&(s||(s=new S(this,n)),typeof t=="string")){if(typeof s[t]>"u")throw new TypeError(`No method named "${t}"`);s[t](e)}})}static getInstance(t){return R.getData(t,we)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,typeof e=="object"?e:null)}}const hl={property:"color",defaultValue:null,inherit:!0},Wt=(i,t)=>{const{property:e,defaultValue:s,inherit:n}={...hl,...t},r=document.createElement("div");r.classList.add(i),document.body.appendChild(r);const a=window.getComputedStyle(r)[e]||s,u=window.getComputedStyle(r.parentElement)[e];return document.body.removeChild(r),!n&&u&&a===u?s:a||s},yi="ripple",ye="twe.ripple",fl="rgba({{color}}, 0.2) 0, rgba({{color}}, 0.3) 40%, rgba({{color}}, 0.4) 50%, rgba({{color}}, 0.5) 60%, rgba({{color}}, 0) 70%",pl=["[data-twe-ripple-init]"],Ce=[0,0,0],_l=[{name:"primary",gradientColor:Wt("text-primary",{defaultValue:"#3B71CA",inherit:!1})},{name:"secondary",gradientColor:Wt("text-primary-100",{defaultValue:"#9FA6B2",inherit:!1})},{name:"success",gradientColor:Wt("text-success",{defaultValue:"#14A44D",inherit:!1})},{name:"danger",gradientColor:Wt("text-danger",{defaultValue:"#DC4C64",inherit:!1})},{name:"warning",gradientColor:Wt("text-warning",{defaultValue:"#E4A11B",inherit:!1})},{name:"info",gradientColor:Wt("text-info",{defaultValue:"#54B4D3",inherit:!1})},{name:"light",gradientColor:"#fbfbfb"},{name:"dark",gradientColor:"#262626"}],wn=.5,gl={rippleCentered:!1,rippleColor:"",rippleColorDark:"",rippleDuration:"500ms",rippleRadius:0,rippleUnbound:!1},ml={rippleCentered:"boolean",rippleColor:"string",rippleColorDark:"string",rippleDuration:"string",rippleRadius:"number",rippleUnbound:"boolean"},El={ripple:"relative overflow-hidden inline-block align-bottom",rippleWave:"rounded-[50%] opacity-50 pointer-events-none absolute touch-none scale-0 transition-[transform,_opacity] ease-[cubic-bezier(0,0,0.15,1),_cubic-bezier(0,0,0.15,1)] z-[999]",unbound:"overflow-visible"},vl={ripple:"string",rippleWave:"string",unbound:"string"};class Oe{constructor(t,e,s){this._element=t,this._options=this._getConfig(e),this._classes=this._getClasses(s),this._currentColor=this._options.rippleColor,this._element&&(R.setData(t,ye,this),E.addClass(this._element,this._classes.ripple)),this._clickHandler=this._createRipple.bind(this),this._rippleTimer=null,this._isMinWidthSet=!1,this._initialClasses=null,this.init()}static get NAME(){return yi}init(){this._addClickEvent(this._element)}dispose(){R.removeData(this._element,ye),d.off(this._element,"mousedown",this._clickHandler),this._element=null,this._options=null}_autoInit(t){pl.forEach(e=>{h.closest(t.target,e)&&(this._element=h.closest(t.target,e))}),this._element.style.minWidth||(E.style(this._element,{"min-width":getComputedStyle(this._element).width}),this._isMinWidthSet=!0),this._options=this._getConfig(),this._classes=this._getClasses(),this._initialClasses=[...this._element.classList],E.addClass(this._element,this._classes.ripple),this._createRipple(t)}_addClickEvent(t){d.on(t,"mousedown",this._clickHandler)}_createRipple(t){this._element.className.indexOf(this._classes.ripple)<0&&E.addClass(this._element,this._classes.ripple);const{layerX:e,layerY:s}=t,n=t.offsetX||e,r=t.offsetY||s,o=this._element.offsetHeight,a=this._element.offsetWidth,l=this._durationToMsNumber(this._options.rippleDuration),u={offsetX:this._options.rippleCentered?o/2:n,offsetY:this._options.rippleCentered?a/2:r,height:o,width:a},c=this._getDiameter(u),f=this._options.rippleRadius||c/2,v={delay:l*wn,duration:l-l*wn},g={left:this._options.rippleCentered?`${a/2-f}px`:`${n-f}px`,top:this._options.rippleCentered?`${o/2-f}px`:`${r-f}px`,height:`${this._options.rippleRadius*2||c}px`,width:`${this._options.rippleRadius*2||c}px`,transitionDelay:`0s, ${v.delay}ms`,transitionDuration:`${l}ms, ${v.duration}ms`},O=Yt("div");this._createHTMLRipple({wrapper:this._element,ripple:O,styles:g}),this._removeHTMLRipple({ripple:O,duration:l})}_createHTMLRipple({wrapper:t,ripple:e,styles:s}){Object.keys(s).forEach(n=>e.style[n]=s[n]),E.addClass(e,this._classes.rippleWave),e.setAttribute("data-twe-ripple-ref",""),this._addColor(e,t),this._toggleUnbound(t),this._appendRipple(e,t)}_removeHTMLRipple({ripple:t,duration:e}){this._rippleTimer&&(clearTimeout(this._rippleTimer),this._rippleTimer=null),t&&setTimeout(()=>{t.classList.add("!opacity-0")},10),this._rippleTimer=setTimeout(()=>{if(t&&(t.remove(),this._element)){h.find("[data-twe-ripple-ref]",this._element).forEach(n=>{n.remove()}),this._isMinWidthSet&&(E.style(this._element,{"min-width":""}),this._isMinWidthSet=!1);const s=this._initialClasses?this._addedNewRippleClasses(this._classes.ripple,this._initialClasses):this._classes.ripple.split(" ");E.removeClass(this._element,s)}},e)}_addedNewRippleClasses(t,e){return t.split(" ").filter(s=>e.findIndex(n=>s===n)===-1)}_durationToMsNumber(t){return Number(t.replace("ms","").replace("s","000"))}_getConfig(t={}){const e=E.getDataAttributes(this._element);return t={...gl,...e,...t},x(yi,t,ml),t}_getClasses(t={}){const e=E.getDataClassAttributes(this._element);return t={...El,...e,...t},x(yi,t,vl),t}_getDiameter({offsetX:t,offsetY:e,height:s,width:n}){const r=e<=s/2,o=t<=n/2,a=(v,g)=>Math.sqrt(v**2+g**2),l=e===s/2&&t===n/2,u={first:r===!0&&o===!1,second:r===!0&&o===!0,third:r===!1&&o===!0,fourth:r===!1&&o===!1},c={topLeft:a(t,e),topRight:a(n-t,e),bottomLeft:a(t,s-e),bottomRight:a(n-t,s-e)};let f=0;return l||u.fourth?f=c.topLeft:u.third?f=c.topRight:u.second?f=c.bottomRight:u.first&&(f=c.bottomLeft),f*2}_appendRipple(t,e){e.appendChild(t),setTimeout(()=>{E.addClass(t,"opacity-0 scale-100")},50)}_toggleUnbound(t){this._options.rippleUnbound===!0?E.addClass(t,this._classes.unbound):E.removeClass(t,this._classes.unbound)}_addColor(t){let e=this._options.rippleColor||"rgb(0,0,0)";(localStorage.theme==="dark"||!("theme"in localStorage)&&window.matchMedia("(prefers-color-scheme: dark)").matches)&&(e=this._options.rippleColorDark||this._options.rippleColor);const s=_l.find(o=>o.name===e.toLowerCase()),n=s?this._colorToRGB(s.gradientColor).join(","):this._colorToRGB(e).join(","),r=fl.split("{{color}}").join(`${n}`);t.style.backgroundImage=`radial-gradient(circle, ${r})`}_colorToRGB(t){function e(r){return r.length<7&&(r=`#${r[1]}${r[1]}${r[2]}${r[2]}${r[3]}${r[3]}`),[parseInt(r.substr(1,2),16),parseInt(r.substr(3,2),16),parseInt(r.substr(5,2),16)]}function s(r){const o=document.body.appendChild(document.createElement("fictum")),a="rgb(1, 2, 3)";return o.style.color=a,o.style.color!==a||(o.style.color=r,o.style.color===a||o.style.color==="")?Ce:(r=getComputedStyle(o).color,document.body.removeChild(o),r)}function n(r){return r=r.match(/[.\d]+/g).map(o=>+Number(o)),r.length=3,r}return t.toLowerCase()==="transparent"?Ce:t[0]==="#"?e(t):(t.indexOf("rgb")===-1&&(t=s(t)),t.indexOf("rgb")===0?n(t):Ce)}static autoInitial(t){return function(e){t._autoInit(e)}}static jQueryInterface(t){return this.each(function(){return R.getData(this,ye)?null:new Oe(this,t)})}static getInstance(t){return R.getData(t,ye)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,typeof e=="object"?e:null)}}const Ci="modal",X=".twe.modal",Tn="Escape",An={backdrop:!0,keyboard:!0,focus:!0},yn={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean"},bl={show:"transform-none",static:"scale-[1.02]",staticProperties:"transition-scale duration-300 ease-in-out",backdrop:"opacity-50 transition-all duration-300 ease-in-out fixed top-0 left-0 z-[1040] bg-black w-screen h-screen"},wl={show:"string",static:"string",staticProperties:"string",backdrop:"string"},Tl=`hide${X}`,Al=`hidePrevented${X}`,yl=`hidden${X}`,Cl=`show${X}`,Ol=`shown${X}`,Cn=`resize${X}`,On=`click.dismiss${X}`,Dn=`keydown.dismiss${X}`,Dl=`mouseup.dismiss${X}`,Nn=`mousedown.dismiss${X}`,Sn="data-twe-modal-open",Ln="data-twe-open",Jt="[data-twe-modal-dialog-ref]",Nl="[data-twe-modal-body-ref]";class te extends et{constructor(t,e,s){super(t),this._config=this._getConfig(e),this._classes=this._getClasses(s),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._scrollBar=new ci,this._dialog=h.findOne(Jt,this._element),this._isShown=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._didInit=!1,this._init()}static get NAME(){return Ci}static get Default(){return An}static get getDefaultType(){return yn}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||d.trigger(this._element,Cl,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isAnimated()&&(this._isTransitioning=!0),this._scrollBar.hide(),document.body.setAttribute(Sn,"true"),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),d.on(this._dialog,Nn,()=>{d.one(this._element,Dl,s=>{s.target===this._element&&(this._ignoreBackdropClick=!0)})}),this._showElement(t),this._showBackdrop())}hide(){if(!this._isShown||this._isTransitioning||d.trigger(this._element,Tl).defaultPrevented)return;this._isShown=!1;const e=this._isAnimated();e&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),this._focustrap.disable(),h.findOne(Jt,this._element).classList.remove(this._classes.show),d.off(this._element,On),d.off(this._dialog,Nn),this._queueCallback(()=>this._hideModal(),this._element,e),this._element.removeAttribute(Ln)}dispose(){[window,document,this._dialog].forEach(t=>d.off(t,X)),this._backdrop.dispose(),this._focustrap.disable(),super.dispose()}handleUpdate(){this._adjustDialog()}_init(){this._didInit||(js(te),this._didInit=!0)}_initializeBackDrop(){return new Vs({isVisible:!!this._config.backdrop,isAnimated:this._isAnimated(),backdropClasses:this._classes.backdrop})}_initializeFocusTrap(){return new Ws(this._element,{event:"keydown",condition:t=>t.key==="Tab"})}_showElement(t){const e=this._isAnimated(),s=h.findOne(Nl,this._dialog);(!this._element.parentNode||this._element.parentNode.nodeType!==Node.ELEMENT_NODE)&&document.body.append(this._element),this._element.style.display="block",this._element.classList.remove("hidden"),this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.setAttribute(`${Ln}`,"true"),this._element.scrollTop=0;const n=h.findOne(Jt,this._element);n.classList.add(this._classes.show),n.classList.remove("opacity-0"),n.classList.add("opacity-100"),s&&(s.scrollTop=0),e&&Ut(this._element);const r=()=>{this._config.focus&&this._focustrap.trap(),this._isTransitioning=!1,d.trigger(this._element,Ol,{relatedTarget:t})};this._queueCallback(r,this._dialog,e)}_setEscapeEvent(){this._isShown?d.on(document,Dn,t=>{this._config.keyboard&&t.key===Tn?(t.preventDefault(),this.hide()):!this._config.keyboard&&t.key===Tn&&this._triggerBackdropTransition()}):d.off(this._element,Dn)}_setResizeEvent(){this._isShown?d.on(window,Cn,()=>this._adjustDialog()):d.off(window,Cn)}_hideModal(){const t=h.findOne(Jt,this._element);t.classList.remove(this._classes.show),t.classList.remove("opacity-100"),t.classList.add("opacity-0");const e=ke(t);setTimeout(()=>{this._element.style.display="none"},e),this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.removeAttribute(Sn),this._resetAdjustments(),this._scrollBar.reset(),d.trigger(this._element,yl)})}_showBackdrop(t){d.on(this._element,On,e=>{if(this._ignoreBackdropClick){this._ignoreBackdropClick=!1;return}e.target===e.currentTarget&&(this._config.backdrop===!0?this.hide():this._config.backdrop==="static"&&this._triggerBackdropTransition())}),this._backdrop.show(t)}_isAnimated(){return!!h.findOne(Jt,this._element)}_triggerBackdropTransition(){if(d.trigger(this._element,Al).defaultPrevented)return;const{classList:e,scrollHeight:s,style:n}=this._element,r=s>document.documentElement.clientHeight;if(!r&&n.overflowY==="hidden"||e.contains(this._classes.static))return;r||(n.overflowY="hidden"),e.add(...this._classes.static.split(" ")),e.add(...this._classes.staticProperties.split(" "));const o=ke(this._element);this._queueCallback(()=>{e.remove(this._classes.static),setTimeout(()=>{e.remove(...this._classes.staticProperties.split(" "))},o),r||this._queueCallback(()=>{n.overflowY=""},this._dialog)},this._dialog),this._element.focus()}_getConfig(t){return t={...An,...E.getDataAttributes(this._element),...typeof t=="object"?t:{}},x(Ci,t,yn),t}_getClasses(t){const e=E.getDataClassAttributes(this._element);return t={...bl,...e,...t},x(Ci,t,wl),t}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),s=e>0;(!s&&t&&!W()||s&&!t&&W())&&(this._element.style.paddingLeft=`${e}px`),(s&&!t&&!W()||!s&&t&&W())&&(this._element.style.paddingRight=`${e}px`)}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each(function(){const s=te.getOrCreateInstance(this,t);if(typeof t=="string"){if(typeof s[t]>"u")throw new TypeError(`No method named "${t}"`);s[t](e)}})}}const Sl={carousel:{name:"Carousel",selector:"[data-twe-carousel-init]",isToggler:!1},input:{name:"Input",selector:"[data-twe-input-wrapper-init]",isToggler:!1},scrollspy:{name:"ScrollSpy",selector:"[data-twe-spy='scroll']",isToggler:!1},button:{name:"Button",selector:"[data-twe-toggle='button']",isToggler:!0,callback:(i,t)=>{d.on(document,`click.twe.${i.NAME}`,t,e=>{e.preventDefault();const s=e.target.closest(t);i.getOrCreateInstance(s).toggle()})}},collapse:{name:"Collapse",selector:"[data-twe-collapse-init]",isToggler:!0,callback:(i,t)=>{d.on(document,`click.twe.${i.NAME}.data-api`,t,function(e){(e.target.tagName==="A"||e.delegateTarget&&e.delegateTarget.tagName==="A")&&e.preventDefault();const s=Re(this);h.find(s).forEach(r=>{i.getOrCreateInstance(r,{toggle:!1}).toggle()})})}},dropdown:{name:"Dropdown",selector:"[data-twe-dropdown-toggle-ref]",isToggler:!0,callback:(i,t)=>{d.on(document,`click.twe.${i.NAME}`,t,function(e){e.preventDefault(),i.getOrCreateInstance(this).toggle()})}},ripple:{name:"Ripple",selector:"[data-twe-ripple-init]",isToggler:!0,callback:(i,t)=>{d.one(document,"mousedown",t,i.autoInitial(new i))}},offcanvas:{name:"Offcanvas",selector:"[data-twe-offcanvas-toggle]",isToggler:!0,callback:(i,t)=>{d.on(document,`click.twe.${i.NAME}.data-api`,t,function(e){const s=st(this);if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),ct(this))return;d.one(s,i.EVENT_HIDDEN,()=>{Ct(this)&&this.focus()});const n=h.findOne(i.OPEN_SELECTOR);n&&n!==s&&i.getInstance(n).hide(),i.getOrCreateInstance(s).toggle(this)})}},tab:{name:"Tab",selector:"[data-twe-toggle='tab'], [data-twe-toggle='pill'], [data-twe-toggle='list']",isToggler:!0,callback:(i,t)=>{d.on(document,`click.twe.${i.NAME}.data-api`,t,function(e){if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),ct(this))return;i.getOrCreateInstance(this).show()})}},tooltip:{name:"Tooltip",selector:"[data-twe-toggle='tooltip']",isToggler:!1,callback:(i,t)=>{[].slice.call(document.querySelectorAll(t)).map(function(s){return new i(s)})}},popover:{name:"Popover",selector:"[data-twe-toggle='popover']",isToggler:!0,callback:(i,t)=>{[].slice.call(document.querySelectorAll(t)).map(function(s){return new i(s)})}},modal:{name:"Modal",selector:"[data-twe-toggle='modal']",isToggler:!0,callback:(i,t)=>{d.on(document,`click.twe.${i.NAME}`,t,function(e){const s=st(this);["A","AREA"].includes(this.tagName)&&e.preventDefault(),d.one(s,i.EVENT_SHOW,o=>{o.defaultPrevented||d.one(s,i.EVENT_HIDDEN,()=>{Ct(this)&&this.focus()})});const n=h.findOne(`[${i.OPEN_SELECTOR}="true"]`);n&&i.getInstance(n).hide(),i.getOrCreateInstance(s).toggle(this)})}}},Ll=i=>{Pi(()=>{const t=xi();if(t){const e=i.NAME,s=t.fn[e];t.fn[e]=i.jQueryInterface,t.fn[e].Constructor=i,t.fn[e].noConflict=()=>(t.fn[e]=s,i.jQueryInterface)}})};class $l{constructor(){this.inits=[]}get initialized(){return this.inits}isInited(t){return this.inits.includes(t)}add(t){this.isInited(t)||this.inits.push(t)}}const Oi=new $l;let Bt;const Il=i=>Bt[i.NAME]||null,Rl=(i,t)=>{if(!i||!t.allowReinits&&Oi.isInited(i.NAME))return;Oi.add(i.NAME);const e=Il(i),s=(e==null?void 0:e.isToggler)||!1;if(Ll(i),e!=null&&e.advanced){e==null||e.advanced(i,e==null?void 0:e.selector);return}if(s){e==null||e.callback(i,e==null?void 0:e.selector);return}h.find(e==null?void 0:e.selector).forEach(n=>{let r=i.getInstance(n);r||(r=new i(n),e!=null&&e.onInit&&r[e.onInit]())})},kl={allowReinits:!1,checkOtherImports:!1};class xl{constructor(t){$i(this,"init",(t,e)=>{t.forEach(s=>Rl(s,e))});$i(this,"initTWE",(t,e)=>{const s={...kl,...e},n=Object.keys(Bt).map(r=>{if(!!document.querySelector(Bt[r].selector)){const a=t[Bt[r].name];return!a&&!Oi.isInited(r)&&s.checkOtherImports&&console.warn(`Please import ${Bt[r].name} from "tw-elements" package and add it to a object parameter inside "initTWE" function`),a}});this.init(n,s)});Bt=t}}const $n=new xl(Sl).initTWE;$n({Button:ne,Dropdown:z,Collapse:kt,Offcanvas:Pt,Carousel:q,Popover:ge,ScrollSpy:me,Tab:be,Tooltip:Et,Input:S,Ripple:Oe,Modal:te}),L.Button=ne,L.Carousel=q,L.Collapse=kt,L.Dropdown=z,L.Input=S,L.Modal=te,L.Offcanvas=Pt,L.Popover=ge,L.Ripple=Oe,L.ScrollSpy=me,L.Tab=be,L.Tooltip=Et,L.initTWE=$n,Object.defineProperty(L,Symbol.toStringTag,{value:"Module"})}); +//# sourceMappingURL=tw-elements.umd.min.js.map diff --git a/core/http/static/general.css b/core/http/static/general.css index fd1161e8..63007cf5 100644 --- a/core/http/static/general.css +++ b/core/http/static/general.css @@ -81,10 +81,10 @@ ul { li { font-size: 0.875rem; /* Small text size */ color: #4a5568; /* Dark gray text */ - background-color: #f7fafc; /* Very light gray background */ + /* background-color: #f7fafc; Very light gray background */ border-radius: 0.375rem; /* Rounded corners */ padding: 0.5rem; /* Padding inside each list item */ - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); /* Subtle shadow */ + /*box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); Subtle shadow */ margin-bottom: 0.5rem; /* Vertical space between list items */ } diff --git a/core/http/views/chat.html b/core/http/views/chat.html index 1e490b65..79c39570 100644 --- a/core/http/views/chat.html +++ b/core/http/views/chat.html @@ -37,7 +37,7 @@ SOFTWARE.
- {{template "views/partials/navbar"}} + {{template "views/partials/navbar" .}}
diff --git a/core/http/views/p2p.html b/core/http/views/p2p.html new file mode 100644 index 00000000..0396924e --- /dev/null +++ b/core/http/views/p2p.html @@ -0,0 +1,150 @@ + + +{{template "views/partials/head" .}} + + +
+ + {{template "views/partials/navbar" .}} +
+
+ +

+ Distributed inference with P2P + + + +

+
LocalAI uses P2P technologies to enable distribution of work between peers. It is possible to share an instance with Federation and/or split the weights of a model across peers (only available with llama.cpp models). You can now share computational resources between your devices or your friends!
+ + +
+ +

Federated Nodes:

+

You can start LocalAI in federated mode to share your instance, or start the federated server to balance requests between nodes of the federation.

+ +
+
+
+ +
+ +

Start a federated instance

+ + + + + + +
+ + + +
+
+ + + +
+ +

Workers (llama.cpp):

+

You can start llama.cpp workers to distribute weights between the workers and offload part of the computation. To start a new worker, you can use the CLI or Docker.

+ +
+
+
+
+ +

Start a new llama.cpp P2P worker

+ + + + + +
+ + +
+
+ +
+
+ + {{template "views/partials/footer" .}} +
+ + + + + diff --git a/core/http/views/partials/footer.html b/core/http/views/partials/footer.html index 7fc7e504..6e732f96 100644 --- a/core/http/views/partials/footer.html +++ b/core/http/views/partials/footer.html @@ -1,4 +1,5 @@ \ No newline at end of file + + diff --git a/core/http/views/partials/navbar.html b/core/http/views/partials/navbar.html index caa1f3b7..9bf5b96a 100644 --- a/core/http/views/partials/navbar.html +++ b/core/http/views/partials/navbar.html @@ -21,6 +21,9 @@ Generate images TTS Talk + {{ if .IsP2PEnabled }} + Swarm + {{ end }} API
@@ -34,6 +37,9 @@ Generate images TTS Talk + {{ if .IsP2PEnabled }} + Swarm + {{ end }} API
diff --git a/core/http/views/talk.html b/core/http/views/talk.html index d0caedab..afb494e9 100644 --- a/core/http/views/talk.html +++ b/core/http/views/talk.html @@ -10,7 +10,7 @@
- {{template "views/partials/navbar"}} + {{template "views/partials/navbar" .}}
diff --git a/core/p2p/node.go b/core/p2p/node.go new file mode 100644 index 00000000..1d5356e6 --- /dev/null +++ b/core/p2p/node.go @@ -0,0 +1,50 @@ +package p2p + +import ( + "sync" + "time" +) + +const defaultServicesID = "services_localai" +const FederatedID = "federated" + +type NodeData struct { + Name string + ID string + TunnelAddress string + LastSeen time.Time +} + +func (d NodeData) IsOnline() bool { + now := time.Now() + // if the node was seen in the last 40 seconds, it's online + return now.Sub(d.LastSeen) < 40*time.Second +} + +var mu sync.Mutex +var nodes = map[string]map[string]NodeData{} + +func GetAvailableNodes(serviceID string) []NodeData { + if serviceID == "" { + serviceID = defaultServicesID + } + mu.Lock() + defer mu.Unlock() + var availableNodes = []NodeData{} + for _, v := range nodes[serviceID] { + availableNodes = append(availableNodes, v) + } + return availableNodes +} + +func AddNode(serviceID string, node NodeData) { + if serviceID == "" { + serviceID = defaultServicesID + } + mu.Lock() + defer mu.Unlock() + if nodes[serviceID] == nil { + nodes[serviceID] = map[string]NodeData{} + } + nodes[serviceID][node.ID] = node +} diff --git a/core/p2p/p2p.go b/core/p2p/p2p.go index 67196c97..79e8051e 100644 --- a/core/p2p/p2p.go +++ b/core/p2p/p2p.go @@ -10,19 +10,18 @@ import ( "io" "net" "os" - "strings" + "sync" "time" + "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p/core/peer" "github.com/mudler/LocalAI/pkg/utils" + "github.com/mudler/edgevpn/pkg/config" "github.com/mudler/edgevpn/pkg/node" "github.com/mudler/edgevpn/pkg/protocol" + "github.com/mudler/edgevpn/pkg/services" "github.com/mudler/edgevpn/pkg/types" "github.com/phayes/freeport" - - "github.com/ipfs/go-log" - "github.com/mudler/edgevpn/pkg/config" - "github.com/mudler/edgevpn/pkg/services" zlog "github.com/rs/zerolog/log" "github.com/mudler/edgevpn/pkg/logger" @@ -34,6 +33,15 @@ func GenerateToken() string { return newData.Base64() } +func IsP2PEnabled() bool { + return true +} + +func nodeID(s string) string { + hostname, _ := os.Hostname() + return fmt.Sprintf("%s-%s", hostname, s) +} + func allocateLocalService(ctx context.Context, node *node.Node, listenAddr, service string) error { zlog.Info().Msgf("Allocating service '%s' on: %s", service, listenAddr) @@ -53,16 +61,16 @@ func allocateLocalService(ctx context.Context, node *node.Node, listenAddr, serv 10*time.Second, func() { // Retrieve current ID for ip in the blockchain - _, found := ledger.GetKey(protocol.UsersLedgerKey, node.Host().ID().String()) + //_, found := ledger.GetKey(protocol.UsersLedgerKey, node.Host().ID().String()) // If mismatch, update the blockchain - if !found { - updatedMap := map[string]interface{}{} - updatedMap[node.Host().ID().String()] = &types.User{ - PeerID: node.Host().ID().String(), - Timestamp: time.Now().String(), - } - ledger.Add(protocol.UsersLedgerKey, updatedMap) + //if !found { + updatedMap := map[string]interface{}{} + updatedMap[node.Host().ID().String()] = &types.User{ + PeerID: node.Host().ID().String(), + Timestamp: time.Now().String(), } + ledger.Add(protocol.UsersLedgerKey, updatedMap) + // } }, ) @@ -80,7 +88,6 @@ func allocateLocalService(ctx context.Context, node *node.Node, listenAddr, serv continue } - // ll.Info("New connection from", l.Addr().String()) // Handle connections in a new goroutine, forwarding to the p2p service go func() { // Retrieve current ID for ip in the blockchain @@ -137,24 +144,30 @@ func copyStream(closer chan struct{}, dst io.Writer, src io.Reader) { // This is the main of the server (which keeps the env variable updated) // This starts a goroutine that keeps LLAMACPP_GRPC_SERVERS updated with the discovered services -func LLamaCPPRPCServerDiscoverer(ctx context.Context, token string) error { - tunnels, err := discoveryTunnels(ctx, token) +func ServiceDiscoverer(ctx context.Context, n *node.Node, token, servicesID string, discoveryFunc func()) error { + if servicesID == "" { + servicesID = defaultServicesID + } + tunnels, err := discoveryTunnels(ctx, n, token, servicesID) if err != nil { return err } - + // TODO: discoveryTunnels should return all the nodes that are available? + // In this way we updated availableNodes here instead of appending + // e.g. we have a LastSeen field in NodeData that is updated in discoveryTunnels + // each time the node is seen + // In this case the below function should be idempotent and just keep track of the nodes go func() { - totalTunnels := []string{} for { select { case <-ctx.Done(): zlog.Error().Msg("Discoverer stopped") return case tunnel := <-tunnels: - - totalTunnels = append(totalTunnels, tunnel) - os.Setenv("LLAMACPP_GRPC_SERVERS", strings.Join(totalTunnels, ",")) - zlog.Debug().Msgf("setting LLAMACPP_GRPC_SERVERS to %s", strings.Join(totalTunnels, ",")) + AddNode(servicesID, tunnel) + if discoveryFunc != nil { + discoveryFunc() + } } } }() @@ -162,19 +175,10 @@ func LLamaCPPRPCServerDiscoverer(ctx context.Context, token string) error { return nil } -func discoveryTunnels(ctx context.Context, token string) (chan string, error) { - tunnels := make(chan string) +func discoveryTunnels(ctx context.Context, n *node.Node, token, servicesID string) (chan NodeData, error) { + tunnels := make(chan NodeData) - nodeOpts, err := newNodeOpts(token) - if err != nil { - return nil, err - } - - n, err := node.New(nodeOpts...) - if err != nil { - return nil, fmt.Errorf("creating a new node: %w", err) - } - err = n.Start(ctx) + err := n.Start(ctx) if err != nil { return nil, fmt.Errorf("creating a new node: %w", err) } @@ -184,8 +188,14 @@ func discoveryTunnels(ctx context.Context, token string) (chan string, error) { } // get new services, allocate and return to the channel + + // TODO: + // a function ensureServices that: + // - starts a service if not started, if the worker is Online + // - checks that workers are Online, if not cancel the context of allocateLocalService + // - discoveryTunnels should return all the nodes and addresses associated with it + // - the caller should take now care of the fact that we are always returning fresh informations go func() { - emitted := map[string]bool{} for { select { case <-ctx.Done(): @@ -195,20 +205,20 @@ func discoveryTunnels(ctx context.Context, token string) (chan string, error) { time.Sleep(5 * time.Second) zlog.Debug().Msg("Searching for workers") - data := ledger.LastBlock().Storage["services_localai"] - for k := range data { + data := ledger.LastBlock().Storage[servicesID] + for k, v := range data { zlog.Info().Msgf("Found worker %s", k) - if _, found := emitted[k]; !found { - emitted[k] = true - //discoveredPeers <- k - port, err := freeport.GetFreePort() - if err != nil { - fmt.Print(err) - } - tunnelAddress := fmt.Sprintf("127.0.0.1:%d", port) - go allocateLocalService(ctx, n, tunnelAddress, k) - tunnels <- tunnelAddress + nd := &NodeData{} + if err := v.Unmarshal(nd); err != nil { + zlog.Error().Msg("cannot unmarshal node data") + continue } + ensureService(ctx, n, nd, k) + muservice.Lock() + if _, ok := service[nd.Name]; ok { + tunnels <- service[nd.Name].NodeData + } + muservice.Unlock() } } } @@ -217,8 +227,60 @@ func discoveryTunnels(ctx context.Context, token string) (chan string, error) { return tunnels, err } +type nodeServiceData struct { + NodeData NodeData + CancelFunc context.CancelFunc +} + +var service = map[string]nodeServiceData{} +var muservice sync.Mutex + +func ensureService(ctx context.Context, n *node.Node, nd *NodeData, sserv string) { + muservice.Lock() + defer muservice.Unlock() + if ndService, found := service[nd.Name]; !found { + if !nd.IsOnline() { + // if node is offline and not present, do nothing + return + } + newCtxm, cancel := context.WithCancel(ctx) + // Start the service + port, err := freeport.GetFreePort() + if err != nil { + fmt.Print(err) + } + tunnelAddress := fmt.Sprintf("127.0.0.1:%d", port) + nd.TunnelAddress = tunnelAddress + service[nd.Name] = nodeServiceData{ + NodeData: *nd, + CancelFunc: cancel, + } + go allocateLocalService(newCtxm, n, tunnelAddress, sserv) + zlog.Debug().Msgf("Starting service %s on %s", sserv, tunnelAddress) + } else { + // Check if the service is still alive + // if not cancel the context + if !nd.IsOnline() && !ndService.NodeData.IsOnline() { + ndService.CancelFunc() + delete(service, nd.Name) + zlog.Info().Msgf("Node %s is offline, deleting", nd.ID) + } else if nd.IsOnline() { + // update last seen inside service + nd.TunnelAddress = ndService.NodeData.TunnelAddress + service[nd.Name] = nodeServiceData{ + NodeData: *nd, + CancelFunc: ndService.CancelFunc, + } + zlog.Debug().Msgf("Node %s is still online", nd.ID) + } + } +} + // This is the P2P worker main -func BindLLamaCPPWorker(ctx context.Context, host, port, token string) error { +func ExposeService(ctx context.Context, host, port, token, servicesID string) error { + if servicesID == "" { + servicesID = defaultServicesID + } llger := logger.New(log.LevelFatal) nodeOpts, err := newNodeOpts(token) @@ -248,22 +310,40 @@ func BindLLamaCPPWorker(ctx context.Context, host, port, token string) error { ledger.Announce( ctx, - 10*time.Second, + 20*time.Second, func() { // Retrieve current ID for ip in the blockchain - _, found := ledger.GetKey("services_localai", name) + //_, found := ledger.GetKey("services_localai", name) // If mismatch, update the blockchain - if !found { - updatedMap := map[string]interface{}{} - updatedMap[name] = "p2p" - ledger.Add("services_localai", updatedMap) + //if !found { + updatedMap := map[string]interface{}{} + updatedMap[name] = &NodeData{ + Name: name, + LastSeen: time.Now(), + ID: nodeID(name), } + ledger.Add(servicesID, updatedMap) + // } }, ) return err } +func NewNode(token string) (*node.Node, error) { + nodeOpts, err := newNodeOpts(token) + if err != nil { + return nil, err + } + + n, err := node.New(nodeOpts...) + if err != nil { + return nil, fmt.Errorf("creating a new node: %w", err) + } + + return n, nil +} + func newNodeOpts(token string) ([]node.Option, error) { llger := logger.New(log.LevelFatal) defaultInterval := 10 * time.Second diff --git a/core/p2p/p2p_disabled.go b/core/p2p/p2p_disabled.go index 59314aa6..340a4fb4 100644 --- a/core/p2p/p2p_disabled.go +++ b/core/p2p/p2p_disabled.go @@ -6,16 +6,26 @@ package p2p import ( "context" "fmt" + + "github.com/mudler/edgevpn/pkg/node" ) func GenerateToken() string { return "not implemented" } -func LLamaCPPRPCServerDiscoverer(ctx context.Context, token string) error { +func ServiceDiscoverer(ctx context.Context, node *node.Node, token, servicesID string, fn func()) error { return fmt.Errorf("not implemented") } -func BindLLamaCPPWorker(ctx context.Context, host, port, token string) error { +func ExposeService(ctx context.Context, host, port, token, servicesID string) error { return fmt.Errorf("not implemented") } + +func IsP2PEnabled() bool { + return false +} + +func NewNode(token string) (*node.Node, error) { + return nil, fmt.Errorf("not implemented") +} diff --git a/docs/static/install.sh b/docs/static/install.sh index 4efdc757..61be710b 100644 --- a/docs/static/install.sh +++ b/docs/static/install.sh @@ -76,6 +76,8 @@ DOCKER_INSTALL=${DOCKER_INSTALL:-$docker_found} USE_AIO=${USE_AIO:-false} API_KEY=${API_KEY:-} CORE_IMAGES=${CORE_IMAGES:-false} +P2P_TOKEN=${P2P_TOKEN:-} +WORKER=${WORKER:-false} # nprocs -1 if available nproc; then procs=$(nproc) @@ -132,7 +134,14 @@ configure_systemd() { info "Adding current user to local-ai group..." $SUDO usermod -a -G local-ai $(whoami) - + STARTCOMMAND="run" + if [ "$WORKER" = true ]; then + if [ -n "$P2P_TOKEN" ]; then + STARTCOMMAND="worker p2p-llama-cpp-rpc" + else + STARTCOMMAND="worker llama-cpp-rpc" + fi + fi info "Creating local-ai systemd service..." cat </dev/null [Unit] @@ -140,7 +149,7 @@ Description=LocalAI Service After=network-online.target [Service] -ExecStart=$BINDIR/local-ai run +ExecStart=$BINDIR/local-ai $STARTCOMMAND User=local-ai Group=local-ai Restart=always @@ -159,6 +168,11 @@ EOF $SUDO echo "THREADS=$THREADS" | $SUDO tee -a /etc/localai.env >/dev/null $SUDO echo "MODELS_PATH=$MODELS_PATH" | $SUDO tee -a /etc/localai.env >/dev/null + if [ -n "$P2P_TOKEN" ]; then + $SUDO echo "LOCALAI_P2P_TOKEN=$P2P_TOKEN" | $SUDO tee -a /etc/localai.env >/dev/null + $SUDO echo "LOCALAI_P2P=true" | $SUDO tee -a /etc/localai.env >/dev/null + fi + SYSTEMCTL_RUNNING="$(systemctl is-system-running || true)" case $SYSTEMCTL_RUNNING in running|degraded) @@ -407,6 +421,19 @@ install_docker() { # exit 0 fi + STARTCOMMAND="run" + if [ "$WORKER" = true ]; then + if [ -n "$P2P_TOKEN" ]; then + STARTCOMMAND="worker p2p-llama-cpp-rpc" + else + STARTCOMMAND="worker llama-cpp-rpc" + fi + fi + envs="" + if [ -n "$P2P_TOKEN" ]; then + envs="-e LOCALAI_P2P_TOKEN=$P2P_TOKEN -e LOCALAI_P2P=true" + fi + IMAGE_TAG= if [ "$HAS_CUDA" ]; then IMAGE_TAG=${VERSION}-cublas-cuda12-ffmpeg @@ -430,7 +457,8 @@ install_docker() { --restart=always \ -e API_KEY=$API_KEY \ -e THREADS=$THREADS \ - -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG + $envs \ + -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG $STARTCOMMAND elif [ "$HAS_AMD" ]; then IMAGE_TAG=${VERSION}-hipblas-ffmpeg # CORE @@ -448,7 +476,8 @@ install_docker() { --restart=always \ -e API_KEY=$API_KEY \ -e THREADS=$THREADS \ - -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG + $envs \ + -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG $STARTCOMMAND elif [ "$HAS_INTEL" ]; then IMAGE_TAG=${VERSION}-sycl-f32-ffmpeg # CORE @@ -465,7 +494,8 @@ install_docker() { --restart=always \ -e API_KEY=$API_KEY \ -e THREADS=$THREADS \ - -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG + $envs \ + -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG $STARTCOMMAND else IMAGE_TAG=${VERSION}-ffmpeg # CORE @@ -481,7 +511,8 @@ install_docker() { -e MODELS_PATH=/models \ -e API_KEY=$API_KEY \ -e THREADS=$THREADS \ - -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG + $envs \ + -d -p $PORT:8080 --name local-ai localai/localai:$IMAGE_TAG $STARTCOMMAND fi install_success diff --git a/embedded/webui_static.yaml b/embedded/webui_static.yaml index d9d1f064..c65d64ee 100644 --- a/embedded/webui_static.yaml +++ b/embedded/webui_static.yaml @@ -16,6 +16,9 @@ - filename: "tw-elements.css" url: "https://cdn.jsdelivr.net/npm/tw-elements/css/tw-elements.min.css" sha: "72746af5326d6eb3647f504efa81b5e0f50ed486f37cc8262a4169781ad310d3" +- filename: "tw-elements.js" + url: "https://cdn.jsdelivr.net/npm/tw-elements/js/tw-elements.umd.min.js" + sha: "2985706362e92360b65c8697cc32490bb9c0a5df9cd9b7251a97c1c5a661a40a" - filename: "tailwindcss.js" url: "https://cdn.tailwindcss.com/3.3.0" sha: "dbff048aa4581e6eae7f1cb2c641f72655ea833b3bb82923c4a59822e11ca594"