From 9f37d3faf1d58b15aed15b74cbc520dcc06cc4c4 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 27 Oct 2026 12:50:34 -0700 Subject: [PATCH] Initial changes testing and running vue3 --- server/index.js | 5 +- shell.nix | 112 +- startLocalDev.sh | 49 + syncDown.sh | 5 +- v2client-vite-vue3/.gitignore | 24 + v2client-vite-vue3/.vscode/extensions.json | 3 + v2client-vite-vue3/README.md | 5 + v2client-vite-vue3/index.html | 12 + v2client-vite-vue3/package-lock.json | 1766 ++++++ v2client-vite-vue3/package.json | 28 + v2client-vite-vue3/pnpm-lock.yaml | 1666 ++++++ v2client-vite-vue3/pnpm-workspace.yaml | 2 + v2client-vite-vue3/public/vite.svg | 1 + v2client-vite-vue3/src/App.vue | 306 + v2client-vite-vue3/src/EventBus.js | 12 + v2client-vite-vue3/src/Helpers.js | 68 + .../src/assets/roboto-latin-bold.woff2 | Bin 0 -> 11020 bytes .../src/assets/roboto-latin.woff2 | Bin 0 -> 11016 bytes .../src/assets/semantic-helper.css | 1003 ++++ v2client-vite-vue3/src/assets/squire.js | 5015 +++++++++++++++++ v2client-vite-vue3/src/assets/vue.svg | 1 + .../components/AnimatedCounterComponent.vue | 112 + .../src/components/AttachmentDisplayCard.vue | 245 + .../src/components/ColorPicker.vue | 175 + .../src/components/FastFilters.vue | 93 + .../src/components/FileUploadButton.vue | 94 + .../GlobalNotificationComponent.vue | 171 + .../src/components/GlobalSiteMenu.vue | 541 ++ .../src/components/HelloWorld.vue | 60 + .../src/components/LoadingIconComponent.vue | 55 + .../src/components/LoginFormComponent.vue | 241 + .../src/components/LogoComponent.vue | 88 + .../Metrictracking/MetricGraphsComponent.vue | 433 ++ .../Metrictracking/PillCalendarGraph.vue | 552 ++ .../src/components/ModalComponent.vue | 164 + .../components/NoteDeleteButtonComponent.vue | 43 + .../src/components/NoteInputPanel.vue | 1746 ++++++ .../components/NoteMenuButtonComponent.vue | 43 + .../src/components/NoteTagEdit.vue | 339 ++ .../src/components/NoteTitleDisplayCard.vue | 768 +++ .../src/components/PasteButton.vue | 116 + .../src/components/SearchInput.vue | 222 + .../src/components/ShareNoteComponent.vue | 176 + .../src/components/SideSlideMenuComponent.vue | 162 + .../SimpleAttachmentNoteComponent.vue | 123 + .../src/components/SvgDisplayer.vue | 44 + .../src/components/TagDisplayComponent.vue | 148 + .../components/TextColorTooltipComponent.vue | 121 + v2client-vite-vue3/src/main.js | 40 + .../src/mixins/SquireButtonFunctions.js | 443 ++ .../src/pages/AttachmentsPage.vue | 256 + .../src/pages/BookmarkletPage.vue | 66 + .../src/pages/CycletrackingPage.vue | 298 + v2client-vite-vue3/src/pages/HelpPage.vue | 24 + v2client-vite-vue3/src/pages/HomePage.vue | 622 ++ v2client-vite-vue3/src/pages/LoginPage.vue | 54 + .../src/pages/MetrictrackingPage.vue | 1706 ++++++ v2client-vite-vue3/src/pages/NotFoundPage.vue | 30 + v2client-vite-vue3/src/pages/NotesPage.vue | 1068 ++++ v2client-vite-vue3/src/pages/OverviewPage.vue | 764 +++ v2client-vite-vue3/src/pages/QuickPage.vue | 171 + v2client-vite-vue3/src/pages/SettingsPage.vue | 284 + v2client-vite-vue3/src/pages/SharePage.vue | 122 + v2client-vite-vue3/src/pages/TermsPage.vue | 17 + v2client-vite-vue3/src/router/index.js | 39 + v2client-vite-vue3/src/stores/mainStore.js | 196 + v2client-vite-vue3/src/style.css | 79 + v2client-vite-vue3/vite.config.js | 27 + 68 files changed, 23452 insertions(+), 12 deletions(-) create mode 100755 startLocalDev.sh create mode 100644 v2client-vite-vue3/.gitignore create mode 100644 v2client-vite-vue3/.vscode/extensions.json create mode 100644 v2client-vite-vue3/README.md create mode 100644 v2client-vite-vue3/index.html create mode 100644 v2client-vite-vue3/package-lock.json create mode 100644 v2client-vite-vue3/package.json create mode 100644 v2client-vite-vue3/pnpm-lock.yaml create mode 100644 v2client-vite-vue3/pnpm-workspace.yaml create mode 100644 v2client-vite-vue3/public/vite.svg create mode 100644 v2client-vite-vue3/src/App.vue create mode 100644 v2client-vite-vue3/src/EventBus.js create mode 100644 v2client-vite-vue3/src/Helpers.js create mode 100644 v2client-vite-vue3/src/assets/roboto-latin-bold.woff2 create mode 100644 v2client-vite-vue3/src/assets/roboto-latin.woff2 create mode 100644 v2client-vite-vue3/src/assets/semantic-helper.css create mode 100644 v2client-vite-vue3/src/assets/squire.js create mode 100644 v2client-vite-vue3/src/assets/vue.svg create mode 100644 v2client-vite-vue3/src/components/AnimatedCounterComponent.vue create mode 100644 v2client-vite-vue3/src/components/AttachmentDisplayCard.vue create mode 100644 v2client-vite-vue3/src/components/ColorPicker.vue create mode 100644 v2client-vite-vue3/src/components/FastFilters.vue create mode 100644 v2client-vite-vue3/src/components/FileUploadButton.vue create mode 100644 v2client-vite-vue3/src/components/GlobalNotificationComponent.vue create mode 100644 v2client-vite-vue3/src/components/GlobalSiteMenu.vue create mode 100644 v2client-vite-vue3/src/components/HelloWorld.vue create mode 100644 v2client-vite-vue3/src/components/LoadingIconComponent.vue create mode 100644 v2client-vite-vue3/src/components/LoginFormComponent.vue create mode 100644 v2client-vite-vue3/src/components/LogoComponent.vue create mode 100644 v2client-vite-vue3/src/components/Metrictracking/MetricGraphsComponent.vue create mode 100644 v2client-vite-vue3/src/components/Metrictracking/PillCalendarGraph.vue create mode 100644 v2client-vite-vue3/src/components/ModalComponent.vue create mode 100644 v2client-vite-vue3/src/components/NoteDeleteButtonComponent.vue create mode 100644 v2client-vite-vue3/src/components/NoteInputPanel.vue create mode 100644 v2client-vite-vue3/src/components/NoteMenuButtonComponent.vue create mode 100644 v2client-vite-vue3/src/components/NoteTagEdit.vue create mode 100644 v2client-vite-vue3/src/components/NoteTitleDisplayCard.vue create mode 100644 v2client-vite-vue3/src/components/PasteButton.vue create mode 100644 v2client-vite-vue3/src/components/SearchInput.vue create mode 100644 v2client-vite-vue3/src/components/ShareNoteComponent.vue create mode 100644 v2client-vite-vue3/src/components/SideSlideMenuComponent.vue create mode 100644 v2client-vite-vue3/src/components/SimpleAttachmentNoteComponent.vue create mode 100644 v2client-vite-vue3/src/components/SvgDisplayer.vue create mode 100644 v2client-vite-vue3/src/components/TagDisplayComponent.vue create mode 100644 v2client-vite-vue3/src/components/TextColorTooltipComponent.vue create mode 100644 v2client-vite-vue3/src/main.js create mode 100644 v2client-vite-vue3/src/mixins/SquireButtonFunctions.js create mode 100644 v2client-vite-vue3/src/pages/AttachmentsPage.vue create mode 100644 v2client-vite-vue3/src/pages/BookmarkletPage.vue create mode 100644 v2client-vite-vue3/src/pages/CycletrackingPage.vue create mode 100644 v2client-vite-vue3/src/pages/HelpPage.vue create mode 100644 v2client-vite-vue3/src/pages/HomePage.vue create mode 100644 v2client-vite-vue3/src/pages/LoginPage.vue create mode 100644 v2client-vite-vue3/src/pages/MetrictrackingPage.vue create mode 100644 v2client-vite-vue3/src/pages/NotFoundPage.vue create mode 100644 v2client-vite-vue3/src/pages/NotesPage.vue create mode 100644 v2client-vite-vue3/src/pages/OverviewPage.vue create mode 100644 v2client-vite-vue3/src/pages/QuickPage.vue create mode 100644 v2client-vite-vue3/src/pages/SettingsPage.vue create mode 100644 v2client-vite-vue3/src/pages/SharePage.vue create mode 100644 v2client-vite-vue3/src/pages/TermsPage.vue create mode 100644 v2client-vite-vue3/src/router/index.js create mode 100644 v2client-vite-vue3/src/stores/mainStore.js create mode 100644 v2client-vite-vue3/src/style.css create mode 100644 v2client-vite-vue3/vite.config.js diff --git a/server/index.js b/server/index.js index 39d25c4..0888712 100644 --- a/server/index.js +++ b/server/index.js @@ -8,7 +8,10 @@ const ports = { } //Allow user of @ in in require calls. Config in package.json -require('module-alias/register') +// Point module-alias at the repo root explicitly. The bare /register entry +// locates package.json by walking up from its own directory, which lands +// inside .pnpm/ under pnpm's symlinked store and fails to find it. +require('module-alias')(require('path').join(__dirname, '..')) //Auth helper, used for decoding users web token let Auth = require('@helpers/Auth') diff --git a/shell.nix b/shell.nix index 2c34676..9c57bfb 100644 --- a/shell.nix +++ b/shell.nix @@ -1,22 +1,116 @@ +{ backupFile ? "" }: with import { }; -let - pythonPackages = python311Packages; -in pkgs.mkShell rec { +pkgs.mkShell { name = "Solid Scribe Env"; - buildInputs = [ - # node for server + buildInputs = [ nodejs - #nodePackages.npm nodemon pnpm pm2 + mariadb + openssl ]; - postShellHook = '' - echo("Welcome to solid scribe") + shellHook = '' + echo "Welcome to Solid Scribe" alias npm=pnpm - ''; + MYSQL_DATADIR="$PWD/.mysql/data" + MYSQL_SOCKET="$PWD/.mysql/mysql.sock" + MYSQL_PIDFILE="$PWD/.mysql/mysql.pid" + + mkdir -p "$MYSQL_DATADIR" + + # Initialize data directory on first run + if [ ! -d "$MYSQL_DATADIR/mysql" ]; then + echo "Initializing MySQL data directory (first run)..." + mysql_install_db \ + --datadir="$MYSQL_DATADIR" \ + --skip-test-db \ + --auth-root-authentication-method=normal \ + 2>/dev/null + echo "Initialization complete." + fi + + # Start mysqld if not already running + STARTED_MYSQL=0 + if ! mysqladmin --socket="$MYSQL_SOCKET" ping &>/dev/null 2>&1; then + STARTED_MYSQL=1 + echo "Starting MySQL..." + mysqld --no-defaults \ + --datadir="$MYSQL_DATADIR" \ + --socket="$MYSQL_SOCKET" \ + --pid-file="$MYSQL_PIDFILE" \ + --port=3306 \ + --bind-address=127.0.0.1 \ + --log-error="$PWD/.mysql/error.log" & + + until mysqladmin --socket="$MYSQL_SOCKET" ping &>/dev/null 2>&1; do + sleep 0.5 + done + echo "MySQL ready." + + mysql -u root --socket="$MYSQL_SOCKET" \ + -e "CREATE DATABASE IF NOT EXISTS application;" 2>/dev/null + fi + + # Set app DB env vars — dotenv won't override these since they're already in process.env + export DB_HOST=127.0.0.1 + export DB_USER=root + export DB_PASS= + + # Stable dev JWT key, persisted across shell sessions + if [ -z "''${JSON_KEY:-}" ]; then + if [ -s "$PWD/.mysql/jwt_key" ]; then + export JSON_KEY=$(cat "$PWD/.mysql/jwt_key") + else + export JSON_KEY=$(openssl rand -hex 32) + echo "$JSON_KEY" > "$PWD/.mysql/jwt_key" + echo "Generated dev JSON_KEY (saved to .mysql/jwt_key)" + fi + fi + + # Restore a SQL dump — handles both plain .sql and .gz files + restore-db() { + local file="''${1:-}" + if [ -z "$file" ]; then + echo "Usage: restore-db " + return 1 + fi + echo "Restoring database from $file ..." + if [[ "$file" == *.gz ]]; then + gunzip -c "$file" | mysql -u root --socket="$MYSQL_SOCKET" + else + mysql -u root --socket="$MYSQL_SOCKET" < "$file" + fi + # The dump is --all-databases, so it includes the `mysql` schema and + # overwrites local root with production's credentials. In-memory grants + # are still the old ones until FLUSH, so fix root now while we can + # still authenticate. + mysql -u root --socket="$MYSQL_SOCKET" -e " + ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('''); + GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION; + FLUSH PRIVILEGES;" + + echo "Restore complete." + } + export -f restore-db + + # Auto-restore if --argstr backupFile was passed to nix-shell + if [ -n "${backupFile}" ]; then + restore-db "${backupFile}" + fi + + if [ "$STARTED_MYSQL" = "1" ]; then + trap "echo 'Stopping MySQL...'; mysqladmin -u root --socket=$MYSQL_SOCKET shutdown 2>/dev/null" EXIT + else + echo "MySQL already running (started by another shell) -- leaving it up on exit." + fi + + # Do not start the app here. + # This same shell.nix is used for hosting the app on dev + # use the startDevServerAndClient.sh script + ''; } diff --git a/startLocalDev.sh b/startLocalDev.sh new file mode 100755 index 0000000..4338898 --- /dev/null +++ b/startLocalDev.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# +# Local dev: API server + v2client (Vue 3 / Vite). +# Run from inside nix-shell, which provides node/pnpm/pm2/mariadb, +# starts the local MySQL, and exports DB_* and JSON_KEY. +# +# Unlike startDevServerAndClient.sh (remote dev box, /home/mab/ss, Vue 2 +# client) this is path-relative and points at v2client-vite-vue3. + +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$REPO" + +if [ -z "${JSON_KEY:-}" ]; then + echo "JSON_KEY is not set -- run this from inside nix-shell." >&2 + exit 1 +fi + +if ! mysqladmin --socket="$REPO/.mysql/mysql.sock" ping &>/dev/null; then + echo "Local MySQL is not running -- run this from inside nix-shell." >&2 + exit 1 +fi + +echo '::--:: Starting API server (:3000) + socket.io (:3001) under pm2...' +cd "$REPO/server" +# `env -u name`: nix's mkShell exports its `name` attr ("Solid Scribe Env") +# as $name, and pm2 uses $name to override the app name from ecosystem.config.js. +pm2 delete NoteServer &>/dev/null || true +pm2 flush &>/dev/null || true +env -u name pm2 start ecosystem.config.js +cd "$REPO" + +echo '::--:: Starting v2client dev server -> http://localhost:5173' +cd "$REPO/v2client-vite-vue3" +[ -d node_modules ] || pnpm install +screen -S "V2ClientScreen" -X quit &>/dev/null || true +screen -dmS "V2ClientScreen" bash -c "cd '$REPO/v2client-vite-vue3'; pnpm dev" + +cat < :3000, /socket -> :3001) + api http://localhost:3000 + + logs pm2 logs NoteServer + screen -r V2ClientScreen (detach: ctrl-a d) + stop pm2 delete NoteServer; screen -S V2ClientScreen -X quit +MSG diff --git a/syncDown.sh b/syncDown.sh index 6492f6b..d4e8209 100755 --- a/syncDown.sh +++ b/syncDown.sh @@ -1,4 +1,4 @@ -#!/bin/bash +# # #!/bin/bash #Syncs local data with sandbox # v - Verbose @@ -11,5 +11,6 @@ # e - Send Files via SSH # z - Compress for speed # h - Human Readable file sizes +# p - port -rsync -e 'ssh' --exclude-from=dontSync.txt -havzC --update mab@marvin.local:/home/mab/ss/ . +rsync -e 'ssh -p 13326' --exclude-from=dontSync.txt -havz --update mab@blarvin.local:/home/mab/ss/ . diff --git a/v2client-vite-vue3/.gitignore b/v2client-vite-vue3/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/v2client-vite-vue3/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/v2client-vite-vue3/.vscode/extensions.json b/v2client-vite-vue3/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/v2client-vite-vue3/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/v2client-vite-vue3/README.md b/v2client-vite-vue3/README.md new file mode 100644 index 0000000..1511959 --- /dev/null +++ b/v2client-vite-vue3/README.md @@ -0,0 +1,5 @@ +# Vue 3 + Vite + +This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` + + diff --git a/v2client-vite-vue3/package-lock.json b/v2client-vite-vue3/package-lock.json new file mode 100644 index 0000000..d6171c6 --- /dev/null +++ b/v2client-vite-vue3/package-lock.json @@ -0,0 +1,1766 @@ +{ + "name": "v2client-vite-vue3", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "v2client-vite-vue3", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.1.13", + "tailwindcss": "^4.1.13", + "vue": "^3.5.18" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "vite": "^7.1.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", + "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz", + "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz", + "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz", + "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz", + "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz", + "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz", + "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz", + "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz", + "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz", + "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz", + "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz", + "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz", + "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz", + "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz", + "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz", + "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz", + "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz", + "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz", + "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz", + "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz", + "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", + "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", + "lightningcss": "1.30.1", + "magic-string": "^0.30.18", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.13" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", + "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-x64": "4.1.13", + "@tailwindcss/oxide-freebsd-x64": "4.1.13", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-x64-musl": "4.1.13", + "@tailwindcss/oxide-wasm32-wasi": "4.1.13", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", + "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", + "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", + "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", + "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", + "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", + "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", + "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", + "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", + "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", + "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", + "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", + "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.13.tgz", + "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==", + "dependencies": { + "@tailwindcss/node": "4.1.13", + "@tailwindcss/oxide": "4.1.13", + "tailwindcss": "4.1.13" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", + "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "dependencies": { + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", + "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", + "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/runtime-core": "3.5.21", + "@vue/shared": "3.5.21", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", + "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "dependencies": { + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "vue": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==" + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "engines": { + "node": ">=18" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", + "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.50.1", + "@rollup/rollup-android-arm64": "4.50.1", + "@rollup/rollup-darwin-arm64": "4.50.1", + "@rollup/rollup-darwin-x64": "4.50.1", + "@rollup/rollup-freebsd-arm64": "4.50.1", + "@rollup/rollup-freebsd-x64": "4.50.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", + "@rollup/rollup-linux-arm-musleabihf": "4.50.1", + "@rollup/rollup-linux-arm64-gnu": "4.50.1", + "@rollup/rollup-linux-arm64-musl": "4.50.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", + "@rollup/rollup-linux-ppc64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-musl": "4.50.1", + "@rollup/rollup-linux-s390x-gnu": "4.50.1", + "@rollup/rollup-linux-x64-gnu": "4.50.1", + "@rollup/rollup-linux-x64-musl": "4.50.1", + "@rollup/rollup-openharmony-arm64": "4.50.1", + "@rollup/rollup-win32-arm64-msvc": "4.50.1", + "@rollup/rollup-win32-ia32-msvc": "4.50.1", + "@rollup/rollup-win32-x64-msvc": "4.50.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==" + }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", + "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-sfc": "3.5.21", + "@vue/runtime-dom": "3.5.21", + "@vue/server-renderer": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/v2client-vite-vue3/package.json b/v2client-vite-vue3/package.json new file mode 100644 index 0000000..b3b1888 --- /dev/null +++ b/v2client-vite-vue3/package.json @@ -0,0 +1,28 @@ +{ + "name": "v2client-vite-vue3", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.13", + "axios": "^1.1.3", + "diff-match-patch": "^1.0.5", + "fomantic-ui-css": "^2.9.0", + "mitt": "^3.0.1", + "socket.io-client": "^2.5.0", + "tailwindcss": "^4.1.13", + "vue": "^3.5.18", + "vue-router": "^4.0.0", + "vuedraggable": "^4.1.0", + "vuex": "^4.0.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "vite": "^7.1.2" + } +} diff --git a/v2client-vite-vue3/pnpm-lock.yaml b/v2client-vite-vue3/pnpm-lock.yaml new file mode 100644 index 0000000..ec158c6 --- /dev/null +++ b/v2client-vite-vue3/pnpm-lock.yaml @@ -0,0 +1,1666 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tailwindcss/vite': + specifier: ^4.1.13 + version: 4.1.18(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)) + axios: + specifier: ^1.1.3 + version: 1.18.1 + diff-match-patch: + specifier: ^1.0.5 + version: 1.0.5 + fomantic-ui-css: + specifier: ^2.9.0 + version: 2.9.4 + mitt: + specifier: ^3.0.1 + version: 3.0.1 + socket.io-client: + specifier: ^2.5.0 + version: 2.5.0 + tailwindcss: + specifier: ^4.1.13 + version: 4.1.18 + vue: + specifier: ^3.5.18 + version: 3.5.26 + vue-router: + specifier: ^4.0.0 + version: 4.6.4(vue@3.5.26) + vuedraggable: + specifier: ^4.1.0 + version: 4.1.0(vue@3.5.26) + vuex: + specifier: ^4.0.0 + version: 4.1.0(vue@3.5.26) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^6.0.1 + version: 6.0.3(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.26) + vite: + specifier: ^7.1.2 + version: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2) + +packages: + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + + '@rollup/rollup-android-arm-eabi@4.54.0': + resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.54.0': + resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.54.0': + resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.54.0': + resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.54.0': + resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.54.0': + resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.54.0': + resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.54.0': + resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.54.0': + resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openharmony-arm64@4.54.0': + resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.54.0': + resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.54.0': + resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} + cpu: [x64] + os: [win32] + + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.18': + resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@vitejs/plugin-vue@6.0.3': + resolution: {integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vue: ^3.2.25 + + '@vue/compiler-core@3.5.26': + resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==} + + '@vue/compiler-dom@3.5.26': + resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==} + + '@vue/compiler-sfc@3.5.26': + resolution: {integrity: sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==} + + '@vue/compiler-ssr@3.5.26': + resolution: {integrity: sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/reactivity@3.5.26': + resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==} + + '@vue/runtime-core@3.5.26': + resolution: {integrity: sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==} + + '@vue/runtime-dom@3.5.26': + resolution: {integrity: sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==} + + '@vue/server-renderer@3.5.26': + resolution: {integrity: sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==} + peerDependencies: + vue: 3.5.26 + + '@vue/shared@3.5.26': + resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==} + + after@0.8.2: + resolution: {integrity: sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + arraybuffer.slice@0.0.7: + resolution: {integrity: sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.18.1: + resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + + backo2@1.0.2: + resolution: {integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==} + + base64-arraybuffer@0.1.4: + resolution: {integrity: sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==} + engines: {node: '>= 0.6.0'} + + blob@0.0.5: + resolution: {integrity: sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + component-bind@1.0.0: + resolution: {integrity: sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + component-inherit@0.0.3: + resolution: {integrity: sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + engine.io-client@3.5.6: + resolution: {integrity: sha512-2fDMKiXSU7bGRDCWEw9cHEdRNfoU8cpP6lt+nwJhv72tSJpO7YBsqMqYZ63eVvwX3l9prPl2k/mxhfVhY+SDWg==} + + engine.io-parser@2.2.1: + resolution: {integrity: sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==} + + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + engines: {node: '>=10.13.0'} + + entities@7.0.0: + resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==} + engines: {node: '>=0.12'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + fomantic-ui-css@2.9.4: + resolution: {integrity: sha512-4U8enaBjxAdjMfpTKhf0Of9/XZJo/WSh90FaF85gIYy669eGYe0tRFzE9T0lIiLRmxa20yKLhT6TvLxGau13dA==} + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-binary2@1.0.3: + resolution: {integrity: sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==} + + has-cors@1.1.0: + resolution: {integrity: sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + indexof@0.0.1: + resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==} + + isarray@2.0.1: + resolution: {integrity: sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jquery@3.7.1: + resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + parseqs@0.0.6: + resolution: {integrity: sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==} + + parseuri@0.0.6: + resolution: {integrity: sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + rollup@4.54.0: + resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + socket.io-client@2.5.0: + resolution: {integrity: sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw==} + + socket.io-parser@3.3.5: + resolution: {integrity: sha512-pn+xG/oVnofxqteOawycpHw9QKclpNRa+Z7RW0vZmrIpkgZOVSVzBvY1YGR+p9kIEZXkeAjpHa21wRNLCZ6UAA==} + + sortablejs@1.14.0: + resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-array@0.1.4: + resolution: {integrity: sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==} + + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vue-router@4.6.4: + resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} + peerDependencies: + vue: ^3.5.0 + + vue@3.5.26: + resolution: {integrity: sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vuedraggable@4.1.0: + resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} + peerDependencies: + vue: ^3.0.1 + + vuex@4.1.0: + resolution: {integrity: sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==} + peerDependencies: + vue: ^3.2.0 + + ws@7.5.11: + resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlhttprequest-ssl@1.6.3: + resolution: {integrity: sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==} + engines: {node: '>=0.4.0'} + + yeast@0.1.2: + resolution: {integrity: sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==} + +snapshots: + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@rolldown/pluginutils@1.0.0-beta.53': {} + + '@rollup/rollup-android-arm-eabi@4.54.0': + optional: true + + '@rollup/rollup-android-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-x64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.54.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.54.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.54.0': + optional: true + + '@tailwindcss/node@4.1.18': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.4 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.18 + + '@tailwindcss/oxide-android-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide@4.1.18': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/vite@4.1.18(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 + tailwindcss: 4.1.18 + vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2) + + '@types/estree@1.0.8': {} + + '@vitejs/plugin-vue@6.0.3(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.26)': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.53 + vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2) + vue: 3.5.26 + + '@vue/compiler-core@3.5.26': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.26 + entities: 7.0.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.26': + dependencies: + '@vue/compiler-core': 3.5.26 + '@vue/shared': 3.5.26 + + '@vue/compiler-sfc@3.5.26': + dependencies: + '@babel/parser': 7.28.5 + '@vue/compiler-core': 3.5.26 + '@vue/compiler-dom': 3.5.26 + '@vue/compiler-ssr': 3.5.26 + '@vue/shared': 3.5.26 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.26': + dependencies: + '@vue/compiler-dom': 3.5.26 + '@vue/shared': 3.5.26 + + '@vue/devtools-api@6.6.4': {} + + '@vue/reactivity@3.5.26': + dependencies: + '@vue/shared': 3.5.26 + + '@vue/runtime-core@3.5.26': + dependencies: + '@vue/reactivity': 3.5.26 + '@vue/shared': 3.5.26 + + '@vue/runtime-dom@3.5.26': + dependencies: + '@vue/reactivity': 3.5.26 + '@vue/runtime-core': 3.5.26 + '@vue/shared': 3.5.26 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.26(vue@3.5.26)': + dependencies: + '@vue/compiler-ssr': 3.5.26 + '@vue/shared': 3.5.26 + vue: 3.5.26 + + '@vue/shared@3.5.26': {} + + after@0.8.2: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + arraybuffer.slice@0.0.7: {} + + asynckit@0.4.0: {} + + axios@1.18.1: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + + backo2@1.0.2: {} + + base64-arraybuffer@0.1.4: {} + + blob@0.0.5: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + component-bind@1.0.0: {} + + component-emitter@1.3.1: {} + + component-inherit@0.0.3: {} + + csstype@3.2.3: {} + + debug@3.1.0: + dependencies: + ms: 2.0.0 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + delayed-stream@1.0.0: {} + + detect-libc@2.1.2: {} + + diff-match-patch@1.0.5: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + engine.io-client@3.5.6: + dependencies: + component-emitter: 1.3.1 + component-inherit: 0.0.3 + debug: 3.1.0 + engine.io-parser: 2.2.1 + has-cors: 1.1.0 + indexof: 0.0.1 + parseqs: 0.0.6 + parseuri: 0.0.6 + ws: 7.5.11 + xmlhttprequest-ssl: 1.6.3 + yeast: 0.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@2.2.1: + dependencies: + after: 0.8.2 + arraybuffer.slice: 0.0.7 + base64-arraybuffer: 0.1.4 + blob: 0.0.5 + has-binary2: 1.0.3 + + enhanced-resolve@5.18.4: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + entities@7.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + estree-walker@2.0.2: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + follow-redirects@1.16.0: {} + + fomantic-ui-css@2.9.4: + dependencies: + jquery: 3.7.1 + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-binary2@1.0.3: + dependencies: + isarray: 2.0.1 + + has-cors@1.1.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + indexof@0.0.1: {} + + isarray@2.0.1: {} + + jiti@2.6.1: {} + + jquery@3.7.1: {} + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mitt@3.0.1: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + parseqs@0.0.6: {} + + parseuri@0.0.6: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + proxy-from-env@2.1.0: {} + + rollup@4.54.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.54.0 + '@rollup/rollup-android-arm64': 4.54.0 + '@rollup/rollup-darwin-arm64': 4.54.0 + '@rollup/rollup-darwin-x64': 4.54.0 + '@rollup/rollup-freebsd-arm64': 4.54.0 + '@rollup/rollup-freebsd-x64': 4.54.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 + '@rollup/rollup-linux-arm-musleabihf': 4.54.0 + '@rollup/rollup-linux-arm64-gnu': 4.54.0 + '@rollup/rollup-linux-arm64-musl': 4.54.0 + '@rollup/rollup-linux-loong64-gnu': 4.54.0 + '@rollup/rollup-linux-ppc64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-musl': 4.54.0 + '@rollup/rollup-linux-s390x-gnu': 4.54.0 + '@rollup/rollup-linux-x64-gnu': 4.54.0 + '@rollup/rollup-linux-x64-musl': 4.54.0 + '@rollup/rollup-openharmony-arm64': 4.54.0 + '@rollup/rollup-win32-arm64-msvc': 4.54.0 + '@rollup/rollup-win32-ia32-msvc': 4.54.0 + '@rollup/rollup-win32-x64-gnu': 4.54.0 + '@rollup/rollup-win32-x64-msvc': 4.54.0 + fsevents: 2.3.3 + + socket.io-client@2.5.0: + dependencies: + backo2: 1.0.2 + component-bind: 1.0.0 + component-emitter: 1.3.1 + debug: 3.1.0 + engine.io-client: 3.5.6 + has-binary2: 1.0.3 + indexof: 0.0.1 + parseqs: 0.0.6 + parseuri: 0.0.6 + socket.io-parser: 3.3.5 + to-array: 0.1.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@3.3.5: + dependencies: + component-emitter: 1.3.1 + debug: 3.1.0 + isarray: 2.0.1 + transitivePeerDependencies: + - supports-color + + sortablejs@1.14.0: {} + + source-map-js@1.2.1: {} + + tailwindcss@4.1.18: {} + + tapable@2.3.0: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-array@0.1.4: {} + + vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.54.0 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + + vue-router@4.6.4(vue@3.5.26): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.26 + + vue@3.5.26: + dependencies: + '@vue/compiler-dom': 3.5.26 + '@vue/compiler-sfc': 3.5.26 + '@vue/runtime-dom': 3.5.26 + '@vue/server-renderer': 3.5.26(vue@3.5.26) + '@vue/shared': 3.5.26 + + vuedraggable@4.1.0(vue@3.5.26): + dependencies: + sortablejs: 1.14.0 + vue: 3.5.26 + + vuex@4.1.0(vue@3.5.26): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.26 + + ws@7.5.11: {} + + xmlhttprequest-ssl@1.6.3: {} + + yeast@0.1.2: {} diff --git a/v2client-vite-vue3/pnpm-workspace.yaml b/v2client-vite-vue3/pnpm-workspace.yaml new file mode 100644 index 0000000..5ed0b5a --- /dev/null +++ b/v2client-vite-vue3/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: true diff --git a/v2client-vite-vue3/public/vite.svg b/v2client-vite-vue3/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/v2client-vite-vue3/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v2client-vite-vue3/src/App.vue b/v2client-vite-vue3/src/App.vue new file mode 100644 index 0000000..0ac6f2c --- /dev/null +++ b/v2client-vite-vue3/src/App.vue @@ -0,0 +1,306 @@ + + + \ No newline at end of file diff --git a/v2client-vite-vue3/src/EventBus.js b/v2client-vite-vue3/src/EventBus.js new file mode 100644 index 0000000..9bfe59d --- /dev/null +++ b/v2client-vite-vue3/src/EventBus.js @@ -0,0 +1,12 @@ +import mitt from 'mitt' + +const emitter = mitt() + +// Preserves the Vue 2 $on/$emit/$off API used throughout the codebase +const EventBus = { + $on: (...args) => emitter.on(...args), + $off: (...args) => emitter.off(...args), + $emit: (...args) => emitter.emit(...args), +} + +export default EventBus diff --git a/v2client-vite-vue3/src/Helpers.js b/v2client-vite-vue3/src/Helpers.js new file mode 100644 index 0000000..70d3d58 --- /dev/null +++ b/v2client-vite-vue3/src/Helpers.js @@ -0,0 +1,68 @@ +const helpers = {} + +helpers.timeAgo = (time) => { + + if(time == null){ + time = Math.round(time/1000) + } + + if(time.toString().length >= 13){ + time = Math.round(time/1000) + } + + const time_formats = [ + [ 60, 'seconds', 1 ], + [ 120, '1 minute ago', '1 minute from now' ], + [ 3600, 'minutes', 60 ], + [ 7200, '1 hour ago', '1 hour from now' ], + [ 86400, 'hours', 3600 ], + [ 172800, 'yesterday', 'tomorrow' ], + [ 604800, 'days', 86400 ], + [ 1209600, 'last week', 'next week' ], + [ 2419200, 'weeks', 604800 ], + [ 4838400, 'last month', 'next month' ], + [ 29030400, 'months', 2419200 ], + [ 58060800, 'last year', 'next year' ], + [ 2903040000, 'years', 29030400 ], + [ 5806080000, 'last century', 'next century' ], + [ 58060800000, 'centuries', 2903040000 ] + ] + + //How many seconds ago was input event time? + let seconds = Math.round((+new Date)/1000) - time + let token = 'ago' + let list_choice = 1 + + if (seconds == 0) { + return 'Just now' + } + + if (seconds < 0) { + seconds = Math.abs(seconds) + token = 'from now' + list_choice = 2 + } + + + let i = 0 + let format = null + while (format = time_formats[i++]){ + if (seconds < format[0]) { + if (typeof format[2] == 'string') { + return format[list_choice] + } else { + return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token + } + } + } + + return time +} + +const plugin = { + install(app) { + app.config.globalProperties.$helpers = helpers + } +} + +export default plugin \ No newline at end of file diff --git a/v2client-vite-vue3/src/assets/roboto-latin-bold.woff2 b/v2client-vite-vue3/src/assets/roboto-latin-bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1cdd68c821394eaf0e4a87e1dd9c4df1c6e8bf0d GIT binary patch literal 11020 zcmV+nEA!NMPew8T0RR9104odt4gdfE095<{04lWr0RR9100000000000000000000 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%0DgjfrLy!OH7I_d+2agrjP7B^5ej@@NE3BQ? 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 CxrlrK literal 0 HcmV?d00001 diff --git a/v2client-vite-vue3/src/assets/semantic-helper.css b/v2client-vite-vue3/src/assets/semantic-helper.css new file mode 100644 index 0000000..4307ef9 --- /dev/null +++ b/v2client-vite-vue3/src/assets/semantic-helper.css @@ -0,0 +1,1003 @@ +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + src: local('Roboto'), local('Roboto-Regular'), url(./roboto-latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + src: local('Roboto Bold'), local('Roboto-Bold'), url(./roboto-latin-bold.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +body { + margin: 0; + padding: 0; +/* overflow-x: hidden;*/ + min-width: 320px; + background: green; + font-family: 'Roboto', system-ui, -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 14px; + line-height: 1.4285em; + color: rgba(0, 0, 0, 0.87); + position: relative; +} + +:root { + + /*main accent for all buttons, icons and logos*/ + --main-accent: #21BA45; + + /*theme colors */ + --body_bg_color: #f5f6f7; + --small_element_bg_color: #fff; + --text_color: #3d3d3d; + --dark_border_color: #DFE1E6; + --border_color: #DFE1E6; + + /* Global purple menu styles */ + --menu-border: #534c68; + --menu-background: #221f2b; + + /* edit menu styles, text, accent */ + --menu-text: #5e6268; + --menu-accent: #cecece; +} + +html { + /*scrollbar-width: none;*/ + width: 100%; + height:100%; + padding: 0; + margin: 0; + background: var(--body_bg_color); +} +a:hover { + text-decoration: underline; +} +div.ui.basic.segment.no-fluf-segment { + margin-top: 0px; +} + +.page-container { + /*width: 100%;*/ + display: block; + margin: 0; + padding: 0.5rem; + box-sizing: border-box; + overflow: hidden; +} + +/* Night mode modifiers */ + + /*Make images sepia in night mode */ + .night-mode img { + filter: grayscale(50%) brightness(80%) sepia(80%); + } + .night-mode i.green { + color: #877A61 !important; + } + .night-mode .green.label { + background-color: #877A61 !important; + border-color: #877A61 !important; + } + .night-mode .green.button { + background-color: #534428 !important; + } + +/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/ +body { + color: var(--text_color); + background: none; + font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif; +} +#app { +/* background: var(--body_bg_color);*/ +} + +.ui.segment { + color: var(--text_color); + background-color: var(--small_element_bg_color); + border-color: var(--dark_border_color); +} +.button-sub { + display: inline-block; + width: 100%; + font-size: 0.9em; + color: grey; + opacity: 0.9; + padding: 4px 0 0 0; + text-align: center; +} + + + +.ui.form input:not([type]), +.ui.form input:not([type]):focus, +.ui.form textarea:not([type]), +.ui.form textarea:not([type]):focus { + color: var(--text_color); + background-color: var(--small_element_bg_color); + border-color: var(--dark_border_color); +} +.ui.form input[type="password"], +.ui.form input[type="text"], +.ui.input > input { + color: var(--text_color); + background-color: var(--small_element_bg_color); + border-color: var(--dark_border_color); +} +.ui.form input[type="password"]:focus, .ui.form input[type="password"]:active, +.ui.form input[type="text"]:focus, .ui.form input[type="text"]:active, +.ui.input > input:focus, .ui.input > input:active { + color: var(--text_color); + background-color: var(--small_element_bg_color); + border-color: var(--main-accent); + border-right-color: var(--main-accent) !important; +} +.ui.basic.label, .ui.header, .ui.header div.sub.header { + color: var(--text_color); + background-color: transparent; + border-color: var(--dark_border_color); +} +.ui.dividing.header { + border-bottom-color: var(--dark_border_color); +} +.ui.dividing.header > .sub.header { + color: var(--dark_border_color); +} +.ui.icon.input > i.icon { + color: var(--text_color); +} +div.ui.basic.green.label { + background-color: var(--small_element_bg_color) !important; +} +.ui.basic.button, .ui.basic.buttons .button { + background-color: var(--small_element_bg_color); + color: var(--text_color) !important; + border: 1px solid; + border-color: var(--dark_border_color) !important; + box-shadow: none; +} +.ui.basic.button:focus, .ui.basic.button:hover { + background-color: var(--small_element_bg_color) !important; + color: var(--text_color) !important; + box-shadow: none; +} +.ui.tabular.menu .item { + background-color: var(--small_element_bg_color) !important; + color: var(--text_color) !important; +} +.ui.tabular.menu .item.active { + background-color: var(--small_element_bg_color) !important; + color: var(--text_color) !important; + border-color: var(--dark_border_color) !important; +} +/*Overwrites for modifiable theme color */ +i.green.icon.icon.icon.icon, i.green.icon.icon.icon.icon.icon { + color: var(--main-accent); +} +.button { + box-shadow: 2px 2px 4px -2px rgba(40, 40, 40, 0.89) !important; + transition: all 0.9s ease; + position: relative; +} +.button:hover { + box-shadow: 3px 2px 3px -2px rgba(40, 40, 40, 0.95) !important; +} +.button:active { + transform: translateY(1px); +} + +.ui.green.buttons, .ui.green.button, .ui.green.button:hover { + background-color: var(--main-accent); +} +.ui.basic.green.button, .ui.basic.green.buttons .button:hover, .ui.basic.green.button:hover, .ui.basic.green.button:focus { + box-shadow: var(--main-accent) 0px 0px 0px 1px inset; +} +.ui.green.labels .label, .ui.ui.ui.green.label { + background-color: var(--main-accent); + border-color: var(--main-accent); +} +.ui.grid > .green.row, .ui.grid > .green.column, .ui.grid > .row > .green.column { + background-color: var(--main-accent); +} +.ui.green.header { + color: var(--main-accent); +} + +/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/ + +/*// +// Purple Global Menu +//*/ +.note-menu { + width: 100%; + /*display: block;*/ + display: inline-table; + background: var(--menu-background); + color: white; + /*overflow: hidden;*/ + border: 1px solid var(--menu-border); + /*height: 50px;*/ +} +.note-menu > .nm-button { + padding: 10px 15px; + cursor: pointer; + text-align: center; + box-sizing: border-box; + font-size: 1.2em; + vertical-align: middle; + /*height: 40px;*/ + display: table-cell; + position: relative; +} +.nm-button i.icon { + margin: 0; +} +.nm-button span { + font-size: 0.9em; +} +.nm-button.right { + float: right; + border-left: 1px solid var(--menu-border); +} +.nm-button:hover { + background-color: #534c68; + color: white; +} +.nm-button + .nm-button { + border-left: 1px solid #534c68; +} +/*.shrink-icons-on-mobile.note-menu span { + display: none; +}*/ + +/* Shrink button text for mobile */ +@media only screen and (max-width: 740px) { + .note-menu > .nm-button { + font-size: 1.4em; + padding: 10px 1px; + min-width: 40px; + } + .note-menu .nm-button span { + font-size: 0.7em; + line-height: 0.4em; + margin-left: 0; + } + .nm-button i.icon { + width: 100%; + } + /*prevents buttons from being jammed into corners of round phones*/ + .shrink-icons-on-mobile.note-menu { + padding: 0 20px; + } + .shrink-icons-on-mobile .nm-button { + padding: 2px 3px; + } + .shrink-icons-on-mobile .nm-button i.icon { + font-size: 0.7em; + } +} + +/*// +// Purple Global Menu +//*/ + +.note-status-indicator { + position: absolute; + width: 100px; + padding: 16px 0; + box-sizing: border-box; + text-align: center; + color: var(--text_color); + bottom: -13px; + right: -7px; + z-index: 100; + cursor: pointer; +} +.text-container { + max-width: 1000px; + display: block; + margin-left: auto; + margin-right: auto; + background-color: var(--small_element_bg_color) !important; +} + +/* squire text styles */ + .squire-box { + border: none; + /*height: calc(100% - 69px);*/ + + min-height: 300px; + background-color: var(--small_element_bg_color); + /*margin-bottom: 15px;*/ + + box-sizing: border-box; + padding: 10px 15px 10px; + /*background: transparent;*/ + overflow: hidden; + font-size: 1.2em; + line-height: 1.8em; + word-wrap: break-word; + /*border-bottom: 1px solid #ccc;*/ + scrollbar-width: none; + scrollbar-color: transparent transparent; + caret-color: var(--main-accent); + + margin-left: auto; + margin-right: auto; + max-width: 1100px; + + box-shadow: 0 8px 24px rgba(0,0,0,0.1); + + } + .squire-box::selection, + .squire-box::-moz-selection { + background: #cce2ffa6; + color: inherit; + } + /*Makes the first line real big */ + .squire-box:focus { + outline: none; + } + .squire-box span.size { + line-height: 1.3em; + } + .squire-box a { + cursor: pointer; + } + .night-mode .note-card-text i:not(.icon), + .night-mode .squire-box i:not(.icon) { + background-color: rgba(255, 255, 255, 0.2); + } + + .note-card-text code, + .squire-box code, + .note-card-text pre, + .squire-box pre { + /*word-wrap: break-word;*/ + display: inline-block; + border-left: 2px solid var(--main-accent); + padding-left: 15px; + } + .note-card-text p, + .squire-box p { + margin-bottom: 0; + line-height: 1.5em; + } + .note-card-text blockquote, + .squire-box blockquote { + margin: 0; + padding: 0 0 0 2.5em; + } + .note-card-text u, + .squire-box u { + text-decoration-color: var(--main-accent); + } + .note-card-text img { + max-width:100%; + height: auto; + max-height: 200px; + margin: 10px 0 0; + } + .squire-box img { + max-width:100%; + height: auto; + } + .note-card-text li > p, + .squire-box p, + .squire-box li > p { + margin-bottom: 0; + } + .note-card-text ol, + .squire-box ol, + .note-card-text ul, + .squire-box ul { + margin: 3px 0; + display: block; + } + /* Add border 1 indent level */ + .note-card-text > ol > ol, + .squire-box > ol > ol, + .note-card-text > ul > ul, + .squire-box > ul > ul + { + border-left: 1px solid var(--border_color); + } + .note-card-text ol > ol, + .squire-box ol > ol, + .note-card-text ul > ul, + .squire-box ul > ul { + list-style-type: upper-alpha; + } + + +ol { + counter-reset: item; +} +ol li { + display: block; +} +ol li:before { +content: counters(item, ".") "."; +counter-increment: item; +padding-right: 10px; +} + + .note-card-text ul > li, + .squire-box ul > li { + position: relative; + list-style-type: none; + } + .note-card-text ul > li:before, + .squire-box ul > li:before { + + /*filled circle */ + content: "\f111"; + /*empty square*/ + /*content: "\F0C8";*/ + + font-family: 'Icons'; + /*font-family: 'outline-icons';*/ + backface-visibility: hidden; + font-style: normal; + font-weight: normal; + text-decoration: inherit; + text-align: center; + font-size: 0.25em; + + height: 100%; + width: 20px; + display: inline-block; + position: absolute; + + left: -25px; + /*border: 2px solid #444;*/ + /*border-radius: 4px;*/ + bottom: 0; + top: 0; + cursor: pointer; + opacity: 0.7; + + color: var(--text_color); + text-align: center; + } + /* filled in check circle */ + ul > li.active:before { + + font-family: 'Icons'; + content: "\F14A"; + color: var(--main-accent); + opacity: 1; + + font-size: 1em; + } + /* hover - transparent icon */ + .squire-box ul > li:hover:not(.active):before { + font-family: 'outline-icons'; + content: "\f14a"; + opacity: 0.4; + + font-size: 1em; + } + + .note-title-display-card .divide, + .squire-box .divide { + width: 100%; + display: inline-block; + height: 2px; + background-color: var(--main-accent); + } + + table { + width: 100%; + border-collapse: collapse; + } + + tr { + display: flex; + } + + th, td { + border: 1px solid #ddd; + border-bottom: 1px solid #ddd; + font-weight: normal; + flex: 1; + } +/* table:hover th, table:hover td { + border: 1px solid black; + }*/ + + th, td { + padding: 3px; + text-align: left; + } + .table-tic-table { + } + .table-tic-table > div { + height: 21px; + margin: 0; + padding: 0; + } + .tabletic { + display: inline-block; + border: 1px solid black; + border-radius: 2px; + width: 20px; + height: 20px; + margin: 0 1px 1px 0; + cursor: pointer; + } + + .t-table { + width: 100%; + display: inline-block; + border: 1px solid black; + } + + .t-table > span, + .t-table > div { + display: flex; /* aligns all child elements (flex items) in a row */ + } + + .t-table > span > span, + .t-table > div > div { + flex: 1; /* distributes space on the line equally among items */ + border: 1px solid #DDD; + } + + + /* adjust checkboxes for mobile. Make them a little bigger, easier to click */ + @media only screen and (max-width: 740px) { + + .ui.button.shrinking { + font-size: 0.85714286rem; + margin: 0 3px; + padding: 10px 7px !important; + + } + + .note-card-text ul > li, + .squire-box ul > li { + min-height: 30px; + } + + /*empty check box*/ + .note-card-text ul > li:before, + .squire-box ul > li:before, + .squire-box ul > li:hover:not(.active):before { + + /*empty checkmark*/ + /*font-family: 'Icons';*/ + /*content: "\f058";*/ + + content: "\F0C8"; + font-family: 'outline-icons'; + + left: -40px; + font-size: 1.4em; + opacity: 0.2; + } + /*Filled check box */ + ul > li.active:before { + + font-family: 'Icons'; + content: "\F14A"; + + color: var(--main-accent); + opacity: 1; + } + + /* Remove indent line on mobile */ + .note-card-text > ol > ol, + .squire-box > ol > ol, + .note-card-text > ul > ul, + .squire-box > ul > ul + { + border-left: none; + } + } + + +.clickable { + cursor: pointer; +} +.relative { + position: relative; +} +.float-right { + float: right; +} +.ui.grid.reduced-padding > .column { + padding-left: 2px; + padding-right: 2px; + padding-top: 5px; + padding-bottom: 5px; +} + +.textarea-height { + height: calc(100% - 90px); +} +.mobile-textarea-height { + height: 100%; +} + +.ui.white.button { + background: #FFF; +} +.white.row { + background-color: rgba(255, 255, 255, 0.9); +} + +.input-floating-button { + position: absolute; + top: 19px; + transform: translateY(-50%); + right: 1px; +} + +.fade-in-fwd { + animation: fade-in-fwd 0.8s both; +} + +/* div that comes up, blocking interaction annd requiring authentication */ +.auth-block { + background-color: rgba(0,0,0,0.9); + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + height: 100vh; + width: 100%; + z-index: 200; +} +.auth-block > div { + position: absolute; + top: 40%; + left: 50%; + transform: translate(-50%, -40%); + width: 300px; +} + + +/** + * ---------------------------------------- + * animation fade-in-fwd + * ---------------------------------------- + */ +@keyframes fade-in-fwd { + 0% { + transform: translateZ(-80px); + opacity: 0; + } + 100% { + transform: translateZ(0); + opacity: 1; + } +} + +/*Fomantic Tooltips*/ +/* Content */ +[data-tooltip] { + position: relative; +} + +/* Arrow */ +[data-tooltip]:before { + pointer-events: none; + position: absolute; + content: ''; + font-size: 1rem; + width: 10px; + height: 10px; + background: #1B1C1D; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + z-index: 1901; +} + +/* Popup */ +[data-tooltip]:after { + min-width: 40px; + pointer-events: none; + content: attr(data-tooltip); + position: absolute; + text-transform: none; + text-align: center; + white-space: pre; + font-size: 1rem; + border: 1px solid #D4D4D5; + line-height: 1.4285em; + max-width: none; + background: #1B1C1D; + padding: 0.5em; + font-weight: normal; + font-style: normal; + /*color: var(--main-accent);*/ + color: white; + border-radius: 0.28571429rem; + z-index: 1900; +} + +/* Default Position (Top Center) */ +[data-tooltip]:not([data-position]):before { + top: auto; + right: auto; + bottom: 100%; + left: 50%; + background: #1B1C1D; + margin-left: -0.07142857rem; + margin-bottom: 0.14285714rem; +} +[data-tooltip]:not([data-position]):after { + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + bottom: 100%; + margin-bottom: 0.5em; +} + +/* Animation */ +[data-tooltip]:before, +[data-tooltip]:after { + pointer-events: none; + visibility: hidden; + opacity: 0; + /*transition: opacity 0.2s ease;*/ +} +[data-tooltip]:before { + -webkit-transform: rotate(45deg) scale(0) !important; + transform: rotate(45deg) scale(0) !important; + -webkit-transform-origin: center top; + transform-origin: center top; +} +[data-tooltip]:after { + -webkit-transform-origin: center bottom; + transform-origin: center bottom; +} +[data-tooltip]:hover:before, +[data-tooltip]:hover:after { + visibility: visible; + pointer-events: auto; + opacity: 1; +} +[data-tooltip]:hover:before { + -webkit-transform: rotate(45deg) scale(1) !important; + transform: rotate(45deg) scale(1) !important; +} + + +/*-------------- + Position + ---------------*/ + +[data-position~="top"][data-tooltip]:before { + background: #1B1C1D; +} + +/* Top Center */ +[data-position="top center"][data-tooltip]:after { + top: auto; + right: auto; + left: 50%; + bottom: 100%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + margin-bottom: 0.5em; +} +[data-position="top center"][data-tooltip]:before { + top: auto; + right: auto; + bottom: 100%; + left: 50%; + background: #1B1C1D; + margin-left: -0.07142857rem; + margin-bottom: 0.14285714rem; +} + +/* Top Left */ +[data-position="top left"][data-tooltip]:after { + top: auto; + right: auto; + left: 0; + bottom: 100%; + margin-bottom: 0.5em; +} +[data-position="top left"][data-tooltip]:before { + top: auto; + right: auto; + bottom: 100%; + left: 1em; + margin-left: -0.07142857rem; + margin-bottom: 0.14285714rem; +} + +/* Top Right */ +[data-position="top right"][data-tooltip]:after { + top: auto; + left: auto; + right: 0; + bottom: 100%; + margin-bottom: 0.5em; +} +[data-position="top right"][data-tooltip]:before { + top: auto; + left: auto; + bottom: 100%; + right: 1em; + margin-left: -0.07142857rem; + margin-bottom: 0.14285714rem; +} +[data-position~="bottom"][data-tooltip]:before { + background: #1B1C1D; + -webkit-box-shadow: -1px -1px 0 0 #bababc; + box-shadow: -1px -1px 0 0 #bababc; +} + +/* Bottom Center */ +[data-position="bottom center"][data-tooltip]:after { + bottom: auto; + right: auto; + left: 50%; + top: 100%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + margin-top: 0.5em; +} +[data-position="bottom center"][data-tooltip]:before { + bottom: auto; + right: auto; + top: 100%; + left: 30%; + margin-left: -0.07142857rem; + margin-top: 0.14285714rem; +} + +/* Bottom Left */ +[data-position="bottom left"][data-tooltip]:after { + left: 0; + top: 100%; + margin-top: 0.5em; +} +[data-position="bottom left"][data-tooltip]:before { + bottom: auto; + right: auto; + top: 100%; + left: 1em; + margin-left: -0.07142857rem; + margin-top: 0.14285714rem; +} + +/* Bottom Right */ +[data-position="bottom right"][data-tooltip]:after { + right: 0; + top: 100%; + margin-top: 0.5em; +} +[data-position="bottom right"][data-tooltip]:before { + bottom: auto; + left: auto; + top: 100%; + right: 1em; + margin-left: -0.14285714rem; + margin-top: 0.07142857rem; +} + +/* Left Center */ +[data-position="left center"][data-tooltip]:after { + right: 100%; + top: 50%; + margin-right: 0.5em; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); +} +[data-position="left center"][data-tooltip]:before { + right: 100%; + top: 50%; + margin-top: -0.14285714rem; + margin-right: -0.07142857rem; + background: #1B1C1D; +} + +/* Right Center */ +[data-position="right center"][data-tooltip]:after { + left: 100%; + top: 50%; + margin-left: 0.5em; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); +} +[data-position="right center"][data-tooltip]:before { + left: 100%; + top: 50%; + margin-top: -0.07142857rem; + margin-left: 0.14285714rem; + background: #1B1C1D; +} + +[data-position~="bottom"][data-tooltip]:before { + -webkit-transform-origin: center bottom; + transform-origin: center bottom; +} +[data-position~="bottom"][data-tooltip]:after { + -webkit-transform-origin: center top; + transform-origin: center top; +} +[data-position="left center"][data-tooltip]:before { + -webkit-transform-origin: top center; + transform-origin: top center; +} +[data-position="left center"][data-tooltip]:after { + -webkit-transform-origin: right center; + transform-origin: right center; +} +[data-position="right center"][data-tooltip]:before { + -webkit-transform-origin: right center; + transform-origin: right center; +} +[data-position="right center"][data-tooltip]:after { + -webkit-transform-origin: left center; + transform-origin: left center; +} +@media only screen and (max-width: 740px) { + /*hide tooltips on mobile*/ + [data-tooltip]:hover:before, + [data-tooltip]:hover:after { + visibility: visible; + opacity: 0; + } +} + + +.glint:after { + + content: ''; + position: absolute; + + top: 0; + left: 0; + bottom: 0; + + width: 100%; + opacity: 0; + pointer-events: none; + z-index: 1; + + background: linear-gradient( + 130deg, + rgba(255,255,255,0) 45%, + rgba(255,255,255,1) 50%, + var(--main-accent) 55%, + rgba(255,255,255,0) 60% + ); + + animation: glint-animation 0.8s linear 1; + animation-delay: 0.9s; +} + +@keyframes glint-animation { + 0% { + left: -100%; + opacity: 1; + } + 100% { + left: 100%; + opacity: 0; + } +} + +.shade { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: rgba(0,0,0,0.7); + z-index: 1000; +} diff --git a/v2client-vite-vue3/src/assets/squire.js b/v2client-vite-vue3/src/assets/squire.js new file mode 100644 index 0000000..c5c8f82 --- /dev/null +++ b/v2client-vite-vue3/src/assets/squire.js @@ -0,0 +1,5015 @@ +/* Copyright © 2011-2015 by Neil Jenkins. MIT Licensed. */ + +( function ( doc, undefined ) { + +"use strict"; + +var DOCUMENT_POSITION_PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING +var ELEMENT_NODE = 1; // Node.ELEMENT_NODE; +var TEXT_NODE = 3; // Node.TEXT_NODE; +var DOCUMENT_NODE = 9; // Node.DOCUMENT_NODE; +var DOCUMENT_FRAGMENT_NODE = 11; // Node.DOCUMENT_FRAGMENT_NODE; +var SHOW_ELEMENT = 1; // NodeFilter.SHOW_ELEMENT; +var SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT; + +var START_TO_START = 0; // Range.START_TO_START +var START_TO_END = 1; // Range.START_TO_END +var END_TO_END = 2; // Range.END_TO_END +var END_TO_START = 3; // Range.END_TO_START + +var ZWS = '\u200B'; + +var win = doc.defaultView; + +var ua = navigator.userAgent; + +var isAndroid = /Android/.test( ua ); +var isMac = /Mac OS X/.test( ua ); +var isWin = /Windows NT/.test( ua ); +var isIOS = /iP(?:ad|hone|od)/.test( ua ) || + ( isMac && !!navigator.maxTouchPoints ); + +var isGecko = /Gecko\//.test( ua ); +var isEdge = /Edge\//.test( ua ); +var isWebKit = !isEdge && /WebKit\//.test( ua ); +var isIE = /Trident\/[4567]\./.test( ua ); + +var ctrlKey = isMac ? 'meta-' : 'ctrl-'; + +var cantFocusEmptyTextNodes = isWebKit; + +var canObserveMutations = typeof MutationObserver !== 'undefined'; +var canWeakMap = typeof WeakMap !== 'undefined'; + +// Use [^ \t\r\n] instead of \S so that nbsp does not count as white-space +var notWS = /[^ \t\r\n]/; + +var indexOf = Array.prototype.indexOf; + +/* + Native TreeWalker is buggy in IE and Opera: + * IE9/10 sometimes throw errors when calling TreeWalker#nextNode or + TreeWalker#previousNode. No way to feature detect this. + * Some versions of Opera have a bug in TreeWalker#previousNode which makes + it skip to the wrong node. + + Rather than risk further bugs, it's easiest just to implement our own + (subset) of the spec in all browsers. +*/ + +var typeToBitArray = { + // ELEMENT_NODE + 1: 1, + // ATTRIBUTE_NODE + 2: 2, + // TEXT_NODE + 3: 4, + // COMMENT_NODE + 8: 128, + // DOCUMENT_NODE + 9: 256, + // DOCUMENT_FRAGMENT_NODE + 11: 1024 +}; + +var always = function () { + return true; +}; + +function TreeWalker ( root, nodeType, filter ) { + this.root = this.currentNode = root; + this.nodeType = nodeType; + this.filter = filter || always; +} + +TreeWalker.prototype.nextNode = function () { + var current = this.currentNode, + root = this.root, + nodeType = this.nodeType, + filter = this.filter, + node; + while ( true ) { + node = current.firstChild; + while ( !node && current ) { + if ( current === root ) { + break; + } + node = current.nextSibling; + if ( !node ) { current = current.parentNode; } + } + if ( !node ) { + return null; + } + if ( ( typeToBitArray[ node.nodeType ] & nodeType ) && + filter( node ) ) { + this.currentNode = node; + return node; + } + current = node; + } +}; + +TreeWalker.prototype.previousNode = function () { + var current = this.currentNode, + root = this.root, + nodeType = this.nodeType, + filter = this.filter, + node; + while ( true ) { + if ( current === root ) { + return null; + } + node = current.previousSibling; + if ( node ) { + while ( current = node.lastChild ) { + node = current; + } + } else { + node = current.parentNode; + } + if ( !node ) { + return null; + } + if ( ( typeToBitArray[ node.nodeType ] & nodeType ) && + filter( node ) ) { + this.currentNode = node; + return node; + } + current = node; + } +}; + +// Previous node in post-order. +TreeWalker.prototype.previousPONode = function () { + var current = this.currentNode, + root = this.root, + nodeType = this.nodeType, + filter = this.filter, + node; + while ( true ) { + node = current.lastChild; + while ( !node && current ) { + if ( current === root ) { + break; + } + node = current.previousSibling; + if ( !node ) { current = current.parentNode; } + } + if ( !node ) { + return null; + } + if ( ( typeToBitArray[ node.nodeType ] & nodeType ) && + filter( node ) ) { + this.currentNode = node; + return node; + } + current = node; + } +}; + +var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|EL|FN)|EM|FONT|HR|I(?:FRAME|MG|NPUT|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:AMP|MALL|PAN|TR(?:IKE|ONG)|U[BP])?|TIME|U|VAR|WBR)$/; + +var leafNodeNames = { + BR: 1, + HR: 1, + IFRAME: 1, + IMG: 1, + INPUT: 1 +}; + +function every ( nodeList, fn ) { + var l = nodeList.length; + while ( l-- ) { + if ( !fn( nodeList[l] ) ) { + return false; + } + } + return true; +} + +// --- + +var UNKNOWN = 0; +var INLINE = 1; +var BLOCK = 2; +var CONTAINER = 3; + +var nodeCategoryCache = canWeakMap ? new WeakMap() : null; + +function isLeaf ( node ) { + return node.nodeType === ELEMENT_NODE && !!leafNodeNames[ node.nodeName ]; +} +function getNodeCategory ( node ) { + switch ( node.nodeType ) { + case TEXT_NODE: + return INLINE; + case ELEMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + if ( canWeakMap && nodeCategoryCache.has( node ) ) { + return nodeCategoryCache.get( node ); + } + break; + default: + return UNKNOWN; + } + + var nodeCategory; + if ( !every( node.childNodes, isInline ) ) { + // Malformed HTML can have block tags inside inline tags. Need to treat + // these as containers rather than inline. See #239. + nodeCategory = CONTAINER; + } else if ( inlineNodeNames.test( node.nodeName ) ) { + nodeCategory = INLINE; + } else { + nodeCategory = BLOCK; + } + if ( canWeakMap ) { + nodeCategoryCache.set( node, nodeCategory ); + } + return nodeCategory; +} +function isInline ( node ) { + return getNodeCategory( node ) === INLINE; +} +function isBlock ( node ) { + return getNodeCategory( node ) === BLOCK; +} +function isContainer ( node ) { + return getNodeCategory( node ) === CONTAINER; +} + +function getBlockWalker ( node, root ) { + var walker = new TreeWalker( root, SHOW_ELEMENT, isBlock ); + walker.currentNode = node; + return walker; +} +function getPreviousBlock ( node, root ) { + node = getBlockWalker( node, root ).previousNode(); + return node !== root ? node : null; +} +function getNextBlock ( node, root ) { + node = getBlockWalker( node, root ).nextNode(); + return node !== root ? node : null; +} + +function isEmptyBlock ( block ) { + return !block.textContent && !block.querySelector( 'IMG' ); +} + +function areAlike ( node, node2 ) { + return !isLeaf( node ) && ( + node.nodeType === node2.nodeType && + node.nodeName === node2.nodeName && + node.nodeName !== 'A' && + node.className === node2.className && + ( ( !node.style && !node2.style ) || + node.style.cssText === node2.style.cssText ) + ); +} +function hasTagAttributes ( node, tag, attributes ) { + if ( node.nodeName !== tag ) { + return false; + } + for ( var attr in attributes ) { + if ( node.getAttribute( attr ) !== attributes[ attr ] ) { + return false; + } + } + return true; +} +function getNearest ( node, root, tag, attributes ) { + while ( node && node !== root ) { + if ( hasTagAttributes( node, tag, attributes ) ) { + return node; + } + node = node.parentNode; + } + return null; +} +function isOrContains ( parent, node ) { + while ( node ) { + if ( node === parent ) { + return true; + } + node = node.parentNode; + } + return false; +} + +function getPath ( node, root, config ) { + var path = ''; + var id, className, classNames, dir, styleNames; + if ( node && node !== root ) { + path = getPath( node.parentNode, root, config ); + if ( node.nodeType === ELEMENT_NODE ) { + path += ( path ? '>' : '' ) + node.nodeName; + if ( id = node.id ) { + path += '#' + id; + } + if ( className = node.className.trim() ) { + classNames = className.split( /\s\s*/ ); + classNames.sort(); + path += '.'; + path += classNames.join( '.' ); + } + if ( dir = node.dir ) { + path += '[dir=' + dir + ']'; + } + if ( classNames ) { + styleNames = config.classNames; + if ( indexOf.call( classNames, styleNames.highlight ) > -1 ) { + path += '[backgroundColor=' + + node.style.backgroundColor.replace( / /g,'' ) + ']'; + } + if ( indexOf.call( classNames, styleNames.colour ) > -1 ) { + path += '[color=' + + node.style.color.replace( / /g,'' ) + ']'; + } + if ( indexOf.call( classNames, styleNames.fontFamily ) > -1 ) { + path += '[fontFamily=' + + node.style.fontFamily.replace( / /g,'' ) + ']'; + } + if ( indexOf.call( classNames, styleNames.fontSize ) > -1 ) { + path += '[fontSize=' + node.style.fontSize + ']'; + } + } + } + } + return path; +} + +function getLength ( node ) { + var nodeType = node.nodeType; + return nodeType === ELEMENT_NODE || nodeType === DOCUMENT_FRAGMENT_NODE ? + node.childNodes.length : node.length || 0; +} + +function detach ( node ) { + var parent = node.parentNode; + if ( parent ) { + parent.removeChild( node ); + } + return node; +} +function replaceWith ( node, node2 ) { + var parent = node.parentNode; + if ( parent ) { + parent.replaceChild( node2, node ); + } +} +function empty ( node ) { + var frag = node.ownerDocument.createDocumentFragment(), + childNodes = node.childNodes, + l = childNodes ? childNodes.length : 0; + while ( l-- ) { + frag.appendChild( node.firstChild ); + } + return frag; +} + +function createElement ( doc, tag, props, children ) { + var el = doc.createElement( tag ), + attr, value, i, l; + if ( props instanceof Array ) { + children = props; + props = null; + } + if ( props ) { + for ( attr in props ) { + value = props[ attr ]; + if ( value !== undefined ) { + el.setAttribute( attr, value ); + } + } + } + if ( children ) { + for ( i = 0, l = children.length; i < l; i += 1 ) { + el.appendChild( children[i] ); + } + } + return el; +} + +function fixCursor ( node, root ) { + // In Webkit and Gecko, block level elements are collapsed and + // unfocusable if they have no content. To remedy this, a
must be + // inserted. In Opera and IE, we just need a textnode in order for the + // cursor to appear. + var self = root.__squire__; + var doc = node.ownerDocument; + var originalNode = node; + var fixer, child; + + if ( node === root ) { + if ( !( child = node.firstChild ) || child.nodeName === 'BR' ) { + fixer = self.createDefaultBlock(); + if ( child ) { + node.replaceChild( fixer, child ); + } + else { + node.appendChild( fixer ); + } + node = fixer; + fixer = null; + } + } + + if ( node.nodeType === TEXT_NODE ) { + return originalNode; + } + + if ( isInline( node ) ) { + child = node.firstChild; + while ( cantFocusEmptyTextNodes && child && + child.nodeType === TEXT_NODE && !child.data ) { + node.removeChild( child ); + child = node.firstChild; + } + if ( !child ) { + if ( cantFocusEmptyTextNodes ) { + fixer = doc.createTextNode( ZWS ); + self._didAddZWS(); + } else { + fixer = doc.createTextNode( '' ); + } + } + } else if ( !node.querySelector( 'BR' ) ) { + fixer = createElement( doc, 'BR' ); + while ( ( child = node.lastElementChild ) && !isInline( child ) ) { + node = child; + } + } + if ( fixer ) { + try { + node.appendChild( fixer ); + } catch ( error ) { + self.didError({ + name: 'Squire: fixCursor – ' + error, + message: 'Parent: ' + node.nodeName + '/' + node.innerHTML + + ' appendChild: ' + fixer.nodeName + }); + } + } + + return originalNode; +} + +// Recursively examine container nodes and wrap any inline children. +function fixContainer ( container, root ) { + var children = container.childNodes; + var doc = container.ownerDocument; + var wrapper = null; + var i, l, child, isBR; + + for ( i = 0, l = children.length; i < l; i += 1 ) { + child = children[i]; + isBR = child.nodeName === 'BR'; + if ( !isBR && isInline( child ) ) { + if ( !wrapper ) { + wrapper = createElement( doc, 'div' ); + } + wrapper.appendChild( child ); + i -= 1; + l -= 1; + } else if ( isBR || wrapper ) { + if ( !wrapper ) { + wrapper = createElement( doc, 'div' ); + } + fixCursor( wrapper, root ); + if ( isBR ) { + container.replaceChild( wrapper, child ); + } else { + container.insertBefore( wrapper, child ); + i += 1; + l += 1; + } + wrapper = null; + } + if ( isContainer( child ) ) { + fixContainer( child, root ); + } + } + if ( wrapper ) { + container.appendChild( fixCursor( wrapper, root ) ); + } + return container; +} + +function split ( node, offset, stopNode, root ) { + var nodeType = node.nodeType, + parent, clone, next; + if ( nodeType === TEXT_NODE && node !== stopNode ) { + return split( + node.parentNode, node.splitText( offset ), stopNode, root ); + } + if ( nodeType === ELEMENT_NODE ) { + if ( typeof( offset ) === 'number' ) { + offset = offset < node.childNodes.length ? + node.childNodes[ offset ] : null; + } + if ( node === stopNode ) { + return offset; + } + + // Clone node without children + parent = node.parentNode; + clone = node.cloneNode( false ); + + // Add right-hand siblings to the clone + while ( offset ) { + next = offset.nextSibling; + clone.appendChild( offset ); + offset = next; + } + + // Maintain li numbering if inside a quote. + if ( node.nodeName === 'OL' && + getNearest( node, root, 'BLOCKQUOTE' ) ) { + clone.start = ( +node.start || 1 ) + node.childNodes.length - 1; + } + + // DO NOT NORMALISE. This may undo the fixCursor() call + // of a node lower down the tree! + + // We need something in the element in order for the cursor to appear. + fixCursor( node, root ); + fixCursor( clone, root ); + + // Inject clone after original node + if ( next = node.nextSibling ) { + parent.insertBefore( clone, next ); + } else { + parent.appendChild( clone ); + } + + // Keep on splitting up the tree + return split( parent, clone, stopNode, root ); + } + return offset; +} + +function _mergeInlines ( node, fakeRange ) { + var children = node.childNodes, + l = children.length, + frags = [], + child, prev, len; + while ( l-- ) { + child = children[l]; + prev = l && children[ l - 1 ]; + if ( l && isInline( child ) && areAlike( child, prev ) && + !leafNodeNames[ child.nodeName ] ) { + if ( fakeRange.startContainer === child ) { + fakeRange.startContainer = prev; + fakeRange.startOffset += getLength( prev ); + } + if ( fakeRange.endContainer === child ) { + fakeRange.endContainer = prev; + fakeRange.endOffset += getLength( prev ); + } + if ( fakeRange.startContainer === node ) { + if ( fakeRange.startOffset > l ) { + fakeRange.startOffset -= 1; + } + else if ( fakeRange.startOffset === l ) { + fakeRange.startContainer = prev; + fakeRange.startOffset = getLength( prev ); + } + } + if ( fakeRange.endContainer === node ) { + if ( fakeRange.endOffset > l ) { + fakeRange.endOffset -= 1; + } + else if ( fakeRange.endOffset === l ) { + fakeRange.endContainer = prev; + fakeRange.endOffset = getLength( prev ); + } + } + detach( child ); + if ( child.nodeType === TEXT_NODE ) { + prev.appendData( child.data ); + } + else { + frags.push( empty( child ) ); + } + } + else if ( child.nodeType === ELEMENT_NODE ) { + len = frags.length; + while ( len-- ) { + child.appendChild( frags.pop() ); + } + _mergeInlines( child, fakeRange ); + } + } +} + +function mergeInlines ( node, range ) { + if ( node.nodeType === TEXT_NODE ) { + node = node.parentNode; + } + if ( node.nodeType === ELEMENT_NODE ) { + var fakeRange = { + startContainer: range.startContainer, + startOffset: range.startOffset, + endContainer: range.endContainer, + endOffset: range.endOffset + }; + _mergeInlines( node, fakeRange ); + range.setStart( fakeRange.startContainer, fakeRange.startOffset ); + range.setEnd( fakeRange.endContainer, fakeRange.endOffset ); + } +} + +function mergeWithBlock ( block, next, range, root ) { + var container = next; + var parent, last, offset; + while ( ( parent = container.parentNode ) && + parent !== root && + parent.nodeType === ELEMENT_NODE && + parent.childNodes.length === 1 ) { + container = parent; + } + detach( container ); + + offset = block.childNodes.length; + + // Remove extra
fixer if present. + last = block.lastChild; + if ( last && last.nodeName === 'BR' ) { + block.removeChild( last ); + offset -= 1; + } + + block.appendChild( empty( next ) ); + + range.setStart( block, offset ); + range.collapse( true ); + mergeInlines( block, range ); +} + +function mergeContainers ( node, root ) { + var prev = node.previousSibling, + first = node.firstChild, + doc = node.ownerDocument, + isListItem = ( node.nodeName === 'LI' ), + needsFix, block; + + // Do not merge LIs, unless it only contains a UL + if ( isListItem && ( !first || !/^[OU]L$/.test( first.nodeName ) ) ) { + return; + } + + if ( prev && areAlike( prev, node ) ) { + if ( !isContainer( prev ) ) { + if ( isListItem ) { + block = createElement( doc, 'DIV' ); + block.appendChild( empty( prev ) ); + prev.appendChild( block ); + } else { + return; + } + } + detach( node ); + needsFix = !isContainer( node ); + prev.appendChild( empty( node ) ); + if ( needsFix ) { + fixContainer( prev, root ); + } + if ( first ) { + mergeContainers( first, root ); + } + } else if ( isListItem ) { + prev = createElement( doc, 'DIV' ); + node.insertBefore( prev, first ); + fixCursor( prev, root ); + } +} + +var getNodeBefore = function ( node, offset ) { + var children = node.childNodes; + while ( offset && node.nodeType === ELEMENT_NODE ) { + node = children[ offset - 1 ]; + children = node.childNodes; + offset = children.length; + } + return node; +}; + +var getNodeAfter = function ( node, offset ) { + if ( node.nodeType === ELEMENT_NODE ) { + var children = node.childNodes; + if ( offset < children.length ) { + node = children[ offset ]; + } else { + while ( node && !node.nextSibling ) { + node = node.parentNode; + } + if ( node ) { node = node.nextSibling; } + } + } + return node; +}; + +// --- + +var insertNodeInRange = function ( range, node ) { + // Insert at start. + var startContainer = range.startContainer, + startOffset = range.startOffset, + endContainer = range.endContainer, + endOffset = range.endOffset, + parent, children, childCount, afterSplit; + + // If part way through a text node, split it. + if ( startContainer.nodeType === TEXT_NODE ) { + parent = startContainer.parentNode; + children = parent.childNodes; + if ( startOffset === startContainer.length ) { + startOffset = indexOf.call( children, startContainer ) + 1; + if ( range.collapsed ) { + endContainer = parent; + endOffset = startOffset; + } + } else { + if ( startOffset ) { + afterSplit = startContainer.splitText( startOffset ); + if ( endContainer === startContainer ) { + endOffset -= startOffset; + endContainer = afterSplit; + } + else if ( endContainer === parent ) { + endOffset += 1; + } + startContainer = afterSplit; + } + startOffset = indexOf.call( children, startContainer ); + } + startContainer = parent; + } else { + children = startContainer.childNodes; + } + + childCount = children.length; + + if ( startOffset === childCount ) { + startContainer.appendChild( node ); + } else { + startContainer.insertBefore( node, children[ startOffset ] ); + } + + if ( startContainer === endContainer ) { + endOffset += children.length - childCount; + } + + range.setStart( startContainer, startOffset ); + range.setEnd( endContainer, endOffset ); +}; + +var extractContentsOfRange = function ( range, common, root ) { + var startContainer = range.startContainer, + startOffset = range.startOffset, + endContainer = range.endContainer, + endOffset = range.endOffset; + + if ( !common ) { + common = range.commonAncestorContainer; + } + + if ( common.nodeType === TEXT_NODE ) { + common = common.parentNode; + } + + var endNode = split( endContainer, endOffset, common, root ), + startNode = split( startContainer, startOffset, common, root ), + frag = common.ownerDocument.createDocumentFragment(), + next, before, after, beforeText, afterText; + + // End node will be null if at end of child nodes list. + while ( startNode !== endNode ) { + next = startNode.nextSibling; + frag.appendChild( startNode ); + startNode = next; + } + + startContainer = common; + startOffset = endNode ? + indexOf.call( common.childNodes, endNode ) : + common.childNodes.length; + + // Merge text nodes if adjacent. IE10 in particular will not focus + // between two text nodes + after = common.childNodes[ startOffset ]; + before = after && after.previousSibling; + if ( before && + before.nodeType === TEXT_NODE && + after.nodeType === TEXT_NODE ) { + startContainer = before; + startOffset = before.length; + beforeText = before.data; + afterText = after.data; + + // If we now have two adjacent spaces, the second one needs to become + // a nbsp, otherwise the browser will swallow it due to HTML whitespace + // collapsing. + if ( beforeText.charAt( beforeText.length - 1 ) === ' ' && + afterText.charAt( 0 ) === ' ' ) { + afterText = ' ' + afterText.slice( 1 ); // nbsp + } + before.appendData( afterText ); + detach( after ); + } + + range.setStart( startContainer, startOffset ); + range.collapse( true ); + + fixCursor( common, root ); + + return frag; +}; + +var deleteContentsOfRange = function ( range, root ) { + var startBlock = getStartBlockOfRange( range, root ); + var endBlock = getEndBlockOfRange( range, root ); + var needsMerge = ( startBlock !== endBlock ); + var frag, child; + + // Move boundaries up as much as possible without exiting block, + // to reduce need to split. + moveRangeBoundariesDownTree( range ); + moveRangeBoundariesUpTree( range, startBlock, endBlock, root ); + + // Remove selected range + frag = extractContentsOfRange( range, null, root ); + + // Move boundaries back down tree as far as possible. + moveRangeBoundariesDownTree( range ); + + // If we split into two different blocks, merge the blocks. + if ( needsMerge ) { + // endBlock will have been split, so need to refetch + endBlock = getEndBlockOfRange( range, root ); + if ( startBlock && endBlock && startBlock !== endBlock ) { + mergeWithBlock( startBlock, endBlock, range, root ); + } + } + + // Ensure block has necessary children + if ( startBlock ) { + fixCursor( startBlock, root ); + } + + // Ensure root has a block-level element in it. + child = root.firstChild; + if ( !child || child.nodeName === 'BR' ) { + fixCursor( root, root ); + range.selectNodeContents( root.firstChild ); + } else { + range.collapse( true ); + } + return frag; +}; + +// --- + +// Contents of range will be deleted. +// After method, range will be around inserted content +var insertTreeFragmentIntoRange = function ( range, frag, root ) { + var firstInFragIsInline = frag.firstChild && isInline( frag.firstChild ); + var node, block, blockContentsAfterSplit, stopPoint, container, offset; + var replaceBlock, firstBlockInFrag, nodeAfterSplit, nodeBeforeSplit; + var tempRange; + + // Fixup content: ensure no top-level inline, and add cursor fix elements. + fixContainer( frag, root ); + node = frag; + while ( ( node = getNextBlock( node, root ) ) ) { + fixCursor( node, root ); + } + + // Delete any selected content. + if ( !range.collapsed ) { + deleteContentsOfRange( range, root ); + } + + // Move range down into text nodes. + moveRangeBoundariesDownTree( range ); + range.collapse( false ); // collapse to end + + // Where will we split up to? First blockquote parent, otherwise root. + stopPoint = getNearest( range.endContainer, root, 'BLOCKQUOTE' ) || root; + + // Merge the contents of the first block in the frag with the focused block. + // If there are contents in the block after the focus point, collect this + // up to insert in the last block later. This preserves the style that was + // present in this bit of the page. + // + // If the block being inserted into is empty though, replace it instead of + // merging if the fragment had block contents. + // e.g.

Foo

+ // This seems a reasonable approximation of user intent. + + block = getStartBlockOfRange( range, root ); + firstBlockInFrag = getNextBlock( frag, frag ); + replaceBlock = !firstInFragIsInline && !!block && isEmptyBlock( block ); + if ( block && firstBlockInFrag && !replaceBlock && + // Don't merge table cells or PRE elements into block + !getNearest( firstBlockInFrag, frag, 'PRE' ) && + !getNearest( firstBlockInFrag, frag, 'TABLE' ) ) { + moveRangeBoundariesUpTree( range, block, block, root ); + range.collapse( true ); // collapse to start + container = range.endContainer; + offset = range.endOffset; + // Remove trailing
– we don't want this considered content to be + // inserted again later + cleanupBRs( block, root, false ); + if ( isInline( container ) ) { + // Split up to block parent. + nodeAfterSplit = split( + container, offset, getPreviousBlock( container, root ), root ); + container = nodeAfterSplit.parentNode; + offset = indexOf.call( container.childNodes, nodeAfterSplit ); + } + if ( /*isBlock( container ) && */offset !== getLength( container ) ) { + // Collect any inline contents of the block after the range point + blockContentsAfterSplit = + root.ownerDocument.createDocumentFragment(); + while ( ( node = container.childNodes[ offset ] ) ) { + blockContentsAfterSplit.appendChild( node ); + } + } + // And merge the first block in. + mergeWithBlock( container, firstBlockInFrag, range, root ); + + // And where we will insert + offset = indexOf.call( container.parentNode.childNodes, container ) + 1; + container = container.parentNode; + range.setEnd( container, offset ); + } + + // Is there still any content in the fragment? + if ( getLength( frag ) ) { + if ( replaceBlock ) { + range.setEndBefore( block ); + range.collapse( false ); + detach( block ); + } + moveRangeBoundariesUpTree( range, stopPoint, stopPoint, root ); + // Now split after block up to blockquote (if a parent) or root + nodeAfterSplit = split( + range.endContainer, range.endOffset, stopPoint, root ); + nodeBeforeSplit = nodeAfterSplit ? + nodeAfterSplit.previousSibling : + stopPoint.lastChild; + stopPoint.insertBefore( frag, nodeAfterSplit ); + if ( nodeAfterSplit ) { + range.setEndBefore( nodeAfterSplit ); + } else { + range.setEnd( stopPoint, getLength( stopPoint ) ); + } + block = getEndBlockOfRange( range, root ); + + // Get a reference that won't be invalidated if we merge containers. + moveRangeBoundariesDownTree( range ); + container = range.endContainer; + offset = range.endOffset; + + // Merge inserted containers with edges of split + if ( nodeAfterSplit && isContainer( nodeAfterSplit ) ) { + mergeContainers( nodeAfterSplit, root ); + } + nodeAfterSplit = nodeBeforeSplit && nodeBeforeSplit.nextSibling; + if ( nodeAfterSplit && isContainer( nodeAfterSplit ) ) { + mergeContainers( nodeAfterSplit, root ); + } + range.setEnd( container, offset ); + } + + // Insert inline content saved from before. + if ( blockContentsAfterSplit ) { + tempRange = range.cloneRange(); + mergeWithBlock( block, blockContentsAfterSplit, tempRange, root ); + range.setEnd( tempRange.endContainer, tempRange.endOffset ); + } + moveRangeBoundariesDownTree( range ); +}; + +// --- + +var isNodeContainedInRange = function ( range, node, partial ) { + var nodeRange = node.ownerDocument.createRange(); + + nodeRange.selectNode( node ); + + if ( partial ) { + // Node must not finish before range starts or start after range + // finishes. + var nodeEndBeforeStart = ( range.compareBoundaryPoints( + END_TO_START, nodeRange ) > -1 ), + nodeStartAfterEnd = ( range.compareBoundaryPoints( + START_TO_END, nodeRange ) < 1 ); + return ( !nodeEndBeforeStart && !nodeStartAfterEnd ); + } + else { + // Node must start after range starts and finish before range + // finishes + var nodeStartAfterStart = ( range.compareBoundaryPoints( + START_TO_START, nodeRange ) < 1 ), + nodeEndBeforeEnd = ( range.compareBoundaryPoints( + END_TO_END, nodeRange ) > -1 ); + return ( nodeStartAfterStart && nodeEndBeforeEnd ); + } +}; + +var moveRangeBoundariesDownTree = function ( range ) { + var startContainer = range.startContainer, + startOffset = range.startOffset, + endContainer = range.endContainer, + endOffset = range.endOffset, + maySkipBR = true, + child; + + while ( startContainer.nodeType !== TEXT_NODE ) { + child = startContainer.childNodes[ startOffset ]; + if ( !child || isLeaf( child ) ) { + break; + } + startContainer = child; + startOffset = 0; + } + if ( endOffset ) { + while ( endContainer.nodeType !== TEXT_NODE ) { + child = endContainer.childNodes[ endOffset - 1 ]; + if ( !child || isLeaf( child ) ) { + if ( maySkipBR && child && child.nodeName === 'BR' ) { + endOffset -= 1; + maySkipBR = false; + continue; + } + break; + } + endContainer = child; + endOffset = getLength( endContainer ); + } + } else { + while ( endContainer.nodeType !== TEXT_NODE ) { + child = endContainer.firstChild; + if ( !child || isLeaf( child ) ) { + break; + } + endContainer = child; + } + } + + // If collapsed, this algorithm finds the nearest text node positions + // *outside* the range rather than inside, but also it flips which is + // assigned to which. + if ( range.collapsed ) { + range.setStart( endContainer, endOffset ); + range.setEnd( startContainer, startOffset ); + } else { + range.setStart( startContainer, startOffset ); + range.setEnd( endContainer, endOffset ); + } +}; + +var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) { + var startContainer = range.startContainer; + var startOffset = range.startOffset; + var endContainer = range.endContainer; + var endOffset = range.endOffset; + var maySkipBR = true; + var parent; + + if ( !startMax ) { + startMax = range.commonAncestorContainer; + } + if ( !endMax ) { + endMax = startMax; + } + + while ( !startOffset && + startContainer !== startMax && + startContainer !== root ) { + parent = startContainer.parentNode; + startOffset = indexOf.call( parent.childNodes, startContainer ); + startContainer = parent; + } + + while ( true ) { + if ( endContainer === endMax || endContainer === root ) { + break; + } + if ( maySkipBR && + endContainer.nodeType !== TEXT_NODE && + endContainer.childNodes[ endOffset ] && + endContainer.childNodes[ endOffset ].nodeName === 'BR' ) { + endOffset += 1; + maySkipBR = false; + } + if ( endOffset !== getLength( endContainer ) ) { + break; + } + parent = endContainer.parentNode; + endOffset = indexOf.call( parent.childNodes, endContainer ) + 1; + endContainer = parent; + } + + range.setStart( startContainer, startOffset ); + range.setEnd( endContainer, endOffset ); +}; + +var moveRangeBoundaryOutOf = function ( range, nodeName, root ) { + var parent = getNearest( range.endContainer, root, 'A' ); + if ( parent ) { + var clone = range.cloneRange(); + parent = parent.parentNode; + moveRangeBoundariesUpTree( clone, parent, parent, root ); + if ( clone.endContainer === parent ) { + range.setStart( clone.endContainer, clone.endOffset ); + range.setEnd( clone.endContainer, clone.endOffset ); + } + } + return range; +}; + +// Returns the first block at least partially contained by the range, +// or null if no block is contained by the range. +var getStartBlockOfRange = function ( range, root ) { + var container = range.startContainer, + block; + + // If inline, get the containing block. + if ( isInline( container ) ) { + block = getPreviousBlock( container, root ); + } else if ( container !== root && isBlock( container ) ) { + block = container; + } else { + block = getNodeBefore( container, range.startOffset ); + block = getNextBlock( block, root ); + } + // Check the block actually intersects the range + return block && isNodeContainedInRange( range, block, true ) ? block : null; +}; + +// Returns the last block at least partially contained by the range, +// or null if no block is contained by the range. +var getEndBlockOfRange = function ( range, root ) { + var container = range.endContainer, + block, child; + + // If inline, get the containing block. + if ( isInline( container ) ) { + block = getPreviousBlock( container, root ); + } else if ( container !== root && isBlock( container ) ) { + block = container; + } else { + block = getNodeAfter( container, range.endOffset ); + if ( !block || !isOrContains( root, block ) ) { + block = root; + while ( child = block.lastChild ) { + block = child; + } + } + block = getPreviousBlock( block, root ); + } + // Check the block actually intersects the range + return block && isNodeContainedInRange( range, block, true ) ? block : null; +}; + +var contentWalker = new TreeWalker( null, + SHOW_TEXT|SHOW_ELEMENT, + function ( node ) { + return node.nodeType === TEXT_NODE ? + notWS.test( node.data ) : + node.nodeName === 'IMG'; + } +); + +var rangeDoesStartAtBlockBoundary = function ( range, root ) { + var startContainer = range.startContainer; + var startOffset = range.startOffset; + var nodeAfterCursor; + + // If in the middle or end of a text node, we're not at the boundary. + contentWalker.root = null; + if ( startContainer.nodeType === TEXT_NODE ) { + if ( startOffset ) { + return false; + } + nodeAfterCursor = startContainer; + } else { + nodeAfterCursor = getNodeAfter( startContainer, startOffset ); + if ( nodeAfterCursor && !isOrContains( root, nodeAfterCursor ) ) { + nodeAfterCursor = null; + } + // The cursor was right at the end of the document + if ( !nodeAfterCursor ) { + nodeAfterCursor = getNodeBefore( startContainer, startOffset ); + if ( nodeAfterCursor.nodeType === TEXT_NODE && + nodeAfterCursor.length ) { + return false; + } + } + } + + // Otherwise, look for any previous content in the same block. + contentWalker.currentNode = nodeAfterCursor; + contentWalker.root = getStartBlockOfRange( range, root ); + + return !contentWalker.previousNode(); +}; + +var rangeDoesEndAtBlockBoundary = function ( range, root ) { + var endContainer = range.endContainer, + endOffset = range.endOffset, + length; + + // If in a text node with content, and not at the end, we're not + // at the boundary + contentWalker.root = null; + if ( endContainer.nodeType === TEXT_NODE ) { + length = endContainer.data.length; + if ( length && endOffset < length ) { + return false; + } + contentWalker.currentNode = endContainer; + } else { + contentWalker.currentNode = getNodeBefore( endContainer, endOffset ); + } + + // Otherwise, look for any further content in the same block. + contentWalker.root = getEndBlockOfRange( range, root ); + + return !contentWalker.nextNode(); +}; + +var expandRangeToBlockBoundaries = function ( range, root ) { + var start = getStartBlockOfRange( range, root ), + end = getEndBlockOfRange( range, root ), + parent; + + if ( start && end ) { + parent = start.parentNode; + range.setStart( parent, indexOf.call( parent.childNodes, start ) ); + parent = end.parentNode; + range.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 ); + } +}; + +var keys = { + 8: 'backspace', + 9: 'tab', + 13: 'enter', + 32: 'space', + 33: 'pageup', + 34: 'pagedown', + 37: 'left', + 39: 'right', + 46: 'delete', + 191: '/', + 219: '[', + 220: '\\', + 221: ']' +}; + +// Ref: http://unixpapa.com/js/key.html +var onKey = function ( event ) { + var code = event.keyCode, + key = keys[ code ], + modifiers = '', + range = this.getSelection(); + + if ( event.defaultPrevented ) { + return; + } + + if ( !key ) { + key = String.fromCharCode( code ).toLowerCase(); + // Only reliable for letters and numbers + if ( !/^[A-Za-z0-9]$/.test( key ) ) { + key = ''; + } + } + + // Function keys + if ( 111 < code && code < 124 ) { + key = 'f' + ( code - 111 ); + } + + // We need to apply the backspace/delete handlers regardless of + // control key modifiers. + if ( key !== 'backspace' && key !== 'delete' ) { + if ( event.altKey ) { modifiers += 'alt-'; } + if ( event.ctrlKey ) { modifiers += 'ctrl-'; } + if ( event.metaKey ) { modifiers += 'meta-'; } + if ( event.shiftKey ) { modifiers += 'shift-'; } + } + // However, on Windows, shift-delete is apparently "cut" (WTF right?), so + // we want to let the browser handle shift-delete in this situation. + if ( isWin && event.shiftKey && key === 'delete' ) { + modifiers += 'shift-'; + } + + key = modifiers + key; + + if ( this._keyHandlers[ key ] ) { + this._keyHandlers[ key ]( this, event, range ); + // !event.isComposing stops us from blatting Kana-Kanji conversion in Safari + } else if ( !range.collapsed && !event.isComposing && + !event.ctrlKey && !event.metaKey && + ( event.key || key ).length === 1 ) { + // Record undo checkpoint. + this.saveUndoState( range ); + // Delete the selection + deleteContentsOfRange( range, this._root ); + this._ensureBottomLine(); + this.setSelection( range ); + this._updatePath( range, true ); + } +}; + +var mapKeyTo = function ( method ) { + return function ( self, event ) { + event.preventDefault(); + self[ method ](); + }; +}; + +var mapKeyToFormat = function ( tag, remove ) { + remove = remove || null; + return function ( self, event ) { + event.preventDefault(); + var range = self.getSelection(); + if ( self.hasFormat( tag, null, range ) ) { + self.changeFormat( null, { tag: tag }, range ); + } else { + self.changeFormat( { tag: tag }, remove, range ); + } + }; +}; + +// If you delete the content inside a span with a font styling, Webkit will +// replace it with a tag (!). If you delete all the text inside a +// link in Opera, it won't delete the link. Let's make things consistent. If +// you delete all text inside an inline tag, remove the inline tag. +var afterDelete = function ( self, range ) { + try { + if ( !range ) { range = self.getSelection(); } + var node = range.startContainer, + parent; + // Climb the tree from the focus point while we are inside an empty + // inline element + if ( node.nodeType === TEXT_NODE ) { + node = node.parentNode; + } + parent = node; + while ( isInline( parent ) && + ( !parent.textContent || parent.textContent === ZWS ) ) { + node = parent; + parent = node.parentNode; + } + // If focused in empty inline element + if ( node !== parent ) { + // Move focus to just before empty inline(s) + range.setStart( parent, + indexOf.call( parent.childNodes, node ) ); + range.collapse( true ); + // Remove empty inline(s) + parent.removeChild( node ); + // Fix cursor in block + if ( !isBlock( parent ) ) { + parent = getPreviousBlock( parent, self._root ); + } + fixCursor( parent, self._root ); + // Move cursor into text node + moveRangeBoundariesDownTree( range ); + } + // If you delete the last character in the sole
in Chrome, + // it removes the div and replaces it with just a
inside the + // root. Detach the
; the _ensureBottomLine call will insert a new + // block. + if ( node === self._root && + ( node = node.firstChild ) && node.nodeName === 'BR' ) { + detach( node ); + } + self._ensureBottomLine(); + self.setSelection( range ); + self._updatePath( range, true ); + } catch ( error ) { + self.didError( error ); + } +}; + +var detachUneditableNode = function ( node, root ) { + var parent; + while (( parent = node.parentNode )) { + if ( parent === root || parent.isContentEditable ) { + break; + } + node = parent; + } + detach( node ); +}; + +var handleEnter = function ( self, shiftKey, range ) { + var root = self._root; + var block, parent, node, offset, nodeAfterSplit; + + // Save undo checkpoint and add any links in the preceding section. + // Remove any zws so we don't think there's content in an empty + // block. + self._recordUndoState( range ); + if ( self._config.addLinks ) { + addLinks( range.startContainer, root, self ); + } + self._removeZWS(); + self._getRangeAndRemoveBookmark( range ); + + // Selected text is overwritten, therefore delete the contents + // to collapse selection. + if ( !range.collapsed ) { + deleteContentsOfRange( range, root ); + } + + block = getStartBlockOfRange( range, root ); + + // Inside a PRE, insert literal newline, unless on blank line. + if ( block && ( parent = getNearest( block, root, 'PRE' ) ) ) { + moveRangeBoundariesDownTree( range ); + node = range.startContainer; + offset = range.startOffset; + if ( node.nodeType !== TEXT_NODE ) { + node = self._doc.createTextNode( '' ); + parent.insertBefore( node, parent.firstChild ); + } + // If blank line: split and insert default block + if ( !shiftKey && + ( node.data.charAt( offset - 1 ) === '\n' || + rangeDoesStartAtBlockBoundary( range, root ) ) && + ( node.data.charAt( offset ) === '\n' || + rangeDoesEndAtBlockBoundary( range, root ) ) ) { + node.deleteData( offset && offset - 1, offset ? 2 : 1 ); + nodeAfterSplit = + split( node, offset && offset - 1, root, root ); + node = nodeAfterSplit.previousSibling; + if ( !node.textContent ) { + detach( node ); + } + node = self.createDefaultBlock(); + nodeAfterSplit.parentNode.insertBefore( node, nodeAfterSplit ); + if ( !nodeAfterSplit.textContent ) { + detach( nodeAfterSplit ); + } + range.setStart( node, 0 ); + } else { + node.insertData( offset, '\n' ); + fixCursor( parent, root ); + // Firefox bug: if you set the selection in the text node after + // the new line, it draws the cursor before the line break still + // but if you set the selection to the equivalent position + // in the parent, it works. + if ( node.length === offset + 1 ) { + range.setStartAfter( node ); + } else { + range.setStart( node, offset + 1 ); + } + } + range.collapse( true ); + self.setSelection( range ); + self._updatePath( range, true ); + self._docWasChanged(); + return; + } + + // If this is a malformed bit of document or in a table; + // just play it safe and insert a
. + if ( !block || shiftKey || /^T[HD]$/.test( block.nodeName ) ) { + // If inside an
, move focus out + moveRangeBoundaryOutOf( range, 'A', root ); + insertNodeInRange( range, self.createElement( 'BR' ) ); + range.collapse( false ); + self.setSelection( range ); + self._updatePath( range, true ); + return; + } + + // If in a list, we'll split the LI instead. + if ( parent = getNearest( block, root, 'LI' ) ) { + block = parent; + } + + if ( isEmptyBlock( block ) ) { + // Break list + if ( getNearest( block, root, 'UL' ) || + getNearest( block, root, 'OL' ) ) { + return self.decreaseListLevel( range ); + } + // Break blockquote + else if ( getNearest( block, root, 'BLOCKQUOTE' ) ) { + return self.modifyBlocks( removeBlockQuote, range ); + } + } + + // Otherwise, split at cursor point. + nodeAfterSplit = splitBlock( self, block, + range.startContainer, range.startOffset ); + + // Clean up any empty inlines if we hit enter at the beginning of the + // block + removeZWS( block ); + removeEmptyInlines( block ); + fixCursor( block, root ); + + // Focus cursor + // If there's a / etc. at the beginning of the split + // make sure we focus inside it. + while ( nodeAfterSplit.nodeType === ELEMENT_NODE ) { + var child = nodeAfterSplit.firstChild, + next; + + // Don't continue links over a block break; unlikely to be the + // desired outcome. + if ( nodeAfterSplit.nodeName === 'A' && + ( !nodeAfterSplit.textContent || + nodeAfterSplit.textContent === ZWS ) ) { + child = self._doc.createTextNode( '' ); + replaceWith( nodeAfterSplit, child ); + nodeAfterSplit = child; + break; + } + + while ( child && child.nodeType === TEXT_NODE && !child.data ) { + next = child.nextSibling; + if ( !next || next.nodeName === 'BR' ) { + break; + } + detach( child ); + child = next; + } + + // 'BR's essentially don't count; they're a browser hack. + // If you try to select the contents of a 'BR', FF will not let + // you type anything! + if ( !child || child.nodeName === 'BR' || + child.nodeType === TEXT_NODE ) { + break; + } + nodeAfterSplit = child; + } + range = self.createRange( nodeAfterSplit, 0 ); + self.setSelection( range ); + self._updatePath( range, true ); +}; + +var keyHandlers = { + // This song and dance is to force iOS to do enable the shift key + // automatically on enter. When you do the DOM split manipulation yourself, + // WebKit doesn't reset the IME state and so presents auto-complete options + // as though you were continuing to type on the previous line, and doesn't + // auto-enable the shift key. The old trick of blurring and focussing + // again no longer works in iOS 13, and I tried various execCommand options + // but they didn't seem to do anything. The only solution I've found is to + // let iOS handle the enter key, then after it's done that reset the HTML + // to what it was before and handle it properly in Squire; the IME state of + // course doesn't reset so you end up in the correct state! + enter: isIOS ? function ( self, event, range ) { + self._saveRangeToBookmark( range ); + var html = self._getHTML(); + var restoreAndDoEnter = function () { + self.removeEventListener( 'keyup', restoreAndDoEnter ); + self._setHTML( html ); + range = self._getRangeAndRemoveBookmark(); + // Ignore the shift key on iOS, as this is for auto-capitalisation. + handleEnter( self, false, range ); + }; + self.addEventListener( 'keyup', restoreAndDoEnter ); + } : function ( self, event, range ) { + event.preventDefault(); + handleEnter( self, event.shiftKey, range ); + }, + + 'shift-enter': function ( self, event, range ) { + return self._keyHandlers.enter( self, event, range ); + }, + + backspace: function ( self, event, range ) { + var root = self._root; + self._removeZWS(); + // Record undo checkpoint. + self.saveUndoState( range ); + // If not collapsed, delete contents + if ( !range.collapsed ) { + event.preventDefault(); + deleteContentsOfRange( range, root ); + afterDelete( self, range ); + } + // If at beginning of block, merge with previous + else if ( rangeDoesStartAtBlockBoundary( range, root ) ) { + event.preventDefault(); + var current = getStartBlockOfRange( range, root ); + var previous; + if ( !current ) { + return; + } + // In case inline data has somehow got between blocks. + fixContainer( current.parentNode, root ); + // Now get previous block + previous = getPreviousBlock( current, root ); + // Must not be at the very beginning of the text area. + if ( previous ) { + // If not editable, just delete whole block. + if ( !previous.isContentEditable ) { + detachUneditableNode( previous, root ); + return; + } + // Otherwise merge. + mergeWithBlock( previous, current, range, root ); + // If deleted line between containers, merge newly adjacent + // containers. + current = previous.parentNode; + while ( current !== root && !current.nextSibling ) { + current = current.parentNode; + } + if ( current !== root && ( current = current.nextSibling ) ) { + mergeContainers( current, root ); + } + self.setSelection( range ); + } + // If at very beginning of text area, allow backspace + // to break lists/blockquote. + else if ( current ) { + // Break list + if ( getNearest( current, root, 'UL' ) || + getNearest( current, root, 'OL' ) ) { + return self.decreaseListLevel( range ); + } + // Break blockquote + else if ( getNearest( current, root, 'BLOCKQUOTE' ) ) { + return self.modifyBlocks( decreaseBlockQuoteLevel, range ); + } + self.setSelection( range ); + self._updatePath( range, true ); + } + } + // Otherwise, leave to browser but check afterwards whether it has + // left behind an empty inline tag. + else { + self.setSelection( range ); + setTimeout( function () { afterDelete( self ); }, 0 ); + } + }, + 'delete': function ( self, event, range ) { + var root = self._root; + var current, next, originalRange, + cursorContainer, cursorOffset, nodeAfterCursor; + self._removeZWS(); + // Record undo checkpoint. + self.saveUndoState( range ); + // If not collapsed, delete contents + if ( !range.collapsed ) { + event.preventDefault(); + deleteContentsOfRange( range, root ); + afterDelete( self, range ); + } + // If at end of block, merge next into this block + else if ( rangeDoesEndAtBlockBoundary( range, root ) ) { + event.preventDefault(); + current = getStartBlockOfRange( range, root ); + if ( !current ) { + return; + } + // In case inline data has somehow got between blocks. + fixContainer( current.parentNode, root ); + // Now get next block + next = getNextBlock( current, root ); + // Must not be at the very end of the text area. + if ( next ) { + // If not editable, just delete whole block. + if ( !next.isContentEditable ) { + detachUneditableNode( next, root ); + return; + } + // Otherwise merge. + mergeWithBlock( current, next, range, root ); + // If deleted line between containers, merge newly adjacent + // containers. + next = current.parentNode; + while ( next !== root && !next.nextSibling ) { + next = next.parentNode; + } + if ( next !== root && ( next = next.nextSibling ) ) { + mergeContainers( next, root ); + } + self.setSelection( range ); + self._updatePath( range, true ); + } + } + // Otherwise, leave to browser but check afterwards whether it has + // left behind an empty inline tag. + else { + // But first check if the cursor is just before an IMG tag. If so, + // delete it ourselves, because the browser won't if it is not + // inline. + originalRange = range.cloneRange(); + moveRangeBoundariesUpTree( range, root, root, root ); + cursorContainer = range.endContainer; + cursorOffset = range.endOffset; + if ( cursorContainer.nodeType === ELEMENT_NODE ) { + nodeAfterCursor = cursorContainer.childNodes[ cursorOffset ]; + if ( nodeAfterCursor && nodeAfterCursor.nodeName === 'IMG' ) { + event.preventDefault(); + detach( nodeAfterCursor ); + moveRangeBoundariesDownTree( range ); + afterDelete( self, range ); + return; + } + } + self.setSelection( originalRange ); + setTimeout( function () { afterDelete( self ); }, 0 ); + } + }, + tab: function ( self, event, range ) { + var root = self._root; + var node, parent; + self._removeZWS(); + // If no selection and at start of block + if ( range.collapsed && rangeDoesStartAtBlockBoundary( range, root ) ) { + node = getStartBlockOfRange( range, root ); + // Iterate through the block's parents + while ( ( parent = node.parentNode ) ) { + // If we find a UL or OL (so are in a list, node must be an LI) + if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) { + // Then increase the list level + event.preventDefault(); + self.increaseListLevel( range ); + break; + } + node = parent; + } + } + }, + 'shift-tab': function ( self, event, range ) { + var root = self._root; + var node; + self._removeZWS(); + // If no selection and at start of block + if ( range.collapsed && rangeDoesStartAtBlockBoundary( range, root ) ) { + // Break list + node = range.startContainer; + if ( getNearest( node, root, 'UL' ) || + getNearest( node, root, 'OL' ) ) { + event.preventDefault(); + self.decreaseListLevel( range ); + } + } + }, + space: function ( self, _, range ) { + var node; + var root = self._root; + self._recordUndoState( range ); + if ( self._config.addLinks ) { + addLinks( range.startContainer, root, self ); + } + self._getRangeAndRemoveBookmark( range ); + + // If the cursor is at the end of a link (foo|) then move it + // outside of the link (foo|) so that the space is not part of + // the link text. + node = range.endContainer; + if ( range.collapsed && range.endOffset === getLength( node ) ) { + do { + if ( node.nodeName === 'A' ) { + range.setStartAfter( node ); + break; + } + } while ( !node.nextSibling && + ( node = node.parentNode ) && node !== root ); + } + // Delete the selection if not collapsed + if ( !range.collapsed ) { + deleteContentsOfRange( range, root ); + self._ensureBottomLine(); + self.setSelection( range ); + self._updatePath( range, true ); + } + + self.setSelection( range ); + }, + left: function ( self ) { + self._removeZWS(); + }, + right: function ( self ) { + self._removeZWS(); + } +}; + +// Firefox pre v29 incorrectly handles Cmd-left/Cmd-right on Mac: +// it goes back/forward in history! Override to do the right +// thing. +// https://bugzilla.mozilla.org/show_bug.cgi?id=289384 +if ( isMac && isGecko ) { + keyHandlers[ 'meta-left' ] = function ( self, event ) { + event.preventDefault(); + var sel = getWindowSelection( self ); + if ( sel && sel.modify ) { + sel.modify( 'move', 'backward', 'lineboundary' ); + } + }; + keyHandlers[ 'meta-right' ] = function ( self, event ) { + event.preventDefault(); + var sel = getWindowSelection( self ); + if ( sel && sel.modify ) { + sel.modify( 'move', 'forward', 'lineboundary' ); + } + }; +} + +// System standard for page up/down on Mac is to just scroll, not move the +// cursor. On Linux/Windows, it should move the cursor, but some browsers don't +// implement this natively. Override to support it. +if ( !isMac ) { + keyHandlers.pageup = function ( self ) { + self.moveCursorToStart(); + }; + keyHandlers.pagedown = function ( self ) { + self.moveCursorToEnd(); + }; +} + +const changeIndentationLevel = function ( methodIfInQuote, methodIfInList ) { + return function ( self, event ) { + event.preventDefault(); + var path = self.getPath(); + if ( /(?:^|>)BLOCKQUOTE/.test( path ) || + !/(?:^|>)[OU]L/.test( path ) ) { + self[ methodIfInQuote ](); + } else { + self[ methodIfInList ](); + } + }; +}; + +const toggleList = function ( listRegex, methodIfNotInList ) { + return function ( self, event ) { + event.preventDefault(); + var path = self.getPath(); + if ( !listRegex.test( path ) ) { + self[ methodIfNotInList ](); + } else { + self.removeList(); + } + }; +}; + +keyHandlers[ ctrlKey + 'b' ] = mapKeyToFormat( 'B' ); +keyHandlers[ ctrlKey + 'i' ] = mapKeyToFormat( 'I' ); +keyHandlers[ ctrlKey + 'u' ] = mapKeyToFormat( 'U' ); +keyHandlers[ ctrlKey + 'shift-7' ] = mapKeyToFormat( 'S' ); +keyHandlers[ ctrlKey + 'shift-5' ] = mapKeyToFormat( 'SUB', { tag: 'SUP' } ); +keyHandlers[ ctrlKey + 'shift-6' ] = mapKeyToFormat( 'SUP', { tag: 'SUB' } ); +keyHandlers[ ctrlKey + 'shift-8' ] = + toggleList( /(?:^|>)UL/, 'makeUnorderedList' ); +keyHandlers[ ctrlKey + 'shift-9' ] = + toggleList( /(?:^|>)OL/, 'makeOrderedList' ); +keyHandlers[ ctrlKey + '[' ] = + changeIndentationLevel( 'decreaseQuoteLevel', 'decreaseListLevel' ); +keyHandlers[ ctrlKey + ']' ] = + changeIndentationLevel( 'increaseQuoteLevel', 'increaseListLevel' ); +keyHandlers[ ctrlKey + 'd' ] = mapKeyTo( 'toggleCode' ); +keyHandlers[ ctrlKey + 'y' ] = mapKeyTo( 'redo' ); +keyHandlers[ ctrlKey + 'z' ] = mapKeyTo( 'undo' ); +keyHandlers[ ctrlKey + 'shift-z' ] = mapKeyTo( 'redo' ); + +var fontSizes = { + 1: 10, + 2: 13, + 3: 16, + 4: 18, + 5: 24, + 6: 32, + 7: 48 +}; + +var styleToSemantic = { + fontWeight: { + regexp: /^bold|^700/i, + replace: function ( doc ) { + return createElement( doc, 'B' ); + } + }, + fontStyle: { + regexp: /^italic/i, + replace: function ( doc ) { + return createElement( doc, 'I' ); + } + }, + fontFamily: { + regexp: notWS, + replace: function ( doc, classNames, family ) { + return createElement( doc, 'SPAN', { + 'class': classNames.fontFamily, + style: 'font-family:' + family + }); + } + }, + fontSize: { + regexp: notWS, + replace: function ( doc, classNames, size ) { + return createElement( doc, 'SPAN', { + 'class': classNames.fontSize, + style: 'font-size:' + size + }); + } + }, + textDecoration: { + regexp: /^underline/i, + replace: function ( doc ) { + return createElement( doc, 'U' ); + } + } +}; + +var replaceWithTag = function ( tag ) { + return function ( node, parent ) { + var el = createElement( node.ownerDocument, tag ); + var attributes = node.attributes; + var i, l, attribute; + for ( i = 0, l = attributes.length; i < l; i += 1 ) { + attribute = attributes[i]; + el.setAttribute( attribute.name, attribute.value ); + } + parent.replaceChild( el, node ); + el.appendChild( empty( node ) ); + return el; + }; +}; + +var replaceStyles = function ( node, parent, config ) { + var style = node.style; + var doc = node.ownerDocument; + var attr, converter, css, newTreeBottom, newTreeTop, el; + + for ( attr in styleToSemantic ) { + converter = styleToSemantic[ attr ]; + css = style[ attr ]; + if ( css && converter.regexp.test( css ) ) { + el = converter.replace( doc, config.classNames, css ); + if ( el.nodeName === node.nodeName && + el.className === node.className ) { + continue; + } + if ( !newTreeTop ) { + newTreeTop = el; + } + if ( newTreeBottom ) { + newTreeBottom.appendChild( el ); + } + newTreeBottom = el; + node.style[ attr ] = ''; + } + } + + if ( newTreeTop ) { + newTreeBottom.appendChild( empty( node ) ); + node.appendChild( newTreeTop ); + } + + return newTreeBottom || node; +}; + +var stylesRewriters = { + SPAN: replaceStyles, + STRONG: replaceWithTag( 'B' ), + EM: replaceWithTag( 'I' ), + INS: replaceWithTag( 'U' ), + STRIKE: replaceWithTag( 'S' ), + FONT: function ( node, parent, config ) { + var face = node.face; + var size = node.size; + var colour = node.color; + var doc = node.ownerDocument; + var classNames = config.classNames; + var fontSpan, sizeSpan, colourSpan; + var newTreeBottom, newTreeTop; + if ( face ) { + fontSpan = createElement( doc, 'SPAN', { + 'class': classNames.fontFamily, + style: 'font-family:' + face + }); + newTreeTop = fontSpan; + newTreeBottom = fontSpan; + } + if ( size ) { + sizeSpan = createElement( doc, 'SPAN', { + 'class': classNames.fontSize, + style: 'font-size:' + fontSizes[ size ] + 'px' + }); + if ( !newTreeTop ) { + newTreeTop = sizeSpan; + } + if ( newTreeBottom ) { + newTreeBottom.appendChild( sizeSpan ); + } + newTreeBottom = sizeSpan; + } + if ( colour && /^#?([\dA-F]{3}){1,2}$/i.test( colour ) ) { + if ( colour.charAt( 0 ) !== '#' ) { + colour = '#' + colour; + } + colourSpan = createElement( doc, 'SPAN', { + 'class': classNames.colour, + style: 'color:' + colour + }); + if ( !newTreeTop ) { + newTreeTop = colourSpan; + } + if ( newTreeBottom ) { + newTreeBottom.appendChild( colourSpan ); + } + newTreeBottom = colourSpan; + } + if ( !newTreeTop ) { + newTreeTop = newTreeBottom = createElement( doc, 'SPAN' ); + } + parent.replaceChild( newTreeTop, node ); + newTreeBottom.appendChild( empty( node ) ); + return newTreeBottom; + }, + TT: function ( node, parent, config ) { + var el = createElement( node.ownerDocument, 'SPAN', { + 'class': config.classNames.fontFamily, + style: 'font-family:menlo,consolas,"courier new",monospace' + }); + parent.replaceChild( el, node ); + el.appendChild( empty( node ) ); + return el; + } +}; + +var allowedBlock = /^(?:A(?:DDRESS|RTICLE|SIDE|UDIO)|BLOCKQUOTE|CAPTION|D(?:[DLT]|IV)|F(?:IGURE|IGCAPTION|OOTER)|H[1-6]|HEADER|L(?:ABEL|EGEND|I)|O(?:L|UTPUT)|P(?:RE)?|SECTION|T(?:ABLE|BODY|D|FOOT|H|HEAD|R)|COL(?:GROUP)?|UL)$/; + +var blacklist = /^(?:HEAD|META|STYLE)/; + +var walker = new TreeWalker( null, SHOW_TEXT|SHOW_ELEMENT ); + +/* + Two purposes: + + 1. Remove nodes we don't want, such as weird tags, comment nodes + and whitespace nodes. + 2. Convert inline tags into our preferred format. +*/ +var cleanTree = function cleanTree ( node, config, preserveWS ) { + var children = node.childNodes, + nonInlineParent, i, l, child, nodeName, nodeType, rewriter, childLength, + startsWithWS, endsWithWS, data, sibling; + + nonInlineParent = node; + while ( isInline( nonInlineParent ) ) { + nonInlineParent = nonInlineParent.parentNode; + } + walker.root = nonInlineParent; + + for ( i = 0, l = children.length; i < l; i += 1 ) { + child = children[i]; + nodeName = child.nodeName; + nodeType = child.nodeType; + rewriter = stylesRewriters[ nodeName ]; + if ( nodeType === ELEMENT_NODE ) { + childLength = child.childNodes.length; + if ( rewriter ) { + child = rewriter( child, node, config ); + } else if ( blacklist.test( nodeName ) ) { + node.removeChild( child ); + i -= 1; + l -= 1; + continue; + } else if ( !allowedBlock.test( nodeName ) && !isInline( child ) ) { + i -= 1; + l += childLength - 1; + node.replaceChild( empty( child ), child ); + continue; + } + if ( childLength ) { + cleanTree( child, config, + preserveWS || ( nodeName === 'PRE' ) ); + } + } else { + if ( nodeType === TEXT_NODE ) { + data = child.data; + startsWithWS = !notWS.test( data.charAt( 0 ) ); + endsWithWS = !notWS.test( data.charAt( data.length - 1 ) ); + if ( preserveWS || ( !startsWithWS && !endsWithWS ) ) { + continue; + } + // Iterate through the nodes; if we hit some other content + // before the start of a new block we don't trim + if ( startsWithWS ) { + walker.currentNode = child; + while ( sibling = walker.previousPONode() ) { + nodeName = sibling.nodeName; + if ( nodeName === 'IMG' || + ( nodeName === '#text' && + notWS.test( sibling.data ) ) ) { + break; + } + if ( !isInline( sibling ) ) { + sibling = null; + break; + } + } + data = data.replace( /^[ \r\n]+/g, sibling ? ' ' : '' ); + } + if ( endsWithWS ) { + walker.currentNode = child; + while ( sibling = walker.nextNode() ) { + if ( nodeName === 'IMG' || + ( nodeName === '#text' && + notWS.test( sibling.data ) ) ) { + break; + } + if ( !isInline( sibling ) ) { + sibling = null; + break; + } + } + data = data.replace( /[ \r\n]+$/g, sibling ? ' ' : '' ); + } + if ( data ) { + child.data = data; + continue; + } + } + node.removeChild( child ); + i -= 1; + l -= 1; + } + } + return node; +}; + +// --- + +var removeEmptyInlines = function removeEmptyInlines ( node ) { + var children = node.childNodes, + l = children.length, + child; + while ( l-- ) { + child = children[l]; + if ( child.nodeType === ELEMENT_NODE && !isLeaf( child ) ) { + removeEmptyInlines( child ); + if ( isInline( child ) && !child.firstChild ) { + node.removeChild( child ); + } + } else if ( child.nodeType === TEXT_NODE && !child.data ) { + node.removeChild( child ); + } + } +}; + +// --- + +var notWSTextNode = function ( node ) { + return node.nodeType === ELEMENT_NODE ? + node.nodeName === 'BR' : + notWS.test( node.data ); +}; +var isLineBreak = function ( br, isLBIfEmptyBlock ) { + var block = br.parentNode; + var walker; + while ( isInline( block ) ) { + block = block.parentNode; + } + walker = new TreeWalker( + block, SHOW_ELEMENT|SHOW_TEXT, notWSTextNode ); + walker.currentNode = br; + return !!walker.nextNode() || + ( isLBIfEmptyBlock && !walker.previousNode() ); +}; + +//
elements are treated specially, and differently depending on the +// browser, when in rich text editor mode. When adding HTML from external +// sources, we must remove them, replacing the ones that actually affect +// line breaks by wrapping the inline text in a
. Browsers that want
+// elements at the end of each block will then have them added back in a later +// fixCursor method call. +var cleanupBRs = function ( node, root, keepForBlankLine ) { + var brs = node.querySelectorAll( 'BR' ); + var brBreaksLine = []; + var l = brs.length; + var i, br, parent; + + // Must calculate whether the
breaks a line first, because if we + // have two
s next to each other, after the first one is converted + // to a block split, the second will be at the end of a block and + // therefore seem to not be a line break. But in its original context it + // was, so we should also convert it to a block split. + for ( i = 0; i < l; i += 1 ) { + brBreaksLine[i] = isLineBreak( brs[i], keepForBlankLine ); + } + while ( l-- ) { + br = brs[l]; + // Cleanup may have removed it + parent = br.parentNode; + if ( !parent ) { continue; } + // If it doesn't break a line, just remove it; it's not doing + // anything useful. We'll add it back later if required by the + // browser. If it breaks a line, wrap the content in div tags + // and replace the brs. + if ( !brBreaksLine[l] ) { + detach( br ); + } else if ( !isInline( parent ) ) { + fixContainer( parent, root ); + } + } +}; + +// The (non-standard but supported enough) innerText property is based on the +// render tree in Firefox and possibly other browsers, so we must insert the +// DOM node into the document to ensure the text part is correct. +var setClipboardData = + function ( event, contents, root, willCutCopy, toPlainText, plainTextOnly ) { + var clipboardData = event.clipboardData; + var doc = event.target.ownerDocument; + var body = doc.body; + var node = createElement( doc, 'div' ); + var html, text; + + node.appendChild( contents ); + + html = node.innerHTML; + if ( willCutCopy ) { + html = willCutCopy( html ); + } + + if ( toPlainText ) { + text = toPlainText( html ); + } else { + // Firefox will add an extra new line for BRs at the end of block when + // calculating innerText, even though they don't actually affect + // display, so we need to remove them first. + cleanupBRs( node, root, true ); + node.setAttribute( 'style', + 'position:fixed;overflow:hidden;bottom:100%;right:100%;' ); + body.appendChild( node ); + text = node.innerText || node.textContent; + text = text.replace( / /g, ' ' ); // Replace nbsp with regular space + body.removeChild( node ); + } + // Firefox (and others?) returns unix line endings (\n) even on Windows. + // If on Windows, normalise to \r\n, since Notepad and some other crappy + // apps do not understand just \n. + if ( isWin ) { + text = text.replace( /\r?\n/g, '\r\n' ); + } + + if ( !plainTextOnly && text !== html ) { + clipboardData.setData( 'text/html', html ); + } + clipboardData.setData( 'text/plain', text ); + event.preventDefault(); +}; + +var onCut = function ( event ) { + var range = this.getSelection(); + var root = this._root; + var self = this; + var startBlock, endBlock, copyRoot, contents, parent, newContents; + + // Nothing to do + if ( range.collapsed ) { + event.preventDefault(); + return; + } + + // Save undo checkpoint + this.saveUndoState( range ); + + // Edge only seems to support setting plain text as of 2016-03-11. + if ( !isEdge && event.clipboardData ) { + // Clipboard content should include all parents within block, or all + // parents up to root if selection across blocks + startBlock = getStartBlockOfRange( range, root ); + endBlock = getEndBlockOfRange( range, root ); + copyRoot = ( ( startBlock === endBlock ) && startBlock ) || root; + // Extract the contents + contents = deleteContentsOfRange( range, root ); + // Add any other parents not in extracted content, up to copy root + parent = range.commonAncestorContainer; + if ( parent.nodeType === TEXT_NODE ) { + parent = parent.parentNode; + } + while ( parent && parent !== copyRoot ) { + newContents = parent.cloneNode( false ); + newContents.appendChild( contents ); + contents = newContents; + parent = parent.parentNode; + } + // Set clipboard data + setClipboardData( + event, contents, root, this._config.willCutCopy, null, false ); + } else { + setTimeout( function () { + try { + // If all content removed, ensure div at start of root. + self._ensureBottomLine(); + } catch ( error ) { + self.didError( error ); + } + }, 0 ); + } + + this.setSelection( range ); +}; + +var _onCopy = function ( event, range, root, willCutCopy, toPlainText, plainTextOnly ) { + var startBlock, endBlock, copyRoot, contents, parent, newContents; + // Edge only seems to support setting plain text as of 2016-03-11. + if ( !isEdge && event.clipboardData ) { + // Clipboard content should include all parents within block, or all + // parents up to root if selection across blocks + startBlock = getStartBlockOfRange( range, root ); + endBlock = getEndBlockOfRange( range, root ); + copyRoot = ( ( startBlock === endBlock ) && startBlock ) || root; + // Clone range to mutate, then move up as high as possible without + // passing the copy root node. + range = range.cloneRange(); + moveRangeBoundariesDownTree( range ); + moveRangeBoundariesUpTree( range, copyRoot, copyRoot, root ); + // Extract the contents + contents = range.cloneContents(); + // Add any other parents not in extracted content, up to copy root + parent = range.commonAncestorContainer; + if ( parent.nodeType === TEXT_NODE ) { + parent = parent.parentNode; + } + while ( parent && parent !== copyRoot ) { + newContents = parent.cloneNode( false ); + newContents.appendChild( contents ); + contents = newContents; + parent = parent.parentNode; + } + // Set clipboard data + setClipboardData( event, contents, root, willCutCopy, toPlainText, plainTextOnly ); + } +}; + +var onCopy = function ( event ) { + _onCopy( + event, + this.getSelection(), + this._root, + this._config.willCutCopy, + null, + false + ); +}; + +// Need to monitor for shift key like this, as event.shiftKey is not available +// in paste event. +function monitorShiftKey ( event ) { + this.isShiftDown = event.shiftKey; +} + +var onPaste = function ( event ) { + var clipboardData = event.clipboardData; + var items = clipboardData && clipboardData.items; + var choosePlain = this.isShiftDown; + var fireDrop = false; + var hasRTF = false; + var hasImage = false; + var plainItem = null; + var htmlItem = null; + var self = this; + var l, item, type, types, data; + + // Current HTML5 Clipboard interface + // --------------------------------- + // https://html.spec.whatwg.org/multipage/interaction.html + if ( items ) { + l = items.length; + while ( l-- ) { + item = items[l]; + type = item.type; + if ( type === 'text/html' ) { + htmlItem = item; + // iOS copy URL gives you type text/uri-list which is just a list + // of 1 or more URLs separated by new lines. Can just treat as + // plain text. + } else if ( type === 'text/plain' || type === 'text/uri-list' ) { + plainItem = item; + } else if ( type === 'text/rtf' ) { + hasRTF = true; + } else if ( /^image\/.*/.test( type ) ) { + hasImage = true; + } + } + + // Treat image paste as a drop of an image file. When you copy + // an image in Chrome/Firefox (at least), it copies the image data + // but also an HTML version (referencing the original URL of the image) + // and a plain text version. + // + // However, when you copy in Excel, you get html, rtf, text, image; + // in this instance you want the html version! So let's try using + // the presence of text/rtf as an indicator to choose the html version + // over the image. + if ( hasImage && !( hasRTF && htmlItem ) ) { + event.preventDefault(); + this.fireEvent( 'dragover', { + dataTransfer: clipboardData, + /*jshint loopfunc: true */ + preventDefault: function () { + fireDrop = true; + } + /*jshint loopfunc: false */ + }); + if ( fireDrop ) { + this.fireEvent( 'drop', { + dataTransfer: clipboardData + }); + } + return; + } + + // Edge only provides access to plain text as of 2016-03-11 and gives no + // indication there should be an HTML part. However, it does support + // access to image data, so we check for that first. Otherwise though, + // fall through to fallback clipboard handling methods + if ( !isEdge ) { + event.preventDefault(); + if ( htmlItem && ( !choosePlain || !plainItem ) ) { + htmlItem.getAsString( function ( html ) { + self.insertHTML( html, true ); + }); + } else if ( plainItem ) { + plainItem.getAsString( function ( text ) { + self.insertPlainText( text, true ); + }); + } + return; + } + } + + // Old interface + // ------------- + + // Safari (and indeed many other OS X apps) copies stuff as text/rtf + // rather than text/html; even from a webpage in Safari. The only way + // to get an HTML version is to fallback to letting the browser insert + // the content. Same for getting image data. *Sigh*. + // + // Firefox is even worse: it doesn't even let you know that there might be + // an RTF version on the clipboard, but it will also convert to HTML if you + // let the browser insert the content. I've filed + // https://bugzilla.mozilla.org/show_bug.cgi?id=1254028 + types = clipboardData && clipboardData.types; + if ( !isEdge && types && ( + indexOf.call( types, 'text/html' ) > -1 || ( + !isGecko && + indexOf.call( types, 'text/plain' ) > -1 && + indexOf.call( types, 'text/rtf' ) < 0 ) + )) { + event.preventDefault(); + // Abiword on Linux copies a plain text and html version, but the HTML + // version is the empty string! So always try to get HTML, but if none, + // insert plain text instead. On iOS, Facebook (and possibly other + // apps?) copy links as type text/uri-list, but also insert a **blank** + // text/plain item onto the clipboard. Why? Who knows. + if ( !choosePlain && ( data = clipboardData.getData( 'text/html' ) ) ) { + this.insertHTML( data, true ); + } else if ( + ( data = clipboardData.getData( 'text/plain' ) ) || + ( data = clipboardData.getData( 'text/uri-list' ) ) ) { + this.insertPlainText( data, true ); + } + return; + } + + // No interface. Includes all versions of IE :( + // -------------------------------------------- + + this._awaitingPaste = true; + + var body = this._doc.body, + range = this.getSelection(), + startContainer = range.startContainer, + startOffset = range.startOffset, + endContainer = range.endContainer, + endOffset = range.endOffset; + + // We need to position the pasteArea in the visible portion of the screen + // to stop the browser auto-scrolling. + var pasteArea = this.createElement( 'DIV', { + contenteditable: 'true', + style: 'position:fixed; overflow:hidden; top:0; right:100%; width:1px; height:1px;' + }); + body.appendChild( pasteArea ); + range.selectNodeContents( pasteArea ); + this.setSelection( range ); + + // A setTimeout of 0 means this is added to the back of the + // single javascript thread, so it will be executed after the + // paste event. + setTimeout( function () { + try { + // IE sometimes fires the beforepaste event twice; make sure it is + // not run again before our after paste function is called. + self._awaitingPaste = false; + + // Get the pasted content and clean + var html = '', + next = pasteArea, + first, range; + + // #88: Chrome can apparently split the paste area if certain + // content is inserted; gather them all up. + while ( pasteArea = next ) { + next = pasteArea.nextSibling; + detach( pasteArea ); + // Safari and IE like putting extra divs around things. + first = pasteArea.firstChild; + if ( first && first === pasteArea.lastChild && + first.nodeName === 'DIV' ) { + pasteArea = first; + } + html += pasteArea.innerHTML; + } + + range = self.createRange( + startContainer, startOffset, endContainer, endOffset ); + self.setSelection( range ); + + if ( html ) { + self.insertHTML( html, true ); + } + } catch ( error ) { + self.didError( error ); + } + }, 0 ); +}; + +// On Windows you can drag an drop text. We can't handle this ourselves, because +// as far as I can see, there's no way to get the drop insertion point. So just +// save an undo state and hope for the best. +var onDrop = function ( event ) { + var types = event.dataTransfer.types; + var l = types.length; + var hasPlain = false; + var hasHTML = false; + while ( l-- ) { + switch ( types[l] ) { + case 'text/plain': + hasPlain = true; + break; + case 'text/html': + hasHTML = true; + break; + default: + return; + } + } + if ( hasHTML || hasPlain ) { + this.saveUndoState(); + } +}; + +function mergeObjects ( base, extras, mayOverride ) { + var prop, value; + if ( !base ) { + base = {}; + } + if ( extras ) { + for ( prop in extras ) { + if ( mayOverride || !( prop in base ) ) { + value = extras[ prop ]; + base[ prop ] = ( value && value.constructor === Object ) ? + mergeObjects( base[ prop ], value, mayOverride ) : + value; + } + } + } + return base; +} + +function Squire ( root, config ) { + if ( root.nodeType === DOCUMENT_NODE ) { + root = root.body; + } + var doc = root.ownerDocument; + var win = doc.defaultView; + var mutation; + + this._win = win; + this._doc = doc; + this._root = root; + + this._events = {}; + + this._isFocused = false; + this._lastSelection = null; + + this._hasZWS = false; + + this._lastAnchorNode = null; + this._lastFocusNode = null; + this._path = ''; + this._willUpdatePath = false; + + if ( 'onselectionchange' in doc ) { + this.addEventListener( 'selectionchange', this._updatePathOnEvent ); + } else { + this.addEventListener( 'keyup', this._updatePathOnEvent ); + this.addEventListener( 'mouseup', this._updatePathOnEvent ); + } + + this._undoIndex = -1; + this._undoStack = []; + this._undoStackLength = 0; + this._isInUndoState = false; + this._ignoreChange = false; + this._ignoreAllChanges = false; + + if ( canObserveMutations ) { + mutation = new MutationObserver( this._docWasChanged.bind( this ) ); + mutation.observe( root, { + childList: true, + attributes: true, + characterData: true, + subtree: true + }); + this._mutation = mutation; + } else { + this.addEventListener( 'keyup', this._keyUpDetectChange ); + } + + // On blur, restore focus except if the user taps or clicks to focus a + // specific point. Can't actually use click event because focus happens + // before click, so use mousedown/touchstart + this._restoreSelection = false; + this.addEventListener( 'blur', enableRestoreSelection ); + this.addEventListener( 'mousedown', disableRestoreSelection ); + this.addEventListener( 'touchstart', disableRestoreSelection ); + this.addEventListener( 'focus', restoreSelection ); + + // IE sometimes fires the beforepaste event twice; make sure it is not run + // again before our after paste function is called. + this._awaitingPaste = false; + this.addEventListener( 'cut', onCut ); + this.addEventListener( 'copy', onCopy ); + this.addEventListener( 'keydown', monitorShiftKey ); + this.addEventListener( 'keyup', monitorShiftKey ); + this.addEventListener( 'paste', onPaste ); + this.addEventListener( 'drop', onDrop ); + this.addEventListener( 'keydown', onKey ); + + // Add key handlers + this._keyHandlers = Object.create( keyHandlers ); + + // Override default properties + this.setConfig( config ); + + root.setAttribute( 'contenteditable', 'true' ); + // Grammarly breaks the editor, *sigh* + root.setAttribute( 'data-gramm', 'false' ); + + // Remove Firefox's built-in controls + try { + doc.execCommand( 'enableObjectResizing', false, 'false' ); + doc.execCommand( 'enableInlineTableEditing', false, 'false' ); + } catch ( error ) {} + + root.__squire__ = this; + + // Need to register instance before calling setHTML, so that the fixCursor + // function can lookup any default block tag options set. + this.setHTML( '' ); +} + +var proto = Squire.prototype; + +var sanitizeToDOMFragment = function ( html, isPaste, self ) { + var doc = self._doc; + var frag = html ? DOMPurify.sanitize( html, { + ALLOW_UNKNOWN_PROTOCOLS: true, + WHOLE_DOCUMENT: false, + RETURN_DOM: true, + RETURN_DOM_FRAGMENT: true, + FORCE_BODY: false + }) : null; + return frag ? doc.importNode( frag, true ) : doc.createDocumentFragment(); +}; + +proto.setConfig = function ( config ) { + config = mergeObjects({ + blockTag: 'DIV', + blockAttributes: null, + tagAttributes: { + blockquote: null, + ul: null, + ol: null, + li: null, + a: null + }, + classNames: { + colour: 'colour', + fontFamily: 'font', + fontSize: 'size', + highlight: 'highlight' + }, + leafNodeNames: leafNodeNames, + undo: { + documentSizeThreshold: -1, // -1 means no threshold + undoLimit: -1 // -1 means no limit + }, + isInsertedHTMLSanitized: true, + isSetHTMLSanitized: true, + sanitizeToDOMFragment: + typeof DOMPurify !== 'undefined' && DOMPurify.isSupported ? + sanitizeToDOMFragment : null, + willCutCopy: null, + addLinks: true + }, config, true ); + + // Users may specify block tag in lower case + config.blockTag = config.blockTag.toUpperCase(); + + this._config = config; + + return this; +}; + +proto.createElement = function ( tag, props, children ) { + return createElement( this._doc, tag, props, children ); +}; + +proto.createDefaultBlock = function ( children ) { + var config = this._config; + return fixCursor( + this.createElement( config.blockTag, config.blockAttributes, children ), + this._root + ); +}; + +proto.didError = function ( error ) { + console.log( error ); +}; + +proto.getDocument = function () { + return this._doc; +}; +proto.getRoot = function () { + return this._root; +}; + +proto.modifyDocument = function ( modificationCallback ) { + var mutation = this._mutation; + if ( mutation ) { + if ( mutation.takeRecords().length ) { + this._docWasChanged(); + } + mutation.disconnect(); + } + + this._ignoreAllChanges = true; + modificationCallback(); + this._ignoreAllChanges = false; + + if ( mutation ) { + mutation.observe( this._root, { + childList: true, + attributes: true, + characterData: true, + subtree: true + }); + this._ignoreChange = false; + } +}; + +// --- Events --- + +// Subscribing to these events won't automatically add a listener to the +// document node, since these events are fired in a custom manner by the +// editor code. +var customEvents = { + pathChange: 1, select: 1, input: 1, undoStateChange: 1 +}; + +proto.fireEvent = function ( type, event ) { + var handlers = this._events[ type ]; + var isFocused, l, obj; + // UI code, especially modal views, may be monitoring for focus events and + // immediately removing focus. In certain conditions, this can cause the + // focus event to fire after the blur event, which can cause an infinite + // loop. So we detect whether we're actually focused/blurred before firing. + if ( /^(?:focus|blur)/.test( type ) ) { + isFocused = this._root === this._doc.activeElement; + if ( type === 'focus' ) { + if ( !isFocused || this._isFocused ) { + return this; + } + this._isFocused = true; + } else { + if ( isFocused || !this._isFocused ) { + return this; + } + this._isFocused = false; + } + } + if ( handlers ) { + if ( !event ) { + event = {}; + } + if ( event.type !== type ) { + event.type = type; + } + // Clone handlers array, so any handlers added/removed do not affect it. + handlers = handlers.slice(); + l = handlers.length; + while ( l-- ) { + obj = handlers[l]; + try { + if ( obj.handleEvent ) { + obj.handleEvent( event ); + } else { + obj.call( this, event ); + } + } catch ( error ) { + error.details = 'Squire: fireEvent error. Event type: ' + type; + this.didError( error ); + } + } + } + return this; +}; + +proto.destroy = function () { + var events = this._events; + var type; + + for ( type in events ) { + this.removeEventListener( type ); + } + if ( this._mutation ) { + this._mutation.disconnect(); + } + delete this._root.__squire__; + + // Destroy undo stack + this._undoIndex = -1; + this._undoStack = []; + this._undoStackLength = 0; +}; + +proto.handleEvent = function ( event ) { + this.fireEvent( event.type, event ); +}; + +proto.addEventListener = function ( type, fn ) { + var handlers = this._events[ type ]; + var target = this._root; + if ( !fn ) { + this.didError({ + name: 'Squire: addEventListener with null or undefined fn', + message: 'Event type: ' + type + }); + return this; + } + if ( !handlers ) { + handlers = this._events[ type ] = []; + if ( !customEvents[ type ] ) { + if ( type === 'selectionchange' ) { + target = this._doc; + } + target.addEventListener( type, this, true ); + } + } + handlers.push( fn ); + return this; +}; + +proto.removeEventListener = function ( type, fn ) { + var handlers = this._events[ type ]; + var target = this._root; + var l; + if ( handlers ) { + if ( fn ) { + l = handlers.length; + while ( l-- ) { + if ( handlers[l] === fn ) { + handlers.splice( l, 1 ); + } + } + } else { + handlers.length = 0; + } + if ( !handlers.length ) { + delete this._events[ type ]; + if ( !customEvents[ type ] ) { + if ( type === 'selectionchange' ) { + target = this._doc; + } + target.removeEventListener( type, this, true ); + } + } + } + return this; +}; + +// --- Selection and Path --- + +proto.createRange = + function ( range, startOffset, endContainer, endOffset ) { + if ( range instanceof this._win.Range ) { + return range.cloneRange(); + } + var domRange = this._doc.createRange(); + domRange.setStart( range, startOffset ); + if ( endContainer ) { + domRange.setEnd( endContainer, endOffset ); + } else { + domRange.setEnd( range, startOffset ); + } + return domRange; +}; + +proto.getCursorPosition = function ( range ) { + if ( ( !range && !( range = this.getSelection() ) ) || + !range.getBoundingClientRect ) { + return null; + } + // Get the bounding rect + var rect = range.getBoundingClientRect(); + var node, parent; + if ( rect && !rect.top ) { + this._ignoreChange = true; + node = this._doc.createElement( 'SPAN' ); + node.textContent = ZWS; + insertNodeInRange( range, node ); + rect = node.getBoundingClientRect(); + parent = node.parentNode; + parent.removeChild( node ); + mergeInlines( parent, range ); + } + return rect; +}; + +proto._moveCursorTo = function ( toStart ) { + var root = this._root, + range = this.createRange( root, toStart ? 0 : root.childNodes.length ); + moveRangeBoundariesDownTree( range ); + this.setSelection( range ); + return this; +}; +proto.moveCursorToStart = function () { + return this._moveCursorTo( true ); +}; +proto.moveCursorToEnd = function () { + return this._moveCursorTo( false ); +}; + +var getWindowSelection = function ( self ) { + return self._win.getSelection() || null; +}; + +proto.setSelection = function ( range ) { + if ( range ) { + this._lastSelection = range; + // If we're setting selection, that automatically, and synchronously, // triggers a focus event. So just store the selection and mark it as + // needing restore on focus. + if ( !this._isFocused ) { + enableRestoreSelection.call( this ); + } else { + // iOS bug: if you don't focus the iframe before setting the + // selection, you can end up in a state where you type but the input + // doesn't get directed into the contenteditable area but is instead + // lost in a black hole. Very strange. + if ( isIOS ) { + this._win.focus(); + } + var sel = getWindowSelection( this ); + if ( sel && sel.setBaseAndExtent ) { + sel.setBaseAndExtent( + range.startContainer, + range.startOffset, + range.endContainer, + range.endOffset, + ); + } else if ( sel ) { + // This is just for IE11 + sel.removeAllRanges(); + sel.addRange( range ); + } + } + } + return this; +}; + +proto.getSelection = function () { + var sel = getWindowSelection( this ); + var root = this._root; + var selection, startContainer, endContainer, node; + // If not focused, always rely on cached selection; another function may + // have set it but the DOM is not modified until focus again + if ( this._isFocused && sel && sel.rangeCount ) { + selection = sel.getRangeAt( 0 ).cloneRange(); + startContainer = selection.startContainer; + endContainer = selection.endContainer; + // FF can return the selection as being inside an . WTF? + if ( startContainer && isLeaf( startContainer ) ) { + selection.setStartBefore( startContainer ); + } + if ( endContainer && isLeaf( endContainer ) ) { + selection.setEndBefore( endContainer ); + } + } + if ( selection && + isOrContains( root, selection.commonAncestorContainer ) ) { + this._lastSelection = selection; + } else { + selection = this._lastSelection; + node = selection.commonAncestorContainer; + // Check the editor is in the live document; if not, the range has + // probably been rewritten by the browser and is bogus + if ( !isOrContains( node.ownerDocument, node ) ) { + selection = null; + } + } + if ( !selection ) { + selection = this.createRange( root.firstChild, 0 ); + } + return selection; +}; + +function enableRestoreSelection () { + this._restoreSelection = true; +} +function disableRestoreSelection () { + this._restoreSelection = false; +} +function restoreSelection () { + if ( this._restoreSelection ) { + this.setSelection( this._lastSelection ); + } +} + +proto.getSelectedText = function () { + var range = this.getSelection(); + if ( !range || range.collapsed ) { + return ''; + } + var walker = new TreeWalker( + range.commonAncestorContainer, + SHOW_TEXT|SHOW_ELEMENT, + function ( node ) { + return isNodeContainedInRange( range, node, true ); + } + ); + var startContainer = range.startContainer; + var endContainer = range.endContainer; + var node = walker.currentNode = startContainer; + var textContent = ''; + var addedTextInBlock = false; + var value; + + if ( !walker.filter( node ) ) { + node = walker.nextNode(); + } + + while ( node ) { + if ( node.nodeType === TEXT_NODE ) { + value = node.data; + if ( value && ( /\S/.test( value ) ) ) { + if ( node === endContainer ) { + value = value.slice( 0, range.endOffset ); + } + if ( node === startContainer ) { + value = value.slice( range.startOffset ); + } + textContent += value; + addedTextInBlock = true; + } + } else if ( node.nodeName === 'BR' || + addedTextInBlock && !isInline( node ) ) { + textContent += '\n'; + addedTextInBlock = false; + } + node = walker.nextNode(); + } + + return textContent; +}; + +proto.getPath = function () { + return this._path; +}; + +// --- Workaround for browsers that can't focus empty text nodes --- + +// WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=15256 + +// Walk down the tree starting at the root and remove any ZWS. If the node only +// contained ZWS space then remove it too. We may want to keep one ZWS node at +// the bottom of the tree so the block can be selected. Define that node as the +// keepNode. +var removeZWS = function ( root, keepNode ) { + var walker = new TreeWalker( root, SHOW_TEXT ); + var parent, node, index; + while ( node = walker.nextNode() ) { + while ( ( index = node.data.indexOf( ZWS ) ) > -1 && + ( !keepNode || node.parentNode !== keepNode ) ) { + if ( node.length === 1 ) { + do { + parent = node.parentNode; + parent.removeChild( node ); + node = parent; + walker.currentNode = parent; + } while ( isInline( node ) && !getLength( node ) ); + break; + } else { + node.deleteData( index, 1 ); + } + } + } +}; + +proto._didAddZWS = function () { + this._hasZWS = true; +}; +proto._removeZWS = function () { + if ( !this._hasZWS ) { + return; + } + removeZWS( this._root ); + this._hasZWS = false; +}; + +// --- Path change events --- + +proto._updatePath = function ( range, force ) { + if ( !range ) { + return; + } + var anchor = range.startContainer, + focus = range.endContainer, + newPath; + if ( force || anchor !== this._lastAnchorNode || + focus !== this._lastFocusNode ) { + this._lastAnchorNode = anchor; + this._lastFocusNode = focus; + newPath = ( anchor && focus ) ? ( anchor === focus ) ? + getPath( focus, this._root, this._config ) : '(selection)' : ''; + if ( this._path !== newPath ) { + this._path = newPath; + this.fireEvent( 'pathChange', { path: newPath } ); + } + } + this.fireEvent( range.collapsed ? 'cursor' : 'select', { + range: range + }); +}; + +// selectionchange is fired synchronously in IE when removing current selection +// and when setting new selection; keyup/mouseup may have processing we want +// to do first. Either way, send to next event loop. +proto._updatePathOnEvent = function () { + var self = this; + if ( self._isFocused && !self._willUpdatePath ) { + self._willUpdatePath = true; + setTimeout( function () { + self._willUpdatePath = false; + self._updatePath( self.getSelection() ); + }, 0 ); + } +}; + +// --- Focus --- + +proto.focus = function () { + this._root.focus({ preventScroll: true }); + + if ( isIE ) { + this.fireEvent( 'focus' ); + } + + return this; +}; + +proto.blur = function () { + this._root.blur(); + + if ( isIE ) { + this.fireEvent( 'blur' ); + } + + return this; +}; + +// --- Bookmarking --- + +var startSelectionId = 'squire-selection-start'; +var endSelectionId = 'squire-selection-end'; + +proto._saveRangeToBookmark = function ( range ) { + var startNode = this.createElement( 'INPUT', { + id: startSelectionId, + type: 'hidden' + }), + endNode = this.createElement( 'INPUT', { + id: endSelectionId, + type: 'hidden' + }), + temp; + + insertNodeInRange( range, startNode ); + range.collapse( false ); + insertNodeInRange( range, endNode ); + + // In a collapsed range, the start is sometimes inserted after the end! + if ( startNode.compareDocumentPosition( endNode ) & + DOCUMENT_POSITION_PRECEDING ) { + startNode.id = endSelectionId; + endNode.id = startSelectionId; + temp = startNode; + startNode = endNode; + endNode = temp; + } + + range.setStartAfter( startNode ); + range.setEndBefore( endNode ); +}; + +proto._getRangeAndRemoveBookmark = function ( range ) { + var root = this._root, + start = root.querySelector( '#' + startSelectionId ), + end = root.querySelector( '#' + endSelectionId ); + + if ( start && end ) { + var startContainer = start.parentNode, + endContainer = end.parentNode, + startOffset = indexOf.call( startContainer.childNodes, start ), + endOffset = indexOf.call( endContainer.childNodes, end ); + + if ( startContainer === endContainer ) { + endOffset -= 1; + } + + detach( start ); + detach( end ); + + if ( !range ) { + range = this._doc.createRange(); + } + range.setStart( startContainer, startOffset ); + range.setEnd( endContainer, endOffset ); + + // Merge any text nodes we split + mergeInlines( startContainer, range ); + if ( startContainer !== endContainer ) { + mergeInlines( endContainer, range ); + } + + // If we didn't split a text node, we should move into any adjacent + // text node to current selection point + if ( range.collapsed ) { + startContainer = range.startContainer; + if ( startContainer.nodeType === TEXT_NODE ) { + endContainer = startContainer.childNodes[ range.startOffset ]; + if ( !endContainer || endContainer.nodeType !== TEXT_NODE ) { + endContainer = + startContainer.childNodes[ range.startOffset - 1 ]; + } + if ( endContainer && endContainer.nodeType === TEXT_NODE ) { + range.setStart( endContainer, 0 ); + range.collapse( true ); + } + } + } + } + return range || null; +}; + +// --- Undo --- + +proto._keyUpDetectChange = function ( event ) { + var code = event.keyCode; + // Presume document was changed if: + // 1. A modifier key (other than shift) wasn't held down + // 2. The key pressed is not in range 16<=x<=20 (control keys) + // 3. The key pressed is not in range 33<=x<=45 (navigation keys) + if ( !event.ctrlKey && !event.metaKey && !event.altKey && + ( code < 16 || code > 20 ) && + ( code < 33 || code > 45 ) ) { + this._docWasChanged(); + } +}; + +proto._docWasChanged = function () { + if ( canWeakMap ) { + nodeCategoryCache = new WeakMap(); + } + if ( this._ignoreAllChanges ) { + return; + } + + if ( canObserveMutations && this._ignoreChange ) { + this._ignoreChange = false; + return; + } + if ( this._isInUndoState ) { + this._isInUndoState = false; + this.fireEvent( 'undoStateChange', { + canUndo: true, + canRedo: false + }); + } + this.fireEvent( 'input' ); +}; + +// Leaves bookmark +proto._recordUndoState = function ( range, replace ) { + // Don't record if we're already in an undo state + if ( !this._isInUndoState|| replace ) { + // Advance pointer to new position + var undoIndex = this._undoIndex; + var undoStack = this._undoStack; + var undoConfig = this._config.undo; + var undoThreshold = undoConfig.documentSizeThreshold; + var undoLimit = undoConfig.undoLimit; + var html; + + if ( !replace ) { + undoIndex += 1; + } + + // Truncate stack if longer (i.e. if has been previously undone) + if ( undoIndex < this._undoStackLength ) { + undoStack.length = this._undoStackLength = undoIndex; + } + + // Get data + if ( range ) { + this._saveRangeToBookmark( range ); + } + html = this._getHTML(); + + // If this document is above the configured size threshold, + // limit the number of saved undo states. + // Threshold is in bytes, JS uses 2 bytes per character + if ( undoThreshold > -1 && html.length * 2 > undoThreshold ) { + if ( undoLimit > -1 && undoIndex > undoLimit ) { + undoStack.splice( 0, undoIndex - undoLimit ); + undoIndex = undoLimit; + this._undoStackLength = undoLimit; + } + } + + // Save data + undoStack[ undoIndex ] = html; + this._undoIndex = undoIndex; + this._undoStackLength += 1; + this._isInUndoState = true; + } +}; + +proto.saveUndoState = function ( range ) { + if ( range === undefined ) { + range = this.getSelection(); + } + this._recordUndoState( range, this._isInUndoState ); + this._getRangeAndRemoveBookmark( range ); + + return this; +}; + +proto.undo = function () { + // Sanity check: must not be at beginning of the history stack + if ( this._undoIndex !== 0 || !this._isInUndoState ) { + // Make sure any changes since last checkpoint are saved. + this._recordUndoState( this.getSelection(), false ); + + this._undoIndex -= 1; + this._setHTML( this._undoStack[ this._undoIndex ] ); + var range = this._getRangeAndRemoveBookmark(); + if ( range ) { + this.setSelection( range ); + } + this._isInUndoState = true; + this.fireEvent( 'undoStateChange', { + canUndo: this._undoIndex !== 0, + canRedo: true + }); + this.fireEvent( 'input' ); + } + return this; +}; + +proto.redo = function () { + // Sanity check: must not be at end of stack and must be in an undo + // state. + var undoIndex = this._undoIndex, + undoStackLength = this._undoStackLength; + if ( undoIndex + 1 < undoStackLength && this._isInUndoState ) { + this._undoIndex += 1; + this._setHTML( this._undoStack[ this._undoIndex ] ); + var range = this._getRangeAndRemoveBookmark(); + if ( range ) { + this.setSelection( range ); + } + this.fireEvent( 'undoStateChange', { + canUndo: true, + canRedo: undoIndex + 2 < undoStackLength + }); + this.fireEvent( 'input' ); + } + return this; +}; + +// --- Inline formatting --- + +// Looks for matching tag and attributes, so won't work +// if instead of etc. +proto.hasFormat = function ( tag, attributes, range ) { + // 1. Normalise the arguments and get selection + tag = tag.toUpperCase(); + if ( !attributes ) { attributes = {}; } + if ( !range && !( range = this.getSelection() ) ) { + return false; + } + + // Sanitize range to prevent weird IE artifacts + if ( !range.collapsed && + range.startContainer.nodeType === TEXT_NODE && + range.startOffset === range.startContainer.length && + range.startContainer.nextSibling ) { + range.setStartBefore( range.startContainer.nextSibling ); + } + if ( !range.collapsed && + range.endContainer.nodeType === TEXT_NODE && + range.endOffset === 0 && + range.endContainer.previousSibling ) { + range.setEndAfter( range.endContainer.previousSibling ); + } + + // If the common ancestor is inside the tag we require, we definitely + // have the format. + var root = this._root; + var common = range.commonAncestorContainer; + var walker, node; + if ( getNearest( common, root, tag, attributes ) ) { + return true; + } + + // If common ancestor is a text node and doesn't have the format, we + // definitely don't have it. + if ( common.nodeType === TEXT_NODE ) { + return false; + } + + // Otherwise, check each text node at least partially contained within + // the selection and make sure all of them have the format we want. + walker = new TreeWalker( common, SHOW_TEXT, function ( node ) { + return isNodeContainedInRange( range, node, true ); + }); + + var seenNode = false; + while ( node = walker.nextNode() ) { + if ( !getNearest( node, root, tag, attributes ) ) { + return false; + } + seenNode = true; + } + + return seenNode; +}; + +// Extracts the font-family and font-size (if any) of the element +// holding the cursor. If there's a selection, returns an empty object. +proto.getFontInfo = function ( range ) { + var fontInfo = { + color: undefined, + backgroundColor: undefined, + family: undefined, + size: undefined + }; + var seenAttributes = 0; + var element, style, attr; + + if ( !range && !( range = this.getSelection() ) ) { + return fontInfo; + } + + element = range.commonAncestorContainer; + if ( range.collapsed || element.nodeType === TEXT_NODE ) { + if ( element.nodeType === TEXT_NODE ) { + element = element.parentNode; + } + while ( seenAttributes < 4 && element ) { + if ( style = element.style ) { + if ( !fontInfo.color && ( attr = style.color ) ) { + fontInfo.color = attr; + seenAttributes += 1; + } + if ( !fontInfo.backgroundColor && + ( attr = style.backgroundColor ) ) { + fontInfo.backgroundColor = attr; + seenAttributes += 1; + } + if ( !fontInfo.family && ( attr = style.fontFamily ) ) { + fontInfo.family = attr; + seenAttributes += 1; + } + if ( !fontInfo.size && ( attr = style.fontSize ) ) { + fontInfo.size = attr; + seenAttributes += 1; + } + } + element = element.parentNode; + } + } + return fontInfo; +}; + +proto._addFormat = function ( tag, attributes, range ) { + // If the range is collapsed we simply insert the node by wrapping + // it round the range and focus it. + var root = this._root; + var el, walker, startContainer, endContainer, startOffset, endOffset, + node, needsFormat, block; + + if ( range.collapsed ) { + el = fixCursor( this.createElement( tag, attributes ), root ); + insertNodeInRange( range, el ); + range.setStart( el.firstChild, el.firstChild.length ); + range.collapse( true ); + + // Clean up any previous formats that may have been set on this block + // that are unused. + block = el; + while ( isInline( block ) ) { + block = block.parentNode; + } + removeZWS( block, el ); + } + // Otherwise we find all the textnodes in the range (splitting + // partially selected nodes) and if they're not already formatted + // correctly we wrap them in the appropriate tag. + else { + // Create an iterator to walk over all the text nodes under this + // ancestor which are in the range and not already formatted + // correctly. + // + // In Blink/WebKit, empty blocks may have no text nodes, just a
. + // Therefore we wrap this in the tag as well, as this will then cause it + // to apply when the user types something in the block, which is + // presumably what was intended. + // + // IMG tags are included because we may want to create a link around + // them, and adding other styles is harmless. + walker = new TreeWalker( + range.commonAncestorContainer, + SHOW_TEXT|SHOW_ELEMENT, + function ( node ) { + return ( node.nodeType === TEXT_NODE || + node.nodeName === 'BR' || + node.nodeName === 'IMG' + ) && isNodeContainedInRange( range, node, true ); + } + ); + + // Start at the beginning node of the range and iterate through + // all the nodes in the range that need formatting. + startContainer = range.startContainer; + startOffset = range.startOffset; + endContainer = range.endContainer; + endOffset = range.endOffset; + + // Make sure we start with a valid node. + walker.currentNode = startContainer; + if ( !walker.filter( startContainer ) ) { + startContainer = walker.nextNode(); + startOffset = 0; + } + + // If there are no interesting nodes in the selection, abort + if ( !startContainer ) { + return range; + } + + do { + node = walker.currentNode; + needsFormat = !getNearest( node, root, tag, attributes ); + if ( needsFormat ) { + //
can never be a container node, so must have a text node + // if node == (end|start)Container + if ( node === endContainer && node.length > endOffset ) { + node.splitText( endOffset ); + } + if ( node === startContainer && startOffset ) { + node = node.splitText( startOffset ); + if ( endContainer === startContainer ) { + endContainer = node; + endOffset -= startOffset; + } + startContainer = node; + startOffset = 0; + } + el = this.createElement( tag, attributes ); + replaceWith( node, el ); + el.appendChild( node ); + } + } while ( walker.nextNode() ); + + // If we don't finish inside a text node, offset may have changed. + if ( endContainer.nodeType !== TEXT_NODE ) { + if ( node.nodeType === TEXT_NODE ) { + endContainer = node; + endOffset = node.length; + } else { + // If
, we must have just wrapped it, so it must have only + // one child + endContainer = node.parentNode; + endOffset = 1; + } + } + + // Now set the selection to as it was before + range = this.createRange( + startContainer, startOffset, endContainer, endOffset ); + } + return range; +}; + +proto._removeFormat = function ( tag, attributes, range, partial ) { + // Add bookmark + this._saveRangeToBookmark( range ); + + // We need a node in the selection to break the surrounding + // formatted text. + var doc = this._doc, + fixer; + if ( range.collapsed ) { + if ( cantFocusEmptyTextNodes ) { + fixer = doc.createTextNode( ZWS ); + this._didAddZWS(); + } else { + fixer = doc.createTextNode( '' ); + } + insertNodeInRange( range, fixer ); + } + + // Find block-level ancestor of selection + var root = range.commonAncestorContainer; + while ( isInline( root ) ) { + root = root.parentNode; + } + + // Find text nodes inside formatTags that are not in selection and + // add an extra tag with the same formatting. + var startContainer = range.startContainer, + startOffset = range.startOffset, + endContainer = range.endContainer, + endOffset = range.endOffset, + toWrap = [], + examineNode = function ( node, exemplar ) { + // If the node is completely contained by the range then + // we're going to remove all formatting so ignore it. + if ( isNodeContainedInRange( range, node, false ) ) { + return; + } + + var isText = ( node.nodeType === TEXT_NODE ), + child, next; + + // If not at least partially contained, wrap entire contents + // in a clone of the tag we're removing and we're done. + if ( !isNodeContainedInRange( range, node, true ) ) { + // Ignore bookmarks and empty text nodes + if ( node.nodeName !== 'INPUT' && + ( !isText || node.data ) ) { + toWrap.push([ exemplar, node ]); + } + return; + } + + // Split any partially selected text nodes. + if ( isText ) { + if ( node === endContainer && endOffset !== node.length ) { + toWrap.push([ exemplar, node.splitText( endOffset ) ]); + } + if ( node === startContainer && startOffset ) { + node.splitText( startOffset ); + toWrap.push([ exemplar, node ]); + } + } + // If not a text node, recurse onto all children. + // Beware, the tree may be rewritten with each call + // to examineNode, hence find the next sibling first. + else { + for ( child = node.firstChild; child; child = next ) { + next = child.nextSibling; + examineNode( child, exemplar ); + } + } + }, + formatTags = Array.prototype.filter.call( + root.getElementsByTagName( tag ), function ( el ) { + return isNodeContainedInRange( range, el, true ) && + hasTagAttributes( el, tag, attributes ); + } + ); + + if ( !partial ) { + formatTags.forEach( function ( node ) { + examineNode( node, node ); + }); + } + + // Now wrap unselected nodes in the tag + toWrap.forEach( function ( item ) { + // [ exemplar, node ] tuple + var el = item[0].cloneNode( false ), + node = item[1]; + replaceWith( node, el ); + el.appendChild( node ); + }); + // and remove old formatting tags. + formatTags.forEach( function ( el ) { + replaceWith( el, empty( el ) ); + }); + + // Merge adjacent inlines: + this._getRangeAndRemoveBookmark( range ); + if ( fixer ) { + range.collapse( false ); + } + mergeInlines( root, range ); + + return range; +}; + +proto.changeFormat = function ( add, remove, range, partial ) { + // Normalise the arguments and get selection + if ( !range && !( range = this.getSelection() ) ) { + return this; + } + + // Save undo checkpoint + this.saveUndoState( range ); + + if ( remove ) { + range = this._removeFormat( remove.tag.toUpperCase(), + remove.attributes || {}, range, partial ); + } + if ( add ) { + range = this._addFormat( add.tag.toUpperCase(), + add.attributes || {}, range ); + } + + this.setSelection( range ); + this._updatePath( range, true ); + + // We're not still in an undo state + if ( !canObserveMutations ) { + this._docWasChanged(); + } + + return this; +}; + +// --- Block formatting --- + +var tagAfterSplit = { + DT: 'DD', + DD: 'DT', + LI: 'LI', + PRE: 'PRE' +}; + +var splitBlock = function ( self, block, node, offset ) { + var splitTag = tagAfterSplit[ block.nodeName ], + splitProperties = null, + nodeAfterSplit = split( node, offset, block.parentNode, self._root ), + config = self._config; + + if ( !splitTag ) { + splitTag = config.blockTag; + splitProperties = config.blockAttributes; + } + + // Make sure the new node is the correct type. + if ( !hasTagAttributes( nodeAfterSplit, splitTag, splitProperties ) ) { + block = createElement( nodeAfterSplit.ownerDocument, + splitTag, splitProperties ); + if ( nodeAfterSplit.dir ) { + block.dir = nodeAfterSplit.dir; + } + replaceWith( nodeAfterSplit, block ); + block.appendChild( empty( nodeAfterSplit ) ); + nodeAfterSplit = block; + } + return nodeAfterSplit; +}; + +proto.forEachBlock = function ( fn, mutates, range ) { + if ( !range && !( range = this.getSelection() ) ) { + return this; + } + + // Save undo checkpoint + if ( mutates ) { + this.saveUndoState( range ); + } + + var root = this._root; + var start = getStartBlockOfRange( range, root ); + var end = getEndBlockOfRange( range, root ); + if ( start && end ) { + do { + if ( fn( start ) || start === end ) { break; } + } while ( start = getNextBlock( start, root ) ); + } + + if ( mutates ) { + this.setSelection( range ); + + // Path may have changed + this._updatePath( range, true ); + + // We're not still in an undo state + if ( !canObserveMutations ) { + this._docWasChanged(); + } + } + return this; +}; + +proto.modifyBlocks = function ( modify, range ) { + if ( !range && !( range = this.getSelection() ) ) { + return this; + } + + // 1. Save undo checkpoint and bookmark selection + this._recordUndoState( range, this._isInUndoState ); + + var root = this._root; + var frag; + + // 2. Expand range to block boundaries + expandRangeToBlockBoundaries( range, root ); + + // 3. Remove range. + moveRangeBoundariesUpTree( range, root, root, root ); + frag = extractContentsOfRange( range, root, root ); + + // 4. Modify tree of fragment and reinsert. + insertNodeInRange( range, modify.call( this, frag ) ); + + // 5. Merge containers at edges + if ( range.endOffset < range.endContainer.childNodes.length ) { + mergeContainers( range.endContainer.childNodes[ range.endOffset ], root ); + } + mergeContainers( range.startContainer.childNodes[ range.startOffset ], root ); + + // 6. Restore selection + this._getRangeAndRemoveBookmark( range ); + this.setSelection( range ); + this._updatePath( range, true ); + + // 7. We're not still in an undo state + if ( !canObserveMutations ) { + this._docWasChanged(); + } + + return this; +}; + +var increaseBlockQuoteLevel = function ( frag ) { + return this.createElement( 'BLOCKQUOTE', + this._config.tagAttributes.blockquote, [ + frag + ]); +}; + +var decreaseBlockQuoteLevel = function ( frag ) { + var blockquotes = frag.querySelectorAll( 'blockquote' ); + Array.prototype.filter.call( blockquotes, function ( el ) { + return !getNearest( el.parentNode, frag, 'BLOCKQUOTE' ); + }).forEach( function ( el ) { + replaceWith( el, empty( el ) ); + }); + return frag; +}; + +var removeBlockQuote = function (/* frag */) { + return this.createDefaultBlock([ + this.createElement( 'INPUT', { + id: startSelectionId, + type: 'hidden' + }), + this.createElement( 'INPUT', { + id: endSelectionId, + type: 'hidden' + }) + ]); +}; + +var makeList = function ( self, frag, type ) { + var walker = getBlockWalker( frag, self._root ), + node, tag, prev, newLi, + tagAttributes = self._config.tagAttributes, + listAttrs = tagAttributes[ type.toLowerCase() ], + listItemAttrs = tagAttributes.li; + + while ( node = walker.nextNode() ) { + if ( node.parentNode.nodeName === 'LI' ) { + node = node.parentNode; + walker.currentNode = node.lastChild; + } + if ( node.nodeName !== 'LI' ) { + newLi = self.createElement( 'LI', listItemAttrs ); + if ( node.dir ) { + newLi.dir = node.dir; + } + + // Have we replaced the previous block with a new