├── .gitignore ├── Makefile ├── README.md ├── docs └── img │ └── screenshot.png ├── frontend ├── .gitignore ├── components.d.ts ├── index.html ├── package.json ├── postcss.config.js ├── public │ └── favicon.ico ├── src │ ├── App.vue │ ├── assets │ │ ├── index.css │ │ └── logo.png │ ├── components │ │ ├── EventBadge.vue │ │ ├── SiteQueue.vue │ │ └── StatusBar.vue │ ├── const.ts │ ├── env.d.ts │ ├── main.ts │ ├── models │ │ ├── events_queue.ts │ │ └── site.ts │ ├── state.ts │ └── ws.ts ├── tailwind.config.js ├── tsconfig.json └── vite.config.ts ├── livefeed ├── __init__.py ├── apps.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── feed_mq.py │ │ ├── init_db.py │ │ └── install.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── templates │ ├── app │ │ └── index.html │ ├── base.html │ ├── index.html │ ├── livefeed.html │ └── login_form.html ├── urls.py └── views.py ├── sandbox ├── __init__.py ├── manage.py ├── settings │ ├── __init__.py │ ├── base.py │ ├── demo.py │ └── tests.py ├── urls.py └── wsgi.py ├── setup.cfg ├── setup.py ├── static └── app │ └── assets │ ├── index-ef32a193.css │ ├── index.js │ └── vendor-6f17e660.js ├── tests ├── __init__.py ├── conftest.py └── utils.py └── theme ├── __init__.py ├── apps.py └── static_src ├── .gitignore ├── package.json ├── postcss.config.js ├── src └── styles.css └── tailwind.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # virtualenv 2 | .venv 3 | 4 | # Packaging 5 | build 6 | dist 7 | *.egg-info 8 | **/node_modules/ 9 | yarn.lock 10 | package-lock.json 11 | /centrifugo 12 | 13 | # Byte-compiled / optimized / DLL files 14 | __pycache__/ 15 | *.py[cod] 16 | 17 | # Site media and builded statics 18 | /var/ 19 | /sandbox/static 20 | 21 | # Settings 22 | localsettings.py 23 | 24 | # Unit test / coverage reports 25 | htmlcov/ 26 | .coverage 27 | .cache 28 | .pytest_cache 29 | nosetests.xml 30 | coverage.xml 31 | .tox 32 | 33 | # VS Code 34 | .vscode 35 | 36 | # Mac Os 37 | .DS_Store 38 | 39 | # Temp files 40 | *~ 41 | .~lock* 42 | 43 | # Swap files 44 | *.sw[po] 45 | 46 | # Logging files 47 | *.log 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VENV_PATH=.venv 2 | PYTHON_INTERPRETER=python3 3 | PYTHON_BIN=$(VENV_PATH)/bin/python 4 | PIP=$(VENV_PATH)/bin/pip 5 | TWINE=$(VENV_PATH)/bin/twine 6 | DJANGO_MANAGE=$(VENV_PATH)/bin/python sandbox/manage.py 7 | FLAKE=$(VENV_PATH)/bin/flake8 8 | BLACK=$(VENV_PATH)/bin/black 9 | PYTEST=$(VENV_PATH)/bin/pytest 10 | PYRIGTH=$(VENV_PATH)/bin/pyright 11 | SPHINX_RELOAD=$(VENV_PATH)/bin/python sphinx_reload.py 12 | 13 | DEMO_DJANGO_SECRET_KEY=samplesecretfordev 14 | APPLICATION_NAME=livefeed 15 | PACKAGE_SLUG=`echo $(APPLICATION_NAME) | tr '-' '_'` 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo 20 | @echo " install -- to install this project with virtualenv and Pip" 21 | @echo " initdb -- to initialize the database" 22 | @echo " pushevents -- to generate fake events for the demo" 23 | @echo 24 | @echo " clean -- to clean EVERYTHING (Warning)" 25 | @echo " clean-var -- to clean data (uploaded medias, database, etc..)" 26 | @echo " clean-install -- to clean Python side installation" 27 | @echo " clean-pycache -- to remove all __pycache__, this is recursive from current directory" 28 | @echo 29 | @echo " run -- to run Django development server" 30 | @echo " runws -- to run the websockets server" 31 | @echo " watchtw -- to run the Tailwind watch autocompile" 32 | @echo " buildtw -- to run build Tailwind for production" 33 | @echo " migrate -- to apply demo database migrations" 34 | @echo " migrations -- to create new migrations for application after changes" 35 | @echo " superuser -- to create a superuser for Django admin" 36 | @echo 37 | @echo " flake -- to launch Flake8 checking" 38 | @echo " test -- to launch base test suite using Pytest" 39 | @echo " test-initial -- to launch tests with pytest and re-initialized database (for after new application or model changes)" 40 | @echo " quality -- to launch Flake8 checking and every tests suites" 41 | @echo " codecheck -- check the code quality with Pycheck" 42 | @echo 43 | 44 | clean-pycache: 45 | @echo "" 46 | @echo "==== Clear Python cache ====" 47 | @echo "" 48 | rm -Rf .pytest_cache 49 | find . -type d -name "__pycache__"|xargs rm -Rf 50 | find . -name "*\.pyc"|xargs rm -f 51 | .PHONY: clean-pycache 52 | 53 | clean-install: 54 | @echo "" 55 | @echo "==== Clear installation ====" 56 | @echo "" 57 | rm -Rf $(VENV_PATH) 58 | rm -Rf $(PACKAGE_SLUG).egg-info 59 | .PHONY: clean-install 60 | 61 | clean-var: 62 | @echo "" 63 | @echo "==== Clear var/ directory ====" 64 | @echo "" 65 | rm -Rf var 66 | .PHONY: clean-var 67 | 68 | clean: clean-var clean-doc clean-install clean-pycache 69 | .PHONY: clean 70 | 71 | venv: 72 | @echo "" 73 | @echo "==== Install virtual environment ====" 74 | @echo "" 75 | virtualenv -p $(PYTHON_INTERPRETER) $(VENV_PATH) 76 | # This is required for those ones using old distribution 77 | $(PIP) install --upgrade pip 78 | $(PIP) install --upgrade setuptools 79 | .PHONY: venv 80 | 81 | create-var-dirs: 82 | @mkdir -p var/db 83 | @mkdir -p var/static/css 84 | @mkdir -p var/media 85 | @mkdir -p sandbox/media 86 | @mkdir -p sandbox/static/css 87 | .PHONY: create-var-dirs 88 | 89 | installws: 90 | @echo "" 91 | @echo "==== Installing the local websockets server ====" 92 | @echo "" 93 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 94 | $(DJANGO_MANAGE) install 95 | @echo "" 96 | @echo " To run the demo:" 97 | @echo " - run make runws in one terminal for the websockets server" 98 | @echo " - run make run in another terminal for the http server" 99 | @echo "" 100 | .PHONY: installws 101 | 102 | install: venv create-var-dirs 103 | @echo "" 104 | @echo "==== Installing the backend ====" 105 | @echo "" 106 | $(PIP) install -e .[dev] 107 | rm -Rf $(PACKAGE_SLUG).egg-info 108 | ${MAKE} migrate 109 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 110 | $(DJANGO_MANAGE) tailwind install 111 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 112 | $(DJANGO_MANAGE) tailwind build 113 | ${MAKE} installws 114 | ${MAKE} initdb 115 | .PHONY: install 116 | 117 | initdb: 118 | @echo "" 119 | @echo "==== Initializing database ====" 120 | @echo "" 121 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 122 | $(DJANGO_MANAGE) init_db 123 | ${MAKE} superuser 124 | .PHONY: initdb 125 | 126 | pushevents: 127 | @echo "" 128 | @echo "==== Generating events ====" 129 | @echo "" 130 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 131 | $(DJANGO_MANAGE) feed_mq 132 | .PHONY: pushevents 133 | 134 | migrations: 135 | @echo "" 136 | @echo "==== Making application migrations ====" 137 | @echo "" 138 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 139 | $(DJANGO_MANAGE) makemigrations $(APPLICATION_NAME) 140 | .PHONY: migrations 141 | 142 | migrate: 143 | @echo "" 144 | @echo "==== Apply pending migrations ====" 145 | @echo "" 146 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 147 | $(DJANGO_MANAGE) migrate 148 | .PHONY: migrate 149 | 150 | superuser: 151 | @echo "" 152 | @echo "==== Create new superuser ====" 153 | @echo "" 154 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 155 | $(DJANGO_MANAGE) createsuperuser 156 | .PHONY: superuser 157 | 158 | run: 159 | @echo "" 160 | @echo "==== Running development server ====" 161 | @echo "" 162 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 163 | $(DJANGO_MANAGE) runserver 0.0.0.0:8000 164 | .PHONY: run 165 | 166 | runws: 167 | @echo "" 168 | @echo "==== Running the websockets server ====" 169 | @echo "" 170 | $(DJANGO_MANAGE) runws 171 | .PHONY: runws 172 | 173 | buildtw: 174 | @echo "" 175 | @echo "==== Building Tailwind for production ====" 176 | @echo "" 177 | $(DJANGO_MANAGE) tailwind build 178 | .PHONY: buildtw 179 | 180 | watchtw: 181 | @echo "" 182 | @echo "==== Running Tailwind watch autocompile ====" 183 | @echo "" 184 | $(DJANGO_MANAGE) tailwind start 185 | .PHONY: watchtw 186 | 187 | shell: 188 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 189 | $(DJANGO_MANAGE) shell_plus 190 | .PHONY: shell 191 | 192 | check: 193 | @DJANGO_SECRET_KEY=$(DEMO_DJANGO_SECRET_KEY) \ 194 | $(DJANGO_MANAGE) check 195 | .PHONY: check 196 | 197 | flake: 198 | @echo "" 199 | @echo "==== Flake ====" 200 | @echo "" 201 | $(FLAKE) --show-source $(APPLICATION_NAME) 202 | $(FLAKE) --show-source tests 203 | .PHONY: flake 204 | 205 | test: 206 | @echo "" 207 | @echo "==== Tests ====" 208 | @echo "" 209 | $(PYTEST) -vv --reuse-db tests/ 210 | rm -Rf var/media-tests/ 211 | .PHONY: test 212 | 213 | test-initial: 214 | @echo "" 215 | @echo "==== Tests from zero ====" 216 | @echo "" 217 | $(PYTEST) -vv --reuse-db --create-db tests/ 218 | rm -Rf var/media-tests/ 219 | .PHONY: test-initial 220 | 221 | quality: test-initial freeze-dependencies 222 | @echo "" 223 | @echo "♥ ♥ Everything should be fine ♥ ♥" 224 | @echo "" 225 | .PHONY: quality 226 | 227 | format: 228 | $(BLACK) --extend-exclude='/*/migrations/*|setup.py' . 229 | .PHONY: format 230 | 231 | dryformat: 232 | $(BLACK) --extend-exclude='/*/migrations/*|setup.py' --check . 233 | .PHONY: dryformat 234 | 235 | codecheck: 236 | pycheck --django 237 | .PHONY: codecheck 238 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Django Mqueue Livefeed 2 | ====================== 3 | 4 | A demo showing how to monitor events coming from Django sites via websockets with [django-mqueue](https://github.com/synw/django-mqueue) 5 | and [django-instant](https://github.com/synw/django-instant). Two versions are available: 6 | 7 | - [Single page app](#single-page-app) with Vuejs 8 | - [Template demo](#template-demo) with Alpinejs 9 | 10 | 11 | Install 12 | ------- 13 | 14 | Clone the repository and install the project: 15 | 16 | ``` 17 | make install 18 | ``` 19 | 20 | This will install the backend and a local Centrifugo websockets server for the demo 21 | 22 | Or install the Centrifugo websockets server [manualy](https://github.com/synw/django-instant#quick-start) 23 | and configure the Django settings 24 | 25 | Template demo 26 | ------------- 27 | 28 | Run the http server: 29 | 30 | ``` 31 | make run 32 | ``` 33 | 34 | Run the websockets server in another terminal: 35 | 36 | ``` 37 | make runws 38 | ``` 39 | 40 | Open the frontend at localhost:8000 and login with your superuser 41 | 42 | In another terminal auto generate events for the demo: 43 | 44 | ``` 45 | make pushevents 46 | ``` 47 | 48 | Watch the events coming in the frontend 49 | 50 | Single page app 51 | --------------- 52 | 53 | Run the same commands as above and click the spa link in frontend to see the 54 | compiled demo. To run it in dev mode and build it read the instructions below. 55 | 56 | The frontend is a Vuejs 3 Typescript app running on Vitejs. It is compiled to be served by 57 | Django as static files and an *index.html* template. 58 | 59 | To install the frontend for dev mode: 60 | ``` 61 | yarn install 62 | ``` 63 | 64 | To run the frontend with Vitejs in dev mode on localhost:3000: 65 | 66 | ``` 67 | yarn dev 68 | ``` 69 | 70 | To compile the frontend to Django template and static files: 71 | 72 | ``` 73 | yarn build 74 | ``` 75 | 76 | Screenshot 77 | ---------- 78 | 79 | ![Livefeed screenshot](docs/img/screenshot.png) 80 | 81 | -------------------------------------------------------------------------------- /docs/img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/docs/img/screenshot.png -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | !/doc/dist 6 | *.local 7 | yarn.lock 8 | yarn-error.log 9 | .yarnrc 10 | /dev 11 | .vscode 12 | storybook-static -------------------------------------------------------------------------------- /frontend/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/vue-next/pull/3399 4 | 5 | declare module 'vue' { 6 | export interface GlobalComponents { 7 | EventBadge: typeof import('./src/components/EventBadge.vue')['default'] 8 | IFaRegularBellSlash: typeof import('~icons/fa-regular/bell-slash')['default'] 9 | IGisEarthNetworkO: typeof import('~icons/gis/earth-network-o')['default'] 10 | SiteQueue: typeof import('./src/components/SiteQueue.vue')['default'] 11 | StatusBar: typeof import('./src/components/StatusBar.vue')['default'] 12 | } 13 | } 14 | 15 | export { } 16 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Mqueue Livefeed 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "livefeed", 3 | "version": "0.3.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "serve": "vite preview", 8 | "build:prepare": "del -f ../static/app/* ../livefeed/templates/app/index.html", 9 | "build:build": "vite build", 10 | "build:moveindex": "move-file ../static/app/index.html ../livefeed/templates/app/index.html", 11 | "build:clean": "del ./dist", 12 | "build": "run-s build::prepare build::build build::moveindex build:clean" 13 | }, 14 | "dependencies": { 15 | "djangoinstant": "^0.2.0", 16 | "vue": "^3.2.25" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "^7.17.0", 20 | "@iconify/json": "^2.0.33", 21 | "@iconify/vue": "^3.1.3", 22 | "@snowind/header": "^0.0.8", 23 | "@snowind/input": "^0.0.4", 24 | "@snowind/plugin": "^0.0.8", 25 | "@snowind/sidebar": "^0.0.3", 26 | "@snowind/state": "^0.0.2", 27 | "@snowind/switch": "^0.0.4", 28 | "@snowind/toast": "^0.0.1", 29 | "@tailwindcss/forms": "^0.4.0", 30 | "@vitejs/plugin-vue": "^2.0.0", 31 | "@vue/compiler-sfc": "^3.2.19", 32 | "autoprefixer": "^10.4.2", 33 | "babel-loader": "^8.2.3", 34 | "postcss": "^8.4.6", 35 | "rollup-plugin-typescript2": "^0.31.2", 36 | "sass": "^1.49.7", 37 | "tailwindcss": "^3.0.18", 38 | "tslib": "^2.3.1", 39 | "typescript": "^4.4.4", 40 | "unplugin-icons": "^0.13.0", 41 | "unplugin-vue-components": "^0.17.16", 42 | "vite": "^2.7.2", 43 | "vue-loader": "^16.8.3", 44 | "vue-tsc": "^0.29.8", 45 | "del-cli": "^4.0.1", 46 | "move-file-cli": "^3.0.0", 47 | "npm-run-all": "^4.1.5" 48 | } 49 | } -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('tailwindcss'), 4 | require('autoprefixer'), 5 | ] 6 | } -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/src/assets/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /frontend/src/components/EventBadge.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/components/SiteQueue.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 45 | 46 | -------------------------------------------------------------------------------- /frontend/src/components/StatusBar.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 33 | -------------------------------------------------------------------------------- /frontend/src/const.ts: -------------------------------------------------------------------------------- 1 | import Site from "@/models/site"; 2 | 3 | const sites: Record = { 4 | "Site 1": new Site("Site 1"), 5 | "Site 2": new Site("Site 2"), 6 | "Site 3": new Site("Site 3"), 7 | "Site 4": new Site("Site 4"), 8 | "Site 5": new Site("Site 5"), 9 | "Site 6": new Site("Site 6"), 10 | } 11 | 12 | const eventIcons: Record = { 13 | "important": 'fa fa-exclamation', 14 | "ok": 'fa fa-thumbs-up', 15 | "info": 'fa fa-info-circle', 16 | "debug": 'fa fa-cog', 17 | "warning": 'fa fa-exclamation', 18 | "error": 'fa fa-exclamation-triangle', 19 | "edited": 'fas fa-edit', 20 | "created": 'fas fa-plus', 21 | "deleted": 'far fa-trash-alt', 22 | } 23 | 24 | const eventTypes = Object.keys(eventIcons); 25 | 26 | export { sites, eventIcons, eventTypes }; -------------------------------------------------------------------------------- /frontend/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import './assets/index.css'; 4 | 5 | createApp(App).mount('#app') 6 | -------------------------------------------------------------------------------- /frontend/src/models/events_queue.ts: -------------------------------------------------------------------------------- 1 | import { eventTypes } from "@/const"; 2 | import { reactive } from "vue"; 3 | 4 | export default class EventsQueue { 5 | counter = reactive>({}); 6 | 7 | constructor() { 8 | // initialize event types counter 9 | eventTypes.forEach((et) => { 10 | this.counter[et] = 0; 11 | }); 12 | } 13 | 14 | increment(eventType: string): void { 15 | this.counter[eventType]++; 16 | } 17 | } -------------------------------------------------------------------------------- /frontend/src/models/site.ts: -------------------------------------------------------------------------------- 1 | import { computed, ref, Ref } from "vue"; 2 | import { Message } from "djangoinstant"; 3 | 4 | export default class Site { 5 | name: string; 6 | messages: Ref> = ref([]); 7 | numMessages = computed(() => this.messages.value.length); 8 | 9 | constructor(name: string) { 10 | this.name = name; 11 | } 12 | 13 | get hasMessages(): boolean { 14 | return this.messages.value.length > 0; 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /frontend/src/state.ts: -------------------------------------------------------------------------------- 1 | import { User } from "@snowind/state"; 2 | import EventsQueue from "./models/events_queue"; 3 | 4 | const eventsQueue = new EventsQueue(); 5 | const user = new User(); 6 | 7 | export { user, eventsQueue } -------------------------------------------------------------------------------- /frontend/src/ws.ts: -------------------------------------------------------------------------------- 1 | import { Instant, Message } from "djangoinstant"; 2 | import { sites } from "@/const"; 3 | import { eventsQueue } from "./state"; 4 | 5 | let instant = new Instant( 6 | "http://localhost:8000", // Django backend's address 7 | "ws://localhost:8427", // Centrifugo server's address 8 | true, // verbosity (optional, default: false) 9 | ); 10 | 11 | function _onMessage(msg: Message): void { 12 | //console.log("MSG", typeof msg, JSON.stringify(msg, null, " ")); 13 | sites[msg.site].messages.value.unshift(msg); 14 | eventsQueue.increment(msg.eventClass); 15 | } 16 | 17 | function initWs() { 18 | const uriFromBackend = localStorage.getItem("wsUri"); 19 | let uri = "ws://localhost:8427"; 20 | if (uriFromBackend !== null && import.meta.env.PROD) { 21 | uri = uriFromBackend 22 | } 23 | instant = new Instant("http://localhost:8000", uri, true); 24 | console.log("Ws uri", instant.websocketsUri) 25 | instant.onMessage = _onMessage; 26 | } 27 | 28 | async function connectWs() { 29 | await instant.get_token(); 30 | await instant.connect(); 31 | } 32 | 33 | export { instant, initWs, connectWs } -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const colors = require('tailwindcss/colors') 2 | 3 | module.exports = { 4 | content: [ 5 | './index.html', 6 | './src/**/*.{js,jsx,ts,tsx,vue}', 7 | './node_modules/@snowind/**/*.{vue,js,ts}', 8 | ], 9 | darkMode: 'class', 10 | plugins: [ 11 | require('@tailwindcss/forms'), 12 | require('@snowind/plugin') 13 | ], 14 | /*theme: { 15 | extend: { 16 | colors: { 17 | 'background': { 18 | DEFAULT: colors.white, 19 | dark: colors.neutral[900] 20 | }, 21 | 'primary': { 22 | DEFAULT: colors.cyan[700], 23 | dark: colors.cyan[800], 24 | }, 25 | 'secondary': { 26 | DEFAULT: colors.cyan[500], 27 | dark: colors.slate[600], 28 | }, 29 | 'light': { 30 | DEFAULT: colors.slate[200], 31 | dark: colors.neutral[700] 32 | }, 33 | } 34 | }, 35 | }*/ 36 | } -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "allowJs": true, 12 | "lib": [ 13 | "esnext", 14 | "dom" 15 | ], 16 | "types": [ 17 | "vite/client" 18 | ], 19 | "baseUrl": ".", 20 | "paths": { 21 | "@/*": [ 22 | "src/*" 23 | ] 24 | } 25 | }, 26 | "include": [ 27 | "src/**/*.ts", 28 | "src/**/*.d.ts", 29 | "src/**/*.tsx", 30 | "src/**/*.vue" 31 | ] 32 | } -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { defineConfig } from 'vite' 3 | import typescript2 from "rollup-plugin-typescript2" 4 | import vue from '@vitejs/plugin-vue' 5 | import Components from 'unplugin-vue-components/vite' 6 | import Icons from 'unplugin-icons/vite' 7 | import IconsResolver from 'unplugin-icons/resolver' 8 | 9 | export default defineConfig({ 10 | plugins: [ 11 | typescript2({ 12 | check: false, 13 | tsconfig: path.resolve(__dirname, 'tsconfig.json'), 14 | clean: true 15 | }), 16 | vue(), 17 | Components({ 18 | resolvers: [ 19 | IconsResolver() 20 | ], 21 | }), 22 | Icons({ 23 | scale: 1.2, 24 | defaultClass: 'inline-block align-middle', 25 | compiler: 'vue3', 26 | }), 27 | ], 28 | base: process.env.NODE_ENV === 'production' ? '/static/app/' : './', 29 | resolve: { 30 | alias: [ 31 | { find: '@/', replacement: '/src/' } 32 | ] 33 | }, 34 | publicDir: "../static/app", 35 | build: { 36 | emptyOutDir: false, 37 | rollupOptions: { 38 | output: { 39 | dir: "../static/app", 40 | chunkFileNames: "assets/[name]-[hash].js", 41 | entryFileNames: "assets/[name].js", 42 | assetFileNames: "assets/[name]-[hash][extname]" 43 | } 44 | }, 45 | } 46 | }) -------------------------------------------------------------------------------- /livefeed/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | 3 | import os 4 | from setuptools.config import read_configuration 5 | 6 | import pkg_resources 7 | 8 | PROJECT_DIR = os.path.join(os.path.dirname(__file__), "..") 9 | 10 | 11 | def _extract_version(package_name): 12 | """ 13 | Get package version from installed distribution or configuration file if not 14 | installed 15 | """ 16 | try: 17 | return pkg_resources.get_distribution(package_name).version 18 | except pkg_resources.DistributionNotFound: 19 | _conf = read_configuration(os.path.join(PROJECT_DIR, "setup.cfg")) 20 | return _conf["metadata"]["version"] 21 | 22 | 23 | __version__ = "" 24 | -------------------------------------------------------------------------------- /livefeed/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class livefeedConfig(AppConfig): 5 | name = "livefeed" 6 | verbose_name = "livefeed" 7 | default_auto_field = "django.db.models.AutoField" 8 | -------------------------------------------------------------------------------- /livefeed/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/livefeed/management/__init__.py -------------------------------------------------------------------------------- /livefeed/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/livefeed/management/commands/__init__.py -------------------------------------------------------------------------------- /livefeed/management/commands/feed_mq.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | import random 3 | 4 | from django.core.management.base import BaseCommand 5 | 6 | from mqueue.models import MEvent 7 | from livefeed.models import MonitoredSite 8 | 9 | 10 | class Command(BaseCommand): 11 | help = "Feed mqueue with test events" 12 | 13 | def handle(self, *args, **options): 14 | ecs = [ 15 | "error", 16 | "warning", 17 | "created", 18 | "edited", 19 | "deleted", 20 | "info", 21 | "debug", 22 | "important", 23 | "ok", 24 | ] 25 | sites = list(MonitoredSite.objects.all().values_list("name", flat=True)) 26 | # feed with events 27 | while True: 28 | site = random.choice(sites) 29 | ec = random.choice(ecs) 30 | data = dict(site=site) 31 | MEvent.objects.create( 32 | name="An event has occured", event_class=ec, data=data 33 | ) 34 | delay = random.uniform(0.2, 3) 35 | sleep(delay) 36 | -------------------------------------------------------------------------------- /livefeed/management/commands/init_db.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from instant.models import Channel 4 | from livefeed.models import MonitoredSite 5 | 6 | 7 | class Command(BaseCommand): 8 | help = "Initialize the database with some data" 9 | 10 | def handle(self, *args, **options): 11 | sites = ["site1", "site2", "site3", "site4", "site5", "site6"] 12 | titles = ["Site 1", "Site 2", "Site 3", "Site 4", "Site 5", "Site 6"] 13 | # create the monitored site objects 14 | i = 0 15 | for slug in sites: 16 | title = titles[i] 17 | _, created = MonitoredSite.objects.get_or_create(slug=slug, name=title) 18 | if created is True: 19 | print("Created monitored site", title) 20 | i += 1 21 | # create the websockets channel 22 | _, created = Channel.objects.get_or_create(name="$livefeed", level="superuser") 23 | if created is True: 24 | print("Created websockets channel") 25 | -------------------------------------------------------------------------------- /livefeed/management/commands/install.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import subprocess 4 | from typing import List 5 | 6 | from django.core.management.base import BaseCommand 7 | 8 | from instant.init import generate_settings_from_conf 9 | 10 | 11 | class Command(BaseCommand): 12 | help = "Install the websockets server and generate the settings" 13 | 14 | def handle(self, *args, **options): 15 | basepath = os.getcwd() 16 | subprocess.call( 17 | [ 18 | basepath + "/.venv/bin/python", 19 | basepath + "/sandbox/manage.py", 20 | "installws", 21 | ] 22 | ) 23 | filepath = basepath + "/centrifugo/config.json" 24 | settings: List[str] = [] 25 | with open(filepath, "r") as f: 26 | content = f.read() 27 | conf = json.loads(content) 28 | settings = generate_settings_from_conf(conf) 29 | print(settings) 30 | f.close() 31 | settingspath = basepath + "/localsettings.py" 32 | if not os.path.exists(settingspath): 33 | with open(settingspath, "w") as f: 34 | f.write("\n".join(settings) + "\n") 35 | f.close() 36 | print("localsettings.py file generated with websockets config") 37 | -------------------------------------------------------------------------------- /livefeed/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.3 on 2022-03-03 09:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='MonitoredSite', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=120, verbose_name='Name')), 19 | ('slug', models.SlugField(unique=True, verbose_name='Slug')), 20 | ], 21 | options={ 22 | 'verbose_name': 'Monitored site', 23 | 'verbose_name_plural': 'Monitored sites', 24 | 'ordering': ['name'], 25 | }, 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /livefeed/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/livefeed/migrations/__init__.py -------------------------------------------------------------------------------- /livefeed/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import models 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | 6 | class MonitoredSite(models.Model): 7 | name = models.CharField(max_length=120, verbose_name=_("Name")) 8 | slug = models.SlugField(unique=True, verbose_name=_("Slug")) 9 | 10 | class Meta: 11 | verbose_name = _("Monitored site") 12 | verbose_name_plural = _("Monitored sites") 13 | ordering = ["name"] 14 | 15 | def __str__(self): 16 | return self.name 17 | -------------------------------------------------------------------------------- /livefeed/templates/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Mqueue Livefeed 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /livefeed/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static tailwind_tags %} 2 | 3 | 4 | 5 | 6 | 7 | {% block head_title %}Django Mqueue livefeed{% endblock head_title %} 8 | {% block head_extra %}{% endblock head_extra %} 9 | {% tailwind_css %} 10 | 11 | 12 | 13 |
14 |
Mqueue livefeed
15 |
16 | {% block content %}{% endblock %} 17 |
18 |
19 | {% block endbody %}{% endblock %} 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /livefeed/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 | 13 |
14 | {% endblock content %} 15 | -------------------------------------------------------------------------------- /livefeed/templates/livefeed.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static %} 3 | 4 | {% block head_extra %} 5 | 6 | 7 | 8 | 9 | 10 | {% endblock %} 11 | 12 | {% block content %} 13 |
14 | {% include "mqueue/livefeed/messages.html" %} 15 |
16 | {% endblock %} 17 | 18 | {% block endbody %} 19 | 20 | 21 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /livefeed/templates/login_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block head_title %}LoginView{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
{% csrf_token %} 9 | {{ form.as_p }} 10 |
11 | 12 |
13 |
14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /livefeed/urls.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import TemplateView 2 | from django.urls import path, include 3 | from django.conf import settings 4 | from django.conf.urls.static import static 5 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 6 | 7 | from .views import LoginView, IndexView, SpaAppView 8 | 9 | urlpatterns = [ 10 | path( 11 | "template_demo/", 12 | TemplateView.as_view(template_name="mqueue/livefeed/index.html"), 13 | ), 14 | path("instant/", include("instant.urls")), 15 | path("login/", LoginView.as_view(), name="login"), 16 | path("app/", SpaAppView.as_view(), name="home"), 17 | path("", IndexView.as_view()), 18 | ] 19 | 20 | if settings.DEBUG is True: 21 | urlpatterns = ( 22 | urlpatterns 23 | + staticfiles_urlpatterns() 24 | + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 25 | ) 26 | -------------------------------------------------------------------------------- /livefeed/views.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, Union 2 | from django.conf import settings 3 | from django.http.response import HttpResponseBase, HttpResponseRedirect 4 | from django.views.generic import TemplateView 5 | from django.contrib.auth.views import LoginView, redirect_to_login 6 | 7 | 8 | class IndexView(LoginView): 9 | template_name = "index.html" 10 | 11 | def dispatch( 12 | self, request, *args, **kwargs 13 | ) -> Union[HttpResponseRedirect, HttpResponseBase]: 14 | if self.request.user.is_superuser is False: # type: ignore 15 | return redirect_to_login("/", "/login/") 16 | return super(IndexView, self).dispatch(request, *args, **kwargs) 17 | 18 | 19 | class LoginView(LoginView): 20 | template_name = "login_form.html" 21 | 22 | 23 | class SpaAppView(TemplateView): 24 | template_name = "app/index.html" 25 | 26 | def dispatch( 27 | self, request, *args, **kwargs 28 | ) -> Union[HttpResponseRedirect, HttpResponseBase]: 29 | if self.request.user.is_superuser is False: # type: ignore 30 | return redirect_to_login("/", "/login/") 31 | return super(SpaAppView, self).dispatch(request, *args, **kwargs) 32 | 33 | def get_context_data(self, **kwargs) -> Dict[str, Any]: 34 | context = super(SpaAppView, self).get_context_data(**kwargs) 35 | uri = settings.CENTRIFUGO_HOST.replace("https", "wss").replace("http", "ws") 36 | if settings.CENTRIFUGO_PORT is not None: 37 | uri += f":{settings.CENTRIFUGO_PORT}" 38 | context["uri"] = uri 39 | return context 40 | -------------------------------------------------------------------------------- /sandbox/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/sandbox/__init__.py -------------------------------------------------------------------------------- /sandbox/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Django management script for sandbox 4 | """ 5 | import os 6 | 7 | from django.core import management 8 | 9 | if __name__ == "__main__": 10 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.demo") 11 | 12 | management.execute_from_command_line() 13 | -------------------------------------------------------------------------------- /sandbox/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/sandbox/settings/__init__.py -------------------------------------------------------------------------------- /sandbox/settings/base.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: F403,F405 2 | """ 3 | Base Django settings for sandbox 4 | """ 5 | 6 | from os.path import abspath, dirname, join, normpath 7 | 8 | 9 | SECRET_KEY = "***TOPSECRET***" 10 | 11 | 12 | # Root of project 13 | BASE_DIR = normpath( 14 | join( 15 | dirname(abspath(__file__)), 16 | "..", 17 | "..", 18 | ) 19 | ) 20 | 21 | # Django project 22 | PROJECT_PATH = join(BASE_DIR, "sandbox") 23 | VAR_PATH = join(BASE_DIR, "var") 24 | 25 | DEBUG = False 26 | 27 | # Https is never enabled on default and development environment, only for 28 | # integration and production. 29 | HTTPS_ENABLED = False 30 | 31 | ADMINS = ( 32 | # ("Admin", "PUT_ADMIN_EMAIL_HERE"), 33 | ) 34 | 35 | INTERNAL_IPS = [ 36 | "127.0.0.1", 37 | ] 38 | 39 | MANAGERS = ADMINS 40 | 41 | DATABASES = {} 42 | 43 | # Hosts/domain names that are valid for this site; required if DEBUG is False 44 | # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts 45 | ALLOWED_HOSTS = ["*"] 46 | 47 | # Local time zone for this installation. Choices can be found here: 48 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 49 | # although not all choices may be available on all operating systems. 50 | # In a Windows environment this must be set to your system time zone. 51 | TIME_ZONE = "America/Chicago" 52 | 53 | # Language code for this installation. All choices can be found here: 54 | # http://www.i18nguy.com/unicode/language-identifiers.html 55 | LANGUAGE_CODE = "en" 56 | 57 | LANGUAGES = ( 58 | ("en", "English"), 59 | ("fr", "Français"), 60 | ) 61 | 62 | # A tuple of directories where Django looks for translation files 63 | LOCALE_PATHS = [ 64 | join(PROJECT_PATH, "locale"), 65 | ] 66 | 67 | SITE_ID = 1 68 | 69 | # If you set this to False, Django will make some optimizations so as not 70 | # to load the internationalization machinery. 71 | USE_I18N = True 72 | 73 | # If you set this to False, Django will not format dates, numbers and 74 | # calendars according to the current locale. 75 | USE_L10N = True 76 | 77 | # If you set this to False, Django will not use timezone-aware datetimes. 78 | USE_TZ = True 79 | 80 | # Absolute filesystem path to the directory that will hold user-uploaded files. 81 | # Example: "/var/www/example.com/media/" 82 | MEDIA_ROOT = join(VAR_PATH, "media") 83 | 84 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 85 | # trailing slash. 86 | # Examples: "http://example.com/media/", "http://media.example.com/" 87 | MEDIA_URL = "/media/" 88 | 89 | # Absolute path to the directory static files should be collected to. 90 | # Don't put anything in this directory yourself; store your static files 91 | # in apps "static/" subdirectories and in STATICFILES_DIRS. 92 | # Example: "/var/www/example.com/static/" 93 | STATIC_ROOT = join(VAR_PATH, "static") 94 | 95 | # URL prefix for static files. 96 | # Example: "http://example.com/static/", "http://static.example.com/" 97 | STATIC_URL = "/static/" 98 | 99 | # Additional locations of static files 100 | STATICFILES_DIRS = [ 101 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 102 | # Always use forward slashes, even on Windows. 103 | # Don't forget to use absolute paths, not relative paths. 104 | join(BASE_DIR, "static"), 105 | ] 106 | 107 | MIDDLEWARE = [ 108 | "corsheaders.middleware.CorsMiddleware", 109 | "django.contrib.sessions.middleware.SessionMiddleware", 110 | "django.middleware.locale.LocaleMiddleware", 111 | "django.middleware.common.CommonMiddleware", 112 | "django.middleware.csrf.CsrfViewMiddleware", 113 | "django.contrib.auth.middleware.AuthenticationMiddleware", 114 | "django.contrib.messages.middleware.MessageMiddleware", 115 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 116 | "django.middleware.security.SecurityMiddleware", 117 | "django_browser_reload.middleware.BrowserReloadMiddleware", 118 | ] 119 | 120 | ROOT_URLCONF = "sandbox.urls" 121 | 122 | # Python dotted path to the WSGI application used by Django"s runserver. 123 | WSGI_APPLICATION = "sandbox.wsgi.application" 124 | 125 | TEMPLATES = [ 126 | { 127 | "BACKEND": "django.template.backends.django.DjangoTemplates", 128 | "DIRS": [ 129 | join(PROJECT_PATH, "templates"), 130 | ], 131 | "APP_DIRS": True, 132 | "OPTIONS": { 133 | "debug": False, 134 | "context_processors": [ 135 | "django.contrib.auth.context_processors.auth", 136 | "django.template.context_processors.debug", 137 | "django.template.context_processors.i18n", 138 | "django.template.context_processors.request", 139 | "django.template.context_processors.media", 140 | "django.template.context_processors.static", 141 | "django.template.context_processors.tz", 142 | "django.contrib.messages.context_processors.messages", 143 | ], 144 | }, 145 | }, 146 | ] 147 | 148 | INSTALLED_APPS = [ 149 | "django.contrib.admin", 150 | "django.contrib.auth", 151 | "django.contrib.contenttypes", 152 | "django.contrib.sessions", 153 | "django.contrib.messages", 154 | "django.contrib.sites", 155 | "django.contrib.staticfiles", 156 | "django.forms", 157 | "django_extensions", 158 | "tailwind", 159 | "theme", 160 | "django_browser_reload", 161 | "corsheaders", 162 | "mqueue", 163 | "instant", 164 | "livefeed", 165 | ] 166 | 167 | LOGIN_REDIRECT_URL = "/" 168 | LOGOUT_REDIRECT_URL = "/" 169 | 170 | # Ensure we can override applications widgets templates from project template 171 | # directory, require also 'django.forms' in INSTALLED_APPS 172 | FORM_RENDERER = "django.forms.renderers.TemplatesSetting" 173 | 174 | TAILWIND_APP_NAME = "theme" 175 | 176 | SITE_SLUG = "livefeed" 177 | SITE_NAME = "Livefeed" 178 | 179 | MQUEUE_HOOKS = { 180 | "centrifugo": { 181 | "path": "mqueue.hooks.centrifugo", 182 | "channel": "$livefeed", 183 | }, 184 | } 185 | 186 | CORS_ALLOWED_ORIGINS = [ 187 | "http://localhost:3000", 188 | ] 189 | CORS_ALLOW_CREDENTIALS = True 190 | 191 | try: 192 | from localsettings import * # type: ignore 193 | except ModuleNotFoundError: 194 | pass 195 | -------------------------------------------------------------------------------- /sandbox/settings/demo.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: F403,F405 2 | # pyright: reportUndefinedVariable=false 3 | """ 4 | Django settings for demonstration 5 | 6 | Intended to be used with ``make run``. 7 | """ 8 | from sandbox.settings.base import * 9 | 10 | DEBUG = True 11 | 12 | DATABASES = { 13 | "default": { 14 | "ENGINE": "django.db.backends.sqlite3", 15 | "NAME": join(VAR_PATH, "db", "db.sqlite3"), # noqa 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sandbox/settings/tests.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: F403,F405 2 | # pyright: reportUndefinedVariable=false 3 | """ 4 | Django settings for tests 5 | """ 6 | 7 | from sandbox.settings.base import * 8 | 9 | DATABASES = { 10 | "default": { 11 | "ENGINE": "django.db.backends.sqlite3", 12 | "NAME": ":memory:", 13 | "TEST": { 14 | "NAME": join(VAR_PATH, "db", "tests.sqlite3"), # noqa 15 | }, 16 | } 17 | } 18 | 19 | # Media directory dedicated to tests to avoid polluting other environment 20 | # media directory 21 | MEDIA_ROOT = join(VAR_PATH, "media-tests") 22 | -------------------------------------------------------------------------------- /sandbox/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL Configuration for sandbox 3 | """ 4 | from django.conf import settings 5 | from django.conf.urls.static import static 6 | from django.contrib import admin 7 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 8 | from django.urls import include, path 9 | 10 | 11 | urlpatterns = [ 12 | path("admin/", admin.site.urls), 13 | path("__reload__/", include("django_browser_reload.urls")), 14 | path("", include("livefeed.urls")), 15 | ] 16 | 17 | # This is only needed when using runserver with settings "DEBUG" enabled 18 | if settings.DEBUG: 19 | urlpatterns = ( 20 | urlpatterns 21 | + staticfiles_urlpatterns() 22 | + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 23 | ) 24 | -------------------------------------------------------------------------------- /sandbox/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for Sandbox. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | """ 6 | 7 | import os 8 | 9 | from django.core.wsgi import get_wsgi_application 10 | 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.demo") 12 | 13 | application = get_wsgi_application() 14 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; livefeed package 3 | ;; 4 | [metadata] 5 | name = livefeed 6 | long_description = file:README.md 7 | long_description_content_type = text/markdown 8 | license = MIT 9 | keywords = Python Django 10 | classifiers = 11 | Development Status :: 2 - Pre-Alpha 12 | Intended Audience :: Developers 13 | License :: OSI Approved :: MIT License 14 | Natural Language :: English 15 | Programming Language :: Python :: 3.8 16 | Framework :: Django 17 | Framework :: Django :: 3.0 18 | Framework :: Django :: 4.0 19 | 20 | [options] 21 | include_package_data = True 22 | install_requires = 23 | Django>=3.0 24 | packages = find: 25 | zip_safe = True 26 | 27 | [options.extras_require] 28 | dev = 29 | flake8 30 | pytest 31 | pytest-django 32 | factory-boy 33 | pyquery 34 | freezegun 35 | black 36 | django-extensions 37 | django-browser-reload 38 | django-tailwind 39 | django-instant 40 | django-mqueue 41 | django-cors-headers 42 | 43 | [options.packages.find] 44 | where = . 45 | exclude= 46 | data 47 | tests 48 | sandbox 49 | 50 | [wheel] 51 | universal = 0 52 | 53 | ;; 54 | ;; Third-party packages configuration 55 | ;; 56 | [flake8] 57 | max-line-length = 88 58 | exclude = 59 | .git, 60 | .venv, 61 | build, 62 | __pycache__ 63 | */migrations/* 64 | 65 | [tool:pytest] 66 | DJANGO_SETTINGS_MODULE = sandbox.settings.tests 67 | addopts = -vv 68 | python_files = 69 | *.py 70 | testpaths = 71 | tests 72 | 73 | [tox:tox] 74 | minversion = 3.4.0 75 | envlist = py{36,37,38}-django{22,30,31,40} 76 | 77 | [testenv] 78 | 79 | deps = 80 | django22: Django>=2.2,<2.3 81 | django30: Django>=3.0,<3.1 82 | django31: Django>=3.1,<3.2 83 | django40: Django>=4.0 84 | 85 | commands = 86 | pip install -e .[dev] 87 | pytest -vv tests 88 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import setup 4 | 5 | setup() 6 | -------------------------------------------------------------------------------- /static/app/assets/index-ef32a193.css: -------------------------------------------------------------------------------- 1 | *,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e5e5}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#a3a3a3}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#a3a3a3}input::placeholder,textarea::placeholder{opacity:1;color:#a3a3a3}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#737373;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--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);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#737373;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#737373;opacity:1}input::placeholder,textarea::placeholder{color:#737373;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23737373' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 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;color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#737373;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--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:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px auto -webkit-focus-ring-color}*,:before,:after{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.btn{border-width:1px;border-radius:.25rem;padding:.25rem 1rem;letter-spacing:.05em;cursor:pointer;border-color:transparent}.btn:hover{opacity:.9}.btn:disabled{opacity:.75;cursor:not-allowed}.slide-x{overflow-x:hidden;transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}.slide-y{overflow-y:hidden;transition-property:max-height;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}.slidedown{max-height:1000px}.slideup{max-height:0}.sw-sidebar{width:80px}.sw-sidebar.opened{width:208px}.sw-input{border-radius:.125rem}.sw-input label{background-color:#fff;top:0%;transform:translateY(-50%);font-size:.8em}.sw-input label.unset{color:#1f2937}.sw-input label.valid{color:#16a34a}.sw-input label.invalid{color:#ef4444}.sw-input .unset{border-color:#6b7280}.sw-input .valid{border-color:#16a34a}.sw-input .invalid{border-color:#ef4444}.sw-input :focus{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);outline:2px solid transparent;outline-offset:2px;--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(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.sw-input :focus.unset{--tw-ring-color: rgb(107 114 128 / var(--tw-ring-opacity));--tw-ring-opacity: .1}.sw-input :focus.valid{--tw-ring-color: rgb(22 163 74 / var(--tw-ring-opacity));--tw-ring-opacity: .1}.sw-input :focus.invalid{--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity));--tw-ring-opacity: .1}.sw-switch .dot{background-color:#e2e8f0;width:1rem;height:1rem}.sw-switch .dot.big{width:1.5rem;height:1.5rem}.sw-switch .bg{background-color:#6b7280;width:2.5rem;height:1.5rem}.sw-switch .bg.big{width:3.5rem;height:2rem}.sw-switch input:checked~.dot{transform:translate(100%);background-color:#fff}.sw-switch input:checked~.bg{background-color:#16a34a}.sw-switch.primary input:checked~.dot{transform:translate(100%);background-color:#fff}.sw-switch.primary input:checked~.bg{background-color:#0e7490}.sw-switch.secondary input:checked~.dot{transform:translate(100%);background-color:#fff}.sw-switch.secondary input:checked~.bg{background-color:#06b6d4}.sw-switch.danger input:checked~.dot{transform:translate(100%);background-color:#fff}.sw-switch.danger input:checked~.bg{background-color:#ef4444}.sw-switch.warning input:checked~.dot{transform:translate(100%);background-color:#fff}.sw-switch.warning input:checked~.bg{background-color:#f59e0b}.dark .txt-background{color:#171717}.dark .txt-primary{color:#155e75}.dark .txt-secondary{color:#475569}.dark .txt-neutral{color:#d1d5db}.dark .txt-light{color:#404040}.dark .txt-danger{color:#f87171}.dark .txt-warning{color:#f59e0b}.dark .txt-success{color:#16a34a}.dark .block-background{background-color:#171717}.dark .block-primary{background-color:#155e75}.dark .block-secondary{background-color:#475569}.dark .block-neutral{background-color:#d1d5db}.dark .block-light{background-color:#404040}.dark .block-danger{background-color:#f87171}.dark .block-warning{background-color:#f59e0b}.dark .block-success{background-color:#16a34a}.dark .background{background-color:#171717;color:#fff}.dark .primary{background-color:#155e75;color:#fff}.dark .secondary{background-color:#475569;color:#fff}.dark .neutral{background-color:#d1d5db;color:#1f2937}.dark .light{background-color:#404040;color:#fff}.dark .danger{background-color:#f87171;color:#fff}.dark .warning{background-color:#f59e0b;color:#fff}.dark .success{background-color:#16a34a;color:#fff}.dark .sw-input input,.dark .sw-input label{background-color:#171717}.dark .sw-input label.unset{color:#fff}.dark .sw-input label.valid{color:#16a34a}.dark .sw-input label.invalid{color:#f87171}.dark .sw-input .unset{border-color:#d1d5db}.dark .sw-input .valid{border-color:#16a34a}.dark .sw-input .invalid{border-color:#f87171}.dark .sw-input :focus.unset{--tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity));--tw-ring-opacity: .1}.dark .sw-input :focus.valid{--tw-ring-color: rgb(22 163 74 / var(--tw-ring-opacity));--tw-ring-opacity: .1}.dark .sw-input :focus.invalid{--tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity));--tw-ring-opacity: .1}.dark .sw-switch .dot{background-color:#fff}.dark .sw-switch.bg{background-color:#d1d5db}.dark .sw-switch input:checked~.dot{background-color:#fff}.dark .sw-switch input:checked~.bg{background-color:#16a34a}.dark .sw-switch.primary input:checked~.dot{transform:translate(100%);background-color:#fff}.dark .sw-switch.primary input:checked~.bg{background-color:#155e75}.dark .sw-switch.secondary input:checked~.dot{transform:translate(100%);background-color:#fff}.dark .sw-switch.secondary input:checked~.bg{background-color:#475569}.dark .sw-switch.danger input:checked~.dot{transform:translate(100%);background-color:#fff}.dark .sw-switch.danger input:checked~.bg{background-color:#f87171}.dark .sw-switch.warning input:checked~.dot{transform:translate(100%);background-color:#fff}.dark .sw-switch.warning input:checked~.bg{background-color:#f59e0b}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-x-0{left:0px;right:0px}.left-2{left:.5rem}.left-1{left:.25rem}.top-1{top:.25rem}.bottom-12{bottom:3rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-3{margin-left:.75rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mt-3{margin-top:.75rem}.mt-8{margin-top:2rem}.ml-5{margin-left:1.25rem}.mr-5{margin-right:1.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-screen{height:100vh}.h-16{height:4rem}.h-80{height:20rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.w-screen{width:100vw}.w-full{width:100%}.w-60{width:15rem}.w-20{width:5rem}.w-52{width:13rem}.w-4{width:1rem}.w-6{width:1.5rem}.w-10{width:2.5rem}.w-max{width:-webkit-max-content;width:-moz-max-content;width:max-content}.flex-none{flex:none}.flex-grow{flex-grow:1}.translate-y-24{--tw-translate-y: 6rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform,.transform-gpu{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.rounded-sm{border-radius:.125rem}.rounded-full{border-radius:9999px}.rounded-xl{border-radius:.75rem}.border-b{border-bottom-width:1px}.border-none{border-style:none}.border-light{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity))}.bg-background{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-primary{--tw-bg-opacity: 1;background-color:rgb(14 116 144 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity))}.bg-background-dark{--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity))}.fill-current{fill:currentColor}.p-5{padding:1.25rem}.p-3{padding:.75rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.capitalize{text-transform:capitalize}.text-foreground{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.text-primary-r{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-light{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.text-foreground-dark{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.opacity-100{opacity:1}.opacity-0{opacity:0}.shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring{--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(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-neutral{--tw-ring-opacity: 1;--tw-ring-color: rgb(107 114 128 / var(--tw-ring-opacity))}.ring-success{--tw-ring-opacity: 1;--tw-ring-color: rgb(22 163 74 / var(--tw-ring-opacity))}.ring-danger{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity))}.ring-neutral-dark{--tw-ring-opacity: 1;--tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity))}.ring-success-dark{--tw-ring-opacity: 1;--tw-ring-color: rgb(22 163 74 / var(--tw-ring-opacity))}.ring-danger-dark{--tw-ring-opacity: 1;--tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity))}.ring-opacity-10{--tw-ring-opacity: .1}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-none{transition-property:none}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.txt-background{color:#fff}.txt-primary{color:#0e7490}.txt-secondary{color:#06b6d4}.txt-neutral{color:#6b7280}.txt-light{color:#e2e8f0}.txt-danger{color:#ef4444}.txt-warning{color:#f59e0b}.txt-success{color:#16a34a}.block-background{background-color:#fff}.block-primary{background-color:#0e7490}.block-secondary{background-color:#06b6d4}.block-neutral{background-color:#6b7280}.block-light{background-color:#e2e8f0}.block-danger{background-color:#ef4444}.block-warning{background-color:#f59e0b}.block-success{background-color:#16a34a}.background{background-color:#fff;color:#1f2937}.primary{background-color:#0e7490;color:#fff}.secondary{background-color:#06b6d4;color:#fff}.neutral{background-color:#6b7280;color:#fff}.light{background-color:#e2e8f0;color:#1f2937}.danger{background-color:#ef4444;color:#fff}.warning{background-color:#f59e0b;color:#fff}.success{background-color:#16a34a;color:#fff}.background-dark{background-color:#171717;color:#fff}.primary-dark{background-color:#155e75;color:#fff}.secondary-dark{background-color:#475569;color:#fff}.neutral-dark{background-color:#d1d5db;color:#1f2937}.light-dark{background-color:#404040;color:#fff}.danger-dark{background-color:#f87171;color:#fff}.warning-dark{background-color:#f59e0b;color:#fff}.success-dark{background-color:#16a34a;color:#fff}.dark .dark\:bg-background-dark{--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity))}.dark .dark\:text-foreground-dark{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}@media (min-width: 640px){.sm\:hidden{display:none}}@media (min-width: 768px){.md\:hidden{display:none}}@media (min-width: 1024px){.lg\:hidden{display:none}}@media (min-width: 1280px){.xl\:hidden{display:none}}@media (min-width: 1536px){.\32xl\:hidden{display:none}} 2 | -------------------------------------------------------------------------------- /static/app/assets/index.js: -------------------------------------------------------------------------------- 1 | var L=Object.defineProperty;var N=(e,c,n)=>c in e?L(e,c,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[c]=n;var p=(e,c,n)=>(N(e,typeof c!="symbol"?c+"":c,n),n);import{o as a,c as r,a as s,r as O,b as q,d as E,U as I,I as M,e as $,n as x,t as h,f as S,g as Q,F as y,h as C,i,j as V,u as d,k as j}from"./vendor-6f17e660.js";const F=function(){const c=document.createElement("link").relList;if(c&&c.supports&&c.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))l(t);new MutationObserver(t=>{for(const o of t)if(o.type==="childList")for(const u of o.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&l(u)}).observe(document,{childList:!0,subtree:!0});function n(t){const o={};return t.integrity&&(o.integrity=t.integrity),t.referrerpolicy&&(o.referrerPolicy=t.referrerpolicy),t.crossorigin==="use-credentials"?o.credentials="include":t.crossorigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function l(t){if(t.ep)return;t.ep=!0;const o=n(t);fetch(t.href,o)}};F();const U={class:"inline-block align-middle",width:"1.2em",height:"1.2em",preserveAspectRatio:"xMidYMid meet",viewBox:"0 0 100 100"},W=s("path",{fill:"currentColor",d:"M37.328 0c-6.874 0-12.5 5.626-12.5 12.5S30.454 25 37.328 25c1.316 0 2.586-.208 3.78-.59l6.576 13.977c-8.125 5.119-12.366 15.123-9.754 24.87a22.435 22.435 0 0 0 1.959 4.901l-17.75 11.404l-.002-.001C19.839 76.78 16.367 75 12.5 75C5.626 75 0 80.626 0 87.5S5.626 100 12.5 100S25 94.374 25 87.5c0-1.577-.308-3.084-.848-4.477L42.104 71.49c5.418 6.747 14.504 10.055 23.382 7.676c11.98-3.21 19.12-15.577 15.91-27.557a22.347 22.347 0 0 0-5.525-9.77l9.877-12.808A9.878 9.878 0 0 0 90 30c5.493 0 10-4.507 10-10s-4.507-10-10-10s-10 4.507-10 10c0 2.539.97 4.859 2.549 6.629l-9.707 12.586c-5.3-3.826-12.213-5.335-19.002-3.516c-.892.24-1.754.535-2.59.871l-6.58-13.986c3.116-2.28 5.158-5.95 5.158-10.084c0-6.874-5.626-12.5-12.5-12.5zm0 5c4.172 0 7.5 3.328 7.5 7.5c0 4.172-3.328 7.5-7.5 7.5a7.462 7.462 0 0 1-7.5-7.5c0-4.172 3.328-7.5 7.5-7.5zM90 15c2.791 0 5 2.209 5 5s-2.209 5-5 5s-5-2.209-5-5s2.209-5 5-5zM60.453 37.445c8.504.337 16.175 6.124 18.514 14.762a.6.6 0 0 1 .015.05a.6.6 0 0 1 .012.06C81.813 62.96 75.483 73.9 64.84 76.751c-10.663 2.857-21.637-3.478-24.494-14.14c-2.857-10.663 3.48-21.64 14.142-24.497a19.964 19.964 0 0 1 5.965-.67zm-.445 1.192a18.42 18.42 0 0 0-1.89.064c.36.039.614.15.136.344l-.432-.027c-.383-.102-.504-.181-.482-.237c-.846.107-1.695.268-2.541.494l-.135.04a11.17 11.17 0 0 1 1.125-.145c.254.845.87 2.384-.809 2.773c-1.348.444-1.813 3.902-3.185 1.864c-.219-1.176 1.14-4.026-1.027-2.541c-.313.872.186 2.203-.801 2.295c-.011 1.301-1.148.832-1.477.357c-.382.387.59.858.18 1.357c1.204-1.38.452 1.204 1.445 1.551c-.595.985 1.367 2.57-.459 2.526c-1.625.169.684-2.294-1.006-.961c-1.127.59-.88 1.259.067 1.199c-.603.972-1.618 1.288-2.188 1.076a16.799 16.799 0 0 0-2.629 3.791c-1.461.466-.323 3.376-1.117 3.262c-.324-.898-.408-2.466-1.295-1.998c-.259-.015-.447.146-.58.406a18.59 18.59 0 0 0 .026 2.898c.058.137.125.25.199.325c.862-1.893.385 1.095.834 1.533c1.051.936.358 3.297 2.23 3.215c.301-1.381 1.178-.438 1.278-.621c.548-.256 1.34 1.056 2.322.587c1.163-.446 1.2 2.066 2.902 1.89c2.149.043 2.66 2.77 4.944 2.513c1.501.412 5.228-.23 4.648 2.164c-1.513 1.156.407 3.961-2.26 4.027c-.509.307-.598.928-.908 1.41c2.405.328 4.913.194 7.406-.474a18.735 18.735 0 0 0 11.362-8.664c.102-1.139-.007-2.348.47-3.407c.035-1.684-1.864-2.37-1.334-4.062c-.77.309-2.436-.264-2.97 1.268c-1.004.283-1.669 1.7-2.766 1.347c-1.441-.405-3.099-.885-3.855-2.299c-.164-1.216-.93-2.626-1.11-3.746c.051-2.035 1.246-3.57 1.057-5.517c.098-.147.956-1.285.992-1.332c1.23-.697 2.459-3.301 3.818-1.385c.322.123 2.366.547 2.012-.125c-.256-1.13 2.89-1.323.967-2.375c-.184-.051-.835-.065-1.205.154c-.154.083-.652 1.057-1.54.166C69.266 45 71 47.953 69.31 46.64c1.17-.771-2.145-1.483-1.225-.8c1.466 1.443-.714.823-.69-.448c-1.678.236-.073 3.077-1.472 3.955c.01-.001.02-.007.031-.008l-.256.105l-.4.149c-.479-.283-1.588-.038-1.56-1.235c-1.013-1.012-.45-1.652.804-1.867c1.023-1.12-3.033-1.05-.701-2.117c-1.05.355-1.487.516-1.979-.766c.61 1.59-1.778 1.137-.906-.14c.354-.406-1.229-.618-.086-1.14c.79-.258 3.238 2.567 3.006.542c.497-.32-.835-1.08-.598-1.203c-1.297.832-2.377-1.546-2.638-2.475a5.48 5.48 0 0 0 1.234-.427a18.632 18.632 0 0 0-1.865-.127zm2.662 1.134a.375.375 0 0 0-.137.008c-.18.064.546.386.137-.008zm.057.563c-.325.056 1.485.72.625.082c-.362-.075-.55-.095-.625-.082zm1.533.531c-.16.044-.1.208.494.504c.313.606.062 1.005.625.233c.015-.545-.854-.809-1.12-.737zm-6.838.653c.623.011 1.087.292-.11.777c-.268.07-.574.103-.828-.037c-.241-.338-.025-.553.317-.66c.188-.06.413-.084.62-.08zm3.53.23c.078 0 .505.494.081.211c-.095-.156-.108-.211-.082-.211zm-1.243.033c.08-.064.384.403-.01.145c-.016-.087-.009-.13.01-.145zm9.967.787c-.286.114 1.193 1.843 1.178.88c1.15-.507-.507-1.16-.657-.647c-.294-.199-.455-.259-.521-.233zm-20.701.342c-.02-.01-.047-.01-.084.004c-.1.04-.266.181-.508.477c-.333.262-.817 1.457-.133.486c.552-.251.856-.89.725-.967zm-1.936 1.258c-.16.03-.37.162-.643.437c-1.029.372-.465 2.705-.472 2.557c.256-.291.362-1.001.775-1.072c.732-.28 1.03-2.055.34-1.922zm20.895.426a.1.1 0 0 0-.04.01c-.055.034.126.15.731.392c.166-.102 1.28.49.947.08c-.43-.246-1.412-.504-1.638-.482zm2.8.183c-.088.07.549.602.387.139c-.247-.133-.357-.162-.387-.139zm1.395 1.024c.239.059.475.564-.242.431c-.007-.269.058-.39.142-.425a.158.158 0 0 1 .1-.006zm-5.125.935c.23-.102.383 1.042-.23.621c.068-.415.153-.587.23-.62zm-19.346 1.582c-.143.037.08.34.28.084c-.144-.077-.232-.096-.28-.084zm-3.709.506c-.234.034.244 1.019.29.162c-.145-.127-.236-.17-.29-.162zm.283.951c-.165.087.018 1.193.094.036c-.04-.04-.07-.048-.094-.036zm13.967.887c.29-.05.36.421-.342.268c.125-.176.246-.251.342-.268zm.93.063c.127.012.01.487-.213.187c.1-.144.17-.192.213-.187zm-15.045.27c-.201.012-.4.762.13.134c-.037-.102-.084-.138-.13-.135zm18.691.868c.112.055.136.474-.164.186c.038-.11.077-.165.112-.184a.055.055 0 0 1 .052-.002zm1.565 1.096c.175-.047.505.624-.047.639c-.08-.448-.033-.618.047-.639zm-.805.639c.435-.005.782.155-.07.414l-.385.103l-.076-.013c-.234-.226-.114-.368.11-.442c.122-.04.276-.06.421-.062zm-20.15 3.627c.105-.03.552.697.054.35c-.088-.247-.09-.34-.054-.35zm-.04.693c.112-.01.33 1.015-.048.453c-.013-.33.012-.45.049-.453zm-.991.615c.11-.017.27-.018.494.002c.669.97 1.804 1.039 1.636 1.59c.83.098 2.305 1.227.366 1.064c-1.302-.158.167-1.007-1.323-1.01c-.21-.569-1.944-1.522-1.173-1.646zm1.623.22c.082 0 .26.178.257.454l-.103-.033c-.186-.245-.217-.371-.184-.408a.039.039 0 0 1 .03-.012zm18.841.643c.192-.054.277.548-.144.163c.05-.103.1-.15.144-.163zm-1.101.04c.218-.034 1.007.223.256.273c-.335-.183-.355-.258-.256-.273zm1.154.5c.168.035.06.5-.34.248c.103-.133.187-.204.252-.233a.145.145 0 0 1 .088-.015zm-.568.222c.111-.017.19.526-.11.256c.032-.181.072-.25.11-.256zm-19.016.324c.236-.013 1.076.532.264.473c-.354-.346-.371-.467-.264-.473zm31.272.25c.111-.017.188.524-.112.254c.032-.181.074-.248.112-.254zm-28.32.147c.277.049.615.533-.212.428c-.046-.262.01-.382.1-.418a.2.2 0 0 1 .111-.01zm-5.01.217c.08.401.167.804.275 1.205a18.734 18.734 0 0 0 8.492 11.256c-1.383-1.243-3.108-2.08-4.209-3.637c-1.8-1.355-1.204-3.488-1.951-5.203c-.332-.925-.16.65-.797-.467c-.741-.853-1.254-2.107-1.81-3.154zm6.632.838c.125.014.684 1.689.166.964l-.091-.275c-.111-.515-.116-.694-.075-.69zM12.5 80c4.172 0 7.5 3.328 7.5 7.5c0 4.172-3.328 7.5-7.5 7.5A7.462 7.462 0 0 1 5 87.5c0-4.172 3.328-7.5 7.5-7.5z",color:"currentColor"},null,-1),D=[W];function H(e,c){return a(),r("svg",U,D)}var P={name:"gis-earth-network-o",render:H};class m{constructor(c){p(this,"name");p(this,"messages",O([]));p(this,"numMessages",q(()=>this.messages.value.length));this.name=c}get hasMessages(){return this.messages.value.length>0}}const _={"Site 1":new m("Site 1"),"Site 2":new m("Site 2"),"Site 3":new m("Site 3"),"Site 4":new m("Site 4"),"Site 5":new m("Site 5"),"Site 6":new m("Site 6")},b={important:"fa fa-exclamation",ok:"fa fa-thumbs-up",info:"fa fa-info-circle",debug:"fa fa-cog",warning:"fa fa-exclamation",error:"fa fa-exclamation-triangle",edited:"fas fa-edit",created:"fas fa-plus",deleted:"far fa-trash-alt"},R=Object.keys(b);class T{constructor(){p(this,"counter",E({}));R.forEach(c=>{this.counter[c]=0})}increment(c){this.counter[c]++}}const A=new T,Y=new I;let g=new M("http://localhost:8000","ws://localhost:8427",!0);function G(e){console.log("MSG",typeof e,JSON.stringify(e,null," ")),_[e.site].messages.value.unshift(e),A.increment(e.eventClass)}function J(){const e=localStorage.getItem("wsUri");let c="ws://localhost:8427";e!==null&&(c=e),g=new M("http://localhost:8000",c,!0),console.log("Ws uri",g.websocketsUri),g.onMessage=G}async function K(){await g.get_token(),await g.connect()}const X={class:"inline-block align-middle",width:"1.2em",height:"1.2em",preserveAspectRatio:"xMidYMid meet",viewBox:"0 0 640 512"},Z=s("path",{fill:"currentColor",d:"M633.99 471.02L36 3.51C29.1-2.01 19.03-.9 13.51 6l-10 12.49C-2.02 25.39-.9 35.46 6 40.98l598 467.51c6.9 5.52 16.96 4.4 22.49-2.49l10-12.49c5.52-6.9 4.41-16.97-2.5-22.49zM163.53 368c16.71-22.03 34.48-55.8 41.4-110.58l-45.47-35.55c-3.27 90.73-36.47 120.68-54.84 140.42c-6 6.45-8.66 14.16-8.61 21.71c.11 16.4 12.98 32 32.1 32h279.66l-61.4-48H163.53zM320 96c61.86 0 112 50.14 112 112c0 .2-.06.38-.06.58c.02 16.84 1.16 31.77 2.79 45.73l59.53 46.54c-8.31-22.13-14.34-51.49-14.34-92.85c0-77.7-54.48-139.9-127.94-155.16V32c0-17.67-14.32-32-31.98-32s-31.98 14.33-31.98 32v20.84c-26.02 5.41-49.45 16.94-69.13 32.72l38.17 29.84C275 103.18 296.65 96 320 96zm0 416c35.32 0 63.97-28.65 63.97-64H256.03c0 35.35 28.65 64 63.97 64z"},null,-1),e1=[Z];function c1(e,c){return a(),r("svg",X,e1)}var t1={name:"fa-regular-bell-slash",render:c1},k=(e,c)=>{const n=e.__vccOpts||e;for(const[l,t]of c)n[l]=t;return n};const s1=$({props:{eventClass:{type:String,required:!0}},setup(){return{eventIcons:b}}}),n1={class:"ml-1 capitalize"};function o1(e,c,n,l,t,o){return a(),r("span",{class:x(["mq-label",`mq-${e.eventClass}`])},[s("i",{class:x(e.eventIcons[e.eventClass])},null,2),s("span",n1,h(e.eventClass),1)],2)}var w=k(s1,[["render",o1]]);const a1=$({components:{EventBadge:w},props:{site:{type:m,required:!0}},setup(){return{eventIcons:b}}}),r1={class:"overflow-auto h-80"},i1={class:"text-2xl"},l1={key:0,class:"ml-2 text-base text-light"},u1={key:0,class:"mt-3 space-y-2"},d1={key:1,class:"mt-8 ml-5"};function m1(e,c,n,l,t,o){const u=w,v=t1;return a(),r("div",r1,[s("div",i1,[S(h(e.site.name)+" ",1),e.site.hasMessages?(a(),r("span",l1,"( "+h(e.site.numMessages)+" )",1)):Q("",!0)]),e.site.hasMessages===!0?(a(),r("div",u1,[(a(!0),r(y,null,C(e.site.messages.value,(z,B)=>(a(),r("div",{key:B},[i(u,{"event-class":z.eventClass},null,8,["event-class"]),S(" \xA0"+h(z.msg),1)]))),128))])):(a(),r("div",d1,[i(v,{class:"text-3xl text-light"})]))])}var f=k(a1,[["render",m1]]);const _1=$({components:{EventBadge:w},setup(){return{eventsQueue:A}}}),f1=s("div",{class:"flex items-center justify-center w-full p-3 bg-gray-100 border-b border-light"},"Status",-1),p1={class:"flex flex-col w-full"},h1={class:"flex-grow"},g1={class:"flex-none"};function v1(e,c,n,l,t,o){const u=w;return a(),r(y,null,[f1,s("div",p1,[(a(!0),r(y,null,C(Object.keys(e.eventsQueue.counter),(v,z)=>(a(),r("div",{key:z,class:"flex flex-row p-3 border-b"},[s("div",h1,[i(u,{"event-class":v},null,8,["event-class"])]),s("div",g1,h(e.eventsQueue.counter[v]),1)]))),128))])],64)}var z1=k(_1,[["render",v1]]);const $1={class:"flex items-center w-full h-16 bg-primary text-primary-r"},w1=S("\xA0\xA0 "),x1=s("span",{class:"text-2xl"},"Mqueue Livefeed",-1),S1={class:"flex flex-row"},y1={class:"flex-grow p-5"},b1={class:"grid grid-cols-3 gap-6"},k1={class:"flex-none w-60"},M1=$({setup(e){return V(()=>{J(),K()}),(c,n)=>{const l=P;return a(),r("div",{class:x([{dark:d(Y).isDarkMode.value==!0},"w-screen h-screen bg-background dark:bg-background-dark text-foreground dark:text-foreground-dark"])},[s("div",$1,[i(l,{class:"ml-3 text-3xl"}),w1,x1]),s("div",S1,[s("div",y1,[s("div",b1,[i(f,{site:d(_)["Site 1"]},null,8,["site"]),i(f,{site:d(_)["Site 2"]},null,8,["site"]),i(f,{site:d(_)["Site 3"]},null,8,["site"]),i(f,{site:d(_)["Site 4"]},null,8,["site"]),i(f,{site:d(_)["Site 5"]},null,8,["site"]),i(f,{site:d(_)["Site 6"]},null,8,["site"])])]),s("div",k1,[i(z1)])])],2)}}});j(M1).mount("#app"); 2 | -------------------------------------------------------------------------------- /static/app/assets/vendor-6f17e660.js: -------------------------------------------------------------------------------- 1 | var Rs=Object.defineProperty;var xs=(e,t,n)=>t in e?Rs(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var de=(e,t,n)=>(xs(e,typeof t!="symbol"?t+"":t,n),n);function wn(e,t){const n=Object.create(null),s=e.split(",");for(let i=0;i!!n[i.toLowerCase()]:i=>!!n[i]}const Ps="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Os=wn(Ps);function Er(e){return!!e||e===""}function Tn(e){if(K(e)){const t={};for(let n=0;n{if(n){const s=n.split(Cs);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function En(e){let t="";if(le(e))t=e;else if(K(e))for(let n=0;nle(e)?e:e==null?"":K(e)||fe(e)&&(e.toString===Or||!z(e.toString))?JSON.stringify(e,Rr,2):String(e),Rr=(e,t)=>t&&t.__v_isRef?Rr(e,t.value):ct(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,i])=>(n[`${s} =>`]=i,n),{})}:xr(t)?{[`Set(${t.size})`]:[...t.values()]}:fe(t)&&!K(t)&&!kr(t)?String(t):t,te={},ut=[],ke=()=>{},Ms=()=>!1,Ns=/^on[^a-z]/,Wt=e=>Ns.test(e),Rn=e=>e.startsWith("onUpdate:"),pe=Object.assign,xn=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Ls=Object.prototype.hasOwnProperty,G=(e,t)=>Ls.call(e,t),K=Array.isArray,ct=e=>Kt(e)==="[object Map]",xr=e=>Kt(e)==="[object Set]",z=e=>typeof e=="function",le=e=>typeof e=="string",Pn=e=>typeof e=="symbol",fe=e=>e!==null&&typeof e=="object",Pr=e=>fe(e)&&z(e.then)&&z(e.catch),Or=Object.prototype.toString,Kt=e=>Or.call(e),js=e=>Kt(e).slice(8,-1),kr=e=>Kt(e)==="[object Object]",On=e=>le(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,jt=wn(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),zt=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Fs=/-(\w)/g,ft=zt(e=>e.replace(Fs,(t,n)=>n?n.toUpperCase():"")),Ds=/\B([A-Z])/g,ht=zt(e=>e.replace(Ds,"-$1").toLowerCase()),Cr=zt(e=>e.charAt(0).toUpperCase()+e.slice(1)),Zt=zt(e=>e?`on${Cr(e)}`:""),Tt=(e,t)=>!Object.is(e,t),Qt=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},As=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let Xn;const Bs=()=>Xn||(Xn=typeof globalThis!="undefined"?globalThis:typeof self!="undefined"?self:typeof window!="undefined"?window:typeof global!="undefined"?global:{});let Ee;class Us{constructor(t=!1){this.active=!0,this.effects=[],this.cleanups=[],!t&&Ee&&(this.parent=Ee,this.index=(Ee.scopes||(Ee.scopes=[])).push(this)-1)}run(t){if(this.active)try{return Ee=this,t()}finally{Ee=this.parent}}on(){Ee=this}off(){Ee=this.parent}stop(t){if(this.active){let n,s;for(n=0,s=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},Ir=e=>(e.w&Ke)>0,Mr=e=>(e.n&Ke)>0,qs=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let s=0;s{(g==="length"||g>=s)&&c.push(h)});else switch(n!==void 0&&c.push(l.get(n)),t){case"add":K(e)?On(n)&&c.push(l.get("length")):(c.push(l.get(Ze)),ct(e)&&c.push(l.get(fn)));break;case"delete":K(e)||(c.push(l.get(Ze)),ct(e)&&c.push(l.get(fn)));break;case"set":ct(e)&&c.push(l.get(Ze));break}if(c.length===1)c[0]&&an(c[0]);else{const h=[];for(const g of c)g&&h.push(...g);an(kn(h))}}function an(e,t){for(const n of K(e)?e:[...e])(n!==Le||n.allowRecurse)&&(n.scheduler?n.scheduler():n.run())}const Ks=wn("__proto__,__v_isRef,__isVue"),jr=new Set(Object.getOwnPropertyNames(Symbol).map(e=>Symbol[e]).filter(Pn)),zs=In(),Xs=In(!1,!0),Ys=In(!0),Gn=Gs();function Gs(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const s=Z(this);for(let u=0,l=this.length;u{e[t]=function(...n){dt();const s=Z(this)[t].apply(this,n);return _t(),s}}),e}function In(e=!1,t=!1){return function(s,i,u){if(i==="__v_isReactive")return!e;if(i==="__v_isReadonly")return e;if(i==="__v_isShallow")return t;if(i==="__v_raw"&&u===(e?t?hi:Ur:t?Br:Ar).get(s))return s;const l=K(s);if(!e&&l&&G(Gn,i))return Reflect.get(Gn,i,u);const c=Reflect.get(s,i,u);return(Pn(i)?jr.has(i):Ks(i))||(e||we(s,"get",i),t)?c:ae(c)?!l||!On(i)?c.value:c:fe(c)?e?Hr(c):Ln(c):c}}const Vs=Fr(),Zs=Fr(!0);function Fr(e=!1){return function(n,s,i,u){let l=n[s];if(Et(l)&&ae(l)&&!ae(i))return!1;if(!e&&!Et(i)&&($r(i)||(i=Z(i),l=Z(l)),!K(n)&&ae(l)&&!ae(i)))return l.value=i,!0;const c=K(n)&&On(s)?Number(s)e,Xt=e=>Reflect.getPrototypeOf(e);function Ct(e,t,n=!1,s=!1){e=e.__v_raw;const i=Z(e),u=Z(t);t!==u&&!n&&we(i,"get",t),!n&&we(i,"get",u);const{has:l}=Xt(i),c=s?Mn:n?Fn:Rt;if(l.call(i,t))return c(e.get(t));if(l.call(i,u))return c(e.get(u));e!==i&&e.get(t)}function It(e,t=!1){const n=this.__v_raw,s=Z(n),i=Z(e);return e!==i&&!t&&we(s,"has",e),!t&&we(s,"has",i),e===i?n.has(e):n.has(e)||n.has(i)}function Mt(e,t=!1){return e=e.__v_raw,!t&&we(Z(e),"iterate",Ze),Reflect.get(e,"size",e)}function Vn(e){e=Z(e);const t=Z(this);return Xt(t).has.call(t,e)||(t.add(e),Be(t,"add",e,e)),this}function Zn(e,t){t=Z(t);const n=Z(this),{has:s,get:i}=Xt(n);let u=s.call(n,e);u||(e=Z(e),u=s.call(n,e));const l=i.call(n,e);return n.set(e,t),u?Tt(t,l)&&Be(n,"set",e,t):Be(n,"add",e,t),this}function Qn(e){const t=Z(this),{has:n,get:s}=Xt(t);let i=n.call(t,e);i||(e=Z(e),i=n.call(t,e)),s&&s.call(t,e);const u=t.delete(e);return i&&Be(t,"delete",e,void 0),u}function er(){const e=Z(this),t=e.size!==0,n=e.clear();return t&&Be(e,"clear",void 0,void 0),n}function Nt(e,t){return function(s,i){const u=this,l=u.__v_raw,c=Z(l),h=t?Mn:e?Fn:Rt;return!e&&we(c,"iterate",Ze),l.forEach((g,E)=>s.call(i,h(g),h(E),u))}}function Lt(e,t,n){return function(...s){const i=this.__v_raw,u=Z(i),l=ct(u),c=e==="entries"||e===Symbol.iterator&&l,h=e==="keys"&&l,g=i[e](...s),E=n?Mn:t?Fn:Rt;return!t&&we(u,"iterate",h?fn:Ze),{next(){const{value:I,done:T}=g.next();return T?{value:I,done:T}:{value:c?[E(I[0]),E(I[1])]:E(I),done:T}},[Symbol.iterator](){return this}}}}function He(e){return function(...t){return e==="delete"?!1:this}}function si(){const e={get(u){return Ct(this,u)},get size(){return Mt(this)},has:It,add:Vn,set:Zn,delete:Qn,clear:er,forEach:Nt(!1,!1)},t={get(u){return Ct(this,u,!1,!0)},get size(){return Mt(this)},has:It,add:Vn,set:Zn,delete:Qn,clear:er,forEach:Nt(!1,!0)},n={get(u){return Ct(this,u,!0)},get size(){return Mt(this,!0)},has(u){return It.call(this,u,!0)},add:He("add"),set:He("set"),delete:He("delete"),clear:He("clear"),forEach:Nt(!0,!1)},s={get(u){return Ct(this,u,!0,!0)},get size(){return Mt(this,!0)},has(u){return It.call(this,u,!0)},add:He("add"),set:He("set"),delete:He("delete"),clear:He("clear"),forEach:Nt(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(u=>{e[u]=Lt(u,!1,!1),n[u]=Lt(u,!0,!1),t[u]=Lt(u,!1,!0),s[u]=Lt(u,!0,!0)}),[e,n,t,s]}const[ii,oi,ui,ci]=si();function Nn(e,t){const n=t?e?ci:ui:e?oi:ii;return(s,i,u)=>i==="__v_isReactive"?!e:i==="__v_isReadonly"?e:i==="__v_raw"?s:Reflect.get(G(n,i)&&i in s?n:s,i,u)}const li={get:Nn(!1,!1)},fi={get:Nn(!1,!0)},ai={get:Nn(!0,!1)},Ar=new WeakMap,Br=new WeakMap,Ur=new WeakMap,hi=new WeakMap;function di(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function _i(e){return e.__v_skip||!Object.isExtensible(e)?0:di(js(e))}function Ln(e){return Et(e)?e:jn(e,!1,Dr,li,Ar)}function pi(e){return jn(e,!1,ri,fi,Br)}function Hr(e){return jn(e,!0,ni,ai,Ur)}function jn(e,t,n,s,i){if(!fe(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const u=i.get(e);if(u)return u;const l=_i(e);if(l===0)return e;const c=new Proxy(e,l===2?s:n);return i.set(e,c),c}function lt(e){return Et(e)?lt(e.__v_raw):!!(e&&e.__v_isReactive)}function Et(e){return!!(e&&e.__v_isReadonly)}function $r(e){return!!(e&&e.__v_isShallow)}function Jr(e){return lt(e)||Et(e)}function Z(e){const t=e&&e.__v_raw;return t?Z(t):e}function qr(e){return Dt(e,"__v_skip",!0),e}const Rt=e=>fe(e)?Ln(e):e,Fn=e=>fe(e)?Hr(e):e;function Wr(e){qe&&Le&&(e=Z(e),Lr(e.dep||(e.dep=kn())))}function Kr(e,t){e=Z(e),e.dep&&an(e.dep)}function ae(e){return!!(e&&e.__v_isRef===!0)}function At(e){return zr(e,!1)}function bi(e){return zr(e,!0)}function zr(e,t){return ae(e)?e:new gi(e,t)}class gi{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:Z(t),this._value=n?t:Rt(t)}get value(){return Wr(this),this._value}set value(t){t=this.__v_isShallow?t:Z(t),Tt(t,this._rawValue)&&(this._rawValue=t,this._value=this.__v_isShallow?t:Rt(t),Kr(this))}}function Dn(e){return ae(e)?e.value:e}const mi={get:(e,t,n)=>Dn(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const i=e[t];return ae(i)&&!ae(n)?(i.value=n,!0):Reflect.set(e,t,n,s)}};function Xr(e){return lt(e)?e:new Proxy(e,mi)}class vi{constructor(t,n,s,i){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this._dirty=!0,this.effect=new Cn(t,()=>{this._dirty||(this._dirty=!0,Kr(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!i,this.__v_isReadonly=s}get value(){const t=Z(this);return Wr(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function yi(e,t,n=!1){let s,i;const u=z(e);return u?(s=e,i=ke):(s=e.get,i=e.set),new vi(s,i,u||!i,n)}Promise.resolve();function We(e,t,n,s){let i;try{i=s?e(...s):e()}catch(u){Yt(u,t,n)}return i}function Ce(e,t,n,s){if(z(e)){const u=We(e,t,n,s);return u&&Pr(u)&&u.catch(l=>{Yt(l,t,n)}),u}const i=[];for(let u=0;u>>1;xt(Se[s])Fe&&Se.splice(t,1)}function Zr(e,t,n,s){K(e)?n.push(...e):(!t||!t.includes(e,e.allowRecurse?s+1:s))&&n.push(e),Vr()}function Ri(e){Zr(e,mt,vt,st)}function xi(e){Zr(e,$e,yt,it)}function Bn(e,t=null){if(vt.length){for(dn=t,mt=[...new Set(vt)],vt.length=0,st=0;stxt(n)-xt(s)),it=0;it<$e.length;it++)$e[it]();$e=null,it=0}}const xt=e=>e.id==null?1/0:e.id;function es(e){hn=!1,Bt=!0,Bn(e),Se.sort((n,s)=>xt(n)-xt(s));const t=ke;try{for(Fe=0;FeB.trim()):I&&(i=n.map(As))}let c,h=s[c=Zt(t)]||s[c=Zt(ft(t))];!h&&u&&(h=s[c=Zt(ht(t))]),h&&Ce(h,e,6,i);const g=s[c+"Once"];if(g){if(!e.emitted)e.emitted={};else if(e.emitted[c])return;e.emitted[c]=!0,Ce(g,e,6,i)}}function ts(e,t,n=!1){const s=t.emitsCache,i=s.get(e);if(i!==void 0)return i;const u=e.emits;let l={},c=!1;if(!z(e)){const h=g=>{const E=ts(g,t,!0);E&&(c=!0,pe(l,E))};!n&&t.mixins.length&&t.mixins.forEach(h),e.extends&&h(e.extends),e.mixins&&e.mixins.forEach(h)}return!u&&!c?(s.set(e,null),null):(K(u)?u.forEach(h=>l[h]=null):pe(l,u),s.set(e,l),l)}function Un(e,t){return!e||!Wt(t)?!1:(t=t.slice(2).replace(/Once$/,""),G(e,t[0].toLowerCase()+t.slice(1))||G(e,ht(t))||G(e,t))}let De=null,ns=null;function Ut(e){const t=De;return De=e,ns=e&&e.type.__scopeId||null,t}function Oi(e,t=De,n){if(!t||e._n)return e;const s=(...i)=>{s._d&&lr(-1);const u=Ut(t),l=e(...i);return Ut(u),s._d&&lr(1),l};return s._n=!0,s._c=!0,s._d=!0,s}function en(e){const{type:t,vnode:n,proxy:s,withProxy:i,props:u,propsOptions:[l],slots:c,attrs:h,emit:g,render:E,renderCache:I,data:T,setupState:B,ctx:q,inheritAttrs:F}=e;let j,U;const J=Ut(e);try{if(n.shapeFlag&4){const se=i||s;j=Ne(E.call(se,se,I,u,B,T,q)),U=h}else{const se=t;j=Ne(se.length>1?se(u,{attrs:h,slots:c,emit:g}):se(u,null)),U=t.props?h:ki(h)}}catch(se){wt.length=0,Yt(se,e,1),j=Ae(tt)}let Q=j;if(U&&F!==!1){const se=Object.keys(U),{shapeFlag:me}=Q;se.length&&me&7&&(l&&se.some(Rn)&&(U=Ci(U,l)),Q=Pt(Q,U))}return n.dirs&&(Q.dirs=Q.dirs?Q.dirs.concat(n.dirs):n.dirs),n.transition&&(Q.transition=n.transition),j=Q,Ut(J),j}const ki=e=>{let t;for(const n in e)(n==="class"||n==="style"||Wt(n))&&((t||(t={}))[n]=e[n]);return t},Ci=(e,t)=>{const n={};for(const s in e)(!Rn(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function Ii(e,t,n){const{props:s,children:i,component:u}=e,{props:l,children:c,patchFlag:h}=t,g=u.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&h>=0){if(h&1024)return!0;if(h&16)return s?tr(s,l,g):!!l;if(h&8){const E=t.dynamicProps;for(let I=0;Ie.__isSuspense;function Li(e,t){t&&t.pendingBranch?K(e)?t.effects.push(...e):t.effects.push(e):xi(e)}function ji(e,t){if(he){let n=he.provides;const s=he.parent&&he.parent.provides;s===n&&(n=he.provides=Object.create(s)),n[e]=t}}function tn(e,t,n=!1){const s=he||De;if(s){const i=s.parent==null?s.vnode.appContext&&s.vnode.appContext.provides:s.parent.provides;if(i&&e in i)return i[e];if(arguments.length>1)return n&&z(t)?t.call(s.proxy):t}}const nr={};function St(e,t,n){return rs(e,t,n)}function rs(e,t,{immediate:n,deep:s,flush:i,onTrack:u,onTrigger:l}=te){const c=he;let h,g=!1,E=!1;if(ae(e)?(h=()=>e.value,g=$r(e)):lt(e)?(h=()=>e,s=!0):K(e)?(E=!0,g=e.some(lt),h=()=>e.map(U=>{if(ae(U))return U.value;if(lt(U))return ot(U);if(z(U))return We(U,c,2)})):z(e)?t?h=()=>We(e,c,2):h=()=>{if(!(c&&c.isUnmounted))return I&&I(),Ce(e,c,3,[T])}:h=ke,t&&s){const U=h;h=()=>ot(U())}let I,T=U=>{I=j.onStop=()=>{We(U,c,4)}};if(Ot)return T=ke,t?n&&Ce(t,c,3,[h(),E?[]:void 0,T]):h(),ke;let B=E?[]:nr;const q=()=>{if(!!j.active)if(t){const U=j.run();(s||g||(E?U.some((J,Q)=>Tt(J,B[Q])):Tt(U,B)))&&(I&&I(),Ce(t,c,3,[U,B===nr?void 0:B,T]),B=U)}else j.run()};q.allowRecurse=!!t;let F;i==="sync"?F=q:i==="post"?F=()=>ge(q,c&&c.suspense):F=()=>{!c||c.isMounted?Ri(q):q()};const j=new Cn(h,F);return t?n?q():B=j.run():i==="post"?ge(j.run.bind(j),c&&c.suspense):j.run(),()=>{j.stop(),c&&c.scope&&xn(c.scope.effects,j)}}function Fi(e,t,n){const s=this.proxy,i=le(e)?e.includes(".")?ss(s,e):()=>s[e]:e.bind(s,s);let u;z(t)?u=t:(u=t.handler,n=t);const l=he;at(this);const c=rs(i,u.bind(s),n);return l?at(l):et(),c}function ss(e,t){const n=t.split(".");return()=>{let s=e;for(let i=0;i{ot(n,t)});else if(kr(e))for(const n in e)ot(e[n],t);return e}function Eu(e){return z(e)?{setup:e,name:e.name}:e}const _n=e=>!!e.type.__asyncLoader,is=e=>e.type.__isKeepAlive;function Di(e,t){os(e,"a",t)}function Ai(e,t){os(e,"da",t)}function os(e,t,n=he){const s=e.__wdc||(e.__wdc=()=>{let i=n;for(;i;){if(i.isDeactivated)return;i=i.parent}return e()});if(Gt(t,s,n),n){let i=n.parent;for(;i&&i.parent;)is(i.parent.vnode)&&Bi(s,t,n,i),i=i.parent}}function Bi(e,t,n,s){const i=Gt(t,e,s,!0);us(()=>{xn(s[t],i)},n)}function Gt(e,t,n=he,s=!1){if(n){const i=n[e]||(n[e]=[]),u=t.__weh||(t.__weh=(...l)=>{if(n.isUnmounted)return;dt(),at(n);const c=Ce(t,n,e,l);return et(),_t(),c});return s?i.unshift(u):i.push(u),u}}const Ue=e=>(t,n=he)=>(!Ot||e==="sp")&&Gt(e,t,n),Ui=Ue("bm"),Hi=Ue("m"),$i=Ue("bu"),Ji=Ue("u"),qi=Ue("bum"),us=Ue("um"),Wi=Ue("sp"),Ki=Ue("rtg"),zi=Ue("rtc");function Xi(e,t=he){Gt("ec",e,t)}let pn=!0;function Yi(e){const t=ls(e),n=e.proxy,s=e.ctx;pn=!1,t.beforeCreate&&rr(t.beforeCreate,e,"bc");const{data:i,computed:u,methods:l,watch:c,provide:h,inject:g,created:E,beforeMount:I,mounted:T,beforeUpdate:B,updated:q,activated:F,deactivated:j,beforeDestroy:U,beforeUnmount:J,destroyed:Q,unmounted:se,render:me,renderTracked:Te,renderTriggered:Re,errorCaptured:be,serverPrefetch:ie,expose:oe,inheritAttrs:P,components:m,directives:b,filters:S}=t;if(g&&Gi(g,s,null,e.appContext.config.unwrapInjectedRef),l)for(const o in l){const a=l[o];z(a)&&(s[o]=a.bind(n))}if(i){const o=i.call(n,n);fe(o)&&(e.data=Ln(o))}if(pn=!0,u)for(const o in u){const a=u[o],p=z(a)?a.bind(n,n):z(a.get)?a.get.bind(n,n):ke,R=!z(a)&&z(a.set)?a.set.bind(n):ke,M=Co({get:p,set:R});Object.defineProperty(s,o,{enumerable:!0,configurable:!0,get:()=>M.value,set:D=>M.value=D})}if(c)for(const o in c)cs(c[o],s,n,o);if(h){const o=z(h)?h.call(n):h;Reflect.ownKeys(o).forEach(a=>{ji(a,o[a])})}E&&rr(E,e,"c");function r(o,a){K(a)?a.forEach(p=>o(p.bind(n))):a&&o(a.bind(n))}if(r(Ui,I),r(Hi,T),r($i,B),r(Ji,q),r(Di,F),r(Ai,j),r(Xi,be),r(zi,Te),r(Ki,Re),r(qi,J),r(us,se),r(Wi,ie),K(oe))if(oe.length){const o=e.exposed||(e.exposed={});oe.forEach(a=>{Object.defineProperty(o,a,{get:()=>n[a],set:p=>n[a]=p})})}else e.exposed||(e.exposed={});me&&e.render===ke&&(e.render=me),P!=null&&(e.inheritAttrs=P),m&&(e.components=m),b&&(e.directives=b)}function Gi(e,t,n=ke,s=!1){K(e)&&(e=bn(e));for(const i in e){const u=e[i];let l;fe(u)?"default"in u?l=tn(u.from||i,u.default,!0):l=tn(u.from||i):l=tn(u),ae(l)&&s?Object.defineProperty(t,i,{enumerable:!0,configurable:!0,get:()=>l.value,set:c=>l.value=c}):t[i]=l}}function rr(e,t,n){Ce(K(e)?e.map(s=>s.bind(t.proxy)):e.bind(t.proxy),t,n)}function cs(e,t,n,s){const i=s.includes(".")?ss(n,s):()=>n[s];if(le(e)){const u=t[e];z(u)&&St(i,u)}else if(z(e))St(i,e.bind(n));else if(fe(e))if(K(e))e.forEach(u=>cs(u,t,n,s));else{const u=z(e.handler)?e.handler.bind(n):t[e.handler];z(u)&&St(i,u,e)}}function ls(e){const t=e.type,{mixins:n,extends:s}=t,{mixins:i,optionsCache:u,config:{optionMergeStrategies:l}}=e.appContext,c=u.get(t);let h;return c?h=c:!i.length&&!n&&!s?h=t:(h={},i.length&&i.forEach(g=>Ht(h,g,l,!0)),Ht(h,t,l)),u.set(t,h),h}function Ht(e,t,n,s=!1){const{mixins:i,extends:u}=t;u&&Ht(e,u,n,!0),i&&i.forEach(l=>Ht(e,l,n,!0));for(const l in t)if(!(s&&l==="expose")){const c=Vi[l]||n&&n[l];e[l]=c?c(e[l],t[l]):t[l]}return e}const Vi={data:sr,props:Ge,emits:Ge,methods:Ge,computed:Ge,beforeCreate:_e,created:_e,beforeMount:_e,mounted:_e,beforeUpdate:_e,updated:_e,beforeDestroy:_e,beforeUnmount:_e,destroyed:_e,unmounted:_e,activated:_e,deactivated:_e,errorCaptured:_e,serverPrefetch:_e,components:Ge,directives:Ge,watch:Qi,provide:sr,inject:Zi};function sr(e,t){return t?e?function(){return pe(z(e)?e.call(this,this):e,z(t)?t.call(this,this):t)}:t:e}function Zi(e,t){return Ge(bn(e),bn(t))}function bn(e){if(K(e)){const t={};for(let n=0;n0)&&!(l&16)){if(l&8){const E=e.vnode.dynamicProps;for(let I=0;I{h=!0;const[T,B]=as(I,t,!0);pe(l,T),B&&c.push(...B)};!n&&t.mixins.length&&t.mixins.forEach(E),e.extends&&E(e.extends),e.mixins&&e.mixins.forEach(E)}if(!u&&!h)return s.set(e,ut),ut;if(K(u))for(let E=0;E-1,B[1]=F<0||q-1||G(B,"default"))&&c.push(I)}}}const g=[l,c];return s.set(e,g),g}function ir(e){return e[0]!=="$"}function or(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:e===null?"null":""}function ur(e,t){return or(e)===or(t)}function cr(e,t){return K(t)?t.findIndex(n=>ur(n,e)):z(t)&&ur(t,e)?0:-1}const hs=e=>e[0]==="_"||e==="$stable",Hn=e=>K(e)?e.map(Ne):[Ne(e)],no=(e,t,n)=>{const s=Oi((...i)=>Hn(t(...i)),n);return s._c=!1,s},ds=(e,t,n)=>{const s=e._ctx;for(const i in e){if(hs(i))continue;const u=e[i];if(z(u))t[i]=no(i,u,s);else if(u!=null){const l=Hn(u);t[i]=()=>l}}},_s=(e,t)=>{const n=Hn(t);e.slots.default=()=>n},ro=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=Z(t),Dt(t,"_",n)):ds(t,e.slots={})}else e.slots={},t&&_s(e,t);Dt(e.slots,Vt,1)},so=(e,t,n)=>{const{vnode:s,slots:i}=e;let u=!0,l=te;if(s.shapeFlag&32){const c=t._;c?n&&c===1?u=!1:(pe(i,t),!n&&c===1&&delete i._):(u=!t.$stable,ds(t,i)),l=t}else t&&(_s(e,t),l={default:1});if(u)for(const c in i)!hs(c)&&!(c in l)&&delete i[c]};function Xe(e,t,n,s){const i=e.dirs,u=t&&t.dirs;for(let l=0;lmn(T,t&&(K(t)?t[B]:t),n,s,i));return}if(_n(s)&&!i)return;const u=s.shapeFlag&4?qn(s.component)||s.component.proxy:s.el,l=i?null:u,{i:c,r:h}=e,g=t&&t.r,E=c.refs===te?c.refs={}:c.refs,I=c.setupState;if(g!=null&&g!==h&&(le(g)?(E[g]=null,G(I,g)&&(I[g]=null)):ae(g)&&(g.value=null)),z(h))We(h,c,12,[l,E]);else{const T=le(h),B=ae(h);if(T||B){const q=()=>{if(e.f){const F=T?E[h]:h.value;i?K(F)&&xn(F,u):K(F)?F.includes(u)||F.push(u):T?E[h]=[u]:(h.value=[u],e.k&&(E[e.k]=h.value))}else T?(E[h]=l,G(I,h)&&(I[h]=l)):ae(h)&&(h.value=l,e.k&&(E[e.k]=l))};l?(q.id=-1,ge(q,n)):q()}}}const ge=Li;function uo(e){return co(e)}function co(e,t){const n=Bs();n.__VUE__=!0;const{insert:s,remove:i,patchProp:u,createElement:l,createText:c,createComment:h,setText:g,setElementText:E,parentNode:I,nextSibling:T,setScopeId:B=ke,cloneNode:q,insertStaticContent:F}=e,j=(f,d,v,y=null,w=null,O=null,N=!1,k=null,C=!!d.dynamicChildren)=>{if(f===d)return;f&&!bt(f,d)&&(y=xe(f),L(f,w,O,!0),f=null),d.patchFlag===-2&&(C=!1,d.dynamicChildren=null);const{type:x,ref:$,shapeFlag:A}=d;switch(x){case $n:U(f,d,v,y);break;case tt:J(f,d,v,y);break;case nn:f==null&&Q(d,v,y,N);break;case je:b(f,d,v,y,w,O,N,k,C);break;default:A&1?Te(f,d,v,y,w,O,N,k,C):A&6?S(f,d,v,y,w,O,N,k,C):(A&64||A&128)&&x.process(f,d,v,y,w,O,N,k,C,Pe)}$!=null&&w&&mn($,f&&f.ref,O,d||f,!d)},U=(f,d,v,y)=>{if(f==null)s(d.el=c(d.children),v,y);else{const w=d.el=f.el;d.children!==f.children&&g(w,d.children)}},J=(f,d,v,y)=>{f==null?s(d.el=h(d.children||""),v,y):d.el=f.el},Q=(f,d,v,y)=>{[f.el,f.anchor]=F(f.children,d,v,y,f.el,f.anchor)},se=({el:f,anchor:d},v,y)=>{let w;for(;f&&f!==d;)w=T(f),s(f,v,y),f=w;s(d,v,y)},me=({el:f,anchor:d})=>{let v;for(;f&&f!==d;)v=T(f),i(f),f=v;i(d)},Te=(f,d,v,y,w,O,N,k,C)=>{N=N||d.type==="svg",f==null?Re(d,v,y,w,O,N,k,C):oe(f,d,w,O,N,k,C)},Re=(f,d,v,y,w,O,N,k)=>{let C,x;const{type:$,props:A,shapeFlag:H,transition:W,patchFlag:Y,dirs:re}=f;if(f.el&&q!==void 0&&Y===-1)C=f.el=q(f.el);else{if(C=f.el=l(f.type,O,A&&A.is,A),H&8?E(C,f.children):H&16&&ie(f.children,C,null,y,w,O&&$!=="foreignObject",N,k),re&&Xe(f,null,y,"created"),A){for(const ne in A)ne!=="value"&&!jt(ne)&&u(C,ne,null,A[ne],O,f.children,y,w,V);"value"in A&&u(C,"value",null,A.value),(x=A.onVnodeBeforeMount)&&Me(x,y,f)}be(C,f,f.scopeId,N,y)}re&&Xe(f,null,y,"beforeMount");const ee=(!w||w&&!w.pendingBranch)&&W&&!W.persisted;ee&&W.beforeEnter(C),s(C,d,v),((x=A&&A.onVnodeMounted)||ee||re)&&ge(()=>{x&&Me(x,y,f),ee&&W.enter(C),re&&Xe(f,null,y,"mounted")},w)},be=(f,d,v,y,w)=>{if(v&&B(f,v),y)for(let O=0;O{for(let x=C;x{const k=d.el=f.el;let{patchFlag:C,dynamicChildren:x,dirs:$}=d;C|=f.patchFlag&16;const A=f.props||te,H=d.props||te;let W;v&&Ye(v,!1),(W=H.onVnodeBeforeUpdate)&&Me(W,v,d,f),$&&Xe(d,f,v,"beforeUpdate"),v&&Ye(v,!0);const Y=w&&d.type!=="foreignObject";if(x?P(f.dynamicChildren,x,k,v,y,Y,O):N||p(f,d,k,null,v,y,Y,O,!1),C>0){if(C&16)m(k,d,A,H,v,y,w);else if(C&2&&A.class!==H.class&&u(k,"class",null,H.class,w),C&4&&u(k,"style",A.style,H.style,w),C&8){const re=d.dynamicProps;for(let ee=0;ee{W&&Me(W,v,d,f),$&&Xe(d,f,v,"updated")},y)},P=(f,d,v,y,w,O,N)=>{for(let k=0;k{if(v!==y){for(const k in y){if(jt(k))continue;const C=y[k],x=v[k];C!==x&&k!=="value"&&u(f,k,x,C,N,d.children,w,O,V)}if(v!==te)for(const k in v)!jt(k)&&!(k in y)&&u(f,k,v[k],null,N,d.children,w,O,V);"value"in y&&u(f,"value",v.value,y.value)}},b=(f,d,v,y,w,O,N,k,C)=>{const x=d.el=f?f.el:c(""),$=d.anchor=f?f.anchor:c("");let{patchFlag:A,dynamicChildren:H,slotScopeIds:W}=d;W&&(k=k?k.concat(W):W),f==null?(s(x,v,y),s($,v,y),ie(d.children,v,$,w,O,N,k,C)):A>0&&A&64&&H&&f.dynamicChildren?(P(f.dynamicChildren,H,v,w,O,N,k),(d.key!=null||w&&d===w.subTree)&&bs(f,d,!0)):p(f,d,v,$,w,O,N,k,C)},S=(f,d,v,y,w,O,N,k,C)=>{d.slotScopeIds=k,f==null?d.shapeFlag&512?w.ctx.activate(d,v,y,N,C):_(d,v,y,w,O,N,C):r(f,d,C)},_=(f,d,v,y,w,O,N)=>{const k=f.component=Eo(f,y,w);if(is(f)&&(k.ctx.renderer=Pe),Ro(k),k.asyncDep){if(w&&w.registerDep(k,o),!f.el){const C=k.subTree=Ae(tt);J(null,C,d,v)}return}o(k,f,d,v,w,O,N)},r=(f,d,v)=>{const y=d.component=f.component;if(Ii(f,d,v))if(y.asyncDep&&!y.asyncResolved){a(y,d,v);return}else y.next=d,Ei(y.update),y.update();else d.component=f.component,d.el=f.el,y.vnode=d},o=(f,d,v,y,w,O,N)=>{const k=()=>{if(f.isMounted){let{next:$,bu:A,u:H,parent:W,vnode:Y}=f,re=$,ee;Ye(f,!1),$?($.el=Y.el,a(f,$,N)):$=Y,A&&Qt(A),(ee=$.props&&$.props.onVnodeBeforeUpdate)&&Me(ee,W,$,Y),Ye(f,!0);const ne=en(f),Oe=f.subTree;f.subTree=ne,j(Oe,ne,I(Oe.el),xe(Oe),f,w,O),$.el=ne.el,re===null&&Mi(f,ne.el),H&&ge(H,w),(ee=$.props&&$.props.onVnodeUpdated)&&ge(()=>Me(ee,W,$,Y),w)}else{let $;const{el:A,props:H}=d,{bm:W,m:Y,parent:re}=f,ee=_n(d);if(Ye(f,!1),W&&Qt(W),!ee&&($=H&&H.onVnodeBeforeMount)&&Me($,re,d),Ye(f,!0),A&&ze){const ne=()=>{f.subTree=en(f),ze(A,f.subTree,f,w,null)};ee?d.type.__asyncLoader().then(()=>!f.isUnmounted&&ne()):ne()}else{const ne=f.subTree=en(f);j(null,ne,v,y,f,w,O),d.el=ne.el}if(Y&&ge(Y,w),!ee&&($=H&&H.onVnodeMounted)){const ne=d;ge(()=>Me($,re,ne),w)}d.shapeFlag&256&&f.a&&ge(f.a,w),f.isMounted=!0,d=v=y=null}},C=f.effect=new Cn(k,()=>Gr(f.update),f.scope),x=f.update=C.run.bind(C);x.id=f.uid,Ye(f,!0),x()},a=(f,d,v)=>{d.component=f;const y=f.vnode.props;f.vnode=d,f.next=null,to(f,d.props,y,v),so(f,d.children,v),dt(),Bn(void 0,f.update),_t()},p=(f,d,v,y,w,O,N,k,C=!1)=>{const x=f&&f.children,$=f?f.shapeFlag:0,A=d.children,{patchFlag:H,shapeFlag:W}=d;if(H>0){if(H&128){M(x,A,v,y,w,O,N,k,C);return}else if(H&256){R(x,A,v,y,w,O,N,k,C);return}}W&8?($&16&&V(x,w,O),A!==x&&E(v,A)):$&16?W&16?M(x,A,v,y,w,O,N,k,C):V(x,w,O,!0):($&8&&E(v,""),W&16&&ie(A,v,y,w,O,N,k,C))},R=(f,d,v,y,w,O,N,k,C)=>{f=f||ut,d=d||ut;const x=f.length,$=d.length,A=Math.min(x,$);let H;for(H=0;H$?V(f,w,O,!0,!1,A):ie(d,v,y,w,O,N,k,C,A)},M=(f,d,v,y,w,O,N,k,C)=>{let x=0;const $=d.length;let A=f.length-1,H=$-1;for(;x<=A&&x<=H;){const W=f[x],Y=d[x]=C?Je(d[x]):Ne(d[x]);if(bt(W,Y))j(W,Y,v,null,w,O,N,k,C);else break;x++}for(;x<=A&&x<=H;){const W=f[A],Y=d[H]=C?Je(d[H]):Ne(d[H]);if(bt(W,Y))j(W,Y,v,null,w,O,N,k,C);else break;A--,H--}if(x>A){if(x<=H){const W=H+1,Y=W<$?d[W].el:y;for(;x<=H;)j(null,d[x]=C?Je(d[x]):Ne(d[x]),v,Y,w,O,N,k,C),x++}}else if(x>H)for(;x<=A;)L(f[x],w,O,!0),x++;else{const W=x,Y=x,re=new Map;for(x=Y;x<=H;x++){const ye=d[x]=C?Je(d[x]):Ne(d[x]);ye.key!=null&&re.set(ye.key,x)}let ee,ne=0;const Oe=H-Y+1;let rt=!1,Wn=0;const pt=new Array(Oe);for(x=0;x=Oe){L(ye,w,O,!0);continue}let Ie;if(ye.key!=null)Ie=re.get(ye.key);else for(ee=Y;ee<=H;ee++)if(pt[ee-Y]===0&&bt(ye,d[ee])){Ie=ee;break}Ie===void 0?L(ye,w,O,!0):(pt[Ie-Y]=x+1,Ie>=Wn?Wn=Ie:rt=!0,j(ye,d[Ie],v,null,w,O,N,k,C),ne++)}const Kn=rt?lo(pt):ut;for(ee=Kn.length-1,x=Oe-1;x>=0;x--){const ye=Y+x,Ie=d[ye],zn=ye+1<$?d[ye+1].el:y;pt[x]===0?j(null,Ie,v,zn,w,O,N,k,C):rt&&(ee<0||x!==Kn[ee]?D(Ie,v,zn,2):ee--)}}},D=(f,d,v,y,w=null)=>{const{el:O,type:N,transition:k,children:C,shapeFlag:x}=f;if(x&6){D(f.component.subTree,d,v,y);return}if(x&128){f.suspense.move(d,v,y);return}if(x&64){N.move(f,d,v,Pe);return}if(N===je){s(O,d,v);for(let A=0;Ak.enter(O),w);else{const{leave:A,delayLeave:H,afterLeave:W}=k,Y=()=>s(O,d,v),re=()=>{A(O,()=>{Y(),W&&W()})};H?H(O,Y,re):re()}else s(O,d,v)},L=(f,d,v,y=!1,w=!1)=>{const{type:O,props:N,ref:k,children:C,dynamicChildren:x,shapeFlag:$,patchFlag:A,dirs:H}=f;if(k!=null&&mn(k,null,v,f,!0),$&256){d.ctx.deactivate(f);return}const W=$&1&&H,Y=!_n(f);let re;if(Y&&(re=N&&N.onVnodeBeforeUnmount)&&Me(re,d,f),$&6)ce(f.component,v,y);else{if($&128){f.suspense.unmount(v,y);return}W&&Xe(f,null,d,"beforeUnmount"),$&64?f.type.remove(f,d,v,w,Pe,y):x&&(O!==je||A>0&&A&64)?V(x,d,v,!1,!0):(O===je&&A&384||!w&&$&16)&&V(C,d,v),y&&X(f)}(Y&&(re=N&&N.onVnodeUnmounted)||W)&&ge(()=>{re&&Me(re,d,f),W&&Xe(f,null,d,"unmounted")},v)},X=f=>{const{type:d,el:v,anchor:y,transition:w}=f;if(d===je){ue(v,y);return}if(d===nn){me(f);return}const O=()=>{i(v),w&&!w.persisted&&w.afterLeave&&w.afterLeave()};if(f.shapeFlag&1&&w&&!w.persisted){const{leave:N,delayLeave:k}=w,C=()=>N(v,O);k?k(f.el,O,C):C()}else O()},ue=(f,d)=>{let v;for(;f!==d;)v=T(f),i(f),f=v;i(d)},ce=(f,d,v)=>{const{bum:y,scope:w,update:O,subTree:N,um:k}=f;y&&Qt(y),w.stop(),O&&(O.active=!1,L(N,f,d,v)),k&&ge(k,d),ge(()=>{f.isUnmounted=!0},d),d&&d.pendingBranch&&!d.isUnmounted&&f.asyncDep&&!f.asyncResolved&&f.suspenseId===d.pendingId&&(d.deps--,d.deps===0&&d.resolve())},V=(f,d,v,y=!1,w=!1,O=0)=>{for(let N=O;Nf.shapeFlag&6?xe(f.component.subTree):f.shapeFlag&128?f.suspense.next():T(f.anchor||f.el),nt=(f,d,v)=>{f==null?d._vnode&&L(d._vnode,null,null,!0):j(d._vnode||null,f,d,null,null,null,v),Qr(),d._vnode=f},Pe={p:j,um:L,m:D,r:X,mt:_,mc:ie,pc:p,pbc:P,n:xe,o:e};let ve,ze;return t&&([ve,ze]=t(Pe)),{render:nt,hydrate:ve,createApp:oo(nt,ve)}}function Ye({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function bs(e,t,n=!1){const s=e.children,i=t.children;if(K(s)&&K(i))for(let u=0;u>1,e[n[c]]0&&(t[s]=n[u-1]),n[u]=s)}}for(u=n.length,l=n[u-1];u-- >0;)n[u]=l,l=t[l];return n}const fo=e=>e.__isTeleport,ao=Symbol(),je=Symbol(void 0),$n=Symbol(void 0),tt=Symbol(void 0),nn=Symbol(void 0),wt=[];let Qe=null;function ho(e=!1){wt.push(Qe=e?null:[])}function _o(){wt.pop(),Qe=wt[wt.length-1]||null}let $t=1;function lr(e){$t+=e}function gs(e){return e.dynamicChildren=$t>0?Qe||ut:null,_o(),$t>0&&Qe&&Qe.push(e),e}function Ru(e,t,n,s,i,u){return gs(vs(e,t,n,s,i,u,!0))}function po(e,t,n,s,i){return gs(Ae(e,t,n,s,i,!0))}function bo(e){return e?e.__v_isVNode===!0:!1}function bt(e,t){return e.type===t.type&&e.key===t.key}const Vt="__vInternal",ms=({key:e})=>e!=null?e:null,Ft=({ref:e,ref_key:t,ref_for:n})=>e!=null?le(e)||ae(e)||z(e)?{i:De,r:e,k:t,f:!!n}:e:null;function vs(e,t=null,n=null,s=0,i=null,u=e===je?0:1,l=!1,c=!1){const h={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ms(t),ref:t&&Ft(t),scopeId:ns,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:u,patchFlag:s,dynamicProps:i,dynamicChildren:null,appContext:null};return c?(Jn(h,n),u&128&&e.normalize(h)):n&&(h.shapeFlag|=le(n)?8:16),$t>0&&!l&&Qe&&(h.patchFlag>0||u&6)&&h.patchFlag!==32&&Qe.push(h),h}const Ae=go;function go(e,t=null,n=null,s=0,i=null,u=!1){if((!e||e===ao)&&(e=tt),bo(e)){const c=Pt(e,t,!0);return n&&Jn(c,n),c}if(ko(e)&&(e=e.__vccOpts),t){t=mo(t);let{class:c,style:h}=t;c&&!le(c)&&(t.class=En(c)),fe(h)&&(Jr(h)&&!K(h)&&(h=pe({},h)),t.style=Tn(h))}const l=le(e)?1:Ni(e)?128:fo(e)?64:fe(e)?4:z(e)?2:0;return vs(e,t,n,s,i,l,u,!0)}function mo(e){return e?Jr(e)||Vt in e?pe({},e):e:null}function Pt(e,t,n=!1){const{props:s,ref:i,patchFlag:u,children:l}=e,c=t?yo(s||{},t):s;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&ms(c),ref:t&&t.ref?n&&i?K(i)?i.concat(Ft(t)):[i,Ft(t)]:Ft(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:l,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==je?u===-1?16:u|16:u,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Pt(e.ssContent),ssFallback:e.ssFallback&&Pt(e.ssFallback),el:e.el,anchor:e.anchor}}function vo(e=" ",t=0){return Ae($n,null,e,t)}function xu(e="",t=!1){return t?(ho(),po(tt,null,e)):Ae(tt,null,e)}function Ne(e){return e==null||typeof e=="boolean"?Ae(tt):K(e)?Ae(je,null,e.slice()):typeof e=="object"?Je(e):Ae($n,null,String(e))}function Je(e){return e.el===null||e.memo?e:Pt(e)}function Jn(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(K(t))n=16;else if(typeof t=="object")if(s&65){const i=t.default;i&&(i._c&&(i._d=!1),Jn(e,i()),i._c&&(i._d=!0));return}else{n=32;const i=t._;!i&&!(Vt in t)?t._ctx=De:i===3&&De&&(De.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else z(t)?(t={default:t,_ctx:De},n=32):(t=String(t),s&64?(n=16,t=[vo(t)]):n=8);e.children=t,e.shapeFlag|=n}function yo(...e){const t={};for(let n=0;nt(l,c,void 0,u&&u[c]));else{const l=Object.keys(e);i=new Array(l.length);for(let c=0,h=l.length;ce?ys(e)?qn(e)||e.proxy:vn(e.parent):null,Jt=pe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>vn(e.parent),$root:e=>vn(e.root),$emit:e=>e.emit,$options:e=>ls(e),$forceUpdate:e=>()=>Gr(e.update),$nextTick:e=>wi.bind(e.proxy),$watch:e=>Fi.bind(e)}),So={get({_:e},t){const{ctx:n,setupState:s,data:i,props:u,accessCache:l,type:c,appContext:h}=e;let g;if(t[0]!=="$"){const B=l[t];if(B!==void 0)switch(B){case 1:return s[t];case 2:return i[t];case 4:return n[t];case 3:return u[t]}else{if(s!==te&&G(s,t))return l[t]=1,s[t];if(i!==te&&G(i,t))return l[t]=2,i[t];if((g=e.propsOptions[0])&&G(g,t))return l[t]=3,u[t];if(n!==te&&G(n,t))return l[t]=4,n[t];pn&&(l[t]=0)}}const E=Jt[t];let I,T;if(E)return t==="$attrs"&&we(e,"get",t),E(e);if((I=c.__cssModules)&&(I=I[t]))return I;if(n!==te&&G(n,t))return l[t]=4,n[t];if(T=h.config.globalProperties,G(T,t))return T[t]},set({_:e},t,n){const{data:s,setupState:i,ctx:u}=e;return i!==te&&G(i,t)?(i[t]=n,!0):s!==te&&G(s,t)?(s[t]=n,!0):G(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(u[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:i,propsOptions:u}},l){let c;return!!n[l]||e!==te&&G(e,l)||t!==te&&G(t,l)||(c=u[0])&&G(c,l)||G(s,l)||G(Jt,l)||G(i.config.globalProperties,l)},defineProperty(e,t,n){return n.get!=null?this.set(e,t,n.get(),null):n.value!=null&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}},wo=ps();let To=0;function Eo(e,t,n){const s=e.type,i=(t?t.appContext:e.appContext)||wo,u={uid:To++,vnode:e,type:s,parent:t,appContext:i,root:null,next:null,subTree:null,effect:null,update:null,scope:new Us(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:t?t.provides:Object.create(i.provides),accessCache:null,renderCache:[],components:null,directives:null,propsOptions:as(s,i),emitsOptions:ts(s,i),emit:null,emitted:null,propsDefaults:te,inheritAttrs:s.inheritAttrs,ctx:te,data:te,props:te,attrs:te,slots:te,refs:te,setupState:te,setupContext:null,suspense:n,suspenseId:n?n.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return u.ctx={_:u},u.root=t?t.root:u,u.emit=Pi.bind(null,u),e.ce&&e.ce(u),u}let he=null;const at=e=>{he=e,e.scope.on()},et=()=>{he&&he.scope.off(),he=null};function ys(e){return e.vnode.shapeFlag&4}let Ot=!1;function Ro(e,t=!1){Ot=t;const{props:n,children:s}=e.vnode,i=ys(e);eo(e,n,i,t),ro(e,s);const u=i?xo(e,t):void 0;return Ot=!1,u}function xo(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=qr(new Proxy(e.ctx,So));const{setup:s}=n;if(s){const i=e.setupContext=s.length>1?Oo(e):null;at(e),dt();const u=We(s,e,0,[e.props,i]);if(_t(),et(),Pr(u)){if(u.then(et,et),t)return u.then(l=>{fr(e,l,t)}).catch(l=>{Yt(l,e,0)});e.asyncDep=u}else fr(e,u,t)}else Ss(e,t)}function fr(e,t,n){z(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:fe(t)&&(e.setupState=Xr(t)),Ss(e,n)}let ar;function Ss(e,t,n){const s=e.type;if(!e.render){if(!t&&ar&&!s.render){const i=s.template;if(i){const{isCustomElement:u,compilerOptions:l}=e.appContext.config,{delimiters:c,compilerOptions:h}=s,g=pe(pe({isCustomElement:u,delimiters:c},l),h);s.render=ar(i,g)}}e.render=s.render||ke}at(e),dt(),Yi(e),_t(),et()}function Po(e){return new Proxy(e.attrs,{get(t,n){return we(e,"get","$attrs"),t[n]}})}function Oo(e){const t=s=>{e.exposed=s||{}};let n;return{get attrs(){return n||(n=Po(e))},slots:e.slots,emit:e.emit,expose:t}}function qn(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Xr(qr(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Jt)return Jt[n](e)}}))}function ko(e){return z(e)&&"__vccOpts"in e}const Co=(e,t)=>yi(e,t,Ot),Io="3.2.31",Mo="http://www.w3.org/2000/svg",Ve=typeof document!="undefined"?document:null,hr=Ve&&Ve.createElement("template"),No={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const i=t?Ve.createElementNS(Mo,e):Ve.createElement(e,n?{is:n}:void 0);return e==="select"&&s&&s.multiple!=null&&i.setAttribute("multiple",s.multiple),i},createText:e=>Ve.createTextNode(e),createComment:e=>Ve.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Ve.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},cloneNode(e){const t=e.cloneNode(!0);return"_value"in e&&(t._value=e._value),t},insertStaticContent(e,t,n,s,i,u){const l=n?n.previousSibling:t.lastChild;if(i&&(i===u||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),n),!(i===u||!(i=i.nextSibling)););else{hr.innerHTML=s?`${e}`:e;const c=hr.content;if(s){const h=c.firstChild;for(;h.firstChild;)c.appendChild(h.firstChild);c.removeChild(h)}t.insertBefore(c,n)}return[l?l.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function Lo(e,t,n){const s=e._vtc;s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function jo(e,t,n){const s=e.style,i=le(n);if(n&&!i){for(const u in n)yn(s,u,n[u]);if(t&&!le(t))for(const u in t)n[u]==null&&yn(s,u,"")}else{const u=s.display;i?t!==n&&(s.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(s.display=u)}}const dr=/\s*!important$/;function yn(e,t,n){if(K(n))n.forEach(s=>yn(e,t,s));else if(t.startsWith("--"))e.setProperty(t,n);else{const s=Fo(e,t);dr.test(n)?e.setProperty(ht(s),n.replace(dr,""),"important"):e[s]=n}}const _r=["Webkit","Moz","ms"],rn={};function Fo(e,t){const n=rn[t];if(n)return n;let s=ft(t);if(s!=="filter"&&s in e)return rn[t]=s;s=Cr(s);for(let i=0;i<_r.length;i++){const u=_r[i]+s;if(u in e)return rn[t]=u}return t}const pr="http://www.w3.org/1999/xlink";function Do(e,t,n,s,i){if(s&&t.startsWith("xlink:"))n==null?e.removeAttributeNS(pr,t.slice(6,t.length)):e.setAttributeNS(pr,t,n);else{const u=Os(t);n==null||u&&!Er(n)?e.removeAttribute(t):e.setAttribute(t,u?"":n)}}function Ao(e,t,n,s,i,u,l){if(t==="innerHTML"||t==="textContent"){s&&l(s,i,u),e[t]=n==null?"":n;return}if(t==="value"&&e.tagName!=="PROGRESS"&&!e.tagName.includes("-")){e._value=n;const c=n==null?"":n;(e.value!==c||e.tagName==="OPTION")&&(e.value=c),n==null&&e.removeAttribute(t);return}if(n===""||n==null){const c=typeof e[t];if(c==="boolean"){e[t]=Er(n);return}else if(n==null&&c==="string"){e[t]="",e.removeAttribute(t);return}else if(c==="number"){try{e[t]=0}catch{}e.removeAttribute(t);return}}try{e[t]=n}catch{}}let qt=Date.now,ws=!1;if(typeof window!="undefined"){qt()>document.createEvent("Event").timeStamp&&(qt=()=>performance.now());const e=navigator.userAgent.match(/firefox\/(\d+)/i);ws=!!(e&&Number(e[1])<=53)}let Sn=0;const Bo=Promise.resolve(),Uo=()=>{Sn=0},Ho=()=>Sn||(Bo.then(Uo),Sn=qt());function $o(e,t,n,s){e.addEventListener(t,n,s)}function Jo(e,t,n,s){e.removeEventListener(t,n,s)}function qo(e,t,n,s,i=null){const u=e._vei||(e._vei={}),l=u[t];if(s&&l)l.value=s;else{const[c,h]=Wo(t);if(s){const g=u[t]=Ko(s,i);$o(e,c,g,h)}else l&&(Jo(e,c,l,h),u[t]=void 0)}}const br=/(?:Once|Passive|Capture)$/;function Wo(e){let t;if(br.test(e)){t={};let n;for(;n=e.match(br);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}return[ht(e.slice(2)),t]}function Ko(e,t){const n=s=>{const i=s.timeStamp||qt();(ws||i>=n.attached-1)&&Ce(zo(s,n.value),t,5,[s])};return n.value=e,n.attached=Ho(),n}function zo(e,t){if(K(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>i=>!i._stopped&&s&&s(i))}else return t}const gr=/^on[a-z]/,Xo=(e,t,n,s,i=!1,u,l,c,h)=>{t==="class"?Lo(e,s,i):t==="style"?jo(e,n,s):Wt(t)?Rn(t)||qo(e,t,n,s,l):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Yo(e,t,s,i))?Ao(e,t,s,u,l,c,h):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),Do(e,t,s,i))};function Yo(e,t,n,s){return s?!!(t==="innerHTML"||t==="textContent"||t in e&&gr.test(t)&&z(n)):t==="spellcheck"||t==="draggable"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||gr.test(t)&&le(n)?!1:t in e}const Go=pe({patchProp:Xo},No);let mr;function Vo(){return mr||(mr=uo(Go))}const Ou=(...e)=>{const t=Vo().createApp(...e),{mount:n}=t;return t.mount=s=>{const i=Zo(s);if(!i)return;const u=t._component;!z(u)&&!u.render&&!u.template&&(u.template=i.innerHTML),i.innerHTML="";const l=n(i,!1,i instanceof SVGElement);return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),l},t};function Zo(e){return le(e)?document.querySelector(e):e}var Qo=typeof globalThis!="undefined"?globalThis:typeof window!="undefined"?window:typeof global!="undefined"?global:typeof self!="undefined"?self:{};function eu(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Ts={exports:{}};(function(e,t){(function(s,i){e.exports=i()})(Qo,function(){return function(){var n={382:function(l,c,h){Object.defineProperty(c,"__esModule",{value:!0}),c.Centrifuge=void 0;var g=B(h(187)),E=B(h(471)),I=h(147),T=h(853);function B(m){return m&&m.__esModule?m:{default:m}}function q(m){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?q=function(S){return typeof S}:q=function(S){return S&&typeof Symbol=="function"&&S.constructor===Symbol&&S!==Symbol.prototype?"symbol":typeof S},q(m)}function F(m,b){if(!(m instanceof b))throw new TypeError("Cannot call a class as a function")}function j(m,b){for(var S=0;S0&&(D+="&"),D+=encodeURIComponent(X)+"="+encodeURIComponent(o[X]));D.length>0&&(D="?"+D),L.open("POST",r+D,!0),"withCredentials"in L&&(L.withCredentials=!this._config.disableWithCredentials),L.setRequestHeader("X-Requested-With","XMLHttpRequest"),L.setRequestHeader("Content-Type","application/json");for(var ue in a)a.hasOwnProperty(ue)&&L.setRequestHeader(ue,a[ue]);return L.onreadystatechange=function(){if(L.readyState===4)if(L.status===200){var ce,V=!1;try{ce=JSON.parse(L.responseText),V=!0}catch{R({error:"Invalid JSON. Data was: "+L.responseText,status:200,data:null})}V&&R({data:ce,status:200})}else M._log("wrong status code in AJAX response",L.status),R({status:L.status,data:null})},setTimeout(function(){return L.send(JSON.stringify(p))},20),L}},{key:"_log",value:function(){(0,T.log)("info",arguments)}},{key:"_debug",value:function(){this._config.debug===!0&&(0,T.log)("debug",arguments)}},{key:"_websocketSupported",value:function(){return this._config.websocket!==null?!0:!(typeof WebSocket!="function"&&(typeof WebSocket=="undefined"?"undefined":q(WebSocket))!=="object")}},{key:"_setFormat",value:function(r){if(!this._formatOverride(r)){if(r==="protobuf")throw new Error("not implemented by JSON only Centrifuge client \u2013 use client with Protobuf");this._binary=!1,this._methodType=I.JsonMethodType,this._pushType=I.JsonPushType,this._encoder=new I.JsonEncoder,this._decoder=new I.JsonDecoder}}},{key:"_formatOverride",value:function(r){return!1}},{key:"_configure",value:function(r){if(!("Promise"in h.g))throw new Error("Promise polyfill required");if((0,T.extend)(this._config,r||{}),this._debug("centrifuge config",this._config),!this._url)throw new Error("url required");var o=(0,T.startsWith)(this._url,"ws")&&this._url.indexOf("format=protobuf")>-1;if(o||this._config.protocol==="protobuf")this._setFormat("protobuf"),this._protocol="protobuf";else{if(this._config.protocol!==""&&this._config.protocol!=="json")throw new Error("unsupported protocol "+this._config.protocol);this._setFormat("json")}if((0,T.startsWith)(this._url,"http"))if(this._debug("client will try to connect to SockJS endpoint"),this._config.sockjs!==null)this._debug("SockJS explicitly provided in options"),this._sockjs=this._config.sockjs;else{if(typeof h.g.SockJS=="undefined")throw new Error("SockJS not found, use ws:// in url or include SockJS");this._debug("use globally defined SockJS"),this._sockjs=h.g.SockJS}else this._debug("client will connect to websocket endpoint");this._xmlhttprequest=this._config.xmlhttprequest}},{key:"_setStatus",value:function(r){this._status!==r&&(this._debug("Status",this._status,"->",r),this._status=r)}},{key:"_isDisconnected",value:function(){return this._status==="disconnected"}},{key:"_isConnecting",value:function(){return this._status==="connecting"}},{key:"_isConnected",value:function(){return this._status==="connected"}},{key:"_nextMessageId",value:function(){return++this._messageId}},{key:"_resetRetry",value:function(){this._debug("reset retries count to 0"),this._retries=0}},{key:"_getRetryInterval",value:function(){var r=(0,T.backoff)(this._retries,this._config.minRetry,this._config.maxRetry);return this._retries+=1,r}},{key:"_abortInflightXHRs",value:function(){for(var r in this._xhrs){try{this._xhrs[r].abort()}catch(o){this._debug("error aborting xhr",o)}delete this._xhrs[r]}}},{key:"_clearConnectedState",value:function(r){this._clientID=null,this._stopPing();for(var o in this._callbacks)if(this._callbacks.hasOwnProperty(o)){var a=this._callbacks[o];clearTimeout(a.timeout);var p=a.errback;if(!p)continue;p({error:this._createErrorObject("disconnected")})}this._callbacks={};for(var R in this._subs)if(this._subs.hasOwnProperty(R)){var M=this._subs[R];r?(M._isSuccess()&&(M._triggerUnsubscribe(),M._recover=!0),M._shouldResubscribe()&&M._setSubscribing()):M._setUnsubscribed()}this._abortInflightXHRs(),this._refreshTimeout!==null&&(clearTimeout(this._refreshTimeout),this._refreshTimeout=null);for(var D in this._subRefreshTimeouts)this._subRefreshTimeouts.hasOwnProperty(D)&&this._subRefreshTimeouts[D]&&this._clearSubRefreshTimeout(D);this._subRefreshTimeouts={},this._reconnect||(this._subs={})}},{key:"_isTransportOpen",value:function(){return this._isSockjs?this._transport&&this._transport.transport&&this._transport.transport.readyState===this._transport.transport.OPEN:this._transport&&this._transport.readyState===this._transport.OPEN}},{key:"_transportSend",value:function(r){if(!r.length)return!0;if(!this._isTransportOpen()){for(var o in r){var a=o.id;if(a in this._callbacks){var p=this._callbacks[a];clearTimeout(this._callbacks[a].timeout),delete this._callbacks[a];var R=p.errback;R({error:this._createErrorObject(oe,0)})}}return!1}return this._transport.send(this._encoder.encodeCommands(r)),!0}},{key:"_getSubProtocol",value:function(){return this._protocol?"centrifuge-"+this._protocol:""}},{key:"_setupTransport",value:function(){var r=this;if(this._isSockjs=!1,this._sockjs!==null){var o={transports:this._config.sockjsTransports};this._config.sockjsServer!==null&&(o.server=this._config.sockjsServer),this._config.sockjsTimeout!==null&&(o.timeout=this._config.sockjsTimeout),this._isSockjs=!0,this._transport=new this._sockjs(this._url,null,o)}else{if(!this._websocketSupported()){this._debug("No Websocket support and no SockJS configured, can not connect");return}this._config.websocket!==null?this._websocket=this._config.websocket:this._websocket=WebSocket;var a=this._getSubProtocol();a!==""?this._transport=new this._websocket(this._url,a):this._transport=new this._websocket(this._url),this._binary===!0&&(this._transport.binaryType="arraybuffer")}this._transport.onopen=function(){r._transportClosed=!1,r._isSockjs?(r._transportName="sockjs-"+r._transport.transport,r._transport.onheartbeat=function(){return r._restartPing()}):r._transportName="websocket";var p={};(r._token||r._connectData||r._config.name||r._config.version)&&(p.params={}),r._token&&(p.params.token=r._token),r._connectData&&(p.params.data=r._connectData),r._config.name&&(p.params.name=r._config.name),r._config.version&&(p.params.version=r._config.version);var R={},M=!1;for(var D in r._serverSubs)if(r._serverSubs.hasOwnProperty(D)&&r._serverSubs[D].recoverable){M=!0;var L={recover:!0};r._serverSubs[D].seq||r._serverSubs[D].gen?(r._serverSubs[D].seq&&(L.seq=r._serverSubs[D].seq),r._serverSubs[D].gen&&(L.gen=r._serverSubs[D].gen)):r._serverSubs[D].offset&&(L.offset=r._serverSubs[D].offset),r._serverSubs[D].epoch&&(L.epoch=r._serverSubs[D].epoch),R[D]=L}M&&(p.params||(p.params={}),p.params.subs=R),r._latencyStart=new Date,r._call(p).then(function(X){r._connectResponse(r._decoder.decodeCommandResult(r._methodType.CONNECT,X.result),M),X.next&&X.next()},function(X){var ue=X.error;ue.code===109&&(r._refreshRequired=!0),r._disconnect("connect error",!0),X.next&&X.next()})},this._transport.onerror=function(p){r._debug("transport level error",p)},this._transport.onclose=function(p){r._transportClosed=!0;var R=oe,M=!0;if(p&&"reason"in p&&p.reason)try{var D=JSON.parse(p.reason);r._debug("reason is an advice object",D),R=D.reason,M=D.reconnect}catch{R=p.reason,r._debug("reason is a plain string",R)}if(r._config.onTransportClose!==null&&r._config.onTransportClose({event:p,reason:R,reconnect:M}),r._disconnect(R,M),r._reconnect===!0){r._reconnecting=!0;var L=r._getRetryInterval();r._debug("reconnect after "+L+" milliseconds"),setTimeout(function(){r._reconnect===!0&&(r._refreshRequired?r._refresh():r._connect())},L)}},this._transport.onmessage=function(p){r._dataReceived(p.data)}}},{key:"rpc",value:function(r){return this._rpc("",r)}},{key:"namedRPC",value:function(r,o){return this._rpc(r,o)}},{key:"_rpc",value:function(r,o){var a={data:o};r!==""&&(a.method=r);var p={method:this._methodType.RPC,params:a};return this._methodCall(p,function(R){return R})}},{key:"send",value:function(r){var o={method:this._methodType.SEND,params:{data:r}};if(!this.isConnected())return Promise.reject(this._createErrorObject(oe,0));var a=this._transportSend([o]);return a?Promise.resolve({}):Promise.reject(this._createErrorObject(oe,0))}},{key:"_getHistoryParams",value:function(r,o){var a={channel:r};return o!==void 0&&(o.since&&(a.since={offset:o.since.offset},o.since.epoch&&(a.since.epoch=o.since.epoch)),o.limit!==void 0&&(a.limit=o.limit),o.reverse===!0&&(a.reverse=!0)),a}},{key:"_methodCall",value:function(r,o){var a=this;return this.isConnected()?new Promise(function(p,R){a._call(r).then(function(M){p(o(a._decoder.decodeCommandResult(r.method,M.result))),M.next&&M.next()},function(M){R(M.error),M.next&&M.next()})}):Promise.reject(this._createErrorObject(oe,0))}},{key:"publish",value:function(r,o){var a={method:this._methodType.PUBLISH,params:{channel:r,data:o}};return this._methodCall(a,function(){return{}})}},{key:"history",value:function(r,o){var a=this._getHistoryParams(r,o),p={method:this._methodType.HISTORY,params:a};return this._methodCall(p,function(R){return{publications:R.publications,epoch:R.epoch||"",offset:R.offset||0}})}},{key:"presence",value:function(r){var o={method:this._methodType.PRESENCE,params:{channel:r}};return this._methodCall(o,function(a){return{presence:a.presence}})}},{key:"presenceStats",value:function(r){var o={method:this._methodType.PRESENCE_STATS,params:{channel:r}};return this._methodCall(o,function(a){return{num_users:a.num_users,num_clients:a.num_clients}})}},{key:"_dataReceived",value:function(r){var o=this,a=this._decoder.decodeReplies(r);this._dispatchPromise=this._dispatchPromise.then(function(){var p;o._dispatchPromise=new Promise(function(R){p=R}),o._dispatchSynchronized(a,p)}),this._restartPing()}},{key:"_dispatchSynchronized",value:function(r,o){var a=this,p=Promise.resolve(),R=function(L){r.hasOwnProperty(L)&&(p=p.then(function(){return a._dispatchReply(r[L])}))};for(var M in r)R(M);p=p.then(function(){o()})}},{key:"_dispatchReply",value:function(r){var o,a=new Promise(function(R){o=R});if(r==null)return this._debug("dispatch: got undefined or null reply"),o(),a;var p=r.id;return p&&p>0?this._handleReply(r,o):this._handlePush(r.result,o),a}},{key:"_call",value:function(r){var o=this;return new Promise(function(a,p){var R=o._addMessage(r);o._registerCall(R,a,p)})}},{key:"_connect",value:function(){if(this.isConnected()){this._debug("connect called when already connected");return}this._status!=="connecting"&&(this._debug("start connecting"),this._setStatus("connecting"),this._clientID=null,this._reconnect=!0,this._setupTransport())}},{key:"_disconnect",value:function(r,o){var a=o||!1;if(a===!1&&(this._reconnect=!1),this._isDisconnected()){a||this._clearConnectedState(a);return}if(this._clearConnectedState(a),this._debug("disconnected:",r,o),this._setStatus("disconnected"),this._refreshTimeout&&(clearTimeout(this._refreshTimeout),this._refreshTimeout=null),this._reconnecting===!1){for(var p in this._serverSubs)this._serverSubs.hasOwnProperty(p)&&this.emit("unsubscribe",{channel:p});this.emit("disconnect",{reason:r,reconnect:a})}a===!1&&(this._subs={},this._serverSubs={}),this._transportClosed||this._transport.close()}},{key:"_refreshFailed",value:function(){this._numRefreshFailed=0,this._isDisconnected()||this._disconnect("refresh failed",!1),this._config.onRefreshFailed!==null&&this._config.onRefreshFailed()}},{key:"_refresh",value:function(){var r=this;if(this._debug("refresh token"),this._config.refreshAttempts===0){this._debug("refresh attempts set to 0, do not send refresh request at all"),this._refreshFailed();return}this._refreshTimeout!==null&&(clearTimeout(this._refreshTimeout),this._refreshTimeout=null);var o=this._clientID,a=this._newXHRID(),p=function(L){if(a in r._xhrs&&delete r._xhrs[a],r._clientID===o){if(L.error||L.status!==200){if(L.error?r._debug("error refreshing connection token",L.error):r._debug("error refreshing connection token: wrong status code",L.status),r._numRefreshFailed++,r._refreshTimeout!==null&&(clearTimeout(r._refreshTimeout),r._refreshTimeout=null),r._config.refreshAttempts!==null&&r._numRefreshFailed>=r._config.refreshAttempts){r._refreshFailed();return}var X=Math.round(Math.random()*1e3*Math.max(r._numRefreshFailed,20)),ue=r._config.refreshInterval+X;r._refreshTimeout=setTimeout(function(){return r._refresh()},ue);return}if(r._numRefreshFailed=0,r._token=L.data.token,!r._token){r._refreshFailed();return}if(r._isDisconnected()&&r._reconnect)r._debug("token refreshed, connect from scratch"),r._connect();else{r._debug("send refreshed token");var ce={method:r._methodType.REFRESH,params:{token:r._token}};r._call(ce).then(function(V){r._refreshResponse(r._decoder.decodeCommandResult(r._methodType.REFRESH,V.result)),V.next&&V.next()},function(V){r._refreshError(V.error),V.next&&V.next()})}}};if(this._config.onRefresh!==null){var R={};this._config.onRefresh(R,p)}else{var M=this._ajax(this._config.refreshEndpoint,this._config.refreshParams,this._config.refreshHeaders,this._config.refreshData,p);this._xhrs[a]=M}}},{key:"_refreshError",value:function(r){var o=this;this._debug("refresh error",r),this._refreshTimeout&&(clearTimeout(this._refreshTimeout),this._refreshTimeout=null);var a=this._config.refreshInterval+Math.round(Math.random()*1e3);this._refreshTimeout=setTimeout(function(){return o._refresh()},a)}},{key:"_refreshResponse",value:function(r){var o=this;this._refreshTimeout&&(clearTimeout(this._refreshTimeout),this._refreshTimeout=null),r.expires&&(this._clientID=r.client,this._refreshTimeout=setTimeout(function(){return o._refresh()},this._getTTLMilliseconds(r.ttl)))}},{key:"_newXHRID",value:function(){return this._xhrID++,this._xhrID}},{key:"_subRefresh",value:function(r){var o=this;if(this._debug("refresh subscription token for channel",r),this._subRefreshTimeouts[r]!==void 0)this._clearSubRefreshTimeout(r);else return;var a=this._clientID,p=this._newXHRID(),R=function(X){if(p in o._xhrs&&delete o._xhrs[p],!(X.error||X.status!==200||o._clientID!==a)){var ue={};if(X.data.channels)for(var ce in X.data.channels){var V=X.data.channels[ce];!V.channel||(ue[V.channel]=V.token)}var xe=ue[r];if(!!xe){var nt={method:o._methodType.SUB_REFRESH,params:{channel:r,token:xe}},Pe=o._getSub(r);Pe!==null&&o._call(nt).then(function(ve){o._subRefreshResponse(r,o._decoder.decodeCommandResult(o._methodType.SUB_REFRESH,ve.result)),ve.next&&ve.next()},function(ve){o._subRefreshError(r,ve.error),ve.next&&ve.next()})}}},M={client:this._clientID,channels:[r]};if(this._config.onPrivateSubscribe!==null)this._config.onPrivateSubscribe({data:M},R);else{var D=this._ajax(this._config.subscribeEndpoint,this._config.subscribeParams,this._config.subscribeHeaders,M,R);this._xhrs[p]=D}}},{key:"_clearSubRefreshTimeout",value:function(r){this._subRefreshTimeouts[r]!==void 0&&(clearTimeout(this._subRefreshTimeouts[r]),delete this._subRefreshTimeouts[r])}},{key:"_subRefreshError",value:function(r,o){var a=this;this._debug("subscription refresh error",r,o),this._clearSubRefreshTimeout(r);var p=this._getSub(r);if(p!==null){var R=Math.round(Math.random()*1e3),M=setTimeout(function(){return a._subRefresh(r)},this._config.subRefreshInterval+R);this._subRefreshTimeouts[r]=M}}},{key:"_subRefreshResponse",value:function(r,o){var a=this;this._debug("subscription refresh success",r),this._clearSubRefreshTimeout(r);var p=this._getSub(r);if(p!==null&&o.expires===!0){var R=setTimeout(function(){return a._subRefresh(r)},this._getTTLMilliseconds(o.ttl));this._subRefreshTimeouts[r]=R}}},{key:"_subscribe",value:function(r,o){var a=this;this._debug("subscribing on",r.channel);var p=r.channel;if(p in this._subs||(this._subs[p]=r),!this.isConnected()){r._setNew();return}r._setSubscribing(o);var R={method:this._methodType.SUBSCRIBE,params:{channel:p}};if(r._subscribeData&&(R.params.data=r._subscribeData),(0,T.startsWith)(p,this._config.privateChannelPrefix))this._isSubscribeBatching?this._privateChannels[p]=!0:(this.startSubscribeBatching(),this._subscribe(r),this.stopSubscribeBatching());else{var M=r._needRecover();if(M===!0){R.params.recover=!0;var D=this._getLastSeq(p),L=this._getLastGen(p);if(D||L)D&&(R.params.seq=D),L&&(R.params.gen=L);else{var X=this._getLastOffset(p);X&&(R.params.offset=X)}var ue=this._getLastEpoch(p);ue&&(R.params.epoch=ue)}this._call(R).then(function(ce){a._subscribeResponse(p,M,a._decoder.decodeCommandResult(a._methodType.SUBSCRIBE,ce.result)),ce.next&&ce.next()},function(ce){a._subscribeError(p,ce.error),ce.next&&ce.next()})}}},{key:"_unsubscribe",value:function(r){delete this._subs[r.channel],delete this._lastOffset[r.channel],delete this._lastSeq[r.channel],delete this._lastGen[r.channel],this.isConnected()&&this._addMessage({method:this._methodType.UNSUBSCRIBE,params:{channel:r.channel}})}},{key:"_getTTLMilliseconds",value:function(r){return Math.min(r*1e3,2147483647)}},{key:"getSub",value:function(r){return this._getSub(r)}},{key:"_getSub",value:function(r){var o=this._subs[r];return o||null}},{key:"_isServerSub",value:function(r){return this._serverSubs[r]!==void 0}},{key:"_connectResponse",value:function(r,o){var a=this,p=this._reconnecting;if(this._reconnecting=!1,this._resetRetry(),this._refreshRequired=!1,!this.isConnected()){this._latencyStart!==null&&(this._latency=new Date().getTime()-this._latencyStart.getTime(),this._latencyStart=null),this._clientID=r.client,this._setStatus("connected"),this._refreshTimeout&&clearTimeout(this._refreshTimeout),r.expires&&(this._refreshTimeout=setTimeout(function(){return a._refresh()},this._getTTLMilliseconds(r.ttl))),this.startBatching(),this.startSubscribeBatching();for(var R in this._subs)if(this._subs.hasOwnProperty(R)){var M=this._subs[R];M._shouldResubscribe()&&this._subscribe(M,p)}this.stopSubscribeBatching(),this.stopBatching(),this._startPing();var D={client:r.client,transport:this._transportName,latency:this._latency};r.data&&(D.data=r.data),this.emit("connect",D),r.subs&&this._processServerSubs(r.subs)}}},{key:"_processServerSubs",value:function(r){for(var o in r)if(r.hasOwnProperty(o)){var a=r[o],p=this._serverSubs[o]!==void 0,R={channel:o,isResubscribe:p};R=this._expandSubscribeContext(R,a),this.emit("subscribe",R)}for(var M in r)if(r.hasOwnProperty(M)){var D=r[M];if(D.recovered){var L=D.publications;if(L&&L.length>0){L.length>1&&(!L[0].offset||L[0].offset>L[1].offset)&&(L=L.reverse());for(var X in L)L.hasOwnProperty(X)&&this._handlePublication(M,L[X])}}this._serverSubs[M]={seq:D.seq,gen:D.gen,offset:D.offset,epoch:D.epoch,recoverable:D.recoverable}}}},{key:"_stopPing",value:function(){this._pongTimeout!==null&&(clearTimeout(this._pongTimeout),this._pongTimeout=null),this._pingTimeout!==null&&(clearTimeout(this._pingTimeout),this._pingTimeout=null)}},{key:"_startPing",value:function(){var r=this;this._config.ping!==!0||this._config.pingInterval<=0||!this.isConnected()||(this._pingTimeout=setTimeout(function(){if(!r.isConnected()){r._stopPing();return}r.ping(),r._pongTimeout=setTimeout(function(){r._disconnect("no ping",!0)},r._config.pongWaitTimeout)},this._config.pingInterval))}},{key:"_restartPing",value:function(){this._stopPing(),this._startPing()}},{key:"_subscribeError",value:function(r,o){var a=this._getSub(r);if(!!a&&!!a._isSubscribing()){if(o.code===0&&o.message===ie){this._disconnect("timeout",!0);return}a._setSubscribeError(o)}}},{key:"_expandSubscribeContext",value:function(r,o){var a=!1;"recovered"in o&&(a=o.recovered),r.recovered=a;var p=!1;"positioned"in o&&(p=o.positioned);var R="";"epoch"in o&&(R=o.epoch);var M=0;return"offset"in o&&(M=o.offset),p&&(r.streamPosition={offset:M,epoch:R}),o.data&&(r.data=o.data),r}},{key:"_subscribeResponse",value:function(r,o,a){var p=this,R=this._getSub(r);if(!!R&&!!R._isSubscribing()){R._setSubscribeSuccess(a);var M=a.publications;if(M&&M.length>0){M.length>=2&&!M[0].offset&&!M[1].offset&&(M=M.reverse());for(var D in M)M.hasOwnProperty(D)&&this._handlePublication(r,M[D])}if(a.recoverable&&(!o||!a.recovered)&&(this._lastSeq[r]=a.seq||0,this._lastGen[r]=a.gen||0,this._lastOffset[r]=a.offset||0),this._lastEpoch[r]=a.epoch||"",a.recoverable&&(R._recoverable=!0),a.expires===!0){var L=setTimeout(function(){return p._subRefresh(r)},this._getTTLMilliseconds(a.ttl));this._subRefreshTimeouts[r]=L}}}},{key:"_handleReply",value:function(r,o){var a=r.id,p=r.result;if(!(a in this._callbacks)){o();return}var R=this._callbacks[a];if(clearTimeout(this._callbacks[a].timeout),delete this._callbacks[a],(0,T.errorExists)(r)){var D=R.errback;if(!D){o();return}var L=r.error;D({error:L,next:o})}else{var M=R.callback;if(!M)return;M({result:p,next:o})}}},{key:"_handleJoin",value:function(r,o){var a={info:o.info},p=this._getSub(r);if(!p){this._isServerSub(r)&&(a.channel=r,this.emit("join",a));return}p.emit("join",a)}},{key:"_handleLeave",value:function(r,o){var a={info:o.info},p=this._getSub(r);if(!p){this._isServerSub(r)&&(a.channel=r,this.emit("leave",a));return}p.emit("leave",a)}},{key:"_handleUnsub",value:function(r,o){var a={},p=this._getSub(r);if(!p){this._isServerSub(r)&&(delete this._serverSubs[r],a.channel=r,this.emit("unsubscribe",a));return}p.unsubscribe(),o.resubscribe===!0&&p.subscribe()}},{key:"_handleSub",value:function(r,o){this._serverSubs[r]={seq:o.seq,gen:o.gen,offset:o.offset,epoch:o.epoch,recoverable:o.recoverable};var a={channel:r,isResubscribe:!1};a=this._expandSubscribeContext(a,o),this.emit("subscribe",a)}},{key:"_handlePublication",value:function(r,o){var a=this._getSub(r),p={data:o.data,seq:o.seq,gen:o.gen,offset:o.offset};if(o.info&&(p.info=o.info),!a){this._isServerSub(r)&&(o.seq!==void 0&&(this._serverSubs[r].seq=o.seq),o.gen!==void 0&&(this._serverSubs[r].gen=o.gen),o.offset!==void 0&&(this._serverSubs[r].offset=o.offset),p.channel=r,this.emit("publish",p));return}o.seq!==void 0&&(this._lastSeq[r]=o.seq),o.gen!==void 0&&(this._lastGen[r]=o.gen),o.offset!==void 0&&(this._lastOffset[r]=o.offset),a.emit("publish",p)}},{key:"_handleMessage",value:function(r){this.emit("message",r.data)}},{key:"_handlePush",value:function(r,o){var a=this._decoder.decodePush(r),p=0;"type"in a&&(p=a.type);var R=a.channel;if(p===this._pushType.PUBLICATION){var M=this._decoder.decodePushData(this._pushType.PUBLICATION,a.data);this._handlePublication(R,M)}else if(p===this._pushType.MESSAGE){var D=this._decoder.decodePushData(this._pushType.MESSAGE,a.data);this._handleMessage(D)}else if(p===this._pushType.JOIN){var L=this._decoder.decodePushData(this._pushType.JOIN,a.data);this._handleJoin(R,L)}else if(p===this._pushType.LEAVE){var X=this._decoder.decodePushData(this._pushType.LEAVE,a.data);this._handleLeave(R,X)}else if(p===this._pushType.UNSUBSCRIBE){var ue=this._decoder.decodePushData(this._pushType.UNSUBSCRIBE,a.data);this._handleUnsub(R,ue)}else if(p===this._pushType.SUBSCRIBE){var ce=this._decoder.decodePushData(this._pushType.UNSUBSCRIBE,a.data);this._handleSub(R,ce)}o()}},{key:"_flush",value:function(){var r=this._messages.slice(0);this._messages=[],this._transportSend(r)}},{key:"_ping",value:function(){var r=this,o={method:this._methodType.PING};this._call(o).then(function(a){r._pingResponse(r._decoder.decodeCommandResult(r._methodType.PING,a.result)),a.next&&a.next()},function(a){r._debug("ping error",a.error),a.next&&a.next()})}},{key:"_pingResponse",value:function(r){!this.isConnected()||(this._stopPing(),this._startPing())}},{key:"_getLastSeq",value:function(r){var o=this._lastSeq[r];return o||0}},{key:"_getLastOffset",value:function(r){var o=this._lastOffset[r];return o||0}},{key:"_getLastGen",value:function(r){var o=this._lastGen[r];return o||0}},{key:"_getLastEpoch",value:function(r){var o=this._lastEpoch[r];return o||""}},{key:"_createErrorObject",value:function(r,o){var a={message:r,code:o||0};return a}},{key:"_registerCall",value:function(r,o,a){var p=this;this._callbacks[r]={callback:o,errback:a,timeout:null},this._callbacks[r].timeout=setTimeout(function(){delete p._callbacks[r],(0,T.isFunction)(a)&&a({error:p._createErrorObject(ie)})},this._config.timeout)}},{key:"_addMessage",value:function(r){var o=this._nextMessageId();return r.id=o,this._isBatching===!0?this._messages.push(r):this._transportSend([r]),o}},{key:"isConnected",value:function(){return this._isConnected()}},{key:"connect",value:function(){this._connect()}},{key:"disconnect",value:function(){this._disconnect("client",!1)}},{key:"ping",value:function(){return this._ping()}},{key:"startBatching",value:function(){this._isBatching=!0}},{key:"stopBatching",value:function(){this._isBatching=!1,this._flush()}},{key:"startSubscribeBatching",value:function(){this._isSubscribeBatching=!0}},{key:"stopSubscribeBatching",value:function(){var r=this;this._isSubscribeBatching=!1;var o=this._privateChannels;this._privateChannels={};var a=[];for(var p in o)if(o.hasOwnProperty(p)){var R=this._getSub(p);if(!R)continue;a.push(p)}if(a.length===0){this._debug("no private channels found, no need to make request");return}var M={client:this._clientID,channels:a},D=this._clientID,L=this._newXHRID(),X=function(V){if(L in r._xhrs&&delete r._xhrs[L],r._clientID===D){if(V.error||V.status!==200){r._debug("authorization request failed");for(var xe in a)if(a.hasOwnProperty(xe)){var nt=a[xe];r._subscribeError(nt,r._createErrorObject("authorization request failed"))}return}var Pe={};if(V.data.channels)for(var ve in V.data.channels){var ze=V.data.channels[ve];!ze.channel||(Pe[ze.channel]=ze.token)}var f=!1;r._isBatching||(r.startBatching(),f=!0);for(var d in a)if(a.hasOwnProperty(d)){var v=function(){var y=a[d],w=Pe[y];if(w){var O={method:r._methodType.SUBSCRIBE,params:{channel:y,token:w}},N=r._getSub(y);if(N===null)return"continue";var k=N._needRecover();if(k===!0){O.params.recover=!0;var C=r._getLastSeq(y),x=r._getLastGen(y);if(C||x)C&&(O.params.seq=C),x&&(O.params.gen=x);else{var $=r._getLastOffset(y);$&&(O.params.offset=$)}var A=r._getLastEpoch(y);A&&(O.params.epoch=A)}r._call(O).then(function(H){r._subscribeResponse(y,k,r._decoder.decodeCommandResult(r._methodType.SUBSCRIBE,H.result)),H.next&&H.next()},function(H){r._subscribeError(y,H.error),H.next&&H.next()})}else return r._subscribeError(y,r._createErrorObject("permission denied",103)),"continue"}();if(v==="continue")continue}f&&r.stopBatching()}};if(this._config.onPrivateSubscribe!==null)this._config.onPrivateSubscribe({data:M},X);else{var ue=this._ajax(this._config.subscribeEndpoint,this._config.subscribeParams,this._config.subscribeHeaders,M,X);this._xhrs[L]=ue}}},{key:"_setSubscribeSince",value:function(r,o){this._lastOffset[r.channel]=o.offset,this._lastEpoch[r.channel]=o.epoch,r._setNeedRecover(!0)}},{key:"subscribe",value:function(r,o,a){var p=this._getSub(r);if(p!==null)return p._setEvents(o),p._isUnsubscribed()&&p.subscribe(a),p;var R=new E.default(this,r,o);return this._subs[r]=R,R.subscribe(a),R}}]),S}(g.default);c.Centrifuge=P},579:function(l,c,h){Object.defineProperty(c,"__esModule",{value:!0}),c.default=void 0;var g=h(382),E=g.Centrifuge;c.default=E,l.exports=c.default},147:function(l,c){Object.defineProperty(c,"__esModule",{value:!0}),c.JsonPushType=c.JsonMethodType=c.JsonEncoder=c.JsonDecoder=void 0;function h(F,j){if(!(F instanceof j))throw new TypeError("Cannot call a class as a function")}function g(F,j){for(var U=0;U0&&(o=b[0]),o instanceof Error)throw o;var a=new Error("Unhandled error."+(o?" ("+o.message+")":""));throw a.context=o,a}var p=r[m];if(p===void 0)return!1;if(typeof p=="function")h(p,this,b);else for(var R=p.length,M=me(p,R),S=0;S0&&o.length>_&&!o.warned){o.warned=!0;var a=new Error("Possible EventEmitter memory leak detected. "+o.length+" "+String(m)+" listeners added. Use emitter.setMaxListeners() to increase limit");a.name="MaxListenersExceededWarning",a.emitter=P,a.type=m,a.count=o.length,E(a)}return P}T.prototype.addListener=function(m,b){return j(this,m,b,!1)},T.prototype.on=T.prototype.addListener,T.prototype.prependListener=function(m,b){return j(this,m,b,!0)};function U(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,arguments.length===0?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function J(P,m,b){var S={fired:!1,wrapFn:void 0,target:P,type:m,listener:b},_=U.bind(S);return _.listener=b,S.wrapFn=_,_}T.prototype.once=function(m,b){return q(b),this.on(m,J(this,m,b)),this},T.prototype.prependOnceListener=function(m,b){return q(b),this.prependListener(m,J(this,m,b)),this},T.prototype.removeListener=function(m,b){var S,_,r,o,a;if(q(b),_=this._events,_===void 0)return this;if(S=_[m],S===void 0)return this;if(S===b||S.listener===b)--this._eventsCount===0?this._events=Object.create(null):(delete _[m],_.removeListener&&this.emit("removeListener",m,S.listener||b));else if(typeof S!="function"){for(r=-1,o=S.length-1;o>=0;o--)if(S[o]===b||S[o].listener===b){a=S[o].listener,r=o;break}if(r<0)return this;r===0?S.shift():Te(S,r),S.length===1&&(_[m]=S[0]),_.removeListener!==void 0&&this.emit("removeListener",m,a||b)}return this},T.prototype.off=T.prototype.removeListener,T.prototype.removeAllListeners=function(m){var b,S,_;if(S=this._events,S===void 0)return this;if(S.removeListener===void 0)return arguments.length===0?(this._events=Object.create(null),this._eventsCount=0):S[m]!==void 0&&(--this._eventsCount===0?this._events=Object.create(null):delete S[m]),this;if(arguments.length===0){var r=Object.keys(S),o;for(_=0;_=0;_--)this.removeListener(m,b[_]);return this};function Q(P,m,b){var S=P._events;if(S===void 0)return[];var _=S[m];return _===void 0?[]:typeof _=="function"?b?[_.listener||_]:[_]:b?Re(_):me(_,_.length)}T.prototype.listeners=function(m){return Q(this,m,!0)},T.prototype.rawListeners=function(m){return Q(this,m,!1)},T.listenerCount=function(P,m){return typeof P.listenerCount=="function"?P.listenerCount(m):se.call(P,m)},T.prototype.listenerCount=se;function se(P){var m=this._events;if(m!==void 0){var b=m[P];if(typeof b=="function")return 1;if(b!==void 0)return b.length}return 0}T.prototype.eventNames=function(){return this._eventsCount>0?g(this._events):[]};function me(P,m){for(var b=new Array(m),S=0;Sconsole.log(JSON.stringify(t,null," ")));this.backendUri=t,this.websocketsUri=n,this.verbose=s,this.client=new tu(`${this.websocketsUri}/connection/websocket`,{subscribeEndpoint:`${this.backendUri}/instant/subscribe/`,debug:s})}set onMessage(t){this._onMessage=t}async connect(t=!0){t&&this._subscribeToAllChannels();let n;const s=new Promise(i=>{n=i});this.client.on("connect",function(){n(!0)}),this.client.connect(),await s}_subscribeToAllChannels(){this.channels.forEach(t=>{this.client.subscribe(t.name,n=>{this.verbose&&console.log(n);const s=this._process_raw_message(JSON.parse(n.data));this._onMessage(s)})})}async login(t,n){console.log("Login");const s={username:t,password:n},i=this.postHeader(s),u=this.backendUri+"/instant/login/",l=await fetch(u,i);if(!l.ok)throw console.log("Response not ok",l),new Error(l.statusText);const c=await l.json();this._process_response(c)}async get_token(){const t=this.postHeader({}),n=this.backendUri+"/instant/get_token/",s=await fetch(n,t);if(!s.ok)throw console.log("Response not ok",s),new Error(s.statusText);const i=await s.json();this._process_response(i)}postHeader(t){const n={method:"post",credentials:"include",mode:"cors",body:JSON.stringify(t)};return n.headers={"Content-Type":"application/json"},this.csrfToken!==""&&(n.headers={"Content-Type":"application/json","X-CSRFToken":this.csrfToken}),n}_process_response(t){console.log("Tokens",JSON.stringify(t,null," ")),this.csrfToken=t.csrf_token,this.client.setToken(t.ws_token),t.channels.forEach(n=>{this.channels.add(n)})}_process_raw_message(t){let n;try{n=new nu(t)}catch(s){throw new Error(`Can not process message ${t} ${s}`)}return n}}var ru=Object.defineProperty,su=(e,t,n)=>t in e?ru(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,sn=(e,t,n)=>(su(e,typeof t!="symbol"?t+"":t,n),n);function Es(e){return $s()?(Js(e),!0):!1}const iu=typeof window!="undefined",ou=e=>typeof e=="string",on=()=>{};function uu(e,t){function n(...s){e(()=>t.apply(this,s),{fn:t,thisArg:this,args:s})}return n}const cu=e=>e();function lu(e,t){var n;if(typeof e=="number")return e+t;const s=((n=e.match(/^-?[0-9]+\.?[0-9]*/))==null?void 0:n[0])||"",i=e.slice(s.length),u=parseFloat(s)+t;return Number.isNaN(u)?e:u+i}var vr=Object.getOwnPropertySymbols,fu=Object.prototype.hasOwnProperty,au=Object.prototype.propertyIsEnumerable,hu=(e,t)=>{var n={};for(var s in e)fu.call(e,s)&&t.indexOf(s)<0&&(n[s]=e[s]);if(e!=null&&vr)for(var s of vr(e))t.indexOf(s)<0&&au.call(e,s)&&(n[s]=e[s]);return n};function du(e,t,n={}){const s=n,{eventFilter:i=cu}=s,u=hu(s,["eventFilter"]);return St(e,uu(i,t),u)}const kt=iu?window:void 0;function _u(...e){let t,n,s,i;if(ou(e[0])?([n,s,i]=e,t=kt):[t,n,s,i]=e,!t)return on;let u=on;const l=St(()=>Dn(t),h=>{u(),!!h&&(h.addEventListener(n,s,i),u=()=>{h.removeEventListener(n,s,i),u=on})},{immediate:!0,flush:"post"}),c=()=>{l(),u()};return Es(c),c}function un(e,t={}){const{window:n=kt}=t;if(!n)return At(!1);const s=n.matchMedia(e),i=At(s.matches),u=l=>{i.value=l.matches};return"addEventListener"in s?s.addEventListener("change",u):s.addListener(u),Es(()=>{"removeEventListener"in s?s.removeEventListener("change",u):s.removeListener(u)}),i}const pu={sm:640,md:768,lg:1024,xl:1280,"2xl":1536};var bu=Object.defineProperty,yr=Object.getOwnPropertySymbols,gu=Object.prototype.hasOwnProperty,mu=Object.prototype.propertyIsEnumerable,Sr=(e,t,n)=>t in e?bu(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,vu=(e,t)=>{for(var n in t||(t={}))gu.call(t,n)&&Sr(e,n,t[n]);if(yr)for(var n of yr(t))mu.call(t,n)&&Sr(e,n,t[n]);return e};function yu(e,t={}){function n(c,h){let g=e[c];return h!=null&&(g=lu(g,h)),typeof g=="number"&&(g=`${g}px`),g}const{window:s=kt}=t;function i(c){return s?s.matchMedia(c).matches:!1}const u=c=>un(`(min-width: ${n(c)})`,t),l=Object.keys(e).reduce((c,h)=>(Object.defineProperty(c,h,{get:()=>u(h),enumerable:!0,configurable:!0}),c),{});return vu({greater:u,smaller(c){return un(`(max-width: ${n(c,-.1)})`,t)},between(c,h){return un(`(min-width: ${n(c)}) and (max-width: ${n(h,-.1)})`,t)},isGreater(c){return i(`(min-width: ${n(c)})`)},isSmaller(c){return i(`(max-width: ${n(c,-.1)})`)},isInBetween(c,h){return i(`(min-width: ${n(c)}) and (max-width: ${n(h,-.1)})`)}},l)}const Su={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))}};function wr(e,t,n=(i=>(i=kt)==null?void 0:i.localStorage)(),s={}){var i;const{flush:u="pre",deep:l=!0,listenToStorageChanges:c=!0,writeDefaults:h=!0,shallow:g,window:E=kt,eventFilter:I,onError:T=J=>{console.error(J)}}=s,B=Dn(t),q=B==null?"any":B instanceof Set?"set":B instanceof Map?"map":typeof B=="boolean"?"boolean":typeof B=="string"?"string":typeof B=="object"||Array.isArray(B)?"object":Number.isNaN(B)?"any":"number",F=(g?bi:At)(t),j=(i=s.serializer)!=null?i:Su[q];function U(J){if(!(!n||J&&J.key!==e))try{const Q=J?J.newValue:n.getItem(e);Q==null?(F.value=B,h&&B!==null&&n.setItem(e,j.write(B))):F.value=j.read(Q)}catch(Q){T(Q)}}return U(),E&&c&&_u(E,"storage",J=>setTimeout(()=>U(J),0)),n&&du(F,()=>{try{F.value==null?n.removeItem(e):n.setItem(e,j.write(F.value))}catch(J){T(J)}},{flush:u,deep:l,eventFilter:I}),F}var Tr;(function(e){e.UP="UP",e.RIGHT="RIGHT",e.DOWN="DOWN",e.LEFT="LEFT",e.NONE="NONE"})(Tr||(Tr={}));class Cu{constructor(){sn(this,"name",wr("name","anonymous")),sn(this,"isDarkMode",wr("isDarkMode",!1)),sn(this,"isLoggedIn",At(!1)),this.checkDarkMode()}toggleDarkMode(){this.isDarkMode.value=!this.isDarkMode.value,this.checkDarkMode()}checkDarkMode(){this.isDarkMode.value===!0?document.body.classList.add("bg-background-dark","text-foreground-dark"):document.body.classList.remove("bg-background-dark","text-foreground-dark")}}yu(pu);export{je as F,ku as I,Cu as U,vs as a,Co as b,Ru as c,Ln as d,Eu as e,vo as f,xu as g,Pu as h,Ae as i,Hi as j,Ou as k,En as n,ho as o,At as r,Tu as t,Dn as u}; 4 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pytest fixtures 3 | """ 4 | import os 5 | import pytest 6 | 7 | import livefeed 8 | 9 | 10 | class FixturesSettingsTestMixin(object): 11 | """ 12 | A mixin containing settings about application. This is almost about useful 13 | paths which may be used in tests. 14 | 15 | Attributes: 16 | application_path (str): Absolute path to the application directory. 17 | package_path (str): Absolute path to the package directory. 18 | tests_dir (str): Directory name which include tests. 19 | tests_path (str): Absolute path to the tests directory. 20 | fixtures_dir (str): Directory name which include tests datas. 21 | fixtures_path (str): Absolute path to the tests datas. 22 | """ 23 | def __init__(self): 24 | # Base fixture datas directory 25 | self.application_path = os.path.abspath( 26 | os.path.dirname(livefeed.__file__) 27 | ) 28 | self.package_path = os.path.normpath( 29 | os.path.join( 30 | os.path.abspath( 31 | os.path.dirname(livefeed.__file__) 32 | ), 33 | "..", 34 | ) 35 | ) 36 | 37 | self.tests_dir = "tests" 38 | self.tests_path = os.path.join( 39 | self.package_path, 40 | self.tests_dir, 41 | ) 42 | 43 | self.fixtures_dir = "data_fixtures" 44 | self.fixtures_path = os.path.join( 45 | self.tests_path, 46 | self.fixtures_dir 47 | ) 48 | 49 | def format(self, content): 50 | """ 51 | Format given string to include some values related to this application. 52 | 53 | Arguments: 54 | content (str): Content string to format with possible values. 55 | 56 | Returns: 57 | str: Given string formatted with possible values. 58 | """ 59 | return content.format( 60 | HOMEDIR=os.path.expanduser("~"), 61 | PACKAGE=self.package_path, 62 | APPLICATION=self.application_path, 63 | TESTS=self.tests_path, 64 | FIXTURES=self.fixtures_path, 65 | #VERSION=livefeed.__version__, 66 | USER_AGENT=livefeed.USER_AGENT, 67 | ) 68 | 69 | 70 | @pytest.fixture(scope="session") 71 | def temp_builds_dir(tmpdir_factory): 72 | """ 73 | Shortcut to prepare a temporary build directory where to create temporary 74 | content from tests. 75 | """ 76 | fn = tmpdir_factory.mktemp("builds") 77 | return fn 78 | 79 | 80 | @pytest.fixture(scope="module") 81 | def tests_settings(): 82 | """ 83 | Initialize and return settings for tests. 84 | 85 | Example: 86 | You may use it in tests like this: :: 87 | 88 | def test_foo(tests_settings): 89 | print(tests_settings.package_path) 90 | print(tests_settings.format("foo: {VERSION}")) 91 | """ 92 | return FixturesSettingsTestMixin() 93 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: E203 2 | """ 3 | ============== 4 | Test utilities 5 | ============== 6 | 7 | """ 8 | from django.contrib.sites.models import Site 9 | from django.template.response import TemplateResponse 10 | from django.test.html import parse_html 11 | from django.urls import reverse 12 | 13 | from pyquery import PyQuery as pq 14 | 15 | 16 | # A dummy password that should pass form validation 17 | VALID_PASSWORD_SAMPLE = "Azerty12345678" 18 | 19 | # This is the common dummy URL which Django REST Framework will use when it does not 20 | # have any request when it resolve URL to absolute (like from Hyperlinked classes) 21 | DRF_DUMMY_HOST_URL = "http://testserver" 22 | 23 | 24 | # A dummy blank GIF file in byte value to simulate an uploaded file like with 25 | # 'django.core.files.uploadedfile.SimpleUploadedFile' 26 | DUMMY_GIF_BYTES = ( 27 | b"\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04" 28 | b"\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02" 29 | b"\x02\x4c\x01\x00\x3b" 30 | ) 31 | 32 | 33 | def get_website_url(site_settings=None): 34 | """ 35 | A shortand to retrieve the full website URL according to Site ID and HTTP 36 | protocole settings. 37 | 38 | Keyword Arguments: 39 | site_settings (django.conf.settings): Settings object, if not given the method 40 | will be unable to determine if HTTPS is enabled or not, so it will always 41 | return a HTTP URL. 42 | 43 | Returns: 44 | string: Full website URL. 45 | """ 46 | domain = Site.objects.get_current().domain 47 | 48 | protocol = "http://" 49 | if site_settings and site_settings.HTTPS_ENABLED: 50 | protocol = "https://" 51 | 52 | return "{}{}".format(protocol, domain) 53 | 54 | 55 | def get_relative_path(site_url, url): 56 | """ 57 | From given URL, retrieve the relative path (URL without domain and starting 58 | slash). 59 | 60 | Arguments: 61 | site_url (string): Website URL to remove from given ``url`` 62 | argument. 63 | url (string): Full URL (starting with http/https) to make relative to 64 | website URL. 65 | 66 | Returns: 67 | string: Admin change view URL path for given model object. 68 | """ 69 | if url.startswith(site_url): 70 | return url[len(site_url) :] 71 | 72 | return url 73 | 74 | 75 | def get_admin_add_url(model): 76 | """ 77 | Return the right admin URL for add form view for given class. 78 | 79 | Arguments: 80 | model (Model object): A model object to use to find its admin 81 | add form view URL. 82 | 83 | Returns: 84 | string: Admin add form view URL path. 85 | """ 86 | url_pattern = "admin:{app}_{model}_add" 87 | 88 | return reverse( 89 | url_pattern.format(app=model._meta.app_label, model=model._meta.model_name) 90 | ) 91 | 92 | 93 | def get_admin_change_url(obj): 94 | """ 95 | Return the right admin URL for a change view for given object. 96 | 97 | Arguments: 98 | obj (Model object): A model object instance to use to find its admin 99 | change view URL. 100 | 101 | Returns: 102 | string: Admin change view URL path. 103 | """ 104 | url_pattern = "admin:{app}_{model}_change" 105 | 106 | return reverse( 107 | url_pattern.format(app=obj._meta.app_label, model=obj._meta.model_name), 108 | args=[obj.pk], 109 | ) 110 | 111 | 112 | def get_admin_list_url(model): 113 | """ 114 | Return the right admin URL for a list view for given class. 115 | 116 | Arguments: 117 | model (Model object): A model object to use to find its admin 118 | list view URL. 119 | 120 | Returns: 121 | string: Admin list view URL path. 122 | """ 123 | url_pattern = "admin:{app}_{model}_changelist" 124 | 125 | return reverse( 126 | url_pattern.format(app=model._meta.app_label, model=model._meta.model_name) 127 | ) 128 | 129 | 130 | def decode_response_or_string(content): 131 | """ 132 | Shortand to get HTML string from either a TemplateResponse (as returned 133 | from Django test client) or a simple string so you can blindly give a 134 | response or a string without to care about content type. 135 | 136 | Arguments: 137 | content (TemplateResponse or string): If content is a string it will 138 | just return it. If content is a TemplateResponse it will decode byte 139 | string from its ``content`` attribute. 140 | 141 | Returns: 142 | string: HTML string. 143 | """ 144 | if isinstance(content, TemplateResponse): 145 | return content.content.decode() 146 | return content 147 | 148 | 149 | def html_element(content): 150 | """ 151 | Shortand to use Django HTML parsing on given content. 152 | 153 | This is more useful for comparaison on HTML parts. 154 | 155 | Arguments: 156 | content (TemplateResponse or string): HTML content to parse. 157 | 158 | Returns: 159 | django.test.html.Element: A Python object structure able to perform 160 | comparaison on a semantical way. See ``django.test.html.parse_html`` for 161 | more details. 162 | """ 163 | return parse_html(decode_response_or_string(content)) 164 | 165 | 166 | def html_pyquery(content): 167 | """ 168 | Shortand to use Pyquery parsing on given content. 169 | 170 | This is more useful to dig in advanced HTML content. PyQuery is basically a 171 | wrapper around ``lxml.etree`` it helps with a more intuitive API (alike 172 | Jquery) to traverse elements but when reaching a node content it will 173 | return ``lxml.html.HtmlElement`` object which have a less intuitive API. 174 | 175 | Arguments: 176 | content (TemplateResponse or string): HTML content to parse. 177 | 178 | Returns: 179 | pyquery.PyQuery: A PyQuery object. 180 | """ 181 | return pq(decode_response_or_string(content), parser="html") 182 | 183 | 184 | def queryset_values(queryset, names=["slug", "language"], orders=["slug", "language"]): 185 | """ 186 | An helper to just return a list of dict values ordered from given queryset. 187 | 188 | Arguments: 189 | queryset (Queryset): A queryset to turn to values. 190 | 191 | Keyword Arguments: 192 | names (list): A list of field names to return as values for each object. 193 | Default return "slug" and "language" values only. 194 | orders (list): A list of field names to order results. 195 | Default order first on "slug" then "language". 196 | 197 | Returns: 198 | list: A list of dict items for all result objects. 199 | """ 200 | return list(queryset.values(*names).order_by(*orders)) 201 | 202 | 203 | def compact_form_errors(form): 204 | """ 205 | Build a compact dict of field errors without messages. 206 | 207 | This is a helper for errors, keeping it more easy to test since messages 208 | may be too long and can be translated which is more difficult to test. 209 | 210 | Arguments: 211 | form (django.forms.Form): A bounded form. 212 | 213 | Returns: 214 | dict: A dict of invalid fields, each item is indexed by field name and 215 | value is a list of error codes. 216 | """ 217 | errors = {} 218 | 219 | for name, validationerror in form.errors.as_data().items(): 220 | errors[name] = [item.code for item in validationerror] 221 | 222 | return errors 223 | 224 | 225 | def build_post_data_from_object(model, obj, ignore=["id"]): 226 | """ 227 | Build a payload suitable to a POST request from given object data. 228 | 229 | Arguments: 230 | model (django.db.models.Model): A model object used to find object 231 | attributes to extract values. 232 | obj (object): A instance of given model or a dict (like the one returned 233 | by a factory ``build()`` method. 234 | ignore (list): List of field name to ignore for value 235 | extraction. Default to "id" but will not be enough for any field 236 | with foreign keys, automatic primary keys, etc.. 237 | 238 | Returns: 239 | dict: Payload data to use in POST request. 240 | """ 241 | data = {} 242 | 243 | fields = [f.name for f in model._meta.get_fields() if f.name not in ignore] 244 | 245 | for name in fields: 246 | if obj is dict: 247 | data[name] = obj.get(name) 248 | else: 249 | data[name] = getattr(obj, name) 250 | 251 | return data 252 | -------------------------------------------------------------------------------- /theme/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synw/django-mqueue-livefeed/cebbca2a0918b2ffdf14a4fa7049a5d4780d900d/theme/__init__.py -------------------------------------------------------------------------------- /theme/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ThemeConfig(AppConfig): 5 | name = 'theme' 6 | -------------------------------------------------------------------------------- /theme/static_src/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /theme/static_src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "theme", 3 | "version": "3.1.1", 4 | "description": "", 5 | "scripts": { 6 | "start": "npm run dev", 7 | "build": "npm run build:clean && npm run build:tailwind", 8 | "build:clean": "rimraf ../static/css/dist", 9 | "build:tailwind": "cross-env NODE_ENV=production tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css --minify", 10 | "dev": "cross-env NODE_ENV=development tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css -w", 11 | "tailwindcss": "node ./node_modules/tailwindcss/lib/cli.js" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@tailwindcss/aspect-ratio": "^0.4.0", 18 | "@tailwindcss/forms": "^0.4.0", 19 | "@tailwindcss/line-clamp": "^0.3.1", 20 | "@tailwindcss/typography": "^0.5.0", 21 | "cross-env": "^7.0.3", 22 | "postcss": "^8.4.5", 23 | "postcss-import": "^14.0.2", 24 | "postcss-nested": "^5.0.6", 25 | "postcss-simple-vars": "^6.0.3", 26 | "rimraf": "^3.0.2", 27 | "tailwindcss": "^3.0.13" 28 | } 29 | } -------------------------------------------------------------------------------- /theme/static_src/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "postcss-import": {}, 4 | "postcss-simple-vars": {}, 5 | "postcss-nested": {} 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /theme/static_src/src/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | #loginform > p { 6 | @apply flex w-96 space-y-3 7 | } 8 | #loginform > p > label { 9 | @apply flex-grow 10 | } 11 | #loginform input[type=submit] { 12 | @apply bg-slate-500 text-white p-2 rounded-sm 13 | } 14 | -------------------------------------------------------------------------------- /theme/static_src/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a minimal config. 3 | * 4 | * If you need the full config, get it from here: 5 | * https://unpkg.com/browse/tailwindcss@latest/stubs/defaultConfig.stub.js 6 | */ 7 | 8 | module.exports = { 9 | content: [ 10 | /** 11 | * HTML. Paths to Django template files that will contain Tailwind CSS classes. 12 | */ 13 | 14 | /* Templates within theme app (/templates), e.g. base.html. */ 15 | '../templates/**/*.html', 16 | 17 | /* 18 | * Main templates directory of the project (BASE_DIR/templates). 19 | * Adjust the following line to match your project structure. 20 | */ 21 | '../../templates/**/*.html', 22 | 23 | /* 24 | * Templates in other django apps (BASE_DIR//templates). 25 | * Adjust the following line to match your project structure. 26 | */ 27 | '../../**/templates/**/*.html', 28 | 29 | /** 30 | * JS: If you use Tailwind CSS in JavaScript, uncomment the following lines and make sure 31 | * patterns match your project structure. 32 | */ 33 | /* JS 1: Ignore any JavaScript in node_modules folder. */ 34 | // '!../../**/node_modules', 35 | /* JS 2: Process all JavaScript files in the project. */ 36 | // '../../**/*.js', 37 | 38 | /** 39 | * Python: If you use Tailwind CSS classes in Python, uncomment the following line 40 | * and make sure the pattern below matches your project structure. 41 | */ 42 | // '../../**/*.py' 43 | ], 44 | theme: { 45 | extend: {}, 46 | }, 47 | plugins: [ 48 | /** 49 | * '@tailwindcss/forms' is the forms plugin that provides a minimal styling 50 | * for forms. If you don't like it or have own styling for forms, 51 | * comment the line below to disable '@tailwindcss/forms'. 52 | */ 53 | require('@tailwindcss/forms'), 54 | require('@tailwindcss/typography'), 55 | require('@tailwindcss/line-clamp'), 56 | require('@tailwindcss/aspect-ratio'), 57 | ], 58 | } 59 | --------------------------------------------------------------------------------