├── .formatter.exs ├── .github └── workflows │ └── run-test.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── assets ├── css │ ├── app.css │ └── themes.css ├── js │ ├── app.js │ └── chart.js ├── package-lock.json ├── package.json └── postcss.config.js ├── config └── config.exs ├── dev.exs ├── dist ├── css │ └── app.css └── js │ ├── app.js │ └── app.js.map ├── lib ├── orion.ex ├── orion │ ├── application.ex │ ├── match_spec.ex │ ├── match_spec_store.ex │ └── session_pubsub.ex ├── orion_web.ex └── orion_web │ ├── assets.ex │ ├── components.ex │ ├── layout_view.ex │ ├── layouts │ └── home.html.heex │ ├── measurement_live.ex │ ├── page_live.ex │ └── router.ex ├── mix.exs ├── mix.lock ├── press_release.md ├── screenshot.png ├── shell.nix └── test ├── orion_web ├── live │ └── page_live_test.exs └── views │ └── layout_view_test.exs ├── support ├── channel_case.ex └── conn_case.ex └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | plugins: [Phoenix.LiveView.HTMLFormatter], 3 | import_deps: [:phoenix], 4 | inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}"] 5 | ] 6 | -------------------------------------------------------------------------------- /.github/workflows/run-test.yaml: -------------------------------------------------------------------------------- 1 | name: run tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: erlef/setup-beam@v1 12 | with: 13 | otp-version: "24.0.x" 14 | elixir-version: "1.12.x" 15 | - run: mix deps.get 16 | - run: mix test 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | orion-*.tar 24 | 25 | # If NPM crashes, it generates a log, let's ignore it too. 26 | npm-debug.log 27 | 28 | # The directory NPM downloads your dependencies sources to. 29 | /assets/node_modules/ 30 | 31 | # Since we are building assets from assets/, 32 | # we ignore priv/static. You may want to comment 33 | # this depending on your deployment strategy. 34 | /priv/static/ 35 | 36 | .envrc -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## v1.0.7 [2025-03-28] 4 | 5 | * Update CSS to tailwind 4.0 6 | 7 | ## v1.0.6 [2025-03-27] 8 | 9 | * Use Phoenix LiveView v1.0+, enabling to run on newer liveview 10 | * Updates assets dependencies 11 | 12 | ## v1.0.5 [2024-02-13] 13 | 14 | * Fix phoenix version errors, close [#16](https://github.com/LivewareProblems/Orion/issues/16) 15 | 16 | ## v1.0.4 [2023-12-16] 17 | 18 | * Relax phoenix_live_view version constraints, see [#15](https://github.com/LivewareProblems/Orion/pull/15) thanks to [@wkirschbaum](https://github.com/wkirschbaum) 19 | 20 | ## v1.0.3 [2023-04-29] 21 | 22 | * Fix multiple bugs with the JS and update dependencies 23 | * Fix [#13](https://github.com/LivewareProblems/Orion/issues/13), which came 24 | from misunderstanding erlang tracing behaviour. 25 | 26 | ## v1.0.2 (2023-02-19) 27 | 28 | * serve assets from the router, see [#11](https://github.com/LivewareProblems/Orion/pull/11) thanks to [@mcrum](https://github.com/mcrumm) 29 | 30 | ## v1.0.1 (2023-02-12) 31 | 32 | * Fix documentation landing page 33 | 34 | ## v1.0.0 (2023-02-12) 35 | 36 | * Stable release 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Thomas Depierre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Orion 2 | 3 | [Official Documentation on Hexdoc](https://hexdocs.pm/orion/Orion.html) 4 | 5 | 6 | Orion is a Dynamic Distributed Profiler. It allows you to profile any function 7 | in a beam cluster and get back an histogram representing the profile of the 8 | function calls across the whole cluster. Live, with low overhead, making it 9 | suitable to run in production. It uses Erlang dynamic tracing under the hood. 10 | 11 | It is meant to be used as a library in part of your existing application. 12 | 13 | If you run your application non clustered, you will be able to trace the node 14 | you connect to. 15 | 16 | If your applications are connected via Distributed Erlang, then you will get 17 | a histogram of every call on every node, aggregated. 18 | 19 | ![screenshot](https://github.com/LivewareProblems/Orion/raw/main/screenshot.png) 20 | 21 | ## Non Goals 22 | 23 | - Be useable in any BEAM language. This may happen in the future but for now we 24 | depend on dog_sketch which is in elixir 25 | - Making it easy to run the UI locally and connect remotely to a cluster. This 26 | may come in the future or in a paid extension. If you are interested, [contact 27 | me on the Elixir Forum](https://elixirforum.com/u/dianaolympos/summary). In 28 | the meantime, you can use the `mix dev` local development setup as a starting 29 | point to do your own. Orion totally work remotely connected with erlang 30 | distribution, so as long as you can connect to your cluster (and deactivate 31 | the `:self_profile` option in your endpont), it should just work. 32 | - Session handling, in particular personal auth, and more. This may come in the 33 | future or in a paid extension. If you are interested, [contact me on the 34 | Elixir Forum](https://elixirforum.com/u/dianaolympos/summary). Refreshes clean 35 | up the UI. 36 | 37 | ## Installation 38 | 39 | To start using Orion, you will need three steps: 40 | 41 | 1. Add the `orion` dependency 42 | 2. Configure LiveView 43 | 3. Add UI access 44 | 45 | ### 1. Add the `orion` dependency 46 | 47 | Add the following to your `mix.exs` and run `mix deps.get`: 48 | 49 | ```elixir 50 | def deps do 51 | [ 52 | {:orion, "~> 1.0"} 53 | ] 54 | end 55 | ``` 56 | 57 | ### 2. Configure LiveView 58 | 59 | The Orion UI is built on top of LiveView. If LiveView is already installed in 60 | your app, feel free to skip this section. 61 | 62 | If you plan to use LiveView in your application in the future, we recommend you 63 | to follow [the official installation 64 | instructions](https://hexdocs.pm/phoenix_live_view/installation.html). This 65 | guide only covers the minimum steps necessary for the Orion UI itself to run. 66 | 67 | First, update your endpoint's configuration to include a signing salt. You can 68 | generate a signing salt by running `mix phx.gen.secret 32` (note Phoenix v1.5+ 69 | apps already have this configuration): 70 | 71 | ```elixir 72 | # config/config.exs 73 | config :my_app, MyAppWeb.Endpoint, 74 | live_view: [signing_salt: "SECRET_SALT"] 75 | ``` 76 | 77 | Then add the `Phoenix.LiveView.Socket` declaration to your endpoint: 78 | 79 | ```elixir 80 | socket "/live", Phoenix.LiveView.Socket 81 | ``` 82 | 83 | And you are good to go! 84 | 85 | ### 3. Add Orion UI access for development-only usage 86 | 87 | Once installed, update your router's configuration to forward requests to an 88 | OrionWeb with a unique `name` of your choosing: 89 | 90 | ```elixir 91 | # lib/my_app_web/router.ex 92 | use MyAppWeb, :router 93 | import OrionWeb.Router 94 | ... 95 | if Mix.env() == :dev do 96 | scope "/" do 97 | pipe_through [:browser] 98 | live_orion "/orion" 99 | end 100 | end 101 | ``` 102 | 103 | This is all. Run `mix phx.server` and access the "/orion" to start profiling. 104 | 105 | ### Extra: Add Orion access on all environments (including production) 106 | 107 | If you want to use the Orion UI in production, you should put it behind some 108 | authentication and allow only admins to access it. 109 | 110 | If you have an authentication layer already for admins, `live_orion` accept an 111 | `:on_mount` option, to specify the hooks to validate your authentication, as 112 | described in the [official phoenix guide about 113 | security](https://hexdocs.pm/phoenix_live_view/security-model.html#mounting-considerations) 114 | 115 | If your application does not have an admins-only section yet, you can use 116 | `Plug.BasicAuth` to set up some basic authentication as long as you are also 117 | using SSL (which you should anyway): 118 | 119 | ```elixir 120 | # lib/my_app_web/router.ex 121 | use MyAppWeb, :router 122 | import OrionWeb.Router 123 | ... 124 | pipeline :admins_only do 125 | plug :admin_basic_auth 126 | end 127 | scope "/" do 128 | pipe_through [:browser, :admins_only] 129 | live_orion "/orion" 130 | end 131 | defp admin_basic_auth(conn, _opts) do 132 | username = System.fetch_env!("AUTH_USERNAME") 133 | password = System.fetch_env!("AUTH_PASSWORD") 134 | Plug.BasicAuth.basic_auth(conn, username: username, password: password) 135 | end 136 | ``` 137 | 138 | If you are running your application behind a proxy or a webserver, you also have 139 | to make sure they are configured for allowing WebSocket upgrades. For example, 140 | [here is an 141 | article](https://web.archive.org/web/20171104012240/https://dennisreimann.de/articles/phoenix-nginx-config.html) 142 | on how to configure Nginx with Phoenix and WebSockets. 143 | 144 | Finally, you will also want to configure your `config/prod.exs` and use your 145 | domain name under the `check_origin` configuration: 146 | 147 | ```elixir 148 | check_origin: ["//myapp.com"] 149 | ``` 150 | 151 | Then you should be good to go! 152 | 153 | 154 | 155 | ## Contributing 156 | 157 | You need elixir 1.12+ and OTP 24+. 158 | 159 | Orion is a phoenix liveview application for the frontend. 160 | 161 | To start your Phoenix server: 162 | 163 | - Install dependencies with `mix setup` 164 | 165 | If you want to see it in action 166 | 167 | - Start the development endpoint with `mix dev` 168 | 169 | Now you can visit [`localhost:4001`](http://localhost:4001) from your browser. 170 | 171 | ## License 172 | 173 | MIT License. Copyright (c) 2023 Thomas Depierre. 174 | -------------------------------------------------------------------------------- /assets/css/app.css: -------------------------------------------------------------------------------- 1 | /*! purgecss start ignore */ 2 | 3 | @import "uplot/dist/uPlot.min.css"; 4 | 5 | /*! purgecss end ignore */ 6 | 7 | @import "tailwindcss"; 8 | @import "./themes.css"; 9 | @plugin "@tailwindcss/forms"; 10 | 11 | @source "./js"; 12 | @source "../../lib"; 13 | 14 | @custom-variant phx-click-loading ([".phx-click-loading&", ".phx-click-loading &"]); 15 | @custom-variant phx-submit-loading ([".phx-submit-loading&", ".phx-submit-loading &"]); 16 | @custom-variant phx-change-loading ([".phx-change-loading&", ".phx-change-loading &"]); 17 | 18 | [data-phx-root-id] { 19 | display: contents; 20 | } 21 | -------------------------------------------------------------------------------- /assets/css/themes.css: -------------------------------------------------------------------------------- 1 | @theme { 2 | --container-full: 100%; 3 | --color-*: initial; 4 | 5 | --color-blue-10: #f0f5fc; 6 | --color-blue-20: #cfe0fc; 7 | --color-blue-30: #accbfc; 8 | --color-blue-40: #84b1fa; 9 | --color-blue-50: #5691f0; 10 | --color-blue-60: #3272d9; 11 | --color-blue-70: #1d5bbf; 12 | --color-blue-80: #114599; 13 | --color-blue-90: #103570; 14 | --color-blue-100: #15233b; 15 | 16 | --color-bronze-10: #fcf2e6; 17 | --color-bronze-20: #fad8af; 18 | --color-bronze-30: #f5bc76; 19 | --color-bronze-40: #e89c3f; 20 | --color-bronze-50: #cf7911; 21 | --color-bronze-60: #ad5f00; 22 | --color-bronze-70: #8a4d03; 23 | --color-bronze-80: #693d07; 24 | --color-bronze-90: #4d2f0b; 25 | --color-bronze-100: #33210c; 26 | 27 | --color-dusk-10: #f4f2f7; 28 | --color-dusk-20: #e3dcf7; 29 | --color-dusk-30: #cec2f0; 30 | --color-dusk-40: #b5a6e3; 31 | --color-dusk-50: #9886cf; 32 | --color-dusk-60: #7a68b3; 33 | --color-dusk-70: #645396; 34 | --color-dusk-80: #4f4178; 35 | --color-dusk-90: #3a3154; 36 | --color-dusk-100: #282436; 37 | 38 | --color-gray-10: #f5f7fa; 39 | --color-gray-20: #ebeff5; 40 | --color-gray-30: #dde3ed; 41 | --color-gray-40: #c8d1e0; 42 | --color-gray-50: #afbacc; 43 | --color-gray-60: #8e99ab; 44 | --color-gray-70: #707a8a; 45 | --color-gray-80: #58606e; 46 | --color-gray-90: #434a54; 47 | --color-gray-100: #333840; 48 | 49 | --color-green-10: #e1faeb; 50 | --color-green-20: #abedc5; 51 | --color-green-30: #7ddba3; 52 | --color-green-40: #57c282; 53 | --color-green-50: #3ba164; 54 | --color-green-60: #2a854e; 55 | --color-green-70: #20693d; 56 | --color-green-80: #1a5230; 57 | --color-green-90: #153d25; 58 | --color-green-100: #112b1b; 59 | 60 | --color-indigo-10: #f2f2fc; 61 | --color-indigo-20: #dcdcfc; 62 | --color-indigo-30: #c2c2fc; 63 | --color-indigo-40: #a7a7fa; 64 | --color-indigo-50: #8585f2; 65 | --color-indigo-60: #6767e6; 66 | --color-indigo-70: #4d4dd1; 67 | --color-indigo-80: #3737b3; 68 | --color-indigo-90: #28288a; 69 | --color-indigo-100: #202057; 70 | 71 | --color-magenta-10: #faf0f4; 72 | --color-magenta-20: #fad4e4; 73 | --color-magenta-30: #fab4d1; 74 | --color-magenta-40: #f78bb8; 75 | --color-magenta-50: #ed5393; 76 | --color-magenta-60: #d6246e; 77 | --color-magenta-70: #b01355; 78 | --color-magenta-80: #8a1244; 79 | --color-magenta-90: #611535; 80 | --color-magenta-100: #421527; 81 | 82 | --color-purple-10: #f5f0fa; 83 | --color-purple-20: #ead9fa; 84 | --color-purple-30: #dabcf7; 85 | --color-purple-40: #c79bf2; 86 | --color-purple-50: #ae74e8; 87 | --color-purple-60: #9656d6; 88 | --color-purple-70: #7d3cbd; 89 | --color-purple-80: #642b9e; 90 | --color-purple-90: #4b2175; 91 | --color-purple-100: #371c52; 92 | 93 | --color-red-10: #faf0f0; 94 | --color-red-20: #fad4d4; 95 | --color-red-30: #fab6b6; 96 | --color-red-40: #fa8e8e; 97 | --color-red-50: #f55353; 98 | --color-red-60: #de1b1b; 99 | --color-red-70: #b80d0d; 100 | --color-red-80: #8f0e0e; 101 | --color-red-90: #661414; 102 | --color-red-100: #451717; 103 | 104 | --color-sky-10: #e8f4fa; 105 | --color-sky-20: #bbe5fa; 106 | --color-sky-30: #8dd4f7; 107 | --color-sky-40: #53baed; 108 | --color-sky-50: #229ad6; 109 | --color-sky-60: #0c7bb3; 110 | --color-sky-70: #066391; 111 | --color-sky-80: #064d70; 112 | --color-sky-90: #093952; 113 | --color-sky-100: #0c2938; 114 | 115 | --color-slate-10: #edf4f7; 116 | --color-slate-20: #cbe3f5; 117 | --color-slate-30: #a7ceeb; 118 | --color-slate-40: #84b7db; 119 | --color-slate-50: #5e95bd; 120 | --color-slate-60: #48799c; 121 | --color-slate-70: #376180; 122 | --color-slate-80: #2d4d63; 123 | --color-slate-90: #243947; 124 | --color-slate-100: #1d2830; 125 | 126 | --color-teal-10: #e4f7f6; 127 | --color-teal-20: #a8ede9; 128 | --color-teal-30: #6cd9d2; 129 | --color-teal-40: #45bfb7; 130 | --color-teal-50: #28a199; 131 | --color-teal-60: #17827b; 132 | --color-teal-70: #116963; 133 | --color-teal-80: #0d524d; 134 | --color-teal-90: #0b3d3a; 135 | --color-teal-100: #092b29; 136 | 137 | --color-black: #1d1f24; 138 | --color-white: #ffffff; 139 | --color-transparent: rgba(0, 0, 0, 0); 140 | } 141 | 142 | @layer base { 143 | dark { 144 | --color-blue-100: #f0f5fc; 145 | --color-blue-90: #cfe0fc; 146 | --color-blue-80: #accbfc; 147 | --color-blue-70: #84b1fa; 148 | --color-blue-60: #5691f0; 149 | --color-blue-50: #3272d9; 150 | --color-blue-40: #1d5bbf; 151 | --color-blue-30: #114599; 152 | --color-blue-20: #103570; 153 | --color-blue-10: #15233b; 154 | 155 | --color-bronze-100: #fcf2e6; 156 | --color-bronze-90: #fad8af; 157 | --color-bronze-80: #f5bc76; 158 | --color-bronze-70: #e89c3f; 159 | --color-bronze-60: #cf7911; 160 | --color-bronze-50: #ad5f00; 161 | --color-bronze-40: #8a4d03; 162 | --color-bronze-30: #693d07; 163 | --color-bronze-20: #4d2f0b; 164 | --color-bronze-10: #33210c; 165 | 166 | --color-dusk-100: #f4f2f7; 167 | --color-dusk-90: #e3dcf7; 168 | --color-dusk-80: #cec2f0; 169 | --color-dusk-70: #b5a6e3; 170 | --color-dusk-60: #9886cf; 171 | --color-dusk-50: #7a68b3; 172 | --color-dusk-40: #645396; 173 | --color-dusk-30: #4f4178; 174 | --color-dusk-20: #3a3154; 175 | --color-dusk-10: #282436; 176 | 177 | --color-gray-100: #f5f7fa; 178 | --color-gray-90: #ebeff5; 179 | --color-gray-80: #dde3ed; 180 | --color-gray-70: #c8d1e0; 181 | --color-gray-60: #afbacc; 182 | --color-gray-50: #8e99ab; 183 | --color-gray-40: #707a8a; 184 | --color-gray-30: #58606e; 185 | --color-gray-20: #434a54; 186 | --color-gray-10: #333840; 187 | 188 | --color-green-100: #e1faeb; 189 | --color-green-90: #abedc5; 190 | --color-green-80: #7ddba3; 191 | --color-green-70: #57c282; 192 | --color-green-60: #3ba164; 193 | --color-green-50: #2a854e; 194 | --color-green-40: #20693d; 195 | --color-green-30: #1a5230; 196 | --color-green-20: #153d25; 197 | --color-green-10: #112b1b; 198 | 199 | --color-indigo-100: #f2f2fc; 200 | --color-indigo-90: #dcdcfc; 201 | --color-indigo-80: #c2c2fc; 202 | --color-indigo-70: #a7a7fa; 203 | --color-indigo-60: #8585f2; 204 | --color-indigo-50: #6767e6; 205 | --color-indigo-40: #4d4dd1; 206 | --color-indigo-30: #3737b3; 207 | --color-indigo-20: #28288a; 208 | --color-indigo-10: #202057; 209 | 210 | --color-magenta-100: #faf0f4; 211 | --color-magenta-90: #fad4e4; 212 | --color-magenta-80: #fab4d1; 213 | --color-magenta-70: #f78bb8; 214 | --color-magenta-60: #ed5393; 215 | --color-magenta-50: #d6246e; 216 | --color-magenta-40: #b01355; 217 | --color-magenta-30: #8a1244; 218 | --color-magenta-20: #611535; 219 | --color-magenta-10: #421527; 220 | 221 | --color-purple-100: #f5f0fa; 222 | --color-purple-90: #ead9fa; 223 | --color-purple-80: #dabcf7; 224 | --color-purple-70: #c79bf2; 225 | --color-purple-60: #ae74e8; 226 | --color-purple-50: #9656d6; 227 | --color-purple-40: #7d3cbd; 228 | --color-purple-30: #642b9e; 229 | --color-purple-20: #4b2175; 230 | --color-purple-10: #371c52; 231 | 232 | --color-red-100: #faf0f0; 233 | --color-red-90: #fad4d4; 234 | --color-red-80: #fab6b6; 235 | --color-red-70: #fa8e8e; 236 | --color-red-60: #f55353; 237 | --color-red-50: #de1b1b; 238 | --color-red-40: #b80d0d; 239 | --color-red-30: #8f0e0e; 240 | --color-red-20: #661414; 241 | --color-red-10: #451717; 242 | 243 | --color-sky-100: #e8f4fa; 244 | --color-sky-90: #bbe5fa; 245 | --color-sky-80: #8dd4f7; 246 | --color-sky-70: #53baed; 247 | --color-sky-60: #229ad6; 248 | --color-sky-50: #0c7bb3; 249 | --color-sky-40: #066391; 250 | --color-sky-30: #064d70; 251 | --color-sky-20: #093952; 252 | --color-sky-10: #0c2938; 253 | 254 | --color-slate-100: #edf4f7; 255 | --color-slate-90: #cbe3f5; 256 | --color-slate-80: #a7ceeb; 257 | --color-slate-70: #84b7db; 258 | --color-slate-60: #5e95bd; 259 | --color-slate-50: #48799c; 260 | --color-slate-40: #376180; 261 | --color-slate-30: #2d4d63; 262 | --color-slate-20: #243947; 263 | --color-slate-10: #1d2830; 264 | 265 | --color-teal-100: #e4f7f6; 266 | --color-teal-90: #a8ede9; 267 | --color-teal-80: #6cd9d2; 268 | --color-teal-70: #45bfb7; 269 | --color-teal-60: #28a199; 270 | --color-teal-50: #17827b; 271 | --color-teal-40: #116963; 272 | --color-teal-30: #0d524d; 273 | --color-teal-20: #0b3d3a; 274 | --color-teal-10: #092b29; 275 | 276 | --color-white: #1d1f24; 277 | --color-black: #ffffff; 278 | --color-transparent: rgba(0, 0, 0, 0); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | import topbar from "topbar" 2 | import { ChartData } from "./chart.js" 3 | 4 | let socketPath = document.querySelector("html").getAttribute("phx-socket") || "/live" 5 | let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") 6 | 7 | let Hooks = { 8 | ChartData: ChartData 9 | } 10 | 11 | let liveSocket = new LiveView.LiveSocket(socketPath, Phoenix.Socket, { hooks: Hooks, params: { _csrf_token: csrfToken } }) 12 | 13 | // Show progress bar on live navigation and form submits 14 | topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" }) 15 | window.addEventListener("phx:page-loading-start", info => topbar.show()) 16 | window.addEventListener("phx:page-loading-stop", info => topbar.hide()) 17 | 18 | // connect if there are any LiveViews on the page 19 | liveSocket.connect() 20 | 21 | // expose liveSocket on window for web console debug logs and latency simulation: 22 | // >> liveSocket.enableDebug() 23 | // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session 24 | // >> liveSocket.disableLatencySim() 25 | window.liveSocket = liveSocket 26 | 27 | -------------------------------------------------------------------------------- /assets/js/chart.js: -------------------------------------------------------------------------------- 1 | import uPlot from 'uplot' 2 | 3 | let chart = null; 4 | 5 | function paths(u, sidx, i0, i1) { 6 | const s = u.series[sidx]; 7 | const xdata = u.data[0]; 8 | const ydata = u.data[sidx]; 9 | const scaleX = 'x'; 10 | const scaleY = s.scale; 11 | 12 | const stroke = new Path2D(); 13 | 14 | const x_width = Math.abs((u.valToPos(xdata[0], scaleX, true) - u.valToPos(xdata[1], scaleX, true)) / 2); 15 | 16 | stroke.moveTo( 17 | Math.round(u.valToPos(xdata[0], scaleX, true)), 18 | Math.round(u.valToPos(ydata[0], scaleY, true)) 19 | ); 20 | 21 | for (let i = i0; i < i1; i++) { 22 | let x0 = Math.round(u.valToPos(xdata[i], scaleX, true)); 23 | let y0 = Math.round(u.valToPos(ydata[i], scaleY, true)); 24 | let x1 = Math.round(u.valToPos(xdata[i + 1], scaleX, true)); 25 | let y1 = Math.round(u.valToPos(ydata[i + 1], scaleY, true)); 26 | 27 | stroke.lineTo(x0 - x_width, y0); 28 | stroke.lineTo(x1 - x_width, y0); 29 | 30 | if (i == i1 - 1) { 31 | stroke.lineTo(x1 - x_width, y1); 32 | stroke.lineTo(x1, y1); 33 | } 34 | } 35 | 36 | const fill = new Path2D(stroke); 37 | 38 | let minY = Math.round(u.valToPos(u.scales[scaleY].min, scaleY, true)); 39 | let minX = Math.round(u.valToPos(u.scales[scaleX].min, scaleX, true)); 40 | let maxX = Math.round(u.valToPos(u.scales[scaleX].max, scaleX, true)); 41 | 42 | fill.lineTo(maxX, minY); 43 | fill.lineTo(minX, minY); 44 | 45 | return { 46 | stroke, 47 | fill, 48 | }; 49 | } 50 | 51 | function safe_to_fixed(number, decimals) { 52 | return number && number.toFixed(decimals) 53 | } 54 | 55 | function make_opts(parent, scale) { 56 | let rect = { width: parent.clientWidth, height: 600 }; 57 | 58 | let scaler = null; 59 | if (scale == "Linear") { 60 | scaler = (x) => x && x 61 | } else if (scale == "Log10") { 62 | scaler = (x) => x && Math.pow(10, x) 63 | } else if (scale == "Log2") { 64 | scaler = (x) => x && Math.pow(2, x) 65 | } 66 | 67 | return { 68 | id: parent.id + "-chart", 69 | width: rect.width, 70 | height: rect.height, 71 | labelSize: 10, 72 | labelFont: "bold 8px Arial", 73 | ticks: { show: false }, 74 | points: { show: false }, 75 | font: "8px Arial", 76 | padding: [null, 30, null, 30], 77 | series: [ 78 | { value: '{HH}:{mm}:{ss}' }, 79 | { 80 | label: "P99", 81 | stroke: "rgb(155, 214, 206)", 82 | value: (self, rawValue) => safe_to_fixed(scaler(rawValue), 3) + "ms", 83 | fill: "rgb(155, 214, 206, 0.5 )", 84 | paths: paths, 85 | scale: "ms" 86 | }, 87 | { 88 | label: "P90", 89 | stroke: "rgb(79, 169, 184)", 90 | value: (self, rawValue) => safe_to_fixed(scaler(rawValue), 3) + "ms", 91 | fill: "rgb(79, 169, 184, 0.5)", 92 | paths: paths, 93 | scale: "ms" 94 | }, 95 | { 96 | label: "P50", 97 | stroke: "rgb(2, 88, 115)", 98 | value: (self, rawValue) => safe_to_fixed(scaler(rawValue), 3) + "ms", 99 | fill: "rgb(2, 88, 115, 0.5)", 100 | paths: paths, 101 | scale: "ms" 102 | }, 103 | { 104 | label: "count", 105 | stroke: "rgb(30, 30, 30)", 106 | value: (self, rawValue) => scaler(rawValue) + "cps", 107 | scale: "calls" 108 | } 109 | ], 110 | axes: [ 111 | { 112 | values: [ 113 | [1, "{HH}:{mm}:{ss}", null, null, null, null, null, null, 1],] 114 | }, 115 | { 116 | scale: "ms", 117 | grid: { show: false }, 118 | values: (u, vals, space) => vals.map((val) => safe_to_fixed(scaler(val), 3) + "ms") 119 | }, 120 | { 121 | side: 1, 122 | values: (u, vals, space) => vals.map((val) => safe_to_fixed(scaler(val), 1) + " calls"), 123 | scale: "calls", 124 | grid: { show: false }, 125 | }, 126 | ] 127 | }; 128 | } 129 | 130 | export class HistoChart { 131 | constructor(chartEl, data, scale) { 132 | let opts = make_opts(chartEl, scale) 133 | this.uplotChart = new uPlot(opts, data, chartEl); 134 | } 135 | 136 | updateData(quantile_data, scale) { 137 | this.uplotChart.setData(quantile_data, scale) 138 | } 139 | } 140 | 141 | let scale = ""; 142 | 143 | export const ChartData = { 144 | mounted() { 145 | let chartEl = this.el.parentElement.querySelector('.chart'); 146 | scale = JSON.parse(chartEl.dataset.scale); 147 | let quantile_data = JSON.parse(chartEl.dataset.quantile); 148 | this.chart = new HistoChart(chartEl, quantile_data, scale); 149 | }, 150 | updated() { 151 | let chartEl = this.el.parentElement.querySelector('.chart'); 152 | let new_scale = JSON.parse(chartEl.dataset.scale); 153 | 154 | if (scale == new_scale) { 155 | let quantile_data = JSON.parse(chartEl.dataset.quantile); 156 | this.chart.updateData(quantile_data, scale); 157 | } else { 158 | this.mounted(); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": {}, 3 | "description": " ", 4 | "license": "MIT", 5 | "scripts": {}, 6 | "dependencies": { 7 | "@tailwindcss/postcss": "^4.0.17", 8 | "phoenix": "file:../deps/phoenix", 9 | "phoenix_html": "file:../deps/phoenix_html", 10 | "phoenix_live_view": "file:../deps/phoenix_live_view", 11 | "topbar": "^3.0.0", 12 | "uplot": "^1.1.2" 13 | }, 14 | "devDependencies": { 15 | "@tailwindcss/forms": "^0.5.3", 16 | "autoprefixer": "^10.2.6", 17 | "cssnano": "^7.0.0", 18 | "eslint": "^9.0.0", 19 | "postcss": "^8.1.6", 20 | "postcss-cli": "^11.0.0", 21 | "postcss-preset-env": "^10.0.0", 22 | "prettier": "^3.1.0", 23 | "tailwindcss": "^4.0.17", 24 | "tslib": "^2.4.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /assets/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-preset-env': {}, 4 | "@tailwindcss/postcss": {}, 5 | ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}) 6 | } 7 | } -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # Configures the endpoint 4 | config :orion, OrionWeb.Endpoint, 5 | url: [host: "localhost"], 6 | secret_key_base: "/AlsXZICxnL/Lp3Qo4Z74blNgAB2WCkqwpwrg9pW9kMeAOJ/Efdqi4BB3cuoh4vo", 7 | pubsub_server: Orion.PubSub, 8 | live_view: [signing_salt: "CuaBSTgW"] 9 | 10 | # Configures Elixir's Logger 11 | config :logger, level: :warning 12 | config :logger, :console, format: "[$level] $message\n" 13 | 14 | # Use Jason for JSON parsing in Phoenix 15 | config :phoenix, :json_library, Jason 16 | # Set a higher stacktrace during development. Avoid configuring such 17 | # in production as building large stacktraces may be expensive. 18 | config :phoenix, :stacktrace_depth, 20 19 | 20 | if config_env() == :assets do 21 | config :esbuild, 22 | version: "0.25.1", 23 | default: [ 24 | args: 25 | ~w(js/app.js --bundle --minify --sourcemap=external --target=es2020 --outdir=../dist/js), 26 | cd: Path.expand("../assets", __DIR__), 27 | env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} 28 | ] 29 | else 30 | config :esbuild, 31 | version: "0.25.1", 32 | default: [ 33 | args: ~w(js/app.js --bundle --sourcemap=external --target=es2020 --outdir=../dist/js), 34 | cd: Path.expand("../assets", __DIR__), 35 | env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} 36 | ] 37 | end 38 | -------------------------------------------------------------------------------- /dev.exs: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Development Server for Orion 3 | # 4 | # Options: 5 | # 6 | # Usage: 7 | # 8 | # $ iex -S mix dev [flags] 9 | ####################################### 10 | 11 | Logger.configure(level: :debug) 12 | 13 | argv = System.argv() 14 | {opts, _, _} = OptionParser.parse(argv, strict: [port: :integer]) 15 | options = Map.new(opts) 16 | 17 | Application.put_env(:orion, OrionWeb.Endpoint, 18 | url: [host: "localhost"], 19 | secret_key_base: "/AlsXZICxnL/Lp3Qo4Z74blNgAB2WCkqwpwrg9pW9kMeAOJ/Efdqi4BB3cuoh4vo", 20 | live_view: [signing_salt: "CuaBSTgW"], 21 | http: [port: System.get_env("PORT") || options["port"] || 4001], 22 | debug_errors: true, 23 | check_origin: false, 24 | pubsub_server: Orion.PubSub, 25 | watchers: [ 26 | esbuild: {Esbuild, :install_and_run, [:default, ~w(--watch)]}, 27 | npx: [ 28 | "postcss", 29 | "css/app.css", 30 | "--env=development", 31 | "--output=../dist/css/app.css", 32 | "--watch", 33 | cd: Path.expand("assets", __DIR__) 34 | ] 35 | ], 36 | live_reload: [ 37 | patterns: [ 38 | ~r"dist/.*(js|css|png|jpeg|jpg|gif|svg)$", 39 | ~r"lib/orion_web/.*(ex)$", 40 | ~r"lib/orion_web/layouts/.*(ex)$" 41 | ] 42 | ] 43 | ) 44 | 45 | defmodule OrionDemoWeb.Router do 46 | use Phoenix.Router 47 | import OrionWeb.Router 48 | 49 | @live_orion_prefix "" 50 | 51 | pipeline :browser do 52 | plug :fetch_session 53 | plug :protect_from_forgery 54 | plug :put_csp 55 | end 56 | 57 | scope "/" do 58 | pipe_through :browser 59 | 60 | live_orion("/", 61 | csp_nonce_assign_key: %{ 62 | style: :style_csp_nonce, 63 | script: :script_csp_nonce 64 | } 65 | # ,fake_data: true 66 | ) 67 | end 68 | 69 | defp nonce do 70 | 16 |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false) 71 | end 72 | 73 | def put_csp(conn, _opts) do 74 | style_nonce = nonce() 75 | script_nonce = nonce() 76 | 77 | conn 78 | |> assign(:style_csp_nonce, style_nonce) 79 | |> assign(:script_csp_nonce, script_nonce) 80 | |> put_resp_header( 81 | "content-security-policy", 82 | "default-src; script-src 'nonce-#{script_nonce}'; style-src-elem 'nonce-#{style_nonce}'; " <> 83 | "img-src data: ; font-src data: ; connect-src 'self'; frame-src 'self' ;" 84 | ) 85 | end 86 | end 87 | 88 | defmodule OrionWeb.Endpoint do 89 | use Phoenix.Endpoint, otp_app: :orion 90 | 91 | # The session will be stored in the cookie and signed, 92 | # this means its contents can be read but not tampered with. 93 | # Set :encryption_salt if you would also like to encrypt it. 94 | @session_options [ 95 | store: :cookie, 96 | key: "_orion_key", 97 | signing_salt: "YLC5E6bd" 98 | ] 99 | 100 | socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] 101 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket 102 | 103 | plug Phoenix.LiveReloader 104 | plug Phoenix.CodeReloader 105 | 106 | plug Plug.Session, @session_options 107 | 108 | plug Plug.RequestId 109 | plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] 110 | 111 | plug OrionDemoWeb.Router 112 | end 113 | 114 | Application.ensure_all_started(:orion_collector) 115 | Application.put_env(:phoenix, :serve_endpoints, true) 116 | 117 | Task.start(fn -> 118 | children = [] 119 | 120 | children = 121 | children ++ 122 | [ 123 | {Phoenix.PubSub, [name: Orion.PubSub, adapter: Phoenix.PubSub.PG2]}, 124 | # {Registry, keys: :duplicate, name: Orion.SessionPubsub}, 125 | OrionWeb.Endpoint 126 | ] 127 | 128 | {:ok, _} = Supervisor.start_link(children, strategy: :one_for_one) 129 | Process.sleep(:infinity) 130 | end) 131 | -------------------------------------------------------------------------------- /dist/css/app.css: -------------------------------------------------------------------------------- 1 | /*! tailwindcss v4.0.17 | MIT License | https://tailwindcss.com */ 2 | /*! purgecss start ignore*/.uplot:not(#\#):not(#\#):not(#\#):not(#\#),.uplot:not(#\#):not(#\#):not(#\#):not(#\#) *,.uplot:not(#\#):not(#\#):not(#\#):not(#\#) :after,.uplot:not(#\#):not(#\#):not(#\#):not(#\#) :before{box-sizing:border-box}.uplot:not(#\#):not(#\#):not(#\#):not(#\#){font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;width:-moz-min-content;width:min-content}.u-title:not(#\#):not(#\#):not(#\#):not(#\#){font-size:18px;font-weight:700;text-align:center}.u-wrap:not(#\#):not(#\#):not(#\#):not(#\#){position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.u-over:not(#\#):not(#\#):not(#\#):not(#\#),.u-under:not(#\#):not(#\#):not(#\#):not(#\#){position:absolute}.u-under:not(#\#):not(#\#):not(#\#):not(#\#){overflow:hidden}.uplot:not(#\#):not(#\#):not(#\#):not(#\#) canvas{display:block;height:100%;position:relative;width:100%}.u-axis:not(#\#):not(#\#):not(#\#):not(#\#){position:absolute}.u-legend:not(#\#):not(#\#):not(#\#):not(#\#){font-size:14px;margin:auto;text-align:center}.u-inline:not(#\#):not(#\#):not(#\#):not(#\#){display:block}.u-inline:not(#\#):not(#\#):not(#\#):not(#\#) *{display:inline-block}.u-inline:not(#\#):not(#\#):not(#\#):not(#\#) tr{margin-right:16px}.u-legend:not(#\#):not(#\#):not(#\#):not(#\#) th{font-weight:600}.u-legend:not(#\#):not(#\#):not(#\#):not(#\#) th>*{display:inline-block;vertical-align:middle}.u-legend .u-marker{background-clip:padding-box!important}.u-legend:not(#\#):not(#\#):not(#\#):not(#\#) .u-marker{height:1em;margin-right:4px;width:1em}.u-inline.u-live:not(#\#):not(#\#):not(#\#):not(#\#) th:after{content:":";vertical-align:middle}.u-inline:not(.u-live):not(#\#):not(#\#):not(#\#):not(#\#) .u-value{display:none}.u-series:not(#\#):not(#\#):not(#\#):not(#\#)>*{padding:4px}.u-series:not(#\#):not(#\#):not(#\#):not(#\#) th{cursor:pointer}.u-legend:not(#\#):not(#\#):not(#\#):not(#\#) .u-off>*{opacity:.3}.u-select:not(#\#):not(#\#):not(#\#):not(#\#){background:rgba(0,0,0,.071);pointer-events:none;position:absolute}.u-cursor-x:not(#\#):not(#\#):not(#\#):not(#\#),.u-cursor-y:not(#\#):not(#\#):not(#\#):not(#\#){left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.u-hz:not(#\#):not(#\#):not(#\#):not(#\#) .u-cursor-x,.u-vt:not(#\#):not(#\#):not(#\#):not(#\#) .u-cursor-y{border-right:1px dashed #607d8b;height:100%}.u-hz:not(#\#):not(#\#):not(#\#):not(#\#) .u-cursor-y,.u-vt:not(#\#):not(#\#):not(#\#):not(#\#) .u-cursor-x{border-bottom:1px dashed #607d8b;width:100%}.u-cursor-pt{background-clip:padding-box!important}.u-cursor-pt:not(#\#):not(#\#):not(#\#):not(#\#){border:0 solid;border-radius:50%;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.u-axis.u-off:not(#\#):not(#\#):not(#\#):not(#\#),.u-cursor-pt.u-off:not(#\#):not(#\#):not(#\#):not(#\#),.u-cursor-x.u-off:not(#\#):not(#\#):not(#\#):not(#\#),.u-cursor-y.u-off:not(#\#):not(#\#):not(#\#):not(#\#),.u-select.u-off:not(#\#):not(#\#):not(#\#):not(#\#){display:none}:host,:root{--spacing:.25rem;--breakpoint-xl:80rem;--container-sm:24rem;--text-sm:.875rem;--text-sm--line-height:1.42857;--text-4xl:2.25rem;--text-4xl--line-height:1.11111;--font-weight-medium:500;--font-weight-bold:700;--leading-tight:1.25;--radius-md:.375rem;--radius-lg:.5rem;--container-full:100%;--color-blue-50:#5691f0;--color-dusk-50:#9886cf;--color-dusk-60:#7a68b3;--color-gray-60:#8e99ab;--color-red-30:#fab6b6;--color-red-40:#fa8e8e;--color-red-60:#de1b1b;--color-red-80:#8f0e0e;--color-teal-50:#28a199;--color-teal-60:#17827b;--color-black:#1d1f24;--color-white:#fff}:not(#\#),:not(#\#)::backdrop,:not(#\#):after,:not(#\#):before{border:0 solid;box-sizing:border-box;margin:0;padding:0}:not(#\#)::file-selector-button{border:0 solid;box-sizing:border-box;margin:0;padding:0}:host:not(#\#),html:not(#\#){-webkit-text-size-adjust:100%;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}hr:not(#\#){border-top-width:1px;color:inherit;height:0}abbr:where([title]):not(#\#){-webkit-text-decoration:underline dotted;text-decoration:underline;text-decoration:underline dotted}h1:not(#\#),h2:not(#\#),h3:not(#\#),h4:not(#\#),h5:not(#\#),h6:not(#\#){font-size:inherit;font-weight:inherit}a:not(#\#){color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b:not(#\#),strong:not(#\#){font-weight:bolder}code:not(#\#),kbd:not(#\#),pre:not(#\#),samp:not(#\#){font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small:not(#\#){font-size:80%}sub:not(#\#),sup:not(#\#){font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub:not(#\#){bottom:-.25em}sup:not(#\#){top:-.5em}table:not(#\#){border-collapse:collapse;border-color:inherit;text-indent:0}:-moz-focusring:not(#\#){outline:auto}progress:not(#\#){vertical-align:baseline}summary:not(#\#){display:list-item}menu:not(#\#),ol:not(#\#),ul:not(#\#){list-style:none}audio:not(#\#),canvas:not(#\#),embed:not(#\#),iframe:not(#\#),img:not(#\#),object:not(#\#),svg:not(#\#),video:not(#\#){display:block;vertical-align:middle}img:not(#\#),video:not(#\#){height:auto;max-width:100%}button:not(#\#),input:not(#\#),optgroup:not(#\#),select:not(#\#),textarea:not(#\#){background-color:transparent;border-radius:0;color:inherit;font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;opacity:1}:not(#\#)::file-selector-button{background-color:transparent;border-radius:0;color:inherit;font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;opacity:1}:where(select[multiple]):not(#\#) optgroup{font-weight:bolder}:where(select[size]):not(#\#) optgroup{font-weight:bolder}:where(select[multiple]):not(#\#) optgroup option{padding-left:20px}:where(select[size]):not(#\#) optgroup option{padding-left:20px}:not(#\#)::file-selector-button{margin-right:4px}:not(#\#)::-moz-placeholder{opacity:1}:not(#\#)::placeholder{opacity:1}@supports (not (-webkit-appearance:-apple-pay-button)) or (contain-intrinsic-size:1px){:not(#\#)::-moz-placeholder{color:color-mix(in oklab,currentColor 50%,transparent)}:not(#\#)::placeholder{color:color-mix(in oklab,currentColor 50%,transparent)}}textarea:not(#\#){resize:vertical}:not(#\#)::-webkit-search-decoration{-webkit-appearance:none}:not(#\#)::-webkit-date-and-time-value{min-height:1lh}:-moz-ui-invalid:not(#\#){box-shadow:none}button:not(#\#),input:where([type=button],[type=reset],[type=submit]):not(#\#){-webkit-appearance:button;-moz-appearance:button;appearance:button}:not(#\#)::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}:not(#\#)::-webkit-inner-spin-button,:not(#\#)::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])):not(#\#):not(#\#):not(#\#){display:none!important}dark:not(#\#){--color-blue-100:#f0f5fc;--color-blue-90:#cfe0fc;--color-blue-80:#accbfc;--color-blue-70:#84b1fa;--color-blue-60:#5691f0;--color-blue-50:#3272d9;--color-blue-40:#1d5bbf;--color-blue-30:#114599;--color-blue-20:#103570;--color-blue-10:#15233b;--color-bronze-100:#fcf2e6;--color-bronze-90:#fad8af;--color-bronze-80:#f5bc76;--color-bronze-70:#e89c3f;--color-bronze-60:#cf7911;--color-bronze-50:#ad5f00;--color-bronze-40:#8a4d03;--color-bronze-30:#693d07;--color-bronze-20:#4d2f0b;--color-bronze-10:#33210c;--color-dusk-100:#f4f2f7;--color-dusk-90:#e3dcf7;--color-dusk-80:#cec2f0;--color-dusk-70:#b5a6e3;--color-dusk-60:#9886cf;--color-dusk-50:#7a68b3;--color-dusk-40:#645396;--color-dusk-30:#4f4178;--color-dusk-20:#3a3154;--color-dusk-10:#282436;--color-gray-100:#f5f7fa;--color-gray-90:#ebeff5;--color-gray-80:#dde3ed;--color-gray-70:#c8d1e0;--color-gray-60:#afbacc;--color-gray-50:#8e99ab;--color-gray-40:#707a8a;--color-gray-30:#58606e;--color-gray-20:#434a54;--color-gray-10:#333840;--color-green-100:#e1faeb;--color-green-90:#abedc5;--color-green-80:#7ddba3;--color-green-70:#57c282;--color-green-60:#3ba164;--color-green-50:#2a854e;--color-green-40:#20693d;--color-green-30:#1a5230;--color-green-20:#153d25;--color-green-10:#112b1b;--color-indigo-100:#f2f2fc;--color-indigo-90:#dcdcfc;--color-indigo-80:#c2c2fc;--color-indigo-70:#a7a7fa;--color-indigo-60:#8585f2;--color-indigo-50:#6767e6;--color-indigo-40:#4d4dd1;--color-indigo-30:#3737b3;--color-indigo-20:#28288a;--color-indigo-10:#202057;--color-magenta-100:#faf0f4;--color-magenta-90:#fad4e4;--color-magenta-80:#fab4d1;--color-magenta-70:#f78bb8;--color-magenta-60:#ed5393;--color-magenta-50:#d6246e;--color-magenta-40:#b01355;--color-magenta-30:#8a1244;--color-magenta-20:#611535;--color-magenta-10:#421527;--color-purple-100:#f5f0fa;--color-purple-90:#ead9fa;--color-purple-80:#dabcf7;--color-purple-70:#c79bf2;--color-purple-60:#ae74e8;--color-purple-50:#9656d6;--color-purple-40:#7d3cbd;--color-purple-30:#642b9e;--color-purple-20:#4b2175;--color-purple-10:#371c52;--color-red-100:#faf0f0;--color-red-90:#fad4d4;--color-red-80:#fab6b6;--color-red-70:#fa8e8e;--color-red-60:#f55353;--color-red-50:#de1b1b;--color-red-40:#b80d0d;--color-red-30:#8f0e0e;--color-red-20:#661414;--color-red-10:#451717;--color-sky-100:#e8f4fa;--color-sky-90:#bbe5fa;--color-sky-80:#8dd4f7;--color-sky-70:#53baed;--color-sky-60:#229ad6;--color-sky-50:#0c7bb3;--color-sky-40:#066391;--color-sky-30:#064d70;--color-sky-20:#093952;--color-sky-10:#0c2938;--color-slate-100:#edf4f7;--color-slate-90:#cbe3f5;--color-slate-80:#a7ceeb;--color-slate-70:#84b7db;--color-slate-60:#5e95bd;--color-slate-50:#48799c;--color-slate-40:#376180;--color-slate-30:#2d4d63;--color-slate-20:#243947;--color-slate-10:#1d2830;--color-teal-100:#e4f7f6;--color-teal-90:#a8ede9;--color-teal-80:#6cd9d2;--color-teal-70:#45bfb7;--color-teal-60:#28a199;--color-teal-50:#17827b;--color-teal-40:#116963;--color-teal-30:#0d524d;--color-teal-20:#0b3d3a;--color-teal-10:#092b29;--color-white:#1d1f24;--color-black:#fff;--color-transparent:transparent}[multiple]:not(#\#),[type=date]:not(#\#),[type=datetime-local]:not(#\#),[type=email]:not(#\#),[type=month]:not(#\#),[type=number]:not(#\#),[type=password]:not(#\#),[type=search]:not(#\#),[type=tel]:not(#\#),[type=text]:not(#\#),[type=time]:not(#\#),[type=url]:not(#\#),[type=week]:not(#\#),input:where(:not([type])):not(#\#),select:not(#\#),textarea:not(#\#){-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:#6a7282;--tw-shadow:0 0 transparent;background-color:#fff;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem}input:where(:not([type])):not(.does-not-exist):focus:not(#\#){outline-offset:2px;--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#155dfc;--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color);border-color:#155dfc;box-shadow:0 0 0 2px#fff,0 0 0 1px#155dfc,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid transparent}[multiple]:focus:not(#\#),[type=date]:focus:not(#\#),[type=datetime-local]:focus:not(#\#),[type=email]:focus:not(#\#),[type=month]:focus:not(#\#),[type=number]:focus:not(#\#),[type=password]:focus:not(#\#),[type=search]:focus:not(#\#),[type=tel]:focus:not(#\#),[type=text]:focus:not(#\#),[type=time]:focus:not(#\#),[type=url]:focus:not(#\#),[type=week]:focus:not(#\#),select:not(.does-not-exist):focus:not(#\#),textarea:not(.does-not-exist):focus:not(#\#){outline-offset:2px;--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#155dfc;--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color);border-color:#155dfc;box-shadow:0 0 0 2px#fff,0 0 0 1px#155dfc,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid transparent}input:not(#\#)::-moz-placeholder,textarea:not(#\#)::-moz-placeholder{color:#6a7282;opacity:1}input:not(#\#)::placeholder,textarea:not(#\#)::placeholder{color:#6a7282;opacity:1}:not(#\#)::-webkit-datetime-edit-fields-wrapper{padding:0}:not(#\#)::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}:not(#\#)::-webkit-datetime-edit{display:inline-flex;padding-bottom:0;padding-top:0}:not(#\#)::-webkit-datetime-edit-month-field,:not(#\#)::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}:not(#\#)::-webkit-datetime-edit-day-field,:not(#\#)::-webkit-datetime-edit-hour-field{padding-bottom:0;padding-top:0}:not(#\#)::-webkit-datetime-edit-minute-field,:not(#\#)::-webkit-datetime-edit-second-field{padding-bottom:0;padding-top:0}:not(#\#)::-webkit-datetime-edit-meridiem-field,:not(#\#)::-webkit-datetime-edit-millisecond-field{padding-bottom:0;padding-top:0}select:not(#\#){background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='oklch(0.551 0.027 264.364)' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]:not(#\#),[size]:where(select:not([size="1"])):not(#\#){background-image:none;background-position:0 0;background-repeat:repeat;background-size:auto auto;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:inherit;print-color-adjust:inherit}[type=checkbox]:not(#\#),[type=radio]:not(#\#){-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:#6a7282;color:#155dfc;height:1rem;-webkit-print-color-adjust:exact;print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:1rem;--tw-shadow:0 0 transparent;background-color:#fff;background-origin:border-box;border-width:1px;display:inline-block;flex-shrink:0;padding:0}[type=checkbox]:not(#\#){border-radius:0}[type=radio]:not(#\#){border-radius:100%}[type=checkbox]:focus:not(#\#),[type=radio]:focus:not(#\#){outline-offset:2px;--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#155dfc;--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:0 0 0 2px#fff,0 0 0 4px#155dfc,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid transparent}[type=checkbox]:checked:not(#\#),[type=radio]:checked:not(#\#){background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:checked:not(#\#){background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=checkbox]:checked:not(#\#){-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked:not(#\#){background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=radio]:checked:not(#\#){-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus:not(#\#),[type=checkbox]:checked:hover:not(#\#),[type=radio]:checked:focus:not(#\#),[type=radio]:checked:hover:not(#\#){background-color:currentColor;border-color:transparent}[type=checkbox]:indeterminate:not(#\#){background-color:currentColor;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}@media (forced-colors:active){[type=checkbox]:indeterminate:not(#\#){-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus:not(#\#),[type=checkbox]:indeterminate:hover:not(#\#){background-color:currentColor;border-color:transparent}[type=file]:not(#\#){background:transparent none repeat 0 0/auto auto padding-box border-box scroll;background:initial;border-color:inherit;border-radius:0;border-width:0;font-size:inherit;line-height:inherit;padding:0}[type=file]:focus:not(#\#){outline:1px solid buttontext;outline:1px auto -webkit-focus-ring-color}.static:not(#\#):not(#\#):not(#\#){position:static}.float-left:not(#\#):not(#\#):not(#\#){float:left}.m-4:not(#\#):not(#\#):not(#\#){margin:calc(var(--spacing)*4)}.m-8:not(#\#):not(#\#):not(#\#){margin:calc(var(--spacing)*8)}.mx-1:not(#\#):not(#\#):not(#\#){margin-left:calc(var(--spacing)*1);margin-right:calc(var(--spacing)*1)}.mx-2:not(#\#):not(#\#):not(#\#){margin-left:calc(var(--spacing)*2);margin-right:calc(var(--spacing)*2)}.mx-4:not(#\#):not(#\#):not(#\#){margin-left:calc(var(--spacing)*4);margin-right:calc(var(--spacing)*4)}.mx-auto:not(#\#):not(#\#):not(#\#){margin-left:auto;margin-right:auto}.my-2:not(#\#):not(#\#):not(#\#){margin-bottom:calc(var(--spacing)*2);margin-top:calc(var(--spacing)*2)}.my-3:not(#\#):not(#\#):not(#\#){margin-bottom:calc(var(--spacing)*3);margin-top:calc(var(--spacing)*3)}.mt-1:not(#\#):not(#\#):not(#\#){margin-top:calc(var(--spacing)*1)}.mt-3:not(#\#):not(#\#):not(#\#){margin-top:calc(var(--spacing)*3)}.mt-4:not(#\#):not(#\#):not(#\#){margin-top:calc(var(--spacing)*4)}.mr-3:not(#\#):not(#\#):not(#\#){margin-right:calc(var(--spacing)*3)}.mb-3:not(#\#):not(#\#):not(#\#){margin-bottom:calc(var(--spacing)*3)}.mb-4:not(#\#):not(#\#):not(#\#){margin-bottom:calc(var(--spacing)*4)}.block:not(#\#):not(#\#):not(#\#){display:block}.flex:not(#\#):not(#\#):not(#\#){display:flex}.grid:not(#\#):not(#\#):not(#\#){display:grid}.hidden:not(#\#):not(#\#):not(#\#){display:none}.h-full:not(#\#):not(#\#):not(#\#){height:100%}.w-11\/12:not(#\#):not(#\#):not(#\#){width:91.6667%}.w-30:not(#\#):not(#\#):not(#\#){width:calc(var(--spacing)*30)}.w-full:not(#\#):not(#\#):not(#\#){width:100%;width:var(--container-full)}.max-w-screen-xl:not(#\#):not(#\#):not(#\#){max-width:var(--breakpoint-xl)}.min-w-sm:not(#\#):not(#\#):not(#\#){min-width:var(--container-sm)}.flex-grow:not(#\#):not(#\#):not(#\#),.grow:not(#\#):not(#\#):not(#\#){flex-grow:1}.flex-col:not(#\#):not(#\#):not(#\#){flex-direction:column}.flex-row:not(#\#):not(#\#):not(#\#){flex-direction:row}.flex-wrap:not(#\#):not(#\#):not(#\#){flex-wrap:wrap}.items-center:not(#\#):not(#\#):not(#\#){align-items:center}.justify-between:not(#\#):not(#\#):not(#\#){justify-content:space-between}.justify-center:not(#\#):not(#\#):not(#\#){justify-content:center}.justify-evenly:not(#\#):not(#\#):not(#\#){justify-content:space-evenly}.gap-3:not(#\#):not(#\#):not(#\#){gap:calc(var(--spacing)*3)}.self-end:not(#\#):not(#\#):not(#\#){align-self:flex-end}.self-stretch:not(#\#):not(#\#):not(#\#){align-self:stretch}.rounded:not(#\#):not(#\#):not(#\#){border-radius:.25rem}.rounded-lg:not(#\#):not(#\#):not(#\#){border-radius:var(--radius-lg)}.rounded-md:not(#\#):not(#\#):not(#\#){border-radius:var(--radius-md)}.border:not(#\#):not(#\#):not(#\#){border-style:var(--tw-border-style);border-width:1px}.border-2:not(#\#):not(#\#):not(#\#){border-style:var(--tw-border-style);border-width:2px}.border-gray-60:not(#\#):not(#\#):not(#\#){border-color:var(--color-gray-60)}.border-red-40:not(#\#):not(#\#):not(#\#){border-color:var(--color-red-40)}.bg-dusk-60:not(#\#):not(#\#):not(#\#){background-color:var(--color-dusk-60)}.bg-red-30:not(#\#):not(#\#):not(#\#){background-color:var(--color-red-30)}.bg-teal-60:not(#\#):not(#\#):not(#\#){background-color:var(--color-teal-60)}.bg-white:not(#\#):not(#\#):not(#\#){background-color:var(--color-white)}.px-1:not(#\#):not(#\#):not(#\#){padding-left:calc(var(--spacing)*1);padding-right:calc(var(--spacing)*1)}.px-2:not(#\#):not(#\#):not(#\#){padding-left:calc(var(--spacing)*2);padding-right:calc(var(--spacing)*2)}.px-3:not(#\#):not(#\#):not(#\#){padding-left:calc(var(--spacing)*3);padding-right:calc(var(--spacing)*3)}.px-4:not(#\#):not(#\#):not(#\#){padding-left:calc(var(--spacing)*4);padding-right:calc(var(--spacing)*4)}.py-2:not(#\#):not(#\#):not(#\#){padding-bottom:calc(var(--spacing)*2);padding-top:calc(var(--spacing)*2)}.py-3:not(#\#):not(#\#):not(#\#){padding-bottom:calc(var(--spacing)*3);padding-top:calc(var(--spacing)*3)}.pt-4:not(#\#):not(#\#):not(#\#){padding-top:calc(var(--spacing)*4)}.pb-4:not(#\#):not(#\#):not(#\#){padding-bottom:calc(var(--spacing)*4)}.text-center:not(#\#):not(#\#):not(#\#){text-align:center}.text-4xl:not(#\#):not(#\#):not(#\#){font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-sm:not(#\#):not(#\#):not(#\#){font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.leading-6:not(#\#):not(#\#):not(#\#){--tw-leading:calc(var(--spacing)*6);line-height:calc(var(--spacing)*6)}.leading-tight:not(#\#):not(#\#):not(#\#){--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold:not(#\#):not(#\#):not(#\#){--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium:not(#\#):not(#\#):not(#\#){--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.text-black:not(#\#):not(#\#):not(#\#){color:var(--color-black)}.text-red-60:not(#\#):not(#\#):not(#\#){color:var(--color-red-60)}.text-white:not(#\#):not(#\#):not(#\#){color:var(--color-white)}.shadow-sm:not(#\#):not(#\#):not(#\#){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,rgba(0,0,0,.102)),0 1px 2px -1px var(--tw-shadow-color,rgba(0,0,0,.102));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 1px 3px 0 rgba(0,0,0,.102),0 1px 2px -1px rgba(0,0,0,.102);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media (hover:hover){.hover\:border-teal-50:hover:not(#\#):not(#\#):not(#\#){border-color:var(--color-teal-50)}.hover\:bg-dusk-50:hover:not(#\#):not(#\#):not(#\#){background-color:var(--color-dusk-50)}.hover\:bg-red-80:hover:not(#\#):not(#\#):not(#\#){background-color:var(--color-red-80)}.hover\:text-black:hover:not(#\#):not(#\#):not(#\#){color:var(--color-black)}.hover\:text-white:hover:not(#\#):not(#\#):not(#\#){color:var(--color-white)}}.focus\:border-blue-50:focus:not(#\#):not(#\#):not(#\#){border-color:var(--color-blue-50)}.focus\:border-red-40:focus:not(#\#):not(#\#):not(#\#){border-color:var(--color-red-40)}.focus\:bg-red-80:focus:not(#\#):not(#\#):not(#\#){background-color:var(--color-red-80)}.focus\:text-white:focus:not(#\#):not(#\#):not(#\#){color:var(--color-white)}.focus\:ring-0:focus:not(#\#):not(#\#):not(#\#){--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(var(--tw-ring-offset-width))var(--tw-ring-color,currentColor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:outline-none:focus:not(#\#):not(#\#):not(#\#){--tw-outline-style:none;outline-style:none}@media (min-width:40rem){.sm\:text-sm:not(#\#):not(#\#):not(#\#){font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}[data-phx-root-id]:not(#\#):not(#\#):not(#\#):not(#\#){display:contents}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 transparent}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 transparent}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 transparent}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 transparent}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 transparent} -------------------------------------------------------------------------------- /dist/js/app.js: -------------------------------------------------------------------------------- 1 | (()=>{var ar=Object.create;var to=Object.defineProperty;var ur=Object.getOwnPropertyDescriptor;var cr=Object.getOwnPropertyNames;var hr=Object.getPrototypeOf,pr=Object.prototype.hasOwnProperty;var dr=(e,l)=>()=>(l||e((l={exports:{}}).exports,l),l.exports);var mr=(e,l,s,n)=>{if(l&&typeof l=="object"||typeof l=="function")for(let o of cr(l))!pr.call(e,o)&&o!==s&&to(e,o,{get:()=>l[o],enumerable:!(n=ur(l,o))||n.enumerable});return e};var gr=(e,l,s)=>(s=e!=null?ar(hr(e)):{},mr(l||!e||!e.__esModule?to(s,"default",{value:e,enumerable:!0}):s,e));var no=dr((lo,on)=>{(function(e,l){"use strict";function s(){n.width=e.innerWidth,n.height=5*d.barThickness;var p=n.getContext("2d");p.shadowBlur=d.shadowBlur,p.shadowColor=d.shadowColor;var w,k=p.createLinearGradient(0,0,n.width,0);for(w in d.barColors)k.addColorStop(w,d.barColors[w]);p.lineWidth=d.barThickness,p.beginPath(),p.moveTo(0,d.barThickness/2),p.lineTo(Math.ceil(o*n.width),d.barThickness/2),p.strokeStyle=k,p.stroke()}var n,o,a,u=null,h=null,m=null,d={autoRun:!0,barThickness:3,barColors:{0:"rgba(26, 188, 156, .9)",".25":"rgba(52, 152, 219, .9)",".50":"rgba(241, 196, 15, .9)",".75":"rgba(230, 126, 34, .9)","1.0":"rgba(211, 84, 0, .9)"},shadowBlur:10,shadowColor:"rgba(0, 0, 0, .6)",className:null},_={config:function(p){for(var w in p)d.hasOwnProperty(w)&&(d[w]=p[w])},show:function(p){var w,k;a||(p?m=m||setTimeout(()=>_.show(),p):(a=!0,h!==null&&e.cancelAnimationFrame(h),n||((k=(n=l.createElement("canvas")).style).position="fixed",k.top=k.left=k.right=k.margin=k.padding=0,k.zIndex=100001,k.display="none",d.className&&n.classList.add(d.className),w="resize",p=s,(k=e).addEventListener?k.addEventListener(w,p,!1):k.attachEvent?k.attachEvent("on"+w,p):k["on"+w]=p),n.parentElement||l.body.appendChild(n),n.style.opacity=1,n.style.display="block",_.progress(0),d.autoRun&&function P(){u=e.requestAnimationFrame(P),_.progress("+"+.05*Math.pow(1-Math.sqrt(o),2))}()))},progress:function(p){return p===void 0||(typeof p=="string"&&(p=(0<=p.indexOf("+")||0<=p.indexOf("-")?o:0)+parseFloat(p)),o=1n||s>o?et(e,Qt):ti(e,Qt))}var po=new WeakMap;function mo(e,l,s){let n=l+s,o=po.get(e);n!=o&&(po.set(e,n),e.style.background=l,e.style.borderColor=s)}var go=new WeakMap;function wo(e,l,s,n){let o=l+""+s,a=go.get(e);o!=a&&(go.set(e,o),e.style.height=s+"px",e.style.width=l+"px",e.style.marginLeft=n?-l/2+"px":0,e.style.marginTop=n?-s/2+"px":0)}var ci={passive:!0},Or={...ci,capture:!0};function Xt(e,l,s,n){l.addEventListener(e,s,n?Or:ci)}function li(e,l,s,n){l.removeEventListener(e,s,ci)}El&&ei();function wt(e,l,s,n){let o;s=s||0,n=n||l.length-1;let a=n<=2147483647;for(;n-s>1;)o=a?s+n>>1:tt((s+n)/2),l[o]{let a=-1,u=-1;for(let h=n;h<=o;h++)if(e(s[h])){a=h;break}for(let h=o;h>=n;h--)if(e(s[h])){u=h;break}return[a,u]}}var qo=e=>e!=null,Ko=e=>e!=null&&e>0,pn=Uo(qo),Hr=Uo(Ko);function Wr(e,l,s,n=0,o=!1){let a=o?Hr:pn,u=o?Ko:qo;[l,s]=a(e,l,s);let h=e[l],m=e[l];if(l>-1)if(n==1)h=e[l],m=e[s];else if(n==-1)h=e[s],m=e[l];else for(let d=l;d<=s;d++){let _=e[d];u(_)&&(_m&&(m=_))}return[h??ue,m??-ue]}function dn(e,l,s,n){let o=yo(e),a=yo(l);e==l&&(o==-1?(e*=s,l/=s):(e/=s,l*=s));let u=s==10?Dt:jo,h=o==1?tt:ut,m=a==1?ut:tt,d=h(u(Ee(e))),_=m(u(Ee(l))),p=Tl(s,d),w=Tl(s,_);return s==10&&(d<0&&(p=ce(p,-d)),_<0&&(w=ce(w,-_))),n||s==2?(e=p*o,l=w*a):(e=Qo(e,p),l=mn(l,w)),[e,l]}function hi(e,l,s,n){let o=dn(e,l,s,n);return e==0&&(o[0]=0),l==0&&(o[1]=0),o}var pi=.1,So={mode:3,pad:pi},Bl={pad:0,soft:null,mode:0},Gr={min:Bl,max:Bl};function cn(e,l,s,n){return gn(s)?_o(e,l,s):(Bl.pad=s,Bl.soft=n?0:null,Bl.mode=n?3:0,_o(e,l,Gr))}function ne(e,l){return e??l}function Ir(e,l,s){for(l=ne(l,0),s=ne(s,e.length-1);l<=s;){if(e[l]!=null)return!0;l++}return!1}function _o(e,l,s){let n=s.min,o=s.max,a=ne(n.pad,0),u=ne(o.pad,0),h=ne(n.hard,-ue),m=ne(o.hard,ue),d=ne(n.soft,ue),_=ne(o.soft,-ue),p=ne(n.mode,0),w=ne(o.mode,0),k=l-e,P=Dt(k),C=Ue(Ee(e),Ee(l)),H=Dt(C),W=Ee(H-P);(k<1e-24||W>10)&&(k=0,(e==0||l==0)&&(k=1e-24,p==2&&d!=ue&&(a=0),w==2&&_!=-ue&&(u=0)));let v=k||C||1e3,z=Dt(v),T=Tl(10,tt(z)),j=v*(k==0?e==0?.1:1:a),L=ce(Qo(e-j,T/10),24),J=e>=d&&(p==1||p==3&&L<=d||p==2&&L>=d)?d:ue,U=Ue(h,L=J?J:St(J,L)),Q=v*(k==0?l==0?.1:1:u),B=ce(mn(l+Q,T/10),24),x=l<=_&&(w==1||w==3&&B>=_||w==2&&B<=_)?_:-ue,K=St(m,B>x&&l<=x?x:Ue(x,B));return U==K&&U==0&&(K=100),[U,K]}var Yr=new Intl.NumberFormat(El?Nr.language:"en-US"),di=e=>Yr.format(e),lt=Math,an=lt.PI,Ee=lt.abs,tt=lt.floor,Ae=lt.round,ut=lt.ceil,St=lt.min,Ue=lt.max,Tl=lt.pow,yo=lt.sign,Dt=lt.log10,jo=lt.log2,Br=(e,l=1)=>lt.sinh(e)*l,Jn=(e,l=1)=>lt.asinh(e/l),ue=1/0;function vo(e){return(Dt((e^e>>31)-(e>>31))|0)+1}function ni(e,l,s){return St(Ue(e,l),s)}function Jo(e){return typeof e=="function"}function X(e){return Jo(e)?e:()=>e}var Ur=()=>{},$o=e=>e,Zo=(e,l)=>l,qr=e=>null,bo=e=>!0,ko=(e,l)=>e==l,Kr=/\.\d*?(?=9{6,}|0{6,})/gm,el=e=>{if(es(e)||Ht.has(e))return e;let l=`${e}`,s=l.match(Kr);if(s==null)return e;let n=s[0].length-1;if(l.indexOf("e-")!=-1){let[o,a]=l.split("e");return+`${el(o)}e${a}`}return ce(e,n)};function $t(e,l){return el(ce(el(e/l))*l)}function mn(e,l){return el(ut(el(e/l))*l)}function Qo(e,l){return el(tt(el(e/l))*l)}function ce(e,l=0){if(es(e))return e;let s=10**l,n=e*s*(1+Number.EPSILON);return Ae(n)/s}var Ht=new Map;function Xo(e){return((""+e).split(".")[1]||"").length}function ql(e,l,s,n){let o=[],a=n.map(Xo);for(let u=l;u=0?0:h)+(u>=a[d]?0:a[d]),w=e==10?_:ce(_,p);o.push(w),Ht.set(w,p)}}return o}var Ul={},mi=[],xl=[null,null],Ot=Array.isArray,es=Number.isInteger,jr=e=>e===void 0;function To(e){return typeof e=="string"}function gn(e){let l=!1;if(e!=null){let s=e.constructor;l=s==null||s==Object}return l}function Jr(e){return e!=null&&typeof e=="object"}var $r=Object.getPrototypeOf(Uint8Array),ts="__proto__";function Ml(e,l=gn){let s;if(Ot(e)){let n=e.find(o=>o!=null);if(Ot(n)||l(n)){s=Array(e.length);for(let o=0;oa){for(o=u-1;o>=0&&e[o]==null;)e[o--]=null;for(o=u+1;ou-h)],o=n[0].length,a=new Map;for(let u=0;u"u"?e=>Promise.resolve().then(e):queueMicrotask;function nf(e){let l=e[0],s=l.length,n=Array(s);for(let a=0;al[a]-l[u]);let o=[];for(let a=0;a=n&&e[o]==null;)o--;if(o<=n)return!0;let a=Ue(1,tt((o-n+1)/l));for(let u=e[n],h=n+a;h<=o;h+=a){let m=e[h];if(m!=null){if(m<=u)return!1;u=m}}return!0}var ls=["January","February","March","April","May","June","July","August","September","October","November","December"],ns=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function is(e){return e.slice(0,3)}var rf=ns.map(is),ff=ls.map(is),af={MMMM:ls,MMM:ff,WWWW:ns,WWW:rf};function Wl(e){return(e<10?"0":"")+e}function uf(e){return(e<10?"00":e<100?"0":"")+e}var cf={YYYY:e=>e.getFullYear(),YY:e=>(e.getFullYear()+"").slice(2),MMMM:(e,l)=>l.MMMM[e.getMonth()],MMM:(e,l)=>l.MMM[e.getMonth()],MM:e=>Wl(e.getMonth()+1),M:e=>e.getMonth()+1,DD:e=>Wl(e.getDate()),D:e=>e.getDate(),WWWW:(e,l)=>l.WWWW[e.getDay()],WWW:(e,l)=>l.WWW[e.getDay()],HH:e=>Wl(e.getHours()),H:e=>e.getHours(),h:e=>{let l=e.getHours();return l==0?12:l>12?l-12:l},AA:e=>e.getHours()>=12?"PM":"AM",aa:e=>e.getHours()>=12?"pm":"am",a:e=>e.getHours()>=12?"p":"a",mm:e=>Wl(e.getMinutes()),m:e=>e.getMinutes(),ss:e=>Wl(e.getSeconds()),s:e=>e.getSeconds(),fff:e=>uf(e.getMilliseconds())};function gi(e,l){l=l||af;let s=[],n=/\{([a-z]+)\}|[^{]+/gi,o;for(;o=n.exec(e);)s.push(o[0][0]=="{"?cf[o[1]]:o[0]);return a=>{let u="";for(let h=0;he%1==0,hn=[1,2,2.5,5],df=ql(10,-32,0,hn),ss=ql(10,0,32,hn),mf=ss.filter(os),Zt=df.concat(ss),wi=` 2 | `,rs="{YYYY}",xo=wi+rs,fs="{M}/{D}",Yl=wi+fs,rn=Yl+"/{YY}",as="{aa}",gf="{h}:{mm}",bl=gf+as,Mo=wi+bl,Ao=":{ss}",re=null;function us(e){let l=e*1e3,s=l*60,n=s*60,o=n*24,a=o*30,u=o*365,m=(e==1?ql(10,0,3,hn).filter(os):ql(10,-3,0,hn)).concat([l,l*5,l*10,l*15,l*30,s,s*5,s*10,s*15,s*30,n,n*2,n*3,n*4,n*6,n*8,n*12,o,o*2,o*3,o*4,o*5,o*6,o*7,o*8,o*9,o*10,o*15,a,a*2,a*3,a*4,a*6,u,u*2,u*5,u*10,u*25,u*50,u*100]),d=[[u,rs,re,re,re,re,re,re,1],[o*28,"{MMM}",xo,re,re,re,re,re,1],[o,fs,xo,re,re,re,re,re,1],[n,"{h}"+as,rn,re,Yl,re,re,re,1],[s,bl,rn,re,Yl,re,re,re,1],[l,Ao,rn+" "+bl,re,Yl+" "+bl,re,Mo,re,1],[e,Ao+".{fff}",rn+" "+bl,re,Yl+" "+bl,re,Mo,re,1]];function _(p){return(w,k,P,C,H,W)=>{let v=[],z=H>=u,T=H>=a&&H=o?o:H,B=tt(P)-tt(L),x=U+B+mn(L-U,Q);v.push(x);let K=p(x),F=K.getHours()+K.getMinutes()/s+K.getSeconds()/n,I=H/n,O=w.axes[k]._space,ee=W/O;for(;x=ce(x+H,e==1?0:3),!(x>C);)if(I>1){let N=tt(ce(F+I,6))%24,te=p(x).getHours()-N;te>1&&(te=-1),x-=te*n,F=(F+I)%24;let fe=v[v.length-1];ce((x-fe)/H,3)*ee>=.7&&v.push(x)}else v.push(x)}return v}}return[m,d,_]}var[wf,Sf,_f]=us(1),[yf,vf,bf]=us(.001);ql(2,-53,53,[1]);function Eo(e,l){return e.map(s=>s.map((n,o)=>o==0||o==8||n==null?n:l(o==1||s[8]==0?n:s[1]+n)))}function Po(e,l){return(s,n,o,a,u)=>{let h=l.find(P=>u>=P[0])||l[l.length-1],m,d,_,p,w,k;return n.map(P=>{let C=e(P),H=C.getFullYear(),W=C.getMonth(),v=C.getDate(),z=C.getHours(),T=C.getMinutes(),j=C.getSeconds(),L=H!=m&&h[2]||W!=d&&h[3]||v!=_&&h[4]||z!=p&&h[5]||T!=w&&h[6]||j!=k&&h[7]||h[1];return m=H,d=W,_=v,p=z,w=T,k=j,L(C)})}}function kf(e,l){let s=gi(l);return(n,o,a,u,h)=>o.map(m=>s(e(m)))}function $n(e,l,s){return new Date(e,l,s)}function Do(e,l){return l(e)}var Tf="{YYYY}-{MM}-{DD} {h}:{mm}{aa}";function Lo(e,l){return(s,n,o,a)=>a==null?ui:l(e(n))}function xf(e,l){let s=e.series[l];return s.width?s.stroke(e,l):s.points.width?s.points.stroke(e,l):null}function Mf(e,l){return e.series[l].fill(e,l)}var Af={show:!0,live:!0,isolate:!1,mount:Ur,markers:{show:!0,width:2,stroke:xf,fill:Mf,dash:"solid"},idx:null,idxs:null,values:[]};function Ef(e,l){let s=e.cursor.points,n=at(),o=s.size(e,l);ge(n,Gl,o),ge(n,Il,o);let a=o/-2;ge(n,"marginLeft",a),ge(n,"marginTop",a);let u=s.width(e,l,o);return u&&ge(n,"borderWidth",u),n}function Pf(e,l){let s=e.series[l].points;return s._fill||s._stroke}function Df(e,l){let s=e.series[l].points;return s._stroke||s._fill}function Lf(e,l){return e.series[l].points.size}var Zn=[0,0];function Cf(e,l,s){return Zn[0]=l,Zn[1]=s,Zn}function fn(e,l,s,n=!0){return o=>{o.button==0&&(!n||o.target==l)&&s(o)}}function Qn(e,l,s,n=!0){return o=>{(!n||o.target==l)&&s(o)}}var Ff={show:!0,x:!0,y:!0,lock:!1,move:Cf,points:{one:!1,show:Ef,size:Lf,width:0,stroke:Df,fill:Pf},bind:{mousedown:fn,mouseup:fn,click:fn,dblclick:fn,mousemove:Qn,mouseleave:Qn,mouseenter:Qn},drag:{setScale:!0,x:!0,y:!1,dist:0,uni:null,click:(e,l)=>{l.stopPropagation(),l.stopImmediatePropagation()},_x:!1,_y:!1},focus:{dist:(e,l,s,n,o)=>n-o,prox:-1,bias:0},hover:{skip:[void 0],prox:null,bias:0},left:-10,top:-10,idx:null,dataIdx:null,idxs:null,event:null},cs={show:!0,stroke:"rgba(0,0,0,0.07)",width:2},Si=Te({},cs,{filter:Zo}),hs=Te({},Si,{size:10}),ps=Te({},cs,{show:!1}),_i='12px system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',ds="bold "+_i,ms=1.5,Co={show:!0,scale:"x",stroke:ai,space:50,gap:5,alignTo:1,size:50,labelGap:0,labelSize:30,labelFont:ds,side:2,grid:Si,ticks:hs,border:ps,font:_i,lineGap:ms,rotate:0},Vf="Value",Rf="Time",Fo={show:!0,scale:"x",auto:!1,sorted:1,min:ue,max:-ue,idxs:[]};function zf(e,l,s,n,o){return l.map(a=>a==null?"":di(a))}function Nf(e,l,s,n,o,a,u){let h=[],m=Ht.get(o)||0;s=u?s:ce(mn(s,o),m);for(let d=s;d<=n;d=ce(d+o,m))h.push(Object.is(d,-0)?0:d);return h}function ii(e,l,s,n,o,a,u){let h=[],m=e.scales[e.axes[l].scale].log,d=m==10?Dt:jo,_=tt(d(s));o=Tl(m,_),m==10&&(o=Zt[wt(o,Zt)]);let p=s,w=o*m;m==10&&(w=Zt[wt(w,Zt)]);do h.push(p),p=p+o,m==10&&!Ht.has(p)&&(p=ce(p,Ht.get(o))),p>=w&&(o=p,w=o*m,m==10&&(w=Zt[wt(w,Zt)]));while(p<=n);return h}function Of(e,l,s,n,o,a,u){let m=e.scales[e.axes[l].scale].asinh,d=n>m?ii(e,l,Ue(m,s),n,o):[m],_=n>=0&&s<=0?[0]:[];return(s<-m?ii(e,l,Ue(m,-n),-s,o):[m]).reverse().map(w=>-w).concat(_,d)}var gs=/./,Hf=/[12357]/,Wf=/[125]/,Vo=/1/,oi=(e,l,s,n)=>e.map((o,a)=>l==4&&o==0||a%n==0&&s.test(o.toExponential()[o<0?1:0])?o:null);function Gf(e,l,s,n,o){let a=e.axes[s],u=a.scale,h=e.scales[u],m=e.valToPos,d=a._space,_=m(10,u),p=m(9,u)-_>=d?gs:m(7,u)-_>=d?Hf:m(5,u)-_>=d?Wf:Vo;if(p==Vo){let w=Ee(m(1,u)-_);if(wo,No={show:!0,auto:!0,sorted:0,gaps:ws,alpha:1,facets:[Te({},zo,{scale:"x"}),Te({},zo,{scale:"y"})]},Oo={scale:"y",auto:!0,sorted:0,show:!0,spanGaps:!1,gaps:ws,alpha:1,points:{show:Uf,filter:null},values:null,min:ue,max:-ue,idxs:[],path:null,clip:null};function qf(e,l,s,n,o){return s/10}var Ss={time:!0,auto:!0,distr:1,log:10,asinh:1,min:null,max:null,dir:1,ori:0},Kf=Te({},Ss,{time:!1,ori:1}),Ho={};function _s(e,l){let s=Ho[e];return s||(s={key:e,plots:[],sub(n){s.plots.push(n)},unsub(n){s.plots=s.plots.filter(o=>o!=n)},pub(n,o,a,u,h,m,d){for(let _=0;_{let W=u.pxRound,v=d.dir*(d.ori==0?1:-1),z=d.ori==0?Pl:Dl,T,j;v==1?(T=s,j=n):(T=n,j=s);let L=W(p(h[T],d,C,k)),J=W(w(m[T],_,H,P)),U=W(p(h[j],d,C,k)),Q=W(w(a==1?_.max:_.min,_,H,P)),B=new Path2D(o);return z(B,U,Q),z(B,L,Q),z(B,L,J),B})}function wn(e,l,s,n,o,a){let u=null;if(e.length>0){u=new Path2D;let h=l==0?yn:bi,m=s;for(let p=0;pw[0]){let k=w[0]-m;k>0&&h(u,m,n,k,n+a),m=w[1]}}let d=s+o-m,_=10;d>0&&h(u,m,n-_/2,d,n+a+_)}return u}function Jf(e,l,s){let n=e[e.length-1];n&&n[0]==l?n[1]=s:e.push([l,s])}function vi(e,l,s,n,o,a,u){let h=[],m=e.length;for(let d=o==1?s:n;d>=s&&d<=n;d+=o)if(l[d]===null){let p=d,w=d;if(o==1)for(;++d<=n&&l[d]===null;)w=d;else for(;--d>=s&&l[d]===null;)w=d;let k=a(e[p]),P=w==p?k:a(e[w]),C=p-o;k=u<=0&&C>=0&&C=0&&W>=0&&W=k&&h.push([k,P])}return h}function Wo(e){return e==0?$o:e==1?Ae:l=>$t(l,e)}function ys(e){let l=e==0?Sn:_n,s=e==0?(o,a,u,h,m,d)=>{o.arcTo(a,u,h,m,d)}:(o,a,u,h,m,d)=>{o.arcTo(u,a,m,h,d)},n=e==0?(o,a,u,h,m)=>{o.rect(a,u,h,m)}:(o,a,u,h,m)=>{o.rect(u,a,m,h)};return(o,a,u,h,m,d=0,_=0)=>{d==0&&_==0?n(o,a,u,h,m):(d=St(d,h/2,m/2),_=St(_,h/2,m/2),l(o,a+d,u),s(o,a+h,u,a+h,u+m,d),s(o,a+h,u+m,a,u+m,_),s(o,a,u+m,a,u,_),s(o,a,u,a+h,u,d),o.closePath())}}var Sn=(e,l,s)=>{e.moveTo(l,s)},_n=(e,l,s)=>{e.moveTo(s,l)},Pl=(e,l,s)=>{e.lineTo(l,s)},Dl=(e,l,s)=>{e.lineTo(s,l)},yn=ys(0),bi=ys(1),vs=(e,l,s,n,o,a)=>{e.arc(l,s,n,o,a)},bs=(e,l,s,n,o,a)=>{e.arc(s,l,n,o,a)},ks=(e,l,s,n,o,a,u)=>{e.bezierCurveTo(l,s,n,o,a,u)},Ts=(e,l,s,n,o,a,u)=>{e.bezierCurveTo(s,l,o,n,u,a)};function xs(e){return(l,s,n,o,a)=>tl(l,s,(u,h,m,d,_,p,w,k,P,C,H)=>{let{pxRound:W,points:v}=u,z,T;d.ori==0?(z=Sn,T=vs):(z=_n,T=bs);let j=ce(v.width*oe,3),L=(v.size-v.width)/2*oe,J=ce(L*2,3),U=new Path2D,Q=new Path2D,{left:B,top:x,width:K,height:F}=l.bbox;yn(Q,B-J,x-J,K+J*2,F+J*2);let I=O=>{if(m[O]!=null){let ee=W(p(h[O],d,C,k)),N=W(w(m[O],_,H,P));z(U,ee+L,N),T(U,ee,N,L,0,an*2)}};if(a)a.forEach(I);else for(let O=n;O<=o;O++)I(O);return{stroke:j>0?U:null,fill:U,clip:Q,flags:Al|si}})}function Ms(e){return(l,s,n,o,a,u)=>{n!=o&&(a!=n&&u!=n&&e(l,s,n),a!=o&&u!=o&&e(l,s,o),e(l,s,u))}}var $f=Ms(Pl),Zf=Ms(Dl);function As(e){let l=ne(e?.alignGaps,0);return(s,n,o,a)=>tl(s,n,(u,h,m,d,_,p,w,k,P,C,H)=>{[o,a]=pn(m,o,a);let W=u.pxRound,v=F=>W(p(F,d,C,k)),z=F=>W(w(F,_,H,P)),T,j;d.ori==0?(T=Pl,j=$f):(T=Dl,j=Zf);let L=d.dir*(d.ori==0?1:-1),J={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:Al},U=J.stroke,Q=!1;if(a-o>=C*4){let F=V=>s.posToVal(V,d.key,!0),I=null,O=null,ee,N,qe,we=v(h[L==1?o:a]),te=v(h[o]),fe=v(h[a]),$=F(L==1?te+1:fe-1);for(let V=L==1?o:a;V>=o&&V<=a;V+=L){let xe=h[V],Se=(L==1?xe<$:xe>$)?we:v(xe),se=m[V];Se==we?se!=null?(N=se,I==null?(T(U,Se,z(N)),ee=I=O=N):NO&&(O=N)):se===null&&(Q=!0):(I!=null&&j(U,we,z(I),z(O),z(ee),z(N)),se!=null?(N=se,T(U,Se,z(N)),I=O=ee=N):(I=O=null,se===null&&(Q=!0)),we=Se,$=F(we+L))}I!=null&&I!=O&&qe!=we&&j(U,we,z(I),z(O),z(ee),z(N))}else for(let F=L==1?o:a;F>=o&&F<=a;F+=L){let I=m[F];I===null?Q=!0:I!=null&&T(U,v(h[F]),z(I))}let[x,K]=yi(s,n);if(u.fill!=null||x!=0){let F=J.fill=new Path2D(U),I=u.fillTo(s,n,u.min,u.max,x),O=z(I),ee=v(h[o]),N=v(h[a]);L==-1&&([N,ee]=[ee,N]),T(F,N,O),T(F,ee,O)}if(!u.spanGaps){let F=[];Q&&F.push(...vi(h,m,o,a,L,v,l)),J.gaps=F=u.gaps(s,n,o,a,F),J.clip=wn(F,d.ori,k,P,C,H)}return K!=0&&(J.band=K==2?[Lt(s,n,o,a,U,-1),Lt(s,n,o,a,U,1)]:Lt(s,n,o,a,U,K)),J})}function Qf(e){let l=ne(e.align,1),s=ne(e.ascDesc,!1),n=ne(e.alignGaps,0),o=ne(e.extend,!1);return(a,u,h,m)=>tl(a,u,(d,_,p,w,k,P,C,H,W,v,z)=>{[h,m]=pn(p,h,m);let T=d.pxRound,{left:j,width:L}=a.bbox,J=te=>T(P(te,w,v,H)),U=te=>T(C(te,k,z,W)),Q=w.ori==0?Pl:Dl,B={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:Al},x=B.stroke,K=w.dir*(w.ori==0?1:-1),F=U(p[K==1?h:m]),I=J(_[K==1?h:m]),O=I,ee=I;o&&l==-1&&(ee=j,Q(x,ee,F)),Q(x,I,F);for(let te=K==1?h:m;te>=h&&te<=m;te+=K){let fe=p[te];if(fe==null)continue;let $=J(_[te]),V=U(fe);l==1?Q(x,$,F):Q(x,O,V),Q(x,$,V),F=V,O=$}let N=O;o&&l==1&&(N=j+L,Q(x,N,F));let[qe,we]=yi(a,u);if(d.fill!=null||qe!=0){let te=B.fill=new Path2D(x),fe=d.fillTo(a,u,d.min,d.max,qe),$=U(fe);Q(te,N,$),Q(te,ee,$)}if(!d.spanGaps){let te=[];te.push(...vi(_,p,h,m,K,J,n));let fe=d.width*oe/2,$=s||l==1?fe:-fe,V=s||l==-1?-fe:fe;te.forEach(xe=>{xe[0]+=$,xe[1]+=V}),B.gaps=te=d.gaps(a,u,h,m,te),B.clip=wn(te,w.ori,H,W,v,z)}return we!=0&&(B.band=we==2?[Lt(a,u,h,m,x,-1),Lt(a,u,h,m,x,1)]:Lt(a,u,h,m,x,we)),B})}function Go(e,l,s,n,o,a,u=ue){if(e.length>1){let h=null;for(let m=0,d=1/0;m{}),{fill:p,stroke:w}=d;return(k,P,C,H)=>tl(k,P,(W,v,z,T,j,L,J,U,Q,B,x)=>{let K=W.pxRound,F=s,I=n*oe,O=h*oe,ee=m*oe,N,qe;T.ori==0?[N,qe]=a(k,P):[qe,N]=a(k,P);let we=T.dir*(T.ori==0?1:-1),te=T.ori==0?yn:bi,fe=T.ori==0?_:(E,he,Me,ol,It,yt,Yt)=>{_(E,he,Me,It,ol,Yt,yt)},$=ne(k.bands,mi).find(E=>E.series[0]==P),V=$!=null?$.dir:0,xe=W.fillTo(k,P,W.min,W.max,V),We=K(J(xe,j,x,Q)),Se,se,ct,Je=B,ye=K(W.width*oe),_t=!1,At=null,nt=null,Ct=null,ll=null;p!=null&&(ye==0||w!=null)&&(_t=!0,At=p.values(k,P,C,H),nt=new Map,new Set(At).forEach(E=>{E!=null&&nt.set(E,new Path2D)}),ye>0&&(Ct=w.values(k,P,C,H),ll=new Map,new Set(Ct).forEach(E=>{E!=null&&ll.set(E,new Path2D)})));let{x0:nl,size:Ll}=d;if(nl!=null&&Ll!=null){F=1,v=nl.values(k,P,C,H),nl.unit==2&&(v=v.map(Me=>k.posToVal(U+Me*B,T.key,!0)));let E=Ll.values(k,P,C,H);Ll.unit==2?se=E[0]*B:se=L(E[0],T,B,U)-L(0,T,B,U),Je=Go(v,z,L,T,B,U,Je),ct=Je-se+I}else Je=Go(v,z,L,T,B,U,Je),ct=Je*u+I,se=Je-ct;ct<1&&(ct=0),ye>=se/2&&(ye=0),ct<5&&(K=$o);let jl=ct>0,Wt=Je-ct-(jl?ye:0);se=K(ni(Wt,ee,O)),Se=(F==0?se/2:F==we?0:se)-F*we*((F==0?I/2:0)+(jl?ye/2:0));let Ge={stroke:null,fill:null,clip:null,band:null,gaps:null,flags:0},il=_t?null:new Path2D,Et=null;if($!=null)Et=k.data[$.series[1]];else{let{y0:E,y1:he}=d;E!=null&&he!=null&&(z=he.values(k,P,C,H),Et=E.values(k,P,C,H))}let Gt=N*se,q=qe*se;for(let E=we==1?C:H;E>=C&&E<=H;E+=we){let he=z[E];if(he==null)continue;if(Et!=null){let Ke=Et[E]??0;if(he-Ke==0)continue;We=J(Ke,j,x,Q)}let Me=T.distr!=2||d!=null?v[E]:E,ol=L(Me,T,B,U),It=J(ne(he,xe),j,x,Q),yt=K(ol-Se),Yt=K(Ue(It,We)),$e=K(St(It,We)),it=Yt-$e;if(he!=null){let Ke=he<0?q:Gt,ht=he<0?Gt:q;_t?(ye>0&&Ct[E]!=null&&te(ll.get(Ct[E]),yt,$e+tt(ye/2),se,Ue(0,it-ye),Ke,ht),At[E]!=null&&te(nt.get(At[E]),yt,$e+tt(ye/2),se,Ue(0,it-ye),Ke,ht)):te(il,yt,$e+tt(ye/2),se,Ue(0,it-ye),Ke,ht),fe(k,P,E,yt-ye/2,$e,se+ye,it)}}return ye>0?Ge.stroke=_t?ll:il:_t||(Ge._fill=W.width==0?W._fill:W._stroke??W._fill,Ge.width=0),Ge.fill=_t?nt:il,Ge})}function ea(e,l){let s=ne(l?.alignGaps,0);return(n,o,a,u)=>tl(n,o,(h,m,d,_,p,w,k,P,C,H,W)=>{[a,u]=pn(d,a,u);let v=h.pxRound,z=N=>v(w(N,_,H,P)),T=N=>v(k(N,p,W,C)),j,L,J;_.ori==0?(j=Sn,J=Pl,L=ks):(j=_n,J=Dl,L=Ts);let U=_.dir*(_.ori==0?1:-1),Q=z(m[U==1?a:u]),B=Q,x=[],K=[];for(let N=U==1?a:u;N>=a&&N<=u;N+=U)if(d[N]!=null){let we=m[N],te=z(we);x.push(B=te),K.push(T(d[N]))}let F={stroke:e(x,K,j,J,L,v),fill:null,clip:null,band:null,gaps:null,flags:Al},I=F.stroke,[O,ee]=yi(n,o);if(h.fill!=null||O!=0){let N=F.fill=new Path2D(I),qe=h.fillTo(n,o,h.min,h.max,O),we=T(qe);J(N,B,we),J(N,Q,we)}if(!h.spanGaps){let N=[];N.push(...vi(m,d,a,u,U,z,s)),F.gaps=N=h.gaps(n,o,a,u,N),F.clip=wn(N,_.ori,P,C,H,W)}return ee!=0&&(F.band=ee==2?[Lt(n,o,a,u,I,-1),Lt(n,o,a,u,I,1)]:Lt(n,o,a,u,I,ee)),F})}function ta(e){return ea(la,e)}function la(e,l,s,n,o,a){let u=e.length;if(u<2)return null;let h=new Path2D;if(s(h,e[0],l[0]),u==2)n(h,e[1],l[1]);else{let m=Array(u),d=Array(u-1),_=Array(u-1),p=Array(u-1);for(let w=0;w0!=d[w]>0?m[w]=0:(m[w]=3*(p[w-1]+p[w])/((2*p[w]+p[w-1])/d[w-1]+(p[w]+2*p[w-1])/d[w]),isFinite(m[w])||(m[w]=0));m[u-1]=d[u-2];for(let w=0;w{Oe.pxRatio=oe}));var na=As(),ia=xs();function Yo(e,l,s,n){return(n?[e[0],e[1]].concat(e.slice(2)):[e[0]].concat(e.slice(1))).map((a,u)=>fi(a,u,l,s))}function oa(e,l){return e.map((s,n)=>n==0?{}:Te({},l,s))}function fi(e,l,s,n){return Te({},l==0?s:n,e)}function Es(e,l,s){return l==null?xl:[l,s]}var sa=Es;function ra(e,l,s){return l==null?xl:cn(l,s,pi,!0)}function Ps(e,l,s,n){return l==null?xl:dn(l,s,e.scales[n].log,!1)}var fa=Ps;function Ds(e,l,s,n){return l==null?xl:hi(l,s,e.scales[n].log,!1)}var aa=Ds;function ua(e,l,s,n,o){let a=Ue(vo(e),vo(l)),u=l-e,h=wt(o/n*u,s);do{let m=s[h],d=n*m/u;if(d>=o&&a+(m<5?Ht.get(m):0)<=17)return[m,d]}while(++h(l=Ae((s=+o)*oe))+"px"),[e,l,s]}function ca(e){e.show&&[e.font,e.labelFont].forEach(l=>{let s=ce(l[2]*oe,1);l[0]=l[0].replace(/[0-9.]+px/,s+"px"),l[1]=s})}function Oe(e,l,s){let n={mode:ne(e.mode,1)},o=n.mode;function a(t,i,r,f){let c=i.valToPct(t);return f+r*(i.dir==-1?1-c:c)}function u(t,i,r,f){let c=i.valToPct(t);return f+r*(i.dir==-1?c:1-c)}function h(t,i,r,f){return i.ori==0?a(t,i,r,f):u(t,i,r,f)}n.valToPosH=a,n.valToPosV=u;let m=!1;n.status=0;let d=n.root=at(wr);if(e.id!=null&&(d.id=e.id),et(d,e.class),e.title){let t=at(yr,d);t.textContent=e.title}let _=gt("canvas"),p=n.ctx=_.getContext("2d"),w=at(vr,d);Xt("click",w,t=>{t.target===P&&(pe!=gl||_e!=wl)&&Ne.click(n,t)},!0);let k=n.under=at(br,w);w.appendChild(_);let P=n.over=at(kr,w);e=Ml(e);let C=+ne(e.pxAlign,1),H=Wo(C);(e.plugins||[]).forEach(t=>{t.opts&&(e=t.opts(n,e)||e)});let W=e.ms||.001,v=n.series=o==1?Yo(e.series||[],Fo,Oo,!1):oa(e.series||[null],No),z=n.axes=Yo(e.axes||[],Co,Ro,!0),T=n.scales={},j=n.bands=e.bands||[];j.forEach(t=>{t.fill=X(t.fill||null),t.dir=ne(t.dir,-1)});let L=o==2?v[1].facets[0].scale:v[0].scale,J={axes:js,series:Ys},U=(e.drawOrder||["axes","series"]).map(t=>J[t]);function Q(t){let i=t.distr==3?r=>Dt(r>0?r:t.clamp(n,r,t.min,t.max,t.key)):t.distr==4?r=>Jn(r,t.asinh):t.distr==100?r=>t.fwd(r):r=>r;return r=>{let f=i(r),{_min:c,_max:g}=t,S=g-c;return(f-c)/S}}function B(t){let i=T[t];if(i==null){let r=(e.scales||Ul)[t]||Ul;if(r.from!=null){B(r.from);let f=Te({},T[r.from],r,{key:t});f.valToPct=Q(f),T[t]=f}else{i=T[t]=Te({},t==L?Ss:Kf,r),i.key=t;let f=i.time,c=i.range,g=Ot(c);if((t!=L||o==2&&!f)&&(g&&(c[0]==null||c[1]==null)&&(c={min:c[0]==null?So:{mode:1,hard:c[0],soft:c[0]},max:c[1]==null?So:{mode:1,hard:c[1],soft:c[1]}},g=!1),!g&&gn(c))){let S=c;c=(y,b,M)=>b==null?xl:cn(b,M,S)}i.range=X(c||(f?sa:t==L?i.distr==3?fa:i.distr==4?aa:Es:i.distr==3?Ps:i.distr==4?Ds:ra)),i.auto=X(g?!1:i.auto),i.clamp=X(i.clamp||qf),i._min=i._max=null,i.valToPct=Q(i)}}}B("x"),B("y"),o==1&&v.forEach(t=>{B(t.scale)}),z.forEach(t=>{B(t.scale)});for(let t in e.scales)B(t);let x=T[L],K=x.distr,F,I;x.ori==0?(et(d,Sr),F=a,I=u):(et(d,_r),F=u,I=a);let O={};for(let t in T){let i=T[t];(i.min!=null||i.max!=null)&&(O[t]={min:i.min,max:i.max},i.min=i.max=null)}let ee=e.tzDate||(t=>new Date(Ae(t/W))),N=e.fmtDate||gi,qe=W==1?_f(ee):bf(ee),we=Po(ee,Eo(W==1?Sf:vf,N)),te=Lo(ee,Do(Tf,N)),fe=[],$=n.legend=Te({},Af,e.legend),V=n.cursor=Te({},Ff,{drag:{y:o==2}},e.cursor),xe=$.show,We=V.show,Se=$.markers;$.idxs=fe,Se.width=X(Se.width),Se.dash=X(Se.dash),Se.stroke=X(Se.stroke),Se.fill=X(Se.fill);let se,ct,Je,ye=[],_t=[],At,nt=!1,Ct={};if($.live){let t=v[1]?v[1].values:null;nt=t!=null,At=nt?t(n,1,0):{_:0};for(let i in At)Ct[i]=ui}if(xe)if(se=gt("table",Pr,d),Je=gt("tbody",null,se),$.mount(n,se),nt){ct=gt("thead",null,se,Je);let t=gt("tr",null,ct);gt("th",null,t);for(var ll in At)gt("th",io,t).textContent=ll}else et(se,Lr),$.live&&et(se,Dr);let nl={show:!0},Ll={show:!1};function jl(t,i){if(i==0&&(nt||!$.live||o==2))return xl;let r=[],f=gt("tr",Cr,Je,Je.childNodes[i]);et(f,t.class),t.show||et(f,Qt);let c=gt("th",null,f);if(Se.show){let y=at(Fr,c);if(i>0){let b=Se.width(n,i);b&&(y.style.border=b+"px "+Se.dash(n,i)+" "+Se.stroke(n,i)),y.style.background=Se.fill(n,i)}}let g=at(io,c);t.label instanceof HTMLElement?g.appendChild(t.label):g.textContent=t.label,i>0&&(Se.show||(g.style.color=t.width>0?Se.stroke(n,i):Se.fill(n,i)),Ge("click",c,y=>{if(V._lock)return;Ut(y);let b=v.indexOf(t);if((y.ctrlKey||y.metaKey)!=$.isolate){let M=v.some((A,D)=>D>0&&D!=b&&A.show);v.forEach((A,D)=>{D>0&&bt(D,M?D==b?nl:Ll:nl,!0,ke.setSeries)})}else bt(b,{show:!t.show},!0,ke.setSeries)},!1),rl&&Ge(fo,c,y=>{V._lock||(Ut(y),bt(v.indexOf(t),_l,!0,ke.setSeries))},!1));for(var S in At){let y=gt("td",Vr,f);y.textContent="--",r.push(y)}return[f,r]}let Wt=new Map;function Ge(t,i,r,f=!0){let c=Wt.get(i)||{},g=V.bind[t](n,i,r,f);g&&(Xt(t,i,c[t]=g),Wt.set(i,c))}function il(t,i,r){let f=Wt.get(i)||{};for(let c in f)(t==null||c==t)&&(li(c,i,f[c]),delete f[c]);t==null&&Wt.delete(i)}let Et=0,Gt=0,q=0,E=0,he=0,Me=0,ol=he,It=Me,yt=q,Yt=E,$e=0,it=0,Ke=0,ht=0;n.bbox={};let kn=!1,Jl=!1,sl=!1,Bt=!1,$l=!1,ot=!1;function Tn(t,i,r){(r||t!=n.width||i!=n.height)&&xi(t,i),hl(!1),sl=!0,Jl=!0,pl()}function xi(t,i){n.width=Et=q=t,n.height=Gt=E=i,he=Me=0,zs(),Ns();let r=n.bbox;$e=r.left=$t(he*oe,.5),it=r.top=$t(Me*oe,.5),Ke=r.width=$t(q*oe,.5),ht=r.height=$t(E*oe,.5)}let Fs=3;function Vs(){let t=!1,i=0;for(;!t;){i++;let r=qs(i),f=Ks(i);t=i==Fs||r&&f,t||(xi(n.width,n.height),Jl=!0)}}function Rs({width:t,height:i}){Tn(t,i)}n.setSize=Rs;function zs(){let t=!1,i=!1,r=!1,f=!1;z.forEach((c,g)=>{if(c.show&&c._show){let{side:S,_size:y}=c,b=S%2,M=c.label!=null?c.labelSize:0,A=y+M;A>0&&(b?(q-=A,S==3?(he+=A,f=!0):r=!0):(E-=A,S==0?(Me+=A,t=!0):i=!0))}}),qt[0]=t,qt[1]=r,qt[2]=i,qt[3]=f,q-=Ft[1]+Ft[3],he+=Ft[3],E-=Ft[2]+Ft[0],Me+=Ft[0]}function Ns(){let t=he+q,i=Me+E,r=he,f=Me;function c(g,S){switch(g){case 1:return t+=S,t-S;case 2:return i+=S,i-S;case 3:return r-=S,r+S;case 0:return f-=S,f+S}}z.forEach((g,S)=>{if(g.show&&g._show){let y=g.side;g._pos=c(y,g._size),g.label!=null&&(g._lpos=c(y,g.labelSize))}})}if(V.dataIdx==null){let t=V.hover,i=t.skip=new Set(t.skip??[]);i.add(void 0);let r=t.prox=X(t.prox),f=t.bias??(t.bias=0);V.dataIdx=(c,g,S,y)=>{if(g==0)return S;let b=S,M=r(c,g,S,y)??ue,A=M>=0&&M0;)i.has(le[R])||(Z=R);if(f==0||f==1)for(R=S;G==null&&R++M&&(b=null);return b}}let Ut=t=>{V.event=t};V.idxs=fe,V._lock=!1;let He=V.points;He.show=X(He.show),He.size=X(He.size),He.stroke=X(He.stroke),He.width=X(He.width),He.fill=X(He.fill);let vt=n.focus=Te({},e.focus||{alpha:.3},V.focus),rl=vt.prox>=0,fl=rl&&He.one,st=[],al=[],ul=[];function Mi(t,i){let r=He.show(n,i);if(r instanceof HTMLElement)return et(r,Er),et(r,t.class),Mt(r,-10,-10,q,E),P.insertBefore(r,st[i]),r}function Ai(t,i){if(o==1||i>0){let r=o==1&&T[t.scale].time,f=t.value;t.value=r?To(f)?Lo(ee,Do(f,N)):f||te:f||Yf,t.label=t.label||(r?Rf:Vf)}if(fl||i>0){t.width=t.width==null?1:t.width,t.paths=t.paths||na||qr,t.fillTo=X(t.fillTo||jf),t.pxAlign=+ne(t.pxAlign,C),t.pxRound=Wo(t.pxAlign),t.stroke=X(t.stroke||null),t.fill=X(t.fill||null),t._stroke=t._fill=t._paths=t._focus=null;let r=Bf(Ue(1,t.width),1),f=t.points=Te({},{size:r,width:Ue(1,r*.2),stroke:t.stroke,space:r*2,paths:ia,_stroke:null,_fill:null},t.points);f.show=X(f.show),f.filter=X(f.filter),f.fill=X(f.fill),f.stroke=X(f.stroke),f.paths=X(f.paths),f.pxAlign=t.pxAlign}if(xe){let r=jl(t,i);ye.splice(i,0,r[0]),_t.splice(i,0,r[1]),$.values.push(null)}if(We){fe.splice(i,0,null);let r=null;fl?i==0&&(r=Mi(t,i)):i>0&&(r=Mi(t,i)),st.splice(i,0,r),al.splice(i,0,0),ul.splice(i,0,0)}Ve("addSeries",i)}function Os(t,i){i=i??v.length,t=o==1?fi(t,i,Fo,Oo):fi(t,i,{},No),v.splice(i,0,t),Ai(v[i],i)}n.addSeries=Os;function Hs(t){if(v.splice(t,1),xe){$.values.splice(t,1),_t.splice(t,1);let i=ye.splice(t,1)[0];il(null,i.firstChild),i.remove()}We&&(fe.splice(t,1),st.splice(t,1)[0].remove(),al.splice(t,1),ul.splice(t,1)),Ve("delSeries",t)}n.delSeries=Hs;let qt=[!1,!1,!1,!1];function Ws(t,i){if(t._show=t.show,t.show){let r=t.side%2,f=T[t.scale];f==null&&(t.scale=r?v[1].scale:L,f=T[t.scale]);let c=f.time;t.size=X(t.size),t.space=X(t.space),t.rotate=X(t.rotate),Ot(t.incrs)&&t.incrs.forEach(S=>{!Ht.has(S)&&Ht.set(S,Xo(S))}),t.incrs=X(t.incrs||(f.distr==2?mf:c?W==1?wf:yf:Zt)),t.splits=X(t.splits||(c&&f.distr==1?qe:f.distr==3?ii:f.distr==4?Of:Nf)),t.stroke=X(t.stroke),t.grid.stroke=X(t.grid.stroke),t.ticks.stroke=X(t.ticks.stroke),t.border.stroke=X(t.border.stroke);let g=t.values;t.values=Ot(g)&&!Ot(g[0])?X(g):c?Ot(g)?Po(ee,Eo(g,N)):To(g)?kf(ee,g):g||we:g||zf,t.filter=X(t.filter||(f.distr>=3&&f.log==10?Gf:f.distr==3&&f.log==2?If:Zo)),t.font=Bo(t.font),t.labelFont=Bo(t.labelFont),t._size=t.size(n,null,i,0),t._space=t._rotate=t._incrs=t._found=t._splits=t._values=null,t._size>0&&(qt[i]=!0,t._el=at(Tr,w))}}function Cl(t,i,r,f){let[c,g,S,y]=r,b=i%2,M=0;return b==0&&(y||g)&&(M=i==0&&!c||i==2&&!S?Ae(Co.size/3):0),b==1&&(c||S)&&(M=i==1&&!g||i==3&&!y?Ae(Ro.size/2):0),M}let Ei=n.padding=(e.padding||[Cl,Cl,Cl,Cl]).map(t=>X(ne(t,Cl))),Ft=n._padding=Ei.map((t,i)=>t(n,i,qt,0)),ze,De=null,Le=null,Zl=o==1?v[0].idxs:null,pt=null,Fl=!1;function Pi(t,i){if(l=t??[],n.data=n._data=l,o==2){ze=0;for(let r=1;r=0,ot=!0,pl()}}n.setData=Pi;function xn(){Fl=!0;let t,i;o==1&&(ze>0?(De=Zl[0]=0,Le=Zl[1]=ze-1,t=l[0][De],i=l[0][Le],K==2?(t=De,i=Le):t==i&&(K==3?[t,i]=dn(t,t,x.log,!1):K==4?[t,i]=hi(t,t,x.log,!1):x.time?i=t+Ae(86400/W):[t,i]=cn(t,i,pi,!0))):(De=Zl[0]=t=null,Le=Zl[1]=i=null)),Rt(L,t,i)}let Ql,cl,Mn,An,En,Pn,Dn,Ln,Cn,je;function Di(t,i,r,f,c,g){t??(t=so),r??(r=mi),f??(f="butt"),c??(c=so),g??(g="round"),t!=Ql&&(p.strokeStyle=Ql=t),c!=cl&&(p.fillStyle=cl=c),i!=Mn&&(p.lineWidth=Mn=i),g!=En&&(p.lineJoin=En=g),f!=Pn&&(p.lineCap=Pn=f),r!=An&&p.setLineDash(An=r)}function Li(t,i,r,f){i!=cl&&(p.fillStyle=cl=i),t!=Dn&&(p.font=Dn=t),r!=Ln&&(p.textAlign=Ln=r),f!=Cn&&(p.textBaseline=Cn=f)}function Fn(t,i,r,f,c=0){if(f.length>0&&t.auto(n,Fl)&&(i==null||i.min==null)){let g=ne(De,0),S=ne(Le,f.length-1),y=r.min==null?Wr(f,g,S,c,t.distr==3):[r.min,r.max];t.min=St(t.min,r.min=y[0]),t.max=Ue(t.max,r.max=y[1])}}let Ci={min:null,max:null};function Gs(){for(let f in T){let c=T[f];O[f]==null&&(c.min==null||O[L]!=null&&c.auto(n,Fl))&&(O[f]=Ci)}for(let f in T){let c=T[f];O[f]==null&&c.from!=null&&O[c.from]!=null&&(O[f]=Ci)}O[L]!=null&&hl(!0);let t={};for(let f in O){let c=O[f];if(c!=null){let g=t[f]=Ml(T[f],Jr);if(c.min!=null)Te(g,c);else if(f!=L||o==2)if(ze==0&&g.from==null){let S=g.range(n,null,null,f);g.min=S[0],g.max=S[1]}else g.min=ue,g.max=-ue}}if(ze>0){v.forEach((f,c)=>{if(o==1){let g=f.scale,S=O[g];if(S==null)return;let y=t[g];if(c==0){let b=y.range(n,y.min,y.max,g);y.min=b[0],y.max=b[1],De=wt(y.min,l[0]),Le=wt(y.max,l[0]),Le-De>1&&(l[0][De]y.max&&Le--),f.min=pt[De],f.max=pt[Le]}else f.show&&f.auto&&Fn(y,S,f,l[c],f.sorted);f.idxs[0]=De,f.idxs[1]=Le}else if(c>0&&f.show&&f.auto){let[g,S]=f.facets,y=g.scale,b=S.scale,[M,A]=l[c],D=t[y],Y=t[b];D!=null&&Fn(D,O[y],g,M,g.sorted),Y!=null&&Fn(Y,O[b],S,A,S.sorted),f.min=S.min,f.max=S.max}});for(let f in t){let c=t[f],g=O[f];if(c.from==null&&(g==null||g.min==null)){let S=c.range(n,c.min==ue?null:c.min,c.max==-ue?null:c.max,f);c.min=S[0],c.max=S[1]}}}for(let f in t){let c=t[f];if(c.from!=null){let g=t[c.from];if(g.min==null)c.min=c.max=null;else{let S=c.range(n,g.min,g.max,f);c.min=S[0],c.max=S[1]}}}let i={},r=!1;for(let f in t){let c=t[f],g=T[f];if(g.min!=c.min||g.max!=c.max){g.min=c.min,g.max=c.max;let S=g.distr;g._min=S==3?Dt(g.min):S==4?Jn(g.min,g.asinh):S==100?g.fwd(g.min):g.min,g._max=S==3?Dt(g.max):S==4?Jn(g.max,g.asinh):S==100?g.fwd(g.max):g.max,i[f]=r=!0}}if(r){v.forEach((f,c)=>{o==2?c>0&&i.y&&(f._paths=null):i[f.scale]&&(f._paths=null)});for(let f in i)sl=!0,Ve("setScale",f);We&&V.left>=0&&(Bt=ot=!0)}for(let f in O)O[f]=null}function Is(t){let i=ni(De-1,0,ze-1),r=ni(Le+1,0,ze-1);for(;t[i]==null&&i>0;)i--;for(;t[r]==null&&r0){let t=v.some(i=>i._focus)&&je!=vt.alpha;t&&(p.globalAlpha=je=vt.alpha),v.forEach((i,r)=>{if(r>0&&i.show&&(Fi(r,!1),Fi(r,!0),i._paths==null)){let f=je;je!=i.alpha&&(p.globalAlpha=je=i.alpha);let c=o==2?[0,l[r][0].length-1]:Is(l[r]);i._paths=i.paths(n,r,c[0],c[1]),je!=f&&(p.globalAlpha=je=f)}}),v.forEach((i,r)=>{if(r>0&&i.show){let f=je;je!=i.alpha&&(p.globalAlpha=je=i.alpha),i._paths!=null&&Vi(r,!1);{let c=i._paths!=null?i._paths.gaps:null,g=i.points.show(n,r,De,Le,c),S=i.points.filter(n,r,g,c);(g||S)&&(i.points._paths=i.points.paths(n,r,De,Le,S),Vi(r,!0))}je!=f&&(p.globalAlpha=je=f),Ve("drawSeries",r)}}),t&&(p.globalAlpha=je=1)}}function Fi(t,i){let r=i?v[t].points:v[t];r._stroke=r.stroke(n,t),r._fill=r.fill(n,t)}function Vi(t,i){let r=i?v[t].points:v[t],{stroke:f,fill:c,clip:g,flags:S,_stroke:y=r._stroke,_fill:b=r._fill,_width:M=r.width}=r._paths;M=ce(M*oe,3);let A=null,D=M%2/2;i&&b==null&&(b=M>0?"#fff":y);let Y=r.pxAlign==1&&D>0;if(Y&&p.translate(D,D),!i){let ie=$e-M/2,le=it-M/2,Z=Ke+M,G=ht+M;A=new Path2D,A.rect(ie,le,Z,G)}i?Vn(y,M,r.dash,r.cap,b,f,c,S,g):Bs(t,y,M,r.dash,r.cap,b,f,c,S,A,g),Y&&p.translate(-D,-D)}function Bs(t,i,r,f,c,g,S,y,b,M,A){let D=!1;b!=0&&j.forEach((Y,ie)=>{if(Y.series[0]==t){let le=v[Y.series[1]],Z=l[Y.series[1]],G=(le._paths||Ul).band;Ot(G)&&(G=Y.dir==1?G[0]:G[1]);let R,me=null;le.show&&G&&Ir(Z,De,Le)?(me=Y.fill(n,ie)||g,R=le._paths.clip):G=null,Vn(i,r,f,c,me,S,y,b,M,A,R,G),D=!0}}),D||Vn(i,r,f,c,g,S,y,b,M,A)}let Ri=Al|si;function Vn(t,i,r,f,c,g,S,y,b,M,A,D){Di(t,i,r,f,c),(b||M||D)&&(p.save(),b&&p.clip(b),M&&p.clip(M)),D?(y&Ri)==Ri?(p.clip(D),A&&p.clip(A),en(c,S),Xl(t,g,i)):y&si?(en(c,S),p.clip(D),Xl(t,g,i)):y&Al&&(p.save(),p.clip(D),A&&p.clip(A),en(c,S),p.restore(),Xl(t,g,i)):(en(c,S),Xl(t,g,i)),(b||M||D)&&p.restore()}function Xl(t,i,r){r>0&&(i instanceof Map?i.forEach((f,c)=>{p.strokeStyle=Ql=c,p.stroke(f)}):i!=null&&t&&p.stroke(i))}function en(t,i){i instanceof Map?i.forEach((r,f)=>{p.fillStyle=cl=f,p.fill(r)}):i!=null&&t&&p.fill(i)}function Us(t,i,r,f){let c=z[t],g;if(f<=0)g=[0,0];else{let S=c._space=c.space(n,t,i,r,f),y=c._incrs=c.incrs(n,t,i,r,f,S);g=ua(i,r,y,f,S)}return c._found=g}function Rn(t,i,r,f,c,g,S,y,b,M){let A=S%2/2;C==1&&p.translate(A,A),Di(y,S,b,M,y),p.beginPath();let D,Y,ie,le,Z=c+(f==0||f==3?-g:g);r==0?(Y=c,le=Z):(D=c,ie=Z);for(let G=0;G{if(!r.show)return;let c=T[r.scale];if(c.min==null){r._show&&(i=!1,r._show=!1,hl(!1));return}else r._show||(i=!1,r._show=!0,hl(!1));let g=r.side,S=g%2,{min:y,max:b}=c,[M,A]=Us(f,y,b,S==0?q:E);if(A==0)return;let D=c.distr==2,Y=r._splits=r.splits(n,f,y,b,M,A,D),ie=c.distr==2?Y.map(R=>pt[R]):Y,le=c.distr==2?pt[Y[1]]-pt[Y[0]]:M,Z=r._values=r.values(n,r.filter(n,ie,f,A,le),f,A,le);r._rotate=g==2?r.rotate(n,Z,f,A):0;let G=r._size;r._size=ut(r.size(n,Z,f,t)),G!=null&&r._size!=G&&(i=!1)}),i}function Ks(t){let i=!0;return Ei.forEach((r,f)=>{let c=r(n,f,qt,t);c!=Ft[f]&&(i=!1),Ft[f]=c}),i}function js(){for(let t=0;tpt[Ye]):ie,Z=A.distr==2?pt[ie[1]]-pt[ie[0]]:b,G=i.ticks,R=i.border,me=G.show?G.size:0,ve=Ae(me*oe),Re=Ae((i.alignTo==2?i._size-me-i.gap:i.gap)*oe),ae=i._rotate*-an/180,be=H(i._pos*oe),Ze=(ve+Re)*y,Ie=be+Ze;g=f==0?Ie:0,c=f==1?Ie:0;let rt=i.font[0],dt=i.align==1?vl:i.align==2?qn:ae>0?vl:ae<0?qn:f==0?"center":r==3?qn:vl,Tt=ae||f==1?"middle":r==2?Hl:oo;Li(rt,S,dt,Tt);let Qe=i.font[1]*i.lineGap,ft=ie.map(Ye=>H(h(Ye,A,D,Y))),mt=i._values;for(let Ye=0;Ye{r>0&&(i._paths=null,t&&(o==1?(i.min=null,i.max=null):i.facets.forEach(f=>{f.min=null,f.max=null})))})}let tn=!1,zn=!1,Vl=[];function Js(){zn=!1;for(let t=0;t0&&queueMicrotask(Js)}n.batch=$s;function zi(){if(kn&&(Gs(),kn=!1),sl&&(Vs(),sl=!1),Jl){if(ge(k,vl,he),ge(k,Hl,Me),ge(k,Gl,q),ge(k,Il,E),ge(P,vl,he),ge(P,Hl,Me),ge(P,Gl,q),ge(P,Il,E),ge(w,Gl,Et),ge(w,Il,Gt),_.width=Ae(Et*oe),_.height=Ae(Gt*oe),z.forEach(({_el:t,_show:i,_size:r,_pos:f,side:c})=>{if(t!=null)if(i){let g=c===3||c===0?r:0,S=c%2==1;ge(t,S?"left":"top",f-g),ge(t,S?"width":"height",r),ge(t,S?"top":"left",S?Me:he),ge(t,S?"height":"width",S?E:q),ti(t,Qt)}else et(t,Qt)}),Ql=cl=Mn=En=Pn=Dn=Ln=Cn=An=null,je=1,Nl(!0),he!=ol||Me!=It||q!=yt||E!=Yt){hl(!1);let t=q/yt,i=E/Yt;if(We&&!Bt&&V.left>=0){V.left*=t,V.top*=i,dl&&Mt(dl,Ae(V.left),0,q,E),ml&&Mt(ml,0,Ae(V.top),q,E);for(let r=0;r=0&&de.width>0){de.left*=t,de.width*=t,de.top*=i,de.height*=i;for(let r in In)ge(Sl,r,de[r])}ol=he,It=Me,yt=q,Yt=E}Ve("setSize"),Jl=!1}Et>0&&Gt>0&&(p.clearRect(0,0,_.width,_.height),Ve("drawClear"),U.forEach(t=>t()),Ve("draw")),de.show&&$l&&(ln(de),$l=!1),We&&Bt&&(jt(null,!0,!1),Bt=!1),$.show&&$.live&&ot&&(Wn(),ot=!1),m||(m=!0,n.status=1,Ve("ready")),Fl=!1,tn=!1}n.redraw=(t,i)=>{sl=i||!1,t!==!1?Rt(L,x.min,x.max):pl()};function Nn(t,i){let r=T[t];if(r.from==null){if(ze==0){let f=r.range(n,i.min,i.max,t);i.min=f[0],i.max=f[1]}if(i.min>i.max){let f=i.min;i.min=i.max,i.max=f}if(ze>1&&i.min!=null&&i.max!=null&&i.max-i.min<1e-16)return;t==L&&r.distr==2&&ze>0&&(i.min=wt(i.min,l[0]),i.max=wt(i.max,l[0]),i.min==i.max&&i.max++),O[t]=i,kn=!0,pl()}}n.setScale=Nn;let On,Hn,dl,ml,Ni,Oi,gl,wl,Hi,Wi,pe,_e,Vt=!1,Ne=V.drag,Ce=Ne.x,Fe=Ne.y;We&&(V.x&&(On=at(Mr,P)),V.y&&(Hn=at(Ar,P)),x.ori==0?(dl=On,ml=Hn):(dl=Hn,ml=On),pe=V.left,_e=V.top);let de=n.select=Te({show:!0,over:!0,left:0,width:0,top:0,height:0},e.select),Sl=de.show?at(xr,de.over?P:k):null;function ln(t,i){if(de.show){for(let r in t)de[r]=t[r],r in In&&ge(Sl,r,t[r]);i!==!1&&Ve("setSelect")}}n.setSelect=ln;function Zs(t){if(v[t].show)xe&&ti(ye[t],Qt);else if(xe&&et(ye[t],Qt),We){let r=fl?st[0]:st[t];r!=null&&Mt(r,-10,-10,q,E)}}function Rt(t,i,r){Nn(t,{min:i,max:r})}function bt(t,i,r,f){i.focus!=null&&lr(t),i.show!=null&&v.forEach((c,g)=>{g>0&&(t==g||t==null)&&(c.show=i.show,Zs(g),o==2?(Rt(c.facets[0].scale,null,null),Rt(c.facets[1].scale,null,null)):Rt(c.scale,null,null),pl())}),r!==!1&&Ve("setSeries",t,i),f&&Ol("setSeries",n,t,i)}n.setSeries=bt;function Qs(t,i){Te(j[t],i)}function Xs(t,i){t.fill=X(t.fill||null),t.dir=ne(t.dir,-1),i=i??j.length,j.splice(i,0,t)}function er(t){t==null?j.length=0:j.splice(t,1)}n.addBand=Xs,n.setBand=Qs,n.delBand=er;function tr(t,i){v[t].alpha=i,We&&st[t]!=null&&(st[t].style.opacity=i),xe&&ye[t]&&(ye[t].style.opacity=i)}let Pt,zt,Kt,_l={focus:!0};function lr(t){if(t!=Kt){let i=t==null,r=vt.alpha!=1;v.forEach((f,c)=>{if(o==1||c>0){let g=i||c==0||c==t;f._focus=i?null:g,r&&tr(c,g?1:vt.alpha)}}),Kt=t,r&&pl()}}xe&&rl&&Ge(ao,se,t=>{V._lock||(Ut(t),Kt!=null&&bt(null,_l,!0,ke.setSeries))});function kt(t,i,r){let f=T[i];r&&(t=t/oe-(f.ori==1?Me:he));let c=q;f.ori==1&&(c=E,t=c-t),f.dir==-1&&(t=c-t);let g=f._min,S=f._max,y=t/c,b=g+(S-g)*y,M=f.distr;return M==3?Tl(10,b):M==4?Br(b,f.asinh):M==100?f.bwd(b):b}function nr(t,i){let r=kt(t,L,i);return wt(r,l[0],De,Le)}n.valToIdx=t=>wt(t,l[0]),n.posToIdx=nr,n.posToVal=kt,n.valToPos=(t,i,r)=>T[i].ori==0?a(t,T[i],r?Ke:q,r?$e:0):u(t,T[i],r?ht:E,r?it:0),n.setCursor=(t,i,r)=>{pe=t.left,_e=t.top,jt(null,i,r)};function Gi(t,i){ge(Sl,vl,de.left=t),ge(Sl,Gl,de.width=i)}function Ii(t,i){ge(Sl,Hl,de.top=t),ge(Sl,Il,de.height=i)}let Rl=x.ori==0?Gi:Ii,zl=x.ori==1?Gi:Ii;function ir(){if(xe&&$.live)for(let t=o==2?1:0;t{fe[f]=r}):jr(t.idx)||fe.fill(t.idx),$.idx=fe[0]),xe&&$.live){for(let r=0;r0||o==1&&!nt)&&or(r,fe[r]);ir()}ot=!1,i!==!1&&Ve("setLegend")}n.setLegend=Wn;function or(t,i){let r=v[t],f=t==0&&K==2?pt:l[t],c;nt?c=r.values(n,t,i)??Ct:(c=r.value(n,i==null?null:f[i],t,i),c=c==null?Ct:{_:c}),$.values[t]=c}function jt(t,i,r){Hi=pe,Wi=_e,[pe,_e]=V.move(n,pe,_e),V.left=pe,V.top=_e,We&&(dl&&Mt(dl,Ae(pe),0,q,E),ml&&Mt(ml,0,Ae(_e),q,E));let f,c=De>Le;Pt=ue,zt=null;let g=x.ori==0?q:E,S=x.ori==1?q:E;if(pe<0||ze==0||c){f=V.idx=null;for(let y=0;y0&&me.show){let Ze=ae==null?-10:ae==f?M:F(o==1?l[0][ae]:l[R][0][ae],x,g,0),Ie=be==null?-10:I(be,o==1?T[me.scale]:T[me.facets[1].scale],S,0);if(rl&&be!=null){let rt=x.ori==1?pe:_e,dt=Ee(vt.dist(n,R,ae,Ie,rt));if(dt=0?1:-1,mt=Qe>=0?1:-1;mt==ft&&(mt==1?Tt==1?be>=Qe:be<=Qe:Tt==1?be<=Qe:be>=Qe)&&(Pt=dt,zt=R)}else Pt=dt,zt=R}}if(ot||fl){let rt,dt;x.ori==0?(rt=Ze,dt=Ie):(rt=Ie,dt=Ze);let Tt,Qe,ft,mt,xt,Ye,Xe=!0,Jt=He.bbox;if(Jt!=null){Xe=!1;let Be=Jt(n,R);ft=Be.left,mt=Be.top,Tt=Be.width,Qe=Be.height}else ft=rt,mt=dt,Tt=Qe=He.size(n,R);if(Ye=He.fill(n,R),xt=He.stroke(n,R),fl)R==zt&&Pt<=vt.prox&&(A=ft,D=mt,Y=Tt,ie=Qe,le=Xe,Z=Ye,G=xt);else{let Be=st[R];Be!=null&&(al[R]=ft,ul[R]=mt,wo(Be,Tt,Qe,Xe),mo(Be,Ye,xt),Mt(Be,ut(ft),ut(mt),q,E))}}}}if(fl){let R=vt.prox,me=Kt==null?Pt<=R:Pt>R||zt!=Kt;if(ot||me){let ve=st[0];ve!=null&&(al[0]=A,ul[0]=D,wo(ve,Y,ie,le),mo(ve,Z,G),Mt(ve,ut(A),ut(D),q,E))}}}if(de.show&&Vt)if(t!=null){let[y,b]=ke.scales,[M,A]=ke.match,[D,Y]=t.cursor.sync.scales,ie=t.cursor.drag;if(Ce=ie._x,Fe=ie._y,Ce||Fe){let{left:le,top:Z,width:G,height:R}=t.select,me=t.scales[D].ori,ve=t.posToVal,Re,ae,be,Ze,Ie,rt=y!=null&&M(y,D),dt=b!=null&&A(b,Y);rt&&Ce?(me==0?(Re=le,ae=G):(Re=Z,ae=R),be=T[y],Ze=F(ve(Re,D),be,g,0),Ie=F(ve(Re+ae,D),be,g,0),Rl(St(Ze,Ie),Ee(Ie-Ze))):Rl(0,g),dt&&Fe?(me==1?(Re=le,ae=G):(Re=Z,ae=R),be=T[b],Ze=I(ve(Re,Y),be,S,0),Ie=I(ve(Re+ae,Y),be,S,0),zl(St(Ze,Ie),Ee(Ie-Ze))):zl(0,S)}else Yn()}else{let y=Ee(Hi-Ni),b=Ee(Wi-Oi);if(x.ori==1){let Y=y;y=b,b=Y}Ce=Ne.x&&y>=Ne.dist,Fe=Ne.y&&b>=Ne.dist;let M=Ne.uni;M!=null?Ce&&Fe&&(Ce=y>=M,Fe=b>=M,!Ce&&!Fe&&(b>y?Fe=!0:Ce=!0)):Ne.x&&Ne.y&&(Ce||Fe)&&(Ce=Fe=!0);let A,D;Ce&&(x.ori==0?(A=gl,D=pe):(A=wl,D=_e),Rl(St(A,D),Ee(D-A)),Fe||zl(0,S)),Fe&&(x.ori==1?(A=gl,D=pe):(A=wl,D=_e),zl(St(A,D),Ee(D-A)),Ce||Rl(0,g)),!Ce&&!Fe&&(Rl(0,0),zl(0,0))}if(Ne._x=Ce,Ne._y=Fe,t==null){if(r){if(Xi!=null){let[y,b]=ke.scales;ke.values[0]=y!=null?kt(x.ori==0?pe:_e,y):null,ke.values[1]=b!=null?kt(x.ori==1?pe:_e,b):null}Ol(Kn,n,pe,_e,q,E,f)}if(rl){let y=r&&ke.setSeries,b=vt.prox;Kt==null?Pt<=b&&bt(zt,_l,!0,y):Pt>b?bt(null,_l,!0,y):zt!=Kt&&bt(zt,_l,!0,y)}}ot&&($.idx=f,Wn()),i!==!1&&Ve("setCursor")}let Nt=null;Object.defineProperty(n,"rect",{get(){return Nt==null&&Nl(!1),Nt}});function Nl(t=!1){t?Nt=null:(Nt=P.getBoundingClientRect(),Ve("syncRect",Nt))}function Yi(t,i,r,f,c,g,S){V._lock||Vt&&t!=null&&t.movementX==0&&t.movementY==0||(Gn(t,i,r,f,c,g,S,!1,t!=null),t!=null?jt(null,!0,!0):jt(i,!0,!1))}function Gn(t,i,r,f,c,g,S,y,b){if(Nt==null&&Nl(!1),Ut(t),t!=null)r=t.clientX-Nt.left,f=t.clientY-Nt.top;else{if(r<0||f<0){pe=-10,_e=-10;return}let[M,A]=ke.scales,D=i.cursor.sync,[Y,ie]=D.values,[le,Z]=D.scales,[G,R]=ke.match,me=i.axes[0].side%2==1,ve=x.ori==0?q:E,Re=x.ori==1?q:E,ae=me?g:c,be=me?c:g,Ze=me?f:r,Ie=me?r:f;if(le!=null?r=G(M,le)?h(Y,T[M],ve,0):-10:r=ve*(Ze/ae),Z!=null?f=R(A,Z)?h(ie,T[A],Re,0):-10:f=Re*(Ie/be),x.ori==1){let rt=r;r=f,f=rt}}b&&(i==null||i.cursor.event.type==Kn)&&((r<=1||r>=q-1)&&(r=$t(r,q)),(f<=1||f>=E-1)&&(f=$t(f,E))),y?(Ni=r,Oi=f,[gl,wl]=V.move(n,r,f)):(pe=r,_e=f)}let In={width:0,height:0,left:0,top:0};function Yn(){ln(In,!1)}let Bi,Ui,qi,Ki;function ji(t,i,r,f,c,g,S){Vt=!0,Ce=Fe=Ne._x=Ne._y=!1,Gn(t,i,r,f,c,g,S,!0,!1),t!=null&&(Ge(jn,Xn,Ji,!1),Ol(ro,n,gl,wl,q,E,null));let{left:y,top:b,width:M,height:A}=de;Bi=y,Ui=b,qi=M,Ki=A}function Ji(t,i,r,f,c,g,S){Vt=Ne._x=Ne._y=!1,Gn(t,i,r,f,c,g,S,!1,!0);let{left:y,top:b,width:M,height:A}=de,D=M>0||A>0,Y=Bi!=y||Ui!=b||qi!=M||Ki!=A;if(D&&Y&&ln(de),Ne.setScale&&D&&Y){let ie=y,le=M,Z=b,G=A;if(x.ori==1&&(ie=b,le=A,Z=y,G=M),Ce&&Rt(L,kt(ie,L),kt(ie+le,L)),Fe)for(let R in T){let me=T[R];R!=L&&me.from==null&&me.min!=ue&&Rt(R,kt(Z+G,R),kt(Z,R))}Yn()}else V.lock&&(V._lock=!V._lock,jt(i,!0,t!=null));t!=null&&(il(jn,Xn),Ol(jn,n,pe,_e,q,E,null))}function sr(t,i,r,f,c,g,S){if(V._lock)return;Ut(t);let y=Vt;if(Vt){let b=!0,M=!0,A=10,D,Y;x.ori==0?(D=Ce,Y=Fe):(D=Fe,Y=Ce),D&&Y&&(b=pe<=A||pe>=q-A,M=_e<=A||_e>=E-A),D&&b&&(pe=pe{let c=ke.match[2];r=c(n,i,r),r!=-1&&bt(r,f,!0,!1)},We&&(Ge(ro,P,ji),Ge(Kn,P,Yi),Ge(fo,P,t=>{Ut(t),Nl(!1)}),Ge(ao,P,sr),Ge(uo,P,$i),ri.add(n),n.syncRect=Nl);let nn=n.hooks=e.hooks||{};function Ve(t,i,r){zn?Vl.push([t,i,r]):t in nn&&nn[t].forEach(f=>{f.call(null,n,i,r)})}(e.plugins||[]).forEach(t=>{for(let i in t.hooks)nn[i]=(nn[i]||[]).concat(t.hooks[i])});let Qi=(t,i,r)=>r,ke=Te({key:null,setSeries:!1,filters:{pub:bo,sub:bo},scales:[L,v[1]?v[1].scale:null],match:[ko,ko,Qi],values:[null,null]},V.sync);ke.match.length==2&&ke.match.push(Qi),V.sync=ke;let Xi=ke.key,Bn=_s(Xi);function Ol(t,i,r,f,c,g,S){ke.filters.pub(t,i,r,f,c,g,S)&&Bn.pub(t,i,r,f,c,g,S)}Bn.sub(n);function rr(t,i,r,f,c,g,S){ke.filters.sub(t,i,r,f,c,g,S)&&yl[t](null,i,r,f,c,g,S)}n.pub=rr;function fr(){Bn.unsub(n),ri.delete(n),Wt.clear(),li(un,kl,Zi),d.remove(),se?.remove(),Ve("destroy")}n.destroy=fr;function Un(){Ve("init",e,l),Pi(l||e.data,!1),O[L]?Nn(L,O[L]):xn(),$l=de.show&&(de.width>0||de.height>0),Bt=ot=!0,Tn(e.width,e.height)}return v.forEach(Ai),z.forEach(Ws),s?s instanceof HTMLElement?(s.appendChild(d),Un()):s(n,Un):Un(),n}Oe.assign=Te;Oe.fmtNum=di;Oe.rangeNum=cn;Oe.rangeLog=dn;Oe.rangeAsinh=hi;Oe.orient=tl;Oe.pxRatio=oe;Oe.join=tf;Oe.fmtDate=gi,Oe.tzDate=pf;Oe.sync=_s;{Oe.addGap=Jf,Oe.clipGaps=wn;let e=Oe.paths={points:xs};e.linear=As,e.stepped=Qf,e.bars=Xf,e.spline=ta}function ki(e,l,s,n){let o=e.series[l],a=e.data[0],u=e.data[l],h="x",m=o.scale,d=new Path2D,_=Math.abs((e.valToPos(a[0],h,!0)-e.valToPos(a[1],h,!0))/2);d.moveTo(Math.round(e.valToPos(a[0],h,!0)),Math.round(e.valToPos(u[0],m,!0)));for(let C=s;Co&&o:l=="Log10"?n=o=>o&&Math.pow(10,o):l=="Log2"&&(n=o=>o&&Math.pow(2,o)),{id:e.id+"-chart",width:s.width,height:s.height,labelSize:10,labelFont:"bold 8px Arial",ticks:{show:!1},points:{show:!1},font:"8px Arial",padding:[null,30,null,30],series:[{value:"{HH}:{mm}:{ss}"},{label:"P99",stroke:"rgb(155, 214, 206)",value:(o,a)=>Kl(n(a),3)+"ms",fill:"rgb(155, 214, 206, 0.5 )",paths:ki,scale:"ms"},{label:"P90",stroke:"rgb(79, 169, 184)",value:(o,a)=>Kl(n(a),3)+"ms",fill:"rgb(79, 169, 184, 0.5)",paths:ki,scale:"ms"},{label:"P50",stroke:"rgb(2, 88, 115)",value:(o,a)=>Kl(n(a),3)+"ms",fill:"rgb(2, 88, 115, 0.5)",paths:ki,scale:"ms"},{label:"count",stroke:"rgb(30, 30, 30)",value:(o,a)=>n(a)+"cps",scale:"calls"}],axes:[{values:[[1,"{HH}:{mm}:{ss}",null,null,null,null,null,null,1]]},{scale:"ms",grid:{show:!1},values:(o,a,u)=>a.map(h=>Kl(n(h),3)+"ms")},{side:1,values:(o,a,u)=>a.map(h=>Kl(n(h),1)+" calls"),scale:"calls",grid:{show:!1}}]}}var Ti=class{constructor(l,s,n){let o=ha(l,n);this.uplotChart=new Oe(o,s,l)}updateData(l,s){this.uplotChart.setData(l,s)}},vn="",Ls={mounted(){let e=this.el.parentElement.querySelector(".chart");vn=JSON.parse(e.dataset.scale);let l=JSON.parse(e.dataset.quantile);this.chart=new Ti(e,l,vn)},updated(){let e=this.el.parentElement.querySelector(".chart"),l=JSON.parse(e.dataset.scale);if(vn==l){let s=JSON.parse(e.dataset.quantile);this.chart.updateData(s,vn)}else this.mounted()}};var pa=document.querySelector("html").getAttribute("phx-socket")||"/live",da=document.querySelector("meta[name='csrf-token']").getAttribute("content"),ma={ChartData:Ls},Cs=new LiveView.LiveSocket(pa,Phoenix.Socket,{hooks:ma,params:{_csrf_token:da}});bn.default.config({barColors:{0:"#29d"},shadowColor:"rgba(0, 0, 0, .3)"});window.addEventListener("phx:page-loading-start",e=>bn.default.show());window.addEventListener("phx:page-loading-stop",e=>bn.default.hide());Cs.connect();window.liveSocket=Cs;})(); 4 | /*! Bundled license information: 5 | 6 | topbar/topbar.min.js: 7 | (** 8 | * @license MIT 9 | * topbar 3.0.0 10 | * http://buunguyen.github.io/topbar 11 | * Copyright (c) 2024 Buu Nguyen 12 | *) 13 | */ 14 | -------------------------------------------------------------------------------- /lib/orion.ex: -------------------------------------------------------------------------------- 1 | defmodule Orion do 2 | @external_resource "README.md" 3 | @moduledoc @external_resource 4 | |> File.read!() 5 | |> String.split("") 6 | |> Enum.fetch!(1) 7 | end 8 | -------------------------------------------------------------------------------- /lib/orion/application.ex: -------------------------------------------------------------------------------- 1 | defmodule Orion.Application do 2 | # See https://hexdocs.pm/elixir/Application.html 3 | # for more information on OTP Applications 4 | @moduledoc false 5 | 6 | use Application 7 | 8 | def start(_type, _args) do 9 | children = [ 10 | # Start the Telemetry supervisor 11 | # OrionWeb.Telemetry, 12 | # # Start the PubSub system 13 | # {Phoenix.PubSub, name: Orion.PubSub}, 14 | {Registry, keys: :duplicate, name: Orion.SessionPubsub} 15 | # # Start the Endpoint (http/https) 16 | # OrionWeb.Endpoint 17 | # Start a worker by calling: Orion.Worker.start_link(arg) 18 | # {Orion.Worker, arg} 19 | ] 20 | 21 | # See https://hexdocs.pm/elixir/Supervisor.html 22 | # for other strategies and supported options 23 | opts = [strategy: :one_for_one, name: Orion.Supervisor] 24 | Supervisor.start_link(children, opts) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/orion/match_spec.ex: -------------------------------------------------------------------------------- 1 | defmodule Orion.MatchSpec do 2 | defstruct [:module_name, :function_name, :arity] 3 | @type t :: %__MODULE__{module_name: atom(), function_name: atom, arity: integer()} 4 | 5 | def mfa(%Orion.MatchSpec{module_name: mod, function_name: fun, arity: arity}) 6 | when is_binary(mod) and is_binary(fun) and is_binary(arity) do 7 | first = String.first(mod) 8 | 9 | if String.upcase(first) == first do 10 | {Module.concat([mod]), String.to_atom(fun), String.to_integer(arity)} 11 | else 12 | {String.to_atom(mod), String.to_atom(fun), String.to_integer(arity)} 13 | end 14 | end 15 | 16 | def mfa(%Orion.MatchSpec{}) do 17 | nil 18 | end 19 | 20 | def to_phxlv_session(%__MODULE__{} = ms) do 21 | %{ 22 | "module_name" => ms.module_name, 23 | "function_name" => ms.function_name, 24 | "arity" => ms.arity 25 | } 26 | end 27 | 28 | def from_phxlv_session(session_ms) do 29 | %Orion.MatchSpec{ 30 | module_name: session_ms["module_name"], 31 | function_name: session_ms["function_name"], 32 | arity: session_ms["arity"] 33 | } 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/orion/match_spec_store.ex: -------------------------------------------------------------------------------- 1 | defmodule Orion.MatchSpecStore do 2 | alias Orion.MatchSpec 3 | 4 | @type key :: integer() 5 | @type ms_options :: %{ 6 | self_profile: boolean(), 7 | fake_data: boolean(), 8 | start_pause_status: :running | :paused, 9 | id: String.t() 10 | } 11 | @type session :: map() 12 | 13 | @spec new(key(), MatchSpec.t()) :: session() 14 | def new( 15 | key, 16 | match_spec, 17 | ms_options \\ %{ 18 | self_profile: true, 19 | fake_data: false, 20 | start_pause_status: :running, 21 | id: "default" 22 | } 23 | ) do 24 | %{ 25 | "ms" => MatchSpec.to_phxlv_session(match_spec), 26 | "key" => key, 27 | "options" => ms_options_to_phxlv_session(ms_options) 28 | } 29 | end 30 | 31 | @spec get(session()) :: %{match_spec: MatchSpec.t(), ms_options: ms_options(), key: key} 32 | def get(%{ 33 | "ms" => match_spec, 34 | "key" => key, 35 | "options" => ms_options 36 | }) do 37 | %{ 38 | match_spec: MatchSpec.from_phxlv_session(match_spec), 39 | ms_options: from_phxlv_to_ms_options(ms_options), 40 | key: key 41 | } 42 | end 43 | 44 | def ms_options_to_phxlv_session(%{ 45 | self_profile: self, 46 | fake_data: fake, 47 | start_pause_status: state, 48 | id: id 49 | }) do 50 | %{ 51 | "self_profile" => self, 52 | "fake_data" => fake, 53 | "pause_state" => state, 54 | "id" => id 55 | } 56 | end 57 | 58 | def from_phxlv_to_ms_options(%{ 59 | "self_profile" => self, 60 | "fake_data" => fake, 61 | "pause_state" => state, 62 | "id" => id 63 | }) do 64 | %{ 65 | self_profile: self, 66 | fake_data: fake, 67 | start_pause_status: state, 68 | id: id 69 | } 70 | end 71 | 72 | def string_to_boolean("true"), do: true 73 | def string_to_boolean("false"), do: false 74 | def string_to_boolean(_), do: "false" 75 | 76 | def string_to_state("running"), do: :running 77 | def string_to_state("paused"), do: :paused 78 | def string_to_state(_), do: :paused 79 | end 80 | -------------------------------------------------------------------------------- /lib/orion/session_pubsub.ex: -------------------------------------------------------------------------------- 1 | defmodule Orion.SessionPubsub do 2 | @moduledoc """ 3 | A Registry based pubsub that works per session. Allows all the LV of a session to talk together. 4 | """ 5 | 6 | def register(session, value \\ {}) do 7 | Registry.register(__MODULE__, session, value) 8 | end 9 | 10 | def dispatch(session, message) do 11 | Registry.dispatch(__MODULE__, session, fn charts -> 12 | for {pid, _} <- charts do 13 | send(pid, {:broadcast, message}) 14 | end 15 | end) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/orion_web.ex: -------------------------------------------------------------------------------- 1 | defmodule OrionWeb do 2 | @moduledoc false 3 | 4 | @doc false 5 | def html do 6 | quote do 7 | @moduledoc false 8 | use Phoenix.Component 9 | 10 | unquote(view_helpers()) 11 | end 12 | end 13 | 14 | @doc false 15 | def live_view do 16 | quote do 17 | @moduledoc false 18 | use Phoenix.LiveView 19 | unquote(view_helpers()) 20 | end 21 | end 22 | 23 | @doc false 24 | def live_component do 25 | quote do 26 | @moduledoc false 27 | use Phoenix.LiveComponent 28 | unquote(view_helpers()) 29 | end 30 | end 31 | 32 | defp view_helpers do 33 | quote do 34 | # Use all HTML functionality (forms, tags, etc) 35 | import Phoenix.HTML 36 | import Phoenix.HTML.Form 37 | use PhoenixHTMLHelpers 38 | 39 | # Import convenience functions for LiveView rendering 40 | import Phoenix.LiveView.Helpers 41 | 42 | # Import Orion Components 43 | import OrionWeb.Components 44 | end 45 | end 46 | 47 | @doc """ 48 | Convenience helper for using the functions above. 49 | """ 50 | defmacro __using__(which) when is_atom(which) do 51 | apply(__MODULE__, which, []) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/orion_web/assets.ex: -------------------------------------------------------------------------------- 1 | defmodule OrionWeb.Assets do 2 | # Plug to serve dependency-specific assets for Orion. 3 | @moduledoc false 4 | import Plug.Conn 5 | 6 | phoenix_js_paths = 7 | for app <- [:phoenix, :phoenix_html, :phoenix_live_view] do 8 | path = Application.app_dir(app, ["priv", "static", "#{app}.js"]) 9 | Module.put_attribute(__MODULE__, :external_resource, path) 10 | path 11 | end 12 | 13 | css_path = Path.join(__DIR__, "../../dist/css/app.css") 14 | @external_resource css_path 15 | @css File.read!(css_path) 16 | 17 | js_path = Path.join(__DIR__, "../../dist/js/app.js") 18 | @external_resource js_path 19 | 20 | @js """ 21 | #{for path <- phoenix_js_paths, do: path |> File.read!() |> String.replace("//# sourceMappingURL=", "// ")} 22 | #{File.read!(js_path)} 23 | """ 24 | 25 | @hashes %{ 26 | :css => Base.encode16(:crypto.hash(:md5, @css), case: :lower), 27 | :js => Base.encode16(:crypto.hash(:md5, @js), case: :lower) 28 | } 29 | 30 | def init(asset) when asset in [:css, :js], do: asset 31 | 32 | def call(conn, asset) do 33 | {contents, content_type} = contents_and_type(asset) 34 | 35 | conn 36 | |> put_private(:plug_skip_csrf_protection, true) 37 | |> put_resp_header("content-type", content_type) 38 | |> put_resp_header("cache-control", "public, max-age=31536000, immutable") 39 | |> send_resp(200, contents) 40 | |> halt() 41 | end 42 | 43 | defp contents_and_type(:css), do: {@css, "text/css"} 44 | defp contents_and_type(:js), do: {@js, "text/javascript"} 45 | 46 | @doc """ 47 | Returns the current hash for the given `asset`. 48 | """ 49 | def current_hash(:css), do: @hashes.css 50 | def current_hash(:js), do: @hashes.js 51 | end 52 | -------------------------------------------------------------------------------- /lib/orion_web/components.ex: -------------------------------------------------------------------------------- 1 | defmodule OrionWeb.Components do 2 | use Phoenix.Component 3 | 4 | @doc """ 5 | Renders an input with label and error messages. 6 | A `%Phoenix.HTML.Form{}` and field name may be passed to the input 7 | to build input names and error messages, or all the attributes and 8 | errors may be passed explicitly. 9 | ## Examples 10 | <.input field={@form[:email]} type="email" /> 11 | <.input name="my-input" errors={["oh no!"]} /> 12 | """ 13 | attr :id, :any, default: nil 14 | attr :name, :any 15 | attr :label, :string, default: nil 16 | attr :value, :any 17 | 18 | attr :type, :string, 19 | default: "text", 20 | values: ~w(checkbox color date datetime-local email file hidden month number password 21 | range radio search select tel text textarea time url week) 22 | 23 | attr :field, Phoenix.HTML.FormField, 24 | doc: "a form field struct retrieved from the form, for example: @form[:email]" 25 | 26 | attr :errors, :list, default: [] 27 | attr :checked, :boolean, doc: "the checked flag for checkbox inputs" 28 | attr :prompt, :string, default: nil, doc: "the prompt for select inputs" 29 | attr :options, :list, doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2" 30 | attr :multiple, :boolean, default: false, doc: "the multiple flag for select inputs" 31 | 32 | attr :rest, :global, 33 | include: ~w(autocomplete cols disabled form list max maxlength min minlength 34 | pattern placeholder readonly required rows size step) 35 | 36 | slot :inner_block 37 | 38 | def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do 39 | assigns 40 | |> assign(field: nil, id: assigns.id || field.id) 41 | |> assign(:errors, Enum.map(field.errors, &translate_error(&1))) 42 | |> assign_new(:name, fn -> if assigns.multiple, do: field.name <> "[]", else: field.name end) 43 | |> assign_new(:value, fn -> field.value end) 44 | |> input() 45 | end 46 | 47 | def input(%{type: "checkbox", value: value} = assigns) do 48 | assigns = 49 | assign_new(assigns, :checked, fn -> Phoenix.HTML.Form.normalize_value("checkbox", value) end) 50 | 51 | ~H""" 52 |
53 | 66 | <.error :for={msg <- @errors}><%= msg %> 67 |
68 | """ 69 | end 70 | 71 | def input(%{type: "select"} = assigns) do 72 | ~H""" 73 |
74 | <.label for={@id}><%= @label %> 75 | 85 | <.error :for={msg <- @errors}><%= msg %> 86 |
87 | """ 88 | end 89 | 90 | def input(%{type: "textarea"} = assigns) do 91 | ~H""" 92 |
93 | <.label for={@id}><%= @label %> 94 | 102 | <.error :for={msg <- @errors}><%= msg %> 103 |
104 | """ 105 | end 106 | 107 | # All other inputs text, datetime-local, url, password, etc. are handled here... 108 | def input(assigns) do 109 | ~H""" 110 |
111 | <.label for={@id}><%= @label %> 112 | 123 | <.error :for={msg <- @errors}><%= msg %> 124 |
125 | """ 126 | end 127 | 128 | @doc """ 129 | Renders a label. 130 | """ 131 | attr :for, :string, default: nil 132 | slot :inner_block, required: true 133 | 134 | def label(assigns) do 135 | ~H""" 136 | 139 | """ 140 | end 141 | 142 | @doc """ 143 | Generates a generic error message. 144 | """ 145 | slot :inner_block, required: true 146 | 147 | def error(assigns) do 148 | ~H""" 149 |

150 | <%= render_slot(@inner_block) %> 151 |

152 | """ 153 | end 154 | 155 | def translate_error({msg, opts}) do 156 | # You can make use of gettext to translate error messages by 157 | # uncommenting and adjusting the following code: 158 | 159 | # if count = opts[:count] do 160 | # Gettext.dngettext(<%= @web_namespace %>.Gettext, "errors", msg, msg, count, opts) 161 | # else 162 | # Gettext.dgettext(<%= @web_namespace %>.Gettext, "errors", msg, opts) 163 | # end 164 | 165 | Enum.reduce(opts, msg, fn {key, value}, acc -> 166 | String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end) 167 | end) 168 | end 169 | 170 | @doc """ 171 | Translates the errors for a field from a keyword list of errors. 172 | """ 173 | def translate_errors(errors, field) when is_list(errors) do 174 | for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts}) 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /lib/orion_web/layout_view.ex: -------------------------------------------------------------------------------- 1 | defmodule OrionWeb.LayoutView do 2 | @moduledoc false 3 | use OrionWeb, :html 4 | 5 | embed_templates("layouts/*") 6 | 7 | def render("home.html", assigns), do: home(assigns) 8 | 9 | defp csp_nonce(conn, type) when type in [:script, :style] do 10 | csp_nonce_assign_key = 11 | conn.private.csp_nonce_assign_key[type] 12 | 13 | conn.assigns[csp_nonce_assign_key] 14 | end 15 | 16 | def live_socket_path(conn) do 17 | [Enum.map(conn.script_name, &["/" | &1]) | conn.private.live_socket_path] 18 | end 19 | 20 | # TODO: Remove this and the conditional on Phoenix v1.7+ 21 | @compile {:no_warn_undefined, Phoenix.VerifiedRoutes} 22 | 23 | defp asset_path(conn, asset) when asset in [:css, :js] do 24 | hash = OrionWeb.Assets.current_hash(asset) 25 | 26 | if function_exported?(conn.private.phoenix_router, :__live_orion_prefix__, 0) do 27 | prefix = conn.private.phoenix_router.__live_orion_prefix__() 28 | 29 | Phoenix.VerifiedRoutes.unverified_path( 30 | conn, 31 | conn.private.phoenix_router, 32 | "#{prefix}/#{asset}-#{hash}" 33 | ) 34 | else 35 | apply( 36 | conn.private.phoenix_router.__helpers__(), 37 | :live_orion_asset_path, 38 | [conn, asset, hash] 39 | ) 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/orion_web/layouts/home.html.heex: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | <.live_title prefix="Orion - "><%= assigns[:page_title] || "Orion" %> 13 | 14 |