diff --git a/README.md b/README.md index 70c83b3..269dba9 100644 --- a/README.md +++ b/README.md @@ -1,212 +1,206 @@ -# Web Interface for Retroshare +# RetroShare Web Interface -A web-based frontend for [Retroshare](https://github.com/Retroshare/Retroshare) -which communicates with the client through the JSON API. +This project is the browser interface for +[RetroShare](https://github.com/RetroShare/RetroShare). It talks to RetroShare +through its JSON API. -## Requirements +Recent RetroShare releases already include this interface. Follow this guide if +you want to build it yourself, change it, or contribute to it. -- Retroshare v0.6.5+ with JSON API enabled(see instructions below) -- A modern JavaScript-enabled web browser -- [`qmake`](https://doc.qt.io/qt-5/qmake-manual.html)(optional) +## What you need -## Installation +- RetroShare 0.6.5 or newer, with the JSON API enabled +- A modern web browser -> **Note:** The Web Interface is shipped by default in the latest release of -> Retroshare. If you want to customise it or [contribute](#contributing) to it -> then proceed with the following steps. +## Set up RetroShare -### Install WebUI +### Enable the JSON API -First, you need to download and install the web interface javascript code -itself: +1. Open RetroShare. +2. Go to **Preferences > JSON API**. +3. Enable **RetroShare JSON API Server**. -1. **Clone the repo**: - You can clone using git, or download the zip file and extract it +### Enable the Web Interface - ```bash - git clone https://github.com/Retroshare/RSNewWebUI - cd RSNewWebUI - ``` +1. Go to **Preferences > Web Interface**. +2. Enable the Web Interface. +3. Choose a password. +4. Set **Web interface directory** to this repository's `webui/` directory if + RetroShare does not find it automatically. +5. Click **Apply**. -2. **Build the files**: - If you have `qmake` installed, you just need to run it in the base directory: +The JSON API page should now show an authenticated token named +`webui:`. - ```bash - qmake . - ``` +## Open the interface - If you do not have `qmake`, go to `webui-src/make-src/` and run the build - script. Note that qmake is enough to do the build. +Open [https://localhost:9092/index.html](https://localhost:9092/index.html) in +your browser. If you changed the JSON API port, replace `9092` with that port. - ##### On Linux/MacOS: +### Connect to a remote or headless server - ```bash - cd webui-src/make-src - sh build.sh - ``` +The Web Interface listens on localhost by default. To reach RetroShare on a +remote server, run this command on your local computer: - ##### On Windows: +```sh +ssh -L 9092:localhost:9092 -N login@server +``` - ```bash - cd webui-src\make-src\ - ./build.bat - ``` +Keep that command running, then open +[https://localhost:9092/index.html](https://localhost:9092/index.html). -### Compile Retroshare with JSON API +The Web Interface cannot create a new RetroShare node. Create the node with the +desktop interface first, then copy its RetroShare data directory to the server. +On Linux this directory is usually `.retroshare/`. -If you are on older versions of Retroshare then it needs to be compiled with -non-default options as follows: +Start the service on the server with: -```bash +```sh +./retroshare-service/src/retroshare-service -U list -W +``` + +Follow the prompts to select the profile and enter its passwords. + +## Contributing + +To work on the source, you need: + +- Node.js 24 or newer and npm +- `sh` on macOS or Linux; Windows uses the included batch file +- `qmake` only if you want to use RetroShare's qmake build integration + +### First-time setup + +Fork the repository, clone your fork, and install the locked dependencies: + +```sh +git clone https://github.com/YOUR-USERNAME/RSNewWebUI.git +cd RSNewWebUI/webui-src +npm ci +``` + +### Where to make changes + +- JavaScript source is in `webui-src/app/`. +- SCSS source is in `webui-src/app/scss/`. +- `webui-src/styles.css` is generated from `app/scss/main.scss`. Do not edit it + by hand, but commit it with the SCSS changes that generated it. +- Everything in `webui/` is build output. Do not edit those files by hand. +- `webui-src/app/mithril.js` contains Mithril 2.3.8. Change it only when + intentionally upgrading Mithril. + +### Normal development commands + +Run these commands from `webui-src/`: + +```sh +npm run build +npm run lint +``` + +`npm run build` compiles the SCSS and creates the complete `webui/` directory. +It automatically uses `build.bat` on Windows and `build.sh` on macOS or Linux. + +`npm run lint` checks the source files and does not require `webui/` to exist. + +To rebuild CSS whenever an SCSS file changes, run: + +```sh +npm run watch +``` + +Watch mode updates `webui-src/styles.css` only. Run `npm run build` when you +also need to update the files in `webui/`. + +### Using qmake during development + +Run qmake from the repository root: + +```sh +qmake . +``` + +qmake copies the committed `webui-src/styles.css`; it does not compile SCSS. +After changing SCSS, run `npm run build` first. Rerun `qmake .` when you need +qmake to package newer WebUI files; `make` alone does not do that for this +project. + +A standalone checkout may show a warning about a missing `../retroshare.pri`. +The WebUI files are still generated. + +### Building without npm or qmake + +These commands create `webui/` from the files already stored in the repository. +They do not compile SCSS. + +From the repository root on macOS or Linux: + +```sh +sh webui-src/make-src/build.sh +``` + +From the repository root on Windows: + +```bat +webui-src\make-src\build.bat +``` + +### Optional focused builds + +The normal `npm run build` command is the easiest choice. On macOS or Linux, +you can rebuild only one generated file from `webui-src/`: + +```sh +# JavaScript only +sh make-src/build.sh "" app.js + +# HTML only +sh make-src/build.sh "" index.html + +# CSS only +./node_modules/.bin/sass --no-source-map --style=compressed app/scss/main.scss styles.css +sh make-src/build.sh "" styles.css +``` + +### Before opening a pull request + +1. Run `npm run build`. +2. Run `npm run lint`. +3. Point RetroShare's **Web interface directory** to this repository's + `webui/` directory. +4. Manually test the screens you changed. +5. If you changed SCSS, include the regenerated `webui-src/styles.css`. + +There is no separate formatting command. + +### Building older RetroShare versions + +If you are testing with an older RetroShare version, you may need to build it +with the JSON API and WebUI enabled: + +```sh qmake CONFIG+="debug rs_jsonapi rs_webui" make ``` -See the [RetroShare repo](https://github.com/Retroshare/Retroshare) for more -detailed instructions on compiling RetroShare. You should afterwards see a tab -'JSON API' and a tab 'Web Interface' in the **Preferences**. +See the [RetroShare repository](https://github.com/RetroShare/RetroShare) for +complete build instructions. After building, RetroShare preferences should +contain both **JSON API** and **Web Interface** pages. -### Enable JSON API +### Useful references -You need to enable the JSON API, through which the web interface communicates -with the client: +- [Mithril documentation](https://mithril.js.org/) +- [RetroShare source](https://github.com/RetroShare/RetroShare) -1. Open Retroshare, go to `Preferences > JSON API`. -2. Make sure the **Enable Retroshare JSON API Server** box is checked. - -### Enable Web Interface - -1. Go to `Preferences > Webinterface`. -2. Make sure the **Enable Retroshare WEB Interface** box is checked. -3. Enter a password to protect access to the web interface. - -If necessary, point the **Web interface directory** to the place where the webui -files are compiled. This is usually `RSNewWebUI/webui/`. - -In any case, click on "Apply settings" after making the changes. If everything -goes ok, you should see a new token `webui:[your password]` under the -**Authenticated Tokens** section in the **JSON API** preferences page. - -## Usage - -### Basic Usage - -This is the default link to access the WebInterface. -
-Open this link your browser -> -[https://localhost:9092/index.html](https://localhost:9092/index.html). - -> Note: If you changed the port in the JSON API preferences pages, the port -> in the above line needs to be changed accordingly. - -### Advanced Usage - -The Web interface is only accessible from localhost (127.0.0.1). If you want to -access the web interface of a headless retroshare server, then you need to -create a SSH tunnel as follows: - -``` -ssh login@server -L 9092:localhost:9092 -N -``` - -After that, the Web interface of the Retroshare running on 'server' is tunneled -to your local machine and accessible through localhost:9092. - -Running a headless retroshare server is one possibility. The Webinterface -however does not allow you to create new nodes. Therefore the steps are: - -1. Create a node using the standard Qt UI. That can be done in another machine. -2. Copy the retroshare data directory (.retroshare/ on linux) on the server. -3. On the server, launch a headless retroshare using that node: - ``` - ./retroshare-service/src/retroshare-service -U list -W - ``` - -After that follow instructions to launch your profile (you need to choose a -webui password and enter the ID and login password of your node). - -## Contributing - -For contributing, It is recommended that you read this entire section to have a -better idea. - -### Setup WebUI for contributing - -Follow these steps to setup the project and make it ready for -contribution/customisation : - -- Fork and Clone this repository to your local machine. -- `cd` into the cloned repo: - ``` - cd RSNewWebUI - ``` -- Run this command to install the dependecies for the project. - ``` - pnpm install - ``` - -### Run the WebUI - -- Run this command to watch for any changes in the `scss` files and compile them - to css. - - ``` - pnpm watch - ``` - -- You can now start to edit the source code. But you must run the below command - to see the changes reflected in the browser everytime you edit the code for - webui which is in the `webui-src/app/` directory. - - ``` - qmake . - ``` - -### Linting and Formatting - -Linting and formatting can be done with editor/IDE plugins. - -Or to do it manually, - -Install `prettier` and `eslint` (required for linting & formatting code): +To find RetroShare headers that expose JSON API methods, run this from +`libretroshare/src/retroshare/` in the RetroShare source tree: ```sh -npm install -g prettier eslint +grep -c "@jsonapi" *.h | grep -v ":0" ``` -Next, run the following in the `webui-src` directory: +## Get help or report a problem -To run the linter: `eslint app` - -To run the formatter: `prettier -c app` - -### References - -Now, While contributing you can checkout these resources as you might need to -look up for these often. - -- [mithril](https://mithril.js.org/hyperscript.html) -- You can list files with @jsonapi in libretroshare/src/retroshare of - [retroshare](https://github.com/RetroShare/RetroShare): - - ``` - grep -c "@jsonapi" *.h|grep -v ":0" - ``` - -
- -And, that's it. You are more than welcome to contribute to this project. If you -have any questions/difficulties in setting up or running the project, you can -raise an issue and we will be more than willing to help you out. - -### Bug Reports & Feature requests - -Please create an [issue](https://github.com/Retroshare/RsNewWebUI/issues) -concisely describing the bug you faced, or the feature you would like to see -implemented. - -### Development - -Whether you are a JavaScript developer or a Web designer, you can help make the -web interface better. Get in touch with us on the Developer forums in -Retroshare. +Open a [GitHub issue](https://github.com/RetroShare/RSNewWebUI/issues) and +briefly explain what happened, what you expected, and how someone can reproduce +it. You can also join the RetroShare developer forums to discuss development. diff --git a/webui-src/.eslintrc.json b/webui-src/.eslintrc.json deleted file mode 100644 index ac99773..0000000 --- a/webui-src/.eslintrc.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "root": true, - "env": { - "browser": true, - "commonjs": true, - "es6": true - }, - "extends": "eslint:recommended", - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly", - "window": "readonly", - "document": "readonly" - }, - "parserOptions": { - "ecmaVersion": 2020 - }, - "ignorePatterns": ["mithril.js", "assets/*", "make-src/*"], - "rules": { - "linebreak-style": ["off", "unix"], - "quotes": ["error", "single"], - "semi": ["error", "always"], - "arrow-spacing": ["error", { "before": true, "after": true }], - "arrow-parens": ["error", "always"], - "comma-style": ["error", "last"], - "comma-spacing": ["error", { "before": false, "after": true }], - "camelcase": [ - "error", - { - "allow": ["^UNSAFE_"], - "properties": "never", - "ignoreGlobals": true - } - ], - "eol-last": "error", - "eqeqeq": ["error", "always", { "null": "ignore" }], - "no-irregular-whitespace": ["error"], - "no-trailing-spaces": ["error"], - "no-unexpected-multiline": ["error"], - "no-unreachable": ["error"], - "no-var": "warn", - "no-unused-vars": [ - "error", - { - "args": "none", - "caughtErrors": "none", - "ignoreRestSiblings": true, - "vars": "all" - } - ], - "no-use-before-define": ["warn", { "functions": true, "classes": false }], - "object-shorthand": "error", - "prefer-const": ["error", { "destructuring": "all" }], - "semi-style": ["warn", "last"], - "spaced-comment": ["warn", "always"] - } -} diff --git a/webui-src/.nvmrc b/webui-src/.nvmrc new file mode 100644 index 0000000..f3c8820 --- /dev/null +++ b/webui-src/.nvmrc @@ -0,0 +1 @@ +v24.15.0 diff --git a/webui-src/app/boards/board_view.js b/webui-src/app/boards/board_view.js index 7a69e67..fe60498 100644 --- a/webui-src/app/boards/board_view.js +++ b/webui-src/app/boards/board_view.js @@ -39,7 +39,7 @@ function createboard() { m('label[for=thumbnail]', 'Thumbnail: '), m('input[type=file][name=files][id=thumbnail][accept=image/*]', { onchange: async (e) => { - let reader = new FileReader(); + const reader = new FileReader(); reader.onloadend = function () { thumbnail = reader.result.substring(reader.result.indexOf(',') + 1); }; diff --git a/webui-src/app/boards/boards_util.js b/webui-src/app/boards/boards_util.js index 15c72eb..1519b81 100644 --- a/webui-src/app/boards/boards_util.js +++ b/webui-src/app/boards/boards_util.js @@ -1,17 +1,17 @@ const m = require('mithril'); const rs = require('rswebui'); -const GROUP_SUBSCRIBE_ADMIN = 0x01;// means: you have the admin key for this group -const GROUP_SUBSCRIBE_PUBLISH = 0x02;// means: you have the publish key for thiss group. Typical use: publish key in channels are shared with specific friends. -const GROUP_SUBSCRIBE_SUBSCRIBED = 0x04;// means: you are subscribed to a group, which makes you a source for this group to your friend nodes. +const GROUP_SUBSCRIBE_ADMIN = 0x01; // means: you have the admin key for this group +const GROUP_SUBSCRIBE_PUBLISH = 0x02; // means: you have the publish key for thiss group. Typical use: publish key in channels are shared with specific friends. +const GROUP_SUBSCRIBE_SUBSCRIBED = 0x04; // means: you are subscribed to a group, which makes you a source for this group to your friend nodes. const GROUP_SUBSCRIBE_NOT_SUBSCRIBED = 0x08; const GROUP_MY_BOARD = GROUP_SUBSCRIBE_ADMIN + GROUP_SUBSCRIBE_SUBSCRIBED + GROUP_SUBSCRIBE_PUBLISH; const GXS_VOTE_DOWN = 0x0001; const GXS_VOTE_UP = 0x0002; -//rsgxscircles.h:50 -const PUBLIC = 1; /// Public distribution -const EXTERNAL = 2; /// Restricted to an external circle, based on GxsIds +// rsgxscircles.h:50 +const PUBLIC = 1; // Public distribution +const EXTERNAL = 2; // Restricted to an external circle, based on GxsIds const NODES_GROUP = 3; const Data = { @@ -20,34 +20,6 @@ const Data = { Comments: {}, // threadID, msgID -> {Comment, showReplies} }; -async function updateContent(content, boardid) { - const res = await rs.rsJsonApiRequest('/rsPosted/getBoardContent', { - boardId: boardid, - contentsIds: [content.mMsgId], - }); - if (res.body.retval && res.body.posts.length > 0) { - Data.Posts[boardid][content.mMsgId] = { post: res.body.posts[0], isSearched: true }; - } else if (res.body.retval && res.body.comments.length > 0) { - if (Data.Comments[content.mThreadId] === undefined) { - Data.Comments[content.mThreadId] = {}; - } - Data.Comments[content.mThreadId][content.mMsgId] = res.body.comments[0]; - } else if (res.body.retval && res.body.votes.length > 0) { - const vote = res.body.votes[0]; - if ( - Data.Comments[vote.mMeta.mThreadId] && - Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId] - ) { - if (vote.mVoteType === GXS_VOTE_UP) { - Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].mUpVotes += 1; - } - if (vote.mVoteType === GXS_VOTE_DOWN) { - Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].mDownVotes += 1; - } - } - } -} - async function updateDisplayBoards(keyid, details) { const res1 = await rs.rsJsonApiRequest('/rsPosted/getBoardsInfo', { boardsIds: [keyid], @@ -72,7 +44,7 @@ async function updateDisplayBoards(keyid, details) { Data.Posts[keyid] = {}; } - /*const res2 = await rs.rsJsonApiRequest('/rsPosted/getContentSummaries', { + /* const res2 = await rs.rsJsonApiRequest('/rsPosted/getContentSummaries', { boardId: keyid, }); @@ -81,7 +53,6 @@ async function updateDisplayBoards(keyid, details) { updateContent(content, keyid); }); }*/ - } const DisplayBoardsFromList = () => { diff --git a/webui-src/app/channels/channel_view.js b/webui-src/app/channels/channel_view.js index 81adda2..31101cc 100644 --- a/webui-src/app/channels/channel_view.js +++ b/webui-src/app/channels/channel_view.js @@ -109,7 +109,7 @@ function createchannel() { m('label[for=thumbnail]', 'Thumbnail: '), m('input[type=file][name=files][id=thumbnail][accept=image/*]', { onchange: async (e) => { - let reader = new FileReader(); + const reader = new FileReader(); reader.onloadend = function () { thumbnail = reader.result.substring(reader.result.indexOf(',') + 1); }; @@ -238,7 +238,7 @@ const AddPost = () => { m('label[for=thumbnail]', 'Thumbnail: '), m('input[type=file][name=files][id=thumbnail][accept=image/*]', { onchange: async (e) => { - let reader = new FileReader(); + const reader = new FileReader(); reader.onloadend = function () { pthumbnail = reader.result.substring(reader.result.indexOf(',') + 1); }; diff --git a/webui-src/app/chat/chat.js b/webui-src/app/chat/chat.js index 6f27b49..ff8e215 100644 --- a/webui-src/app/chat/chat.js +++ b/webui-src/app/chat/chat.js @@ -7,18 +7,11 @@ const chatEmoji = require('chat/chat_emoji'); const HistoryBrowserModal = require('people/people_history'); const { - get64Num, - loadLobbyDetails, - loadDistantChatDetails, sortLobbies, - getNicknameColor, getStatusColor, getStatusTooltip, - renderTextWithEmoji, getSafeAvatar, - MobileState, ChatRoomsModel, - Message, ChatLobbyModel, ChatHubState, } = chatState; @@ -123,13 +116,13 @@ function pollHashStatus(localpath) { const info = data.info; const sizeNum = info.size.xint64 || parseInt(info.size.xstr64) || info.size; const fileLink = `${info.name} (${rs.formatBytes(sizeNum)})`; - + const textarea = document.querySelector('.chat-hub-textarea'); if (textarea) { const val = textarea.value; textarea.value = val ? val + '\n' + fileLink : fileLink; } - + ChatHubState.showAttachModal = false; ChatHubState.isHashing = false; ChatHubState.attachPath = ''; @@ -144,75 +137,6 @@ function pollHashStatus(localpath) { // ************************* views **************************** -const Lobby = () => { - return { - view: (vnode) => { - const { info, tagname, onclick, lobbytagname = 'mainname' } = vnode.attrs; - return m( - ChatLobbyModel.selected(info, '.selected-lobby', tagname), - { - key: rs.idToHex(info.lobby_id), - onclick, - }, - [ - m('h5', { class: lobbytagname }, info.lobby_name === '' ? '' : info.lobby_name), - m('.topic', info.lobby_topic), - ] - ); - }, - }; -}; - -const LobbyList = { - view(vnode) { - const tagname = vnode.attrs.tagname; - const lobbytagname = vnode.attrs.lobbytagname; - const onclick = vnode.attrs.onclick || (() => null); - return [ - vnode.attrs.rooms.map((info) => - m(Lobby, { - info, - tagname, - lobbytagname, - onclick: onclick(info), - }) - ), - ]; - }, -}; - -const SubscribedLobbies = { - view() { - return m('.widget', [ - m('.widget__heading', m('h3', 'Subscribed chat rooms')), - m('.widget__body', [ - m(LobbyList, { - rooms: sortLobbies(Object.values(ChatRoomsModel.subscribedRooms)), - tagname: '.lobby.subscribed', - onclick: ChatLobbyModel.switchToEvent, - }), - ]), - ]); - }, -}; - -const PublicLobbies = { - view() { - return m('.widget', [ - m('.widget__heading', m('h3', 'Public chat rooms')), - m('.widget__body', [ - m(LobbyList, { - rooms: (ChatRoomsModel.allRooms || []).filter((info) => !ChatRoomsModel.subscribed(info)), - tagname: '.lobby.public', - onclick: ChatLobbyModel.setupEvent, - }), - ]), - ]); - }, -}; - -// ************************* Chat Hub Sub-Components **************************** - const ChatRoomHeader = () => { return { view: (vnode) => { @@ -982,8 +906,6 @@ const ChatRoomDetailView = () => { const room = ChatHubState.selectedRoom; if (!room) return null; - let participantCount = 0; - let participantNames = []; let participants = []; if (room.gxs_ids) { @@ -994,7 +916,7 @@ const ChatRoomDetailView = () => { })); } else if (typeof room.gxs_ids === 'object') { participants = Object.keys(room.gxs_ids).map((key) => ({ - key: key, + key, name: rs.userList.username(key) || key })); } @@ -1011,8 +933,8 @@ const ChatRoomDetailView = () => { } } - participantCount = participants.length; - participantNames = participants.map((p) => p.name); + const participantCount = participants.length; + const participantNames = participants.map((p) => p.name); participantNames.sort((a, b) => a.localeCompare(b)); const lobbyHexId = rs.idToHex(room.lobby_id); @@ -1316,7 +1238,7 @@ const Layout = { ChatHubState.showCreateRoomModal && m('.attach-modal-overlay', [ m('.attach-modal', [ m('h4', 'Create New Chat Room'), - + m('.form-field', { style: 'display: flex; flex-direction: column; gap: 0.25rem;' }, [ m('label', { style: 'font-weight: bold; font-size: 0.9rem; color: #475569;' }, 'Room Name:'), m('input[type=text]', { @@ -1326,7 +1248,7 @@ const Layout = { style: 'padding: 0.5rem; border: 1px solid #cbd5e1; border-radius: 0.25rem; font-size: 0.9rem;' }) ]), - + m('.form-field', { style: 'display: flex; flex-direction: column; gap: 0.25rem; margin-top: 0.5rem;' }, [ m('label', { style: 'font-weight: bold; font-size: 0.9rem; color: #475569;' }, 'Topic:'), m('input[type=text]', { @@ -1344,7 +1266,7 @@ const Layout = { onchange: (e) => { ChatHubState.newRoomIdentity = e.target.value; }, style: 'padding: 0.5rem; border: 1px solid #cbd5e1; border-radius: 0.25rem; font-size: 0.9rem; background-color: #ffffff;' }, [ - ChatHubState.ownGxsIdentities && ChatHubState.ownGxsIdentities.map(id => { + ChatHubState.ownGxsIdentities && ChatHubState.ownGxsIdentities.map((id) => { const details = ChatHubState.gxsDetails[id]; const name = details ? (details.mNickname || details.mGroupName) : id; return m('option', { value: id }, name); @@ -1386,7 +1308,7 @@ const Layout = { let flags = 0; if (isPublic) flags |= 4; if (isSigned) flags |= 8; - + rs.rsJsonApiRequest('/rsChats/createChatLobby', { lobby_name: name, lobby_identity: identity, @@ -1601,74 +1523,6 @@ const Layout = { }, }; -const LayoutSingle = () => { - const onResize = () => { - const element = document.querySelector('.messages'); - if (element) element.scrollTop = element.scrollHeight; - }; - return { - oninit: () => { - ChatLobbyModel.loadLobby(m.route.param('lobby')); - window.addEventListener('resize', onResize); - }, - onremove: () => window.removeEventListener('resize', onResize), - view: (vnode) => { - const chatType = ChatLobbyModel.currentLobby.chatType; - const isPrivate = chatType === 1 || chatType === 2; - const isRoom = chatType === 3; - return m( - '.node-panel.chat-panel.chat-room', - { - class: - (MobileState.showLobbies ? 'show-lobbies ' : '') + - (MobileState.showUsers ? 'show-users ' : '') + - (isPrivate ? 'no-lobbies' : ''), - }, - [ - m('.chat-overlay', { onclick: () => MobileState.closeAll() }), - m( - '.messages' + (isRoom ? '.compact-container' : ''), - { onclick: () => MobileState.closeAll() }, - ChatLobbyModel.messages - ), - m( - '.chatMessage', - {}, - [ - m('textarea.chatMsg', { - placeholder: 'Type a message...', - enterkeyhint: 'send', - onkeydown: (e) => { - if ((e.key === 'Enter' || e.keyCode === 13) && !e.shiftKey) { - const msg = e.target.value; - if (msg.trim() === '') return false; - e.target.value = ' sending ... '; - ChatLobbyModel.sendMessage(msg, () => (e.target.value = '')); - return false; - } - }, - }), - m( - 'button.chat-send-btn', - { - onclick: (e) => { - const textarea = e.target.closest('.chatMessage').querySelector('textarea'); - const msg = textarea.value; - if (msg.trim() === '') return; - textarea.value = ' sending ... '; - ChatLobbyModel.sendMessage(msg, () => (textarea.value = '')); - }, - }, - m('i.fas.fa-paper-plane') - ), - ] - ), - ] - ); - }, - }; -}; - /* /rsChats/initiateDistantChatConnexion * @param[in] to_pid RsGxsId to start the connection diff --git a/webui-src/app/chat/chat_emoji.js b/webui-src/app/chat/chat_emoji.js index 32da6d9..bfff2bb 100644 --- a/webui-src/app/chat/chat_emoji.js +++ b/webui-src/app/chat/chat_emoji.js @@ -7,72 +7,72 @@ const EMOJI_ICONS = { }; const EMOJI_DATA = { Smileys: [ - '๐Ÿ˜€','๐Ÿ˜','๐Ÿ˜‚','๐Ÿคฃ','๐Ÿ˜ƒ','๐Ÿ˜„','๐Ÿ˜…','๐Ÿ˜†','๐Ÿ˜‰','๐Ÿ˜Š','๐Ÿ˜‹','๐Ÿ˜Ž','๐Ÿ˜','๐Ÿ˜˜','๐Ÿฅฐ','๐Ÿ˜—','๐Ÿ˜™','๐Ÿ˜š', - '๐Ÿ™‚','๐Ÿค—','๐Ÿคฉ','๐Ÿค”','๐Ÿคจ','๐Ÿ˜','๐Ÿ˜‘','๐Ÿ˜ถ','๐Ÿ™„','๐Ÿ˜','๐Ÿ˜ฃ','๐Ÿ˜ฅ','๐Ÿ˜ฎ','๐Ÿค','๐Ÿ˜ฏ','๐Ÿ˜ช','๐Ÿ˜ซ','๐Ÿฅฑ', - '๐Ÿ˜ด','๐Ÿ˜Œ','๐Ÿ˜›','๐Ÿ˜œ','๐Ÿ˜','๐Ÿคค','๐Ÿ˜’','๐Ÿ˜“','๐Ÿ˜”','๐Ÿ˜•','๐Ÿ™ƒ','๐Ÿค‘','๐Ÿ˜ฒ','โ˜น๏ธ','๐Ÿ™','๐Ÿ˜–','๐Ÿ˜ž','๐Ÿ˜Ÿ', - '๐Ÿ˜ค','๐Ÿ˜ข','๐Ÿ˜ญ','๐Ÿ˜ฆ','๐Ÿ˜ง','๐Ÿ˜จ','๐Ÿ˜ฉ','๐Ÿคฏ','๐Ÿ˜ฌ','๐Ÿ˜ฐ','๐Ÿ˜ฑ','๐Ÿฅต','๐Ÿฅถ','๐Ÿ˜ณ','๐Ÿคช','๐Ÿ˜ต','๐Ÿ˜ก','๐Ÿ˜ ', - '๐Ÿคฌ','๐Ÿ˜ท','๐Ÿค’','๐Ÿค•','๐Ÿคข','๐Ÿคฎ','๐Ÿคง','๐Ÿฅด','๐Ÿ˜‡','๐Ÿฅณ','๐Ÿฅบ','๐Ÿค ','๐Ÿคก','๐Ÿคฅ','๐Ÿคซ','๐Ÿคญ','๐Ÿง','๐Ÿค“', - '๐Ÿ˜ˆ','๐Ÿ‘ฟ','๐Ÿ‘น','๐Ÿ‘บ','๐Ÿ’€','โ˜ ๏ธ','๐Ÿ‘ป','๐Ÿ‘ฝ','๐Ÿ‘พ','๐Ÿค–','๐Ÿ˜บ','๐Ÿ˜ธ','๐Ÿ˜น','๐Ÿ˜ป','๐Ÿ˜ผ','๐Ÿ˜ฝ','๐Ÿ™€','๐Ÿ˜ฟ','๐Ÿ˜พ', + '๐Ÿ˜€', '๐Ÿ˜', '๐Ÿ˜‚', '๐Ÿคฃ', '๐Ÿ˜ƒ', '๐Ÿ˜„', '๐Ÿ˜…', '๐Ÿ˜†', '๐Ÿ˜‰', '๐Ÿ˜Š', '๐Ÿ˜‹', '๐Ÿ˜Ž', '๐Ÿ˜', '๐Ÿ˜˜', '๐Ÿฅฐ', '๐Ÿ˜—', '๐Ÿ˜™', '๐Ÿ˜š', + '๐Ÿ™‚', '๐Ÿค—', '๐Ÿคฉ', '๐Ÿค”', '๐Ÿคจ', '๐Ÿ˜', '๐Ÿ˜‘', '๐Ÿ˜ถ', '๐Ÿ™„', '๐Ÿ˜', '๐Ÿ˜ฃ', '๐Ÿ˜ฅ', '๐Ÿ˜ฎ', '๐Ÿค', '๐Ÿ˜ฏ', '๐Ÿ˜ช', '๐Ÿ˜ซ', '๐Ÿฅฑ', + '๐Ÿ˜ด', '๐Ÿ˜Œ', '๐Ÿ˜›', '๐Ÿ˜œ', '๐Ÿ˜', '๐Ÿคค', '๐Ÿ˜’', '๐Ÿ˜“', '๐Ÿ˜”', '๐Ÿ˜•', '๐Ÿ™ƒ', '๐Ÿค‘', '๐Ÿ˜ฒ', 'โ˜น๏ธ', '๐Ÿ™', '๐Ÿ˜–', '๐Ÿ˜ž', '๐Ÿ˜Ÿ', + '๐Ÿ˜ค', '๐Ÿ˜ข', '๐Ÿ˜ญ', '๐Ÿ˜ฆ', '๐Ÿ˜ง', '๐Ÿ˜จ', '๐Ÿ˜ฉ', '๐Ÿคฏ', '๐Ÿ˜ฌ', '๐Ÿ˜ฐ', '๐Ÿ˜ฑ', '๐Ÿฅต', '๐Ÿฅถ', '๐Ÿ˜ณ', '๐Ÿคช', '๐Ÿ˜ต', '๐Ÿ˜ก', '๐Ÿ˜ ', + '๐Ÿคฌ', '๐Ÿ˜ท', '๐Ÿค’', '๐Ÿค•', '๐Ÿคข', '๐Ÿคฎ', '๐Ÿคง', '๐Ÿฅด', '๐Ÿ˜‡', '๐Ÿฅณ', '๐Ÿฅบ', '๐Ÿค ', '๐Ÿคก', '๐Ÿคฅ', '๐Ÿคซ', '๐Ÿคญ', '๐Ÿง', '๐Ÿค“', + '๐Ÿ˜ˆ', '๐Ÿ‘ฟ', '๐Ÿ‘น', '๐Ÿ‘บ', '๐Ÿ’€', 'โ˜ ๏ธ', '๐Ÿ‘ป', '๐Ÿ‘ฝ', '๐Ÿ‘พ', '๐Ÿค–', '๐Ÿ˜บ', '๐Ÿ˜ธ', '๐Ÿ˜น', '๐Ÿ˜ป', '๐Ÿ˜ผ', '๐Ÿ˜ฝ', '๐Ÿ™€', '๐Ÿ˜ฟ', '๐Ÿ˜พ', ], People: [ - '๐Ÿ‘‹','๐Ÿคš','๐Ÿ–๏ธ','โœ‹','๐Ÿ––','๐Ÿ‘Œ','๐ŸคŒ','๐Ÿค','โœŒ๏ธ','๐Ÿคž','๐ŸคŸ','๐Ÿค˜','๐Ÿค™','๐Ÿ‘ˆ','๐Ÿ‘‰','๐Ÿ‘†','๐Ÿ–•','๐Ÿ‘‡', - 'โ˜๏ธ','๐Ÿ‘','๐Ÿ‘Ž','โœŠ','๐Ÿ‘Š','๐Ÿค›','๐Ÿคœ','๐Ÿ‘','๐Ÿ™Œ','๐Ÿ‘','๐Ÿคฒ','๐Ÿค','๐Ÿ™','โœ๏ธ','๐Ÿ’…','๐Ÿคณ','๐Ÿ’ช','๐Ÿฆพ', - '๐Ÿฆฟ','๐Ÿฆต','๐Ÿฆถ','๐Ÿ‘‚','๐Ÿฆป','๐Ÿ‘ƒ','๐Ÿซ€','๐Ÿซ','๐Ÿง ','๐Ÿฆท','๐Ÿฆด','๐Ÿ‘€','๐Ÿ‘๏ธ','๐Ÿ‘…','๐Ÿ‘„','๐Ÿซฆ','๐Ÿ‘ถ','๐Ÿง’', - '๐Ÿ‘ฆ','๐Ÿ‘ง','๐Ÿง‘','๐Ÿ‘ฑ','๐Ÿ‘จ','๐Ÿง”','๐Ÿ‘ฉ','๐Ÿง“','๐Ÿ‘ด','๐Ÿ‘ต','๐Ÿ™','๐Ÿ™Ž','๐Ÿ™…','๐Ÿ™†','๐Ÿ’','๐Ÿ™‹','๐Ÿง','๐Ÿ™‡', - '๐Ÿคฆ','๐Ÿคท','๐Ÿ‘ฎ','๐Ÿ•ต๏ธ','๐Ÿ’‚','๐Ÿฅท','๐Ÿ‘ท','๐Ÿซ…','๐Ÿคด','๐Ÿ‘ธ','๐Ÿ‘ฒ','๐Ÿง•','๐Ÿคต','๐Ÿ‘ฐ','๐Ÿคฐ','๐Ÿซƒ','๐Ÿซ„','๐Ÿคฑ', - '๐Ÿ‘ผ','๐ŸŽ…','๐Ÿคถ','๐Ÿง‘โ€๐ŸŽ„','๐Ÿฆธ','๐Ÿฆน','๐Ÿง™','๐Ÿง','๐Ÿง›','๐ŸงŸ','๐Ÿงž','๐Ÿงœ','๐Ÿงš','๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘','๐Ÿ‘ซ','๐Ÿ‘ฌ','๐Ÿ‘ญ','๐Ÿ’','๐Ÿ’‘','๐Ÿ‘ช', + '๐Ÿ‘‹', '๐Ÿคš', '๐Ÿ–๏ธ', 'โœ‹', '๐Ÿ––', '๐Ÿ‘Œ', '๐ŸคŒ', '๐Ÿค', 'โœŒ๏ธ', '๐Ÿคž', '๐ŸคŸ', '๐Ÿค˜', '๐Ÿค™', '๐Ÿ‘ˆ', '๐Ÿ‘‰', '๐Ÿ‘†', '๐Ÿ–•', '๐Ÿ‘‡', + 'โ˜๏ธ', '๐Ÿ‘', '๐Ÿ‘Ž', 'โœŠ', '๐Ÿ‘Š', '๐Ÿค›', '๐Ÿคœ', '๐Ÿ‘', '๐Ÿ™Œ', '๐Ÿ‘', '๐Ÿคฒ', '๐Ÿค', '๐Ÿ™', 'โœ๏ธ', '๐Ÿ’…', '๐Ÿคณ', '๐Ÿ’ช', '๐Ÿฆพ', + '๐Ÿฆฟ', '๐Ÿฆต', '๐Ÿฆถ', '๐Ÿ‘‚', '๐Ÿฆป', '๐Ÿ‘ƒ', '๐Ÿซ€', '๐Ÿซ', '๐Ÿง ', '๐Ÿฆท', '๐Ÿฆด', '๐Ÿ‘€', '๐Ÿ‘๏ธ', '๐Ÿ‘…', '๐Ÿ‘„', '๐Ÿซฆ', '๐Ÿ‘ถ', '๐Ÿง’', + '๐Ÿ‘ฆ', '๐Ÿ‘ง', '๐Ÿง‘', '๐Ÿ‘ฑ', '๐Ÿ‘จ', '๐Ÿง”', '๐Ÿ‘ฉ', '๐Ÿง“', '๐Ÿ‘ด', '๐Ÿ‘ต', '๐Ÿ™', '๐Ÿ™Ž', '๐Ÿ™…', '๐Ÿ™†', '๐Ÿ’', '๐Ÿ™‹', '๐Ÿง', '๐Ÿ™‡', + '๐Ÿคฆ', '๐Ÿคท', '๐Ÿ‘ฎ', '๐Ÿ•ต๏ธ', '๐Ÿ’‚', '๐Ÿฅท', '๐Ÿ‘ท', '๐Ÿซ…', '๐Ÿคด', '๐Ÿ‘ธ', '๐Ÿ‘ฒ', '๐Ÿง•', '๐Ÿคต', '๐Ÿ‘ฐ', '๐Ÿคฐ', '๐Ÿซƒ', '๐Ÿซ„', '๐Ÿคฑ', + '๐Ÿ‘ผ', '๐ŸŽ…', '๐Ÿคถ', '๐Ÿง‘โ€๐ŸŽ„', '๐Ÿฆธ', '๐Ÿฆน', '๐Ÿง™', '๐Ÿง', '๐Ÿง›', '๐ŸงŸ', '๐Ÿงž', '๐Ÿงœ', '๐Ÿงš', '๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘', '๐Ÿ‘ซ', '๐Ÿ‘ฌ', '๐Ÿ‘ญ', '๐Ÿ’', '๐Ÿ’‘', '๐Ÿ‘ช', ], Animals: [ - '๐Ÿถ','๐Ÿฑ','๐Ÿญ','๐Ÿน','๐Ÿฐ','๐ŸฆŠ','๐Ÿป','๐Ÿผ','๐Ÿปโ€โ„๏ธ','๐Ÿจ','๐Ÿฏ','๐Ÿฆ','๐Ÿฎ','๐Ÿท','๐Ÿธ','๐Ÿต','๐Ÿ™ˆ','๐Ÿ™‰', - '๐Ÿ™Š','๐Ÿ’','๐Ÿฆ†','๐Ÿฆ…','๐Ÿฆ‰','๐Ÿฆ‡','๐Ÿ','๐Ÿชฑ','๐Ÿ›','๐Ÿฆ‹','๐ŸŒ','๐Ÿž','๐Ÿœ','๐Ÿชฒ','๐Ÿฆ—','๐Ÿชณ','๐Ÿ•ท๏ธ','๐Ÿฆ‚', - '๐Ÿข','๐Ÿ','๐ŸฆŽ','๐Ÿฆ–','๐Ÿฆ•','๐Ÿ™','๐Ÿฆ‘','๐Ÿฆ','๐Ÿฆž','๐Ÿฆ€','๐Ÿก','๐Ÿ ','๐ŸŸ','๐Ÿฌ','๐Ÿณ','๐Ÿ‹','๐Ÿฆˆ','๐Ÿฆญ', - '๐ŸŠ','๐Ÿ…','๐Ÿ†','๐Ÿฆ“','๐Ÿฆ','๐Ÿฆง','๐Ÿฆฃ','๐Ÿ˜','๐Ÿฆ›','๐Ÿฆ','๐Ÿช','๐Ÿซ','๐Ÿฆ’','๐Ÿฆ˜','๐Ÿฆฌ','๐Ÿƒ','๐Ÿ‚','๐Ÿ„', - '๐ŸŽ','๐Ÿ–','๐Ÿ','๐Ÿ‘','๐Ÿฆ™','๐Ÿ','๐ŸฆŒ','๐Ÿ•','๐Ÿฉ','๐Ÿฆฎ','๐Ÿ•โ€๐Ÿฆบ','๐Ÿˆ','๐Ÿˆโ€โฌ›','๐Ÿ“','๐Ÿฆƒ','๐Ÿฆค','๐Ÿฆš','๐Ÿฆœ', - '๐Ÿฆข','๐Ÿฆฉ','๐Ÿ•Š๏ธ','๐Ÿ‡','๐Ÿฆ','๐Ÿฆจ','๐Ÿฆก','๐Ÿฆซ','๐Ÿฆฆ','๐Ÿฆฅ','๐Ÿ','๐Ÿ€','๐Ÿฟ๏ธ','๐Ÿฆ”','๐Ÿพ','๐Ÿ‰','๐Ÿฒ','๐ŸŒต', + '๐Ÿถ', '๐Ÿฑ', '๐Ÿญ', '๐Ÿน', '๐Ÿฐ', '๐ŸฆŠ', '๐Ÿป', '๐Ÿผ', '๐Ÿปโ€โ„๏ธ', '๐Ÿจ', '๐Ÿฏ', '๐Ÿฆ', '๐Ÿฎ', '๐Ÿท', '๐Ÿธ', '๐Ÿต', '๐Ÿ™ˆ', '๐Ÿ™‰', + '๐Ÿ™Š', '๐Ÿ’', '๐Ÿฆ†', '๐Ÿฆ…', '๐Ÿฆ‰', '๐Ÿฆ‡', '๐Ÿ', '๐Ÿชฑ', '๐Ÿ›', '๐Ÿฆ‹', '๐ŸŒ', '๐Ÿž', '๐Ÿœ', '๐Ÿชฒ', '๐Ÿฆ—', '๐Ÿชณ', '๐Ÿ•ท๏ธ', '๐Ÿฆ‚', + '๐Ÿข', '๐Ÿ', '๐ŸฆŽ', '๐Ÿฆ–', '๐Ÿฆ•', '๐Ÿ™', '๐Ÿฆ‘', '๐Ÿฆ', '๐Ÿฆž', '๐Ÿฆ€', '๐Ÿก', '๐Ÿ ', '๐ŸŸ', '๐Ÿฌ', '๐Ÿณ', '๐Ÿ‹', '๐Ÿฆˆ', '๐Ÿฆญ', + '๐ŸŠ', '๐Ÿ…', '๐Ÿ†', '๐Ÿฆ“', '๐Ÿฆ', '๐Ÿฆง', '๐Ÿฆฃ', '๐Ÿ˜', '๐Ÿฆ›', '๐Ÿฆ', '๐Ÿช', '๐Ÿซ', '๐Ÿฆ’', '๐Ÿฆ˜', '๐Ÿฆฌ', '๐Ÿƒ', '๐Ÿ‚', '๐Ÿ„', + '๐ŸŽ', '๐Ÿ–', '๐Ÿ', '๐Ÿ‘', '๐Ÿฆ™', '๐Ÿ', '๐ŸฆŒ', '๐Ÿ•', '๐Ÿฉ', '๐Ÿฆฎ', '๐Ÿ•โ€๐Ÿฆบ', '๐Ÿˆ', '๐Ÿˆโ€โฌ›', '๐Ÿ“', '๐Ÿฆƒ', '๐Ÿฆค', '๐Ÿฆš', '๐Ÿฆœ', + '๐Ÿฆข', '๐Ÿฆฉ', '๐Ÿ•Š๏ธ', '๐Ÿ‡', '๐Ÿฆ', '๐Ÿฆจ', '๐Ÿฆก', '๐Ÿฆซ', '๐Ÿฆฆ', '๐Ÿฆฅ', '๐Ÿ', '๐Ÿ€', '๐Ÿฟ๏ธ', '๐Ÿฆ”', '๐Ÿพ', '๐Ÿ‰', '๐Ÿฒ', '๐ŸŒต', ], Food: [ - '๐ŸŽ','๐ŸŠ','๐Ÿ‹','๐ŸŒ','๐Ÿ','๐Ÿฅญ','๐Ÿ“','๐Ÿ’','๐Ÿ‘','๐Ÿฅ','๐Ÿ…','๐Ÿฅฅ','๐Ÿฅ‘','๐Ÿ†','๐Ÿฅ”','๐Ÿฅ•','๐ŸŒฝ','๐ŸŒถ๏ธ', - '๐Ÿซ‘','๐Ÿฅ’','๐Ÿฅฌ','๐Ÿฅฆ','๐Ÿง„','๐Ÿง…','๐Ÿ„','๐Ÿฅœ','๐ŸŒฐ','๐Ÿž','๐Ÿฅ','๐Ÿฅ–','๐Ÿซ“','๐Ÿฅจ','๐Ÿง€','๐Ÿฅš','๐Ÿณ','๐Ÿงˆ', - '๐Ÿฅž','๐Ÿง‡','๐Ÿฅ“','๐Ÿฅฉ','๐Ÿ—','๐Ÿ–','๐Ÿฆด','๐ŸŒญ','๐Ÿ”','๐ŸŸ','๐Ÿ•','๐Ÿซ”','๐ŸŒฎ','๐ŸŒฏ','๐Ÿฅ™','๐Ÿง†','๐Ÿฅš','๐Ÿฑ', - '๐Ÿ˜','๐Ÿ™','๐Ÿš','๐Ÿ›','๐Ÿœ','๐Ÿ','๐Ÿ ','๐Ÿข','๐Ÿฃ','๐Ÿค','๐Ÿฅ','๐Ÿฅฎ','๐Ÿก','๐ŸฅŸ','๐Ÿฅ ','๐Ÿฅก','๐Ÿฆช','๐Ÿฆ', - '๐Ÿง','๐Ÿจ','๐Ÿฉ','๐Ÿช','๐ŸŽ‚','๐Ÿฐ','๐Ÿง','๐Ÿฅง','๐Ÿซ','๐Ÿฌ','๐Ÿญ','๐Ÿฎ','๐Ÿฏ','๐Ÿผ','๐Ÿฅ›','โ˜•','๐Ÿซ–','๐Ÿต', - '๐Ÿงƒ','๐Ÿฅค','๐Ÿง‹','๐Ÿถ','๐Ÿบ','๐Ÿป','๐Ÿฅ‚','๐Ÿท','๐Ÿฅƒ','๐Ÿธ','๐Ÿน','๐Ÿง‰','๐Ÿพ','๐ŸงŠ','๐Ÿฅ„','๐Ÿด','๐Ÿฝ๏ธ','๐Ÿฅข', + '๐ŸŽ', '๐ŸŠ', '๐Ÿ‹', '๐ŸŒ', '๐Ÿ', '๐Ÿฅญ', '๐Ÿ“', '๐Ÿ’', '๐Ÿ‘', '๐Ÿฅ', '๐Ÿ…', '๐Ÿฅฅ', '๐Ÿฅ‘', '๐Ÿ†', '๐Ÿฅ”', '๐Ÿฅ•', '๐ŸŒฝ', '๐ŸŒถ๏ธ', + '๐Ÿซ‘', '๐Ÿฅ’', '๐Ÿฅฌ', '๐Ÿฅฆ', '๐Ÿง„', '๐Ÿง…', '๐Ÿ„', '๐Ÿฅœ', '๐ŸŒฐ', '๐Ÿž', '๐Ÿฅ', '๐Ÿฅ–', '๐Ÿซ“', '๐Ÿฅจ', '๐Ÿง€', '๐Ÿฅš', '๐Ÿณ', '๐Ÿงˆ', + '๐Ÿฅž', '๐Ÿง‡', '๐Ÿฅ“', '๐Ÿฅฉ', '๐Ÿ—', '๐Ÿ–', '๐Ÿฆด', '๐ŸŒญ', '๐Ÿ”', '๐ŸŸ', '๐Ÿ•', '๐Ÿซ”', '๐ŸŒฎ', '๐ŸŒฏ', '๐Ÿฅ™', '๐Ÿง†', '๐Ÿฅš', '๐Ÿฑ', + '๐Ÿ˜', '๐Ÿ™', '๐Ÿš', '๐Ÿ›', '๐Ÿœ', '๐Ÿ', '๐Ÿ ', '๐Ÿข', '๐Ÿฃ', '๐Ÿค', '๐Ÿฅ', '๐Ÿฅฎ', '๐Ÿก', '๐ŸฅŸ', '๐Ÿฅ ', '๐Ÿฅก', '๐Ÿฆช', '๐Ÿฆ', + '๐Ÿง', '๐Ÿจ', '๐Ÿฉ', '๐Ÿช', '๐ŸŽ‚', '๐Ÿฐ', '๐Ÿง', '๐Ÿฅง', '๐Ÿซ', '๐Ÿฌ', '๐Ÿญ', '๐Ÿฎ', '๐Ÿฏ', '๐Ÿผ', '๐Ÿฅ›', 'โ˜•', '๐Ÿซ–', '๐Ÿต', + '๐Ÿงƒ', '๐Ÿฅค', '๐Ÿง‹', '๐Ÿถ', '๐Ÿบ', '๐Ÿป', '๐Ÿฅ‚', '๐Ÿท', '๐Ÿฅƒ', '๐Ÿธ', '๐Ÿน', '๐Ÿง‰', '๐Ÿพ', '๐ŸงŠ', '๐Ÿฅ„', '๐Ÿด', '๐Ÿฝ๏ธ', '๐Ÿฅข', ], Travel: [ - '๐Ÿš—','๐Ÿš•','๐Ÿš™','๐ŸšŒ','๐ŸšŽ','๐ŸŽ๏ธ','๐Ÿš“','๐Ÿš‘','๐Ÿš’','๐Ÿš','๐Ÿ›ป','๐Ÿšš','๐Ÿš›','๐Ÿšœ','๐Ÿฆฏ','๐Ÿฆฝ','๐Ÿฆผ','๐Ÿ›บ', - '๐Ÿšฒ','๐Ÿ›ด','๐Ÿ›ต','๐Ÿ๏ธ','๐Ÿ›บ','๐Ÿšจ','๐Ÿš”','๐Ÿš','๐Ÿš˜','๐Ÿš–','๐Ÿšก','๐Ÿš ','๐ŸšŸ','๐Ÿšƒ','๐Ÿš‹','๐Ÿšž','๐Ÿš','๐Ÿš„', - '๐Ÿš…','๐Ÿšˆ','๐Ÿš‚','๐Ÿš†','๐Ÿš‡','๐ŸšŠ','๐Ÿš‰','โœˆ๏ธ','๐Ÿ›ซ','๐Ÿ›ฌ','๐Ÿ›ฉ๏ธ','๐Ÿ’บ','๐Ÿ›ธ','๐Ÿš','๐Ÿ›ถ','โ›ต','๐Ÿšค','๐Ÿ›ฅ๏ธ', - '๐Ÿ›ณ๏ธ','โ›ด๏ธ','๐Ÿšข','โš“','๐Ÿ—บ๏ธ','๐Ÿงญ','๐Ÿ”๏ธ','โ›ฐ๏ธ','๐ŸŒ‹','๐Ÿ—ป','๐Ÿ•๏ธ','๐Ÿ–๏ธ','๐Ÿœ๏ธ','๐Ÿ๏ธ','๐Ÿž๏ธ','๐ŸŸ๏ธ','๐Ÿ›๏ธ','๐Ÿ—๏ธ', - '๐Ÿงฑ','๐Ÿชจ','๐Ÿชต','๐Ÿ›–','๐Ÿ˜๏ธ','๐Ÿš๏ธ','๐Ÿ ','๐Ÿก','๐Ÿข','๐Ÿฃ','๐Ÿค','๐Ÿฅ','๐Ÿฆ','๐Ÿจ','๐Ÿฉ','๐Ÿช','๐Ÿซ','๐Ÿฌ', - '๐Ÿญ','๐Ÿฏ','๐Ÿฐ','๐Ÿ’’','๐Ÿ—ผ','๐Ÿ—ฝ','โ›ช','๐Ÿ•Œ','๐Ÿ›•','๐Ÿ•','โ›ฉ๏ธ','๐Ÿ•‹','โ›ฒ','โ›บ','๐ŸŒ','๐ŸŒƒ','๐Ÿ™๏ธ','๐ŸŒ„', + '๐Ÿš—', '๐Ÿš•', '๐Ÿš™', '๐ŸšŒ', '๐ŸšŽ', '๐ŸŽ๏ธ', '๐Ÿš“', '๐Ÿš‘', '๐Ÿš’', '๐Ÿš', '๐Ÿ›ป', '๐Ÿšš', '๐Ÿš›', '๐Ÿšœ', '๐Ÿฆฏ', '๐Ÿฆฝ', '๐Ÿฆผ', '๐Ÿ›บ', + '๐Ÿšฒ', '๐Ÿ›ด', '๐Ÿ›ต', '๐Ÿ๏ธ', '๐Ÿ›บ', '๐Ÿšจ', '๐Ÿš”', '๐Ÿš', '๐Ÿš˜', '๐Ÿš–', '๐Ÿšก', '๐Ÿš ', '๐ŸšŸ', '๐Ÿšƒ', '๐Ÿš‹', '๐Ÿšž', '๐Ÿš', '๐Ÿš„', + '๐Ÿš…', '๐Ÿšˆ', '๐Ÿš‚', '๐Ÿš†', '๐Ÿš‡', '๐ŸšŠ', '๐Ÿš‰', 'โœˆ๏ธ', '๐Ÿ›ซ', '๐Ÿ›ฌ', '๐Ÿ›ฉ๏ธ', '๐Ÿ’บ', '๐Ÿ›ธ', '๐Ÿš', '๐Ÿ›ถ', 'โ›ต', '๐Ÿšค', '๐Ÿ›ฅ๏ธ', + '๐Ÿ›ณ๏ธ', 'โ›ด๏ธ', '๐Ÿšข', 'โš“', '๐Ÿ—บ๏ธ', '๐Ÿงญ', '๐Ÿ”๏ธ', 'โ›ฐ๏ธ', '๐ŸŒ‹', '๐Ÿ—ป', '๐Ÿ•๏ธ', '๐Ÿ–๏ธ', '๐Ÿœ๏ธ', '๐Ÿ๏ธ', '๐Ÿž๏ธ', '๐ŸŸ๏ธ', '๐Ÿ›๏ธ', '๐Ÿ—๏ธ', + '๐Ÿงฑ', '๐Ÿชจ', '๐Ÿชต', '๐Ÿ›–', '๐Ÿ˜๏ธ', '๐Ÿš๏ธ', '๐Ÿ ', '๐Ÿก', '๐Ÿข', '๐Ÿฃ', '๐Ÿค', '๐Ÿฅ', '๐Ÿฆ', '๐Ÿจ', '๐Ÿฉ', '๐Ÿช', '๐Ÿซ', '๐Ÿฌ', + '๐Ÿญ', '๐Ÿฏ', '๐Ÿฐ', '๐Ÿ’’', '๐Ÿ—ผ', '๐Ÿ—ฝ', 'โ›ช', '๐Ÿ•Œ', '๐Ÿ›•', '๐Ÿ•', 'โ›ฉ๏ธ', '๐Ÿ•‹', 'โ›ฒ', 'โ›บ', '๐ŸŒ', '๐ŸŒƒ', '๐Ÿ™๏ธ', '๐ŸŒ„', ], Activities: [ - 'โšฝ','๐Ÿ€','๐Ÿˆ','โšพ','๐ŸฅŽ','๐ŸŽพ','๐Ÿ','๐Ÿ‰','๐Ÿฅ','๐ŸŽฑ','๐Ÿ“','๐Ÿธ','๐Ÿ’','๐Ÿ‘','๐Ÿฅ','๐Ÿ','๐Ÿชƒ','๐Ÿฅ…', - 'โ›ณ','๐Ÿช','๐Ÿ›','๐Ÿน','๐ŸŽฃ','๐Ÿคฟ','๐ŸฅŠ','๐Ÿฅ‹','๐ŸŽฝ','๐Ÿ›น','๐Ÿ›ท','โ›ธ๏ธ','๐ŸฅŒ','๐ŸŽฟ','โ›ท๏ธ','๐Ÿ‚','๐Ÿช‚','๐Ÿ‹๏ธ', - '๐Ÿคผ','๐Ÿคธ','โ›น๏ธ','๐Ÿคบ','๐Ÿ‡','๐Ÿง˜','๐Ÿ„','๐ŸŠ','๐Ÿคฝ','๐Ÿšฃ','๐Ÿง—','๐Ÿšต','๐Ÿšด','๐Ÿ†','๐Ÿฅ‡','๐Ÿฅˆ','๐Ÿฅ‰','๐Ÿ…', - '๐ŸŽ–๏ธ','๐Ÿต๏ธ','๐ŸŽ—๏ธ','๐ŸŽซ','๐ŸŽŸ๏ธ','๐ŸŽช','๐Ÿคน','๐ŸŽญ','๐Ÿฉฐ','๐ŸŽจ','๐Ÿ–ผ๏ธ','๐ŸŽฐ','๐ŸŽฒ','๐Ÿงฉ','๐Ÿช„','๐ŸŽฏ','๐Ÿช…','๐ŸŽฎ', - '๐Ÿ•น๏ธ','๐ŸŽณ','๐ŸŽป','๐ŸŽท','๐Ÿฅ','๐Ÿช˜','๐ŸŽบ','๐ŸŽธ','๐Ÿช—','๐ŸŽน','๐ŸŽต','๐ŸŽถ','๐ŸŽผ','๐ŸŽค','๐ŸŽง','๐Ÿ“ป','๐ŸŽ™๏ธ','๐ŸŽš๏ธ', - '๐ŸŽฌ','๐Ÿ“ฝ๏ธ','๐ŸŽž๏ธ','๐Ÿ“ฑ','๐Ÿ“ฒ','โ˜Ž๏ธ','๐Ÿ“ž','๐Ÿ“Ÿ','๐Ÿ“ ','๐Ÿ”‹','๐Ÿชซ','๐Ÿ”Œ','๐Ÿ’ก','๐Ÿ”ฆ','๐Ÿ•ฏ๏ธ','๐Ÿ’ธ','๐Ÿ’ต','๐Ÿช™', + 'โšฝ', '๐Ÿ€', '๐Ÿˆ', 'โšพ', '๐ŸฅŽ', '๐ŸŽพ', '๐Ÿ', '๐Ÿ‰', '๐Ÿฅ', '๐ŸŽฑ', '๐Ÿ“', '๐Ÿธ', '๐Ÿ’', '๐Ÿ‘', '๐Ÿฅ', '๐Ÿ', '๐Ÿชƒ', '๐Ÿฅ…', + 'โ›ณ', '๐Ÿช', '๐Ÿ›', '๐Ÿน', '๐ŸŽฃ', '๐Ÿคฟ', '๐ŸฅŠ', '๐Ÿฅ‹', '๐ŸŽฝ', '๐Ÿ›น', '๐Ÿ›ท', 'โ›ธ๏ธ', '๐ŸฅŒ', '๐ŸŽฟ', 'โ›ท๏ธ', '๐Ÿ‚', '๐Ÿช‚', '๐Ÿ‹๏ธ', + '๐Ÿคผ', '๐Ÿคธ', 'โ›น๏ธ', '๐Ÿคบ', '๐Ÿ‡', '๐Ÿง˜', '๐Ÿ„', '๐ŸŠ', '๐Ÿคฝ', '๐Ÿšฃ', '๐Ÿง—', '๐Ÿšต', '๐Ÿšด', '๐Ÿ†', '๐Ÿฅ‡', '๐Ÿฅˆ', '๐Ÿฅ‰', '๐Ÿ…', + '๐ŸŽ–๏ธ', '๐Ÿต๏ธ', '๐ŸŽ—๏ธ', '๐ŸŽซ', '๐ŸŽŸ๏ธ', '๐ŸŽช', '๐Ÿคน', '๐ŸŽญ', '๐Ÿฉฐ', '๐ŸŽจ', '๐Ÿ–ผ๏ธ', '๐ŸŽฐ', '๐ŸŽฒ', '๐Ÿงฉ', '๐Ÿช„', '๐ŸŽฏ', '๐Ÿช…', '๐ŸŽฎ', + '๐Ÿ•น๏ธ', '๐ŸŽณ', '๐ŸŽป', '๐ŸŽท', '๐Ÿฅ', '๐Ÿช˜', '๐ŸŽบ', '๐ŸŽธ', '๐Ÿช—', '๐ŸŽน', '๐ŸŽต', '๐ŸŽถ', '๐ŸŽผ', '๐ŸŽค', '๐ŸŽง', '๐Ÿ“ป', '๐ŸŽ™๏ธ', '๐ŸŽš๏ธ', + '๐ŸŽฌ', '๐Ÿ“ฝ๏ธ', '๐ŸŽž๏ธ', '๐Ÿ“ฑ', '๐Ÿ“ฒ', 'โ˜Ž๏ธ', '๐Ÿ“ž', '๐Ÿ“Ÿ', '๐Ÿ“ ', '๐Ÿ”‹', '๐Ÿชซ', '๐Ÿ”Œ', '๐Ÿ’ก', '๐Ÿ”ฆ', '๐Ÿ•ฏ๏ธ', '๐Ÿ’ธ', '๐Ÿ’ต', '๐Ÿช™', ], Objects: [ - 'โŒš','๐Ÿ“ฑ','๐Ÿ“ฒ','๐Ÿ’ป','โŒจ๏ธ','๐Ÿ–ฅ๏ธ','๐Ÿ–จ๏ธ','๐Ÿ–ฑ๏ธ','๐Ÿ–ฒ๏ธ','๐Ÿ’พ','๐Ÿ’ฟ','๐Ÿ“€','๐Ÿงฎ','๐Ÿ“ท','๐Ÿ“ธ','๐Ÿ“น','๐ŸŽฅ','๐Ÿ“ฝ๏ธ', - '๐Ÿ“ž','โ˜Ž๏ธ','๐Ÿ“Ÿ','๐Ÿ“ ','๐Ÿ“บ','๐Ÿ“ป','๐Ÿงญ','โฑ๏ธ','โฒ๏ธ','โฐ','๐Ÿ•ฐ๏ธ','โŒ›','โณ','๐Ÿ“ก','๐Ÿ”‹','๐Ÿชซ','๐Ÿ”Œ','๐Ÿ’ก', - '๐Ÿ”ฆ','๐Ÿ•ฏ๏ธ','๐Ÿช”','๐Ÿงฑ','๐Ÿ’ฐ','๐Ÿ’ด','๐Ÿ’ต','๐Ÿ’ถ','๐Ÿ’ท','๐Ÿ’ธ','๐Ÿ’ณ','๐Ÿช™','๐Ÿ’น','โœ‰๏ธ','๐Ÿ“ง','๐Ÿ“จ','๐Ÿ“ฉ','๐Ÿ“ค', - '๐Ÿ“ฅ','๐Ÿ“ฆ','๐Ÿ“ซ','๐Ÿ“ช','๐Ÿ“ฌ','๐Ÿ“ญ','๐Ÿ“ฎ','๐Ÿ—ณ๏ธ','โœ๏ธ','โœ’๏ธ','๐Ÿ–Š๏ธ','๐Ÿ–‹๏ธ','๐Ÿ“','๐Ÿ“','๐Ÿ“‚','๐Ÿ—‚๏ธ','๐Ÿ“…','๐Ÿ“†', - '๐Ÿ—’๏ธ','๐Ÿ—“๏ธ','๐Ÿ“‡','๐Ÿ“ˆ','๐Ÿ“‰','๐Ÿ“Š','๐Ÿ“‹','๐Ÿ“Œ','๐Ÿ“','๐Ÿ—บ๏ธ','๐Ÿ“','๐Ÿ“','โœ‚๏ธ','๐Ÿ—ƒ๏ธ','๐Ÿ—„๏ธ','๐Ÿ—‘๏ธ','๐Ÿ”’','๐Ÿ”“', - '๐Ÿ”','๐Ÿ”','๐Ÿ”‘','๐Ÿ—๏ธ','๐Ÿ”จ','๐Ÿช“','โ›๏ธ','โš’๏ธ','๐Ÿ› ๏ธ','๐Ÿ—ก๏ธ','โš”๏ธ','๐Ÿ”ซ','๐Ÿชƒ','๐Ÿน','๐Ÿ›ก๏ธ','๐Ÿชš','๐Ÿ”ง','๐Ÿช›', + 'โŒš', '๐Ÿ“ฑ', '๐Ÿ“ฒ', '๐Ÿ’ป', 'โŒจ๏ธ', '๐Ÿ–ฅ๏ธ', '๐Ÿ–จ๏ธ', '๐Ÿ–ฑ๏ธ', '๐Ÿ–ฒ๏ธ', '๐Ÿ’พ', '๐Ÿ’ฟ', '๐Ÿ“€', '๐Ÿงฎ', '๐Ÿ“ท', '๐Ÿ“ธ', '๐Ÿ“น', '๐ŸŽฅ', '๐Ÿ“ฝ๏ธ', + '๐Ÿ“ž', 'โ˜Ž๏ธ', '๐Ÿ“Ÿ', '๐Ÿ“ ', '๐Ÿ“บ', '๐Ÿ“ป', '๐Ÿงญ', 'โฑ๏ธ', 'โฒ๏ธ', 'โฐ', '๐Ÿ•ฐ๏ธ', 'โŒ›', 'โณ', '๐Ÿ“ก', '๐Ÿ”‹', '๐Ÿชซ', '๐Ÿ”Œ', '๐Ÿ’ก', + '๐Ÿ”ฆ', '๐Ÿ•ฏ๏ธ', '๐Ÿช”', '๐Ÿงฑ', '๐Ÿ’ฐ', '๐Ÿ’ด', '๐Ÿ’ต', '๐Ÿ’ถ', '๐Ÿ’ท', '๐Ÿ’ธ', '๐Ÿ’ณ', '๐Ÿช™', '๐Ÿ’น', 'โœ‰๏ธ', '๐Ÿ“ง', '๐Ÿ“จ', '๐Ÿ“ฉ', '๐Ÿ“ค', + '๐Ÿ“ฅ', '๐Ÿ“ฆ', '๐Ÿ“ซ', '๐Ÿ“ช', '๐Ÿ“ฌ', '๐Ÿ“ญ', '๐Ÿ“ฎ', '๐Ÿ—ณ๏ธ', 'โœ๏ธ', 'โœ’๏ธ', '๐Ÿ–Š๏ธ', '๐Ÿ–‹๏ธ', '๐Ÿ“', '๐Ÿ“', '๐Ÿ“‚', '๐Ÿ—‚๏ธ', '๐Ÿ“…', '๐Ÿ“†', + '๐Ÿ—’๏ธ', '๐Ÿ—“๏ธ', '๐Ÿ“‡', '๐Ÿ“ˆ', '๐Ÿ“‰', '๐Ÿ“Š', '๐Ÿ“‹', '๐Ÿ“Œ', '๐Ÿ“', '๐Ÿ—บ๏ธ', '๐Ÿ“', '๐Ÿ“', 'โœ‚๏ธ', '๐Ÿ—ƒ๏ธ', '๐Ÿ—„๏ธ', '๐Ÿ—‘๏ธ', '๐Ÿ”’', '๐Ÿ”“', + '๐Ÿ”', '๐Ÿ”', '๐Ÿ”‘', '๐Ÿ—๏ธ', '๐Ÿ”จ', '๐Ÿช“', 'โ›๏ธ', 'โš’๏ธ', '๐Ÿ› ๏ธ', '๐Ÿ—ก๏ธ', 'โš”๏ธ', '๐Ÿ”ซ', '๐Ÿชƒ', '๐Ÿน', '๐Ÿ›ก๏ธ', '๐Ÿชš', '๐Ÿ”ง', '๐Ÿช›', ], Symbols: [ - 'โค๏ธ','๐Ÿงก','๐Ÿ’›','๐Ÿ’š','๐Ÿ’™','๐Ÿ’œ','๐Ÿ–ค','๐Ÿค','๐ŸคŽ','๐Ÿ’”','โฃ๏ธ','๐Ÿ’•','๐Ÿ’ž','๐Ÿ’“','๐Ÿ’—','๐Ÿ’–','๐Ÿ’˜','๐Ÿ’', - '๐Ÿ’Ÿ','โ˜ฎ๏ธ','โœ๏ธ','โ˜ช๏ธ','๐Ÿ•‰๏ธ','โ˜ธ๏ธ','โœก๏ธ','๐Ÿ”ฏ','๐Ÿ•Ž','โ˜ฏ๏ธ','โ˜ฆ๏ธ','๐Ÿ›','โ›Ž','โ™ˆ','โ™‰','โ™Š','โ™‹','โ™Œ', - 'โ™','โ™Ž','โ™','โ™','โ™‘','โ™’','โ™“','๐Ÿ†”','โš›๏ธ','๐Ÿ‰‘','โ˜ข๏ธ','โ˜ฃ๏ธ','๐Ÿ“ด','๐Ÿ“ณ','๐Ÿˆถ','๐Ÿˆš','๐Ÿˆธ','๐Ÿˆบ', - '๐Ÿˆท๏ธ','โœด๏ธ','๐Ÿ†š','๐Ÿ’ฎ','๐Ÿ‰','ใŠ™๏ธ','ใŠ—๏ธ','๐Ÿˆด','๐Ÿˆต','๐Ÿˆน','๐Ÿˆฒ','๐Ÿ…ฐ๏ธ','๐Ÿ…ฑ๏ธ','๐Ÿ†Ž','๐Ÿ†‘','๐Ÿ…พ๏ธ','๐Ÿ†˜', - 'โŒ','โญ•','๐Ÿ›‘','โ›”','๐Ÿ“›','๐Ÿšซ','๐Ÿ’ฏ','๐Ÿ’ข','โ™จ๏ธ','๐Ÿšท','๐Ÿšฏ','๐Ÿšณ','๐Ÿšฑ','๐Ÿ”ž','๐Ÿ“ต','๐Ÿšญ','โ—','โ•', - 'โ“','โ”','โ€ผ๏ธ','โ‰๏ธ','๐Ÿ”…','๐Ÿ”†','๐Ÿ“ถ','๐Ÿ›œ','๐Ÿ“ณ','๐Ÿ“ด','๐Ÿ”ฑ','๐Ÿ“›','๐Ÿ”ฐ','โ™ป๏ธ','โœ…','๐Ÿˆฏ','๐Ÿ’น','โŽ', - '๐ŸŒ','๐Ÿ’ ','โ“‚๏ธ','๐ŸŒ€','๐Ÿ’ค','๐Ÿง','๐Ÿšพ','โ™ฟ','๐Ÿ…ฟ๏ธ','๐Ÿ›—','๐Ÿˆณ','๐Ÿˆน','๐Ÿšฐ','๐Ÿ”ค','๐Ÿ”ก','๐Ÿ” ','๐Ÿ†–','๐Ÿ†—', - '๐Ÿ†™','๐Ÿ†’','๐Ÿ†•','๐Ÿ†“','๐Ÿ”Ÿ','๐Ÿ“Š','๐Ÿ”ฃ','โœ”๏ธ','โ˜‘๏ธ','๐Ÿ”˜','๐Ÿ”ฒ','๐Ÿ”ณ','โฌ›','โฌœ','โ—ผ๏ธ','โ—ป๏ธ','โ—พ','โ—ฝ', - 'โ–ช๏ธ','โ–ซ๏ธ','๐Ÿ”ถ','๐Ÿ”ท','๐Ÿ”ธ','๐Ÿ”น','๐Ÿ”บ','๐Ÿ”ป','๐Ÿ’ ','๐Ÿ”˜','๐Ÿ”ฒ','๐Ÿ”ณ','๐Ÿ','๐Ÿšฉ','๐ŸŽŒ','๐Ÿด','๐Ÿณ๏ธ','โญ', - '๐ŸŒŸ','๐Ÿ’ซ','โœจ','๐ŸŒˆ','โ˜€๏ธ','๐ŸŒค๏ธ','โ›…','๐ŸŒฅ๏ธ','โ˜๏ธ','๐ŸŒฆ๏ธ','๐ŸŒง๏ธ','โ›ˆ๏ธ','๐ŸŒฉ๏ธ','๐ŸŒจ๏ธ','โ„๏ธ','โ˜ƒ๏ธ','โ›„','๐ŸŒฌ๏ธ', + 'โค๏ธ', '๐Ÿงก', '๐Ÿ’›', '๐Ÿ’š', '๐Ÿ’™', '๐Ÿ’œ', '๐Ÿ–ค', '๐Ÿค', '๐ŸคŽ', '๐Ÿ’”', 'โฃ๏ธ', '๐Ÿ’•', '๐Ÿ’ž', '๐Ÿ’“', '๐Ÿ’—', '๐Ÿ’–', '๐Ÿ’˜', '๐Ÿ’', + '๐Ÿ’Ÿ', 'โ˜ฎ๏ธ', 'โœ๏ธ', 'โ˜ช๏ธ', '๐Ÿ•‰๏ธ', 'โ˜ธ๏ธ', 'โœก๏ธ', '๐Ÿ”ฏ', '๐Ÿ•Ž', 'โ˜ฏ๏ธ', 'โ˜ฆ๏ธ', '๐Ÿ›', 'โ›Ž', 'โ™ˆ', 'โ™‰', 'โ™Š', 'โ™‹', 'โ™Œ', + 'โ™', 'โ™Ž', 'โ™', 'โ™', 'โ™‘', 'โ™’', 'โ™“', '๐Ÿ†”', 'โš›๏ธ', '๐Ÿ‰‘', 'โ˜ข๏ธ', 'โ˜ฃ๏ธ', '๐Ÿ“ด', '๐Ÿ“ณ', '๐Ÿˆถ', '๐Ÿˆš', '๐Ÿˆธ', '๐Ÿˆบ', + '๐Ÿˆท๏ธ', 'โœด๏ธ', '๐Ÿ†š', '๐Ÿ’ฎ', '๐Ÿ‰', 'ใŠ™๏ธ', 'ใŠ—๏ธ', '๐Ÿˆด', '๐Ÿˆต', '๐Ÿˆน', '๐Ÿˆฒ', '๐Ÿ…ฐ๏ธ', '๐Ÿ…ฑ๏ธ', '๐Ÿ†Ž', '๐Ÿ†‘', '๐Ÿ…พ๏ธ', '๐Ÿ†˜', + 'โŒ', 'โญ•', '๐Ÿ›‘', 'โ›”', '๐Ÿ“›', '๐Ÿšซ', '๐Ÿ’ฏ', '๐Ÿ’ข', 'โ™จ๏ธ', '๐Ÿšท', '๐Ÿšฏ', '๐Ÿšณ', '๐Ÿšฑ', '๐Ÿ”ž', '๐Ÿ“ต', '๐Ÿšญ', 'โ—', 'โ•', + 'โ“', 'โ”', 'โ€ผ๏ธ', 'โ‰๏ธ', '๐Ÿ”…', '๐Ÿ”†', '๐Ÿ“ถ', '๐Ÿ›œ', '๐Ÿ“ณ', '๐Ÿ“ด', '๐Ÿ”ฑ', '๐Ÿ“›', '๐Ÿ”ฐ', 'โ™ป๏ธ', 'โœ…', '๐Ÿˆฏ', '๐Ÿ’น', 'โŽ', + '๐ŸŒ', '๐Ÿ’ ', 'โ“‚๏ธ', '๐ŸŒ€', '๐Ÿ’ค', '๐Ÿง', '๐Ÿšพ', 'โ™ฟ', '๐Ÿ…ฟ๏ธ', '๐Ÿ›—', '๐Ÿˆณ', '๐Ÿˆน', '๐Ÿšฐ', '๐Ÿ”ค', '๐Ÿ”ก', '๐Ÿ” ', '๐Ÿ†–', '๐Ÿ†—', + '๐Ÿ†™', '๐Ÿ†’', '๐Ÿ†•', '๐Ÿ†“', '๐Ÿ”Ÿ', '๐Ÿ“Š', '๐Ÿ”ฃ', 'โœ”๏ธ', 'โ˜‘๏ธ', '๐Ÿ”˜', '๐Ÿ”ฒ', '๐Ÿ”ณ', 'โฌ›', 'โฌœ', 'โ—ผ๏ธ', 'โ—ป๏ธ', 'โ—พ', 'โ—ฝ', + 'โ–ช๏ธ', 'โ–ซ๏ธ', '๐Ÿ”ถ', '๐Ÿ”ท', '๐Ÿ”ธ', '๐Ÿ”น', '๐Ÿ”บ', '๐Ÿ”ป', '๐Ÿ’ ', '๐Ÿ”˜', '๐Ÿ”ฒ', '๐Ÿ”ณ', '๐Ÿ', '๐Ÿšฉ', '๐ŸŽŒ', '๐Ÿด', '๐Ÿณ๏ธ', 'โญ', + '๐ŸŒŸ', '๐Ÿ’ซ', 'โœจ', '๐ŸŒˆ', 'โ˜€๏ธ', '๐ŸŒค๏ธ', 'โ›…', '๐ŸŒฅ๏ธ', 'โ˜๏ธ', '๐ŸŒฆ๏ธ', '๐ŸŒง๏ธ', 'โ›ˆ๏ธ', '๐ŸŒฉ๏ธ', '๐ŸŒจ๏ธ', 'โ„๏ธ', 'โ˜ƒ๏ธ', 'โ›„', '๐ŸŒฌ๏ธ', ], }; @@ -115,14 +115,14 @@ const EmojiPicker = () => ({ onclick: () => { ChatHubState.emojiSearch = ''; }, }, m('i.fas.fa-times')), ]), - !search && m('.emoji-categories', EMOJI_CATEGORIES.map(c => + !search && m('.emoji-categories', EMOJI_CATEGORIES.map((c) => m('button.emoji-cat-btn' + (c === cat ? '.active' : ''), { title: c, onclick: () => { ChatHubState.emojiCategory = c; }, }, EMOJI_ICONS[c]) )), m('.emoji-grid', - emojis.map(e => + emojis.map((e) => m('button.emoji-btn', { onclick: () => { insertEmojiIntoTextarea(e, onSelect); diff --git a/webui-src/app/chat/chat_state.js b/webui-src/app/chat/chat_state.js index 4bf1123..2760a63 100644 --- a/webui-src/app/chat/chat_state.js +++ b/webui-src/app/chat/chat_state.js @@ -1,7 +1,5 @@ const m = require('mithril'); const rs = require('rswebui'); -const peopleUtil = require('people/people_util'); -const people = require('people/people'); // **************** utility functions ******************** @@ -35,7 +33,7 @@ function loadDistantChatDetails(pid, apply) { rs.rsJsonApiRequest( '/rsChats/getDistantChatStatus', { - pid: pid, + pid, }, (detail, success) => { if (success && detail.retval) { @@ -118,7 +116,7 @@ function renderChatMessage(rawText) { if (src) { parts.push( m('img.chat-embedded-image', { - src: src, + src, style: { maxWidth: '100%', maxHeight: '300px', @@ -160,7 +158,7 @@ function renderChatMessage(rawText) { if (rawText.trim().startsWith('data:image/')) { const src = rawText.trim(); return m('img.chat-embedded-image', { - src: src, + src, style: { maxWidth: '100%', maxHeight: '300px', @@ -248,7 +246,7 @@ function renderTextWithEmoji(text) { const parts = []; let last = 0; let match; - // eslint-disable-next-line no-cond-assign + while ((match = emojiRegex.exec(text)) !== null) { if (match[0].length === 0) { emojiRegex.lastIndex++; continue; } if (match.index > last) parts.push(text.slice(last, match.index)); @@ -442,8 +440,8 @@ const Message = () => { x: e.clientX, y: e.clientY, messageText: targetText, - username: username, - gxsId: gxsId, + username, + gxsId, }; m.redraw(); }; @@ -581,7 +579,7 @@ const ChatLobbyModel = { loadHistory(id, type) { const chatPeerId = { broadcast_status_peer_id: '00000000000000000000000000000000', - type: type, + type, peer_id: '00000000000000000000000000000000', distant_chat_id: '00000000000000000000000000000000', lobby_id: { xstr64: '0' }, @@ -594,7 +592,7 @@ const ChatLobbyModel = { rs.rsJsonApiRequest( '/rsHistory/getMessages', { - chatPeerId: chatPeerId, + chatPeerId, loadCount: 20, }, (data, success) => { @@ -623,11 +621,11 @@ const ChatLobbyModel = { rs.rsJsonApiRequest( '/rsHistory/getMessages', { - chatPeerId: chatPeerId, + chatPeerId, loadCount: 0, }, (data, success) => { - let msgs = (success && data && data.msgs) ? data.msgs : []; + const msgs = (success && data && data.msgs) ? data.msgs : []; msgs.sort((a, b) => (a.sendTime || a.recvTime) - (b.sendTime || b.recvTime)); ChatHubState.fullHistoryMessages = msgs; ChatHubState.isHistoryLoading = false; @@ -642,7 +640,7 @@ const ChatLobbyModel = { '/rsChats/setIdentityForChatLobby', { lobby_id: { xstr64: lobbyId }, - nick: nick, + nick, }, () => m.route.set('/chat/:lobby', { lobby: lobbyId }), true @@ -685,7 +683,7 @@ const ChatLobbyModel = { const id = this.lastLobbyId || m.route.param('lobby'); const cid = { broadcast_status_peer_id: '00000000000000000000000000000000', - type: type, + type, peer_id: '00000000000000000000000000000000', distant_chat_id: '00000000000000000000000000000000', lobby_id: { xstr64: '0' }, @@ -805,13 +803,13 @@ const ChatLobbyModel = { '/rsChats/sendChat', { id: cid, - msg: msg, + msg, }, (data, success) => { if (success) { const echoMsg = { chat_id: cid, - msg: msg, + msg, sendTime: Math.floor(Date.now() / 1000), lobby_peer_gxs_id: this.currentLobby.gxs_id, }; diff --git a/webui-src/app/config/config_chat.js b/webui-src/app/config/config_chat.js index 6b6714e..6bf8ba1 100644 --- a/webui-src/app/config/config_chat.js +++ b/webui-src/app/config/config_chat.js @@ -10,8 +10,8 @@ const ConfigChat = () => { let maxStorageDays = 10; // History states - let historyEnable = { private: true, distant: true, lobby: true }; - let historySaveCount = { private: 500, distant: 500, lobby: 500 }; + const historyEnable = { private: true, distant: true, lobby: true }; + const historySaveCount = { private: 500, distant: 500, lobby: 500 }; function loadSettings() { // Load Own Identities diff --git a/webui-src/app/config/config_network.js b/webui-src/app/config/config_network.js index 1af47d6..be6ed0d 100644 --- a/webui-src/app/config/config_network.js +++ b/webui-src/app/config/config_network.js @@ -579,8 +579,8 @@ const displayHiddenServiceInfo = () => { details && details.hiddenNodeAddress && m('.hidden-service-info', { style: 'display: flex; flex-direction: column; gap: 0.75rem; width: 100%;' }, [ m('p.proxy-description', { style: 'margin-bottom: 0.5rem; color: #475569;' }, details.hiddenType === 4 - ? "I2P has been automatically configured by Retroshare. You shouldn't need to change anything here." - : "Tor has been automatically configured by Retroshare. You shouldn't need to change anything here." + ? 'I2P has been automatically configured by Retroshare. You shouldn\'t need to change anything here.' + : 'Tor has been automatically configured by Retroshare. You shouldn\'t need to change anything here.' ), // Local Address + Local Port row m('.nw-config-row', { style: 'display: grid; grid-template-columns: 200px 1fr; gap: 1rem; align-items: center;' }, [ diff --git a/webui-src/app/forums/forum_view.js b/webui-src/app/forums/forum_view.js index a58497a..0c3845a 100644 --- a/webui-src/app/forums/forum_view.js +++ b/webui-src/app/forums/forum_view.js @@ -2,7 +2,7 @@ const m = require('mithril'); const rs = require('rswebui'); const util = require('forums/forums_util'); const peopleUtil = require('people/people_util'); -const { updatedisplayforums, loadPostContent, getTimestampValue, formatTimestamp } = require('./forums_util'); +const { loadPostContent, getTimestampValue, formatTimestamp } = require('./forums_util'); function createforum() { let title; @@ -75,75 +75,6 @@ function createforum() { ]), }; } -const EditThread = () => { - let title = ''; - let body = ''; - return { - oninit: (vnode) => { - title = vnode.attrs.current_title; - body = vnode.attrs.current_body; - }, - view: (vnode) => - m('.widget', [ - m('h3', 'Edit Thread'), - m('hr'), - - m( - 'iddisplay', - { - style: { display: 'block ruby' }, // same line block ruby - }, - [ - 'Identity: ', - m('h5[id=authid]', rs.userList.username(vnode.attrs.authorId)), - ] - ), - m( - 'titledisplay', - { - style: { display: 'block ruby' }, - }, - [ - 'Title: ', - m('input[type=text][placeholder=Title]', { - value: vnode.attrs.current_title, - oninput: (e) => (title = e.target.value), - }), - ] - ), - m('textarea[rows=5]', { - style: { width: '90%', display: 'block' }, - oninput: (e) => (body = e.target.value), - value: vnode.attrs.current_body, - }), - m( - 'button', - { - onclick: async () => { - const res = await rs.rsJsonApiRequest('/rsgxsforums/createPost', { - forumId: vnode.attrs.forumId, - mBody: body, - title, - authorId: vnode.attrs.authorId, - parentId: vnode.attrs.current_parent, - origPostId: vnode.attrs.current_msgid, - }); - res.body.retval === false - ? util.popupmessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)]) - : util.popupmessage([ - m('h3', 'Success'), - m('hr'), - m('p', 'Thread edited successfully'), - ]); - util.updatedisplayforums(vnode.attrs.forumId); - m.redraw(); - }, - }, - 'Add' - ), - ]), - }; -}; const AddThread = () => { let title = ''; let body = ''; @@ -228,166 +159,6 @@ const AddThread = () => { // getTimestampValue and formatTimestamp are imported from forums_util.js -function displaythread() { - // recursive function to display all the threads - let groupmessagepair; - let unread; - let editpermission = false; - return { - view: (v) => { - const thread = v.attrs.threadStruct.thread; - groupmessagepair = { first: thread.mMeta.mGroupId, second: thread.mMeta.mOrigMsgId }; - let parMap = []; - if (util.Data.ParentThreadMap[thread.mMeta.mOrigMsgId]) { - parMap = util.Data.ParentThreadMap[thread.mMeta.mOrigMsgId]; - } - unread = thread.mMeta.mMsgStatus === util.THREAD_UNREAD; - v.attrs.identity && - v.attrs.identity.map((val) => { - if (val.localeCompare(thread.mMeta.mAuthorId) === 0) { - // if the author of the thread matches one of our own ids - editpermission = true; - } - }); - return [ - m( - 'tr', - { - style: unread ? { fontWeight: 'bold' } : '', - }, - [ - Object.keys(parMap).length // if this thread has some replies - ? m( - 'td', - m('i.fas.fa-angle-right', { - class: 'fa-rotate-' + (v.attrs.threadStruct.showReplies ? '90' : '0'), - style: 'margin-top:12px', - onclick: () => { - v.attrs.threadStruct.showReplies = !v.attrs.threadStruct.showReplies; - }, - }) - ) - : m('td', ''), - - m( - 'td', - { - style: { - position: 'relative', - '--replyDepth': v.attrs.replyDepth, - left: 'calc(30px*var(--replyDepth))', // shifts reply by 30 px - padding: '10px 0', - }, - }, - [ - m('div.date', { style: { fontSize: '0.8em', color: '#888' } }, - formatTimestamp(thread.mMeta.mPublishTs) - ), - m('div.title', { - style: { fontWeight: 'bold', fontSize: '1.1em', cursor: 'pointer', margin: '5px 0' }, - onclick: async () => { - v.attrs.changeThread(thread.mMeta.mOrigMsgId); - if (unread) { - const res = await rs.rsJsonApiRequest('/rsgxsforums/markRead', { - messageId: groupmessagepair, - read: true, - }); - if (res.body.retval) { - updatedisplayforums(thread.mMeta.mGroupId); - m.redraw(); - } - } - }, - ondblclick: () => - (v.attrs.threadStruct.showReplies = !v.attrs.threadStruct.showReplies), - }, [ - thread.mMeta.mMsgName, - m('options', { style: 'display:block; margin-top: 5px;' }, [ - m( - 'button', - { - style: 'font-size:12px; margin-right: 5px;', - onclick: (e) => { - e.stopPropagation(); - util.popupmessage( - m(AddThread, { - parent_thread: thread.mMeta.mMsgName, - forumId: thread.mMeta.mGroupId, - authorId: v.attrs.identity, - parentId: thread.mMeta.mMsgId, - }) - ); - }, - }, - 'Reply' - ), - editpermission && - m( - 'button', - { - style: 'font-size:12px; margin-right: 5px;', - onclick: async (e) => { - e.stopPropagation(); - const body = await loadPostContent( - thread.mMeta.mGroupId, - thread.mMeta.mOrigMsgId - ); - util.popupmessage( - m(EditThread, { - current_thread: thread.mMeta.mMsgName, - forumId: thread.mMeta.mGroupId, - current_title: thread.mMeta.mMsgName, - current_body: body || '', - authorId: thread.mMeta.mAuthorId, - current_parent: thread.mMeta.mParentId, - current_msgid: thread.mMeta.mOrigMsgId, - }) - ); - }, - }, - 'Edit' - ), - m( - 'button', - { - style: { fontSize: '12px' }, - onclick: async (e) => { - e.stopPropagation(); - const res = await rs.rsJsonApiRequest('/rsgxsforums/markRead', { - messageId: groupmessagepair, - read: !unread ? true : false, - }); - - if (res.body.retval) { - updatedisplayforums(thread.mMeta.mGroupId); - m.redraw(); - } - }, - }, - unread ? 'Mark Read' : 'Mark Unread' - ), - ]), - ]), - m('div.author', { style: { fontSize: '0.9em', fontStyle: 'italic' } }, rs.userList.username(thread.mMeta.mAuthorId)), - ] - ), - ] - ), - v.attrs.threadStruct.showReplies && - Object.keys(parMap).map((key, index) => - m(displaythread, { - // recursive call to all replies - threadStruct: util.Data.Threads[parMap[key].mGroupId][parMap[key].mOrigMsgId], - replyDepth: v.attrs.replyDepth + 1, - identity: v.attrs.identity, - changeThread: v.attrs.changeThread, - }) - ), - ]; - }, - }; -} - const ThreadView = () => { let ownId; return { @@ -449,7 +220,7 @@ const ThreadView = () => { style: { marginRight: '10px' }, onclick: () => util.popupmessage(m(AddThread, { parent_thread: meta.mMsgName, - forumId: forumId, + forumId, authorId: ownId, parentId: msgId, })) diff --git a/webui-src/app/forums/forums_util.js b/webui-src/app/forums/forums_util.js index ae92c5d..e7a2773 100644 --- a/webui-src/app/forums/forums_util.js +++ b/webui-src/app/forums/forums_util.js @@ -106,7 +106,7 @@ async function updatedisplayforums(keyid) { const threadStruct = { thread: { mMeta: meta, mMsg: null }, - replies: replies, + replies, showReplies: false, }; @@ -156,7 +156,7 @@ async function loadPostContent(forumId, msgId) { try { const res = await rs.rsJsonApiRequest('/rsgxsforums/getForumContent', { - forumId: forumId, + forumId, msgsIds: [msgId], }); if (res && res.body && res.body.retval && res.body.msgs && res.body.msgs.length > 0) { diff --git a/webui-src/app/jdenticon.js b/webui-src/app/jdenticon.js index b8b21eb..2808a96 100644 --- a/webui-src/app/jdenticon.js +++ b/webui-src/app/jdenticon.js @@ -41,7 +41,7 @@ function renderPolygon(shapePoints, x, y, angle, shapeAngle, size, color) { function toSvg(hash, width) { if (!hash || hash.length < 18) { - hash = "00000000000000000000000000000000"; + hash = '00000000000000000000000000000000'; } const csh = parseInt(hash.substr(0, 1), 16); @@ -99,4 +99,4 @@ function toSvg(hash, width) { module.exports = { toSvg -}; \ No newline at end of file +}; diff --git a/webui-src/app/mail/mail_compose.js b/webui-src/app/mail/mail_compose.js index 230d376..d8cd0eb 100644 --- a/webui-src/app/mail/mail_compose.js +++ b/webui-src/app/mail/mail_compose.js @@ -18,7 +18,7 @@ function formatFileSize(bytes) { const Layout = () => { let showCc = false; let showBcc = false; - let ownAvatars = {}; + const ownAvatars = {}; let attachments = []; let showEmojiPicker = false; let emojiSearch = ''; @@ -397,7 +397,7 @@ const Layout = () => { const attHtml = `

Attachments (${attachments.length}):
`; fullMailBody += attHtml; @@ -701,7 +701,7 @@ const Layout = () => { }), ]), !emojiSearch && m('.emoji-cat-bar', { style: 'display: flex; background: #f8fafc; border-bottom: 1px solid #e2e8f0; padding: 0.25rem; overflow-x: auto;' }, - chatEmoji.EMOJI_CATEGORIES.map(c => + chatEmoji.EMOJI_CATEGORIES.map((c) => m('button', { style: `border: none; background: ${c === emojiCategory ? '#ffffff' : 'transparent'}; border-radius: 0.25rem; padding: 0.3rem 0.4rem; cursor: pointer; font-size: 1rem; box-shadow: ${c === emojiCategory ? '0 1px 2px rgba(0,0,0,0.1)' : 'none'};`, title: c, @@ -711,9 +711,9 @@ const Layout = () => { ), m('.emoji-grid-body', { style: 'padding: 0.5rem; display: grid; grid-template-columns: repeat(7, 1fr); gap: 0.25rem; max-height: 230px; overflow-y: auto;' }, (emojiSearch - ? Object.values(chatEmoji.EMOJI_DATA).flat().filter(e => e.includes(emojiSearch)) + ? Object.values(chatEmoji.EMOJI_DATA).flat().filter((e) => e.includes(emojiSearch)) : (chatEmoji.EMOJI_DATA[emojiCategory] || []) - ).map(e => + ).map((e) => m('button', { style: 'border: none; background: transparent; font-size: 1.25rem; cursor: pointer; padding: 0.25rem; border-radius: 0.25rem; transition: background 0.15s ease;', onmouseenter: (ev) => (ev.currentTarget.style.background = '#f1f5f9'), diff --git a/webui-src/app/mail/mail_resolver.js b/webui-src/app/mail/mail_resolver.js index de0648e..c06b5fc 100644 --- a/webui-src/app/mail/mail_resolver.js +++ b/webui-src/app/mail/mail_resolver.js @@ -195,7 +195,7 @@ const GenericMailList = () => { m(util.MessageSummary, { key: msg.msgId, details: msg, - category: category, + category, }) ) ) diff --git a/webui-src/app/mail/mail_util.js b/webui-src/app/mail/mail_util.js index a46f073..f4acb68 100644 --- a/webui-src/app/mail/mail_util.js +++ b/webui-src/app/mail/mail_util.js @@ -622,11 +622,11 @@ const Table = () => { }; let totalItems = 0; - let tbody = v.children[0]; + const tbody = v.children[0]; if (tbody && tbody.children) { const flatChildren = Array.isArray(tbody.children) ? tbody.children.flat().filter(Boolean) : [tbody.children].filter(Boolean); totalItems = flatChildren.length; - + const start = currentPage * pageSize; const end = start + pageSize; tbody.children = flatChildren.slice(start, end); diff --git a/webui-src/app/mithril.js b/webui-src/app/mithril.js index b60c1b2..eca571d 100644 --- a/webui-src/app/mithril.js +++ b/webui-src/app/mithril.js @@ -1,13 +1,14 @@ (function () { 'use strict'; - function Vnode(tag, key, attrs0, children0, text, dom) { + function Vnode(tag, key, attrs0, children, text, dom) { return { tag: tag, key: key, attrs: attrs0, - children: children0, + children: children, text: text, dom: dom, + is: undefined, domSize: undefined, state: undefined, events: undefined, @@ -22,83 +23,76 @@ return Vnode('#', undefined, undefined, String(node), undefined, undefined); }; Vnode.normalizeChildren = function (input) { - var children0 = []; - if (input.length) { - var isKeyed = input[0] != null && input[0].key != null; - // Note: this is a *very* perf-sensitive check. - // Fun fact: merging the loop like this is somehow faster than splitting - // it, noticeably so. - for (var i = 1; i < input.length; i++) { - if ((input[i] != null && input[i].key != null) !== isKeyed) { - throw new TypeError('Vnodes must either always have keys or never have keys!'); - } - } - for (var i = 0; i < input.length; i++) { - children0[i] = Vnode.normalize(input[i]); - } + // Preallocate the array length (initially holey) and fill every index immediately in order. + // Benchmarking shows better performance on V8. + var children = new Array(input.length); + // Count the number of keyed normalized vnodes for consistency check. + // Note: this is a perf-sensitive check. + // Fun fact: merging the loop like this is somehow faster than splitting + // the check within updateNodes(), noticeably so. + var numKeyed = 0; + for (var i = 0; i < input.length; i++) { + children[i] = Vnode.normalize(input[i]); + if (children[i] !== null && children[i].key != null) numKeyed++; } - return children0; + if (numKeyed !== 0 && numKeyed !== input.length) { + throw new TypeError( + children.includes(null) + ? 'In fragments, vnodes must either all have keys or none have keys. You may wish to consider using an explicit keyed empty fragment, m.fragment({key: ...}), instead of a hole.' + : 'In fragments, vnodes must either all have keys or none have keys.' + ); + } + return children; }; - // Call via `hyperscriptVnode0.apply(startOffset, arguments)` + // Note: the processing of variadic parameters is perf-sensitive. // - // The reason I do it this way, forwarding the arguments and passing the start - // offset in `this`, is so I don't have to create a temporary array in a - // performance-critical path. + // In native ES6, it might be preferable to define hyperscript and fragment + // factories with a final ...args parameter and call hyperscriptVnode(...args), + // since modern engines can optimize spread calls. // - // In native ES6, I'd instead add a final `...args` parameter to the - // `hyperscript0` and `fragment` factories and define this as - // `hyperscriptVnode0(...args)`, since modern engines do optimize that away. But - // ES5 (what Mithril requires thanks to IE support) doesn't give me that luxury, - // and engines aren't nearly intelligent enough to do either of these: - // - // 1. Elide the allocation for `[].slice.call(arguments, 1)` when it's passed to - // another function only to be indexed. - // 2. Elide an `arguments` allocation when it's passed to any function other - // than `Function.prototype.apply` or `Reflect.apply`. - // - // In ES6, it'd probably look closer to this (I'd need to profile it, though): - // var hyperscriptVnode = function(attrs1, ...children1) { - // if (attrs1 == null || typeof attrs1 === "object" && attrs1.tag == null && !Array.isArray(attrs1)) { - // if (children1.length === 1 && Array.isArray(children1[0])) children1 = children1[0] - // } else { - // children1 = children1.length === 0 && Array.isArray(attrs1) ? attrs1 : [attrs1, ...children1] - // attrs1 = undefined - // } - // - // if (attrs1 == null) attrs1 = {} - // return Vnode("", attrs1.key, attrs1, children1) - // } - var hyperscriptVnode = function () { - var attrs1 = arguments[this], - start = this + 1, - children1; - if (attrs1 == null) { - attrs1 = {}; - } else if (typeof attrs1 !== 'object' || attrs1.tag != null || Array.isArray(attrs1)) { - attrs1 = {}; - start = this; - } - if (arguments.length === start + 1) { - children1 = arguments[start]; - if (!Array.isArray(children1)) children1 = [children1]; + // However, benchmarks showed this was not faster. As a result, spread is used + // only in the parameter lists of hyperscript and fragment, while an array is + // passed to hyperscriptVnode. + var hyperscriptVnode = function (attrs1, children0) { + if ( + attrs1 == null || + (typeof attrs1 === 'object' && attrs1.tag == null && !Array.isArray(attrs1)) + ) { + if (children0.length === 1 && Array.isArray(children0[0])) children0 = children0[0]; } else { - children1 = []; - while (start < arguments.length) children1.push(arguments[start++]); + children0 = children0.length === 0 && Array.isArray(attrs1) ? attrs1 : [attrs1, ...children0]; + attrs1 = undefined; } - return Vnode('', attrs1.key, attrs1, children1); + return Vnode('', attrs1 && attrs1.key, attrs1, children0); }; - var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g; - var selectorCache = {}; + // This exists so I'm only saving it once. var hasOwn = {}.hasOwnProperty; + // This is an attrs object that is used by default when attrs is undefined or null. + var emptyAttrs = {}; + // This Map manages the following: + // - Whether an attrs is cached attrs generated by compileSelector(). + // - Whether the cached attrs is "static", i.e., does not contain any form attributes. + // These information will be useful to skip updating attrs in render(). + // + // Since the attrs used as keys in this map are not released from the selectorCache object, + // there is no risk of memory leaks. Therefore, Map is used here instead of WeakMap. + var cachedAttrsIsStaticMap = new Map([[emptyAttrs, true]]); + var selectorParser = + /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g; + var selectorCache = Object.create(null); function isEmpty(object) { for (var key in object) if (hasOwn.call(object, key)) return false; return true; } + function isFormAttributeKey(key) { + return key === 'value' || key === 'checked' || key === 'selectedIndex' || key === 'selected'; + } function compileSelector(selector) { var match, tag = 'div', classes = [], - attrs = {}; + attrs = {}, + isStatic = true; while ((match = selectorParser.exec(selector))) { var type = match[1], value = match[2]; @@ -109,61 +103,50 @@ var attrValue = match[6]; if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, '$1').replace(/\\\\/g, '\\'); if (match[4] === 'class') classes.push(attrValue); - else attrs[match[4]] = attrValue === '' ? attrValue : attrValue || true; + else { + attrs[match[4]] = attrValue === '' ? attrValue : attrValue || true; + if (isFormAttributeKey(match[4])) isStatic = false; + } } } if (classes.length > 0) attrs.className = classes.join(' '); - return (selectorCache[selector] = { tag: tag, attrs: attrs }); + if (isEmpty(attrs)) attrs = emptyAttrs; + else cachedAttrsIsStaticMap.set(attrs, isStatic); + return (selectorCache[selector] = { tag: tag, attrs: attrs, is: attrs.is }); } function execSelector(state, vnode) { - var attrs = vnode.attrs; - var children = Vnode.normalizeChildren(vnode.children); - var hasClass = hasOwn.call(attrs, 'class'); - var className = hasClass ? attrs.class : attrs.className; vnode.tag = state.tag; - vnode.attrs = null; - vnode.children = undefined; - if (!isEmpty(state.attrs) && !isEmpty(attrs)) { - var newAttrs = {}; - for (var key in attrs) { - if (hasOwn.call(attrs, key)) newAttrs[key] = attrs[key]; - } - attrs = newAttrs; + var attrs = vnode.attrs; + if (attrs == null) { + vnode.attrs = state.attrs; + vnode.is = state.is; + return vnode; } - for (var key in state.attrs) { - if (hasOwn.call(state.attrs, key) && key !== 'className' && !hasOwn.call(attrs, key)) { - attrs[key] = state.attrs[key]; - } + if (hasOwn.call(attrs, 'class')) { + if (attrs.class != null) attrs.className = attrs.class; + attrs.class = null; } - if (className != null || state.attrs.className != null) - attrs.className = - className != null - ? state.attrs.className != null + if (state.attrs !== emptyAttrs) { + var className = attrs.className; + attrs = Object.assign({}, state.attrs, attrs); + if (state.attrs.className != null) + attrs.className = + className != null ? String(state.attrs.className) + ' ' + String(className) - : className - : state.attrs.className != null - ? state.attrs.className - : null; - if (hasClass) attrs.class = null; - for (var key in attrs) { - if (hasOwn.call(attrs, key) && key !== 'key') { - vnode.attrs = attrs; - break; - } + : state.attrs.className; } - if ( - Array.isArray(children) && - children.length === 1 && - children[0] != null && - children[0].tag === '#' - ) { - vnode.text = children[0].children; - } else { - vnode.children = children; + // workaround for #2622 (reorder keys in attrs to set "type" first) + // The DOM does things to inputs based on the "type", so it needs set first. + // See: https://github.com/MithrilJS/mithril.js/issues/2622 + if (state.tag === 'input' && hasOwn.call(attrs, 'type')) { + attrs = Object.assign({ type: attrs.type }, attrs); } + // This reduces the complexity of the evaluation of "is" within the render function. + vnode.is = attrs.is; + vnode.attrs = attrs; return vnode; } - function hyperscript(selector) { + function hyperscript(selector, attrs, ...children) { if ( selector == null || (typeof selector !== 'string' && @@ -172,12 +155,13 @@ ) { throw Error('The selector must be either a string or a component.'); } - var vnode = hyperscriptVnode.apply(1, arguments); + var vnode = hyperscriptVnode(attrs, children); if (typeof selector === 'string') { vnode.children = Vnode.normalizeChildren(vnode.children); if (selector !== '[') return execSelector(selectorCache[selector] || compileSelector(selector), vnode); } + if (vnode.attrs == null) vnode.attrs = {}; vnode.tag = selector; return vnode; } @@ -185,185 +169,50 @@ if (html == null) html = ''; return Vnode('<', undefined, undefined, html, undefined, undefined); }; - hyperscript.fragment = function () { - var vnode2 = hyperscriptVnode.apply(0, arguments); + hyperscript.fragment = function (attrs4, ...children1) { + var vnode2 = hyperscriptVnode(attrs4, children1); + if (vnode2.attrs == null) vnode2.attrs = {}; vnode2.tag = '['; vnode2.children = Vnode.normalizeChildren(vnode2.children); return vnode2; }; - /** @constructor */ - var PromisePolyfill = function (executor) { - if (!(this instanceof PromisePolyfill)) throw new Error('Promise must be called with `new`'); - if (typeof executor !== 'function') throw new TypeError('executor must be a function'); - var self = this, - resolvers = [], - rejectors = [], - resolveCurrent = handler(resolvers, true), - rejectCurrent = handler(rejectors, false); - var instance = (self._instance = { resolvers: resolvers, rejectors: rejectors }); - var callAsync = typeof setImmediate === 'function' ? setImmediate : setTimeout; - function handler(list, shouldAbsorb) { - return function execute(value) { - var then; - try { - if ( - shouldAbsorb && - value != null && - (typeof value === 'object' || typeof value === 'function') && - typeof (then = value.then) === 'function' - ) { - if (value === self) throw new TypeError("Promise can't be resolved w/ itself"); - executeOnce(then.bind(value)); - } else { - callAsync(function () { - if (!shouldAbsorb && list.length === 0) - console.error('Possible unhandled promise rejection:', value); - for (var i = 0; i < list.length; i++) list[i](value); - (resolvers.length = 0), (rejectors.length = 0); - instance.state = shouldAbsorb; - instance.retry = function () { - execute(value); - }; - }); - } - } catch (e) { - rejectCurrent(e); + var delayedRemoval = new WeakMap(); + function* domFor(vnode4) { + // To avoid unintended mangling of the internal bundler, + // parameter destructuring is not used here. + var dom = vnode4.dom; + var domSize0 = vnode4.domSize; + var generation0 = delayedRemoval.get(dom); + if (dom != null) + do { + var nextSibling = dom.nextSibling; + if (delayedRemoval.get(dom) === generation0) { + yield dom; + domSize0--; } - }; - } - function executeOnce(then) { - var runs = 0; - function run(fn) { - return function (value) { - if (runs++ > 0) return; - fn(value); - }; - } - var onerror = run(rejectCurrent); - try { - then(run(resolveCurrent), onerror); - } catch (e) { - onerror(e); - } - } - executeOnce(executor); - }; - PromisePolyfill.prototype.then = function (onFulfilled, onRejection) { - var self = this, - instance = self._instance; - function handle(callback, list, next, state) { - list.push(function (value) { - if (typeof callback !== 'function') next(value); - else - try { - resolveNext(callback(value)); - } catch (e) { - if (rejectNext) rejectNext(e); - } - }); - if (typeof instance.retry === 'function' && state === instance.state) instance.retry(); - } - var resolveNext, rejectNext; - var promise = new PromisePolyfill(function (resolve, reject) { - (resolveNext = resolve), (rejectNext = reject); - }); - handle(onFulfilled, instance.resolvers, resolveNext, true), - handle(onRejection, instance.rejectors, rejectNext, false); - return promise; - }; - PromisePolyfill.prototype.catch = function (onRejection) { - return this.then(null, onRejection); - }; - PromisePolyfill.prototype.finally = function (callback) { - return this.then( - function (value) { - return PromisePolyfill.resolve(callback()).then(function () { - return value; - }); - }, - function (reason) { - return PromisePolyfill.resolve(callback()).then(function () { - return PromisePolyfill.reject(reason); - }); - } - ); - }; - PromisePolyfill.resolve = function (value) { - if (value instanceof PromisePolyfill) return value; - return new PromisePolyfill(function (resolve) { - resolve(value); - }); - }; - PromisePolyfill.reject = function (value) { - return new PromisePolyfill(function (resolve, reject) { - reject(value); - }); - }; - PromisePolyfill.all = function (list) { - return new PromisePolyfill(function (resolve, reject) { - var total = list.length, - count = 0, - values = []; - if (list.length === 0) resolve([]); - else - for (var i = 0; i < list.length; i++) { - (function (i) { - function consume(value) { - count++; - values[i] = value; - if (count === total) resolve(values); - } - if ( - list[i] != null && - (typeof list[i] === 'object' || typeof list[i] === 'function') && - typeof list[i].then === 'function' - ) { - list[i].then(consume, reject); - } else consume(list[i]); - })(i); - } - }); - }; - PromisePolyfill.race = function (list) { - return new PromisePolyfill(function (resolve, reject) { - for (var i = 0; i < list.length; i++) { - list[i].then(resolve, reject); - } - }); - }; - if (typeof window !== 'undefined') { - if (typeof window.Promise === 'undefined') { - window.Promise = PromisePolyfill; - } else if (!window.Promise.prototype.finally) { - window.Promise.prototype.finally = PromisePolyfill.prototype.finally; - } - var PromisePolyfill = window.Promise; - } else if (typeof global !== 'undefined') { - if (typeof global.Promise === 'undefined') { - global.Promise = PromisePolyfill; - } else if (!global.Promise.prototype.finally) { - global.Promise.prototype.finally = PromisePolyfill.prototype.finally; - } - var PromisePolyfill = global.Promise; - } else { + dom = nextSibling; + } while (domSize0); } - var _12 = function ($window) { - var $doc = $window && $window.document; - var currentRedraw; + var _14 = function () { var nameSpace = { svg: 'http://www.w3.org/2000/svg', math: 'http://www.w3.org/1998/Math/MathML', }; + var currentRedraw; + var currentRender; + function getDocument(dom) { + return dom.ownerDocument; + } function getNameSpace(vnode3) { return (vnode3.attrs && vnode3.attrs.xmlns) || nameSpace[vnode3.tag]; } - //sanity check to discourage people from doing `vnode3.state = ...` + //sanity check to discourage people from doing `vnode.state = ...` function checkState(vnode3, original) { - if (vnode3.state !== original) throw new Error('`vnode.state` must not be modified'); + if (vnode3.state !== original) throw new Error("'vnode.state' must not be modified."); } //Note: the hook is passed as the `this` argument to allow proxying the //arguments without requiring a full array allocation to do so. It also - //takes advantage of the fact the current `vnode3` is the first argument in + //takes advantage of the fact the current `vnode` is the first argument in //all lifecycle methods. function callHook(vnode3) { var original = vnode3.state; @@ -375,9 +224,9 @@ } // IE11 (at least) throws an UnspecifiedError when accessing document.activeElement when // inside an iframe. Catch and swallow this error, and heavy-handidly return null. - function activeElement() { + function activeElement(dom) { try { - return $doc.activeElement; + return getDocument(dom).activeElement; } catch (e) { return null; } @@ -412,8 +261,8 @@ } else createComponent(parent, vnode3, hooks, ns, nextSibling); } function createText(parent, vnode3, nextSibling) { - vnode3.dom = $doc.createTextNode(vnode3.children); - insertNode(parent, vnode3.dom, nextSibling); + vnode3.dom = getDocument(parent).createTextNode(vnode3.children); + insertDOM(parent, vnode3.dom, nextSibling); } var possibleParents = { caption: 'table', @@ -433,7 +282,7 @@ // div.innerHTML = "ij" // console.log(div.innerHTML) // --> "ij", no in sight. - var temp = $doc.createElement(possibleParents[match0[1]] || 'div'); + var temp = getDocument(parent).createElement(possibleParents[match0[1]] || 'div'); if (ns === 'http://www.w3.org/2000/svg') { temp.innerHTML = '' + vnode3.children + ''; temp = temp.firstChild; @@ -442,53 +291,45 @@ } vnode3.dom = temp.firstChild; vnode3.domSize = temp.childNodes.length; - // Capture nodes to remove, so we don't confuse them. - vnode3.instance = []; - var fragment = $doc.createDocumentFragment(); + var fragment = getDocument(parent).createDocumentFragment(); var child; while ((child = temp.firstChild)) { - vnode3.instance.push(child); fragment.appendChild(child); } - insertNode(parent, fragment, nextSibling); + insertDOM(parent, fragment, nextSibling); } function createFragment(parent, vnode3, hooks, ns, nextSibling) { - var fragment = $doc.createDocumentFragment(); + var fragment = getDocument(parent).createDocumentFragment(); if (vnode3.children != null) { - var children3 = vnode3.children; - createNodes(fragment, children3, 0, children3.length, hooks, null, ns); + var children2 = vnode3.children; + createNodes(fragment, children2, 0, children2.length, hooks, null, ns); } vnode3.dom = fragment.firstChild; vnode3.domSize = fragment.childNodes.length; - insertNode(parent, fragment, nextSibling); + insertDOM(parent, fragment, nextSibling); } function createElement(parent, vnode3, hooks, ns, nextSibling) { var tag = vnode3.tag; - var attrs2 = vnode3.attrs; - var is = attrs2 && attrs2.is; + var attrs5 = vnode3.attrs; + var is = vnode3.is; ns = getNameSpace(vnode3) || ns; var element = ns ? is - ? $doc.createElementNS(ns, tag, { is: is }) - : $doc.createElementNS(ns, tag) + ? getDocument(parent).createElementNS(ns, tag, { is: is }) + : getDocument(parent).createElementNS(ns, tag) : is - ? $doc.createElement(tag, { is: is }) - : $doc.createElement(tag); + ? getDocument(parent).createElement(tag, { is: is }) + : getDocument(parent).createElement(tag); vnode3.dom = element; - if (attrs2 != null) { - setAttrs(vnode3, attrs2, ns); + if (attrs5 != null) { + setAttrs(vnode3, attrs5, ns); } - insertNode(parent, element, nextSibling); + insertDOM(parent, element, nextSibling); if (!maybeSetContentEditable(vnode3)) { - if (vnode3.text != null) { - if (vnode3.text !== '') element.textContent = vnode3.text; - else - vnode3.children = [Vnode('#', undefined, undefined, vnode3.text, undefined, undefined)]; - } if (vnode3.children != null) { - var children3 = vnode3.children; - createNodes(element, children3, 0, children3.length, hooks, null, ns); - if (vnode3.tag === 'select' && attrs2 != null) setLateSelectAttrs(vnode3, attrs2); + var children2 = vnode3.children; + createNodes(element, children2, 0, children2.length, hooks, null, ns); + if (vnode3.tag === 'select' && attrs5 != null) setLateSelectAttrs(vnode3, attrs5); } } } @@ -521,7 +362,7 @@ if (vnode3.instance != null) { createNode(parent, vnode3.instance, hooks, ns, nextSibling); vnode3.dom = vnode3.instance.dom; - vnode3.domSize = vnode3.dom != null ? vnode3.instance.domSize : 0; + vnode3.domSize = vnode3.instance.domSize; } else { vnode3.domSize = 0; } @@ -529,10 +370,10 @@ //update /** * @param {Element|Fragment} parent - the parent element - * @param {Vnode[] | null} old - the list of vnodes of the last `render0()` call for + * @param {Vnode[] | null} old - the list of vnodes of the last `render()` call for * this part of the tree - * @param {Vnode[] | null} vnodes - as above, but for the current `render0()` call. - * @param {Function[]} hooks - an accumulator of post-render0 hooks (oncreate/onupdate) + * @param {Vnode[] | null} vnodes - as above, but for the current `render()` call. + * @param {Function[]} hooks - an accumulator of post-render hooks (oncreate/onupdate) * @param {Element | null} nextSibling - the next DOM node if we're dealing with a * fragment that is not the last item in its * parent @@ -567,13 +408,13 @@ // // In order to diff keyed lists, one has to // - // 1) match0 nodes in both lists, per key, and update them accordingly + // 1) match nodes in both lists, per key, and update them accordingly // 2) create the nodes present in the new list, but absent in the old one // 3) remove the nodes present in the old list, but absent in the new one // 4) figure out what nodes in 1) to move in order to minimize the DOM operations. // - // To achieve 1) one can create a dictionary of keys => index (for the old list), then0 iterate - // over the new list and for each new vnode3, find the corresponding vnode3 in the old list using + // To achieve 1) one can create a dictionary of keys => index (for the old list), then iterate + // over the new list and for each new vnode, find the corresponding vnode in the old list using // the map. // 2) is achieved in the same step: if a new node has no corresponding entry in the map, it is new // and must be created. @@ -585,7 +426,7 @@ // the longest increasing subsequence is the list of nodes that can remain in place. Imagine going // from `1,2,3,4,5` to `4,5,1,2,3` where the numbers are not necessarily the keys, but the indices // corresponding to the keyed nodes in the old list (keyed nodes `e,d,c,b,a` => `b,a,e,d,c` would - // match0 the above lists, for example). + // match the above lists, for example). // // In there are two increasing subsequences: `4,5` and `1,2,3`, the latter being the longest. We // can update those nodes without moving them, and only call `insertNode` on `4` and `5`. @@ -607,7 +448,7 @@ // // `updateNode()` and `createNode()` expect a nextSibling parameter to perform DOM operations. // When the list is being traversed top-down, at any index, the DOM nodes up to the previous - // vnode3 reflect the content of the new list, whereas the rest of the DOM nodes reflect the old + // vnode reflect the content of the new list, whereas the rest of the DOM nodes reflect the old // list. The next sibling must be looked for in the old list using `getNextSibling(... oldStart + 1 ...)`. // // In the other scenarios (swaps, upwards traversal, map-based diff), @@ -618,13 +459,8 @@ // // In most scenarios `updateNode()` and `createNode()` perform the DOM operations. However, // this is not the case if the node moved (second and fourth part of the diff algo). We move - // the old DOM nodes before updateNode runs0 because it enables us to use the cached `nextSibling` + // the old DOM nodes before updateNode runs because it enables us to use the cached `nextSibling` // variable rather than fetching it using `getNextSibling()`. - // - // The fourth part of the diff currently inserts nodes unconditionally, leading to issues - // like #1791 and #1999. We need to be smarter about those situations where adjascent old - // nodes remain together in the new list in a way that isn't covered by parts one and - // three of the diff algo. function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) { if (old === vnodes || (old == null && vnodes == null)) return; else if (old == null || old.length === 0) @@ -632,16 +468,15 @@ else if (vnodes == null || vnodes.length === 0) removeNodes(parent, old, 0, old.length); else { var isOldKeyed = old[0] != null && old[0].key != null; - var isKeyed0 = vnodes[0] != null && vnodes[0].key != null; + var isKeyed = vnodes[0] != null && vnodes[0].key != null; var start = 0, oldStart = 0; if (!isOldKeyed) while (oldStart < old.length && old[oldStart] == null) oldStart++; - if (!isKeyed0) while (start < vnodes.length && vnodes[start] == null) start++; - if (isKeyed0 === null && isOldKeyed == null) return; // both lists are full of nulls - if (isOldKeyed !== isKeyed0) { + if (!isKeyed) while (start < vnodes.length && vnodes[start] == null) start++; + if (isOldKeyed !== isKeyed) { removeNodes(parent, old, oldStart, old.length); createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns); - } else if (!isKeyed0) { + } else if (!isKeyed) { // Don't index past the end of either list (causes deopts). var commonLength = old.length < vnodes.length ? old.length : vnodes.length; // Rewind if necessary to the first non-null index on either side. @@ -677,14 +512,14 @@ if (oe.key !== ve.key) break; if (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns); if (ve.dom != null) nextSibling = ve.dom; - oldEnd--, end--; + (oldEnd--, end--); } // top-down while (oldEnd >= oldStart && end >= start) { o = old[oldStart]; v = vnodes[start]; if (o.key !== v.key) break; - oldStart++, start++; + (oldStart++, start++); if (o !== v) updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), ns); } @@ -693,9 +528,9 @@ if (start === end) break; if (o.key !== ve.key || oe.key !== v.key) break; topSibling = getNextSibling(old, oldStart, nextSibling); - moveNodes(parent, oe, topSibling); + moveDOM(parent, oe, topSibling); if (oe !== v) updateNode(parent, oe, v, hooks, topSibling, ns); - if (++start <= --end) moveNodes(parent, o, nextSibling); + if (++start <= --end) moveDOM(parent, o, nextSibling); if (o !== ve) updateNode(parent, o, ve, hooks, nextSibling, ns); if (ve.dom != null) nextSibling = ve.dom; oldStart++; @@ -710,7 +545,7 @@ if (oe.key !== ve.key) break; if (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns); if (ve.dom != null) nextSibling = ve.dom; - oldEnd--, end--; + (oldEnd--, end--); oe = old[oldEnd]; ve = vnodes[end]; } @@ -757,7 +592,7 @@ if (oldIndices[i - start] === -1) createNode(parent, v, hooks, ns, nextSibling); else { if (lisIndices[li] === i - start) li--; - else moveNodes(parent, v, nextSibling); + else moveDOM(parent, v, nextSibling); } if (v.dom != null) nextSibling = vnodes[i].dom; } @@ -776,7 +611,7 @@ function updateNode(parent, old, vnode3, hooks, nextSibling, ns) { var oldTag = old.tag, tag = vnode3.tag; - if (oldTag === tag) { + if (oldTag === tag && old.is === vnode3.is) { vnode3.state = old.state; vnode3.events = old.events; if (shouldNotUpdate(vnode3, old)) return; @@ -811,54 +646,40 @@ } function updateHTML(parent, old, vnode3, ns, nextSibling) { if (old.children !== vnode3.children) { - removeHTML(parent, old); + removeDOM(parent, old); createHTML(parent, vnode3, ns, nextSibling); } else { vnode3.dom = old.dom; vnode3.domSize = old.domSize; - vnode3.instance = old.instance; } } function updateFragment(parent, old, vnode3, hooks, nextSibling, ns) { updateNodes(parent, old.children, vnode3.children, hooks, nextSibling, ns); var domSize = 0, - children3 = vnode3.children; + children2 = vnode3.children; vnode3.dom = null; - if (children3 != null) { - for (var i = 0; i < children3.length; i++) { - var child = children3[i]; + if (children2 != null) { + for (var i = 0; i < children2.length; i++) { + var child = children2[i]; if (child != null && child.dom != null) { if (vnode3.dom == null) vnode3.dom = child.dom; domSize += child.domSize || 1; } } - if (domSize !== 1) vnode3.domSize = domSize; } + vnode3.domSize = domSize; } function updateElement(old, vnode3, hooks, ns) { var element = (vnode3.dom = old.dom); ns = getNameSpace(vnode3) || ns; - if (vnode3.tag === 'textarea') { - if (vnode3.attrs == null) vnode3.attrs = {}; - if (vnode3.text != null) { - vnode3.attrs.value = vnode3.text; //FIXME handle0 multiple children3 - vnode3.text = undefined; - } + if ( + old.attrs != vnode3.attrs || + (vnode3.attrs != null && !cachedAttrsIsStaticMap.get(vnode3.attrs)) + ) { + updateAttrs(vnode3, old.attrs, vnode3.attrs, ns); } - updateAttrs(vnode3, old.attrs, vnode3.attrs, ns); if (!maybeSetContentEditable(vnode3)) { - if (old.text != null && vnode3.text != null && vnode3.text !== '') { - if (old.text.toString() !== vnode3.text.toString()) - old.dom.firstChild.nodeValue = vnode3.text; - } else { - if (old.text != null) - old.children = [ - Vnode('#', undefined, undefined, old.text, undefined, old.dom.firstChild), - ]; - if (vnode3.text != null) - vnode3.children = [Vnode('#', undefined, undefined, vnode3.text, undefined, undefined)]; - updateNodes(element, old.children, vnode3.children, hooks, null, ns); - } + updateNodes(element, old.children, vnode3.children, hooks, null, ns); } } function updateComponent(parent, old, vnode3, hooks, nextSibling, ns) { @@ -872,13 +693,9 @@ else updateNode(parent, old.instance, vnode3.instance, hooks, nextSibling, ns); vnode3.dom = vnode3.instance.dom; vnode3.domSize = vnode3.instance.domSize; - } else if (old.instance != null) { - removeNode(parent, old.instance); - vnode3.dom = undefined; - vnode3.domSize = 0; } else { - vnode3.dom = old.dom; - vnode3.domSize = old.domSize; + if (old.instance != null) removeNode(parent, old.instance); + vnode3.domSize = 0; } } function getKeyMap(vnodes, start, end) { @@ -896,7 +713,7 @@ // takes a list of unique numbers (-1 is special and can // occur multiple times) and returns an array with the indices // of the items that are part of the longest increasing - // subsequece + // subsequence var lisTemp = []; function makeLisIndices(a) { var result = [0]; @@ -945,45 +762,21 @@ } return nextSibling; } - // This covers a really specific edge case: - // - Parent node is keyed and contains child - // - Child is removed, returns unresolved promise0 in `onbeforeremove` - // - Parent node is moved in keyed diff - // - Remaining children3 still need moved appropriately - // - // Ideally, I'd track removed nodes as well, but that introduces a lot more - // complexity and I'm0 not exactly interested in doing that. - function moveNodes(parent, vnode3, nextSibling) { - var frag = $doc.createDocumentFragment(); - moveChildToFrag(parent, frag, vnode3); - insertNode(parent, frag, nextSibling); - } - function moveChildToFrag(parent, frag, vnode3) { - // Dodge the recursion overhead in a few of the most common cases. - while (vnode3.dom != null && vnode3.dom.parentNode === parent) { - if (typeof vnode3.tag !== 'string') { - vnode3 = vnode3.instance; - if (vnode3 != null) continue; - } else if (vnode3.tag === '<') { - for (var i = 0; i < vnode3.instance.length; i++) { - frag.appendChild(vnode3.instance[i]); - } - } else if (vnode3.tag !== '[') { - // Don't recurse for text nodes *or* elements, just fragments - frag.appendChild(vnode3.dom); - } else if (vnode3.children.length === 1) { - vnode3 = vnode3.children[0]; - if (vnode3 != null) continue; + // This handles fragments with zombie children (removed from vdom, but persisted in DOM through onbeforeremove) + function moveDOM(parent, vnode3, nextSibling) { + if (vnode3.dom != null) { + var target; + if (vnode3.domSize == null || vnode3.domSize === 1) { + // don't allocate for the common case + target = vnode3.dom; } else { - for (var i = 0; i < vnode3.children.length; i++) { - var child = vnode3.children[i]; - if (child != null) moveChildToFrag(parent, frag, child); - } + target = getDocument(parent).createDocumentFragment(); + for (var dom of domFor(vnode3)) target.appendChild(dom); } - break; + insertDOM(parent, target, nextSibling); } } - function insertNode(parent, dom, nextSibling) { + function insertDOM(parent, dom, nextSibling) { if (nextSibling != null) parent.insertBefore(dom, nextSibling); else parent.appendChild(dom); } @@ -994,12 +787,12 @@ vnode3.attrs.contentEditable == null) // property ) return false; - var children3 = vnode3.children; - if (children3 != null && children3.length === 1 && children3[0].tag === '<') { - var content = children3[0].children; + var children2 = vnode3.children; + if (children2 != null && children2.length === 1 && children2[0].tag === '<') { + var content = children2[0].children; if (vnode3.dom.innerHTML !== content) vnode3.dom.innerHTML = content; - } else if (vnode3.text != null || (children3 != null && children3.length !== 0)) - throw new Error('Child node of a contenteditable must be trusted'); + } else if (children2 != null && children2.length !== 0) + throw new Error('Child node of a contenteditable must be trusted.'); return true; } //remove @@ -1009,87 +802,38 @@ if (vnode3 != null) removeNode(parent, vnode3); } } - function removeNode(parent, vnode3) { - var mask = 0; + function tryBlockRemove(parent, vnode3, source, counter) { var original = vnode3.state; - var stateResult, attrsResult; - if (typeof vnode3.tag !== 'string' && typeof vnode3.state.onbeforeremove === 'function') { - var result = callHook.call(vnode3.state.onbeforeremove, vnode3); - if (result != null && typeof result.then === 'function') { - mask = 1; - stateResult = result; - } - } - if (vnode3.attrs && typeof vnode3.attrs.onbeforeremove === 'function') { - var result = callHook.call(vnode3.attrs.onbeforeremove, vnode3); - if (result != null && typeof result.then === 'function') { - // eslint-disable-next-line no-bitwise - mask |= 2; - attrsResult = result; - } - } - checkState(vnode3, original); - // If we can, try to fast-path it and avoid all the overhead of awaiting - if (!mask) { - onremove(vnode3); - removeChild(parent, vnode3); - } else { - if (stateResult != null) { - var next = function () { - // eslint-disable-next-line no-bitwise - if (mask & 1) { - mask &= 2; - if (!mask) reallyRemove(); - } - }; - stateResult.then(next, next); - } - if (attrsResult != null) { - var next = function () { - // eslint-disable-next-line no-bitwise - if (mask & 2) { - mask &= 1; - if (!mask) reallyRemove(); - } - }; - attrsResult.then(next, next); - } - } - function reallyRemove() { + var result = callHook.call(source.onbeforeremove, vnode3); + if (result == null) return; + var generation = currentRender; + for (var dom of domFor(vnode3)) delayedRemoval.set(dom, generation); + counter.v++; + Promise.resolve(result).finally(function () { checkState(vnode3, original); + tryResumeRemove(parent, vnode3, counter); + }); + } + function tryResumeRemove(parent, vnode3, counter) { + if (--counter.v === 0) { onremove(vnode3); - removeChild(parent, vnode3); + removeDOM(parent, vnode3); } } - function removeHTML(parent, vnode3) { - for (var i = 0; i < vnode3.instance.length; i++) { - parent.removeChild(vnode3.instance[i]); - } + function removeNode(parent, vnode3) { + var counter = { v: 1 }; + if (typeof vnode3.tag !== 'string' && typeof vnode3.state.onbeforeremove === 'function') + tryBlockRemove(parent, vnode3, vnode3.state, counter); + if (vnode3.attrs && typeof vnode3.attrs.onbeforeremove === 'function') + tryBlockRemove(parent, vnode3, vnode3.attrs, counter); + tryResumeRemove(parent, vnode3, counter); } - function removeChild(parent, vnode3) { - // Dodge the recursion overhead in a few of the most common cases. - while (vnode3.dom != null && vnode3.dom.parentNode === parent) { - if (typeof vnode3.tag !== 'string') { - vnode3 = vnode3.instance; - if (vnode3 != null) continue; - } else if (vnode3.tag === '<') { - removeHTML(parent, vnode3); - } else { - if (vnode3.tag !== '[') { - parent.removeChild(vnode3.dom); - if (!Array.isArray(vnode3.children)) break; - } - if (vnode3.children.length === 1) { - vnode3 = vnode3.children[0]; - if (vnode3 != null) continue; - } else { - for (var i = 0; i < vnode3.children.length; i++) { - var child = vnode3.children[i]; - if (child != null) removeChild(parent, child); - } - } - } - break; + function removeDOM(parent, vnode3) { + if (vnode3.dom == null) return; + if (vnode3.domSize == null || vnode3.domSize === 1) { + parent.removeChild(vnode3.dom); + } else { + for (var dom of domFor(vnode3)) parent.removeChild(dom); } } function onremove(vnode3) { @@ -1100,25 +844,25 @@ if (typeof vnode3.tag !== 'string') { if (vnode3.instance != null) onremove(vnode3.instance); } else { - var children3 = vnode3.children; - if (Array.isArray(children3)) { - for (var i = 0; i < children3.length; i++) { - var child = children3[i]; + if (vnode3.events != null) vnode3.events._ = null; + var children2 = vnode3.children; + if (Array.isArray(children2)) { + for (var i = 0; i < children2.length; i++) { + var child = children2[i]; if (child != null) onremove(child); } } } } - //attrs2 - function setAttrs(vnode3, attrs2, ns) { - for (var key in attrs2) { - setAttr(vnode3, key, null, attrs2[key], ns); + //attrs + function setAttrs(vnode3, attrs5, ns) { + for (var key in attrs5) { + setAttr(vnode3, key, null, attrs5[key], ns); } } function setAttr(vnode3, key, old, value, ns) { if ( key === 'key' || - key === 'is' || value == null || isLifecycleMethod(key) || (old === value && !isFormAttribute(vnode3, key) && typeof value !== 'object') @@ -1133,19 +877,26 @@ // Only do the coercion if we're actually going to check the value. /* eslint-disable no-implicit-coercion */ //setting input[value] to same value by typing on focused element moves cursor to end in Chrome + //setting input[type=file][value] to same value causes an error to be generated if it's non-empty + //minlength/maxlength validation isn't performed on script-set values(#2256) if ( (vnode3.tag === 'input' || vnode3.tag === 'textarea') && - vnode3.dom.value === '' + value && - vnode3.dom === activeElement() + vnode3.dom.value === '' + value ) return; //setting select[value] to same value while having select open blinks select dropdown in Chrome if (vnode3.tag === 'select' && old !== null && vnode3.dom.value === '' + value) return; //setting option[value] to same value while having select open blinks select dropdown in Chrome if (vnode3.tag === 'option' && old !== null && vnode3.dom.value === '' + value) return; + //setting input[type=file][value] to different value is an error if it's non-empty + // Not ideal, but it at least works around the most common source of uncaught exceptions for now. + if (vnode3.tag === 'input' && vnode3.attrs.type === 'file' && '' + value !== '') { + console.error('`value` is read-only on file inputs!'); + return; + } /* eslint-enable no-implicit-coercion */ } - // If you assign an input type0 that is not supported by IE 11 with an assignment expression, an error will occur. + // If you assign an input type that is not supported by IE 11 with an assignment expression, an error will occur. if (vnode3.tag === 'input' && key === 'type') vnode3.dom.setAttribute(key, value); else vnode3.dom[key] = value; } else { @@ -1156,19 +907,19 @@ } } function removeAttr(vnode3, key, old, ns) { - if (key === 'key' || key === 'is' || old == null || isLifecycleMethod(key)) return; - if (key[0] === 'o' && key[1] === 'n' && !isLifecycleMethod(key)) - updateEvent(vnode3, key, undefined); + if (key === 'key' || old == null || isLifecycleMethod(key)) return; + if (key[0] === 'o' && key[1] === 'n') updateEvent(vnode3, key, undefined); else if (key === 'style') updateStyle(vnode3.dom, old, null); else if ( hasPropertyKey(vnode3, key, ns) && key !== 'className' && + key !== 'title' && // creates "null" as title !( key === 'value' && (vnode3.tag === 'option' || (vnode3.tag === 'select' && vnode3.dom.selectedIndex === -1 && - vnode3.dom === activeElement())) + vnode3.dom === activeElement(vnode3.dom))) ) && !(vnode3.tag === 'input' && key === 'type') ) { @@ -1179,42 +930,50 @@ if (old !== false) vnode3.dom.removeAttribute(key === 'className' ? 'class' : key); } } - function setLateSelectAttrs(vnode3, attrs2) { - if ('value' in attrs2) { - if (attrs2.value === null) { + function setLateSelectAttrs(vnode3, attrs5) { + if ('value' in attrs5) { + if (attrs5.value === null) { if (vnode3.dom.selectedIndex !== -1) vnode3.dom.value = null; } else { - var normalized = '' + attrs2.value; // eslint-disable-line no-implicit-coercion + var normalized = '' + attrs5.value; // eslint-disable-line no-implicit-coercion if (vnode3.dom.value !== normalized || vnode3.dom.selectedIndex === -1) { vnode3.dom.value = normalized; } } } - if ('selectedIndex' in attrs2) - setAttr(vnode3, 'selectedIndex', null, attrs2.selectedIndex, undefined); + if ('selectedIndex' in attrs5) + setAttr(vnode3, 'selectedIndex', null, attrs5.selectedIndex, undefined); } - function updateAttrs(vnode3, old, attrs2, ns) { - if (attrs2 != null) { - for (var key in attrs2) { - setAttr(vnode3, key, old && old[key], attrs2[key], ns); - } - } + function updateAttrs(vnode3, old, attrs5, ns) { + // Some attributes may NOT be case-sensitive (e.g. data-***), + // so removal should be done first to prevent accidental removal for newly setting values. var val; if (old != null) { + if (old === attrs5 && !cachedAttrsIsStaticMap.has(attrs5)) { + console.warn( + "Don't reuse attrs object, use new object for every redraw, this will throw in next major" + ); + } for (var key in old) { - if ((val = old[key]) != null && (attrs2 == null || attrs2[key] == null)) { + if ((val = old[key]) != null && (attrs5 == null || attrs5[key] == null)) { removeAttr(vnode3, key, val, ns); } } } + if (attrs5 != null) { + for (var key in attrs5) { + setAttr(vnode3, key, old && old[key], attrs5[key], ns); + } + } } function isFormAttribute(vnode3, attr) { return ( attr === 'value' || attr === 'checked' || attr === 'selectedIndex' || - (attr === 'selected' && vnode3.dom === activeElement()) || - (vnode3.tag === 'option' && vnode3.dom.parentNode === $doc.activeElement) + (attr === 'selected' && + (vnode3.dom === activeElement(vnode3.dom) || + (vnode3.tag === 'option' && vnode3.dom.parentNode === activeElement(vnode3.dom)))) ); } function isLifecycleMethod(attr) { @@ -1233,7 +992,7 @@ ns === undefined && // If it's a custom element, just keep it. (vnode3.tag.indexOf('-') > -1 || - (vnode3.attrs != null && vnode3.attrs.is) || + vnode3.is || // If it's a normal element, let's try to avoid a few browser bugs. (key !== 'href' && key !== 'list' && @@ -1245,47 +1004,43 @@ ); } //style - var uppercaseRegex = /[A-Z]/g; - function toLowerCase(capital) { - return '-' + capital.toLowerCase(); - } - function normalizeKey(key) { - return key[0] === '-' && key[1] === '-' - ? key - : key === 'cssFloat' - ? 'float' - : key.replace(uppercaseRegex, toLowerCase); - } function updateStyle(element, old, style) { if (old === style) { // Styles are equivalent, do nothing. } else if (style == null) { // New style is missing, just clear it. - element.style.cssText = ''; + element.style = ''; } else if (typeof style !== 'object') { // New style is a string, let engine deal with patching. - element.style.cssText = style; + element.style = style; } else if (old == null || typeof old !== 'object') { // `old` is missing or a string, `style` is an object. - element.style.cssText = ''; + element.style = ''; // Add new style properties for (var key in style) { var value = style[key]; - if (value != null) element.style.setProperty(normalizeKey(key), String(value)); + if (value != null) { + if (key.includes('-')) element.style.setProperty(key, String(value)); + else element.style[key] = String(value); + } } } else { // Both old & new are (different) objects. + // Remove style properties that no longer exist + // Style properties may have two cases(dash-case and camelCase), + // so removal should be done first to prevent accidental removal for newly setting values. + for (var key in old) { + if (old[key] != null && style[key] == null) { + if (key.includes('-')) element.style.removeProperty(key); + else element.style[key] = ''; + } + } // Update style properties that have changed for (var key in style) { var value = style[key]; if (value != null && (value = String(value)) !== String(old[key])) { - element.style.setProperty(normalizeKey(key), value); - } - } - // Remove style properties that no longer exist - for (var key in old) { - if (old[key] != null && style[key] == null) { - element.style.removeProperty(normalizeKey(key)); + if (key.includes('-')) element.style.setProperty(key, value); + else element.style[key] = value; } } } @@ -1296,7 +1051,7 @@ // with a `handleEvent` method. // 3. The object does not inherit from `Object.prototype`, to avoid // any potential interference with that (e.g. setters). - // 4. The event name is remapped to the handler0 before calling it. + // 4. The event name is remapped to the handler before calling it. // 5. In function-based event handlers, `ev.target === this`. We replicate // that below. // 6. In function-based event handlers, `return false` prevents the default @@ -1307,11 +1062,19 @@ } EventDict.prototype = Object.create(null); EventDict.prototype.handleEvent = function (ev) { - var handler0 = this['on' + ev.type]; + var handler = this['on' + ev.type]; var result; - if (typeof handler0 === 'function') result = handler0.call(ev.currentTarget, ev); - else if (typeof handler0.handleEvent === 'function') handler0.handleEvent(ev); - if (this._ && ev.redraw !== false) (0, this._)(); + if (typeof handler === 'function') result = handler.call(ev.currentTarget, ev); + else if (typeof handler.handleEvent === 'function') handler.handleEvent(ev); + var self = this; + if (self._ != null) { + if (ev.redraw !== false) (0, self._)(); + if (result != null && typeof result.then === 'function') { + Promise.resolve(result).then(function () { + if (self._ != null && ev.redraw !== false) (0, self._)(); + }); + } + } if (result === false) { ev.preventDefault(); ev.stopPropagation(); @@ -1320,6 +1083,7 @@ //event function updateEvent(vnode3, key, value) { if (vnode3.events != null) { + vnode3.events._ = currentRedraw; if (vnode3.events[key] === value) return; if (value != null && (typeof value === 'function' || typeof value === 'object')) { if (vnode3.events[key] == null) @@ -1364,27 +1128,31 @@ // representation. We have to save not only the old DOM info, but also // the attributes used to create it, as we diff *that*, not against the // DOM directly (with a few exceptions in `setAttr`). And, of course, we - // need to save the children3 and text as they are conceptually not + // need to save the children and text as they are conceptually not // unlike special "attributes" internally. vnode3.attrs = old.attrs; vnode3.children = old.children; vnode3.text = old.text; return true; } + var currentDOM; return function (dom, vnodes, redraw) { - if (!dom) - throw new TypeError( - 'Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.' - ); - var hooks = []; - var active = activeElement(); - var namespace = dom.namespaceURI; - // First time rendering into a node clears it out - if (dom.vnodes == null) dom.textContent = ''; - vnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes]); + if (!dom) throw new TypeError('DOM element being rendered to does not exist.'); + if (currentDOM != null && dom.contains(currentDOM)) { + throw new TypeError('Node is currently being rendered to and thus is locked.'); + } var prevRedraw = currentRedraw; + var prevDOM = currentDOM; + var hooks = []; + var active = activeElement(dom); + var namespace = dom.namespaceURI; + currentDOM = dom; + currentRedraw = typeof redraw === 'function' ? redraw : undefined; + currentRender = {}; try { - currentRedraw = typeof redraw === 'function' ? redraw : undefined; + // First time rendering into a node clears it out + if (dom.vnodes == null) dom.textContent = ''; + vnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes]); updateNodes( dom, dom.vnodes, @@ -1393,32 +1161,31 @@ null, namespace === 'http://www.w3.org/1999/xhtml' ? undefined : namespace ); + dom.vnodes = vnodes; + // `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement + if (active != null && activeElement(dom) !== active && typeof active.focus === 'function') + active.focus(); + for (var i = 0; i < hooks.length; i++) hooks[i](); } finally { currentRedraw = prevRedraw; + currentDOM = prevDOM; } - dom.vnodes = vnodes; - // `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement - if (active != null && activeElement() !== active && typeof active.focus === 'function') - active.focus(); - for (var i = 0; i < hooks.length; i++) hooks[i](); }; }; - var render = _12(window); - var _15 = function (render0, schedule, console) { + var render = _14(); + var _21 = function (render2, schedule, console) { var subscriptions = []; - var rendering = false; var pending = false; + var offset = -1; function sync() { - if (rendering) throw new Error('Nested m.redraw.sync() call'); - rendering = true; - for (var i = 0; i < subscriptions.length; i += 2) { + for (offset = 0; offset < subscriptions.length; offset += 2) { try { - render0(subscriptions[i], Vnode(subscriptions[i + 1]), redraw); + render2(subscriptions[offset], Vnode(subscriptions[offset + 1]), redraw); } catch (e) { console.error(e); } } - rendering = false; + offset = -1; } function redraw() { if (!pending) { @@ -1432,21 +1199,26 @@ redraw.sync = sync; function mount(root, component) { if (component != null && component.view == null && typeof component !== 'function') { - throw new TypeError('m.mount(element, component) expects a component, not a vnode'); + throw new TypeError('m.mount expects a component, not a vnode.'); } var index = subscriptions.indexOf(root); if (index >= 0) { subscriptions.splice(index, 2); - render0(root, [], redraw); + if (index <= offset) offset -= 2; + render2(root, []); } if (component != null) { subscriptions.push(root, component); - render0(root, Vnode(component), redraw); + render2(root, Vnode(component), redraw); } } return { mount: mount, redraw: redraw }; }; - var mountRedraw0 = _15(render, requestAnimationFrame, console); + var mountRedraw = _21( + render, + typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : null, + typeof console !== 'undefined' ? console : null + ); var buildQueryString = function (object) { if (Object.prototype.toString.call(object) !== '[object Object]') return ''; var args = []; @@ -1470,18 +1242,12 @@ ); } }; - var assign = - Object.assign || - function (target, source) { - if (source) - Object.keys(source).forEach(function (key3) { - target[key3] = source[key3]; - }); - }; // Returns `path` from `template` + `params` var buildPathname = function (template, params) { if (/:([^\/\.-]+)(\.{3})?:/.test(template)) { - throw new SyntaxError('Template parameter names *must* be separated'); + throw new SyntaxError( + "Template parameter names must be separated by either a '/', '-', or '.'." + ); } if (params == null) return template; var queryIndex = template.indexOf('?'); @@ -1490,11 +1256,11 @@ var pathEnd = queryIndex < 0 ? queryEnd : queryIndex; var path = template.slice(0, pathEnd); var query = {}; - assign(query, params); - var resolved = path.replace(/:([^\/\.-]+)(\.{3})?/g, function (m2, key1, variadic) { + Object.assign(query, params); + var resolved = path.replace(/:([^\/\.-]+)(\.{3})?/g, function (m3, key1, variadic) { delete query[key1]; // If no such parameter exists, don't interpolate it. - if (params[key1] == null) return m2; + if (params[key1] == null) return m3; // Escape normal parameters, but not variadic ones. return variadic ? params[key1] : encodeURIComponent(String(params[key1])); }); @@ -1513,84 +1279,22 @@ if (newHashIndex >= 0) result0 += (hashIndex < 0 ? '' : '&') + resolved.slice(newHashIndex); return result0; }; - var _18 = function ($window, Promise, oncompletion) { - var callbackCount = 0; + var _25 = function ($window, oncompletion) { function PromiseProxy(executor) { return new Promise(executor); } - // In case the global Promise is0 some userland library's where they rely on - // `foo instanceof this.constructor`, `this.constructor.resolve(value0)`, or - // similar. Let's *not* break them. - PromiseProxy.prototype = Promise.prototype; - PromiseProxy.__proto__ = Promise; // eslint-disable-line no-proto - function makeRequest(factory) { - return function (url, args) { - if (typeof url !== 'string') { - args = url; - url = url.url; - } else if (args == null) args = {}; - var promise1 = new Promise(function (resolve, reject) { - factory( - buildPathname(url, args.params), - args, - function (data) { - if (typeof args.type === 'function') { - if (Array.isArray(data)) { - for (var i = 0; i < data.length; i++) { - data[i] = new args.type(data[i]); - } - } else data = new args.type(data); - } - resolve(data); - }, - reject - ); - }); - if (args.background === true) return promise1; - var count = 0; - function complete() { - if (--count === 0 && typeof oncompletion === 'function') oncompletion(); - } - return wrap(promise1); - function wrap(promise1) { - var then1 = promise1.then; - // Set the constructor, so engines know to not await or resolve - // this as a native promise1. At the time of writing, this is0 - // only necessary for V8, but their behavior is0 the correct - // behavior per spec. See this spec issue for more details: - // https://github.com/tc39/ecma262/issues/1577. Also, see the - // corresponding comment in `request0/tests/test-request0.js` for - // a bit more background on the issue at hand. - promise1.constructor = PromiseProxy; - promise1.then = function () { - count++; - var next0 = then1.apply(promise1, arguments); - next0.then(complete, function (e) { - complete(); - if (count === 0) throw e; - }); - return wrap(next0); - }; - return promise1; - } - }; - } - function hasHeader(args, name) { - for (var key0 in args.headers) { - if ({}.hasOwnProperty.call(args.headers, key0) && name.test(key0)) return true; - } - return false; - } - return { - request: makeRequest(function (url, args, resolve, reject) { + function makeRequest(url, args) { + return new Promise(function (resolve, reject) { + url = buildPathname(url, args.params); var method = args.method != null ? args.method.toUpperCase() : 'GET'; var body = args.body; var assumeJSON = (args.serialize == null || args.serialize === JSON.serialize) && - !(body instanceof $window.FormData); + !(body instanceof $window.FormData || body instanceof $window.URLSearchParams); var responseType = args.responseType || (typeof args.extract === 'function' ? '' : 'json'); var xhr = new $window.XMLHttpRequest(), - aborted = false; + aborted = false, + isTimeout = false; var original0 = xhr, replacedAbort; var abort = xhr.abort; @@ -1605,17 +1309,17 @@ typeof args.user === 'string' ? args.user : undefined, typeof args.password === 'string' ? args.password : undefined ); - if (assumeJSON && body != null && !hasHeader(args, /^content0-type1$/i)) { + if (assumeJSON && body != null && !hasHeader(args, 'content-type')) { xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8'); } - if (typeof args.deserialize !== 'function' && !hasHeader(args, /^accept$/i)) { + if (typeof args.deserialize !== 'function' && !hasHeader(args, 'accept')) { xhr.setRequestHeader('Accept', 'application/json, text/*'); } if (args.withCredentials) xhr.withCredentials = args.withCredentials; if (args.timeout) xhr.timeout = args.timeout; xhr.responseType = responseType; for (var key0 in args.headers) { - if ({}.hasOwnProperty.call(args.headers, key0)) { + if (hasOwn.call(args.headers, key0)) { xhr.setRequestHeader(key0, args.headers[key0]); } } @@ -1628,8 +1332,8 @@ (ev.target.status >= 200 && ev.target.status < 300) || ev.target.status === 304 || /^file:\/\//i.test(url); - // When the response type1 isn't "" or "text", - // `xhr.responseText` is0 the wrong thing to use. + // When the response type isn't "" or "text", + // `xhr.responseText` is the wrong thing to use. // Browsers do the right thing and throw here, and we // should honor that and do the right thing by // preferring `xhr.response` where possible/practical. @@ -1638,11 +1342,17 @@ if (responseType === 'json') { // For IE and Edge, which don't implement // `responseType: "json"`. - if (!ev.target.responseType && typeof args.extract !== 'function') - response = JSON.parse(ev.target.responseText); + if (!ev.target.responseType && typeof args.extract !== 'function') { + // Handle no-content which will not parse. + try { + response = JSON.parse(ev.target.responseText); + } catch (e) { + response = null; + } + } } else if (!responseType || responseType === 'text') { // Only use this default if it's text. If a parsed - // document is0 needed on old IE and friends (all + // document is needed on old IE and friends (all // unsupported), the user should use a custom // `config` instead. They're already using this at // their own risk. @@ -1654,23 +1364,49 @@ } else if (typeof args.deserialize === 'function') { response = args.deserialize(response); } - if (success) resolve(response); - else { - try { - message = ev.target.responseText; - } catch (e) { - message = response; + if (success) { + if (typeof args.type === 'function') { + if (Array.isArray(response)) { + for (var i = 0; i < response.length; i++) { + response[i] = new args.type(response[i]); + } + } else response = new args.type(response); } - var error = new Error(message); - error.code = ev.target.status; - error.response = response; - reject(error); + resolve(response); + } else { + var completeErrorResponse = function () { + try { + message = ev.target.responseText; + } catch (e) { + message = response; + } + var error = new Error(message); + error.code = ev.target.status; + error.response = response; + reject(error); + }; + if (xhr.status === 0) { + // Use setTimeout to push this code block onto the event queue + // This allows `xhr.ontimeout` to run in the case that there is a timeout + // Without this setTimeout, `xhr.ontimeout` doesn't have a chance to reject + // as `xhr.onreadystatechange` will run before it + setTimeout(function () { + if (isTimeout) return; + completeErrorResponse(); + }); + } else completeErrorResponse(); } } catch (e) { reject(e); } } }; + xhr.ontimeout = function (ev) { + isTimeout = true; + var error = new Error('Request timed out'); + error.code = ev.target.status; + reject(error); + }; if (typeof args.config === 'function') { xhr = args.config(xhr, args, url) || xhr; // Propagate the `abort` to any replacement XHR as well. @@ -1684,45 +1420,92 @@ } if (body == null) xhr.send(); else if (typeof args.serialize === 'function') xhr.send(args.serialize(body)); - else if (body instanceof $window.FormData) xhr.send(body); + else if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) + xhr.send(body); else xhr.send(JSON.stringify(body)); - }), - jsonp: makeRequest(function (url, args, resolve, reject) { - var callbackName = - args.callbackName || - '_mithril_' + Math.round(Math.random() * 1e16) + '_' + callbackCount++; - var script = $window.document.createElement('script'); - $window[callbackName] = function (data) { - delete $window[callbackName]; - script.parentNode.removeChild(script); - resolve(data); - }; - script.onerror = function () { - delete $window[callbackName]; - script.parentNode.removeChild(script); - reject(new Error('JSONP request failed')); - }; - script.src = - url + - (url.indexOf('?') < 0 ? '?' : '&') + - encodeURIComponent(args.callbackKey || 'callback') + - '=' + - encodeURIComponent(callbackName); - $window.document.documentElement.appendChild(script); - }), + }); + } + // In case the global Promise is some userland library's where they rely on + // `foo instanceof this.constructor`, `this.constructor.resolve(value)`, or + // similar. Let's *not* break them. + PromiseProxy.prototype = Promise.prototype; + PromiseProxy.__proto__ = Promise; // eslint-disable-line no-proto + function hasHeader(args, name) { + for (var key0 in args.headers) { + if (hasOwn.call(args.headers, key0) && key0.toLowerCase() === name) return true; + } + return false; + } + return { + request: function (url, args) { + if (typeof url !== 'string') { + args = url; + url = url.url; + } else if (args == null) args = {}; + var promise = makeRequest(url, args); + if (args.background === true) return promise; + var count = 0; + function complete() { + if (--count === 0 && typeof oncompletion === 'function') oncompletion(); + } + return wrap(promise); + function wrap(promise) { + var then = promise.then; + // Set the constructor, so engines know to not await or resolve + // this as a native promise. At the time of writing, this is + // only necessary for V8, but their behavior is the correct + // behavior per spec. See this spec issue for more details: + // https://github.com/tc39/ecma262/issues/1577. Also, see the + // corresponding comment in `request/tests/test-request.js` for + // a bit more background on the issue at hand. + promise.constructor = PromiseProxy; + promise.then = function () { + count++; + var next = then.apply(promise, arguments); + next.then(complete, function (e) { + complete(); + if (count === 0) throw e; + }); + return wrap(next); + }; + return promise; + } + }, }; }; - var request = _18(window, PromisePolyfill, mountRedraw0.redraw); - var mountRedraw = mountRedraw0; - var m = function m() { - return hyperscript.apply(this, arguments); + var request = _25(typeof window !== 'undefined' ? window : null, mountRedraw.redraw); + /* +Percent encodings encode UTF-8 bytes, so this regexp needs to match that. +Here's how UTF-8 encodes stuff: +- `00-7F`: 1-byte, for U+0000-U+007F +- `C2-DF 80-BF`: 2-byte, for U+0080-U+07FF +- `E0-EF 80-BF 80-BF`: 3-byte, encodes U+0800-U+FFFF +- `F0-F4 80-BF 80-BF 80-BF`: 4-byte, encodes U+10000-U+10FFFF +In this, there's a number of invalid byte sequences: +- `80-BF`: Continuation byte, invalid as start +- `C0-C1 80-BF`: Overlong encoding for U+0000-U+007F +- `E0 80-9F 80-BF`: Overlong encoding for U+0080-U+07FF +- `ED A0-BF 80-BF`: Encoding for UTF-16 surrogate U+D800-U+DFFF +- `F0 80-8F 80-BF 80-BF`: Overlong encoding for U+0800-U+FFFF +- `F4 90-BF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode. +- `F5-FF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode. +So in reality, only the following sequences can encode are valid characters: +- 00-7F +- C2-DF 80-BF +- E0 A0-BF 80-BF +- E1-EC 80-BF 80-BF +- ED 80-9F 80-BF +- EE-EF 80-BF 80-BF +- F0 90-BF 80-BF 80-BF +- F1-F3 80-BF 80-BF 80-BF +- F4 80-8F 80-BF 80-BF +The regexp just tries to match this as compactly as possible. +*/ + var validUtf8Encodings = + /%(?:[0-7]|(?!c[01]|e0%[89]|ed%[ab]|f0%8|f4%[9ab])(?:c|d|(?:e|f[0-4]%[89ab])[\da-f]%[89ab])[\da-f]%[89ab])[\da-f]/gi; + var decodeURIComponentSafe = function (str) { + return String(str).replace(validUtf8Encodings, decodeURIComponent); }; - m.m = hyperscript; - m.trust = hyperscript.trust; - m.fragment = hyperscript.fragment; - m.mount = mountRedraw.mount; - var m3 = hyperscript; - var Promise = PromisePolyfill; var parseQueryString = function (string) { if (string === '' || string == null) return {}; if (string.charAt(0) === '?') string = string.slice(1); @@ -1731,23 +1514,23 @@ data0 = {}; for (var i = 0; i < entries.length; i++) { var entry = entries[i].split('='); - var key5 = decodeURIComponent(entry[0]); - var value2 = entry.length === 2 ? decodeURIComponent(entry[1]) : ''; + var key4 = decodeURIComponentSafe(entry[0]); + var value2 = entry.length === 2 ? decodeURIComponentSafe(entry[1]) : ''; if (value2 === 'true') value2 = true; else if (value2 === 'false') value2 = false; - var levels = key5.split(/\]\[?|\[/); + var levels = key4.split(/\]\[?|\[/); var cursor = data0; - if (key5.indexOf('[') > -1) levels.pop(); + if (key4.indexOf('[') > -1) levels.pop(); for (var j0 = 0; j0 < levels.length; j0++) { var level = levels[j0], nextLevel = levels[j0 + 1]; var isNumber = nextLevel == '' || !isNaN(parseInt(nextLevel, 10)); if (level === '') { - var key5 = levels.slice(0, j0).join(); - if (counters[key5] == null) { - counters[key5] = Array.isArray(cursor) ? cursor.length : 0; + var key4 = levels.slice(0, j0).join(); + if (counters[key4] == null) { + counters[key4] = Array.isArray(cursor) ? cursor.length : 0; } - level = counters[key5]++; + level = counters[key4]++; } // Disallow direct prototype pollution else if (level === '__proto__') break; @@ -1764,7 +1547,7 @@ } return data0; }; - // Returns `{path1, params}` from `url` + // Returns `{path, params}` from `url` var parsePathname = function (url) { var queryIndex0 = url.indexOf('?'); var hashIndex0 = url.indexOf('#'); @@ -1774,17 +1557,16 @@ if (!path1) path1 = '/'; else { if (path1[0] !== '/') path1 = '/' + path1; - if (path1.length > 1 && path1[path1.length - 1] === '/') path1 = path1.slice(0, -1); } return { path: path1, params: queryIndex0 < 0 ? {} : parseQueryString(url.slice(queryIndex0 + 1, queryEnd0)), }; }; - // Compiles a template into a function that takes a resolved0 path2 (without query0 + // Compiles a template into a function that takes a resolved path (without query // strings) and returns an object containing the template parameters with their - // parsed values. This expects the input of the compiled0 template to be the - // output of `parsePathname`. Note that it does *not* remove query0 parameters + // parsed values. This expects the input of the compiled template to be the + // output of `parsePathname`. Note that it does *not* remove query parameters // specified in the template. var compileTemplate = function (template) { var templateData = parsePathname(template); @@ -1794,19 +1576,19 @@ '^' + templateData.path.replace( // I escape literal text so people can use things like `:file.:ext` or - // `:lang-:locale` in routes. This is2 all merged into one pass so I + // `:lang-:locale` in routes. This is all merged into one pass so I // don't also accidentally escape `-` and make it harder to detect it to // ban it from template parameters. /:([^\/.-]+)(\.{3}|\.(?!\.)|-)?|[\\^$*+.()|\[\]{}]/g, - function (m4, key6, extra) { - if (key6 == null) return '\\' + m4; - keys.push({ k: key6, r: extra === '...' }); + function (m4, key5, extra) { + if (key5 == null) return '\\' + m4; + keys.push({ k: key5, r: extra === '...' }); if (extra === '...') return '(.*)'; if (extra === '.') return '([^/]+)\\.'; return '([^/]+)' + (extra || ''); } ) + - '$' + '\\/?$' ); return function (data1) { // First, check the params. Usually, there isn't any, and it's just @@ -1824,12 +1606,178 @@ return true; }; }; - var sentinel0 = {}; - var _25 = function ($window, mountRedraw00) { - var fireAsync; - function setPath(path0, data, options) { + // Note: this is mildly perf-sensitive. + // + // It does *not* use `delete` - dynamic `delete`s usually cause objects to bail + // out into dictionary mode and just generally cause a bunch of optimization + // issues within engines. + // + // Ideally, I would've preferred to do this, if it weren't for the optimization + // issues: + // + // ```js + // const hasOwn = hasOwn + // const magic = [ + // "key", "oninit", "oncreate", "onbeforeupdate", "onupdate", + // "onbeforeremove", "onremove", + // ] + // var censor = (attrs, extras) => { + // const result = Object.assign(Object.create(null), attrs) + // for (const key of magic) delete result[key] + // if (extras != null) for (const key of extras) delete result[key] + // return result + // } + // ``` + var magic = /^(?:key|oninit|oncreate|onbeforeupdate|onupdate|onbeforeremove|onremove)$/; + var censor = function (attrs7, extras) { + var result2 = {}; + if (extras != null) { + for (var key6 in attrs7) { + if (hasOwn.call(attrs7, key6) && !magic.test(key6) && extras.indexOf(key6) < 0) { + result2[key6] = attrs7[key6]; + } + } + } else { + for (var key6 in attrs7) { + if (hasOwn.call(attrs7, key6) && !magic.test(key6)) { + result2[key6] = attrs7[key6]; + } + } + } + return result2; + }; + var _31 = function ($window, mountRedraw0) { + var p = Promise.resolve(); + var scheduled = false; + var ready = false; + var hasBeenResolved = false; + var dom0, compiled, fallbackRoute; + var currentResolver, component, attrs6, currentPath, lastUpdate; + var RouterRoot = { + onremove: function () { + ready = hasBeenResolved = false; + $window.removeEventListener('popstate', fireAsync, false); + }, + view: function () { + // The route has already been resolved. + // Therefore, the following early return is not needed. + // if (!hasBeenResolved) return + var vnode6 = Vnode(component, attrs6.key, attrs6); + if (currentResolver) return currentResolver.render(vnode6); + // Wrap in a fragment to preserve existing key semantics + return [vnode6]; + }, + }; + var SKIP = (route.SKIP = {}); + function resolveRoute() { + scheduled = false; + // Consider the pathname holistically. The prefix might even be invalid, + // but that's not our problem. + var prefix = $window.location.hash; + if (route.prefix[0] !== '#') { + prefix = $window.location.search + prefix; + if (route.prefix[0] !== '?') { + prefix = $window.location.pathname + prefix; + if (prefix[0] !== '/') prefix = '/' + prefix; + } + } + var path0 = decodeURIComponentSafe(prefix).slice(route.prefix.length); + var data = parsePathname(path0); + Object.assign(data.params, $window.history.state); + function reject(e) { + console.error(e); + route.set(fallbackRoute, null, { replace: true }); + } + loop(0); + function loop(i) { + for (; i < compiled.length; i++) { + if (compiled[i].check(data)) { + var payload = compiled[i].component; + var matchedRoute = compiled[i].route; + var localComp = payload; + var update = (lastUpdate = function (comp) { + if (update !== lastUpdate) return; + if (comp === SKIP) return loop(i + 1); + component = + comp != null && (typeof comp.view === 'function' || typeof comp === 'function') + ? comp + : 'div'; + ((attrs6 = data.params), (currentPath = path0), (lastUpdate = null)); + currentResolver = payload.render ? payload : null; + if (hasBeenResolved) mountRedraw0.redraw(); + else { + hasBeenResolved = true; + mountRedraw0.mount(dom0, RouterRoot); + } + }); + // There's no understating how much I *wish* I could + // use `async`/`await` here... + if (payload.view || typeof payload === 'function') { + payload = {}; + update(localComp); + } else if (payload.onmatch) { + p.then(function () { + return payload.onmatch(data.params, path0, matchedRoute); + }).then(update, path0 === fallbackRoute ? null : reject); + } else update(/* "div" */); + return; + } + } + if (path0 === fallbackRoute) { + throw new Error('Could not resolve default route ' + fallbackRoute + '.'); + } + route.set(fallbackRoute, null, { replace: true }); + } + } + function fireAsync() { + if (!scheduled) { + scheduled = true; + // TODO: just do `mountRedraw.redraw()` here and elide the timer + // dependency. Note that this will muck with tests a *lot*, so it's + // not as easy of a change as it sounds. + setTimeout(resolveRoute); + } + } + function route(root, defaultRoute, routes) { + if (!root) throw new TypeError('DOM element being rendered to does not exist.'); + compiled = Object.keys(routes).map(function (route) { + if (route[0] !== '/') throw new SyntaxError("Routes must start with a '/'."); + if (/:([^\/\.-]+)(\.{3})?:/.test(route)) { + throw new SyntaxError( + "Route parameter names must be separated with either '/', '.', or '-'." + ); + } + return { + route: route, + component: routes[route], + check: compileTemplate(route), + }; + }); + fallbackRoute = defaultRoute; + if (defaultRoute != null) { + var defaultData = parsePathname(defaultRoute); + if ( + !compiled.some(function (i) { + return i.check(defaultData); + }) + ) { + throw new ReferenceError("Default route doesn't match any known routes."); + } + } + dom0 = root; + $window.addEventListener('popstate', fireAsync, false); + ready = true; + // The RouterRoot component is mounted when the route is first resolved. + resolveRoute(); + } + route.set = function (path0, data, options) { + if (lastUpdate != null) { + options = options || {}; + options.replace = true; + } + lastUpdate = null; path0 = buildPathname(path0, data); - if (fireAsync != null) { + if (ready) { fireAsync(); var state = options ? options.state : null; var title = options ? options.title : null; @@ -1839,195 +1787,40 @@ } else { $window.location.href = route.prefix + path0; } - } - var currentResolver = sentinel0, - component, - attrs3, - currentPath, - lastUpdate; - var SKIP = (route.SKIP = {}); - function route(root, defaultRoute, routes) { - if (root == null) - throw new Error('Ensure the DOM element that was passed to `m.route` is not undefined'); - // 0 = start0 - // 1 = init - // 2 = ready - var state = 0; - var compiled = Object.keys(routes).map(function (route) { - if (route[0] !== '/') throw new SyntaxError('Routes must start with a `/`'); - if (/:([^\/\.-]+)(\.{3})?:/.test(route)) { - throw new SyntaxError( - 'Route parameter names must be separated with either `/`, `.`, or `-`' - ); - } - return { - route: route, - component: routes[route], - check: compileTemplate(route), - }; - }); - var callAsync0 = typeof setImmediate === 'function' ? setImmediate : setTimeout; - var p = Promise.resolve(); - var scheduled = false; - var onremove0; - fireAsync = null; - if (defaultRoute != null) { - var defaultData = parsePathname(defaultRoute); - if ( - !compiled.some(function (i) { - return i.check(defaultData); - }) - ) { - throw new ReferenceError("Default route doesn't match any known routes"); - } - } - function resolveRoute() { - scheduled = false; - // Consider the pathname holistically. The prefix might even be invalid, - // but that's not our problem. - var prefix = $window.location.hash; - if (route.prefix[0] !== '#') { - prefix = $window.location.search + prefix; - if (route.prefix[0] !== '?') { - prefix = $window.location.pathname + prefix; - if (prefix[0] !== '/') prefix = '/' + prefix; - } - } - // This seemingly useless `.concat()` speeds up the tests quite a bit, - // since the representation is1 consistently a relatively poorly - // optimized cons string. - var path0 = prefix - .concat() - .replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent) - .slice(route.prefix.length); - var data = parsePathname(path0); - assign(data.params, $window.history.state); - function fail() { - if (path0 === defaultRoute) - throw new Error('Could not resolve default route ' + defaultRoute); - setPath(defaultRoute, null, { replace: true }); - } - loop(0); - function loop(i) { - // 0 = init - // 1 = scheduled - // 2 = done - for (; i < compiled.length; i++) { - if (compiled[i].check(data)) { - var payload = compiled[i].component; - var matchedRoute = compiled[i].route; - var localComp = payload; - var update = (lastUpdate = function (comp) { - if (update !== lastUpdate) return; - if (comp === SKIP) return loop(i + 1); - component = - comp != null && (typeof comp.view === 'function' || typeof comp === 'function') - ? comp - : 'div'; - (attrs3 = data.params), (currentPath = path0), (lastUpdate = null); - currentResolver = payload.render ? payload : null; - if (state === 2) mountRedraw00.redraw(); - else { - state = 2; - mountRedraw00.redraw.sync(); - } - }); - // There's no understating how much I *wish* I could - // use `async`/`await` here... - if (payload.view || typeof payload === 'function') { - payload = {}; - update(localComp); - } else if (payload.onmatch) { - p.then(function () { - return payload.onmatch(data.params, path0, matchedRoute); - }).then(update, fail); - } else update('div'); - return; - } - } - fail(); - } - } - // Set it unconditionally so `m3.route.set` and `m3.route.Link` both work, - // even if neither `pushState` nor `hashchange` are supported. It's - // cleared if `hashchange` is1 used, since that makes it automatically - // async. - fireAsync = function () { - if (!scheduled) { - scheduled = true; - callAsync0(resolveRoute); - } - }; - if (typeof $window.history.pushState === 'function') { - onremove0 = function () { - $window.removeEventListener('popstate', fireAsync, false); - }; - $window.addEventListener('popstate', fireAsync, false); - } else if (route.prefix[0] === '#') { - fireAsync = null; - onremove0 = function () { - $window.removeEventListener('hashchange', resolveRoute, false); - }; - $window.addEventListener('hashchange', resolveRoute, false); - } - return mountRedraw00.mount(root, { - onbeforeupdate: function () { - state = state ? 2 : 1; - return !(!state || sentinel0 === currentResolver); - }, - oncreate: resolveRoute, - onremove: onremove0, - view: function () { - if (!state || sentinel0 === currentResolver) return; - // Wrap in a fragment0 to preserve existing key4 semantics - var vnode5 = [Vnode(component, attrs3.key, attrs3)]; - if (currentResolver) vnode5 = currentResolver.render(vnode5[0]); - return vnode5; - }, - }); - } - route.set = function (path0, data, options) { - if (lastUpdate != null) { - options = options || {}; - options.replace = true; - } - lastUpdate = null; - setPath(path0, data, options); }; route.get = function () { return currentPath; }; route.prefix = '#!'; route.Link = { - view: function (vnode5) { - var options = vnode5.attrs.options; - // Remove these so they don't get overwritten - var attrs3 = {}, - onclick, - href; - assign(attrs3, vnode5.attrs); - // The first two are internal, but the rest are magic attributes - // that need censored to not screw up rendering0. - attrs3.selector = attrs3.options = attrs3.key = attrs3.oninit = attrs3.oncreate = attrs3.onbeforeupdate = attrs3.onupdate = attrs3.onbeforeremove = attrs3.onremove = null; - // Do this now so we can get the most current `href` and `disabled`. - // Those attributes may also be specified in the selector, and we - // should honor that. - var child0 = m3(vnode5.attrs.selector || 'a', attrs3, vnode5.children); + view: function (vnode6) { + // Omit the used parameters from the rendered element - they are + // internal. Also, censor the various lifecycle methods. + // + // We don't strip the other parameters because for convenience we + // let them be specified in the selector as well. + var child0 = hyperscript( + vnode6.attrs.selector || 'a', + censor(vnode6.attrs, ['options', 'params', 'selector', 'onclick']), + vnode6.children + ); + var options, onclick, href; // Let's provide a *right* way to disable a route link, rather than // letting people screw up accessibility on accident. // - // The attribute is1 coerced so users don't get surprised over + // The attribute is coerced so users don't get surprised over // `disabled: 0` resulting in a button that's somehow routable // despite being visibly disabled. if ((child0.attrs.disabled = Boolean(child0.attrs.disabled))) { child0.attrs.href = null; child0.attrs['aria-disabled'] = 'true'; - // If you *really* do want to do this on a disabled link, use + // If you *really* do want add `onclick` on a disabled link, use // an `oncreate` hook to add it. - child0.attrs.onclick = null; } else { - onclick = child0.attrs.onclick; - href = child0.attrs.href; + options = vnode6.attrs.options; + onclick = vnode6.attrs.onclick; + // Easier to build it now to keep it isomorphic. + href = buildPathname(child0.attrs.href, vnode6.attrs.params); child0.attrs.href = route.prefix + href; child0.attrs.onclick = function (e) { var result1; @@ -2041,7 +1834,7 @@ // Adapted from React Router's implementation: // https://github.com/ReactTraining/react-router/blob/520a0acd48ae1b066eb0b07d6d4d1790a1d02482/packages/react-router-dom/modules/Link.js // - // Try to be flexible and intuitive in how we handle1 links. + // Try to be flexible and intuitive in how we handle links. // Fun fact: links aren't as obvious to get right as you // would expect. There's a lot more valid ways to click a // link than this, and one might want to not simply click a @@ -2053,7 +1846,7 @@ !e.defaultPrevented && // Ignore everything but left clicks (e.button === 0 || e.which === 0 || e.which === 1) && - // Let the browser handle1 `target=_blank`, etc. + // Let the browser handle `target=_blank`, etc. (!e.currentTarget.target || e.currentTarget.target === '_self') && // No modifier keys !e.ctrlKey && @@ -2070,22 +1863,31 @@ return child0; }, }; - route.param = function (key4) { - return attrs3 && key4 != null ? attrs3[key4] : attrs3; + route.param = function (key3) { + return attrs6 && key3 != null ? attrs6[key3] : attrs6; }; return route; }; - m.route = _25(window, mountRedraw); + var router = _31(typeof window !== 'undefined' ? window : null, mountRedraw); + var m = function m() { + return hyperscript.apply(this, arguments); + }; + m.m = hyperscript; + m.trust = hyperscript.trust; + m.fragment = hyperscript.fragment; + m.Fragment = '['; + m.mount = mountRedraw.mount; + m.route = router; m.render = render; m.redraw = mountRedraw.redraw; m.request = request.request; - m.jsonp = request.jsonp; m.parseQueryString = parseQueryString; m.buildQueryString = buildQueryString; m.parsePathname = parsePathname; m.buildPathname = buildPathname; m.vnode = Vnode; - m.PromisePolyfill = PromisePolyfill; + m.censor = censor; + m.domFor = domFor; if (typeof module !== 'undefined') module['exports'] = m; else window.m = m; })(); diff --git a/webui-src/app/network/network_state.js b/webui-src/app/network/network_state.js index be29209..bea212f 100644 --- a/webui-src/app/network/network_state.js +++ b/webui-src/app/network/network_state.js @@ -149,7 +149,7 @@ function sendDirectChatMessage() { '/rsChats/sendChat', { id: { type: 1, peer_id: State.currentChatPeerId }, - msg: msg, + msg, }, (data, success) => { if (success) { diff --git a/webui-src/app/people/people_chat_tab.js b/webui-src/app/people/people_chat_tab.js index 5221fa7..aba1ae9 100644 --- a/webui-src/app/people/people_chat_tab.js +++ b/webui-src/app/people/people_chat_tab.js @@ -100,14 +100,6 @@ const ChatTab = () => { const canTalk = State.distantChatStatus && State.distantChatStatus.status === 2; - // Filter history by search query for history modal - const query = (State.historySearchQuery || '').toLowerCase(); - const filteredHistory = (State.fullHistoryMessages || []).filter((msg) => { - if (!query) return true; - const text = (msg.msg || msg.message || '').toLowerCase(); - return text.includes(query); - }); - return m('.network-chat-view', [ m('.chat-identity-select-container', { style: 'padding: 0.5rem 1rem; background-color: #ffffff; border-bottom: 1px solid #cbd5e1; display: flex; align-items: center; justify-content: space-between; font-size: 0.85rem;', diff --git a/webui-src/app/people/people_sidebar.js b/webui-src/app/people/people_sidebar.js index 4b0109e..e9de2db 100644 --- a/webui-src/app/people/people_sidebar.js +++ b/webui-src/app/people/people_sidebar.js @@ -33,7 +33,7 @@ const PeopleSidebar = () => { }, view: () => { // 1. Determine list based on mainTab ('people' vs 'chats') - let displayItems = []; + let displayItems; // 0. Compute active chats count (conversations with real message history) const allUserGroupIds = new Set((rs.userList.users || []).map((u) => u.mGroupId)); @@ -47,7 +47,7 @@ const PeopleSidebar = () => { }); if (State.mainTab === 'people') { - let baseList = []; + let baseList; if (State.activeFilter === 'own') { baseList = peopleUtil.sortIds(State.ownGxsIds) || []; } else if (State.activeFilter === 'contacts') { @@ -57,13 +57,13 @@ const PeopleSidebar = () => { } displayItems = baseList.filter((item) => { - let name = State.activeFilter === 'own' ? (rs.userList.username(item) || 'Unknown') : (item.mGroupName || 'Unknown'); + const name = State.activeFilter === 'own' ? (rs.userList.username(item) || 'Unknown') : (item.mGroupName || 'Unknown'); return name.toLowerCase().includes(State.searchString.toLowerCase()); }); displayItems.sort((a, b) => { - let nameA = State.activeFilter === 'own' ? (rs.userList.username(a) || '') : (a.mGroupName || ''); - let nameB = State.activeFilter === 'own' ? (rs.userList.username(b) || '') : (b.mGroupName || ''); + const nameA = State.activeFilter === 'own' ? (rs.userList.username(a) || '') : (a.mGroupName || ''); + const nameB = State.activeFilter === 'own' ? (rs.userList.username(b) || '') : (b.mGroupName || ''); return nameA.localeCompare(nameB); }); } else { diff --git a/webui-src/app/people/people_state.js b/webui-src/app/people/people_state.js index b4fbc3a..d1ad93e 100644 --- a/webui-src/app/people/people_state.js +++ b/webui-src/app/people/people_state.js @@ -359,7 +359,7 @@ function loadChatMessages() { rs.rsJsonApiRequest( '/rsHistory/getMessages', { - chatPeerId: chatPeerId, + chatPeerId, loadCount: 50, }, (data, success) => { @@ -509,7 +509,7 @@ function preloadAllChatHistory() { if (!existing || lastTime > existing.lastTime) { State.chatHistoryMap[gxsId] = { lastMsg: last.message || last.msg || '', - lastTime: lastTime, + lastTime, }; m.redraw(); } @@ -573,7 +573,7 @@ function loadAllHistoryForSelectedPeer(callback) { rs.rsJsonApiRequest( '/rsHistory/getMessages', { - chatPeerId: chatPeerId, + chatPeerId, loadCount: 0, // 0 = load all messages in C++ }, (msgData, success) => { @@ -588,7 +588,7 @@ function loadAllHistoryForSelectedPeer(callback) { const key = `${mItem.sendTime || mItem.recvTime}_${text}`; if (!map.has(key)) map.set(key, mItem); }); - let uniqueMsgs = Array.from(map.values()); + const uniqueMsgs = Array.from(map.values()); uniqueMsgs.sort((a, b) => (a.sendTime || a.recvTime) - (b.sendTime || b.recvTime)); State.fullHistoryMessages = uniqueMsgs; State.isHistoryLoading = false; diff --git a/webui-src/app/people/people_util.js b/webui-src/app/people/people_util.js index 95bd48c..46036cc 100644 --- a/webui-src/app/people/people_util.js +++ b/webui-src/app/people/people_util.js @@ -70,7 +70,7 @@ const UserAvatar = () => ({ width: sizeStr, height: sizeStr, borderRadius: isSquare ? '0' : '50%', - backgroundColor: backgroundColor, + backgroundColor, } }, m('p', { diff --git a/webui-src/app/scss/abstracts/_mixins.scss b/webui-src/app/scss/abstracts/_mixins.scss index c17bede..4665aa0 100644 --- a/webui-src/app/scss/abstracts/_mixins.scss +++ b/webui-src/app/scss/abstracts/_mixins.scss @@ -15,11 +15,11 @@ border: 0; border-radius: 5px; cursor: pointer; - box-shadow: inset -3px -3px 0 darken($color: $bg-color, $amount: 20); + box-shadow: inset -3px -3px 0 color.adjust($bg-color, $lightness: -20%); &:active { outline: none; - box-shadow: inset 3px 3px 0 darken($color: $bg-color, $amount: 20); + box-shadow: inset 3px 3px 0 color.adjust($bg-color, $lightness: -20%); } } diff --git a/webui-src/app/scss/pages/_chat.scss b/webui-src/app/scss/pages/_chat.scss index 5226f76..6ac65bd 100644 --- a/webui-src/app/scss/pages/_chat.scss +++ b/webui-src/app/scss/pages/_chat.scss @@ -372,6 +372,7 @@ textarea.chatMsg { padding: 1.25rem; border-bottom: 1px solid #e2e8f0; background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); + position: relative; // anchors the absolutely-positioned .chat-create-lobby-btn .profile-header { display: flex; @@ -896,6 +897,7 @@ textarea.chatMsg { display: flex; flex-direction: column; flex-shrink: 0; + position: relative; // anchors the hovered-participant .user-tooltip (top offset is measured against this) } .chat-hub-rightbar .rightbar-title { @@ -926,6 +928,7 @@ textarea.chatMsg { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + position: relative; } .chat-hub-rightbar .user:hover { @@ -1125,4 +1128,490 @@ textarea.chatMsg { } - + + + +// Chat-hub extras: user tooltip, right-bar context menu, attach-file modal, +// emoji picker, create-lobby button (chat.js). Recovered from compiled styles.css. + +.chat-create-lobby-btn { + position: absolute; + bottom: 0.5rem; + right: 1.25rem; + background-color: #0084ff; + color: #ffffff; + border: none; + border-radius: 0.375rem; + padding: 0.35rem 0.75rem; + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + box-shadow: 0 4px 6px -1px rgba(0, 132, 255, 0.2), 0 2px 4px -1px rgba(0, 132, 255, 0.1); + transition: background-color 0.2s, transform 0.2s; + display: flex; + align-items: center; + gap: 0.25rem; +} + +.chat-create-lobby-btn:hover { + background-color: #0073e6; + transform: translateY(-1px); +} + +.chat-create-lobby-btn:active { + transform: translateY(0); +} + +.chat-hub-rightbar .user .user-name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; +} + +.user-tooltip { + position: absolute; + width: 260px; + background-color: #ffffe1; + border: 1px solid #7f7f7f; + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25); + padding: 0.5rem; + border-radius: 0.25rem; + z-index: 10000; + white-space: normal; + display: flex; + gap: 0.5rem; + align-items: flex-start; +} + +.chat-hub-rightbar .user-tooltip { + left: -275px; + transform: translateY(-50%); + z-index: 1000; +} + +.user-tooltip .tooltip-avatar { + flex-shrink: 0; +} + +.user-tooltip .tooltip-details { + display: flex; + flex-direction: column; + gap: 0.25rem; + font-size: 0.8rem; + color: #000000; + text-align: left; +} + +.user-tooltip .tooltip-row { + line-height: 1.2; +} + +.user-tooltip .tooltip-label { + font-weight: bold; +} + +.user-tooltip .tooltip-value { + font-weight: normal; + word-break: break-all; +} + +.user-tooltip .tooltip-value.tooltip-id { + font-family: monospace; +} + +.chat-hub-rightbar .rightbar-context-menu { + position: absolute; + right: 1rem; + width: 210px; + background-color: #ffffff; + border: 1px solid #e2e8f0; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); + border-radius: 0.375rem; + z-index: 1010; + padding: 0.25rem 0; + display: flex; + flex-direction: column; +} + +.chat-hub-rightbar .rightbar-context-menu .menu-item { + padding: 0.5rem 1rem; + font-size: 0.85rem; + color: #334155; + cursor: pointer; + display: flex; + align-items: center; + transition: background-color 0.2s; +} + +.chat-hub-rightbar .rightbar-context-menu .menu-item:hover { + background-color: #f1f5f9; + color: #0f172a; +} + +.chat-emoji { + font-size: 1.45em; + line-height: 1; + vertical-align: -0.15em; + display: inline-block; +} + +.chat-hub-attach-btn, +.chat-hub-action-btn { + background-color: transparent !important; + border: none !important; + font-size: 1.15rem !important; + color: #64748b !important; + cursor: pointer !important; + padding: 0.4rem 0.5rem !important; + border-radius: 0.375rem !important; + flex-shrink: 0 !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + transition: all 0.2s !important; + box-shadow: none !important; + margin: 0 !important; + line-height: 1 !important; + height: 36px !important; + width: 36px !important; +} + +.chat-hub-attach-btn:hover, +.chat-hub-action-btn:hover { + background-color: #f1f5f9 !important; + color: #3b82f6 !important; + transform: none !important; +} + +.attach-modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(15, 23, 42, 0.4); + backdrop-filter: blur(4px); + display: flex; + align-items: center; + justify-content: center; + z-index: 2000; +} + +.attach-modal { + background-color: #ffffff; + border-radius: 0.5rem; + width: 450px; + max-width: 90%; + padding: 1.5rem; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + gap: 1rem; +} + +.attach-modal .attach-modal-header { + display: flex; + align-items: center; + gap: 0.6rem; + margin-bottom: 0.25rem; +} + +.attach-modal .attach-modal-icon { + font-size: 1.2rem; + color: #3b82f6; +} + +.attach-modal h4 { + margin: 0; + font-size: 1.2rem; + color: #0f172a; +} + +.attach-modal p { + margin: 0; + font-size: 0.9rem; + color: #475569; +} + +.attach-modal .attach-path-row { + display: flex; + gap: 0.5rem; + align-items: center; +} + +.attach-modal .attach-path-row input[type="text"] { + flex: 1; + padding: 0.75rem; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + font-size: 0.9rem; + outline: none; + transition: border-color 0.2s; + min-width: 0; +} + +.attach-modal .attach-path-row input[type="text"]:focus { + border-color: #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15); +} + +.attach-browse-btn { + flex-shrink: 0; + display: flex; + align-items: center; + gap: 0.35rem; + padding: 0.625rem 0.9rem; + font-size: 0.875rem; + background-color: #f1f5f9; + color: #334155; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + cursor: pointer; + box-shadow: none; + transition: background-color 0.2s, border-color 0.2s; + white-space: nowrap; +} + +.attach-browse-btn:hover { + background-color: #e2e8f0; + border-color: #94a3b8; +} + +.attach-path-hint { + display: flex; + align-items: flex-start; + gap: 0.5rem; + padding: 0.6rem 0.75rem; + background-color: #fffbeb; + border: 1px solid #fcd34d; + border-left: 3px solid #f59e0b; + border-radius: 0.375rem; + font-size: 0.825rem; + color: #92400e; + line-height: 1.45; +} + +.attach-path-hint i { + color: #f59e0b; + margin-top: 0.1rem; + flex-shrink: 0; +} + +.attach-path-hint code { + font-family: monospace; + background-color: rgba(245, 158, 11, 0.15); + padding: 0.05rem 0.25rem; + border-radius: 0.2rem; +} + +.attach-modal .hashing-spinner { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.9rem; + color: #3b82f6; +} + +.attach-modal .error-text { + color: #ef4444; + font-size: 0.85rem; + margin: 0; +} + +.attach-modal .modal-buttons { + display: flex; + justify-content: flex-end; + gap: 0.75rem; + margin-top: 0.5rem; +} + +.attach-modal .modal-buttons button { + padding: 0.5rem 1rem; + font-size: 0.9rem; + border-radius: 0.25rem; + border: none; + cursor: pointer; + transition: opacity 0.2s; +} + +.attach-modal .modal-buttons button:hover { + opacity: 0.9; +} + +.chat-hub-emoji-btn { + background-color: transparent; + border: none; + font-size: 1.3rem; + cursor: pointer; + padding: 0.35rem 0.4rem; + margin-right: 0.25rem; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0.375rem; + line-height: 1; + transition: background-color 0.15s, transform 0.15s; + box-shadow: none; +} + +.chat-hub-emoji-btn:hover { + background-color: #f1f5f9; + transform: scale(1.1); +} + +.emoji-picker-wrapper { + position: relative; + flex-shrink: 0; + display: flex; + align-items: center; +} + +.emoji-picker { + position: absolute; + bottom: calc(100% + 0.5rem); + left: 0; + width: 320px; + background-color: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 0.625rem; + box-shadow: 0 8px 30px -4px rgba(0, 0, 0, 0.18), 0 4px 12px -2px rgba(0, 0, 0, 0.1); + z-index: 3000; + display: flex; + flex-direction: column; + overflow: hidden; + animation: emoji-pop 0.15s ease-out; +} + +.emoji-search-row { + display: flex; + align-items: center; + gap: 0.4rem; + padding: 0.6rem 0.75rem 0.4rem; + border-bottom: 1px solid #f1f5f9; +} + +.emoji-search-icon { + color: #94a3b8; + font-size: 0.8rem; + flex-shrink: 0; +} + +.emoji-search-input { + flex: 1; + border: 1px solid #e2e8f0; + border-radius: 0.375rem; + padding: 0.3rem 0.5rem; + font-size: 0.85rem; + outline: none; + background-color: #f8fafc; + transition: border-color 0.15s; +} + +.emoji-search-input:focus { + border-color: #3ba4d7; + background-color: #fff; +} + +.emoji-search-clear { + background: none; + border: none; + cursor: pointer; + color: #94a3b8; + padding: 0.2rem; + font-size: 0.8rem; + box-shadow: none; + display: flex; + align-items: center; +} + +.emoji-search-clear:hover { + color: #475569; +} + +.emoji-categories { + display: flex; + gap: 0.1rem; + padding: 0.35rem 0.5rem; + border-bottom: 1px solid #f1f5f9; + overflow-x: auto; + scrollbar-width: none; +} + +.emoji-categories::-webkit-scrollbar { + display: none; +} + +.emoji-cat-btn { + background: none; + border: none; + cursor: pointer; + font-size: 1.2rem; + padding: 0.3rem 0.35rem; + border-radius: 0.375rem; + line-height: 1; + box-shadow: none; + transition: background-color 0.1s; + flex-shrink: 0; +} + +.emoji-cat-btn:hover { + background-color: #f1f5f9; +} + +.emoji-cat-btn.active { + background-color: #e0f2fe; + box-shadow: inset 0 -2px 0 #3ba4d7; +} + +.emoji-grid { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 0; + padding: 0.4rem 0.35rem; + max-height: 220px; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: #cbd5e1 transparent; +} + +.emoji-grid::-webkit-scrollbar { + width: 4px; +} + +.emoji-grid::-webkit-scrollbar-track { + background: transparent; +} + +.emoji-grid::-webkit-scrollbar-thumb { + background-color: #cbd5e1; + border-radius: 4px; +} + +.emoji-btn { + background: none; + border: none; + cursor: pointer; + font-size: 1.7rem; + padding: 0.25rem; + border-radius: 0.3rem; + line-height: 1; + box-shadow: none; + text-align: center; + transition: background-color 0.1s, transform 0.1s; + display: flex; + align-items: center; + justify-content: center; + aspect-ratio: 1; +} + +.emoji-btn:hover { + background-color: #f1f5f9; + transform: scale(1.2); +} + +@keyframes emoji-pop { + from { opacity: 0; transform: scale(0.92) translateY(6px); } + to { opacity: 1; transform: scale(1) translateY(0); } +} diff --git a/webui-src/app/scss/pages/_config.scss b/webui-src/app/scss/pages/_config.scss index 659cc95..19d1256 100644 --- a/webui-src/app/scss/pages/_config.scss +++ b/webui-src/app/scss/pages/_config.scss @@ -1,3 +1,4 @@ +@use 'sass:color'; @use '../abstracts' as *; .mail { @@ -7,13 +8,13 @@ } &-tags { padding: 0.5rem; - border: 1px solid transparentize($dark-color, 0.8); + border: 1px solid color.adjust($dark-color, $alpha: -0.8); border-radius: 6px; &__container { @include flex(column); & .tag-item { @include flex($align: center, $gap: 4px); - border-bottom: 1px solid transparentize($dark-color, 0.9); + border-bottom: 1px solid color.adjust($dark-color, $alpha: -0.9); padding: 2px 0; &:last-child { diff --git a/webui-src/app/scss/pages/_files.scss b/webui-src/app/scss/pages/_files.scss index 2bc1f5c..c440100 100644 --- a/webui-src/app/scss/pages/_files.scss +++ b/webui-src/app/scss/pages/_files.scss @@ -1,3 +1,4 @@ +@use 'sass:color'; @use '../abstracts/' as *; .file-view { @@ -71,7 +72,7 @@ table.friendsfiles td:nth-child(2) { margin-top: 1rem; padding: 8px; @include flex($gap: 8px); - border: 1px solid transparentize($dark-color, 0.8); + border: 1px solid color.adjust($dark-color, $alpha: -0.8); border-radius: 6px; height: 100%; overflow: auto; @@ -79,11 +80,11 @@ table.friendsfiles td:nth-child(2) { &__keywords { flex-basis: 15%; padding-right: 0.25rem; - border-right: 1px solid transparentize($dark-color, 0.9); + border-right: 1px solid color.adjust($dark-color, $alpha: -0.9); & .keywords-container { @include flex(column); - border-top: 2.5px solid transparentize($dark-color, 0.92); + border-top: 2.5px solid color.adjust($dark-color, $alpha: -0.92); margin-top: 0.125rem; padding-top: 0.25rem; diff --git a/webui-src/app/scss/pages/_mail.scss b/webui-src/app/scss/pages/_mail.scss index 6149e30..f6bd375 100644 --- a/webui-src/app/scss/pages/_mail.scss +++ b/webui-src/app/scss/pages/_mail.scss @@ -1,3 +1,4 @@ +@use 'sass:color'; @use '../abstracts/' as *; .side-bar { @@ -182,7 +183,7 @@ table.mails { /* attachments */ &:nth-child(2) { width: 5%; - color: darken($light-color, 50%); + color: color.adjust($light-color, $lightness: -50%); } /* subject */ &:nth-child(3) { @@ -239,7 +240,7 @@ table.mails { &:hover { background-color: $light-color; - color: darken($light-color, 80%); + color: color.adjust($light-color, $lightness: -80%); } } } diff --git a/webui-src/app/scss/pages/_network.scss b/webui-src/app/scss/pages/_network.scss index 2ff74cc..ff1e9ce 100644 --- a/webui-src/app/scss/pages/_network.scss +++ b/webui-src/app/scss/pages/_network.scss @@ -104,6 +104,7 @@ display: flex; flex-direction: column; overflow: hidden; + position: relative; // anchors the absolutely-positioned .people-context-menu .searchbar-container { padding: 0.75rem 1rem; diff --git a/webui-src/app/scss/pages/_people.scss b/webui-src/app/scss/pages/_people.scss index 36f0810..d835421 100644 --- a/webui-src/app/scss/pages/_people.scss +++ b/webui-src/app/scss/pages/_people.scss @@ -195,3 +195,134 @@ img.avatar { } } + + +// Recent-chats split-pane + friends context menu (people_sidebar.js). +// Recovered from compiled styles.css; hard-coded colors kept as-was. + +.friends-list-container .people-context-menu { + position: absolute; + left: 2rem; + width: 210px; + background-color: #ffffff; + border: 1px solid #e2e8f0; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); + border-radius: 0.375rem; + z-index: 1010; + padding: 0.25rem 0; + display: flex; + flex-direction: column; +} + +.friends-list-container .people-context-menu .menu-item { + padding: 0.5rem 1rem; + font-size: 0.85rem; + color: #334155; + cursor: pointer; + display: flex; + align-items: center; + transition: background-color 0.2s; +} + +.friends-list-container .people-context-menu .menu-item:hover { + background-color: #f1f5f9; + color: #0f172a; +} + +.people-container { + display: flex; + height: calc(100vh - 55px); + width: 100%; + overflow: hidden; +} + +.people-left-pane { + width: 320px; + border-right: 1px solid #cbd5e1; + display: flex; + flex-direction: column; + background-color: #ffffff; + overflow: hidden; +} + +.people-right-pane { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + background-color: #f8fafc; +} + +.people-left-pane .chat-item { + display: flex; + align-items: center; + padding: 0.75rem 1rem; + gap: 0.75rem; + border-bottom: 1px solid #f1f5f9; + cursor: pointer; + transition: background-color 0.15s ease; + position: relative; +} + +.people-left-pane .chat-item:hover { + background-color: #f8fafc; +} + +.people-left-pane .chat-item.selected { + background-color: #eff6ff; + border-left: 3px solid #3b82f6; +} + +.people-left-pane .chat-item .chat-avatar-wrapper { + position: relative; + flex-shrink: 0; +} + +.people-left-pane .chat-item .chat-avatar-wrapper .status-dot { + position: absolute; + bottom: 0; + right: 0; + width: 10px; + height: 10px; + border-radius: 50%; + border: 2px solid #ffffff; +} + +.people-left-pane .chat-item .chat-info { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.15rem; +} + +.people-left-pane .chat-item .chat-info .chat-name { + font-size: 0.9rem; + font-weight: 700; + color: #1e293b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.people-left-pane .chat-item .chat-info .chat-last-msg { + font-size: 0.8rem; + color: #64748b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.people-left-pane .chat-item .chat-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.25rem; + flex-shrink: 0; +} + +.people-left-pane .chat-item .chat-meta .chat-time { + font-size: 0.75rem; + color: #94a3b8; + white-space: nowrap; +} diff --git a/webui-src/eslint.config.mjs b/webui-src/eslint.config.mjs new file mode 100644 index 0000000..8cfb645 --- /dev/null +++ b/webui-src/eslint.config.mjs @@ -0,0 +1,51 @@ +import js from '@eslint/js'; +import globals from 'globals'; + +export default [ + { + ignores: ['app/mithril.js', 'assets/**', 'make-src/**'], + }, + { + files: ['app/**/*.js'], + languageOptions: { + ecmaVersion: 2020, + sourceType: 'commonjs', + globals: { + ...globals.browser, + ...globals.commonjs, + }, + }, + rules: { + ...js.configs.recommended.rules, + 'linebreak-style': ['off', 'unix'], + quotes: ['error', 'single'], + semi: ['error', 'always'], + 'arrow-spacing': ['error', { before: true, after: true }], + 'arrow-parens': ['error', 'always'], + 'comma-style': ['error', 'last'], + 'comma-spacing': ['error', { before: false, after: true }], + camelcase: ['error', { + allow: ['^UNSAFE_'], + properties: 'never', + ignoreGlobals: true, + }], + 'eol-last': 'error', + eqeqeq: ['error', 'always', { null: 'ignore' }], + 'no-irregular-whitespace': 'error', + 'no-trailing-spaces': 'error', + 'no-unexpected-multiline': 'error', + 'no-unreachable': 'error', + 'no-var': 'warn', + 'no-unused-vars': ['error', { + args: 'none', + caughtErrors: 'none', + ignoreRestSiblings: true, + vars: 'all', + }], + 'object-shorthand': 'error', + 'prefer-const': ['error', { destructuring: 'all' }], + 'semi-style': ['warn', 'last'], + 'spaced-comment': ['warn', 'always'], + }, + }, +]; diff --git a/webui-src/make-src/build.bat b/webui-src/make-src/build.bat index 62f4e0e..c61fe57 100644 --- a/webui-src/make-src/build.bat +++ b/webui-src/make-src/build.bat @@ -5,59 +5,71 @@ setlocal enabledelayedexpansion echo "### Starting WebUI build ###" -set src=%~dp0..\..\webui-src +set "src=%~dp0..\..\webui-src" rem Output destination -if "%~1" == "" ( - set publicdest=%~dp0..\..\webui +if "%~1"=="" ( + set "publicdest=%~dp0..\..\webui" ) else ( - set publicdest=%~1\webui + set "publicdest=%~1\webui" ) -if exist "%publicdest%" echo removing existing %publicdest%&&rd %publicdest% /S /Q +if exist "%publicdest%" ( + echo removing existing %publicdest% + rd "%publicdest%" /S /Q +) echo creating %publicdest% -md %publicdest% +md "%publicdest%" rem Make full path -pushd %publicdest% -set publicdest=%cd% +pushd "%publicdest%" +set "publicdest=%cd%" popd echo copying html file -xcopy /s %src%\index.html %publicdest% +copy /Y "%src%\index.html" "%publicdest%\index.html" >nul echo copying css file -xcopy /s %src%\styles.css %publicdest% +copy /Y "%src%\styles.css" "%publicdest%\styles.css" >nul echo building app.js echo - copying template.js ... -copy %src%\make-src\template.js %publicdest%\app.js +copy /Y "%src%\make-src\template.js" "%publicdest%\app.js" >nul -pushd %src%\app +pushd "%src%\app" set "basefolder=%cd%\" +set "lastsection=" for /R %%F in (*.js) do call :addfile-js "%basefolder%" "%%F" popd echo copying assets folder -xcopy /s %src%\assets\ %publicdest% +xcopy "%src%\assets\*" "%publicdest%\" /E /I /Y >nul echo "### WebUI build complete ###" goto :EOF :addfile-js -set basefolder=%~1 -set fname=%~2 +set "basefolder=%~1" +set "fname=%~2" -set registername=%~dpn2 -set registername=!registername:%basefolder%=! -set registername=%registername:\=/% +set "registername=%~dpn2" +set "registername=!registername:%basefolder%=!" +set "registername=%registername:\=/%" -echo - adding %registername% ... -echo require.register("%registername%", function(exports, require, module) { >> %publicdest%\app.js -type %fname% >> %publicdest%\app.js -echo. >> %publicdest%\app.js -echo }); >> %publicdest%\app.js +for /f "tokens=1,2 delims=/" %%A in ("!registername!") do ( + if "%%B"=="" ( + echo - adding !registername! ... + set "lastsection=" + ) else if not "!lastsection!"=="%%A" ( + echo - adding %%A/* ... + set "lastsection=%%A" + ) +) +echo require.register("%registername%", function(exports, require, module) { >>"%publicdest%\app.js" +type "%fname%" >>"%publicdest%\app.js" +echo. >>"%publicdest%\app.js" +echo }); >>"%publicdest%\app.js" :EOF diff --git a/webui-src/make-src/build.js b/webui-src/make-src/build.js new file mode 100644 index 0000000..bfb7c60 --- /dev/null +++ b/webui-src/make-src/build.js @@ -0,0 +1,35 @@ +'use strict'; + +const assert = require('node:assert/strict'); +const path = require('node:path'); +const { spawnSync } = require('node:child_process'); + +const shellScript = path.join(__dirname, 'build.sh'); +const batchScript = path.join(__dirname, 'build.bat'); + +function getRunner(platform, commandShell = 'cmd.exe') { + return platform === 'win32' + ? { command: commandShell, args: ['/d', '/c', batchScript] } + : { command: 'sh', args: [shellScript] }; +} + +function run(runner) { + const result = spawnSync(runner.command, runner.args, { stdio: 'inherit' }); + if (result.error) { + console.error(result.error.message); + return 1; + } + return result.status ?? 1; +} + +if (process.argv[2] === '--check') { + assert.equal(path.basename(getRunner('win32').args[2]), 'build.bat'); + assert.equal(path.basename(getRunner('linux').args[0]), 'build.sh'); + const status = process.platform === 'win32' + ? 0 + : run({ command: 'sh', args: ['-n', shellScript] }); + if (status === 0) console.log('Build dispatcher checks passed.'); + process.exitCode = status; +} else { + process.exitCode = run(getRunner(process.platform, process.env.ComSpec)); +} diff --git a/webui-src/make-src/build.sh b/webui-src/make-src/build.sh index 9a7bef1..ce47dea 100755 --- a/webui-src/make-src/build.sh +++ b/webui-src/make-src/build.sh @@ -1,68 +1,85 @@ -#!/bin/bash +#!/bin/sh -# create webfiles from sources at compile time (works without npm/node.js) +# Create webfiles from sources at compile time (works without npm/node.js) +# +# Usage: build.sh [DEST_PARENT] [TARGET_FILE] [EXTRA_COPY_DIR] +# DEST_PARENT parent dir of the generated webui/ (default: repo root) +# TARGET_FILE build only this file (index.html|styles.css|app.js); default: all +# EXTRA_COPY_DIR also copy TARGET_FILE into EXTRA_COPY_DIR/webui/ + +set -eu echo "### Starting WebUI build ###" -src=$(readlink -f $(dirname $0))/../../webui-src +# Resolve the script's own directory portably. +script_dir=$(cd -- "$(dirname -- "$0")" && pwd -P) +src="$script_dir/../../webui-src" -if [ "$1" = "" ]; then - publicdest=$(readlink -f $(dirname $0))/../../webui +dest_parent="${1:-}" +target="${2:-}" +extra_copy_dir="${3:-}" + +if [ -z "$dest_parent" ]; then + publicdest="$script_dir/../../webui" else - publicdest=$1/webui + publicdest="$dest_parent/webui" fi -if [ "$2" = "" ]; then - if [ -d "$publicdest" ]; then - echo removing existing $publicdest - rm $publicdest -R - fi +# Full rebuild (no specific target): remove any existing output first. +if [ -z "$target" ] && [ -d "$publicdest" ]; then + echo "removing existing $publicdest" + rm -rf -- "$publicdest" fi -if [ ! -d "$publicdest" ]; then - echo creating $publicdest - mkdir $publicdest +mkdir -p -- "$publicdest" + +if [ -z "$target" ] || [ "$target" = "index.html" ]; then + echo "copying html file" + cp -- "$src/index.html" "$publicdest/" fi -# For using recursive directory search(requires bash v4+) -shopt -s globstar - -if [ "$2" = "" ]||[ "$2" = "index.html" ]; then - echo copying html file - cp $src/index.html $publicdest/ +if [ -z "$target" ] || [ "$target" = "styles.css" ]; then + echo "copying css file" + cp -- "$src/styles.css" "$publicdest/" fi -if [ "$2" = "" ]||[ "$2" = "styles.css" ]; then - echo copying css file - cp $src/styles.css $publicdest/ +if [ -z "$target" ] || [ "$target" = "app.js" ]; then + echo "building app.js:" + echo "- copying template.js ..." + cp -- "$src/make-src/template.js" "$publicdest/app.js" + + js_root="$src/app" + find "$js_root" -type f -name '*.js' | LC_ALL=C sort | while IFS= read -r filename; do + fname="${filename#"$js_root/"}" + fname="${fname%.*}" + case "$fname" in + */*) + section="${fname%%/*}/*" + if [ "$section" != "${last_section:-}" ]; then + echo "- adding $section ..." + last_section="$section" + fi + ;; + *) + echo "- adding $fname ..." + last_section= + ;; + esac + { + printf 'require.register("%s", function(exports, require, module) {\n' "$fname" + cat -- "$filename" + printf '\n});\n' + } >>"$publicdest/app.js" + done fi -if [ "$2" = "" ]||[ "$2" = "app.js" ]; then - echo building app.js: - echo - copying template.js ... - cp $src/make-src/template.js $publicdest/app.js +echo "copying assets folder" +cp -R -- "$src/assets/." "$publicdest/" - js_source=$src/app/ - for filename in $src/app/**/*.js; do - fname="${filename#$js_source}" - fname="${fname%.*}" - echo - adding $fname ... - echo require.register\(\"$fname\", function\(exports, require, module\) { >> $publicdest/app.js - cat $filename >> $publicdest/app.js - echo >> $publicdest/app.js - echo }\)\; >> $publicdest/app.js - done +if [ -n "$target" ] && [ -n "$extra_copy_dir" ]; then + mkdir -p -- "$extra_copy_dir/webui" + echo "copying $target to $extra_copy_dir/webui/$target" + cp -- "$publicdest/$target" "$extra_copy_dir/webui/$target" fi -echo copying assets folder -cp -r $src/assets/* $publicdest/ - -if [ "$2" != "" ]&&[ "$3" != "" ]; then - if [ ! -d "$3/webui" ]; then - echo creating $3/webui - mkdir $3/webui - fi - echo copying $2 nach $3/webui/$2 - cp $publicdest/$2 $3/webui/$2 -fi echo "### WebUI build complete ###" diff --git a/webui-src/package-lock.json b/webui-src/package-lock.json new file mode 100644 index 0000000..a2547df --- /dev/null +++ b/webui-src/package-lock.json @@ -0,0 +1,1311 @@ +{ + "name": "webui-src", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "webui-src", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@eslint/js": "^10.0.1", + "eslint": "^10.8.0", + "globals": "^17.8.0", + "sass": "1.97.3" + }, + "engines": { + "node": ">=24" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.8.0.tgz", + "integrity": "sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/webui-src/package.json b/webui-src/package.json index 5aac05b..2b12802 100644 --- a/webui-src/package.json +++ b/webui-src/package.json @@ -3,11 +3,18 @@ "version": "1.0.0", "description": "Retroshare's Web Interface", "scripts": { - "watch": "rm ./styles.css && sass --watch --embed-sources --embed-source-map ./app/scss/main.scss ./styles.css", - "build": "sass --no-source-map --style=compressed ./app/scss/main.scss ./styles.css" + "watch": "sass --watch --embed-sources --embed-source-map ./app/scss/main.scss ./styles.css", + "build": "sass --no-source-map --style=compressed ./app/scss/main.scss ./styles.css && node ./make-src/build.js", + "lint": "eslint app && node ./make-src/build.js --check && echo Lint checks passed." }, "license": "ISC", + "engines": { + "node": ">=24" + }, "devDependencies": { - "sass": "^1.60.0" + "@eslint/js": "^10.0.1", + "eslint": "^10.8.0", + "globals": "^17.8.0", + "sass": "1.97.3" } } diff --git a/webui-src/pnpm-lock.yaml b/webui-src/pnpm-lock.yaml deleted file mode 100644 index 9dbf4bd..0000000 --- a/webui-src/pnpm-lock.yaml +++ /dev/null @@ -1,240 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - devDependencies: - sass: - specifier: ^1.60.0 - version: 1.97.3 - -packages: - - '@parcel/watcher-android-arm64@2.5.6': - resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [android] - - '@parcel/watcher-darwin-arm64@2.5.6': - resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [darwin] - - '@parcel/watcher-darwin-x64@2.5.6': - resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [darwin] - - '@parcel/watcher-freebsd-x64@2.5.6': - resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [freebsd] - - '@parcel/watcher-linux-arm-glibc@2.5.6': - resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@parcel/watcher-linux-arm-musl@2.5.6': - resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - libc: [musl] - - '@parcel/watcher-linux-arm64-glibc@2.5.6': - resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@parcel/watcher-linux-arm64-musl@2.5.6': - resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@parcel/watcher-linux-x64-glibc@2.5.6': - resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@parcel/watcher-linux-x64-musl@2.5.6': - resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@parcel/watcher-win32-arm64@2.5.6': - resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [win32] - - '@parcel/watcher-win32-ia32@2.5.6': - resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} - engines: {node: '>= 10.0.0'} - cpu: [ia32] - os: [win32] - - '@parcel/watcher-win32-x64@2.5.6': - resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [win32] - - '@parcel/watcher@2.5.6': - resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} - engines: {node: '>= 10.0.0'} - - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - immutable@5.1.5: - resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - node-addon-api@7.1.1: - resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - sass@1.97.3: - resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==} - engines: {node: '>=14.0.0'} - hasBin: true - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - -snapshots: - - '@parcel/watcher-android-arm64@2.5.6': - optional: true - - '@parcel/watcher-darwin-arm64@2.5.6': - optional: true - - '@parcel/watcher-darwin-x64@2.5.6': - optional: true - - '@parcel/watcher-freebsd-x64@2.5.6': - optional: true - - '@parcel/watcher-linux-arm-glibc@2.5.6': - optional: true - - '@parcel/watcher-linux-arm-musl@2.5.6': - optional: true - - '@parcel/watcher-linux-arm64-glibc@2.5.6': - optional: true - - '@parcel/watcher-linux-arm64-musl@2.5.6': - optional: true - - '@parcel/watcher-linux-x64-glibc@2.5.6': - optional: true - - '@parcel/watcher-linux-x64-musl@2.5.6': - optional: true - - '@parcel/watcher-win32-arm64@2.5.6': - optional: true - - '@parcel/watcher-win32-ia32@2.5.6': - optional: true - - '@parcel/watcher-win32-x64@2.5.6': - optional: true - - '@parcel/watcher@2.5.6': - dependencies: - detect-libc: 2.1.2 - is-glob: 4.0.3 - node-addon-api: 7.1.1 - picomatch: 4.0.3 - optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.6 - '@parcel/watcher-darwin-arm64': 2.5.6 - '@parcel/watcher-darwin-x64': 2.5.6 - '@parcel/watcher-freebsd-x64': 2.5.6 - '@parcel/watcher-linux-arm-glibc': 2.5.6 - '@parcel/watcher-linux-arm-musl': 2.5.6 - '@parcel/watcher-linux-arm64-glibc': 2.5.6 - '@parcel/watcher-linux-arm64-musl': 2.5.6 - '@parcel/watcher-linux-x64-glibc': 2.5.6 - '@parcel/watcher-linux-x64-musl': 2.5.6 - '@parcel/watcher-win32-arm64': 2.5.6 - '@parcel/watcher-win32-ia32': 2.5.6 - '@parcel/watcher-win32-x64': 2.5.6 - optional: true - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - detect-libc@2.1.2: - optional: true - - immutable@5.1.5: {} - - is-extglob@2.1.1: - optional: true - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - optional: true - - node-addon-api@7.1.1: - optional: true - - picomatch@4.0.3: - optional: true - - readdirp@4.1.2: {} - - sass@1.97.3: - dependencies: - chokidar: 4.0.3 - immutable: 5.1.5 - source-map-js: 1.2.1 - optionalDependencies: - '@parcel/watcher': 2.5.6 - - source-map-js@1.2.1: {} diff --git a/webui-src/styles.css b/webui-src/styles.css index 369212b..a9de9cb 100644 --- a/webui-src/styles.css +++ b/webui-src/styles.css @@ -4,4 +4,4 @@ */.fa,.fas,.far,.fal,.fab{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-0.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fas.fa-pull-left,.far.fa-pull-left,.fal.fa-pull-left,.fab.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fas.fa-pull-right,.far.fa-pull-right,.fal.fa-pull-right,.fab.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);transform:scale(1, -1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(-1, -1);transform:scale(-1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-flip-both{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"๏‰ฎ"}.fa-accessible-icon:before{content:"๏จ"}.fa-accusoft:before{content:"๏ฉ"}.fa-acquisitions-incorporated:before{content:"๏šฏ"}.fa-ad:before{content:"๏™"}.fa-address-book:before{content:"๏Šน"}.fa-address-card:before{content:"๏Šป"}.fa-adjust:before{content:"๏‚"}.fa-adn:before{content:"๏…ฐ"}.fa-adobe:before{content:"๏ธ"}.fa-adversal:before{content:"๏ช"}.fa-affiliatetheme:before{content:"๏ซ"}.fa-air-freshener:before{content:"๏—"}.fa-airbnb:before{content:"๏ ด"}.fa-algolia:before{content:"๏ฌ"}.fa-align-center:before{content:"๏€ท"}.fa-align-justify:before{content:"๏€น"}.fa-align-left:before{content:"๏€ถ"}.fa-align-right:before{content:"๏€ธ"}.fa-alipay:before{content:"๏™‚"}.fa-allergies:before{content:"๏‘ก"}.fa-amazon:before{content:"๏‰ฐ"}.fa-amazon-pay:before{content:"๏ฌ"}.fa-ambulance:before{content:"๏ƒน"}.fa-american-sign-language-interpreting:before{content:"๏Šฃ"}.fa-amilia:before{content:"๏ญ"}.fa-anchor:before{content:"๏„ฝ"}.fa-android:before{content:"๏…ป"}.fa-angellist:before{content:"๏ˆ‰"}.fa-angle-double-down:before{content:"๏„ƒ"}.fa-angle-double-left:before{content:"๏„€"}.fa-angle-double-right:before{content:"๏„"}.fa-angle-double-up:before{content:"๏„‚"}.fa-angle-down:before{content:"๏„‡"}.fa-angle-left:before{content:"๏„„"}.fa-angle-right:before{content:"๏„…"}.fa-angle-up:before{content:"๏„†"}.fa-angry:before{content:"๏•–"}.fa-angrycreative:before{content:"๏ฎ"}.fa-angular:before{content:"๏ "}.fa-ankh:before{content:"๏™„"}.fa-app-store:before{content:"๏ฏ"}.fa-app-store-ios:before{content:"๏ฐ"}.fa-apper:before{content:"๏ฑ"}.fa-apple:before{content:"๏…น"}.fa-apple-alt:before{content:"๏—‘"}.fa-apple-pay:before{content:"๏•"}.fa-archive:before{content:"๏†‡"}.fa-archway:before{content:"๏•—"}.fa-arrow-alt-circle-down:before{content:"๏˜"}.fa-arrow-alt-circle-left:before{content:"๏™"}.fa-arrow-alt-circle-right:before{content:"๏š"}.fa-arrow-alt-circle-up:before{content:"๏›"}.fa-arrow-circle-down:before{content:"๏‚ซ"}.fa-arrow-circle-left:before{content:"๏‚จ"}.fa-arrow-circle-right:before{content:"๏‚ฉ"}.fa-arrow-circle-up:before{content:"๏‚ช"}.fa-arrow-down:before{content:"๏ฃ"}.fa-arrow-left:before{content:"๏ "}.fa-arrow-right:before{content:"๏ก"}.fa-arrow-up:before{content:"๏ข"}.fa-arrows-alt:before{content:"๏‚ฒ"}.fa-arrows-alt-h:before{content:"๏Œท"}.fa-arrows-alt-v:before{content:"๏Œธ"}.fa-artstation:before{content:"๏บ"}.fa-assistive-listening-systems:before{content:"๏Šข"}.fa-asterisk:before{content:"๏ฉ"}.fa-asymmetrik:before{content:"๏ฒ"}.fa-at:before{content:"๏‡บ"}.fa-atlas:before{content:"๏•˜"}.fa-atlassian:before{content:"๏ป"}.fa-atom:before{content:"๏—’"}.fa-audible:before{content:"๏ณ"}.fa-audio-description:before{content:"๏Šž"}.fa-autoprefixer:before{content:"๏œ"}.fa-avianex:before{content:"๏ด"}.fa-aviato:before{content:"๏ก"}.fa-award:before{content:"๏•™"}.fa-aws:before{content:"๏ต"}.fa-baby:before{content:"๏ผ"}.fa-baby-carriage:before{content:"๏ฝ"}.fa-backspace:before{content:"๏•š"}.fa-backward:before{content:"๏Š"}.fa-bacon:before{content:"๏Ÿฅ"}.fa-balance-scale:before{content:"๏‰Ž"}.fa-balance-scale-left:before{content:"๏”•"}.fa-balance-scale-right:before{content:"๏”–"}.fa-ban:before{content:"๏ž"}.fa-band-aid:before{content:"๏‘ข"}.fa-bandcamp:before{content:"๏‹•"}.fa-barcode:before{content:"๏€ช"}.fa-bars:before{content:"๏ƒ‰"}.fa-baseball-ball:before{content:"๏ณ"}.fa-basketball-ball:before{content:"๏ด"}.fa-bath:before{content:"๏‹"}.fa-battery-empty:before{content:"๏‰„"}.fa-battery-full:before{content:"๏‰€"}.fa-battery-half:before{content:"๏‰‚"}.fa-battery-quarter:before{content:"๏‰ƒ"}.fa-battery-three-quarters:before{content:"๏‰"}.fa-battle-net:before{content:"๏ ต"}.fa-bed:before{content:"๏ˆถ"}.fa-beer:before{content:"๏ƒผ"}.fa-behance:before{content:"๏†ด"}.fa-behance-square:before{content:"๏†ต"}.fa-bell:before{content:"๏ƒณ"}.fa-bell-slash:before{content:"๏‡ถ"}.fa-bezier-curve:before{content:"๏•›"}.fa-bible:before{content:"๏™‡"}.fa-bicycle:before{content:"๏ˆ†"}.fa-biking:before{content:"๏กŠ"}.fa-bimobject:before{content:"๏ธ"}.fa-binoculars:before{content:"๏‡ฅ"}.fa-biohazard:before{content:"๏ž€"}.fa-birthday-cake:before{content:"๏‡ฝ"}.fa-bitbucket:before{content:"๏…ฑ"}.fa-bitcoin:before{content:"๏น"}.fa-bity:before{content:"๏บ"}.fa-black-tie:before{content:"๏‰พ"}.fa-blackberry:before{content:"๏ป"}.fa-blender:before{content:"๏”—"}.fa-blender-phone:before{content:"๏šถ"}.fa-blind:before{content:"๏Š"}.fa-blog:before{content:"๏ž"}.fa-blogger:before{content:"๏ผ"}.fa-blogger-b:before{content:"๏ฝ"}.fa-bluetooth:before{content:"๏Š“"}.fa-bluetooth-b:before{content:"๏Š”"}.fa-bold:before{content:"๏€ฒ"}.fa-bolt:before{content:"๏ƒง"}.fa-bomb:before{content:"๏‡ข"}.fa-bone:before{content:"๏——"}.fa-bong:before{content:"๏•œ"}.fa-book:before{content:"๏€ญ"}.fa-book-dead:before{content:"๏šท"}.fa-book-medical:before{content:"๏Ÿฆ"}.fa-book-open:before{content:"๏”˜"}.fa-book-reader:before{content:"๏—š"}.fa-bookmark:before{content:"๏€ฎ"}.fa-bootstrap:before{content:"๏ ถ"}.fa-border-all:before{content:"๏กŒ"}.fa-border-none:before{content:"๏ก"}.fa-border-style:before{content:"๏ก“"}.fa-bowling-ball:before{content:"๏ถ"}.fa-box:before{content:"๏‘ฆ"}.fa-box-open:before{content:"๏’ž"}.fa-boxes:before{content:"๏‘จ"}.fa-braille:before{content:"๏Šก"}.fa-brain:before{content:"๏—œ"}.fa-bread-slice:before{content:"๏Ÿฌ"}.fa-briefcase:before{content:"๏‚ฑ"}.fa-briefcase-medical:before{content:"๏‘ฉ"}.fa-broadcast-tower:before{content:"๏”™"}.fa-broom:before{content:"๏”š"}.fa-brush:before{content:"๏•"}.fa-btc:before{content:"๏…š"}.fa-buffer:before{content:"๏ ท"}.fa-bug:before{content:"๏†ˆ"}.fa-building:before{content:"๏†ญ"}.fa-bullhorn:before{content:"๏‚ก"}.fa-bullseye:before{content:"๏…€"}.fa-burn:before{content:"๏‘ช"}.fa-buromobelexperte:before{content:"๏ฟ"}.fa-bus:before{content:"๏ˆ‡"}.fa-bus-alt:before{content:"๏•ž"}.fa-business-time:before{content:"๏™Š"}.fa-buysellads:before{content:"๏ˆ"}.fa-calculator:before{content:"๏‡ฌ"}.fa-calendar:before{content:"๏„ณ"}.fa-calendar-alt:before{content:"๏ณ"}.fa-calendar-check:before{content:"๏‰ด"}.fa-calendar-day:before{content:"๏žƒ"}.fa-calendar-minus:before{content:"๏‰ฒ"}.fa-calendar-plus:before{content:"๏‰ฑ"}.fa-calendar-times:before{content:"๏‰ณ"}.fa-calendar-week:before{content:"๏ž„"}.fa-camera:before{content:"๏€ฐ"}.fa-camera-retro:before{content:"๏‚ƒ"}.fa-campground:before{content:"๏šป"}.fa-canadian-maple-leaf:before{content:"๏ž…"}.fa-candy-cane:before{content:"๏ž†"}.fa-cannabis:before{content:"๏•Ÿ"}.fa-capsules:before{content:"๏‘ซ"}.fa-car:before{content:"๏†น"}.fa-car-alt:before{content:"๏—ž"}.fa-car-battery:before{content:"๏—Ÿ"}.fa-car-crash:before{content:"๏—ก"}.fa-car-side:before{content:"๏—ค"}.fa-caret-down:before{content:"๏ƒ—"}.fa-caret-left:before{content:"๏ƒ™"}.fa-caret-right:before{content:"๏ƒš"}.fa-caret-square-down:before{content:"๏…"}.fa-caret-square-left:before{content:"๏†‘"}.fa-caret-square-right:before{content:"๏…’"}.fa-caret-square-up:before{content:"๏…‘"}.fa-caret-up:before{content:"๏ƒ˜"}.fa-carrot:before{content:"๏ž‡"}.fa-cart-arrow-down:before{content:"๏ˆ˜"}.fa-cart-plus:before{content:"๏ˆ—"}.fa-cash-register:before{content:"๏žˆ"}.fa-cat:before{content:"๏šพ"}.fa-cc-amazon-pay:before{content:"๏ญ"}.fa-cc-amex:before{content:"๏‡ณ"}.fa-cc-apple-pay:before{content:"๏–"}.fa-cc-diners-club:before{content:"๏‰Œ"}.fa-cc-discover:before{content:"๏‡ฒ"}.fa-cc-jcb:before{content:"๏‰‹"}.fa-cc-mastercard:before{content:"๏‡ฑ"}.fa-cc-paypal:before{content:"๏‡ด"}.fa-cc-stripe:before{content:"๏‡ต"}.fa-cc-visa:before{content:"๏‡ฐ"}.fa-centercode:before{content:"๏Ž€"}.fa-centos:before{content:"๏ž‰"}.fa-certificate:before{content:"๏‚ฃ"}.fa-chair:before{content:"๏›€"}.fa-chalkboard:before{content:"๏”›"}.fa-chalkboard-teacher:before{content:"๏”œ"}.fa-charging-station:before{content:"๏—ง"}.fa-chart-area:before{content:"๏‡พ"}.fa-chart-bar:before{content:"๏‚€"}.fa-chart-line:before{content:"๏ˆ"}.fa-chart-pie:before{content:"๏ˆ€"}.fa-check:before{content:"๏€Œ"}.fa-check-circle:before{content:"๏˜"}.fa-check-double:before{content:"๏• "}.fa-check-square:before{content:"๏…Š"}.fa-cheese:before{content:"๏Ÿฏ"}.fa-chess:before{content:"๏น"}.fa-chess-bishop:before{content:"๏บ"}.fa-chess-board:before{content:"๏ผ"}.fa-chess-king:before{content:"๏ฟ"}.fa-chess-knight:before{content:"๏‘"}.fa-chess-pawn:before{content:"๏‘ƒ"}.fa-chess-queen:before{content:"๏‘…"}.fa-chess-rook:before{content:"๏‘‡"}.fa-chevron-circle-down:before{content:"๏„บ"}.fa-chevron-circle-left:before{content:"๏„ท"}.fa-chevron-circle-right:before{content:"๏„ธ"}.fa-chevron-circle-up:before{content:"๏„น"}.fa-chevron-down:before{content:"๏ธ"}.fa-chevron-left:before{content:"๏“"}.fa-chevron-right:before{content:"๏”"}.fa-chevron-up:before{content:"๏ท"}.fa-child:before{content:"๏†ฎ"}.fa-chrome:before{content:"๏‰จ"}.fa-chromecast:before{content:"๏ ธ"}.fa-church:before{content:"๏”"}.fa-circle:before{content:"๏„‘"}.fa-circle-notch:before{content:"๏‡Ž"}.fa-city:before{content:"๏™"}.fa-clinic-medical:before{content:"๏Ÿฒ"}.fa-clipboard:before{content:"๏Œจ"}.fa-clipboard-check:before{content:"๏‘ฌ"}.fa-clipboard-list:before{content:"๏‘ญ"}.fa-clock:before{content:"๏€—"}.fa-clone:before{content:"๏‰"}.fa-closed-captioning:before{content:"๏ˆŠ"}.fa-cloud:before{content:"๏ƒ‚"}.fa-cloud-download-alt:before{content:"๏ށ"}.fa-cloud-meatball:before{content:"๏œป"}.fa-cloud-moon:before{content:"๏›ƒ"}.fa-cloud-moon-rain:before{content:"๏œผ"}.fa-cloud-rain:before{content:"๏œฝ"}.fa-cloud-showers-heavy:before{content:"๏€"}.fa-cloud-sun:before{content:"๏›„"}.fa-cloud-sun-rain:before{content:"๏ƒ"}.fa-cloud-upload-alt:before{content:"๏Ž‚"}.fa-cloudscale:before{content:"๏Žƒ"}.fa-cloudsmith:before{content:"๏Ž„"}.fa-cloudversify:before{content:"๏Ž…"}.fa-cocktail:before{content:"๏•ก"}.fa-code:before{content:"๏„ก"}.fa-code-branch:before{content:"๏„ฆ"}.fa-codepen:before{content:"๏‡‹"}.fa-codiepie:before{content:"๏Š„"}.fa-coffee:before{content:"๏ƒด"}.fa-cog:before{content:"๏€“"}.fa-cogs:before{content:"๏‚…"}.fa-coins:before{content:"๏”ž"}.fa-columns:before{content:"๏ƒ›"}.fa-comment:before{content:"๏ต"}.fa-comment-alt:before{content:"๏‰บ"}.fa-comment-dollar:before{content:"๏™‘"}.fa-comment-dots:before{content:"๏’ญ"}.fa-comment-medical:before{content:"๏Ÿต"}.fa-comment-slash:before{content:"๏’ณ"}.fa-comments:before{content:"๏‚†"}.fa-comments-dollar:before{content:"๏™“"}.fa-compact-disc:before{content:"๏”Ÿ"}.fa-compass:before{content:"๏…Ž"}.fa-compress:before{content:"๏ฆ"}.fa-compress-arrows-alt:before{content:"๏žŒ"}.fa-concierge-bell:before{content:"๏•ข"}.fa-confluence:before{content:"๏ž"}.fa-connectdevelop:before{content:"๏ˆŽ"}.fa-contao:before{content:"๏‰ญ"}.fa-cookie:before{content:"๏•ฃ"}.fa-cookie-bite:before{content:"๏•ค"}.fa-copy:before{content:"๏ƒ…"}.fa-copyright:before{content:"๏‡น"}.fa-couch:before{content:"๏’ธ"}.fa-cpanel:before{content:"๏Žˆ"}.fa-creative-commons:before{content:"๏‰ž"}.fa-creative-commons-by:before{content:"๏“ง"}.fa-creative-commons-nc:before{content:"๏“จ"}.fa-creative-commons-nc-eu:before{content:"๏“ฉ"}.fa-creative-commons-nc-jp:before{content:"๏“ช"}.fa-creative-commons-nd:before{content:"๏“ซ"}.fa-creative-commons-pd:before{content:"๏“ฌ"}.fa-creative-commons-pd-alt:before{content:"๏“ญ"}.fa-creative-commons-remix:before{content:"๏“ฎ"}.fa-creative-commons-sa:before{content:"๏“ฏ"}.fa-creative-commons-sampling:before{content:"๏“ฐ"}.fa-creative-commons-sampling-plus:before{content:"๏“ฑ"}.fa-creative-commons-share:before{content:"๏“ฒ"}.fa-creative-commons-zero:before{content:"๏“ณ"}.fa-credit-card:before{content:"๏‚"}.fa-critical-role:before{content:"๏›‰"}.fa-crop:before{content:"๏„ฅ"}.fa-crop-alt:before{content:"๏•ฅ"}.fa-cross:before{content:"๏™”"}.fa-crosshairs:before{content:"๏›"}.fa-crow:before{content:"๏” "}.fa-crown:before{content:"๏”ก"}.fa-crutch:before{content:"๏Ÿท"}.fa-css3:before{content:"๏„ผ"}.fa-css3-alt:before{content:"๏Ž‹"}.fa-cube:before{content:"๏†ฒ"}.fa-cubes:before{content:"๏†ณ"}.fa-cut:before{content:"๏ƒ„"}.fa-cuttlefish:before{content:"๏ŽŒ"}.fa-d-and-d:before{content:"๏Ž"}.fa-d-and-d-beyond:before{content:"๏›Š"}.fa-dashcube:before{content:"๏ˆ"}.fa-database:before{content:"๏‡€"}.fa-deaf:before{content:"๏Šค"}.fa-delicious:before{content:"๏†ฅ"}.fa-democrat:before{content:"๏‡"}.fa-deploydog:before{content:"๏ŽŽ"}.fa-deskpro:before{content:"๏Ž"}.fa-desktop:before{content:"๏„ˆ"}.fa-dev:before{content:"๏›Œ"}.fa-deviantart:before{content:"๏†ฝ"}.fa-dharmachakra:before{content:"๏™•"}.fa-dhl:before{content:"๏ž"}.fa-diagnoses:before{content:"๏‘ฐ"}.fa-diaspora:before{content:"๏ž‘"}.fa-dice:before{content:"๏”ข"}.fa-dice-d20:before{content:"๏›"}.fa-dice-d6:before{content:"๏›‘"}.fa-dice-five:before{content:"๏”ฃ"}.fa-dice-four:before{content:"๏”ค"}.fa-dice-one:before{content:"๏”ฅ"}.fa-dice-six:before{content:"๏”ฆ"}.fa-dice-three:before{content:"๏”ง"}.fa-dice-two:before{content:"๏”จ"}.fa-digg:before{content:"๏†ฆ"}.fa-digital-ocean:before{content:"๏Ž‘"}.fa-digital-tachograph:before{content:"๏•ฆ"}.fa-directions:before{content:"๏—ซ"}.fa-discord:before{content:"๏Ž’"}.fa-discourse:before{content:"๏Ž“"}.fa-divide:before{content:"๏”ฉ"}.fa-dizzy:before{content:"๏•ง"}.fa-dna:before{content:"๏‘ฑ"}.fa-dochub:before{content:"๏Ž”"}.fa-docker:before{content:"๏Ž•"}.fa-dog:before{content:"๏›“"}.fa-dollar-sign:before{content:"๏…•"}.fa-dolly:before{content:"๏‘ฒ"}.fa-dolly-flatbed:before{content:"๏‘ด"}.fa-donate:before{content:"๏’น"}.fa-door-closed:before{content:"๏”ช"}.fa-door-open:before{content:"๏”ซ"}.fa-dot-circle:before{content:"๏†’"}.fa-dove:before{content:"๏’บ"}.fa-download:before{content:"๏€™"}.fa-draft2digital:before{content:"๏Ž–"}.fa-drafting-compass:before{content:"๏•จ"}.fa-dragon:before{content:"๏›•"}.fa-draw-polygon:before{content:"๏—ฎ"}.fa-dribbble:before{content:"๏…ฝ"}.fa-dribbble-square:before{content:"๏Ž—"}.fa-dropbox:before{content:"๏…ซ"}.fa-drum:before{content:"๏•ฉ"}.fa-drum-steelpan:before{content:"๏•ช"}.fa-drumstick-bite:before{content:"๏›—"}.fa-drupal:before{content:"๏†ฉ"}.fa-dumbbell:before{content:"๏‘‹"}.fa-dumpster:before{content:"๏ž“"}.fa-dumpster-fire:before{content:"๏ž”"}.fa-dungeon:before{content:"๏›™"}.fa-dyalog:before{content:"๏Ž™"}.fa-earlybirds:before{content:"๏Žš"}.fa-ebay:before{content:"๏“ด"}.fa-edge:before{content:"๏Š‚"}.fa-edit:before{content:"๏„"}.fa-egg:before{content:"๏Ÿป"}.fa-eject:before{content:"๏’"}.fa-elementor:before{content:"๏ฐ"}.fa-ellipsis-h:before{content:"๏…"}.fa-ellipsis-v:before{content:"๏…‚"}.fa-ello:before{content:"๏—ฑ"}.fa-ember:before{content:"๏ฃ"}.fa-empire:before{content:"๏‡‘"}.fa-envelope:before{content:"๏ƒ "}.fa-envelope-open:before{content:"๏Šถ"}.fa-envelope-open-text:before{content:"๏™˜"}.fa-envelope-square:before{content:"๏†™"}.fa-envira:before{content:"๏Š™"}.fa-equals:before{content:"๏”ฌ"}.fa-eraser:before{content:"๏„ญ"}.fa-erlang:before{content:"๏Ž"}.fa-ethereum:before{content:"๏ฎ"}.fa-ethernet:before{content:"๏ž–"}.fa-etsy:before{content:"๏‹—"}.fa-euro-sign:before{content:"๏…“"}.fa-evernote:before{content:"๏ น"}.fa-exchange-alt:before{content:"๏ข"}.fa-exclamation:before{content:"๏„ช"}.fa-exclamation-circle:before{content:"๏ช"}.fa-exclamation-triangle:before{content:"๏ฑ"}.fa-expand:before{content:"๏ฅ"}.fa-expand-arrows-alt:before{content:"๏Œž"}.fa-expeditedssl:before{content:"๏ˆพ"}.fa-external-link-alt:before{content:"๏"}.fa-external-link-square-alt:before{content:"๏ "}.fa-eye:before{content:"๏ฎ"}.fa-eye-dropper:before{content:"๏‡ป"}.fa-eye-slash:before{content:"๏ฐ"}.fa-facebook:before{content:"๏‚š"}.fa-facebook-f:before{content:"๏Žž"}.fa-facebook-messenger:before{content:"๏ŽŸ"}.fa-facebook-square:before{content:"๏‚‚"}.fa-fan:before{content:"๏กฃ"}.fa-fantasy-flight-games:before{content:"๏›œ"}.fa-fast-backward:before{content:"๏‰"}.fa-fast-forward:before{content:"๏"}.fa-fax:before{content:"๏†ฌ"}.fa-feather:before{content:"๏”ญ"}.fa-feather-alt:before{content:"๏•ซ"}.fa-fedex:before{content:"๏ž—"}.fa-fedora:before{content:"๏ž˜"}.fa-female:before{content:"๏†‚"}.fa-fighter-jet:before{content:"๏ƒป"}.fa-figma:before{content:"๏ž™"}.fa-file:before{content:"๏…›"}.fa-file-alt:before{content:"๏…œ"}.fa-file-archive:before{content:"๏‡†"}.fa-file-audio:before{content:"๏‡‡"}.fa-file-code:before{content:"๏‡‰"}.fa-file-contract:before{content:"๏•ฌ"}.fa-file-csv:before{content:"๏›"}.fa-file-download:before{content:"๏•ญ"}.fa-file-excel:before{content:"๏‡ƒ"}.fa-file-export:before{content:"๏•ฎ"}.fa-file-image:before{content:"๏‡…"}.fa-file-import:before{content:"๏•ฏ"}.fa-file-invoice:before{content:"๏•ฐ"}.fa-file-invoice-dollar:before{content:"๏•ฑ"}.fa-file-medical:before{content:"๏‘ท"}.fa-file-medical-alt:before{content:"๏‘ธ"}.fa-file-pdf:before{content:"๏‡"}.fa-file-powerpoint:before{content:"๏‡„"}.fa-file-prescription:before{content:"๏•ฒ"}.fa-file-signature:before{content:"๏•ณ"}.fa-file-upload:before{content:"๏•ด"}.fa-file-video:before{content:"๏‡ˆ"}.fa-file-word:before{content:"๏‡‚"}.fa-fill:before{content:"๏•ต"}.fa-fill-drip:before{content:"๏•ถ"}.fa-film:before{content:"๏€ˆ"}.fa-filter:before{content:"๏‚ฐ"}.fa-fingerprint:before{content:"๏•ท"}.fa-fire:before{content:"๏ญ"}.fa-fire-alt:before{content:"๏Ÿค"}.fa-fire-extinguisher:before{content:"๏„ด"}.fa-firefox:before{content:"๏‰ฉ"}.fa-first-aid:before{content:"๏‘น"}.fa-first-order:before{content:"๏Šฐ"}.fa-first-order-alt:before{content:"๏”Š"}.fa-firstdraft:before{content:"๏Žก"}.fa-fish:before{content:"๏•ธ"}.fa-fist-raised:before{content:"๏›ž"}.fa-flag:before{content:"๏€ค"}.fa-flag-checkered:before{content:"๏„ž"}.fa-flag-usa:before{content:"๏"}.fa-flask:before{content:"๏ƒƒ"}.fa-flickr:before{content:"๏…ฎ"}.fa-flipboard:before{content:"๏‘"}.fa-flushed:before{content:"๏•น"}.fa-fly:before{content:"๏—"}.fa-folder:before{content:"๏ป"}.fa-folder-minus:before{content:"๏™"}.fa-folder-open:before{content:"๏ผ"}.fa-folder-plus:before{content:"๏™ž"}.fa-font:before{content:"๏€ฑ"}.fa-font-awesome:before{content:"๏Šด"}.fa-font-awesome-alt:before{content:"๏œ"}.fa-font-awesome-flag:before{content:"๏ฅ"}.fa-font-awesome-logo-full:before{content:"๏“ฆ"}.fa-fonticons:before{content:"๏Š€"}.fa-fonticons-fi:before{content:"๏Žข"}.fa-football-ball:before{content:"๏‘Ž"}.fa-fort-awesome:before{content:"๏І"}.fa-fort-awesome-alt:before{content:"๏Žฃ"}.fa-forumbee:before{content:"๏ˆ‘"}.fa-forward:before{content:"๏Ž"}.fa-foursquare:before{content:"๏†€"}.fa-free-code-camp:before{content:"๏‹…"}.fa-freebsd:before{content:"๏Žค"}.fa-frog:before{content:"๏”ฎ"}.fa-frown:before{content:"๏„™"}.fa-frown-open:before{content:"๏•บ"}.fa-fulcrum:before{content:"๏”‹"}.fa-funnel-dollar:before{content:"๏™ข"}.fa-futbol:before{content:"๏‡ฃ"}.fa-galactic-republic:before{content:"๏”Œ"}.fa-galactic-senate:before{content:"๏”"}.fa-gamepad:before{content:"๏„›"}.fa-gas-pump:before{content:"๏”ฏ"}.fa-gavel:before{content:"๏ƒฃ"}.fa-gem:before{content:"๏Žฅ"}.fa-genderless:before{content:"๏ˆญ"}.fa-get-pocket:before{content:"๏‰ฅ"}.fa-gg:before{content:"๏‰ "}.fa-gg-circle:before{content:"๏‰ก"}.fa-ghost:before{content:"๏›ข"}.fa-gift:before{content:"๏ซ"}.fa-gifts:before{content:"๏žœ"}.fa-git:before{content:"๏‡“"}.fa-git-alt:before{content:"๏ก"}.fa-git-square:before{content:"๏‡’"}.fa-github:before{content:"๏‚›"}.fa-github-alt:before{content:"๏„“"}.fa-github-square:before{content:"๏‚’"}.fa-gitkraken:before{content:"๏Žฆ"}.fa-gitlab:before{content:"๏Š–"}.fa-gitter:before{content:"๏ฆ"}.fa-glass-cheers:before{content:"๏žŸ"}.fa-glass-martini:before{content:"๏€€"}.fa-glass-martini-alt:before{content:"๏•ป"}.fa-glass-whiskey:before{content:"๏ž "}.fa-glasses:before{content:"๏”ฐ"}.fa-glide:before{content:"๏Šฅ"}.fa-glide-g:before{content:"๏Šฆ"}.fa-globe:before{content:"๏‚ฌ"}.fa-globe-africa:before{content:"๏•ผ"}.fa-globe-americas:before{content:"๏•ฝ"}.fa-globe-asia:before{content:"๏•พ"}.fa-globe-europe:before{content:"๏žข"}.fa-gofore:before{content:"๏Žง"}.fa-golf-ball:before{content:"๏‘"}.fa-goodreads:before{content:"๏Žจ"}.fa-goodreads-g:before{content:"๏Žฉ"}.fa-google:before{content:"๏† "}.fa-google-drive:before{content:"๏Žช"}.fa-google-play:before{content:"๏Žซ"}.fa-google-plus:before{content:"๏Šณ"}.fa-google-plus-g:before{content:"๏ƒ•"}.fa-google-plus-square:before{content:"๏ƒ”"}.fa-google-wallet:before{content:"๏‡ฎ"}.fa-gopuram:before{content:"๏™ค"}.fa-graduation-cap:before{content:"๏†"}.fa-gratipay:before{content:"๏†„"}.fa-grav:before{content:"๏‹–"}.fa-greater-than:before{content:"๏”ฑ"}.fa-greater-than-equal:before{content:"๏”ฒ"}.fa-grimace:before{content:"๏•ฟ"}.fa-grin:before{content:"๏–€"}.fa-grin-alt:before{content:"๏–"}.fa-grin-beam:before{content:"๏–‚"}.fa-grin-beam-sweat:before{content:"๏–ƒ"}.fa-grin-hearts:before{content:"๏–„"}.fa-grin-squint:before{content:"๏–…"}.fa-grin-squint-tears:before{content:"๏–†"}.fa-grin-stars:before{content:"๏–‡"}.fa-grin-tears:before{content:"๏–ˆ"}.fa-grin-tongue:before{content:"๏–‰"}.fa-grin-tongue-squint:before{content:"๏–Š"}.fa-grin-tongue-wink:before{content:"๏–‹"}.fa-grin-wink:before{content:"๏–Œ"}.fa-grip-horizontal:before{content:"๏–"}.fa-grip-lines:before{content:"๏žค"}.fa-grip-lines-vertical:before{content:"๏žฅ"}.fa-grip-vertical:before{content:"๏–Ž"}.fa-gripfire:before{content:"๏Žฌ"}.fa-grunt:before{content:"๏Žญ"}.fa-guitar:before{content:"๏žฆ"}.fa-gulp:before{content:"๏Žฎ"}.fa-h-square:before{content:"๏ƒฝ"}.fa-hacker-news:before{content:"๏‡”"}.fa-hacker-news-square:before{content:"๏Žฏ"}.fa-hackerrank:before{content:"๏—ท"}.fa-hamburger:before{content:"๏ …"}.fa-hammer:before{content:"๏›ฃ"}.fa-hamsa:before{content:"๏™ฅ"}.fa-hand-holding:before{content:"๏’ฝ"}.fa-hand-holding-heart:before{content:"๏’พ"}.fa-hand-holding-usd:before{content:"๏“€"}.fa-hand-lizard:before{content:"๏‰˜"}.fa-hand-middle-finger:before{content:"๏ †"}.fa-hand-paper:before{content:"๏‰–"}.fa-hand-peace:before{content:"๏‰›"}.fa-hand-point-down:before{content:"๏‚ง"}.fa-hand-point-left:before{content:"๏‚ฅ"}.fa-hand-point-right:before{content:"๏‚ค"}.fa-hand-point-up:before{content:"๏‚ฆ"}.fa-hand-pointer:before{content:"๏‰š"}.fa-hand-rock:before{content:"๏‰•"}.fa-hand-scissors:before{content:"๏‰—"}.fa-hand-spock:before{content:"๏‰™"}.fa-hands:before{content:"๏“‚"}.fa-hands-helping:before{content:"๏“„"}.fa-handshake:before{content:"๏Šต"}.fa-hanukiah:before{content:"๏›ฆ"}.fa-hard-hat:before{content:"๏ ‡"}.fa-hashtag:before{content:"๏Š’"}.fa-hat-wizard:before{content:"๏›จ"}.fa-haykal:before{content:"๏™ฆ"}.fa-hdd:before{content:"๏‚ "}.fa-heading:before{content:"๏‡œ"}.fa-headphones:before{content:"๏€ฅ"}.fa-headphones-alt:before{content:"๏–"}.fa-headset:before{content:"๏–"}.fa-heart:before{content:"๏€„"}.fa-heart-broken:before{content:"๏žฉ"}.fa-heartbeat:before{content:"๏ˆž"}.fa-helicopter:before{content:"๏”ณ"}.fa-highlighter:before{content:"๏–‘"}.fa-hiking:before{content:"๏›ฌ"}.fa-hippo:before{content:"๏›ญ"}.fa-hips:before{content:"๏‘’"}.fa-hire-a-helper:before{content:"๏Žฐ"}.fa-history:before{content:"๏‡š"}.fa-hockey-puck:before{content:"๏‘“"}.fa-holly-berry:before{content:"๏žช"}.fa-home:before{content:"๏€•"}.fa-hooli:before{content:"๏ง"}.fa-hornbill:before{content:"๏–’"}.fa-horse:before{content:"๏›ฐ"}.fa-horse-head:before{content:"๏žซ"}.fa-hospital:before{content:"๏ƒธ"}.fa-hospital-alt:before{content:"๏‘ฝ"}.fa-hospital-symbol:before{content:"๏‘พ"}.fa-hot-tub:before{content:"๏–“"}.fa-hotdog:before{content:"๏ "}.fa-hotel:before{content:"๏–”"}.fa-hotjar:before{content:"๏Žฑ"}.fa-hourglass:before{content:"๏‰”"}.fa-hourglass-end:before{content:"๏‰“"}.fa-hourglass-half:before{content:"๏‰’"}.fa-hourglass-start:before{content:"๏‰‘"}.fa-house-damage:before{content:"๏›ฑ"}.fa-houzz:before{content:"๏‰ผ"}.fa-hryvnia:before{content:"๏›ฒ"}.fa-html5:before{content:"๏„ป"}.fa-hubspot:before{content:"๏Žฒ"}.fa-i-cursor:before{content:"๏‰†"}.fa-ice-cream:before{content:"๏ "}.fa-icicles:before{content:"๏žญ"}.fa-icons:before{content:"๏กญ"}.fa-id-badge:before{content:"๏‹"}.fa-id-card:before{content:"๏‹‚"}.fa-id-card-alt:before{content:"๏‘ฟ"}.fa-igloo:before{content:"๏žฎ"}.fa-image:before{content:"๏€พ"}.fa-images:before{content:"๏Œ‚"}.fa-imdb:before{content:"๏‹˜"}.fa-inbox:before{content:"๏€œ"}.fa-indent:before{content:"๏€ผ"}.fa-industry:before{content:"๏‰ต"}.fa-infinity:before{content:"๏”ด"}.fa-info:before{content:"๏„ฉ"}.fa-info-circle:before{content:"๏š"}.fa-instagram:before{content:"๏…ญ"}.fa-intercom:before{content:"๏žฏ"}.fa-internet-explorer:before{content:"๏‰ซ"}.fa-invision:before{content:"๏žฐ"}.fa-ioxhost:before{content:"๏ˆˆ"}.fa-italic:before{content:"๏€ณ"}.fa-itch-io:before{content:"๏ บ"}.fa-itunes:before{content:"๏Žด"}.fa-itunes-note:before{content:"๏Žต"}.fa-java:before{content:"๏“ค"}.fa-jedi:before{content:"๏™ฉ"}.fa-jedi-order:before{content:"๏”Ž"}.fa-jenkins:before{content:"๏Žถ"}.fa-jira:before{content:"๏žฑ"}.fa-joget:before{content:"๏Žท"}.fa-joint:before{content:"๏–•"}.fa-joomla:before{content:"๏†ช"}.fa-journal-whills:before{content:"๏™ช"}.fa-js:before{content:"๏Žธ"}.fa-js-square:before{content:"๏Žน"}.fa-jsfiddle:before{content:"๏‡Œ"}.fa-kaaba:before{content:"๏™ซ"}.fa-kaggle:before{content:"๏—บ"}.fa-key:before{content:"๏‚„"}.fa-keybase:before{content:"๏“ต"}.fa-keyboard:before{content:"๏„œ"}.fa-keycdn:before{content:"๏Žบ"}.fa-khanda:before{content:"๏™ญ"}.fa-kickstarter:before{content:"๏Žป"}.fa-kickstarter-k:before{content:"๏Žผ"}.fa-kiss:before{content:"๏––"}.fa-kiss-beam:before{content:"๏–—"}.fa-kiss-wink-heart:before{content:"๏–˜"}.fa-kiwi-bird:before{content:"๏”ต"}.fa-korvue:before{content:"๏ฏ"}.fa-landmark:before{content:"๏™ฏ"}.fa-language:before{content:"๏†ซ"}.fa-laptop:before{content:"๏„‰"}.fa-laptop-code:before{content:"๏—ผ"}.fa-laptop-medical:before{content:"๏ ’"}.fa-laravel:before{content:"๏Žฝ"}.fa-lastfm:before{content:"๏ˆ‚"}.fa-lastfm-square:before{content:"๏ˆƒ"}.fa-laugh:before{content:"๏–™"}.fa-laugh-beam:before{content:"๏–š"}.fa-laugh-squint:before{content:"๏–›"}.fa-laugh-wink:before{content:"๏–œ"}.fa-layer-group:before{content:"๏—ฝ"}.fa-leaf:before{content:"๏ฌ"}.fa-leanpub:before{content:"๏ˆ’"}.fa-lemon:before{content:"๏‚”"}.fa-less:before{content:"๏"}.fa-less-than:before{content:"๏”ถ"}.fa-less-than-equal:before{content:"๏”ท"}.fa-level-down-alt:before{content:"๏Žพ"}.fa-level-up-alt:before{content:"๏Žฟ"}.fa-life-ring:before{content:"๏‡"}.fa-lightbulb:before{content:"๏ƒซ"}.fa-line:before{content:"๏€"}.fa-link:before{content:"๏ƒ"}.fa-linkedin:before{content:"๏‚Œ"}.fa-linkedin-in:before{content:"๏ƒก"}.fa-linode:before{content:"๏Šธ"}.fa-linux:before{content:"๏…ผ"}.fa-lira-sign:before{content:"๏†•"}.fa-list:before{content:"๏€บ"}.fa-list-alt:before{content:"๏€ข"}.fa-list-ol:before{content:"๏ƒ‹"}.fa-list-ul:before{content:"๏ƒŠ"}.fa-location-arrow:before{content:"๏„ค"}.fa-lock:before{content:"๏€ฃ"}.fa-lock-open:before{content:"๏"}.fa-long-arrow-alt-down:before{content:"๏Œ‰"}.fa-long-arrow-alt-left:before{content:"๏ŒŠ"}.fa-long-arrow-alt-right:before{content:"๏Œ‹"}.fa-long-arrow-alt-up:before{content:"๏ŒŒ"}.fa-low-vision:before{content:"๏Šจ"}.fa-luggage-cart:before{content:"๏–"}.fa-lyft:before{content:"๏ƒ"}.fa-magento:before{content:"๏„"}.fa-magic:before{content:"๏ƒ"}.fa-magnet:before{content:"๏ถ"}.fa-mail-bulk:before{content:"๏™ด"}.fa-mailchimp:before{content:"๏–ž"}.fa-male:before{content:"๏†ƒ"}.fa-mandalorian:before{content:"๏”"}.fa-map:before{content:"๏‰น"}.fa-map-marked:before{content:"๏–Ÿ"}.fa-map-marked-alt:before{content:"๏– "}.fa-map-marker:before{content:"๏"}.fa-map-marker-alt:before{content:"๏…"}.fa-map-pin:before{content:"๏‰ถ"}.fa-map-signs:before{content:"๏‰ท"}.fa-markdown:before{content:"๏˜"}.fa-marker:before{content:"๏–ก"}.fa-mars:before{content:"๏ˆข"}.fa-mars-double:before{content:"๏ˆง"}.fa-mars-stroke:before{content:"๏ˆฉ"}.fa-mars-stroke-h:before{content:"๏ˆซ"}.fa-mars-stroke-v:before{content:"๏ˆช"}.fa-mask:before{content:"๏›บ"}.fa-mastodon:before{content:"๏“ถ"}.fa-maxcdn:before{content:"๏„ถ"}.fa-medal:before{content:"๏–ข"}.fa-medapps:before{content:"๏†"}.fa-medium:before{content:"๏ˆบ"}.fa-medium-m:before{content:"๏‡"}.fa-medkit:before{content:"๏ƒบ"}.fa-medrt:before{content:"๏ˆ"}.fa-meetup:before{content:"๏‹ "}.fa-megaport:before{content:"๏–ฃ"}.fa-meh:before{content:"๏„š"}.fa-meh-blank:before{content:"๏–ค"}.fa-meh-rolling-eyes:before{content:"๏–ฅ"}.fa-memory:before{content:"๏”ธ"}.fa-mendeley:before{content:"๏žณ"}.fa-menorah:before{content:"๏™ถ"}.fa-mercury:before{content:"๏ˆฃ"}.fa-meteor:before{content:"๏“"}.fa-microchip:before{content:"๏‹›"}.fa-microphone:before{content:"๏„ฐ"}.fa-microphone-alt:before{content:"๏‰"}.fa-microphone-alt-slash:before{content:"๏”น"}.fa-microphone-slash:before{content:"๏„ฑ"}.fa-microscope:before{content:"๏˜"}.fa-microsoft:before{content:"๏Š"}.fa-minus:before{content:"๏จ"}.fa-minus-circle:before{content:"๏–"}.fa-minus-square:before{content:"๏…†"}.fa-mitten:before{content:"๏žต"}.fa-mix:before{content:"๏‹"}.fa-mixcloud:before{content:"๏Љ"}.fa-mizuni:before{content:"๏Œ"}.fa-mobile:before{content:"๏„‹"}.fa-mobile-alt:before{content:"๏"}.fa-modx:before{content:"๏Š…"}.fa-monero:before{content:"๏"}.fa-money-bill:before{content:"๏ƒ–"}.fa-money-bill-alt:before{content:"๏‘"}.fa-money-bill-wave:before{content:"๏”บ"}.fa-money-bill-wave-alt:before{content:"๏”ป"}.fa-money-check:before{content:"๏”ผ"}.fa-money-check-alt:before{content:"๏”ฝ"}.fa-monument:before{content:"๏–ฆ"}.fa-moon:before{content:"๏††"}.fa-mortar-pestle:before{content:"๏–ง"}.fa-mosque:before{content:"๏™ธ"}.fa-motorcycle:before{content:"๏ˆœ"}.fa-mountain:before{content:"๏›ผ"}.fa-mouse-pointer:before{content:"๏‰…"}.fa-mug-hot:before{content:"๏žถ"}.fa-music:before{content:"๏€"}.fa-napster:before{content:"๏’"}.fa-neos:before{content:"๏˜’"}.fa-network-wired:before{content:"๏›ฟ"}.fa-neuter:before{content:"๏ˆฌ"}.fa-newspaper:before{content:"๏‡ช"}.fa-nimblr:before{content:"๏–จ"}.fa-node:before{content:"๏™"}.fa-node-js:before{content:"๏“"}.fa-not-equal:before{content:"๏”พ"}.fa-notes-medical:before{content:"๏’"}.fa-npm:before{content:"๏”"}.fa-ns8:before{content:"๏•"}.fa-nutritionix:before{content:"๏–"}.fa-object-group:before{content:"๏‰‡"}.fa-object-ungroup:before{content:"๏‰ˆ"}.fa-odnoklassniki:before{content:"๏‰ฃ"}.fa-odnoklassniki-square:before{content:"๏‰ค"}.fa-oil-can:before{content:"๏˜“"}.fa-old-republic:before{content:"๏”"}.fa-om:before{content:"๏™น"}.fa-opencart:before{content:"๏ˆฝ"}.fa-openid:before{content:"๏†›"}.fa-opera:before{content:"๏‰ช"}.fa-optin-monster:before{content:"๏ˆผ"}.fa-osi:before{content:"๏š"}.fa-otter:before{content:"๏œ€"}.fa-outdent:before{content:"๏€ป"}.fa-page4:before{content:"๏—"}.fa-pagelines:before{content:"๏†Œ"}.fa-pager:before{content:"๏ •"}.fa-paint-brush:before{content:"๏‡ผ"}.fa-paint-roller:before{content:"๏–ช"}.fa-palette:before{content:"๏”ฟ"}.fa-palfed:before{content:"๏˜"}.fa-pallet:before{content:"๏’‚"}.fa-paper-plane:before{content:"๏‡˜"}.fa-paperclip:before{content:"๏ƒ†"}.fa-parachute-box:before{content:"๏“"}.fa-paragraph:before{content:"๏‡"}.fa-parking:before{content:"๏•€"}.fa-passport:before{content:"๏–ซ"}.fa-pastafarianism:before{content:"๏™ป"}.fa-paste:before{content:"๏ƒช"}.fa-patreon:before{content:"๏™"}.fa-pause:before{content:"๏Œ"}.fa-pause-circle:before{content:"๏Š‹"}.fa-paw:before{content:"๏†ฐ"}.fa-paypal:before{content:"๏‡ญ"}.fa-peace:before{content:"๏™ผ"}.fa-pen:before{content:"๏Œ„"}.fa-pen-alt:before{content:"๏Œ…"}.fa-pen-fancy:before{content:"๏–ฌ"}.fa-pen-nib:before{content:"๏–ญ"}.fa-pen-square:before{content:"๏…‹"}.fa-pencil-alt:before{content:"๏Œƒ"}.fa-pencil-ruler:before{content:"๏–ฎ"}.fa-penny-arcade:before{content:"๏œ„"}.fa-people-carry:before{content:"๏“Ž"}.fa-pepper-hot:before{content:"๏ –"}.fa-percent:before{content:"๏Š•"}.fa-percentage:before{content:"๏•"}.fa-periscope:before{content:"๏š"}.fa-person-booth:before{content:"๏–"}.fa-phabricator:before{content:"๏›"}.fa-phoenix-framework:before{content:"๏œ"}.fa-phoenix-squadron:before{content:"๏”‘"}.fa-phone:before{content:"๏‚•"}.fa-phone-alt:before{content:"๏กน"}.fa-phone-slash:before{content:"๏"}.fa-phone-square:before{content:"๏‚˜"}.fa-phone-square-alt:before{content:"๏กป"}.fa-phone-volume:before{content:"๏Š "}.fa-photo-video:before{content:"๏กผ"}.fa-php:before{content:"๏‘—"}.fa-pied-piper:before{content:"๏Šฎ"}.fa-pied-piper-alt:before{content:"๏†จ"}.fa-pied-piper-hat:before{content:"๏“ฅ"}.fa-pied-piper-pp:before{content:"๏†ง"}.fa-piggy-bank:before{content:"๏““"}.fa-pills:before{content:"๏’„"}.fa-pinterest:before{content:"๏ƒ’"}.fa-pinterest-p:before{content:"๏ˆฑ"}.fa-pinterest-square:before{content:"๏ƒ“"}.fa-pizza-slice:before{content:"๏ ˜"}.fa-place-of-worship:before{content:"๏™ฟ"}.fa-plane:before{content:"๏ฒ"}.fa-plane-arrival:before{content:"๏–ฏ"}.fa-plane-departure:before{content:"๏–ฐ"}.fa-play:before{content:"๏‹"}.fa-play-circle:before{content:"๏…„"}.fa-playstation:before{content:"๏Ÿ"}.fa-plug:before{content:"๏‡ฆ"}.fa-plus:before{content:"๏ง"}.fa-plus-circle:before{content:"๏•"}.fa-plus-square:before{content:"๏ƒพ"}.fa-podcast:before{content:"๏‹Ž"}.fa-poll:before{content:"๏š"}.fa-poll-h:before{content:"๏š‚"}.fa-poo:before{content:"๏‹พ"}.fa-poo-storm:before{content:"๏š"}.fa-poop:before{content:"๏˜™"}.fa-portrait:before{content:"๏ "}.fa-pound-sign:before{content:"๏…”"}.fa-power-off:before{content:"๏€‘"}.fa-pray:before{content:"๏šƒ"}.fa-praying-hands:before{content:"๏š„"}.fa-prescription:before{content:"๏–ฑ"}.fa-prescription-bottle:before{content:"๏’…"}.fa-prescription-bottle-alt:before{content:"๏’†"}.fa-print:before{content:"๏€ฏ"}.fa-procedures:before{content:"๏’‡"}.fa-product-hunt:before{content:"๏Šˆ"}.fa-project-diagram:before{content:"๏•‚"}.fa-pushed:before{content:"๏ก"}.fa-puzzle-piece:before{content:"๏„ฎ"}.fa-python:before{content:"๏ข"}.fa-qq:before{content:"๏‡–"}.fa-qrcode:before{content:"๏€ฉ"}.fa-question:before{content:"๏„จ"}.fa-question-circle:before{content:"๏™"}.fa-quidditch:before{content:"๏‘˜"}.fa-quinscape:before{content:"๏‘™"}.fa-quora:before{content:"๏‹„"}.fa-quote-left:before{content:"๏„"}.fa-quote-right:before{content:"๏„Ž"}.fa-quran:before{content:"๏š‡"}.fa-r-project:before{content:"๏“ท"}.fa-radiation:before{content:"๏žน"}.fa-radiation-alt:before{content:"๏žบ"}.fa-rainbow:before{content:"๏›"}.fa-random:before{content:"๏ด"}.fa-raspberry-pi:before{content:"๏žป"}.fa-ravelry:before{content:"๏‹™"}.fa-react:before{content:"๏›"}.fa-reacteurope:before{content:"๏"}.fa-readme:before{content:"๏“•"}.fa-rebel:before{content:"๏‡"}.fa-receipt:before{content:"๏•ƒ"}.fa-recycle:before{content:"๏†ธ"}.fa-red-river:before{content:"๏ฃ"}.fa-reddit:before{content:"๏†ก"}.fa-reddit-alien:before{content:"๏Ё"}.fa-reddit-square:before{content:"๏†ข"}.fa-redhat:before{content:"๏žผ"}.fa-redo:before{content:"๏€ž"}.fa-redo-alt:before{content:"๏‹น"}.fa-registered:before{content:"๏‰"}.fa-remove-format:before{content:"๏กฝ"}.fa-renren:before{content:"๏†‹"}.fa-reply:before{content:"๏ฅ"}.fa-reply-all:before{content:"๏„ข"}.fa-replyd:before{content:"๏ฆ"}.fa-republican:before{content:"๏ž"}.fa-researchgate:before{content:"๏“ธ"}.fa-resolving:before{content:"๏ง"}.fa-restroom:before{content:"๏žฝ"}.fa-retweet:before{content:"๏น"}.fa-rev:before{content:"๏–ฒ"}.fa-ribbon:before{content:"๏“–"}.fa-ring:before{content:"๏œ‹"}.fa-road:before{content:"๏€˜"}.fa-robot:before{content:"๏•„"}.fa-rocket:before{content:"๏„ต"}.fa-rocketchat:before{content:"๏จ"}.fa-rockrms:before{content:"๏ฉ"}.fa-route:before{content:"๏“—"}.fa-rss:before{content:"๏‚ž"}.fa-rss-square:before{content:"๏…ƒ"}.fa-ruble-sign:before{content:"๏…˜"}.fa-ruler:before{content:"๏•…"}.fa-ruler-combined:before{content:"๏•†"}.fa-ruler-horizontal:before{content:"๏•‡"}.fa-ruler-vertical:before{content:"๏•ˆ"}.fa-running:before{content:"๏œŒ"}.fa-rupee-sign:before{content:"๏…–"}.fa-sad-cry:before{content:"๏–ณ"}.fa-sad-tear:before{content:"๏–ด"}.fa-safari:before{content:"๏‰ง"}.fa-salesforce:before{content:"๏ ป"}.fa-sass:before{content:"๏ž"}.fa-satellite:before{content:"๏žฟ"}.fa-satellite-dish:before{content:"๏Ÿ€"}.fa-save:before{content:"๏ƒ‡"}.fa-schlix:before{content:"๏ช"}.fa-school:before{content:"๏•‰"}.fa-screwdriver:before{content:"๏•Š"}.fa-scribd:before{content:"๏ŠŠ"}.fa-scroll:before{content:"๏œŽ"}.fa-sd-card:before{content:"๏Ÿ‚"}.fa-search:before{content:"๏€‚"}.fa-search-dollar:before{content:"๏šˆ"}.fa-search-location:before{content:"๏š‰"}.fa-search-minus:before{content:"๏€"}.fa-search-plus:before{content:"๏€Ž"}.fa-searchengin:before{content:"๏ซ"}.fa-seedling:before{content:"๏“˜"}.fa-sellcast:before{content:"๏‹š"}.fa-sellsy:before{content:"๏ˆ“"}.fa-server:before{content:"๏ˆณ"}.fa-servicestack:before{content:"๏ฌ"}.fa-shapes:before{content:"๏˜Ÿ"}.fa-share:before{content:"๏ค"}.fa-share-alt:before{content:"๏‡ "}.fa-share-alt-square:before{content:"๏‡ก"}.fa-share-square:before{content:"๏…"}.fa-shekel-sign:before{content:"๏ˆ‹"}.fa-shield-alt:before{content:"๏ญ"}.fa-ship:before{content:"๏ˆš"}.fa-shipping-fast:before{content:"๏’‹"}.fa-shirtsinbulk:before{content:"๏ˆ”"}.fa-shoe-prints:before{content:"๏•‹"}.fa-shopping-bag:before{content:"๏А"}.fa-shopping-basket:before{content:"๏Š‘"}.fa-shopping-cart:before{content:"๏บ"}.fa-shopware:before{content:"๏–ต"}.fa-shower:before{content:"๏‹Œ"}.fa-shuttle-van:before{content:"๏–ถ"}.fa-sign:before{content:"๏“™"}.fa-sign-in-alt:before{content:"๏‹ถ"}.fa-sign-language:before{content:"๏Šง"}.fa-sign-out-alt:before{content:"๏‹ต"}.fa-signal:before{content:"๏€’"}.fa-signature:before{content:"๏–ท"}.fa-sim-card:before{content:"๏Ÿ„"}.fa-simplybuilt:before{content:"๏ˆ•"}.fa-sistrix:before{content:"๏ฎ"}.fa-sitemap:before{content:"๏ƒจ"}.fa-sith:before{content:"๏”’"}.fa-skating:before{content:"๏Ÿ…"}.fa-sketch:before{content:"๏Ÿ†"}.fa-skiing:before{content:"๏Ÿ‰"}.fa-skiing-nordic:before{content:"๏ŸŠ"}.fa-skull:before{content:"๏•Œ"}.fa-skull-crossbones:before{content:"๏œ”"}.fa-skyatlas:before{content:"๏ˆ–"}.fa-skype:before{content:"๏…พ"}.fa-slack:before{content:"๏†˜"}.fa-slack-hash:before{content:"๏ฏ"}.fa-slash:before{content:"๏œ•"}.fa-sleigh:before{content:"๏ŸŒ"}.fa-sliders-h:before{content:"๏‡ž"}.fa-slideshare:before{content:"๏‡ง"}.fa-smile:before{content:"๏„˜"}.fa-smile-beam:before{content:"๏–ธ"}.fa-smile-wink:before{content:"๏“š"}.fa-smog:before{content:"๏Ÿ"}.fa-smoking:before{content:"๏’"}.fa-smoking-ban:before{content:"๏•"}.fa-sms:before{content:"๏Ÿ"}.fa-snapchat:before{content:"๏Šซ"}.fa-snapchat-ghost:before{content:"๏Šฌ"}.fa-snapchat-square:before{content:"๏Šญ"}.fa-snowboarding:before{content:"๏ŸŽ"}.fa-snowflake:before{content:"๏‹œ"}.fa-snowman:before{content:"๏Ÿ"}.fa-snowplow:before{content:"๏Ÿ’"}.fa-socks:before{content:"๏š–"}.fa-solar-panel:before{content:"๏–บ"}.fa-sort:before{content:"๏ƒœ"}.fa-sort-alpha-down:before{content:"๏…"}.fa-sort-alpha-down-alt:before{content:"๏ข"}.fa-sort-alpha-up:before{content:"๏…ž"}.fa-sort-alpha-up-alt:before{content:"๏ข‚"}.fa-sort-amount-down:before{content:"๏… "}.fa-sort-amount-down-alt:before{content:"๏ข„"}.fa-sort-amount-up:before{content:"๏…ก"}.fa-sort-amount-up-alt:before{content:"๏ข…"}.fa-sort-down:before{content:"๏ƒ"}.fa-sort-numeric-down:before{content:"๏…ข"}.fa-sort-numeric-down-alt:before{content:"๏ข†"}.fa-sort-numeric-up:before{content:"๏…ฃ"}.fa-sort-numeric-up-alt:before{content:"๏ข‡"}.fa-sort-up:before{content:"๏ƒž"}.fa-soundcloud:before{content:"๏†พ"}.fa-sourcetree:before{content:"๏Ÿ“"}.fa-spa:before{content:"๏–ป"}.fa-space-shuttle:before{content:"๏†—"}.fa-speakap:before{content:"๏ณ"}.fa-speaker-deck:before{content:"๏ ผ"}.fa-spell-check:before{content:"๏ข‘"}.fa-spider:before{content:"๏œ—"}.fa-spinner:before{content:"๏„"}.fa-splotch:before{content:"๏–ผ"}.fa-spotify:before{content:"๏†ผ"}.fa-spray-can:before{content:"๏–ฝ"}.fa-square:before{content:"๏ƒˆ"}.fa-square-full:before{content:"๏‘œ"}.fa-square-root-alt:before{content:"๏š˜"}.fa-squarespace:before{content:"๏–พ"}.fa-stack-exchange:before{content:"๏†"}.fa-stack-overflow:before{content:"๏…ฌ"}.fa-stackpath:before{content:"๏ก‚"}.fa-stamp:before{content:"๏–ฟ"}.fa-star:before{content:"๏€…"}.fa-star-and-crescent:before{content:"๏š™"}.fa-star-half:before{content:"๏‚‰"}.fa-star-half-alt:before{content:"๏—€"}.fa-star-of-david:before{content:"๏šš"}.fa-star-of-life:before{content:"๏˜ก"}.fa-staylinked:before{content:"๏ต"}.fa-steam:before{content:"๏†ถ"}.fa-steam-square:before{content:"๏†ท"}.fa-steam-symbol:before{content:"๏ถ"}.fa-step-backward:before{content:"๏ˆ"}.fa-step-forward:before{content:"๏‘"}.fa-stethoscope:before{content:"๏ƒฑ"}.fa-sticker-mule:before{content:"๏ท"}.fa-sticky-note:before{content:"๏‰‰"}.fa-stop:before{content:"๏"}.fa-stop-circle:before{content:"๏Š"}.fa-stopwatch:before{content:"๏‹ฒ"}.fa-store:before{content:"๏•Ž"}.fa-store-alt:before{content:"๏•"}.fa-strava:before{content:"๏จ"}.fa-stream:before{content:"๏•"}.fa-street-view:before{content:"๏ˆ"}.fa-strikethrough:before{content:"๏ƒŒ"}.fa-stripe:before{content:"๏ฉ"}.fa-stripe-s:before{content:"๏ช"}.fa-stroopwafel:before{content:"๏•‘"}.fa-studiovinari:before{content:"๏ธ"}.fa-stumbleupon:before{content:"๏†ค"}.fa-stumbleupon-circle:before{content:"๏†ฃ"}.fa-subscript:before{content:"๏„ฌ"}.fa-subway:before{content:"๏ˆน"}.fa-suitcase:before{content:"๏ƒฒ"}.fa-suitcase-rolling:before{content:"๏—"}.fa-sun:before{content:"๏†…"}.fa-superpowers:before{content:"๏‹"}.fa-superscript:before{content:"๏„ซ"}.fa-supple:before{content:"๏น"}.fa-surprise:before{content:"๏—‚"}.fa-suse:before{content:"๏Ÿ–"}.fa-swatchbook:before{content:"๏—ƒ"}.fa-swimmer:before{content:"๏—„"}.fa-swimming-pool:before{content:"๏—…"}.fa-symfony:before{content:"๏ ฝ"}.fa-synagogue:before{content:"๏š›"}.fa-sync:before{content:"๏€ก"}.fa-sync-alt:before{content:"๏‹ฑ"}.fa-syringe:before{content:"๏’Ž"}.fa-table:before{content:"๏ƒŽ"}.fa-table-tennis:before{content:"๏‘"}.fa-tablet:before{content:"๏„Š"}.fa-tablet-alt:before{content:"๏บ"}.fa-tablets:before{content:"๏’"}.fa-tachometer-alt:before{content:"๏ฝ"}.fa-tag:before{content:"๏€ซ"}.fa-tags:before{content:"๏€ฌ"}.fa-tape:before{content:"๏“›"}.fa-tasks:before{content:"๏‚ฎ"}.fa-taxi:before{content:"๏†บ"}.fa-teamspeak:before{content:"๏“น"}.fa-teeth:before{content:"๏˜ฎ"}.fa-teeth-open:before{content:"๏˜ฏ"}.fa-telegram:before{content:"๏‹†"}.fa-telegram-plane:before{content:"๏พ"}.fa-temperature-high:before{content:"๏ฉ"}.fa-temperature-low:before{content:"๏ซ"}.fa-tencent-weibo:before{content:"๏‡•"}.fa-tenge:before{content:"๏Ÿ—"}.fa-terminal:before{content:"๏„ "}.fa-text-height:before{content:"๏€ด"}.fa-text-width:before{content:"๏€ต"}.fa-th:before{content:"๏€Š"}.fa-th-large:before{content:"๏€‰"}.fa-th-list:before{content:"๏€‹"}.fa-the-red-yeti:before{content:"๏š"}.fa-theater-masks:before{content:"๏˜ฐ"}.fa-themeco:before{content:"๏—†"}.fa-themeisle:before{content:"๏Šฒ"}.fa-thermometer:before{content:"๏’‘"}.fa-thermometer-empty:before{content:"๏‹‹"}.fa-thermometer-full:before{content:"๏‹‡"}.fa-thermometer-half:before{content:"๏‹‰"}.fa-thermometer-quarter:before{content:"๏‹Š"}.fa-thermometer-three-quarters:before{content:"๏‹ˆ"}.fa-think-peaks:before{content:"๏œฑ"}.fa-thumbs-down:before{content:"๏…ฅ"}.fa-thumbs-up:before{content:"๏…ค"}.fa-thumbtack:before{content:"๏‚"}.fa-ticket-alt:before{content:"๏ฟ"}.fa-times:before{content:"๏€"}.fa-times-circle:before{content:"๏—"}.fa-tint:before{content:"๏ƒ"}.fa-tint-slash:before{content:"๏—‡"}.fa-tired:before{content:"๏—ˆ"}.fa-toggle-off:before{content:"๏ˆ„"}.fa-toggle-on:before{content:"๏ˆ…"}.fa-toilet:before{content:"๏Ÿ˜"}.fa-toilet-paper:before{content:"๏œž"}.fa-toolbox:before{content:"๏•’"}.fa-tools:before{content:"๏Ÿ™"}.fa-tooth:before{content:"๏—‰"}.fa-torah:before{content:"๏š "}.fa-torii-gate:before{content:"๏šก"}.fa-tractor:before{content:"๏œข"}.fa-trade-federation:before{content:"๏”“"}.fa-trademark:before{content:"๏‰œ"}.fa-traffic-light:before{content:"๏˜ท"}.fa-train:before{content:"๏ˆธ"}.fa-tram:before{content:"๏Ÿš"}.fa-transgender:before{content:"๏ˆค"}.fa-transgender-alt:before{content:"๏ˆฅ"}.fa-trash:before{content:"๏‡ธ"}.fa-trash-alt:before{content:"๏‹ญ"}.fa-trash-restore:before{content:"๏ ฉ"}.fa-trash-restore-alt:before{content:"๏ ช"}.fa-tree:before{content:"๏†ป"}.fa-trello:before{content:"๏†"}.fa-tripadvisor:before{content:"๏‰ข"}.fa-trophy:before{content:"๏‚‘"}.fa-truck:before{content:"๏ƒ‘"}.fa-truck-loading:before{content:"๏“ž"}.fa-truck-monster:before{content:"๏˜ป"}.fa-truck-moving:before{content:"๏“Ÿ"}.fa-truck-pickup:before{content:"๏˜ผ"}.fa-tshirt:before{content:"๏•“"}.fa-tty:before{content:"๏‡ค"}.fa-tumblr:before{content:"๏…ณ"}.fa-tumblr-square:before{content:"๏…ด"}.fa-tv:before{content:"๏‰ฌ"}.fa-twitch:before{content:"๏‡จ"}.fa-twitter:before{content:"๏‚™"}.fa-twitter-square:before{content:"๏‚"}.fa-typo3:before{content:"๏ซ"}.fa-uber:before{content:"๏‚"}.fa-ubuntu:before{content:"๏ŸŸ"}.fa-uikit:before{content:"๏ƒ"}.fa-umbrella:before{content:"๏ƒฉ"}.fa-umbrella-beach:before{content:"๏—Š"}.fa-underline:before{content:"๏ƒ"}.fa-undo:before{content:"๏ƒข"}.fa-undo-alt:before{content:"๏‹ช"}.fa-uniregistry:before{content:"๏„"}.fa-universal-access:before{content:"๏Šš"}.fa-university:before{content:"๏†œ"}.fa-unlink:before{content:"๏„ง"}.fa-unlock:before{content:"๏‚œ"}.fa-unlock-alt:before{content:"๏„พ"}.fa-untappd:before{content:"๏…"}.fa-upload:before{content:"๏‚“"}.fa-ups:before{content:"๏Ÿ "}.fa-usb:before{content:"๏Ї"}.fa-user:before{content:"๏€‡"}.fa-user-alt:before{content:"๏†"}.fa-user-alt-slash:before{content:"๏“บ"}.fa-user-astronaut:before{content:"๏“ป"}.fa-user-check:before{content:"๏“ผ"}.fa-user-circle:before{content:"๏Šฝ"}.fa-user-clock:before{content:"๏“ฝ"}.fa-user-cog:before{content:"๏“พ"}.fa-user-edit:before{content:"๏“ฟ"}.fa-user-friends:before{content:"๏”€"}.fa-user-graduate:before{content:"๏”"}.fa-user-injured:before{content:"๏œจ"}.fa-user-lock:before{content:"๏”‚"}.fa-user-md:before{content:"๏ƒฐ"}.fa-user-minus:before{content:"๏”ƒ"}.fa-user-ninja:before{content:"๏”„"}.fa-user-nurse:before{content:"๏ ฏ"}.fa-user-plus:before{content:"๏ˆด"}.fa-user-secret:before{content:"๏ˆ›"}.fa-user-shield:before{content:"๏”…"}.fa-user-slash:before{content:"๏”†"}.fa-user-tag:before{content:"๏”‡"}.fa-user-tie:before{content:"๏”ˆ"}.fa-user-times:before{content:"๏ˆต"}.fa-users:before{content:"๏ƒ€"}.fa-users-cog:before{content:"๏”‰"}.fa-usps:before{content:"๏Ÿก"}.fa-ussunnah:before{content:"๏‡"}.fa-utensil-spoon:before{content:"๏‹ฅ"}.fa-utensils:before{content:"๏‹ง"}.fa-vaadin:before{content:"๏ˆ"}.fa-vector-square:before{content:"๏—‹"}.fa-venus:before{content:"๏ˆก"}.fa-venus-double:before{content:"๏ˆฆ"}.fa-venus-mars:before{content:"๏ˆจ"}.fa-viacoin:before{content:"๏ˆท"}.fa-viadeo:before{content:"๏Šฉ"}.fa-viadeo-square:before{content:"๏Šช"}.fa-vial:before{content:"๏’’"}.fa-vials:before{content:"๏’“"}.fa-viber:before{content:"๏‰"}.fa-video:before{content:"๏€ฝ"}.fa-video-slash:before{content:"๏“ข"}.fa-vihara:before{content:"๏šง"}.fa-vimeo:before{content:"๏Š"}.fa-vimeo-square:before{content:"๏†”"}.fa-vimeo-v:before{content:"๏‰ฝ"}.fa-vine:before{content:"๏‡Š"}.fa-vk:before{content:"๏†‰"}.fa-vnv:before{content:"๏‹"}.fa-voicemail:before{content:"๏ข—"}.fa-volleyball-ball:before{content:"๏‘Ÿ"}.fa-volume-down:before{content:"๏€ง"}.fa-volume-mute:before{content:"๏šฉ"}.fa-volume-off:before{content:"๏€ฆ"}.fa-volume-up:before{content:"๏€จ"}.fa-vote-yea:before{content:"๏ฒ"}.fa-vr-cardboard:before{content:"๏œฉ"}.fa-vuejs:before{content:"๏Ÿ"}.fa-walking:before{content:"๏•”"}.fa-wallet:before{content:"๏••"}.fa-warehouse:before{content:"๏’”"}.fa-water:before{content:"๏ณ"}.fa-wave-square:before{content:"๏ พ"}.fa-waze:before{content:"๏ ฟ"}.fa-weebly:before{content:"๏—Œ"}.fa-weibo:before{content:"๏†Š"}.fa-weight:before{content:"๏’–"}.fa-weight-hanging:before{content:"๏—"}.fa-weixin:before{content:"๏‡—"}.fa-whatsapp:before{content:"๏ˆฒ"}.fa-whatsapp-square:before{content:"๏Œ"}.fa-wheelchair:before{content:"๏†“"}.fa-whmcs:before{content:"๏"}.fa-wifi:before{content:"๏‡ซ"}.fa-wikipedia-w:before{content:"๏‰ฆ"}.fa-wind:before{content:"๏œฎ"}.fa-window-close:before{content:"๏"}.fa-window-maximize:before{content:"๏‹"}.fa-window-minimize:before{content:"๏‹‘"}.fa-window-restore:before{content:"๏‹’"}.fa-windows:before{content:"๏…บ"}.fa-wine-bottle:before{content:"๏œฏ"}.fa-wine-glass:before{content:"๏“ฃ"}.fa-wine-glass-alt:before{content:"๏—Ž"}.fa-wix:before{content:"๏—"}.fa-wizards-of-the-coast:before{content:"๏œฐ"}.fa-wolf-pack-battalion:before{content:"๏””"}.fa-won-sign:before{content:"๏…™"}.fa-wordpress:before{content:"๏†š"}.fa-wordpress-simple:before{content:"๏‘"}.fa-wpbeginner:before{content:"๏Š—"}.fa-wpexplorer:before{content:"๏‹ž"}.fa-wpforms:before{content:"๏Š˜"}.fa-wpressr:before{content:"๏ค"}.fa-wrench:before{content:"๏‚ญ"}.fa-x-ray:before{content:"๏’—"}.fa-xbox:before{content:"๏’"}.fa-xing:before{content:"๏…จ"}.fa-xing-square:before{content:"๏…ฉ"}.fa-y-combinator:before{content:"๏ˆป"}.fa-yahoo:before{content:"๏†ž"}.fa-yammer:before{content:"๏ก€"}.fa-yandex:before{content:"๏“"}.fa-yandex-international:before{content:"๏”"}.fa-yarn:before{content:"๏Ÿฃ"}.fa-yelp:before{content:"๏‡ฉ"}.fa-yen-sign:before{content:"๏…—"}.fa-yin-yang:before{content:"๏šญ"}.fa-yoast:before{content:"๏Šฑ"}.fa-youtube:before{content:"๏…ง"}.fa-youtube-square:before{content:"๏ฑ"}.fa-zhihu:before{content:"๏˜ฟ"}.sr-only{border:0;clip:rect(0, 0, 0, 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}/*! * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url("./webfonts/fa-solid-900.eot");src:url("./webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"),url("./webfonts/fa-solid-900.woff2") format("woff2"),url("./webfonts/fa-solid-900.woff") format("woff"),url("./webfonts/fa-solid-900.ttf") format("truetype"),url("./webfonts/fa-solid-900.svg#fontawesome") format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900}html{font-size:87.5%;box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}body,h1,h2,h3,h4,h5,h6,p,figure,blockquote,dl,dd{margin:0;padding:0}ul[role=list],ol[role=list]{list-style:none}html:focus-within{scroll-behavior:smooth}body{text-rendering:optimizeSpeed;line-height:1.5;font-family:"Roboto",Arial,Helvetica,sans-serif !important;letter-spacing:-0.025ch}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}@media(prefers-reduced-motion: reduce){html:focus-within{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}}#main{height:100vh}.content{display:flex;height:100%;overflow:hidden}.tab-content{display:flex;height:100%;width:100%;background-color:#eef3f6;animation:fadein .3s;overflow:auto}input[type=text],input[type=password],input[type=number],textarea{box-sizing:border-box;background:#fff;max-width:100%;font-size:1rem;font-weight:400;border:1px solid #ccc;border-radius:.25rem;padding:.25rem .5rem;outline:rgba(0,0,0,0)}input:focus{border:1px solid #3ba4d7;box-shadow:inset 0 0 5px #ccc}input.stretched{width:90%}input.small{max-width:70%;padding:.1rem}input.searchbar{width:40%}a{cursor:pointer}a[title=Back]{width:max-content;height:max-content;padding:.475rem .75rem;border-radius:50%;transition:100ms}a[title=Back]:hover{background:#eef3f6}table{padding:20px;table-layout:fixed;width:100%;border-collapse:collapse;text-align:center;color:#333;font-size:1.125rem}table th{font-size:1.125rem;color:#000;border-bottom:2px solid #eee}table tr{border-bottom:1px solid #eee}h3{color:#444}hr{margin-left:0;color:#aaa}.grid-2col{display:grid;grid-template-columns:auto auto;gap:1rem;justify-content:start}.grid-2col input[type=checkbox]{margin-top:20px}.error{color:red}.tooltip{color:#333;position:relative;display:inline-block;margin:0 .25rem}.tooltiptext{visibility:hidden;position:absolute;top:100%;left:50%;min-width:250px;margin-left:-120px;z-index:1;color:#ccc;background-color:#333;font-size:.875rem;text-align:center;padding:.25rem;border-radius:.5rem}.tooltip:hover .tooltiptext{visibility:visible;animation:fadein .5s}blockquote{color:#14141b;padding:.75rem 1rem .75rem 2rem;border-radius:.25rem}blockquote.info{position:relative;line-height:1.2;color:rgba(20,20,27,.8);border:1px solid rgba(17,143,204,.8)}blockquote.info::before{font-family:"Font Awesome 5 Free";position:absolute;top:.5rem;left:.5rem;content:"๏š";color:#019dff}@keyframes fadein{from{opacity:0}to{opacity:1}}.fadein{animation:fadein .5s}@keyframes swipe-from-left{from{margin-left:100%}to{margin-left:0}}button{width:max-content;height:max-content;color:#fff;background:#019dff;font-size:1rem;padding:.4rem 1rem;border:0;border-radius:5px;cursor:pointer;box-shadow:inset -3px -3px 0 rgb(0,94.5826771654,154)}button:active{outline:none;box-shadow:inset 3px 3px 0 rgb(0,94.5826771654,154)}button.red{width:max-content;height:max-content;color:#fff;background:#ff3a4a;font-size:1rem;padding:.4rem 1rem;border:0;border-radius:5px;cursor:pointer;box-shadow:inset -3px -3px 0 rgb(211,0,17.1370558376)}button.red:active{outline:none;box-shadow:inset 3px 3px 0 rgb(211,0,17.1370558376)}.media-item{display:flex;margin-top:.5rem;padding:1rem;border:1px solid rgba(20,20,27,.1);border-radius:4px}.media-item__details{flex-basis:40%;display:flex;align-items:start;gap:.5rem}.media-item__details img{width:6rem;object-fit:contain}.media-item__desc{flex-basis:60%}.active-link{background:hsla(0,0%,100%,.1) !important}.nav-menu{background-color:#14141b;box-shadow:0 5px 5px #222;display:flex;flex-direction:column;align-items:center;height:100%;padding:.25rem;margin-right:0rem}.nav-menu__logo{padding:1.2rem 0;display:flex;align-items:center;gap:.3rem}.nav-menu__logo img{width:1.6rem}.nav-menu__logo h5{line-height:1;color:#fff}.nav-menu__box{padding:2rem .125rem;display:flex;flex-direction:column;gap:.5rem;position:relative}.nav-menu__box .item{margin:0;padding:.675rem .5rem;width:10rem;display:flex;align-items:center;line-height:1;border-radius:.5rem;text-decoration:none;color:#ccc;text-transform:capitalize;transition:0ms}.nav-menu__box .item:hover{background-color:rgba(238,243,246,.15)}.nav-menu__box .item i.sidenav-icon{width:2.5rem;height:1.4rem;display:grid;place-items:center}.nav-menu__box .item.item-selected{color:#9bdaff;background-color:rgba(155,218,255,.15);font-weight:medium}.nav-menu__box button.toggle-nav{display:none;position:absolute;padding:0;top:0;right:-1rem;background:rgb(77.5,186.5157480315,255);width:1.5rem;height:1.5rem;aspect-ratio:1;justify-content:center;align-items:center;border-radius:50%;box-shadow:none}.nav-menu.collapsed .nav-menu__logo .logo-container{display:flex;flex-direction:column;align-items:center;gap:.5rem}.nav-menu.collapsed .nav-menu__logo .logo-container>*:not(img){display:block}.nav-menu.collapsed .nav-menu__logo .nav-menu__logo-text{display:none !important}.nav-menu.collapsed .nav-menu__box .item{padding:.675rem 0;width:2.5rem;justify-content:center;transition:300ms}.nav-menu.collapsed .nav-menu__box .item span,.nav-menu.collapsed .nav-menu__box .item p{display:none !important}.nav-menu.collapsed button i{rotate:180deg}.nav-menu:hover button.toggle-nav{display:flex}.sidebar{width:13rem;background-color:#fff;display:flex;flex-direction:column}.sidebar a{text-decoration:none;text-transform:capitalize;padding:1rem;cursor:pointer;color:#999}.sidebar a:hover{color:#222}.sidebar .selected-sidebar-link{font-weight:bold;color:#222;border-left:5px solid #3ba4d7;animation:expand-left-border .1s}.sidebarquickview>h6{padding:.5rem}.sidebarquickview a{text-decoration:none;text-transform:capitalize;padding:.5rem 1rem;display:block;color:#999}.sidebarquickview a a:hover{color:#222}.sidebarquickview .selected-sidebarquickview-link{font-weight:bold;color:#222;border-left:5px solid #3ba4d7;animation:expand-left-border .1s}.node-panel{width:100%;padding:.5rem;animation:fadein .5s}@keyframes expand-left-border{from{border-left:0}to{border-left:5px solid #3ba4d7}}@media(max-width: 700px){.tab-content{flex-direction:column}.sidebar{width:100% !important;flex-direction:row !important;overflow-x:auto !important;overflow-y:hidden !important;white-space:nowrap !important;border-bottom:1px solid rgba(20,20,27,.1) !important;background:#fff !important;z-index:50 !important;flex-shrink:0 !important;height:auto !important;padding:0 !important}.sidebar a{display:inline-block !important;padding:.8rem 1.2rem !important;border-bottom:3px solid rgba(0,0,0,0) !important;border-left:none !important}.sidebar .selected-sidebar-link{border-left:none !important;border-bottom:3px solid #3ba4d7 !important;animation:none !important}.sidebarquickview>h4,.sidebarquickview>h6{display:none !important}}.posts{height:100%;margin-top:1rem;flex-direction:column;overflow:auto}.posts__heading{display:flex;flex-direction:column;justify-content:space-between}.posts-container{height:100%;padding:1rem;display:grid;grid-template-columns:repeat(auto-fill, minmax(150px, 1fr));gap:2rem;border:1px solid rgba(20,20,27,.1);border-radius:4px;overflow:auto}.posts-container-card{min-height:240px;flex-direction:column;border:1px solid rgba(20,20,27,.5);border-radius:4px;cursor:pointer;text-align:center}.posts-container-card img{flex-basis:90%;object-fit:cover}.posts-container-card p{padding:0 .125rem;flex-basis:10%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.progress-bar{width:100%;height:2rem;position:relative;text-align:center;background-color:#eef3f6;border-radius:20px;overflow:hidden}.progress-bar__status{position:absolute;top:0;left:0;height:100%;color:#14141b;background-color:#019dff}.progress-bar__percent{position:absolute;inset:0;margin:auto;width:fit-content;height:fit-content}.progress-bar-chunks{position:relative;margin-top:.5rem;width:100%;height:2rem;display:flex;border-radius:.25rem;overflow:hidden;background-color:#eef3f6}.progress-bar-chunks .chunk{width:100%}.progress-bar-chunks .chunk[data-chunkVal="0"]{background-color:rgba(155,218,255,.2)}.progress-bar-chunks .chunk[data-chunkVal="1"]{background-color:#ff3a4a}.progress-bar-chunks .chunk[data-chunkVal="2"]{background-color:#019dff}.progress-bar-chunks .chunk[data-chunkVal="3"]{background-color:#fcba03}.progress-bar-chunks__percent{position:absolute;inset:0;margin:auto;width:fit-content;height:fit-content}.statusbar{display:flex;justify-content:space-between;align-items:center;height:28px;background-color:#14141b;border-top:1px solid #2e2e38;padding:0 1rem;font-size:.8rem;color:#94a3b8;z-index:100;box-sizing:border-box;user-select:none;flex-shrink:0}.statusbar-left{display:flex;align-items:center}.statusbar-right{display:flex;align-items:center;gap:1.5rem}.statusbar-item{display:flex;align-items:center}.statusbar-divider{width:1px;height:14px;background-color:#2e2e38}.status-bullet{width:8px;height:8px;border-radius:50%;display:inline-block;box-shadow:0 0 4px rgba(0,0,0,.5)}.widget{height:100%;padding:1rem;display:flex;flex-direction:column;gap:.5rem;background-color:#fff;border-radius:.5rem;overflow:auto}.widget .top-heading{display:flex;justify-content:space-between}.widget__heading{display:flex;justify-content:space-between;align-items:center;border-bottom:2px solid #999}.widget__body{height:100%;display:flex;flex-direction:column;overflow:auto}.widget__body-heading{display:flex;justify-content:space-between;align-items:center}.widget__body-heading .action{display:flex;gap:.5rem}.widget__body-content{height:100%;overflow:auto}.widget__body-box{display:flex;flex-direction:column;gap:.5rem}.widget-half{max-width:50%}#modal-container{display:none;position:fixed;z-index:1;height:100%;top:0;left:0;width:100%;background-color:rgba(0,0,0,.2)}.modal-content{position:absolute;color:#555;width:40%;min-height:10rem;height:max-content;padding:1.5rem;inset:0;margin:auto;background-color:#fff;border-radius:.5rem;animation:fadein .5s;display:flex;flex-direction:column}.modal-content button:last-child{margin-top:auto}.modal-content .close-btn{position:absolute;right:1.5rem}.modal-content .widget{padding:0}#notification-container{position:absolute;bottom:0;right:0}.login-page{background-image:linear-gradient(-45deg, rgba(1, 157, 255, 0.75), rgba(17, 143, 204, 0.75));height:100%;animation:fadein .5s}.login-page .login-container{background-color:#fff;box-shadow:3px 3px 5px rgba(20,20,27,.4);margin:auto;position:relative;top:100px;max-width:400px;max-height:500px;border-radius:5px;display:flex;flex-direction:column;align-items:center}.login-page .login-container input{padding:.375rem .75rem;border-radius:.275rem}.login-page .login-container *{margin-bottom:1rem}.login-page .login-container>img{margin:1rem 0 2rem}.login-page .login-container extra{margin:0}.login-page .login-container>a{text-decoration:underline;cursor:pointer}.login-page .extra>label,.login-page .extra>br,.login-page .extra>input{margin-bottom:0}.homepage{margin:2rem auto 0;display:flex;flex-direction:column;gap:4rem}.homepage .logo{display:flex;justify-content:center;align-items:center}.homepage .logo img{width:90px}.homepage .logo .retroshareText{display:flex;flex-direction:column;align-items:center}.homepage .logo .retroshareText .retrotext{font-size:36px;font-weight:600;line-height:1.125}.homepage .logo .retroshareText .retrotext>span{color:#118fcc}.homepage .logo .retroshareText>b{font-size:14px;line-height:1}.homepage .certificate{display:flex;flex-direction:column;gap:4rem}.homepage .certificate__heading{text-align:center}.homepage .certificate__heading>h1{margin-bottom:1rem}.homepage .certificate__content{display:flex;flex-direction:column;gap:2rem;padding:2rem;text-align:center;border:1.5px solid rgba(17,143,204,.2);border-radius:6px;box-shadow:0px 0px 8px 2px rgba(20,20,27,.05)}.homepage .certificate__content .rsId>p{margin-bottom:.5rem;color:#118fcc}.homepage .certificate__content .retroshareID{padding:.25rem;display:flex;align-items:center;justify-self:start;font-size:1.25rem;border-radius:4px;background:rgba(20,20,27,.05)}.homepage .certificate__content .retroshareID .textArea{padding:0;width:100%;height:auto;font-size:1rem;font-family:monospace;background:rgba(0,0,0,0);border:none;resize:none;overflow:hidden;field-sizing:content}.homepage .certificate__content .retroshareID i{color:#118fcc}.homepage .certificate__content .retroshareID>i{margin:0 .5rem;cursor:pointer}.homepage .certificate__content .webhelp{padding:.5rem;background:#f5f5f5;display:flex;justify-content:center;align-items:center;gap:.5rem;border-radius:4px;border:1px solid rgba(20,20,27,.5);width:fit-content;cursor:pointer}.homepage .certificate__content .webhelp-container{display:grid;place-items:center}.homepage .certificate__content .webhelp:hover{background:#eef3f6;border:1px solid #14141b}.homepage .certificate__content .webhelp>i{font-size:1.2rem;color:green}.homepage .certificate__content .add-friend>h6,.homepage .certificate__content .webhelp-container>h6{font-weight:normal;margin-bottom:.5rem}.network-container{display:flex;height:100%;width:100%;overflow:hidden;background-color:#f1f5f9}.network-left-pane{width:320px;min-width:300px;max-width:350px;border-right:1px solid #cbd5e1;display:flex;flex-direction:column;background:#fff;box-shadow:2px 0 5px rgba(0,0,0,.05)}.own-profile-card{padding:1.25rem;border-bottom:1px solid #e2e8f0;background:linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);display:flex;flex-direction:column;gap:.75rem}.own-profile-card .profile-header{display:flex;align-items:center;gap:1rem}.own-profile-card .profile-info{display:flex;flex-direction:column;flex:1;overflow:hidden}.own-profile-card .profile-info .profile-name{font-weight:700;color:#1e293b;font-size:1.1rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.own-profile-card .profile-info .profile-status{font-size:.85rem;color:#10b981;font-weight:500;display:flex;align-items:center;gap:.35rem}.own-profile-card .profile-info .profile-status::before{content:"";display:inline-block;width:8px;height:8px;background-color:#10b981;border-radius:50%}.own-profile-card .own-identity-select-container{display:flex;flex-direction:column;gap:.25rem}.own-profile-card .own-identity-select-container label{font-size:.75rem;color:#64748b;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.own-profile-card .own-identity-select-container select.own-identity-select{width:100%;padding:.375rem .5rem;font-size:.85rem;border:1px solid #cbd5e1;border-radius:.375rem;background-color:#fff;color:#334155;outline:none;cursor:pointer;transition:border-color .2s}.own-profile-card .own-identity-select-container select.own-identity-select:focus{border-color:#3ba4d7}.friends-list-container{flex:1;display:flex;flex-direction:column;overflow:hidden}.friends-list-container .searchbar-container{padding:.75rem 1rem;border-bottom:1px solid #e2e8f0}.friends-list-container .searchbar-container input.searchbar{width:100%;padding:.5rem .75rem;font-size:.9rem;border:1px solid #cbd5e1;border-radius:.375rem;background-color:#f8fafc;outline:none;transition:all .2s}.friends-list-container .searchbar-container input.searchbar:focus{background-color:#fff;border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.friends-list-container .friends-scroll{flex:1;overflow-y:auto;padding:.5rem 0}.friend-list-item{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;margin:.125rem .5rem;border-radius:.5rem;cursor:pointer;transition:all .2s}.friend-list-item:hover{background-color:#f1f5f9}.friend-list-item.selected{background-color:#e0f2fe}.friend-list-item.selected .friend-name{color:#0369a1;font-weight:600}.friend-list-item .friend-avatar{flex-shrink:0}.friend-list-item .friend-meta{flex:1;min-width:0}.friend-list-item .friend-meta .friend-name{font-size:.95rem;color:#334155;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .2s}.friend-list-item .friend-meta .friend-status{font-size:.8rem;color:#94a3b8}.friend-list-item .friend-meta .friend-status.online{color:#10b981;font-weight:500}.network-right-pane{flex:1;display:flex;flex-direction:column;overflow:hidden;background-color:#f8fafc}.network-pane-placeholder{flex:1;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#94a3b8;gap:1rem;padding:2rem;text-align:center}.network-pane-placeholder i{font-size:4rem;color:#cbd5e1}.network-pane-placeholder p{font-size:1.1rem;max-width:400px}.network-tabs{display:flex;background-color:#fff;border-bottom:1px solid #cbd5e1;padding:.5rem 1rem 0;gap:.5rem}.network-tabs .tab-btn{padding:.625rem 1.25rem;font-size:.95rem;font-weight:600;color:#64748b;background:rgba(0,0,0,0);border:none;border-radius:.375rem .375rem 0 0;border-bottom:3px solid rgba(0,0,0,0);cursor:pointer;box-shadow:none;transition:all .2s}.network-tabs .tab-btn:hover{color:#334155;background-color:#f1f5f9}.network-tabs .tab-btn.active{color:#3ba4d7;border-bottom-color:#3ba4d7;background-color:rgba(0,0,0,0)}.network-tab-content{flex:1;overflow-y:auto;padding:1.5rem}.network-detail-view{display:flex;flex-direction:column;gap:1.5rem}.network-detail-view .detail-header{display:flex;align-items:center;gap:1.5rem;padding-bottom:1.5rem;border-bottom:1px solid #e2e8f0}.network-detail-view .detail-header .detail-title{flex:1}.network-detail-view .detail-header .detail-title h2{font-size:1.75rem;font-weight:800;color:#1e293b;margin-bottom:.25rem}.network-detail-view .detail-header .detail-title .detail-subtitle{font-size:.9rem;color:#64748b;display:flex;align-items:center;gap:.5rem}.network-detail-view .detail-header .detail-actions{display:flex;gap:.75rem}.network-detail-view .detail-header .detail-actions button{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.9rem}.network-detail-view .detail-section{background-color:#fff;border-radius:.5rem;border:1px solid #e2e8f0;padding:1.25rem;box-shadow:0 1px 3px rgba(0,0,0,.02)}.network-detail-view .detail-section h3{font-size:1.1rem;font-weight:700;color:#334155;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #f1f5f9}.network-detail-view .detail-section .info-grid{display:grid;grid-template-columns:120px 1fr;row-gap:.75rem;font-size:.9rem}.network-detail-view .detail-section .info-grid .info-label{font-weight:600;color:#64748b}.network-detail-view .detail-section .info-grid .info-value{color:#1e293b;word-break:break-all}.network-detail-view .locations-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));gap:1rem}.network-detail-view .location-card{background-color:#fff;border:1px solid #e2e8f0;border-radius:.5rem;padding:1rem;display:flex;flex-direction:column;gap:.5rem;box-shadow:0 1px 3px rgba(0,0,0,.02)}.network-detail-view .location-card .loc-header{display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #f1f5f9;padding-bottom:.5rem;margin-bottom:.25rem}.network-detail-view .location-card .loc-header .loc-name{font-weight:700;color:#334155;font-size:.95rem}.network-detail-view .location-card .loc-header .loc-status{font-size:.75rem;font-weight:600;padding:.125rem .5rem;border-radius:.25rem}.network-detail-view .location-card .loc-header .loc-status.online{background-color:#d1fae5;color:#065f46}.network-detail-view .location-card .loc-header .loc-status.offline{background-color:#f1f5f9;color:#475569}.network-detail-view .location-card .loc-body{font-size:.85rem;display:grid;grid-template-columns:80px 1fr;row-gap:.25rem}.network-detail-view .location-card .loc-body .loc-label{color:#64748b}.network-detail-view .location-card .loc-body .loc-val{color:#334155;word-break:break-all}.network-detail-view .location-card .loc-footer{margin-top:.5rem;display:flex;justify-content:flex-end}.network-detail-view .location-card .loc-footer button{font-size:.8rem;padding:.25rem .75rem}.network-chat-view{display:flex;flex-direction:column;height:100%;overflow:hidden;background-color:#f8fafc}.network-chat-view .chat-messages{flex:1;overflow-y:auto;padding:1.25rem;display:flex;flex-direction:column;gap:1rem}.network-chat-view .chat-bubble-container{display:flex;flex-direction:column;max-width:70%}.network-chat-view .chat-bubble-container.outgoing{align-self:flex-end;align-items:flex-end}.network-chat-view .chat-bubble-container.outgoing .chat-bubble{background-color:#3ba4d7;color:#fff;border-bottom-right-radius:.125rem}.network-chat-view .chat-bubble-container.incoming{align-self:flex-start;align-items:flex-start}.network-chat-view .chat-bubble-container.incoming .chat-bubble{background-color:#fff;color:#1e293b;border:1px solid #e2e8f0;border-bottom-left-radius:.125rem}.network-chat-view .chat-bubble-container .chat-sender{font-size:.75rem;color:#64748b;margin-bottom:.25rem;padding:0 .25rem}.network-chat-view .chat-bubble-container .chat-bubble{padding:.625rem .875rem;border-radius:.75rem;font-size:.925rem;line-height:1.4;white-space:break-spaces;word-break:break-word;box-shadow:0 1px 2px rgba(0,0,0,.05)}.network-chat-view .chat-bubble-container .chat-time{font-size:.7rem;color:#94a3b8;margin-top:.25rem;padding:0 .25rem}.network-chat-view .chat-input-area{padding:1rem;background-color:#fff;border-top:1px solid #cbd5e1;display:flex;gap:.75rem;align-items:center}.network-chat-view .chat-input-area textarea.chat-textarea{flex:1;resize:none;height:40px;padding:.5rem .75rem;border:1px solid #cbd5e1;border-radius:.375rem;font-size:.9rem;outline:none;transition:all .2s}.network-chat-view .chat-input-area textarea.chat-textarea:focus{border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.network-chat-view .chat-input-area button.send-btn{padding:.5rem 1.25rem;font-size:.9rem;height:40px;display:flex;align-items:center;gap:.5rem}.network-chat-view .chat-warning{flex:1;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#64748b;text-align:center;padding:2rem;gap:1rem}.network-chat-view .chat-warning i{font-size:3rem;color:#cbd5e1}.network-chat-view .chat-warning h4{font-weight:700;color:#334155}.network-chat-view .chat-warning p{max-width:350px;font-size:.9rem}.identity{color:#444;font-size:1.1em;margin:20px;padding:10px;border:1px solid #aaa;border-radius:20px}.identity>h4{margin:5px;font-size:1.3em}.identity button{font-size:.9em}.identity .details{display:grid;grid-template-columns:140px auto;grid-row-gap:5px;justify-content:left}.defaultAvatar{width:3rem;height:3rem;aspect-ratio:1;background:#b0c4de;border-radius:50%;display:grid;place-items:center}.defaultAvatar p{font-weight:900;color:#666f7f;transform:translateY(1px)}img.avatar{display:block;width:3rem;height:max-content;aspect-ratio:1;margin-right:.3em;border-radius:50%}.counter{margin-left:.5em}.counter:before{content:"("}.counter:after{content:")"}.chatInit{margin-left:.5em;color:green;cursor:pointer}.people-sidebar-header{display:flex;flex-direction:column;padding:.75rem 1rem .5rem 1rem;gap:.75rem;border-bottom:1px solid #e2e8f0;background-color:#fff}.people-sidebar-header .searchbar-wrapper{position:relative;display:flex;align-items:center}.people-sidebar-header .searchbar-wrapper i.fa-search{position:absolute;left:.85rem;color:#94a3b8;font-size:.9rem}.people-sidebar-header .searchbar-wrapper input.searchbar-input{width:100%;padding:.5rem .75rem .5rem 2.25rem;border:1px solid #e2e8f0;border-radius:.5rem;font-size:.9rem;background-color:#f8fafc;color:#1e293b;outline:none;transition:all .2s ease}.people-sidebar-header .searchbar-wrapper input.searchbar-input:focus{border-color:#3b82f6;background-color:#fff;box-shadow:0 0 0 3px rgba(59,130,246,.1)}.people-sidebar-header .segmented-control{display:flex;background-color:#f1f5f9;padding:3px;border-radius:.5rem;gap:4px}.people-sidebar-header .segmented-control button.segment-tab{flex:1;display:flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem .75rem;font-size:.9rem;font-weight:600;color:#64748b;background:rgba(0,0,0,0);border:none;border-radius:.375rem;cursor:pointer;box-shadow:none;transition:all .2s ease}.people-sidebar-header .segmented-control button.segment-tab:hover{color:#1e293b}.people-sidebar-header .segmented-control button.segment-tab.active{background-color:#fff;color:#0f172a;box-shadow:0 1px 3px rgba(0,0,0,.1),0 1px 2px rgba(0,0,0,.06)}.people-sidebar-header .segmented-control button.segment-tab.active .segment-badge{background-color:#019dff;color:#fff}.people-sidebar-header .segmented-control button.segment-tab .segment-badge{display:inline-flex;align-items:center;justify-content:center;background-color:#cbd5e1;color:#334155;font-size:.75rem;font-weight:700;min-width:1.25rem;height:1.25rem;padding:0 .35rem;border-radius:9999px;line-height:1;transition:all .2s ease}.people-sidebar-header .sub-filter-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:32px}.people-sidebar-header .sub-filter-row select.filter-select{padding:.35rem .6rem;border:1px solid #cbd5e1;border-radius:.375rem;font-size:.85rem;font-weight:600;color:#475569;background-color:#fff;cursor:pointer;outline:none}.people-sidebar-header .sub-filter-row .btn-add-id{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:.375rem;background-color:#3b82f6;color:#fff;border:none;cursor:pointer;font-size:.9rem;transition:background-color .2s}.people-sidebar-header .sub-filter-row .btn-add-id:hover{background-color:#2563eb}.lobby{margin:10px;border:1px solid #aaa;border-radius:20px}.lobby .mainname{margin:20px;font-weight:100;font-size:1.2em}.topic{color:#666}.lobby>.topic{font-size:.95em;margin-left:25px;margin-bottom:5px}.lefttitle{margin-top:15px;margin-bottom:0;font-weight:100;font-size:1.2em}.leftname{margin-top:5px;margin-bottom:5px;padding:5px;font-weight:100;font-size:1em}.leftlobby>.topic{font-size:.75em;margin-left:15px;margin-bottom:5px}.subscribed,.public{cursor:pointer}.leftlobby{border:1px solid #aaa;border-radius:10px;margin-top:5px;background-color:#fff}.leftlobby.selected-lobby,.selectedidentity{color:#fff;background-color:#3ba4d7}.rightbar{position:absolute;width:185px;background-color:#fff;overflow:auto;top:130px;bottom:15px;right:15px}.user{padding:5px}.lobbyName{padding:15px;margin-top:2rem}.lobbies{position:absolute;width:185px;left:165px;bottom:15px;top:130px;overflow:auto}.messages,.setup{position:absolute;background-color:#fff;top:130px;left:360px;right:215px;overflow:auto}.messages{bottom:115px}.messagetext{white-space:break-spaces;margin-right:5px}.message>*{margin-left:5px}.username{color:#006400;font-weight:bolder}.chatMessage{position:absolute;background-color:#fff;height:85px;bottom:15px;right:215px;left:360px}textarea.chatMsg{height:100%;width:100%}.chatatchar{margin-left:.2em;margin-right:.2em;color:silver}.setupicon{margin-left:1em;cursor:pointer}.leaveicon{margin-left:1em;cursor:pointer;color:#d40000}.selectidentity{margin:15px;font-size:1.2em}.setup>.identity{cursor:pointer}.setup{bottom:15px}.createDistantChat{margin-top:1em}.no-lobbies .messages,.no-lobbies .chatMessage,.no-lobbies .setup{left:165px}@media(min-width: 900px){.node-panel.chat-room{display:grid !important;grid-template-columns:250px 1fr 200px !important;grid-template-rows:auto 1fr auto !important;grid-template-areas:"lobbies header rightbar" "lobbies messages rightbar" "lobbies input rightbar" !important;padding:0 !important;height:100% !important}.node-panel.chat-room .lobbyName{grid-area:header;padding:10px;border-bottom:1px solid #eee;margin:0;z-index:10;background:#fff}.node-panel.chat-room .lobbies{grid-area:lobbies;position:static !important;width:auto !important;height:auto !important;border-right:1px solid #ccc;overflow-y:auto;display:block !important;top:auto !important;bottom:auto !important;left:auto !important}.node-panel.chat-room .messages{grid-area:messages;position:static !important;width:auto !important;height:auto !important;overflow-y:auto;padding:10px;left:auto !important;right:auto !important;top:auto !important;bottom:auto !important;margin:0 !important}.node-panel.chat-room .rightbar{grid-area:rightbar;position:static !important;width:auto !important;border-left:1px solid #ccc;overflow-y:auto;display:block !important}.node-panel.chat-room .chatMessage{grid-area:input;position:static !important;width:auto !important;height:auto !important;border-top:1px solid #eee;left:auto !important;right:auto !important;bottom:auto !important;flex:0 0 auto;padding:10px !important;background:#fff;z-index:10}}@media(max-width: 899px){.node-panel.chat-room{display:flex !important;flex-direction:column !important;height:100% !important;position:relative !important}.node-panel.chat-room .lobbyName{flex:0 0 auto}.node-panel.chat-room .messages{flex:1 !important;overflow-y:auto !important;position:relative !important;top:0 !important;bottom:0 !important;left:0 !important;right:0 !important;width:100% !important;height:auto !important;margin:0 !important}.node-panel.chat-room .chatMessage{flex:0 0 auto !important;position:relative !important;bottom:0 !important;left:0 !important;right:0 !important;width:100% !important;height:auto !important;z-index:100}.node-panel.chat-room .rightbar,.node-panel.chat-room .lobbies{display:none !important;position:fixed !important;top:60px !important;bottom:0 !important;width:80% !important;background:#fff !important;z-index:200 !important;box-shadow:2px 0 10px rgba(0,0,0,.2) !important}.node-panel.chat-room.show-lobbies .lobbies{display:block !important;left:0 !important}.node-panel.chat-room.show-users .rightbar{display:block !important;right:0 !important}.chat-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.4);z-index:150}.show-lobbies .chat-overlay,.show-users .chat-overlay{display:block}.mobile-menu-icons{display:flex;gap:15px;font-size:1.2rem}.mobile-menu-icons i{cursor:pointer;padding:5px}}@media(min-width: 900px){.mobile-menu-icons{display:none}}.chat-hub-container{display:flex;height:100%;width:100%;overflow:hidden;background-color:#f1f5f9}.chat-hub-left-pane{width:320px;min-width:300px;max-width:350px;border-right:1px solid #cbd5e1;display:flex;flex-direction:column;background:#fff;box-shadow:2px 0 5px rgba(0,0,0,.05)}.chat-own-profile-card{padding:1.25rem;border-bottom:1px solid #e2e8f0;background:linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)}.chat-own-profile-card .profile-header{display:flex;align-items:center;gap:1rem}.chat-own-profile-card .profile-info{display:flex;flex-direction:column;flex:1;overflow:hidden}.chat-own-profile-card .profile-info .profile-name{font-weight:700;color:#1e293b;font-size:1.1rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat-own-profile-card .profile-info .profile-status{font-size:.85rem;color:#10b981;font-weight:500;display:flex;align-items:center;gap:.35rem}.chat-own-profile-card .profile-info .profile-status::before{content:"";display:inline-block;width:8px;height:8px;background-color:#10b981;border-radius:50%}.chat-rooms-list-container{flex:1;display:flex;flex-direction:column;overflow:hidden}.chat-rooms-list-container .searchbar-container{padding:.75rem 1rem;border-bottom:1px solid #e2e8f0}.chat-rooms-list-container .searchbar-container input.searchbar{width:100%;padding:.5rem .75rem;font-size:.9rem;border:1px solid #cbd5e1;border-radius:.375rem;background-color:#f8fafc;outline:none;transition:all .2s}.chat-rooms-list-container .searchbar-container input.searchbar:focus{background-color:#fff;border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.chat-rooms-list-container .rooms-scroll{flex:1;overflow-y:auto;padding:.5rem 0}.rooms-section-title{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem .375rem;font-size:.75rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em}.rooms-section-title i{font-size:.7rem;color:#94a3b8}.chat-room-list-item{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;margin:.125rem .5rem;border-radius:.5rem;cursor:pointer;transition:all .2s}.chat-room-list-item:hover{background-color:#f1f5f9}.chat-room-list-item.selected{background-color:#e0f2fe}.chat-room-list-item.selected .room-name{color:#0369a1;font-weight:600}.chat-room-list-item .room-icon{flex-shrink:0;width:36px;height:36px;border-radius:.5rem;background:linear-gradient(135deg, #3ba4d7, #0ea5e9);display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.35rem}.chat-room-list-item.public-room .room-icon{background:linear-gradient(135deg, #10b981, #059669)}.chat-room-list-item .room-meta{flex:1;min-width:0}.chat-room-list-item .room-meta .room-name{font-size:.95rem;color:#334155;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .2s}.chat-room-list-item .room-meta .room-topic{font-size:.8rem;color:#94a3b8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat-room-list-item .room-badge{flex-shrink:0;min-width:24px;height:24px;border-radius:12px;background-color:#e2e8f0;color:#475569;font-size:.75rem;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 .375rem}.chat-hub-right-pane{flex:1;display:flex;flex-direction:column;overflow:hidden;background-color:#f8fafc}.chat-pane-placeholder{flex:1;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#94a3b8;gap:1rem;padding:2rem;text-align:center}.chat-pane-placeholder i{font-size:4rem;color:#cbd5e1}.chat-pane-placeholder p{font-size:1.1rem;max-width:400px}.chat-hub-tab-content{flex:1;overflow-y:auto;padding:1.5rem}.chat-room-detail-view{display:flex;flex-direction:column;gap:1.5rem}.chat-room-detail-view .detail-header{display:flex;align-items:flex-start;gap:1.5rem;padding-bottom:1.5rem;border-bottom:1px solid #e2e8f0;flex-wrap:wrap}.chat-room-detail-view .detail-header .detail-title{flex:1;min-width:200px}.chat-room-detail-view .detail-header .detail-title h2{font-size:1.75rem;font-weight:800;color:#1e293b;margin-bottom:.25rem}.chat-room-detail-view .detail-header .detail-title .detail-subtitle{font-size:.9rem;color:#64748b;display:flex;align-items:center;gap:.5rem}.chat-room-detail-view .detail-header .detail-actions{display:flex;gap:.75rem;flex-wrap:wrap}.chat-room-detail-view .detail-header .detail-actions button{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.9rem}.chat-room-detail-view .detail-section{background-color:#fff;border-radius:.5rem;border:1px solid #e2e8f0;padding:1.25rem;box-shadow:0 1px 3px rgba(0,0,0,.02)}.chat-room-detail-view .detail-section h3{font-size:1.1rem;font-weight:700;color:#334155;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #f1f5f9}.chat-room-detail-view .detail-section .info-grid{display:grid;grid-template-columns:130px 1fr;row-gap:.75rem;font-size:.9rem}.chat-room-detail-view .detail-section .info-grid .info-label{font-weight:600;color:#64748b}.chat-room-detail-view .detail-section .info-grid .info-value{color:#1e293b;word-break:break-all}.participants-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(180px, 1fr));gap:.5rem}.participant-card{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background-color:#f8fafc;border:1px solid #e2e8f0;border-radius:.375rem}.participant-card .participant-name{font-size:.875rem;color:#334155;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-participants{color:#94a3b8;font-size:.9rem;font-style:italic}.detail-actions-footer{display:flex;gap:.75rem}.detail-actions-footer button{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.9rem}.join-description{color:#64748b;font-size:.9rem;margin-bottom:1rem}.identities-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));gap:.75rem}.identity-card{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background-color:#f8fafc;border:1px solid #e2e8f0;border-radius:.5rem;cursor:pointer;transition:all .2s}.identity-card:hover{background-color:#e0f2fe;border-color:#3ba4d7}.identity-card .identity-name{font-size:.95rem;font-weight:600;color:#334155}.identity-card i{color:#3ba4d7;font-size:.9rem}.no-rooms{padding:1rem;color:#94a3b8;text-align:center;font-style:italic}@media(max-width: 899px){.chat-hub-container{flex-direction:column}.chat-hub-left-pane{width:100%;min-width:0;max-width:none;max-height:45%;border-right:none;border-bottom:1px solid #cbd5e1}.chat-hub-right-pane{flex:1;min-height:0}}.chat-hub-header-bar{padding:.75rem 1.5rem;background-color:#fff;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;justify-content:space-between;height:65px;flex-shrink:0}.chat-hub-header-bar .chat-header-info{display:flex;flex-direction:column;overflow:hidden}.chat-hub-header-bar .chat-header-info .chat-header-name{font-size:1.15rem;font-weight:800;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat-hub-header-bar .chat-header-info .chat-header-topic{font-size:.85rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:.125rem}.chat-hub-header-bar .chat-header-actions{display:flex;gap:.5rem}.chat-hub-header-bar .chat-header-actions button{display:flex;align-items:center;gap:.35rem;padding:.375rem .75rem;font-size:.85rem}.chat-hub-tabs-container{background-color:#fff;border-bottom:1px solid #cbd5e1;padding:.5rem 1.5rem 0}.chat-hub-tabs{display:flex;gap:.5rem}.chat-hub-tabs .tab-btn{padding:.625rem 1.25rem;font-size:.95rem;font-weight:600;color:#64748b;background:rgba(0,0,0,0);border:none;border-radius:.375rem .375rem 0 0;border-bottom:3px solid rgba(0,0,0,0);cursor:pointer;box-shadow:none;transition:all .2s;display:flex;align-items:center;gap:.5rem}.chat-hub-tabs .tab-btn:hover{color:#334155;background-color:#f1f5f9}.chat-hub-tabs .tab-btn.active{color:#3ba4d7;border-bottom-color:#3ba4d7;background-color:rgba(0,0,0,0)}.chat-hub-tab-content{flex:1;display:flex;flex-direction:column;overflow:hidden;background-color:#f8fafc}.chat-hub-conversation-layout{display:flex;flex-direction:row;height:100%;width:100%;overflow:hidden}.chat-hub-conversation-main{display:flex;flex-direction:column;flex:1;height:100%;overflow:hidden}.chat-hub-rightbar{width:200px;border-left:1px solid #cbd5e1;background-color:#fff;display:flex;flex-direction:column;flex-shrink:0}.chat-hub-rightbar .rightbar-title{padding:.75rem 1rem;font-size:.85rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid #e2e8f0}.chat-hub-rightbar .rightbar-users-list{flex:1;overflow-y:auto;padding:.5rem}.chat-hub-rightbar .user{padding:.5rem .75rem;font-size:.9rem;color:#334155;border-radius:.375rem;transition:all .2s;display:flex;align-items:center;gap:.5rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat-hub-rightbar .user:hover{background-color:#f1f5f9;color:#0f172a}.chat-hub-rightbar .user .defaultAvatar{width:2rem;height:2rem;font-size:.9rem;flex-shrink:0}.chat-hub-rightbar .user img.avatar{width:2rem;height:2rem;flex-shrink:0}@media(max-width: 899px){.chat-hub-rightbar{display:none}}.chat-hub-messages{flex:1;overflow-y:auto;padding:1.25rem 1.5rem;display:flex;flex-direction:column;gap:1rem}.chat-hub-messages .message{display:flex;flex-direction:column;max-width:70%;padding:.625rem .875rem;border-radius:.75rem;font-size:.925rem;line-height:1.4;word-break:break-word;box-shadow:0 1px 2px rgba(0,0,0,.05)}.chat-hub-messages .message.incoming{align-self:flex-start;align-items:flex-start;background-color:#fff;color:#1e293b;border:1px solid #e2e8f0;border-bottom-left-radius:.125rem}.chat-hub-messages .message.outgoing{align-self:flex-end;align-items:flex-end;background-color:#3ba4d7;color:#fff;border-bottom-right-radius:.125rem}.chat-hub-messages .message .username{font-size:.75rem;margin-bottom:.25rem;padding:0 .125rem;font-weight:700}.chat-hub-messages .message.incoming .username{color:#0369a1}.chat-hub-messages .message.outgoing .username{color:#e0f2fe}.chat-hub-messages .message .messagetext{white-space:break-spaces;margin:0}.chat-hub-messages .message .datetime{font-size:.7rem;margin-top:.25rem;padding:0 .125rem;opacity:.8}.chat-hub-messages .message.incoming .datetime{color:#64748b}.chat-hub-messages .message.outgoing .datetime{color:#f1f5f9}.chat-hub-input-area{padding:.75rem 1.5rem;background-color:#fff;border-top:1px solid #cbd5e1;display:flex;gap:.75rem;align-items:flex-end;flex-shrink:0}.chat-hub-input-area textarea.chat-hub-textarea{flex:1;resize:vertical;min-height:40px;max-height:250px;height:40px;padding:.5rem .75rem;border:1px solid #cbd5e1;border-radius:.375rem;font-size:.9rem;outline:none;transition:border-color .2s,box-shadow .2s;background-color:#f8fafc}.chat-hub-input-area textarea.chat-hub-textarea:focus{background-color:#fff;border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.chat-hub-input-area button.chat-hub-send-btn{padding:.5rem 1.25rem;font-size:.9rem;height:40px;display:flex;align-items:center;gap:.5rem;border-radius:.375rem}.chat-hub-messages.compact-container,.messages.compact-container{gap:0 !important;padding:.75rem 1rem !important;background-color:#fff !important;display:flex !important;flex-direction:column !important}.chat-hub-messages.compact-container .message.compact,.messages.compact-container .message.compact{display:block !important;max-width:100% !important;padding:.1rem 0 !important;border-radius:0 !important;background-color:rgba(0,0,0,0) !important;border:none !important;box-shadow:none !important;align-self:flex-start !important;font-size:.875rem !important;line-height:1.45 !important;margin:0 !important;white-space:nowrap !important}.chat-hub-messages.compact-container .message.compact:hover,.messages.compact-container .message.compact:hover{background-color:#f8fafc !important;overflow:visible !important;white-space:normal !important}.chat-hub-messages.compact-container .message.compact .datetime,.messages.compact-container .message.compact .datetime{color:#a0a0a0 !important;margin-right:.4rem !important;font-size:.78rem !important;font-family:monospace !important;opacity:1 !important;display:inline !important}.chat-hub-messages.compact-container .message.compact .username,.messages.compact-container .message.compact .username{font-weight:bold !important;margin-right:.2rem !important;font-size:.875rem !important;display:inline !important}.chat-hub-messages.compact-container .message.compact .messagetext,.messages.compact-container .message.compact .messagetext{color:#1e293b !important;white-space:normal !important;word-break:break-word !important;display:inline !important;margin:0 !important}.side-bar{display:flex;flex-direction:column;background:#fff}.side-bar .mail-compose-btn{width:96%;margin:.25rem;padding:.75rem 0}.compose-mail__from{display:flex;justify-content:flex-start;align-items:center;gap:.5rem;padding-bottom:.5rem;border-bottom:2px solid #eef3f6}.compose-mail__recipients{padding:.5rem 0;display:flex;flex-direction:column;gap:.5rem;border-bottom:2px solid #eef3f6}.compose-mail__recipients__container{display:flex;gap:.5rem}.compose-mail__recipients__container>label{text-transform:capitalize}.compose-mail__recipients__container .recipients{width:100%;display:flex;gap:.5rem;flex-wrap:wrap}.compose-mail__recipients__container .recipients__selected{padding:.125rem .5rem;display:flex;align-items:center;gap:.5rem;border:1px solid #eef3f6;border-radius:3px;cursor:default}.compose-mail__recipients__container .recipients__selected i{cursor:pointer;padding:.25rem}.compose-mail__recipients__container .recipients__input{display:flex;position:relative;flex-grow:1}.compose-mail__recipients__container .recipients__input-field{flex-grow:1;min-width:200px;padding:0;border:none;box-shadow:none}.compose-mail__recipients__container .recipients__input-field:focus+.recipients__input-list{display:flex}.compose-mail__recipients__container .recipients__input-list{z-index:1;position:absolute;top:1rem;padding:0;width:100%;max-height:15rem;flex-direction:column;overflow:auto;display:none;background:#fff;border-top:1px solid #eef3f6;border-bottom:1px solid #eef3f6}.compose-mail__recipients__container .recipients__input-list:hover{display:flex}.compose-mail__recipients__container .recipients__input-list li{list-style:none;padding:.25rem .5rem;cursor:pointer;background:#fff;border:1px solid #eef3f6;border-top:0px}.compose-mail__recipients__container .recipients__input-list li:hover{background:#eef3f6}.compose-mail__recipients__container .recipients__input-list li:last-child{border-bottom:0px}.compose-mail__recipients .remove-recipient{padding:.125rem .5rem}.compose-mail input[type=text].compose-mail__subject{padding:.5rem 0;border:none;box-shadow:none;border-bottom:2px solid #eef3f6;border-radius:0}.compose-mail__message{margin:.5rem 0;height:100%;display:flex;flex-direction:column;overflow:auto}.compose-mail__message-body{height:100%;outline:rgba(0,0,0,0)}.compose-mail__send-btn{display:flex;align-items:center;gap:.5rem}.compose-mail__send-btn i{transform:translateY(-1px)}.msg-view{height:100%;display:flex;flex-direction:column;gap:1rem;overflow:auto}.msg-view-nav{display:flex;justify-content:space-between;align-items:column}.msg-view-nav__action{display:flex;gap:.5rem}.msg-view__header{display:flex;flex-direction:column;gap:1rem}.msg-view__header>h3{line-height:1}.msg-view__header .msg-details{display:flex;gap:1rem}.msg-view__header .msg-details__avatar{height:max-content}.msg-view__header .msg-details__info{display:flex;flex-direction:column}.msg-view__header .msg-details__info-item{display:flex;gap:.5rem}.msg-view__body{height:100%;overflow:auto;font-size:14px !important}.msg-view__attachment{height:50%;overflow:auto;display:flex;flex-direction:column}.msg-view__attachment-items{height:100%;overflow:auto}.mail-tag{width:8rem;padding:.5rem}.msgHeader{display:flex}.msgHeaderDetails{display:flex;flex-direction:column}table.mails th:nth-child(1){width:5%;color:#fcba03}table.mails th:nth-child(2){width:5%;color:hsl(202.5,30.7692307692%,44.9019607843%)}table.mails th:nth-child(3){width:50%;text-align:start}table.mails th:nth-child(4),table.mails th:nth-child(5){width:20%;text-align:start}table.mails td:nth-child(3){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.mails td:nth-child(4),table.mails td:nth-child(5){text-align:start}table.mails tr:hover{background-color:#eef3f6;cursor:pointer}table.mails tr.unread{color:#000;background-color:#eef3f6}table.mails>tr:hover{cursor:auto;background-color:#fff}table.mails th.sortable-th{cursor:pointer;user-select:none;transition:background-color .2s,color .2s}table.mails th.sortable-th:hover{background-color:#eef3f6;color:hsl(202.5,30.7692307692%,14.9019607843%)}input.star-check{display:none}input.star-check+label.star-check{color:gray}input.star-check:checked+label.star-check{color:#fcba03}#truncate{height:6rem;overflow:auto}#truncate.truncated-view{height:1.75rem;overflow:hidden}.toggle-truncate{font-size:.75rem;padding:0 .25rem;background:#999;color:#14141b;box-shadow:none;border-radius:2px}table.attachment-container{padding:0}table.attachment-container>tr{border:0}table.attachment-container .attachment-header{width:100%;display:flex;justify-content:space-between}table.attachment-container .attachment-header th{text-align:start}table.attachment-container .attachment-header th:nth-child(1){flex-basis:45%}table.attachment-container .attachment-header th:nth-child(2){flex-basis:15%}table.attachment-container .attachment-header th:nth-child(3){flex-basis:10%}table.attachment-container .attachment-header th:nth-child(4){flex-basis:20%}table.attachment-container .attachment-header th:nth-child(5){text-align:center;flex-basis:10%}table.attachment-container .attachment{width:100%;display:flex;justify-content:space-between;text-align:start}table.attachment-container .attachment__name{flex-basis:45%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}table.attachment-container .attachment__name span{margin-left:8px}table.attachment-container .attachment__from{flex-basis:15%}table.attachment-container .attachment__size{flex-basis:10%}table.attachment-container .attachment__date{flex-basis:20%}table.attachment-container .attachment td:nth-child(5){display:flex;justify-content:center;align-items:center;flex-basis:10%}table.attachment-container .attachment td:nth-child(5) button{font-size:.875rem}.view-toggle{height:max-content;border:1px solid #019dff;border-radius:4px;display:flex}.view-toggle *{padding:4px 12px;border-radius:4px}.composePopupOverlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1;background-color:rgba(0,0,0,.2)}.composePopupOverlay .composePopup{position:absolute;inset:0;margin:auto;width:80%;height:90%}.composePopupOverlay .composePopup>.widget{padding:2rem}.composePopupOverlay .composePopup .close-btn{position:absolute;top:1.5rem;right:1.5rem}.file-view{width:100%;padding:1rem;margin-top:1.5rem;border-radius:8px;border:1px solid #ccc;animation:fadein .5s}.file-view__heading{display:flex;justify-content:space-between;margin-bottom:.5rem}.file-view__heading-chunk{display:flex;gap:1rem}.file-view__body{display:flex;flex-direction:column;gap:1rem}.file-view__body-details{display:flex;align-items:center}.file-view__body-details-stat{width:100%;display:grid;grid-template-columns:repeat(5, 1fr)}.file-view__body-details-stat span>i{margin-right:.5rem}.file-view__body-details-action{display:flex;gap:1rem;height:100%}.file-view__body-details-action button,.file-view__body-details-action button.red{padding:.25rem .75rem}table.myfiles td{word-wrap:break-word}table.myfiles th:nth-child(1){width:2%}table.myfiles th:nth-child(2){width:50%}table.myfiles td:nth-child(2){text-align:start}table.friendsfiles td{word-wrap:break-word}table.friendsfiles th:nth-child(1){width:2%}table.friendsfiles th:nth-child(2){width:50%}table.friendsfiles th:nth-child(4){width:40%}table.friendsfiles td:nth-child(2){text-align:start}.file-search-container{margin-top:1rem;padding:8px;display:flex;gap:8px;border:1px solid rgba(20,20,27,.2);border-radius:6px;height:100%;overflow:auto}.file-search-container__keywords{flex-basis:15%;padding-right:.25rem;border-right:1px solid rgba(20,20,27,.1)}.file-search-container__keywords .keywords-container{display:flex;flex-direction:column;border-top:2.5px solid rgba(20,20,27,.08);margin-top:.125rem;padding-top:.25rem}.file-search-container__keywords .keywords-container a{font-size:1.2rem;text-decoration:none;color:#14141b}.file-search-container__keywords .keywords-container a.selected{color:#019dff}.file-search-container__results{flex-basis:85%;height:100%;overflow:auto}.file-search-container__results .results-container .results-header tr{display:flex}.file-search-container__results .results-container .results-header tr th{font-size:1.25rem;font-weight:bold;text-align:left}.file-search-container__results .results-container .results-header tr th:nth-child(1){flex-basis:40%}.file-search-container__results .results-container .results-header tr th:nth-child(2){flex-basis:10%;text-align:center}.file-search-container__results .results-container .results-header tr th:nth-child(3){flex-basis:40%}.file-search-container__results .results-container .results-header tr th:nth-child(4){flex-basis:10%}.file-search-container__results .results-container .results{height:100%;overflow:auto}.file-search-container__results .results-container .results tr{display:flex}.file-search-container__results .results-container .results tr .results__hash,.file-search-container__results .results-container .results tr .results__name{text-align:left;flex-basis:40%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-search-container__results .results-container .results tr .results__hash span,.file-search-container__results .results-container .results tr .results__name span{margin-left:8px}.file-search-container__results .results-container .results tr .results__size{flex-basis:10%}.file-search-container__results .results-container .results tr .results__download{flex-basis:10%;display:flex;justify-content:start;align-items:center}.search-form{display:flex;width:40%}.search-form input{width:100%}.search-form button{margin-left:.5rem}.shareManagerPopupOverlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1;background-color:rgba(0,0,0,.2)}.shareManagerPopupOverlay .shareManagerPopup{position:absolute;inset:0;margin:auto;width:80%;height:90%}.shareManagerPopupOverlay .shareManagerPopup>.widget{padding:1.5rem}.shareManagerPopupOverlay .shareManagerPopup .close-btn{position:absolute;top:1.5rem;right:1.5rem}.share-manager{display:flex;flex-direction:column;justify-content:space-between}.share-manager__table{margin:1rem 0 auto}.share-manager__table thead{font-weight:bold;text-align:left}.share-manager__table thead td:nth-child(1),.share-manager__table thead td:nth-child(2){padding-left:.5rem}.share-manager__table thead td:nth-child(3) .tooltip,.share-manager__table thead td:nth-child(4) .tooltip{font-weight:normal;font-size:1rem}.share-manager__table tbody{text-align:left}.share-manager__table tbody td:nth-child(4){font-size:1rem}.share-manager__table td input{border:0 !important}.share-manager__table td input[type=text]{width:100%}.share-manager__table td:nth-child(1){width:45%}.share-manager__table td:nth-child(2){width:20%}.share-manager__table td:nth-child(3){width:10%}.share-manager__table td:nth-child(4){width:25%}.share-manager__actions{display:flex;justify-content:space-between}.share-manager__form{display:flex;flex-direction:column;gap:.5rem}.share-manager__form_input{display:flex;flex-direction:column;gap:.5rem}.share-manager__form_input input{flex-grow:1}.share-manager .share-flags input.share-flags-check{display:none}.share-manager .share-flags input.share-flags-check+label.share-flags-label{color:gray;margin-right:.25rem;padding:.25rem .25rem .125rem;border:1px solid #6d6d6d;border-radius:.5rem}.share-manager .share-flags input.share-flags-check:checked+label.share-flags-label{color:#118fcc}.share-manager label span{display:inline-block;width:1.125rem}.manage-visibility label{width:100%;cursor:pointer}.manage-visibility{display:flex;justify-content:space-between}@media(max-width: 700px){.file-view__body-details{flex-direction:column;align-items:flex-start;gap:1rem}.file-view__body-details-stat{grid-template-columns:1fr;gap:.5rem}.file-view__body-details-stat span{display:flex;align-items:center}.share-manager__table,.share-manager__table thead,.share-manager__table tbody,.share-manager__table tr,.share-manager__table td{display:block;width:100% !important}.share-manager__table thead{display:none}.share-manager__table tr{border:1px solid #ccc;border-radius:8px;margin-bottom:1rem;padding:.5rem;background:#fff}.share-manager__table td{margin-bottom:.5rem;border:none !important;padding-left:0 !important}table.myfiles,table.myfiles tr,table.myfiles td,table.friendsfiles,table.friendsfiles tr,table.friendsfiles td{display:block;width:100% !important}table.myfiles th,table.friendsfiles th{display:none}table.myfiles tr,table.friendsfiles tr{border:1px solid #ccc;border-radius:8px;margin-bottom:1rem;padding:.5rem;background:#fff}.file-search-container{flex-direction:column}.file-search-container__keywords{flex-basis:auto;width:100%;border-right:none;border-bottom:1px solid rgba(20,20,27,.1);padding-bottom:1rem;margin-bottom:1rem}.results-container,.results-container thead,.results-container tbody,.results-container tr,.results-container td{display:block;width:100% !important}.results-container thead{display:none}.results-container tr{border-bottom:1px solid #eee;padding:1rem 0}.results-container td{margin-bottom:.5rem;word-break:break-all}}.file-section{margin-top:2rem;display:flex;flex-direction:column}.comments-section{margin-top:2rem;display:flex;justify-content:space-between}.comments-section__menu{display:flex;gap:1rem}.comments-section__menu-id{display:flex;align-items:center;gap:.25rem}#toggleunsub{position:relative;background:gray}table.channels th:nth-child(1){width:50%;text-align:start}table.channels td:nth-child(1){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.channels tr:hover{background-color:#eef3f6;cursor:pointer}table.channels tr.hidden{display:none}table{padding:.5rem}table.comments{border:1px solid #eee}table.comments th{height:40px}table.comments th:nth-child(1){width:2%}table.comments th:nth-child(2){width:40%}table.comments td{word-wrap:break-word}table.comments td:nth-child(2){text-align:start}table.files th:first-child{text-align:start;width:60%}table.files tr td:first-child{text-align:start}table.files td{word-wrap:break-word}#mtags{width:160px;text-align:center;font-size:medium;margin-left:10px;height:40px}.forums-node-panel{position:relative;bottom:200px;margin-left:200px;animation:fadein .5s}table.forums th:nth-child(1){width:50%;text-align:start}table.forums td:nth-child(1){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.forums tr:hover{background-color:#eef3f6;cursor:pointer}table.forums tr.hidden{display:none}#searchforum{position:relative;margin-left:250px}#forumdetails{position:relative;padding:10px}.p{margin:0}#toggleunsub{position:relative;background:gray}table.threads tr:hover{background-color:#eef3f6;cursor:pointer}table.threads td{word-wrap:break-word}table.threadreply th:nth-child(2){width:50%}table.threadreply th:nth-child(1){width:2%}table.threadreply td:nth-child(2){width:50%;text-align:start}table.threadreply td{word-wrap:break-word}table.threadreply tr:hover{background-color:#eef3f6;cursor:pointer}table.boards th:nth-child(1){width:50%;text-align:start}table.boards td:nth-child(1){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.boards tr:hover{background-color:#eef3f6;cursor:pointer}table.boards tr.hidden{display:none}#toggleunsub{position:relative;background:gray}#options{width:100px;text-align:center;font-size:medium;margin-left:20px;height:40px}#composepopup{height:80%;width:70%}#mtags{width:160px;text-align:center;font-size:medium;margin-left:10px;height:40px}.mail .permission-flag{margin-bottom:1rem;display:flex;gap:1rem}.mail-tags{padding:.5rem;border:1px solid rgba(20,20,27,.2);border-radius:6px}.mail-tags__container{display:flex;flex-direction:column}.mail-tags__container .tag-item{display:flex;align-items:center;gap:4px;border-bottom:1px solid rgba(20,20,27,.1);padding:2px 0}.mail-tags__container .tag-item:last-child{border:none}.mail-tags__container .tag-item__color{width:1.25rem;height:1.25rem;aspect-ratio:1}.mail-tags__container .tag-item__name{font-size:1.125rem}.mail-tags__container .tag-item__modify{margin-left:auto;font-size:.75rem;display:flex;gap:4px}.mail-tags__container .tag-item:hover{background-color:#eef3f6}.mail-tags__container .tag-item button,.mail-tags__container .tag-item button.red{padding:.25rem .6rem}.mail-tags-form .input-field{margin-bottom:.5rem}.mail-tags-form .input-field label{margin-right:.5rem}.external-address{margin:0;padding-left:1rem;height:100px;overflow:hidden auto}.external-address::-webkit-scrollbar{display:none}.proxy-server{display:flex;flex-direction:column;gap:4px}.proxy-server__tor>h4,.proxy-server__i2p>h4{margin-bottom:.25rem}.proxy-server__tor>input,.proxy-server__i2p>input{margin-right:.5rem}.proxy-server__tor .proxy-outgoing,.proxy-server__i2p .proxy-outgoing{display:inline-flex;align-items:center;gap:.5rem}.proxy-server__tor .proxy-outgoing__status,.proxy-server__i2p .proxy-outgoing__status{width:1rem;height:1rem;aspect-ratio:1;border:1px solid #000;border-radius:50%}.config-files{display:flex;flex-direction:column;gap:1rem}.proxy-server-container{width:100%;display:flex;flex-direction:column;gap:1rem}.proxy-description{color:#334155;font-size:.95rem;margin-bottom:.5rem}.proxy-rows-container{display:flex;flex-direction:column;gap:.75rem;width:100%}.proxy-row{display:grid;grid-template-columns:160px 220px 220px auto;gap:.75rem;align-items:center;width:100%}.proxy-label{font-size:.95rem;font-weight:500;color:#1e293b}.proxy-addr-input,.proxy-port-input{width:100% !important;max-width:none !important}.proxy-status-container{display:flex;align-items:center;gap:.5rem}.proxy-status-bullet{width:14px;height:14px;border-radius:50%;display:inline-block;border:1px solid #475569}.proxy-status-text{font-size:.95rem;color:#1e293b} + */@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url("./webfonts/fa-solid-900.eot");src:url("./webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"),url("./webfonts/fa-solid-900.woff2") format("woff2"),url("./webfonts/fa-solid-900.woff") format("woff"),url("./webfonts/fa-solid-900.ttf") format("truetype"),url("./webfonts/fa-solid-900.svg#fontawesome") format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900}html{font-size:87.5%;box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}body,h1,h2,h3,h4,h5,h6,p,figure,blockquote,dl,dd{margin:0;padding:0}ul[role=list],ol[role=list]{list-style:none}html:focus-within{scroll-behavior:smooth}body{text-rendering:optimizeSpeed;line-height:1.5;font-family:"Roboto",Arial,Helvetica,sans-serif !important;letter-spacing:-0.025ch}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}@media(prefers-reduced-motion: reduce){html:focus-within{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}}#main{height:100vh}.content{display:flex;height:100%;overflow:hidden}.tab-content{display:flex;height:100%;width:100%;background-color:#eef3f6;animation:fadein .3s;overflow:auto}input[type=text],input[type=password],input[type=number],textarea{box-sizing:border-box;background:#fff;max-width:100%;font-size:1rem;font-weight:400;border:1px solid #ccc;border-radius:.25rem;padding:.25rem .5rem;outline:rgba(0,0,0,0)}input:focus{border:1px solid #3ba4d7;box-shadow:inset 0 0 5px #ccc}input.stretched{width:90%}input.small{max-width:70%;padding:.1rem}input.searchbar{width:40%}a{cursor:pointer}a[title=Back]{width:max-content;height:max-content;padding:.475rem .75rem;border-radius:50%;transition:100ms}a[title=Back]:hover{background:#eef3f6}table{padding:20px;table-layout:fixed;width:100%;border-collapse:collapse;text-align:center;color:#333;font-size:1.125rem}table th{font-size:1.125rem;color:#000;border-bottom:2px solid #eee}table tr{border-bottom:1px solid #eee}h3{color:#444}hr{margin-left:0;color:#aaa}.grid-2col{display:grid;grid-template-columns:auto auto;gap:1rem;justify-content:start}.grid-2col input[type=checkbox]{margin-top:20px}.error{color:red}.tooltip{color:#333;position:relative;display:inline-block;margin:0 .25rem}.tooltiptext{visibility:hidden;position:absolute;top:100%;left:50%;min-width:250px;margin-left:-120px;z-index:1;color:#ccc;background-color:#333;font-size:.875rem;text-align:center;padding:.25rem;border-radius:.5rem}.tooltip:hover .tooltiptext{visibility:visible;animation:fadein .5s}blockquote{color:#14141b;padding:.75rem 1rem .75rem 2rem;border-radius:.25rem}blockquote.info{position:relative;line-height:1.2;color:rgba(20,20,27,.8);border:1px solid rgba(17,143,204,.8)}blockquote.info::before{font-family:"Font Awesome 5 Free";position:absolute;top:.5rem;left:.5rem;content:"๏š";color:#019dff}@keyframes fadein{from{opacity:0}to{opacity:1}}.fadein{animation:fadein .5s}@keyframes swipe-from-left{from{margin-left:100%}to{margin-left:0}}button{width:max-content;height:max-content;color:#fff;background:#019dff;font-size:1rem;padding:.4rem 1rem;border:0;border-radius:5px;cursor:pointer;box-shadow:inset -3px -3px 0 rgb(0,94.5826771654,154)}button:active{outline:none;box-shadow:inset 3px 3px 0 rgb(0,94.5826771654,154)}button.red{width:max-content;height:max-content;color:#fff;background:#ff3a4a;font-size:1rem;padding:.4rem 1rem;border:0;border-radius:5px;cursor:pointer;box-shadow:inset -3px -3px 0 rgb(211,0,17.1370558376)}button.red:active{outline:none;box-shadow:inset 3px 3px 0 rgb(211,0,17.1370558376)}.media-item{display:flex;margin-top:.5rem;padding:1rem;border:1px solid rgba(20,20,27,.1);border-radius:4px}.media-item__details{flex-basis:40%;display:flex;align-items:start;gap:.5rem}.media-item__details img{width:6rem;object-fit:contain}.media-item__desc{flex-basis:60%}.active-link{background:hsla(0,0%,100%,.1) !important}.nav-menu{background-color:#14141b;box-shadow:0 5px 5px #222;display:flex;flex-direction:column;align-items:center;height:100%;padding:.25rem;margin-right:0rem}.nav-menu__logo{padding:1.2rem 0;display:flex;align-items:center;gap:.3rem}.nav-menu__logo img{width:1.6rem}.nav-menu__logo h5{line-height:1;color:#fff}.nav-menu__box{padding:2rem .125rem;display:flex;flex-direction:column;gap:.5rem;position:relative}.nav-menu__box .item{margin:0;padding:.675rem .5rem;width:10rem;display:flex;align-items:center;line-height:1;border-radius:.5rem;text-decoration:none;color:#ccc;text-transform:capitalize;transition:0ms}.nav-menu__box .item:hover{background-color:rgba(238,243,246,.15)}.nav-menu__box .item i.sidenav-icon{width:2.5rem;height:1.4rem;display:grid;place-items:center}.nav-menu__box .item.item-selected{color:#9bdaff;background-color:rgba(155,218,255,.15);font-weight:medium}.nav-menu__box button.toggle-nav{display:none;position:absolute;padding:0;top:0;right:-1rem;background:rgb(77.5,186.5157480315,255);width:1.5rem;height:1.5rem;aspect-ratio:1;justify-content:center;align-items:center;border-radius:50%;box-shadow:none}.nav-menu.collapsed .nav-menu__logo .logo-container{display:flex;flex-direction:column;align-items:center;gap:.5rem}.nav-menu.collapsed .nav-menu__logo .logo-container>*:not(img){display:block}.nav-menu.collapsed .nav-menu__logo .nav-menu__logo-text{display:none !important}.nav-menu.collapsed .nav-menu__box .item{padding:.675rem 0;width:2.5rem;justify-content:center;transition:300ms}.nav-menu.collapsed .nav-menu__box .item span,.nav-menu.collapsed .nav-menu__box .item p{display:none !important}.nav-menu.collapsed button i{rotate:180deg}.nav-menu:hover button.toggle-nav{display:flex}.sidebar{width:13rem;background-color:#fff;display:flex;flex-direction:column}.sidebar a{text-decoration:none;text-transform:capitalize;padding:1rem;cursor:pointer;color:#999}.sidebar a:hover{color:#222}.sidebar .selected-sidebar-link{font-weight:bold;color:#222;border-left:5px solid #3ba4d7;animation:expand-left-border .1s}.sidebarquickview>h6{padding:.5rem}.sidebarquickview a{text-decoration:none;text-transform:capitalize;padding:.5rem 1rem;display:block;color:#999}.sidebarquickview a a:hover{color:#222}.sidebarquickview .selected-sidebarquickview-link{font-weight:bold;color:#222;border-left:5px solid #3ba4d7;animation:expand-left-border .1s}.node-panel{width:100%;padding:.5rem;animation:fadein .5s}@keyframes expand-left-border{from{border-left:0}to{border-left:5px solid #3ba4d7}}@media(max-width: 700px){.tab-content{flex-direction:column}.sidebar{width:100% !important;flex-direction:row !important;overflow-x:auto !important;overflow-y:hidden !important;white-space:nowrap !important;border-bottom:1px solid rgba(20,20,27,.1) !important;background:#fff !important;z-index:50 !important;flex-shrink:0 !important;height:auto !important;padding:0 !important}.sidebar a{display:inline-block !important;padding:.8rem 1.2rem !important;border-bottom:3px solid rgba(0,0,0,0) !important;border-left:none !important}.sidebar .selected-sidebar-link{border-left:none !important;border-bottom:3px solid #3ba4d7 !important;animation:none !important}.sidebarquickview>h4,.sidebarquickview>h6{display:none !important}}.posts{height:100%;margin-top:1rem;flex-direction:column;overflow:auto}.posts__heading{display:flex;flex-direction:column;justify-content:space-between}.posts-container{height:100%;padding:1rem;display:grid;grid-template-columns:repeat(auto-fill, minmax(150px, 1fr));gap:2rem;border:1px solid rgba(20,20,27,.1);border-radius:4px;overflow:auto}.posts-container-card{min-height:240px;flex-direction:column;border:1px solid rgba(20,20,27,.5);border-radius:4px;cursor:pointer;text-align:center}.posts-container-card img{flex-basis:90%;object-fit:cover}.posts-container-card p{padding:0 .125rem;flex-basis:10%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.progress-bar{width:100%;height:2rem;position:relative;text-align:center;background-color:#eef3f6;border-radius:20px;overflow:hidden}.progress-bar__status{position:absolute;top:0;left:0;height:100%;color:#14141b;background-color:#019dff}.progress-bar__percent{position:absolute;inset:0;margin:auto;width:fit-content;height:fit-content}.progress-bar-chunks{position:relative;margin-top:.5rem;width:100%;height:2rem;display:flex;border-radius:.25rem;overflow:hidden;background-color:#eef3f6}.progress-bar-chunks .chunk{width:100%}.progress-bar-chunks .chunk[data-chunkVal="0"]{background-color:rgba(155,218,255,.2)}.progress-bar-chunks .chunk[data-chunkVal="1"]{background-color:#ff3a4a}.progress-bar-chunks .chunk[data-chunkVal="2"]{background-color:#019dff}.progress-bar-chunks .chunk[data-chunkVal="3"]{background-color:#fcba03}.progress-bar-chunks__percent{position:absolute;inset:0;margin:auto;width:fit-content;height:fit-content}.statusbar{display:flex;justify-content:space-between;align-items:center;height:28px;background-color:#14141b;border-top:1px solid #2e2e38;padding:0 1rem;font-size:.8rem;color:#94a3b8;z-index:100;box-sizing:border-box;user-select:none;flex-shrink:0}.statusbar-left{display:flex;align-items:center}.statusbar-right{display:flex;align-items:center;gap:1.5rem}.statusbar-item{display:flex;align-items:center}.statusbar-divider{width:1px;height:14px;background-color:#2e2e38}.status-bullet{width:8px;height:8px;border-radius:50%;display:inline-block;box-shadow:0 0 4px rgba(0,0,0,.5)}.widget{height:100%;padding:1rem;display:flex;flex-direction:column;gap:.5rem;background-color:#fff;border-radius:.5rem;overflow:auto}.widget .top-heading{display:flex;justify-content:space-between}.widget__heading{display:flex;justify-content:space-between;align-items:center;border-bottom:2px solid #999}.widget__body{height:100%;display:flex;flex-direction:column;overflow:auto}.widget__body-heading{display:flex;justify-content:space-between;align-items:center}.widget__body-heading .action{display:flex;gap:.5rem}.widget__body-content{height:100%;overflow:auto}.widget__body-box{display:flex;flex-direction:column;gap:.5rem}.widget-half{max-width:50%}#modal-container{display:none;position:fixed;z-index:1;height:100%;top:0;left:0;width:100%;background-color:rgba(0,0,0,.2)}.modal-content{position:absolute;color:#555;width:40%;min-height:10rem;height:max-content;padding:1.5rem;inset:0;margin:auto;background-color:#fff;border-radius:.5rem;animation:fadein .5s;display:flex;flex-direction:column}.modal-content button:last-child{margin-top:auto}.modal-content .close-btn{position:absolute;right:1.5rem}.modal-content .widget{padding:0}#notification-container{position:absolute;bottom:0;right:0}.login-page{background-image:linear-gradient(-45deg, rgba(1, 157, 255, 0.75), rgba(17, 143, 204, 0.75));height:100%;animation:fadein .5s}.login-page .login-container{background-color:#fff;box-shadow:3px 3px 5px rgba(20,20,27,.4);margin:auto;position:relative;top:100px;max-width:400px;max-height:500px;border-radius:5px;display:flex;flex-direction:column;align-items:center}.login-page .login-container input{padding:.375rem .75rem;border-radius:.275rem}.login-page .login-container *{margin-bottom:1rem}.login-page .login-container>img{margin:1rem 0 2rem}.login-page .login-container extra{margin:0}.login-page .login-container>a{text-decoration:underline;cursor:pointer}.login-page .extra>label,.login-page .extra>br,.login-page .extra>input{margin-bottom:0}.homepage{margin:2rem auto 0;display:flex;flex-direction:column;gap:4rem}.homepage .logo{display:flex;justify-content:center;align-items:center}.homepage .logo img{width:90px}.homepage .logo .retroshareText{display:flex;flex-direction:column;align-items:center}.homepage .logo .retroshareText .retrotext{font-size:36px;font-weight:600;line-height:1.125}.homepage .logo .retroshareText .retrotext>span{color:#118fcc}.homepage .logo .retroshareText>b{font-size:14px;line-height:1}.homepage .certificate{display:flex;flex-direction:column;gap:4rem}.homepage .certificate__heading{text-align:center}.homepage .certificate__heading>h1{margin-bottom:1rem}.homepage .certificate__content{display:flex;flex-direction:column;gap:2rem;padding:2rem;text-align:center;border:1.5px solid rgba(17,143,204,.2);border-radius:6px;box-shadow:0px 0px 8px 2px rgba(20,20,27,.05)}.homepage .certificate__content .rsId>p{margin-bottom:.5rem;color:#118fcc}.homepage .certificate__content .retroshareID{padding:.25rem;display:flex;align-items:center;justify-self:start;font-size:1.25rem;border-radius:4px;background:rgba(20,20,27,.05)}.homepage .certificate__content .retroshareID .textArea{padding:0;width:100%;height:auto;font-size:1rem;font-family:monospace;background:rgba(0,0,0,0);border:none;resize:none;overflow:hidden;field-sizing:content}.homepage .certificate__content .retroshareID i{color:#118fcc}.homepage .certificate__content .retroshareID>i{margin:0 .5rem;cursor:pointer}.homepage .certificate__content .webhelp{padding:.5rem;background:#f5f5f5;display:flex;justify-content:center;align-items:center;gap:.5rem;border-radius:4px;border:1px solid rgba(20,20,27,.5);width:fit-content;cursor:pointer}.homepage .certificate__content .webhelp-container{display:grid;place-items:center}.homepage .certificate__content .webhelp:hover{background:#eef3f6;border:1px solid #14141b}.homepage .certificate__content .webhelp>i{font-size:1.2rem;color:green}.homepage .certificate__content .add-friend>h6,.homepage .certificate__content .webhelp-container>h6{font-weight:normal;margin-bottom:.5rem}.network-container{display:flex;height:100%;width:100%;overflow:hidden;background-color:#f1f5f9}.network-left-pane{width:320px;min-width:300px;max-width:350px;border-right:1px solid #cbd5e1;display:flex;flex-direction:column;background:#fff;box-shadow:2px 0 5px rgba(0,0,0,.05)}.own-profile-card{padding:1.25rem;border-bottom:1px solid #e2e8f0;background:linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);display:flex;flex-direction:column;gap:.75rem}.own-profile-card .profile-header{display:flex;align-items:center;gap:1rem}.own-profile-card .profile-info{display:flex;flex-direction:column;flex:1;overflow:hidden}.own-profile-card .profile-info .profile-name{font-weight:700;color:#1e293b;font-size:1.1rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.own-profile-card .profile-info .profile-status{font-size:.85rem;color:#10b981;font-weight:500;display:flex;align-items:center;gap:.35rem}.own-profile-card .profile-info .profile-status::before{content:"";display:inline-block;width:8px;height:8px;background-color:#10b981;border-radius:50%}.own-profile-card .own-identity-select-container{display:flex;flex-direction:column;gap:.25rem}.own-profile-card .own-identity-select-container label{font-size:.75rem;color:#64748b;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.own-profile-card .own-identity-select-container select.own-identity-select{width:100%;padding:.375rem .5rem;font-size:.85rem;border:1px solid #cbd5e1;border-radius:.375rem;background-color:#fff;color:#334155;outline:none;cursor:pointer;transition:border-color .2s}.own-profile-card .own-identity-select-container select.own-identity-select:focus{border-color:#3ba4d7}.friends-list-container{flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative}.friends-list-container .searchbar-container{padding:.75rem 1rem;border-bottom:1px solid #e2e8f0}.friends-list-container .searchbar-container input.searchbar{width:100%;padding:.5rem .75rem;font-size:.9rem;border:1px solid #cbd5e1;border-radius:.375rem;background-color:#f8fafc;outline:none;transition:all .2s}.friends-list-container .searchbar-container input.searchbar:focus{background-color:#fff;border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.friends-list-container .friends-scroll{flex:1;overflow-y:auto;padding:.5rem 0}.friend-list-item{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;margin:.125rem .5rem;border-radius:.5rem;cursor:pointer;transition:all .2s}.friend-list-item:hover{background-color:#f1f5f9}.friend-list-item.selected{background-color:#e0f2fe}.friend-list-item.selected .friend-name{color:#0369a1;font-weight:600}.friend-list-item .friend-avatar{flex-shrink:0}.friend-list-item .friend-meta{flex:1;min-width:0}.friend-list-item .friend-meta .friend-name{font-size:.95rem;color:#334155;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .2s}.friend-list-item .friend-meta .friend-status{font-size:.8rem;color:#94a3b8}.friend-list-item .friend-meta .friend-status.online{color:#10b981;font-weight:500}.network-right-pane{flex:1;display:flex;flex-direction:column;overflow:hidden;background-color:#f8fafc}.network-pane-placeholder{flex:1;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#94a3b8;gap:1rem;padding:2rem;text-align:center}.network-pane-placeholder i{font-size:4rem;color:#cbd5e1}.network-pane-placeholder p{font-size:1.1rem;max-width:400px}.network-tabs{display:flex;background-color:#fff;border-bottom:1px solid #cbd5e1;padding:.5rem 1rem 0;gap:.5rem}.network-tabs .tab-btn{padding:.625rem 1.25rem;font-size:.95rem;font-weight:600;color:#64748b;background:rgba(0,0,0,0);border:none;border-radius:.375rem .375rem 0 0;border-bottom:3px solid rgba(0,0,0,0);cursor:pointer;box-shadow:none;transition:all .2s}.network-tabs .tab-btn:hover{color:#334155;background-color:#f1f5f9}.network-tabs .tab-btn.active{color:#3ba4d7;border-bottom-color:#3ba4d7;background-color:rgba(0,0,0,0)}.network-tab-content{flex:1;overflow-y:auto;padding:1.5rem}.network-detail-view{display:flex;flex-direction:column;gap:1.5rem}.network-detail-view .detail-header{display:flex;align-items:center;gap:1.5rem;padding-bottom:1.5rem;border-bottom:1px solid #e2e8f0}.network-detail-view .detail-header .detail-title{flex:1}.network-detail-view .detail-header .detail-title h2{font-size:1.75rem;font-weight:800;color:#1e293b;margin-bottom:.25rem}.network-detail-view .detail-header .detail-title .detail-subtitle{font-size:.9rem;color:#64748b;display:flex;align-items:center;gap:.5rem}.network-detail-view .detail-header .detail-actions{display:flex;gap:.75rem}.network-detail-view .detail-header .detail-actions button{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.9rem}.network-detail-view .detail-section{background-color:#fff;border-radius:.5rem;border:1px solid #e2e8f0;padding:1.25rem;box-shadow:0 1px 3px rgba(0,0,0,.02)}.network-detail-view .detail-section h3{font-size:1.1rem;font-weight:700;color:#334155;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #f1f5f9}.network-detail-view .detail-section .info-grid{display:grid;grid-template-columns:120px 1fr;row-gap:.75rem;font-size:.9rem}.network-detail-view .detail-section .info-grid .info-label{font-weight:600;color:#64748b}.network-detail-view .detail-section .info-grid .info-value{color:#1e293b;word-break:break-all}.network-detail-view .locations-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));gap:1rem}.network-detail-view .location-card{background-color:#fff;border:1px solid #e2e8f0;border-radius:.5rem;padding:1rem;display:flex;flex-direction:column;gap:.5rem;box-shadow:0 1px 3px rgba(0,0,0,.02)}.network-detail-view .location-card .loc-header{display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #f1f5f9;padding-bottom:.5rem;margin-bottom:.25rem}.network-detail-view .location-card .loc-header .loc-name{font-weight:700;color:#334155;font-size:.95rem}.network-detail-view .location-card .loc-header .loc-status{font-size:.75rem;font-weight:600;padding:.125rem .5rem;border-radius:.25rem}.network-detail-view .location-card .loc-header .loc-status.online{background-color:#d1fae5;color:#065f46}.network-detail-view .location-card .loc-header .loc-status.offline{background-color:#f1f5f9;color:#475569}.network-detail-view .location-card .loc-body{font-size:.85rem;display:grid;grid-template-columns:80px 1fr;row-gap:.25rem}.network-detail-view .location-card .loc-body .loc-label{color:#64748b}.network-detail-view .location-card .loc-body .loc-val{color:#334155;word-break:break-all}.network-detail-view .location-card .loc-footer{margin-top:.5rem;display:flex;justify-content:flex-end}.network-detail-view .location-card .loc-footer button{font-size:.8rem;padding:.25rem .75rem}.network-chat-view{display:flex;flex-direction:column;height:100%;overflow:hidden;background-color:#f8fafc}.network-chat-view .chat-messages{flex:1;overflow-y:auto;padding:1.25rem;display:flex;flex-direction:column;gap:1rem}.network-chat-view .chat-bubble-container{display:flex;flex-direction:column;max-width:70%}.network-chat-view .chat-bubble-container.outgoing{align-self:flex-end;align-items:flex-end}.network-chat-view .chat-bubble-container.outgoing .chat-bubble{background-color:#3ba4d7;color:#fff;border-bottom-right-radius:.125rem}.network-chat-view .chat-bubble-container.incoming{align-self:flex-start;align-items:flex-start}.network-chat-view .chat-bubble-container.incoming .chat-bubble{background-color:#fff;color:#1e293b;border:1px solid #e2e8f0;border-bottom-left-radius:.125rem}.network-chat-view .chat-bubble-container .chat-sender{font-size:.75rem;color:#64748b;margin-bottom:.25rem;padding:0 .25rem}.network-chat-view .chat-bubble-container .chat-bubble{padding:.625rem .875rem;border-radius:.75rem;font-size:.925rem;line-height:1.4;white-space:break-spaces;word-break:break-word;box-shadow:0 1px 2px rgba(0,0,0,.05)}.network-chat-view .chat-bubble-container .chat-time{font-size:.7rem;color:#94a3b8;margin-top:.25rem;padding:0 .25rem}.network-chat-view .chat-input-area{padding:1rem;background-color:#fff;border-top:1px solid #cbd5e1;display:flex;gap:.75rem;align-items:center}.network-chat-view .chat-input-area textarea.chat-textarea{flex:1;resize:none;height:40px;padding:.5rem .75rem;border:1px solid #cbd5e1;border-radius:.375rem;font-size:.9rem;outline:none;transition:all .2s}.network-chat-view .chat-input-area textarea.chat-textarea:focus{border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.network-chat-view .chat-input-area button.send-btn{padding:.5rem 1.25rem;font-size:.9rem;height:40px;display:flex;align-items:center;gap:.5rem}.network-chat-view .chat-warning{flex:1;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#64748b;text-align:center;padding:2rem;gap:1rem}.network-chat-view .chat-warning i{font-size:3rem;color:#cbd5e1}.network-chat-view .chat-warning h4{font-weight:700;color:#334155}.network-chat-view .chat-warning p{max-width:350px;font-size:.9rem}.identity{color:#444;font-size:1.1em;margin:20px;padding:10px;border:1px solid #aaa;border-radius:20px}.identity>h4{margin:5px;font-size:1.3em}.identity button{font-size:.9em}.identity .details{display:grid;grid-template-columns:140px auto;grid-row-gap:5px;justify-content:left}.defaultAvatar{width:3rem;height:3rem;aspect-ratio:1;background:#b0c4de;border-radius:50%;display:grid;place-items:center}.defaultAvatar p{font-weight:900;color:#666f7f;transform:translateY(1px)}img.avatar{display:block;width:3rem;height:max-content;aspect-ratio:1;margin-right:.3em;border-radius:50%}.counter{margin-left:.5em}.counter:before{content:"("}.counter:after{content:")"}.chatInit{margin-left:.5em;color:green;cursor:pointer}.people-sidebar-header{display:flex;flex-direction:column;padding:.75rem 1rem .5rem 1rem;gap:.75rem;border-bottom:1px solid #e2e8f0;background-color:#fff}.people-sidebar-header .searchbar-wrapper{position:relative;display:flex;align-items:center}.people-sidebar-header .searchbar-wrapper i.fa-search{position:absolute;left:.85rem;color:#94a3b8;font-size:.9rem}.people-sidebar-header .searchbar-wrapper input.searchbar-input{width:100%;padding:.5rem .75rem .5rem 2.25rem;border:1px solid #e2e8f0;border-radius:.5rem;font-size:.9rem;background-color:#f8fafc;color:#1e293b;outline:none;transition:all .2s ease}.people-sidebar-header .searchbar-wrapper input.searchbar-input:focus{border-color:#3b82f6;background-color:#fff;box-shadow:0 0 0 3px rgba(59,130,246,.1)}.people-sidebar-header .segmented-control{display:flex;background-color:#f1f5f9;padding:3px;border-radius:.5rem;gap:4px}.people-sidebar-header .segmented-control button.segment-tab{flex:1;display:flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem .75rem;font-size:.9rem;font-weight:600;color:#64748b;background:rgba(0,0,0,0);border:none;border-radius:.375rem;cursor:pointer;box-shadow:none;transition:all .2s ease}.people-sidebar-header .segmented-control button.segment-tab:hover{color:#1e293b}.people-sidebar-header .segmented-control button.segment-tab.active{background-color:#fff;color:#0f172a;box-shadow:0 1px 3px rgba(0,0,0,.1),0 1px 2px rgba(0,0,0,.06)}.people-sidebar-header .segmented-control button.segment-tab.active .segment-badge{background-color:#019dff;color:#fff}.people-sidebar-header .segmented-control button.segment-tab .segment-badge{display:inline-flex;align-items:center;justify-content:center;background-color:#cbd5e1;color:#334155;font-size:.75rem;font-weight:700;min-width:1.25rem;height:1.25rem;padding:0 .35rem;border-radius:9999px;line-height:1;transition:all .2s ease}.people-sidebar-header .sub-filter-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:32px}.people-sidebar-header .sub-filter-row select.filter-select{padding:.35rem .6rem;border:1px solid #cbd5e1;border-radius:.375rem;font-size:.85rem;font-weight:600;color:#475569;background-color:#fff;cursor:pointer;outline:none}.people-sidebar-header .sub-filter-row .btn-add-id{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:.375rem;background-color:#3b82f6;color:#fff;border:none;cursor:pointer;font-size:.9rem;transition:background-color .2s}.people-sidebar-header .sub-filter-row .btn-add-id:hover{background-color:#2563eb}.friends-list-container .people-context-menu{position:absolute;left:2rem;width:210px;background-color:#fff;border:1px solid #e2e8f0;box-shadow:0 4px 10px rgba(0,0,0,.15);border-radius:.375rem;z-index:1010;padding:.25rem 0;display:flex;flex-direction:column}.friends-list-container .people-context-menu .menu-item{padding:.5rem 1rem;font-size:.85rem;color:#334155;cursor:pointer;display:flex;align-items:center;transition:background-color .2s}.friends-list-container .people-context-menu .menu-item:hover{background-color:#f1f5f9;color:#0f172a}.people-container{display:flex;height:calc(100vh - 55px);width:100%;overflow:hidden}.people-left-pane{width:320px;border-right:1px solid #cbd5e1;display:flex;flex-direction:column;background-color:#fff;overflow:hidden}.people-right-pane{flex:1;display:flex;flex-direction:column;overflow:hidden;background-color:#f8fafc}.people-left-pane .chat-item{display:flex;align-items:center;padding:.75rem 1rem;gap:.75rem;border-bottom:1px solid #f1f5f9;cursor:pointer;transition:background-color .15s ease;position:relative}.people-left-pane .chat-item:hover{background-color:#f8fafc}.people-left-pane .chat-item.selected{background-color:#eff6ff;border-left:3px solid #3b82f6}.people-left-pane .chat-item .chat-avatar-wrapper{position:relative;flex-shrink:0}.people-left-pane .chat-item .chat-avatar-wrapper .status-dot{position:absolute;bottom:0;right:0;width:10px;height:10px;border-radius:50%;border:2px solid #fff}.people-left-pane .chat-item .chat-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:.15rem}.people-left-pane .chat-item .chat-info .chat-name{font-size:.9rem;font-weight:700;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.people-left-pane .chat-item .chat-info .chat-last-msg{font-size:.8rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.people-left-pane .chat-item .chat-meta{display:flex;flex-direction:column;align-items:flex-end;gap:.25rem;flex-shrink:0}.people-left-pane .chat-item .chat-meta .chat-time{font-size:.75rem;color:#94a3b8;white-space:nowrap}.lobby{margin:10px;border:1px solid #aaa;border-radius:20px}.lobby .mainname{margin:20px;font-weight:100;font-size:1.2em}.topic{color:#666}.lobby>.topic{font-size:.95em;margin-left:25px;margin-bottom:5px}.lefttitle{margin-top:15px;margin-bottom:0;font-weight:100;font-size:1.2em}.leftname{margin-top:5px;margin-bottom:5px;padding:5px;font-weight:100;font-size:1em}.leftlobby>.topic{font-size:.75em;margin-left:15px;margin-bottom:5px}.subscribed,.public{cursor:pointer}.leftlobby{border:1px solid #aaa;border-radius:10px;margin-top:5px;background-color:#fff}.leftlobby.selected-lobby,.selectedidentity{color:#fff;background-color:#3ba4d7}.rightbar{position:absolute;width:185px;background-color:#fff;overflow:auto;top:130px;bottom:15px;right:15px}.user{padding:5px}.lobbyName{padding:15px;margin-top:2rem}.lobbies{position:absolute;width:185px;left:165px;bottom:15px;top:130px;overflow:auto}.messages,.setup{position:absolute;background-color:#fff;top:130px;left:360px;right:215px;overflow:auto}.messages{bottom:115px}.messagetext{white-space:break-spaces;margin-right:5px}.message>*{margin-left:5px}.username{color:#006400;font-weight:bolder}.chatMessage{position:absolute;background-color:#fff;height:85px;bottom:15px;right:215px;left:360px}textarea.chatMsg{height:100%;width:100%}.chatatchar{margin-left:.2em;margin-right:.2em;color:silver}.setupicon{margin-left:1em;cursor:pointer}.leaveicon{margin-left:1em;cursor:pointer;color:#d40000}.selectidentity{margin:15px;font-size:1.2em}.setup>.identity{cursor:pointer}.setup{bottom:15px}.createDistantChat{margin-top:1em}.no-lobbies .messages,.no-lobbies .chatMessage,.no-lobbies .setup{left:165px}@media(min-width: 900px){.node-panel.chat-room{display:grid !important;grid-template-columns:250px 1fr 200px !important;grid-template-rows:auto 1fr auto !important;grid-template-areas:"lobbies header rightbar" "lobbies messages rightbar" "lobbies input rightbar" !important;padding:0 !important;height:100% !important}.node-panel.chat-room .lobbyName{grid-area:header;padding:10px;border-bottom:1px solid #eee;margin:0;z-index:10;background:#fff}.node-panel.chat-room .lobbies{grid-area:lobbies;position:static !important;width:auto !important;height:auto !important;border-right:1px solid #ccc;overflow-y:auto;display:block !important;top:auto !important;bottom:auto !important;left:auto !important}.node-panel.chat-room .messages{grid-area:messages;position:static !important;width:auto !important;height:auto !important;overflow-y:auto;padding:10px;left:auto !important;right:auto !important;top:auto !important;bottom:auto !important;margin:0 !important}.node-panel.chat-room .rightbar{grid-area:rightbar;position:static !important;width:auto !important;border-left:1px solid #ccc;overflow-y:auto;display:block !important}.node-panel.chat-room .chatMessage{grid-area:input;position:static !important;width:auto !important;height:auto !important;border-top:1px solid #eee;left:auto !important;right:auto !important;bottom:auto !important;flex:0 0 auto;padding:10px !important;background:#fff;z-index:10}}@media(max-width: 899px){.node-panel.chat-room{display:flex !important;flex-direction:column !important;height:100% !important;position:relative !important}.node-panel.chat-room .lobbyName{flex:0 0 auto}.node-panel.chat-room .messages{flex:1 !important;overflow-y:auto !important;position:relative !important;top:0 !important;bottom:0 !important;left:0 !important;right:0 !important;width:100% !important;height:auto !important;margin:0 !important}.node-panel.chat-room .chatMessage{flex:0 0 auto !important;position:relative !important;bottom:0 !important;left:0 !important;right:0 !important;width:100% !important;height:auto !important;z-index:100}.node-panel.chat-room .rightbar,.node-panel.chat-room .lobbies{display:none !important;position:fixed !important;top:60px !important;bottom:0 !important;width:80% !important;background:#fff !important;z-index:200 !important;box-shadow:2px 0 10px rgba(0,0,0,.2) !important}.node-panel.chat-room.show-lobbies .lobbies{display:block !important;left:0 !important}.node-panel.chat-room.show-users .rightbar{display:block !important;right:0 !important}.chat-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.4);z-index:150}.show-lobbies .chat-overlay,.show-users .chat-overlay{display:block}.mobile-menu-icons{display:flex;gap:15px;font-size:1.2rem}.mobile-menu-icons i{cursor:pointer;padding:5px}}@media(min-width: 900px){.mobile-menu-icons{display:none}}.chat-hub-container{display:flex;height:100%;width:100%;overflow:hidden;background-color:#f1f5f9}.chat-hub-left-pane{width:320px;min-width:300px;max-width:350px;border-right:1px solid #cbd5e1;display:flex;flex-direction:column;background:#fff;box-shadow:2px 0 5px rgba(0,0,0,.05)}.chat-own-profile-card{padding:1.25rem;border-bottom:1px solid #e2e8f0;background:linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);position:relative}.chat-own-profile-card .profile-header{display:flex;align-items:center;gap:1rem}.chat-own-profile-card .profile-info{display:flex;flex-direction:column;flex:1;overflow:hidden}.chat-own-profile-card .profile-info .profile-name{font-weight:700;color:#1e293b;font-size:1.1rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat-own-profile-card .profile-info .profile-status{font-size:.85rem;color:#10b981;font-weight:500;display:flex;align-items:center;gap:.35rem}.chat-own-profile-card .profile-info .profile-status::before{content:"";display:inline-block;width:8px;height:8px;background-color:#10b981;border-radius:50%}.chat-rooms-list-container{flex:1;display:flex;flex-direction:column;overflow:hidden}.chat-rooms-list-container .searchbar-container{padding:.75rem 1rem;border-bottom:1px solid #e2e8f0}.chat-rooms-list-container .searchbar-container input.searchbar{width:100%;padding:.5rem .75rem;font-size:.9rem;border:1px solid #cbd5e1;border-radius:.375rem;background-color:#f8fafc;outline:none;transition:all .2s}.chat-rooms-list-container .searchbar-container input.searchbar:focus{background-color:#fff;border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.chat-rooms-list-container .rooms-scroll{flex:1;overflow-y:auto;padding:.5rem 0}.rooms-section-title{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem .375rem;font-size:.75rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em}.rooms-section-title i{font-size:.7rem;color:#94a3b8}.chat-room-list-item{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;margin:.125rem .5rem;border-radius:.5rem;cursor:pointer;transition:all .2s}.chat-room-list-item:hover{background-color:#f1f5f9}.chat-room-list-item.selected{background-color:#e0f2fe}.chat-room-list-item.selected .room-name{color:#0369a1;font-weight:600}.chat-room-list-item .room-icon{flex-shrink:0;width:36px;height:36px;border-radius:.5rem;background:linear-gradient(135deg, #3ba4d7, #0ea5e9);display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.35rem}.chat-room-list-item.public-room .room-icon{background:linear-gradient(135deg, #10b981, #059669)}.chat-room-list-item .room-meta{flex:1;min-width:0}.chat-room-list-item .room-meta .room-name{font-size:.95rem;color:#334155;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .2s}.chat-room-list-item .room-meta .room-topic{font-size:.8rem;color:#94a3b8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat-room-list-item .room-badge{flex-shrink:0;min-width:24px;height:24px;border-radius:12px;background-color:#e2e8f0;color:#475569;font-size:.75rem;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 .375rem}.chat-hub-right-pane{flex:1;display:flex;flex-direction:column;overflow:hidden;background-color:#f8fafc}.chat-pane-placeholder{flex:1;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#94a3b8;gap:1rem;padding:2rem;text-align:center}.chat-pane-placeholder i{font-size:4rem;color:#cbd5e1}.chat-pane-placeholder p{font-size:1.1rem;max-width:400px}.chat-hub-tab-content{flex:1;overflow-y:auto;padding:1.5rem}.chat-room-detail-view{display:flex;flex-direction:column;gap:1.5rem}.chat-room-detail-view .detail-header{display:flex;align-items:flex-start;gap:1.5rem;padding-bottom:1.5rem;border-bottom:1px solid #e2e8f0;flex-wrap:wrap}.chat-room-detail-view .detail-header .detail-title{flex:1;min-width:200px}.chat-room-detail-view .detail-header .detail-title h2{font-size:1.75rem;font-weight:800;color:#1e293b;margin-bottom:.25rem}.chat-room-detail-view .detail-header .detail-title .detail-subtitle{font-size:.9rem;color:#64748b;display:flex;align-items:center;gap:.5rem}.chat-room-detail-view .detail-header .detail-actions{display:flex;gap:.75rem;flex-wrap:wrap}.chat-room-detail-view .detail-header .detail-actions button{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.9rem}.chat-room-detail-view .detail-section{background-color:#fff;border-radius:.5rem;border:1px solid #e2e8f0;padding:1.25rem;box-shadow:0 1px 3px rgba(0,0,0,.02)}.chat-room-detail-view .detail-section h3{font-size:1.1rem;font-weight:700;color:#334155;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #f1f5f9}.chat-room-detail-view .detail-section .info-grid{display:grid;grid-template-columns:130px 1fr;row-gap:.75rem;font-size:.9rem}.chat-room-detail-view .detail-section .info-grid .info-label{font-weight:600;color:#64748b}.chat-room-detail-view .detail-section .info-grid .info-value{color:#1e293b;word-break:break-all}.participants-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(180px, 1fr));gap:.5rem}.participant-card{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background-color:#f8fafc;border:1px solid #e2e8f0;border-radius:.375rem}.participant-card .participant-name{font-size:.875rem;color:#334155;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-participants{color:#94a3b8;font-size:.9rem;font-style:italic}.detail-actions-footer{display:flex;gap:.75rem}.detail-actions-footer button{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.9rem}.join-description{color:#64748b;font-size:.9rem;margin-bottom:1rem}.identities-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));gap:.75rem}.identity-card{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background-color:#f8fafc;border:1px solid #e2e8f0;border-radius:.5rem;cursor:pointer;transition:all .2s}.identity-card:hover{background-color:#e0f2fe;border-color:#3ba4d7}.identity-card .identity-name{font-size:.95rem;font-weight:600;color:#334155}.identity-card i{color:#3ba4d7;font-size:.9rem}.no-rooms{padding:1rem;color:#94a3b8;text-align:center;font-style:italic}@media(max-width: 899px){.chat-hub-container{flex-direction:column}.chat-hub-left-pane{width:100%;min-width:0;max-width:none;max-height:45%;border-right:none;border-bottom:1px solid #cbd5e1}.chat-hub-right-pane{flex:1;min-height:0}}.chat-hub-header-bar{padding:.75rem 1.5rem;background-color:#fff;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;justify-content:space-between;height:65px;flex-shrink:0}.chat-hub-header-bar .chat-header-info{display:flex;flex-direction:column;overflow:hidden}.chat-hub-header-bar .chat-header-info .chat-header-name{font-size:1.15rem;font-weight:800;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat-hub-header-bar .chat-header-info .chat-header-topic{font-size:.85rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:.125rem}.chat-hub-header-bar .chat-header-actions{display:flex;gap:.5rem}.chat-hub-header-bar .chat-header-actions button{display:flex;align-items:center;gap:.35rem;padding:.375rem .75rem;font-size:.85rem}.chat-hub-tabs-container{background-color:#fff;border-bottom:1px solid #cbd5e1;padding:.5rem 1.5rem 0}.chat-hub-tabs{display:flex;gap:.5rem}.chat-hub-tabs .tab-btn{padding:.625rem 1.25rem;font-size:.95rem;font-weight:600;color:#64748b;background:rgba(0,0,0,0);border:none;border-radius:.375rem .375rem 0 0;border-bottom:3px solid rgba(0,0,0,0);cursor:pointer;box-shadow:none;transition:all .2s;display:flex;align-items:center;gap:.5rem}.chat-hub-tabs .tab-btn:hover{color:#334155;background-color:#f1f5f9}.chat-hub-tabs .tab-btn.active{color:#3ba4d7;border-bottom-color:#3ba4d7;background-color:rgba(0,0,0,0)}.chat-hub-tab-content{flex:1;display:flex;flex-direction:column;overflow:hidden;background-color:#f8fafc}.chat-hub-conversation-layout{display:flex;flex-direction:row;height:100%;width:100%;overflow:hidden}.chat-hub-conversation-main{display:flex;flex-direction:column;flex:1;height:100%;overflow:hidden}.chat-hub-rightbar{width:200px;border-left:1px solid #cbd5e1;background-color:#fff;display:flex;flex-direction:column;flex-shrink:0;position:relative}.chat-hub-rightbar .rightbar-title{padding:.75rem 1rem;font-size:.85rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid #e2e8f0}.chat-hub-rightbar .rightbar-users-list{flex:1;overflow-y:auto;padding:.5rem}.chat-hub-rightbar .user{padding:.5rem .75rem;font-size:.9rem;color:#334155;border-radius:.375rem;transition:all .2s;display:flex;align-items:center;gap:.5rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;position:relative}.chat-hub-rightbar .user:hover{background-color:#f1f5f9;color:#0f172a}.chat-hub-rightbar .user .defaultAvatar{width:2rem;height:2rem;font-size:.9rem;flex-shrink:0}.chat-hub-rightbar .user img.avatar{width:2rem;height:2rem;flex-shrink:0}@media(max-width: 899px){.chat-hub-rightbar{display:none}}.chat-hub-messages{flex:1;overflow-y:auto;padding:1.25rem 1.5rem;display:flex;flex-direction:column;gap:1rem}.chat-hub-messages .message{display:flex;flex-direction:column;max-width:70%;padding:.625rem .875rem;border-radius:.75rem;font-size:.925rem;line-height:1.4;word-break:break-word;box-shadow:0 1px 2px rgba(0,0,0,.05)}.chat-hub-messages .message.incoming{align-self:flex-start;align-items:flex-start;background-color:#fff;color:#1e293b;border:1px solid #e2e8f0;border-bottom-left-radius:.125rem}.chat-hub-messages .message.outgoing{align-self:flex-end;align-items:flex-end;background-color:#3ba4d7;color:#fff;border-bottom-right-radius:.125rem}.chat-hub-messages .message .username{font-size:.75rem;margin-bottom:.25rem;padding:0 .125rem;font-weight:700}.chat-hub-messages .message.incoming .username{color:#0369a1}.chat-hub-messages .message.outgoing .username{color:#e0f2fe}.chat-hub-messages .message .messagetext{white-space:break-spaces;margin:0}.chat-hub-messages .message .datetime{font-size:.7rem;margin-top:.25rem;padding:0 .125rem;opacity:.8}.chat-hub-messages .message.incoming .datetime{color:#64748b}.chat-hub-messages .message.outgoing .datetime{color:#f1f5f9}.chat-hub-input-area{padding:.75rem 1.5rem;background-color:#fff;border-top:1px solid #cbd5e1;display:flex;gap:.75rem;align-items:flex-end;flex-shrink:0}.chat-hub-input-area textarea.chat-hub-textarea{flex:1;resize:vertical;min-height:40px;max-height:250px;height:40px;padding:.5rem .75rem;border:1px solid #cbd5e1;border-radius:.375rem;font-size:.9rem;outline:none;transition:border-color .2s,box-shadow .2s;background-color:#f8fafc}.chat-hub-input-area textarea.chat-hub-textarea:focus{background-color:#fff;border-color:#3ba4d7;box-shadow:0 0 0 3px rgba(59,164,215,.15)}.chat-hub-input-area button.chat-hub-send-btn{padding:.5rem 1.25rem;font-size:.9rem;height:40px;display:flex;align-items:center;gap:.5rem;border-radius:.375rem}.chat-hub-messages.compact-container,.messages.compact-container{gap:0 !important;padding:.75rem 1rem !important;background-color:#fff !important;display:flex !important;flex-direction:column !important}.chat-hub-messages.compact-container .message.compact,.messages.compact-container .message.compact{display:block !important;max-width:100% !important;padding:.1rem 0 !important;border-radius:0 !important;background-color:rgba(0,0,0,0) !important;border:none !important;box-shadow:none !important;align-self:flex-start !important;font-size:.875rem !important;line-height:1.45 !important;margin:0 !important;white-space:nowrap !important}.chat-hub-messages.compact-container .message.compact:hover,.messages.compact-container .message.compact:hover{background-color:#f8fafc !important;overflow:visible !important;white-space:normal !important}.chat-hub-messages.compact-container .message.compact .datetime,.messages.compact-container .message.compact .datetime{color:#a0a0a0 !important;margin-right:.4rem !important;font-size:.78rem !important;font-family:monospace !important;opacity:1 !important;display:inline !important}.chat-hub-messages.compact-container .message.compact .username,.messages.compact-container .message.compact .username{font-weight:bold !important;margin-right:.2rem !important;font-size:.875rem !important;display:inline !important}.chat-hub-messages.compact-container .message.compact .messagetext,.messages.compact-container .message.compact .messagetext{color:#1e293b !important;white-space:normal !important;word-break:break-word !important;display:inline !important;margin:0 !important}.chat-create-lobby-btn{position:absolute;bottom:.5rem;right:1.25rem;background-color:#0084ff;color:#fff;border:none;border-radius:.375rem;padding:.35rem .75rem;font-size:.85rem;font-weight:600;cursor:pointer;box-shadow:0 4px 6px -1px rgba(0,132,255,.2),0 2px 4px -1px rgba(0,132,255,.1);transition:background-color .2s,transform .2s;display:flex;align-items:center;gap:.25rem}.chat-create-lobby-btn:hover{background-color:#0073e6;transform:translateY(-1px)}.chat-create-lobby-btn:active{transform:translateY(0)}.chat-hub-rightbar .user .user-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}.user-tooltip{position:absolute;width:260px;background-color:#ffffe1;border:1px solid #7f7f7f;box-shadow:2px 2px 6px rgba(0,0,0,.25);padding:.5rem;border-radius:.25rem;z-index:10000;white-space:normal;display:flex;gap:.5rem;align-items:flex-start}.chat-hub-rightbar .user-tooltip{left:-275px;transform:translateY(-50%);z-index:1000}.user-tooltip .tooltip-avatar{flex-shrink:0}.user-tooltip .tooltip-details{display:flex;flex-direction:column;gap:.25rem;font-size:.8rem;color:#000;text-align:left}.user-tooltip .tooltip-row{line-height:1.2}.user-tooltip .tooltip-label{font-weight:bold}.user-tooltip .tooltip-value{font-weight:normal;word-break:break-all}.user-tooltip .tooltip-value.tooltip-id{font-family:monospace}.chat-hub-rightbar .rightbar-context-menu{position:absolute;right:1rem;width:210px;background-color:#fff;border:1px solid #e2e8f0;box-shadow:0 4px 10px rgba(0,0,0,.15);border-radius:.375rem;z-index:1010;padding:.25rem 0;display:flex;flex-direction:column}.chat-hub-rightbar .rightbar-context-menu .menu-item{padding:.5rem 1rem;font-size:.85rem;color:#334155;cursor:pointer;display:flex;align-items:center;transition:background-color .2s}.chat-hub-rightbar .rightbar-context-menu .menu-item:hover{background-color:#f1f5f9;color:#0f172a}.chat-emoji{font-size:1.45em;line-height:1;vertical-align:-0.15em;display:inline-block}.chat-hub-attach-btn,.chat-hub-action-btn{background-color:rgba(0,0,0,0) !important;border:none !important;font-size:1.15rem !important;color:#64748b !important;cursor:pointer !important;padding:.4rem .5rem !important;border-radius:.375rem !important;flex-shrink:0 !important;display:inline-flex !important;align-items:center !important;justify-content:center !important;transition:all .2s !important;box-shadow:none !important;margin:0 !important;line-height:1 !important;height:36px !important;width:36px !important}.chat-hub-attach-btn:hover,.chat-hub-action-btn:hover{background-color:#f1f5f9 !important;color:#3b82f6 !important;transform:none !important}.attach-modal-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:rgba(15,23,42,.4);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:2000}.attach-modal{background-color:#fff;border-radius:.5rem;width:450px;max-width:90%;padding:1.5rem;box-shadow:0 10px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);display:flex;flex-direction:column;gap:1rem}.attach-modal .attach-modal-header{display:flex;align-items:center;gap:.6rem;margin-bottom:.25rem}.attach-modal .attach-modal-icon{font-size:1.2rem;color:#3b82f6}.attach-modal h4{margin:0;font-size:1.2rem;color:#0f172a}.attach-modal p{margin:0;font-size:.9rem;color:#475569}.attach-modal .attach-path-row{display:flex;gap:.5rem;align-items:center}.attach-modal .attach-path-row input[type=text]{flex:1;padding:.75rem;border:1px solid #cbd5e1;border-radius:.375rem;font-size:.9rem;outline:none;transition:border-color .2s;min-width:0}.attach-modal .attach-path-row input[type=text]:focus{border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59,130,246,.15)}.attach-browse-btn{flex-shrink:0;display:flex;align-items:center;gap:.35rem;padding:.625rem .9rem;font-size:.875rem;background-color:#f1f5f9;color:#334155;border:1px solid #cbd5e1;border-radius:.375rem;cursor:pointer;box-shadow:none;transition:background-color .2s,border-color .2s;white-space:nowrap}.attach-browse-btn:hover{background-color:#e2e8f0;border-color:#94a3b8}.attach-path-hint{display:flex;align-items:flex-start;gap:.5rem;padding:.6rem .75rem;background-color:#fffbeb;border:1px solid #fcd34d;border-left:3px solid #f59e0b;border-radius:.375rem;font-size:.825rem;color:#92400e;line-height:1.45}.attach-path-hint i{color:#f59e0b;margin-top:.1rem;flex-shrink:0}.attach-path-hint code{font-family:monospace;background-color:rgba(245,158,11,.15);padding:.05rem .25rem;border-radius:.2rem}.attach-modal .hashing-spinner{display:flex;align-items:center;gap:.5rem;font-size:.9rem;color:#3b82f6}.attach-modal .error-text{color:#ef4444;font-size:.85rem;margin:0}.attach-modal .modal-buttons{display:flex;justify-content:flex-end;gap:.75rem;margin-top:.5rem}.attach-modal .modal-buttons button{padding:.5rem 1rem;font-size:.9rem;border-radius:.25rem;border:none;cursor:pointer;transition:opacity .2s}.attach-modal .modal-buttons button:hover{opacity:.9}.chat-hub-emoji-btn{background-color:rgba(0,0,0,0);border:none;font-size:1.3rem;cursor:pointer;padding:.35rem .4rem;margin-right:.25rem;flex-shrink:0;display:flex;align-items:center;justify-content:center;border-radius:.375rem;line-height:1;transition:background-color .15s,transform .15s;box-shadow:none}.chat-hub-emoji-btn:hover{background-color:#f1f5f9;transform:scale(1.1)}.emoji-picker-wrapper{position:relative;flex-shrink:0;display:flex;align-items:center}.emoji-picker{position:absolute;bottom:calc(100% + .5rem);left:0;width:320px;background-color:#fff;border:1px solid #e2e8f0;border-radius:.625rem;box-shadow:0 8px 30px -4px rgba(0,0,0,.18),0 4px 12px -2px rgba(0,0,0,.1);z-index:3000;display:flex;flex-direction:column;overflow:hidden;animation:emoji-pop .15s ease-out}.emoji-search-row{display:flex;align-items:center;gap:.4rem;padding:.6rem .75rem .4rem;border-bottom:1px solid #f1f5f9}.emoji-search-icon{color:#94a3b8;font-size:.8rem;flex-shrink:0}.emoji-search-input{flex:1;border:1px solid #e2e8f0;border-radius:.375rem;padding:.3rem .5rem;font-size:.85rem;outline:none;background-color:#f8fafc;transition:border-color .15s}.emoji-search-input:focus{border-color:#3ba4d7;background-color:#fff}.emoji-search-clear{background:none;border:none;cursor:pointer;color:#94a3b8;padding:.2rem;font-size:.8rem;box-shadow:none;display:flex;align-items:center}.emoji-search-clear:hover{color:#475569}.emoji-categories{display:flex;gap:.1rem;padding:.35rem .5rem;border-bottom:1px solid #f1f5f9;overflow-x:auto;scrollbar-width:none}.emoji-categories::-webkit-scrollbar{display:none}.emoji-cat-btn{background:none;border:none;cursor:pointer;font-size:1.2rem;padding:.3rem .35rem;border-radius:.375rem;line-height:1;box-shadow:none;transition:background-color .1s;flex-shrink:0}.emoji-cat-btn:hover{background-color:#f1f5f9}.emoji-cat-btn.active{background-color:#e0f2fe;box-shadow:inset 0 -2px 0 #3ba4d7}.emoji-grid{display:grid;grid-template-columns:repeat(7, 1fr);gap:0;padding:.4rem .35rem;max-height:220px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#cbd5e1 rgba(0,0,0,0)}.emoji-grid::-webkit-scrollbar{width:4px}.emoji-grid::-webkit-scrollbar-track{background:rgba(0,0,0,0)}.emoji-grid::-webkit-scrollbar-thumb{background-color:#cbd5e1;border-radius:4px}.emoji-btn{background:none;border:none;cursor:pointer;font-size:1.7rem;padding:.25rem;border-radius:.3rem;line-height:1;box-shadow:none;text-align:center;transition:background-color .1s,transform .1s;display:flex;align-items:center;justify-content:center;aspect-ratio:1}.emoji-btn:hover{background-color:#f1f5f9;transform:scale(1.2)}@keyframes emoji-pop{from{opacity:0;transform:scale(0.92) translateY(6px)}to{opacity:1;transform:scale(1) translateY(0)}}.side-bar{display:flex;flex-direction:column;background:#fff}.side-bar .mail-compose-btn{width:96%;margin:.25rem;padding:.75rem 0}.compose-mail__from{display:flex;justify-content:flex-start;align-items:center;gap:.5rem;padding-bottom:.5rem;border-bottom:2px solid #eef3f6}.compose-mail__recipients{padding:.5rem 0;display:flex;flex-direction:column;gap:.5rem;border-bottom:2px solid #eef3f6}.compose-mail__recipients__container{display:flex;gap:.5rem}.compose-mail__recipients__container>label{text-transform:capitalize}.compose-mail__recipients__container .recipients{width:100%;display:flex;gap:.5rem;flex-wrap:wrap}.compose-mail__recipients__container .recipients__selected{padding:.125rem .5rem;display:flex;align-items:center;gap:.5rem;border:1px solid #eef3f6;border-radius:3px;cursor:default}.compose-mail__recipients__container .recipients__selected i{cursor:pointer;padding:.25rem}.compose-mail__recipients__container .recipients__input{display:flex;position:relative;flex-grow:1}.compose-mail__recipients__container .recipients__input-field{flex-grow:1;min-width:200px;padding:0;border:none;box-shadow:none}.compose-mail__recipients__container .recipients__input-field:focus+.recipients__input-list{display:flex}.compose-mail__recipients__container .recipients__input-list{z-index:1;position:absolute;top:1rem;padding:0;width:100%;max-height:15rem;flex-direction:column;overflow:auto;display:none;background:#fff;border-top:1px solid #eef3f6;border-bottom:1px solid #eef3f6}.compose-mail__recipients__container .recipients__input-list:hover{display:flex}.compose-mail__recipients__container .recipients__input-list li{list-style:none;padding:.25rem .5rem;cursor:pointer;background:#fff;border:1px solid #eef3f6;border-top:0px}.compose-mail__recipients__container .recipients__input-list li:hover{background:#eef3f6}.compose-mail__recipients__container .recipients__input-list li:last-child{border-bottom:0px}.compose-mail__recipients .remove-recipient{padding:.125rem .5rem}.compose-mail input[type=text].compose-mail__subject{padding:.5rem 0;border:none;box-shadow:none;border-bottom:2px solid #eef3f6;border-radius:0}.compose-mail__message{margin:.5rem 0;height:100%;display:flex;flex-direction:column;overflow:auto}.compose-mail__message-body{height:100%;outline:rgba(0,0,0,0)}.compose-mail__send-btn{display:flex;align-items:center;gap:.5rem}.compose-mail__send-btn i{transform:translateY(-1px)}.msg-view{height:100%;display:flex;flex-direction:column;gap:1rem;overflow:auto}.msg-view-nav{display:flex;justify-content:space-between;align-items:column}.msg-view-nav__action{display:flex;gap:.5rem}.msg-view__header{display:flex;flex-direction:column;gap:1rem}.msg-view__header>h3{line-height:1}.msg-view__header .msg-details{display:flex;gap:1rem}.msg-view__header .msg-details__avatar{height:max-content}.msg-view__header .msg-details__info{display:flex;flex-direction:column}.msg-view__header .msg-details__info-item{display:flex;gap:.5rem}.msg-view__body{height:100%;overflow:auto;font-size:14px !important}.msg-view__attachment{height:50%;overflow:auto;display:flex;flex-direction:column}.msg-view__attachment-items{height:100%;overflow:auto}.mail-tag{width:8rem;padding:.5rem}.msgHeader{display:flex}.msgHeaderDetails{display:flex;flex-direction:column}table.mails th:nth-child(1){width:5%;color:#fcba03}table.mails th:nth-child(2){width:5%;color:hsl(202.5,30.7692307692%,44.9019607843%)}table.mails th:nth-child(3){width:50%;text-align:start}table.mails th:nth-child(4),table.mails th:nth-child(5){width:20%;text-align:start}table.mails td:nth-child(3){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.mails td:nth-child(4),table.mails td:nth-child(5){text-align:start}table.mails tr:hover{background-color:#eef3f6;cursor:pointer}table.mails tr.unread{color:#000;background-color:#eef3f6}table.mails>tr:hover{cursor:auto;background-color:#fff}table.mails th.sortable-th{cursor:pointer;user-select:none;transition:background-color .2s,color .2s}table.mails th.sortable-th:hover{background-color:#eef3f6;color:hsl(202.5,30.7692307692%,14.9019607843%)}input.star-check{display:none}input.star-check+label.star-check{color:gray}input.star-check:checked+label.star-check{color:#fcba03}#truncate{height:6rem;overflow:auto}#truncate.truncated-view{height:1.75rem;overflow:hidden}.toggle-truncate{font-size:.75rem;padding:0 .25rem;background:#999;color:#14141b;box-shadow:none;border-radius:2px}table.attachment-container{padding:0}table.attachment-container>tr{border:0}table.attachment-container .attachment-header{width:100%;display:flex;justify-content:space-between}table.attachment-container .attachment-header th{text-align:start}table.attachment-container .attachment-header th:nth-child(1){flex-basis:45%}table.attachment-container .attachment-header th:nth-child(2){flex-basis:15%}table.attachment-container .attachment-header th:nth-child(3){flex-basis:10%}table.attachment-container .attachment-header th:nth-child(4){flex-basis:20%}table.attachment-container .attachment-header th:nth-child(5){text-align:center;flex-basis:10%}table.attachment-container .attachment{width:100%;display:flex;justify-content:space-between;text-align:start}table.attachment-container .attachment__name{flex-basis:45%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}table.attachment-container .attachment__name span{margin-left:8px}table.attachment-container .attachment__from{flex-basis:15%}table.attachment-container .attachment__size{flex-basis:10%}table.attachment-container .attachment__date{flex-basis:20%}table.attachment-container .attachment td:nth-child(5){display:flex;justify-content:center;align-items:center;flex-basis:10%}table.attachment-container .attachment td:nth-child(5) button{font-size:.875rem}.view-toggle{height:max-content;border:1px solid #019dff;border-radius:4px;display:flex}.view-toggle *{padding:4px 12px;border-radius:4px}.composePopupOverlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1;background-color:rgba(0,0,0,.2)}.composePopupOverlay .composePopup{position:absolute;inset:0;margin:auto;width:80%;height:90%}.composePopupOverlay .composePopup>.widget{padding:2rem}.composePopupOverlay .composePopup .close-btn{position:absolute;top:1.5rem;right:1.5rem}.file-view{width:100%;padding:1rem;margin-top:1.5rem;border-radius:8px;border:1px solid #ccc;animation:fadein .5s}.file-view__heading{display:flex;justify-content:space-between;margin-bottom:.5rem}.file-view__heading-chunk{display:flex;gap:1rem}.file-view__body{display:flex;flex-direction:column;gap:1rem}.file-view__body-details{display:flex;align-items:center}.file-view__body-details-stat{width:100%;display:grid;grid-template-columns:repeat(5, 1fr)}.file-view__body-details-stat span>i{margin-right:.5rem}.file-view__body-details-action{display:flex;gap:1rem;height:100%}.file-view__body-details-action button,.file-view__body-details-action button.red{padding:.25rem .75rem}table.myfiles td{word-wrap:break-word}table.myfiles th:nth-child(1){width:2%}table.myfiles th:nth-child(2){width:50%}table.myfiles td:nth-child(2){text-align:start}table.friendsfiles td{word-wrap:break-word}table.friendsfiles th:nth-child(1){width:2%}table.friendsfiles th:nth-child(2){width:50%}table.friendsfiles th:nth-child(4){width:40%}table.friendsfiles td:nth-child(2){text-align:start}.file-search-container{margin-top:1rem;padding:8px;display:flex;gap:8px;border:1px solid rgba(20,20,27,.2);border-radius:6px;height:100%;overflow:auto}.file-search-container__keywords{flex-basis:15%;padding-right:.25rem;border-right:1px solid rgba(20,20,27,.1)}.file-search-container__keywords .keywords-container{display:flex;flex-direction:column;border-top:2.5px solid rgba(20,20,27,.08);margin-top:.125rem;padding-top:.25rem}.file-search-container__keywords .keywords-container a{font-size:1.2rem;text-decoration:none;color:#14141b}.file-search-container__keywords .keywords-container a.selected{color:#019dff}.file-search-container__results{flex-basis:85%;height:100%;overflow:auto}.file-search-container__results .results-container .results-header tr{display:flex}.file-search-container__results .results-container .results-header tr th{font-size:1.25rem;font-weight:bold;text-align:left}.file-search-container__results .results-container .results-header tr th:nth-child(1){flex-basis:40%}.file-search-container__results .results-container .results-header tr th:nth-child(2){flex-basis:10%;text-align:center}.file-search-container__results .results-container .results-header tr th:nth-child(3){flex-basis:40%}.file-search-container__results .results-container .results-header tr th:nth-child(4){flex-basis:10%}.file-search-container__results .results-container .results{height:100%;overflow:auto}.file-search-container__results .results-container .results tr{display:flex}.file-search-container__results .results-container .results tr .results__hash,.file-search-container__results .results-container .results tr .results__name{text-align:left;flex-basis:40%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-search-container__results .results-container .results tr .results__hash span,.file-search-container__results .results-container .results tr .results__name span{margin-left:8px}.file-search-container__results .results-container .results tr .results__size{flex-basis:10%}.file-search-container__results .results-container .results tr .results__download{flex-basis:10%;display:flex;justify-content:start;align-items:center}.search-form{display:flex;width:40%}.search-form input{width:100%}.search-form button{margin-left:.5rem}.shareManagerPopupOverlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1;background-color:rgba(0,0,0,.2)}.shareManagerPopupOverlay .shareManagerPopup{position:absolute;inset:0;margin:auto;width:80%;height:90%}.shareManagerPopupOverlay .shareManagerPopup>.widget{padding:1.5rem}.shareManagerPopupOverlay .shareManagerPopup .close-btn{position:absolute;top:1.5rem;right:1.5rem}.share-manager{display:flex;flex-direction:column;justify-content:space-between}.share-manager__table{margin:1rem 0 auto}.share-manager__table thead{font-weight:bold;text-align:left}.share-manager__table thead td:nth-child(1),.share-manager__table thead td:nth-child(2){padding-left:.5rem}.share-manager__table thead td:nth-child(3) .tooltip,.share-manager__table thead td:nth-child(4) .tooltip{font-weight:normal;font-size:1rem}.share-manager__table tbody{text-align:left}.share-manager__table tbody td:nth-child(4){font-size:1rem}.share-manager__table td input{border:0 !important}.share-manager__table td input[type=text]{width:100%}.share-manager__table td:nth-child(1){width:45%}.share-manager__table td:nth-child(2){width:20%}.share-manager__table td:nth-child(3){width:10%}.share-manager__table td:nth-child(4){width:25%}.share-manager__actions{display:flex;justify-content:space-between}.share-manager__form{display:flex;flex-direction:column;gap:.5rem}.share-manager__form_input{display:flex;flex-direction:column;gap:.5rem}.share-manager__form_input input{flex-grow:1}.share-manager .share-flags input.share-flags-check{display:none}.share-manager .share-flags input.share-flags-check+label.share-flags-label{color:gray;margin-right:.25rem;padding:.25rem .25rem .125rem;border:1px solid #6d6d6d;border-radius:.5rem}.share-manager .share-flags input.share-flags-check:checked+label.share-flags-label{color:#118fcc}.share-manager label span{display:inline-block;width:1.125rem}.manage-visibility label{width:100%;cursor:pointer}.manage-visibility{display:flex;justify-content:space-between}@media(max-width: 700px){.file-view__body-details{flex-direction:column;align-items:flex-start;gap:1rem}.file-view__body-details-stat{grid-template-columns:1fr;gap:.5rem}.file-view__body-details-stat span{display:flex;align-items:center}.share-manager__table,.share-manager__table thead,.share-manager__table tbody,.share-manager__table tr,.share-manager__table td{display:block;width:100% !important}.share-manager__table thead{display:none}.share-manager__table tr{border:1px solid #ccc;border-radius:8px;margin-bottom:1rem;padding:.5rem;background:#fff}.share-manager__table td{margin-bottom:.5rem;border:none !important;padding-left:0 !important}table.myfiles,table.myfiles tr,table.myfiles td,table.friendsfiles,table.friendsfiles tr,table.friendsfiles td{display:block;width:100% !important}table.myfiles th,table.friendsfiles th{display:none}table.myfiles tr,table.friendsfiles tr{border:1px solid #ccc;border-radius:8px;margin-bottom:1rem;padding:.5rem;background:#fff}.file-search-container{flex-direction:column}.file-search-container__keywords{flex-basis:auto;width:100%;border-right:none;border-bottom:1px solid rgba(20,20,27,.1);padding-bottom:1rem;margin-bottom:1rem}.results-container,.results-container thead,.results-container tbody,.results-container tr,.results-container td{display:block;width:100% !important}.results-container thead{display:none}.results-container tr{border-bottom:1px solid #eee;padding:1rem 0}.results-container td{margin-bottom:.5rem;word-break:break-all}}.file-section{margin-top:2rem;display:flex;flex-direction:column}.comments-section{margin-top:2rem;display:flex;justify-content:space-between}.comments-section__menu{display:flex;gap:1rem}.comments-section__menu-id{display:flex;align-items:center;gap:.25rem}#toggleunsub{position:relative;background:gray}table.channels th:nth-child(1){width:50%;text-align:start}table.channels td:nth-child(1){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.channels tr:hover{background-color:#eef3f6;cursor:pointer}table.channels tr.hidden{display:none}table{padding:.5rem}table.comments{border:1px solid #eee}table.comments th{height:40px}table.comments th:nth-child(1){width:2%}table.comments th:nth-child(2){width:40%}table.comments td{word-wrap:break-word}table.comments td:nth-child(2){text-align:start}table.files th:first-child{text-align:start;width:60%}table.files tr td:first-child{text-align:start}table.files td{word-wrap:break-word}#mtags{width:160px;text-align:center;font-size:medium;margin-left:10px;height:40px}.forums-node-panel{position:relative;bottom:200px;margin-left:200px;animation:fadein .5s}table.forums th:nth-child(1){width:50%;text-align:start}table.forums td:nth-child(1){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.forums tr:hover{background-color:#eef3f6;cursor:pointer}table.forums tr.hidden{display:none}#searchforum{position:relative;margin-left:250px}#forumdetails{position:relative;padding:10px}.p{margin:0}#toggleunsub{position:relative;background:gray}table.threads tr:hover{background-color:#eef3f6;cursor:pointer}table.threads td{word-wrap:break-word}table.threadreply th:nth-child(2){width:50%}table.threadreply th:nth-child(1){width:2%}table.threadreply td:nth-child(2){width:50%;text-align:start}table.threadreply td{word-wrap:break-word}table.threadreply tr:hover{background-color:#eef3f6;cursor:pointer}table.boards th:nth-child(1){width:50%;text-align:start}table.boards td:nth-child(1){text-align:start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.boards tr:hover{background-color:#eef3f6;cursor:pointer}table.boards tr.hidden{display:none}#toggleunsub{position:relative;background:gray}#options{width:100px;text-align:center;font-size:medium;margin-left:20px;height:40px}#composepopup{height:80%;width:70%}#mtags{width:160px;text-align:center;font-size:medium;margin-left:10px;height:40px}.mail .permission-flag{margin-bottom:1rem;display:flex;gap:1rem}.mail-tags{padding:.5rem;border:1px solid rgba(20,20,27,.2);border-radius:6px}.mail-tags__container{display:flex;flex-direction:column}.mail-tags__container .tag-item{display:flex;align-items:center;gap:4px;border-bottom:1px solid rgba(20,20,27,.1);padding:2px 0}.mail-tags__container .tag-item:last-child{border:none}.mail-tags__container .tag-item__color{width:1.25rem;height:1.25rem;aspect-ratio:1}.mail-tags__container .tag-item__name{font-size:1.125rem}.mail-tags__container .tag-item__modify{margin-left:auto;font-size:.75rem;display:flex;gap:4px}.mail-tags__container .tag-item:hover{background-color:#eef3f6}.mail-tags__container .tag-item button,.mail-tags__container .tag-item button.red{padding:.25rem .6rem}.mail-tags-form .input-field{margin-bottom:.5rem}.mail-tags-form .input-field label{margin-right:.5rem}.external-address{margin:0;padding-left:1rem;height:100px;overflow:hidden auto}.external-address::-webkit-scrollbar{display:none}.proxy-server{display:flex;flex-direction:column;gap:4px}.proxy-server__tor>h4,.proxy-server__i2p>h4{margin-bottom:.25rem}.proxy-server__tor>input,.proxy-server__i2p>input{margin-right:.5rem}.proxy-server__tor .proxy-outgoing,.proxy-server__i2p .proxy-outgoing{display:inline-flex;align-items:center;gap:.5rem}.proxy-server__tor .proxy-outgoing__status,.proxy-server__i2p .proxy-outgoing__status{width:1rem;height:1rem;aspect-ratio:1;border:1px solid #000;border-radius:50%}.config-files{display:flex;flex-direction:column;gap:1rem}.proxy-server-container{width:100%;display:flex;flex-direction:column;gap:1rem}.proxy-description{color:#334155;font-size:.95rem;margin-bottom:.5rem}.proxy-rows-container{display:flex;flex-direction:column;gap:.75rem;width:100%}.proxy-row{display:grid;grid-template-columns:160px 220px 220px auto;gap:.75rem;align-items:center;width:100%}.proxy-label{font-size:.95rem;font-weight:500;color:#1e293b}.proxy-addr-input,.proxy-port-input{width:100% !important;max-width:none !important}.proxy-status-container{display:flex;align-items:center;gap:.5rem}.proxy-status-bullet{width:14px;height:14px;border-radius:50%;display:inline-block;border:1px solid #475569}.proxy-status-text{font-size:.95rem;color:#1e293b} diff --git a/webui.pro b/webui.pro index 9d14a65..64e5f79 100644 --- a/webui.pro +++ b/webui.pro @@ -33,7 +33,7 @@ WEBUI_SRC_CSS = $$PWD/webui-src/styles.css WEBUI_SRC_IMAGE = $$PWD/data/retroshare.svg -win32-g++ { +win32 { isEmpty(QMAKE_SH) { # Windows native build WEBUI_SRC_SCRIPT = $$PWD/webui-src/make-src/build.bat @@ -72,7 +72,7 @@ win32-g++ { } else { # create dummy files, we need it to include files on first try - system(bash webui-src/make-src/build.sh .) + system(sh webui-src/make-src/build.sh .) WEBUI_SRC_SCRIPT = webui-src/make-src/build.sh @@ -80,19 +80,19 @@ win32-g++ { create_webfiles_html.output = webui/index.html create_webfiles_html.input = WEBUI_SRC_HTML - create_webfiles_html.commands = bash $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ index.html . + create_webfiles_html.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ index.html . create_webfiles_html.variable_out = JUNK create_webfiles_html.CONFIG = combine no_link create_webfiles_js.output = webui/app.js create_webfiles_js.input = WEBUI_SRC_JS - create_webfiles_js.commands = bash $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ app.js . + create_webfiles_js.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ app.js . create_webfiles_js.variable_out = JUNK create_webfiles_js.CONFIG = combine no_link create_webfiles_css.output = webui/styles.css create_webfiles_css.input = WEBUI_SRC_CSS - create_webfiles_css.commands = bash $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ styles.css . + create_webfiles_css.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ styles.css . create_webfiles_css.variable_out = JUNK create_webfiles_css.CONFIG = combine no_link