From 69fbf47d7a7e2385f2e6a66afaac91201003a1b4 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Mon, 16 May 2022 13:34:06 +0200 Subject: [PATCH] Message / Code: Support highlight (#1842) --- package-lock.json | 336 +++++++++++++-- package.json | 2 + src/api/gramjs/apiBuilders/messages.ts | 1 + src/api/gramjs/gramjsBuilders/index.ts | 4 +- src/api/types/messages.ts | 1 + src/assets/fonts/icomoon.woff | Bin 43256 -> 43744 bytes src/assets/fonts/icomoon.woff2 | Bin 20176 -> 20408 bytes src/bundles/extra.ts | 3 + src/components/common/code/CodeBlock.scss | 158 +++++++ src/components/common/code/CodeBlock.tsx | 52 +++ .../common/code/CodeOverlay.module.scss | 43 ++ src/components/common/code/CodeOverlay.tsx | 78 ++++ src/components/common/code/PreBlock.tsx | 36 ++ .../common/helpers/renderMessageText.ts | 2 + src/components/common/helpers/renderText.tsx | 8 + .../common/helpers/renderTextWithEntities.tsx | 24 +- src/components/main/hooks/useWebAppFrame.ts | 7 +- src/components/middle/composer/Composer.scss | 10 +- src/components/middle/message/Message.tsx | 4 +- .../middle/message/_message-content.scss | 44 +- .../message/helpers/calculateAuthorWidth.ts | 13 +- .../middle/message/hooks/useOuterHandlers.ts | 1 + src/styles/Telegram T.json | 405 ++++++++---------- src/styles/_variables.scss | 3 + src/styles/icons.scss | 14 +- src/styles/index.scss | 23 +- src/styles/themes.json | 4 +- src/util/highlightCode.ts | 108 +++++ src/util/parseMessageInput.ts | 9 +- webpack.config.js | 11 +- 30 files changed, 1096 insertions(+), 308 deletions(-) create mode 100644 src/components/common/code/CodeBlock.scss create mode 100644 src/components/common/code/CodeBlock.tsx create mode 100644 src/components/common/code/CodeOverlay.module.scss create mode 100644 src/components/common/code/CodeOverlay.tsx create mode 100644 src/components/common/code/PreBlock.tsx create mode 100644 src/util/highlightCode.ts diff --git a/package-lock.json b/package-lock.json index b88117508..c6a948b1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#54443d1938ec1c157e74d2a95e9103dcb3f5c6dd", "events": "^3.3.0", "idb-keyval": "^6.1.0", + "lowlight": "^2.6.1", "opus-recorder": "github:Ajaxy/opus-recorder", "os-browserify": "^0.3.0", "pako": "^2.0.4", @@ -37,6 +38,7 @@ "@statoscope/webpack-plugin": "^5.20.1", "@testing-library/jest-dom": "^5.16.4", "@types/croppie": "^2.6.1", + "@types/hast": "^2.3.4", "@types/jest": "^27.4.1", "@types/react": "^18.0.5", "@types/react-dom": "^18.0.0", @@ -2533,6 +2535,26 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@jest/reporters/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3884,6 +3906,14 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -4092,8 +4122,7 @@ "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", - "dev": true + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, "node_modules/@types/wicg-mediasession": { "version": "1.1.3", @@ -8408,6 +8437,18 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -8583,6 +8624,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -8790,26 +8839,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -9036,6 +9065,14 @@ "integrity": "sha512-QeOvm6cifeZYYdTLm4IxZsXcOE9c4xqfs0z0OJJ0z7hhA9WG0rmcVAyuIp5HBl/znjA/ayYHmpYjBYD/9PG4Fg==", "dev": true }, + "node_modules/highlight.js": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.1.tgz", + "integrity": "sha512-LKzHqnxr4CrD2YsNoIf/o5nJ09j4yi/GcH5BnYz9UnVpZdS4ucMgvP61TDty5xJcFGRjnH4DpujkS9bHT3hq0Q==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -10387,6 +10424,26 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-config/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -11485,6 +11542,26 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-runtime/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12651,6 +12728,20 @@ "tslib": "^2.0.3" } }, + "node_modules/lowlight": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-2.6.1.tgz", + "integrity": "sha512-t0ueDL6SIn9FKHipm78CNjWcJQv0xi6WCjYAICyO6GyPzoT7E58yom1mNwvI7AMwVe3pLwwFT0Bt2gml7uaUeQ==", + "dependencies": { + "@types/hast": "^2.0.0", + "fault": "^2.0.0", + "highlight.js": "~11.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -15088,6 +15179,26 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/replace-in-file/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/replace-in-file/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -15242,6 +15353,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -17202,6 +17333,26 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -20334,6 +20485,20 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -21455,6 +21620,14 @@ "@types/node": "*" } }, + "@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "requires": { + "@types/unist": "*" + } + }, "@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -21663,8 +21836,7 @@ "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", - "dev": true + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, "@types/wicg-mediasession": { "version": "1.1.3", @@ -24903,6 +25075,14 @@ "reusify": "^1.0.4" } }, + "fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "requires": { + "format": "^0.2.0" + } + }, "faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -25039,6 +25219,11 @@ "mime-types": "^2.1.12" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -25186,20 +25371,6 @@ } } }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -25365,6 +25536,11 @@ "integrity": "sha512-QeOvm6cifeZYYdTLm4IxZsXcOE9c4xqfs0z0OJJ0z7hhA9WG0rmcVAyuIp5HBl/znjA/ayYHmpYjBYD/9PG4Fg==", "dev": true }, + "highlight.js": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.1.tgz", + "integrity": "sha512-LKzHqnxr4CrD2YsNoIf/o5nJ09j4yi/GcH5BnYz9UnVpZdS4ucMgvP61TDty5xJcFGRjnH4DpujkS9bHT3hq0Q==" + }, "hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -26325,6 +26501,20 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -27168,6 +27358,20 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -28040,6 +28244,16 @@ "tslib": "^2.0.3" } }, + "lowlight": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-2.6.1.tgz", + "integrity": "sha512-t0ueDL6SIn9FKHipm78CNjWcJQv0xi6WCjYAICyO6GyPzoT7E58yom1mNwvI7AMwVe3pLwwFT0Bt2gml7uaUeQ==", + "requires": { + "@types/hast": "^2.0.0", + "fault": "^2.0.0", + "highlight.js": "~11.5.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -29867,6 +30081,20 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -29977,6 +30205,22 @@ "dev": true, "requires": { "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "run-parallel": { @@ -31488,6 +31732,22 @@ "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "text-table": { diff --git a/package.json b/package.json index 92d5c3aeb..e11a48012 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "@statoscope/webpack-plugin": "^5.20.1", "@testing-library/jest-dom": "^5.16.4", "@types/croppie": "^2.6.1", + "@types/hast": "^2.3.4", "@types/jest": "^27.4.1", "@types/react": "^18.0.5", "@types/react-dom": "^18.0.0", @@ -119,6 +120,7 @@ "emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#54443d1938ec1c157e74d2a95e9103dcb3f5c6dd", "events": "^3.3.0", "idb-keyval": "^6.1.0", + "lowlight": "^2.6.1", "opus-recorder": "github:Ajaxy/opus-recorder", "os-browserify": "^0.3.0", "pako": "^2.0.4", diff --git a/src/api/gramjs/apiBuilders/messages.ts b/src/api/gramjs/apiBuilders/messages.ts index 6cbc405c0..1b075ff51 100644 --- a/src/api/gramjs/apiBuilders/messages.ts +++ b/src/api/gramjs/apiBuilders/messages.ts @@ -1318,6 +1318,7 @@ function buildApiMessageEntity(entity: GramJs.TypeMessageEntity): ApiMessageEnti length, ...(entity instanceof GramJs.MessageEntityMentionName && { userId: buildApiPeerId(entity.userId, 'user') }), ...('url' in entity && { url: entity.url }), + ...('language' in entity && { language: entity.language }), }; } diff --git a/src/api/gramjs/gramjsBuilders/index.ts b/src/api/gramjs/gramjsBuilders/index.ts index e47199726..86d7c16f6 100644 --- a/src/api/gramjs/gramjsBuilders/index.ts +++ b/src/api/gramjs/gramjsBuilders/index.ts @@ -265,7 +265,7 @@ export function buildMessageFromUpdate( export function buildMtpMessageEntity(entity: ApiMessageEntity): GramJs.TypeMessageEntity { const { - type, offset, length, url, userId, + type, offset, length, url, userId, language, } = entity; const user = userId ? localDb.users[userId] : undefined; @@ -282,7 +282,7 @@ export function buildMtpMessageEntity(entity: ApiMessageEntity): GramJs.TypeMess case ApiMessageEntityTypes.Code: return new GramJs.MessageEntityCode({ offset, length }); case ApiMessageEntityTypes.Pre: - return new GramJs.MessageEntityPre({ offset, length, language: '' }); + return new GramJs.MessageEntityPre({ offset, length, language: language || '' }); case ApiMessageEntityTypes.Blockquote: return new GramJs.MessageEntityBlockquote({ offset, length }); case ApiMessageEntityTypes.TextUrl: diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index 4f6e451e1..479e64b1c 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -241,6 +241,7 @@ export interface ApiMessageEntity { length: number; userId?: string; url?: string; + language?: string; } export enum ApiMessageEntityTypes { diff --git a/src/assets/fonts/icomoon.woff b/src/assets/fonts/icomoon.woff index 711761aa724dd4d695bdfa3451bc80ce52144df4..746609e423b953980306e598ba37c170b86cb80f 100644 GIT binary patch delta 842 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�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 literal 20176 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 = ({ text, language, noCopy }) => { + const [isWordWrap, setWordWrap] = useState(true); + + const { result: highlighted } = useAsync(() => { + if (!language) return Promise.resolve(); + return import('../../../util/highlightCode') + .then((lib) => lib.default(text, language)); + }, [language, text]); + + const handleWordWrapToggle = useCallback((wrap) => { + setWordWrap(wrap); + }, []); + + if (!highlighted) { + return ; + } + + const blockClass = buildClassName('code-block', !isWordWrap && 'no-word-wrap'); + + return ( +
+      {highlighted}
+      
+    
+ ); +}; + +export default memo(CodeBlock); diff --git a/src/components/common/code/CodeOverlay.module.scss b/src/components/common/code/CodeOverlay.module.scss new file mode 100644 index 000000000..81cb94fde --- /dev/null +++ b/src/components/common/code/CodeOverlay.module.scss @@ -0,0 +1,43 @@ +.overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease-in-out; +} + +.copy, .wrap { + display: flex; + font-size: 1.25rem; + padding: 0.125rem; + border-radius: 0.125rem; + margin: 0.125rem; + transition: background-color 0.15s ease-in-out; + cursor: pointer; + + &:hover, &.wrapOn { + background-color: var(--color-background-compact-menu-hover); + } +} + +.content { + position: absolute; + top: 0; + right: 0; + + display: flex; + align-items: center; + padding: 0.125rem; + + background-color: var(--color-background-compact-menu); + backdrop-filter: blur(1px); + border-bottom-left-radius: 0.25rem; + pointer-events: all; + + &.hidden { + display: none; + } +} diff --git a/src/components/common/code/CodeOverlay.tsx b/src/components/common/code/CodeOverlay.tsx new file mode 100644 index 000000000..3dcfaea5e --- /dev/null +++ b/src/components/common/code/CodeOverlay.tsx @@ -0,0 +1,78 @@ +import React, { + FC, memo, useCallback, useEffect, useRef, useState, +} from '../../../lib/teact/teact'; +import { getActions } from '../../../global'; + +import { copyTextToClipboard } from '../../../util/clipboard'; +import buildClassName from '../../../util/buildClassName'; +import { areLinesWrapping } from '../helpers/renderText'; + +import useWindowSize from '../../../hooks/useWindowSize'; +import useLang from '../../../hooks/useLang'; + +import styles from './CodeOverlay.module.scss'; + +export type OwnProps = { + className?: string; + text: string; + noCopy?: boolean; + onWordWrapToggle?: (wrap: boolean) => void; +}; + +const CodeOverlay: FC = ({ + text, className, noCopy, onWordWrapToggle, +}) => { + const { showNotification } = getActions(); + // eslint-disable-next-line no-null/no-null + const ref = useRef(null); + const windowSize = useWindowSize(); + const lang = useLang(); + const [isWordWrap, setIsWordWrap] = useState(true); + const [withWordWrapButton, setWithWordWrapButton] = useState(false); + + const checkWordWrap = useCallback(() => { + const isWrap = areLinesWrapping(text, ref.current!.parentElement!); + setWithWordWrapButton(isWrap); + }, [text]); + + useEffect(() => { + if (isWordWrap) { + checkWordWrap(); + } + }, [checkWordWrap, isWordWrap, text, windowSize]); + + const handleCopy = useCallback(() => { + copyTextToClipboard(text); + showNotification({ + message: lang('TextCopied'), + }); + }, [lang, showNotification, text]); + + const handleWordWrapClick = useCallback(() => { + setIsWordWrap(!isWordWrap); + onWordWrapToggle?.(!isWordWrap); + }, [isWordWrap, onWordWrapToggle]); + + const contentClass = buildClassName(styles.content, !withWordWrapButton && noCopy && styles.hidden); + const overlayClass = buildClassName(styles.overlay, className); + const wrapClass = buildClassName(styles.wrap, isWordWrap && styles.wrapOn); + + return ( +
+
+ {withWordWrapButton && ( +
+ +
+ )} + {!noCopy && ( +
+ +
+ )} +
+
+ ); +}; + +export default memo(CodeOverlay); diff --git a/src/components/common/code/PreBlock.tsx b/src/components/common/code/PreBlock.tsx new file mode 100644 index 000000000..5bfd02205 --- /dev/null +++ b/src/components/common/code/PreBlock.tsx @@ -0,0 +1,36 @@ +import React, { + FC, memo, useCallback, useState, +} from '../../../lib/teact/teact'; + +import buildClassName from '../../../util/buildClassName'; + +import CodeOverlay from './CodeOverlay'; + +type OwnProps = { + text: string; + noCopy?: boolean; +}; + +const PreBlock: FC = ({ text, noCopy }) => { + const [isWordWrap, setWordWrap] = useState(true); + + const handleWordWrapToggle = useCallback((wrap) => { + setWordWrap(wrap); + }, []); + + const blockClass = buildClassName('text-entity-pre', !isWordWrap && 'no-word-wrap'); + + return ( +
+      
{text}
+ +
+ ); +}; + +export default memo(PreBlock); diff --git a/src/components/common/helpers/renderMessageText.ts b/src/components/common/helpers/renderMessageText.ts index 4f55af6dd..a10cb37e4 100644 --- a/src/components/common/helpers/renderMessageText.ts +++ b/src/components/common/helpers/renderMessageText.ts @@ -19,6 +19,7 @@ export function renderMessageText( shouldRenderHqEmoji?: boolean, isSimple?: boolean, truncateLength?: number, + isProtected?: boolean, ) { const { text, entities } = message.content.text || {}; @@ -35,6 +36,7 @@ export function renderMessageText( undefined, message.id, isSimple, + isProtected, ); } diff --git a/src/components/common/helpers/renderText.tsx b/src/components/common/helpers/renderText.tsx index 487e17d4d..ca22bcfb8 100644 --- a/src/components/common/helpers/renderText.tsx +++ b/src/components/common/helpers/renderText.tsx @@ -282,3 +282,11 @@ function replaceSimpleMarkdown(textParts: TextPart[], type: 'jsx' | 'html'): Tex }, result); }, [] as TextPart[]); } + +export function areLinesWrapping(text: string, element: HTMLElement) { + const lines = (text.trim().match(/\n/g) || '').length + 1; + const { lineHeight } = getComputedStyle(element); + const lineHeightParsed = parseFloat(lineHeight.split('px')[0]); + + return element.clientHeight >= (lines + 1) * lineHeightParsed; +} diff --git a/src/components/common/helpers/renderTextWithEntities.tsx b/src/components/common/helpers/renderTextWithEntities.tsx index 788714e90..fe538bf06 100644 --- a/src/components/common/helpers/renderTextWithEntities.tsx +++ b/src/components/common/helpers/renderTextWithEntities.tsx @@ -12,6 +12,8 @@ import { getTranslation } from '../../../util/langProvider'; import MentionLink from '../../middle/message/MentionLink'; import SafeLink from '../SafeLink'; import Spoiler from '../spoiler/Spoiler'; +import CodeBlock from '../code/CodeBlock'; +import buildClassName from '../../../util/buildClassName'; interface IOrganizedEntity { entity: ApiMessageEntity; @@ -27,6 +29,7 @@ export function renderTextWithEntities( shouldRenderAsHtml?: boolean, messageId?: number, isSimple?: boolean, + isProtected?: boolean, ) { if (!entities || !entities.length) { return renderMessagePart(text, highlight, shouldRenderHqEmoji, shouldRenderAsHtml, isSimple); @@ -101,7 +104,7 @@ export function renderTextWithEntities( // Render the entity itself const newEntity = shouldRenderAsHtml ? processEntityAsHtml(entity, entityContent, nestedEntityContent) - : processEntity(entity, entityContent, nestedEntityContent, highlight, messageId, isSimple); + : processEntity(entity, entityContent, nestedEntityContent, highlight, messageId, isSimple, isProtected); if (Array.isArray(newEntity)) { renderResult.push(...newEntity); @@ -276,6 +279,7 @@ function processEntity( highlight?: string, messageId?: number, isSimple?: boolean, + isProtected?: boolean, ) { const entityText = typeof entityContent === 'string' && entityContent; const renderedContent = nestedEntityContent.length ? nestedEntityContent : entityContent; @@ -335,7 +339,12 @@ function processEntity( ); case ApiMessageEntityTypes.Code: return ( - + {renderNestedMessagePart()} ); @@ -376,7 +385,7 @@ function processEntity(
); case ApiMessageEntityTypes.Pre: - return
{renderNestedMessagePart()}
; + return ; case ApiMessageEntityTypes.Strike: return {renderNestedMessagePart()}; case ApiMessageEntityTypes.TextUrl: @@ -403,11 +412,14 @@ function processEntityAsHtml( entityContent: TextPart, nestedEntityContent: TextPart[], ) { - const rawEntityText = typeof entityContent === 'string' && entityContent; + const rawEntityText = typeof entityContent === 'string' ? entityContent : undefined; + + // Prevent adding newlines when editing + const content = entity.type === ApiMessageEntityTypes.Pre ? (entityContent as string).trimEnd() : entityContent; const renderedContent = nestedEntityContent.length ? nestedEntityContent.join('') - : renderText(entityContent, ['escape_html', 'emoji_html', 'br_html']).join(''); + : renderText(content, ['escape_html', 'emoji_html', 'br_html']).join(''); if (!rawEntityText) { return renderedContent; @@ -423,7 +435,7 @@ function processEntityAsHtml( case ApiMessageEntityTypes.Code: return `${renderedContent}`; case ApiMessageEntityTypes.Pre: - return `\`\`\`
${renderedContent}
\`\`\``; + return `\`\`\`${entity.language || ''}
${renderedContent}
\`\`\`
`; case ApiMessageEntityTypes.Strike: return `${renderedContent}`; case ApiMessageEntityTypes.MentionName: diff --git a/src/components/main/hooks/useWebAppFrame.ts b/src/components/main/hooks/useWebAppFrame.ts index fbf142331..d0e1471a0 100644 --- a/src/components/main/hooks/useWebAppFrame.ts +++ b/src/components/main/hooks/useWebAppFrame.ts @@ -55,7 +55,7 @@ type WebAppOutboundEvent = { const SCROLLBAR_STYLE = `* { scrollbar-width: thin; - scrollbar-color: rgba(90,90,90,0.3) transparent; + scrollbar-color: %SCROLLBAR_COLOR% transparent; } *::-webkit-scrollbar { @@ -66,7 +66,7 @@ const SCROLLBAR_STYLE = `* { *::-webkit-scrollbar-thumb { border-radius: 6px; - background-color: rgba(90, 90, 90, 0.3); + background-color: %SCROLLBAR_COLOR%; } *::-webkit-scrollbar-corner { @@ -141,7 +141,8 @@ const useWebAppFrame = (isOpen: boolean, isSimpleView: boolean, onEvent: (event: } if (data.eventType === 'iframe_ready') { - sendCustomStyle(SCROLLBAR_STYLE); + const scrollbarColor = getComputedStyle(document.body).getPropertyValue('--color-scrollbar'); + sendCustomStyle(SCROLLBAR_STYLE.replace(/%SCROLLBAR_COLOR%/g, scrollbarColor)); } if (data.eventType === 'web_app_data_send') { diff --git a/src/components/middle/composer/Composer.scss b/src/components/middle/composer/Composer.scss index e3eeb93cc..2775d49c9 100644 --- a/src/components/middle/composer/Composer.scss +++ b/src/components/middle/composer/Composer.scss @@ -363,7 +363,7 @@ line-height: 3.5rem; height: 3.5rem; padding: 0 3.125rem 0 1rem; - font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Helvetica Neue", sans-serif; + font-family: var(--font-family); &::after { content: ""; @@ -394,17 +394,11 @@ calc(0.9rem - var(--border-width)); overflow: hidden; line-height: 1.375; - font-family: Roboto, -apple-system, "Apple Color Emoji", "Helvetica Neue", sans-serif; + font-family: var(--font-family); unicode-bidi: plaintext; text-align: initial; font-size: var(--composer-text-size, 1rem); - body.is-ios &, - body.is-macos & { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Roboto", "Apple Color Emoji", "Helvetica Neue", - sans-serif; - } - &.overflown { overflow-y: auto; overflow-x: hidden; diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index ec36282c1..c44cfd177 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -488,7 +488,9 @@ const Message: FC = ({ }); const withAppendix = contentClassName.includes('has-appendix'); - const textParts = renderMessageText(message, highlight, isEmojiOnlyMessage(customShape)); + const textParts = renderMessageText( + message, highlight, isEmojiOnlyMessage(customShape), undefined, undefined, isProtected, + ); let metaPosition!: MetaPosition; if (phoneCall) { diff --git a/src/components/middle/message/_message-content.scss b/src/components/middle/message/_message-content.scss index a5ba7330a..2728185cd 100644 --- a/src/components/middle/message/_message-content.scss +++ b/src/components/middle/message/_message-content.scss @@ -813,17 +813,51 @@ } } -.text-entity-code, -.text-entity-pre { +.text-entity-code { color: var(--color-code); background: var(--color-code-bg); white-space: pre-wrap; margin: 0; padding: 1px 2px; border-radius: 4px; - font-size: calc(var(--message-text-size, 1rem) - 0.0625rem); + + &.clickable { + cursor: pointer; + } } -.text-entity-code { - cursor: pointer; +// Keep this close to `CodeBlock` style to avoid jumps in height +.text-entity-pre { + white-space: pre-wrap; + background-color: var(--color-code-bg); + margin: 0; + padding: 0.5rem; + margin-block: 0.25rem; + border-radius: 4px; + position: relative; + overflow: hidden; + + &:hover { + .code-overlay { + opacity: 1; + } + } + + &.no-word-wrap { + white-space: pre; + padding-bottom: 0.25rem; + } + + .pre-code { + overflow-x: auto; + } +} + +.text-entity-code, +.text-entity-pre, +.code-block, +.hljs { + --color-scrollbar: var(--color-scrollbar-code); + font-family: var(--font-family-monospace); + font-size: 0.875rem; } diff --git a/src/components/middle/message/helpers/calculateAuthorWidth.ts b/src/components/middle/message/helpers/calculateAuthorWidth.ts index 838fda5a9..f70af85c9 100644 --- a/src/components/middle/message/helpers/calculateAuthorWidth.ts +++ b/src/components/middle/message/helpers/calculateAuthorWidth.ts @@ -1,15 +1,14 @@ -import { IS_IOS } from '../../../../util/environment'; - let element: HTMLSpanElement | undefined; - +let fontFamily: string | undefined; export default function calculateAuthorWidth(text: string) { + if (!fontFamily) { + fontFamily = getComputedStyle(document.documentElement).getPropertyValue('--font-family'); + } + if (!element) { element = document.createElement('span'); // eslint-disable-next-line max-len - element.style.font = IS_IOS - // eslint-disable-next-line max-len - ? '400 12px system-ui, -apple-system, BlinkMacSystemFont, "Roboto", "Apple Color Emoji", "Helvetica Neue", sans-serif' - : '400 12px "Roboto", -apple-system, "Apple Color Emoji", BlinkMacSystemFont, "Helvetica Neue", sans-serif'; + element.style.font = `400 12px ${fontFamily}`; element.style.whiteSpace = 'nowrap'; element.style.position = 'absolute'; element.style.left = '-999px'; diff --git a/src/components/middle/message/hooks/useOuterHandlers.ts b/src/components/middle/message/hooks/useOuterHandlers.ts index 16011aebb..63927d642 100644 --- a/src/components/middle/message/hooks/useOuterHandlers.ts +++ b/src/components/middle/message/hooks/useOuterHandlers.ts @@ -152,6 +152,7 @@ export default function useOuterHandlers( let startedAt: number | undefined; return captureEvents(containerRef.current!, { selectorToPreventScroll: '.MessageList', + excludedClosestSelector: '.no-word-wrap', onSwipe: ((e, direction) => { if (direction === SwipeDirection.Left) { if (!startedAt) { diff --git a/src/styles/Telegram T.json b/src/styles/Telegram T.json index 4247f03e7..4b96e83e3 100644 --- a/src/styles/Telegram T.json +++ b/src/styles/Telegram T.json @@ -2,7 +2,7 @@ "metadata": { "name": "Telegram T", "lastOpened": 0, - "created": 1650375482158 + "created": 1651573979756 }, "iconSets": [ { @@ -158,36 +158,28 @@ { "selection": [ { - "order": 711, + "order": 714, + "id": 61, + "name": "heart-outline", + "prevSize": 32, + "code": 59806, + "tempChar": "" + }, + { + "order": 713, "id": 60, "name": "heart", "prevSize": 32, - "code": 59802, - "tempChar": "" + "code": 59807, + "tempChar": "" }, { "order": 712, "id": 59, - "name": "heart-outline", + "name": "word-wrap", "prevSize": 32, - "code": 59803, - "tempChar": "" - }, - { - "order": 0, - "id": 58, - "name": "reactions", - "prevSize": 32, - "code": 59802, - "tempChar": "" - }, - { - "order": 0, - "id": 57, - "name": "reaction-filled", - "prevSize": 32, - "code": 59803, - "tempChar": "" + "code": 59805, + "tempChar": "" }, { "order": 708, @@ -195,7 +187,7 @@ "name": "webapp", "prevSize": 32, "code": 59795, - "tempChar": "" + "tempChar": "" }, { "order": 707, @@ -203,7 +195,7 @@ "name": "reload", "prevSize": 32, "code": 59796, - "tempChar": "" + "tempChar": "" }, { "order": 706, @@ -211,7 +203,7 @@ "name": "install", "prevSize": 32, "code": 59801, - "tempChar": "" + "tempChar": "" }, { "order": 705, @@ -219,7 +211,7 @@ "name": "favorite-filled", "prevSize": 32, "code": 59800, - "tempChar": "" + "tempChar": "" }, { "order": 702, @@ -227,7 +219,7 @@ "name": "share-screen", "prevSize": 32, "code": 59770, - "tempChar": "" + "tempChar": "" }, { "order": 701, @@ -235,7 +227,7 @@ "name": "video-outlined", "prevSize": 32, "code": 59799, - "tempChar": "" + "tempChar": "" }, { "order": 700, @@ -243,7 +235,7 @@ "name": "stats", "prevSize": 32, "code": 59798, - "tempChar": "" + "tempChar": "" }, { "order": 699, @@ -251,7 +243,7 @@ "name": "copy-media", "prevSize": 32, "code": 59797, - "tempChar": "" + "tempChar": "" }, { "order": 704, @@ -259,7 +251,7 @@ "name": "sidebar", "prevSize": 32, "code": 59794, - "tempChar": "" + "tempChar": "" }, { "order": 690, @@ -267,7 +259,7 @@ "name": "video-stop", "prevSize": 32, "code": 59787, - "tempChar": "" + "tempChar": "" }, { "order": 678, @@ -275,7 +267,7 @@ "name": "speaker", "prevSize": 32, "code": 59777, - "tempChar": "" + "tempChar": "" }, { "order": 679, @@ -283,7 +275,7 @@ "name": "speaker-outline", "prevSize": 32, "code": 59778, - "tempChar": "" + "tempChar": "" }, { "order": 680, @@ -291,7 +283,7 @@ "name": "phone-discard-outline", "prevSize": 32, "code": 59779, - "tempChar": "" + "tempChar": "" }, { "order": 681, @@ -299,7 +291,7 @@ "name": "allow-speak", "prevSize": 32, "code": 59780, - "tempChar": "" + "tempChar": "" }, { "order": 682, @@ -307,7 +299,7 @@ "name": "stop-raising-hand", "prevSize": 32, "code": 59781, - "tempChar": "" + "tempChar": "" }, { "order": 683, @@ -315,7 +307,7 @@ "name": "share-screen-outlined", "prevSize": 32, "code": 59782, - "tempChar": "" + "tempChar": "" }, { "order": 684, @@ -323,7 +315,7 @@ "name": "voice-chat", "prevSize": 32, "code": 59783, - "tempChar": "" + "tempChar": "" }, { "order": 689, @@ -331,7 +323,7 @@ "name": "video", "prevSize": 32, "code": 59784, - "tempChar": "" + "tempChar": "" }, { "order": 686, @@ -339,7 +331,7 @@ "name": "noise-suppression", "prevSize": 32, "code": 59785, - "tempChar": "" + "tempChar": "" }, { "order": 703, @@ -347,7 +339,7 @@ "name": "phone-discard", "prevSize": 32, "code": 59786, - "tempChar": "" + "tempChar": "" }, { "order": 667, @@ -355,7 +347,7 @@ "name": "bot-commands-filled", "prevSize": 32, "code": 59775, - "tempChar": "" + "tempChar": "" }, { "order": 664, @@ -363,7 +355,7 @@ "name": "reply-filled", "prevSize": 32, "code": 59776, - "tempChar": "" + "tempChar": "" }, { "order": 656, @@ -371,7 +363,7 @@ "name": "bug", "prevSize": 32, "code": 59774, - "tempChar": "" + "tempChar": "" }, { "order": 619, @@ -379,7 +371,7 @@ "name": "data", "prevSize": 32, "code": 59773, - "tempChar": "" + "tempChar": "" }, { "order": 622, @@ -387,15 +379,15 @@ "name": "darkmode", "prevSize": 32, "code": 59769, - "tempChar": "" + "tempChar": "" }, { - "order": 0, + "order": 711, "id": 29, "name": "animations", "prevSize": 32, - "code": 59770, - "tempChar": "" + "code": 59804, + "tempChar": "" }, { "order": 626, @@ -403,7 +395,7 @@ "name": "enter", "prevSize": 32, "code": 59771, - "tempChar": "" + "tempChar": "" }, { "order": 627, @@ -411,7 +403,7 @@ "name": "fontsize", "prevSize": 32, "code": 59772, - "tempChar": "" + "tempChar": "" }, { "order": 630, @@ -419,7 +411,7 @@ "name": "permissions", "prevSize": 32, "code": 59766, - "tempChar": "" + "tempChar": "" }, { "order": 631, @@ -427,7 +419,7 @@ "name": "card", "prevSize": 32, "code": 59767, - "tempChar": "" + "tempChar": "" }, { "order": 634, @@ -435,7 +427,7 @@ "name": "truck", "prevSize": 32, "code": 59768, - "tempChar": "" + "tempChar": "" }, { "order": 663, @@ -443,7 +435,7 @@ "name": "share-filled", "prevSize": 32, "code": 59738, - "tempChar": "" + "tempChar": "" }, { "order": 638, @@ -451,7 +443,7 @@ "name": "bold", "prevSize": 32, "code": 59745, - "tempChar": "" + "tempChar": "" }, { "order": 639, @@ -459,7 +451,7 @@ "name": "bot-command", "prevSize": 32, "code": 59746, - "tempChar": "" + "tempChar": "" }, { "order": 642, @@ -467,7 +459,7 @@ "name": "calendar-filter", "prevSize": 32, "code": 59747, - "tempChar": "" + "tempChar": "" }, { "order": 643, @@ -475,7 +467,7 @@ "name": "comments", "prevSize": 32, "code": 59748, - "tempChar": "" + "tempChar": "" }, { "order": 645, @@ -483,7 +475,7 @@ "name": "comments-sticker", "prevSize": 32, "code": 59749, - "tempChar": "" + "tempChar": "" }, { "order": 646, @@ -491,7 +483,7 @@ "name": "arrow-down", "prevSize": 32, "code": 59750, - "tempChar": "" + "tempChar": "" }, { "order": 668, @@ -499,7 +491,7 @@ "name": "email", "prevSize": 32, "code": 59751, - "tempChar": "" + "tempChar": "" }, { "order": 648, @@ -507,7 +499,7 @@ "name": "italic", "prevSize": 32, "code": 59752, - "tempChar": "" + "tempChar": "" }, { "order": 620, @@ -515,7 +507,7 @@ "name": "link", "prevSize": 32, "code": 59753, - "tempChar": "" + "tempChar": "" }, { "order": 621, @@ -523,7 +515,7 @@ "name": "mention", "prevSize": 32, "code": 59754, - "tempChar": "" + "tempChar": "" }, { "order": 624, @@ -531,7 +523,7 @@ "name": "monospace", "prevSize": 32, "code": 59755, - "tempChar": "" + "tempChar": "" }, { "order": 625, @@ -539,7 +531,7 @@ "name": "next", "prevSize": 32, "code": 59756, - "tempChar": "" + "tempChar": "" }, { "order": 628, @@ -547,7 +539,7 @@ "name": "password-off", "prevSize": 32, "code": 59757, - "tempChar": "" + "tempChar": "" }, { "order": 629, @@ -555,7 +547,7 @@ "name": "pin-list", "prevSize": 32, "code": 59758, - "tempChar": "" + "tempChar": "" }, { "order": 632, @@ -563,7 +555,7 @@ "name": "previous", "prevSize": 32, "code": 59759, - "tempChar": "" + "tempChar": "" }, { "order": 633, @@ -571,7 +563,7 @@ "name": "replace", "prevSize": 32, "code": 59760, - "tempChar": "" + "tempChar": "" }, { "order": 636, @@ -579,7 +571,7 @@ "name": "schedule", "prevSize": 32, "code": 59761, - "tempChar": "" + "tempChar": "" }, { "order": 691, @@ -587,7 +579,7 @@ "name": "strikethrough", "prevSize": 32, "code": 59762, - "tempChar": "" + "tempChar": "" }, { "order": 692, @@ -595,7 +587,7 @@ "name": "underlined", "prevSize": 32, "code": 59763, - "tempChar": "" + "tempChar": "" }, { "order": 641, @@ -603,7 +595,7 @@ "name": "zoom-in", "prevSize": 32, "code": 59764, - "tempChar": "" + "tempChar": "" }, { "order": 649, @@ -611,7 +603,7 @@ "name": "zoom-out", "prevSize": 32, "code": 59765, - "tempChar": "" + "tempChar": "" } ], "id": 2, @@ -625,6 +617,21 @@ "height": 1024, "prevSize": 32, "icons": [ + { + "id": 61, + "paths": [ + "M919.6 354.4c-6.533-84.133-54.8-153.067-129.2-184.4-44.667-18.8-95.733-22.933-148-12-44.667 9.467-88.267 29.2-130 58.8-42-30-85.867-50-130.933-59.467-52.133-10.933-103.333-6.8-148 11.867-74.4 31.333-122.667 100.267-129.2 184.4-6 76.667 22 161.2 81.067 244.667 65.2 92.4 165.867 181.2 299.333 264.4 7.733 6.533 17.6 10 27.6 10 7.6 0 15.333-2 22.4-6.4 135.867-83.867 238.133-173.867 304-267.2 58.933-83.333 86.933-168 80.933-244.667zM768.933 549.867c-55.6 78.8-141.867 155.867-256.4 229.333-115.067-73.733-201.6-151.067-257.467-230.133-59.733-84.667-68.667-149.467-65.6-188.933 4.133-52.533 32.267-93.467 77.2-112.4 28.533-12 62.133-14.4 97.333-7.067 39.067 8.267 79.467 28.667 116.933 59.333 15.333 16.4 41.067 18.267 58.533 3.6 38.533-32.267 80.133-53.733 120.533-62.133 35.067-7.333 68.8-4.933 97.333 7.067 44.933 18.933 73.2 59.867 77.2 112.4 3.067 39.467-5.867 104.267-65.6 188.933z" + ], + "attrs": [ + {} + ], + "isMulticolor": false, + "isMulticolor2": false, + "grid": 24, + "tags": [ + "heart-outline" + ] + }, { "id": 60, "paths": [ @@ -643,57 +650,17 @@ { "id": 59, "paths": [ - "M919.6 354.4c-6.533-84.133-54.8-153.067-129.2-184.4-44.667-18.8-95.733-22.933-148-12-44.667 9.467-88.267 29.2-130 58.8-42-30-85.867-50-130.933-59.467-52.133-10.933-103.333-6.8-148 11.867-74.4 31.333-122.667 100.267-129.2 184.4-6 76.667 22 161.2 81.067 244.667 65.2 92.4 165.867 181.2 299.333 264.4 7.733 6.533 17.6 10 27.6 10 7.6 0 15.333-2 22.4-6.4 135.867-83.867 238.133-173.867 304-267.2 58.933-83.333 86.933-168 80.933-244.667zM768.933 549.867c-55.6 78.8-141.867 155.867-256.4 229.333-115.067-73.733-201.6-151.067-257.467-230.133-59.733-84.667-68.667-149.467-65.6-188.933 4.133-52.533 32.267-93.467 77.2-112.4 28.533-12 62.133-14.4 97.333-7.067 39.067 8.267 79.467 28.667 116.933 59.333 15.333 16.4 41.067 18.267 58.533 3.6 38.533-32.267 80.133-53.733 120.533-62.133 35.067-7.333 68.8-4.933 97.333 7.067 44.933 18.933 73.2 59.867 77.2 112.4 3.067 39.467-5.867 104.267-65.6 188.933z" + "M128 341.867h768c25.6 0 42.667-17.067 42.667-42.667s-17.067-42.667-42.667-42.667h-768c-25.6 0-42.667 17.067-42.667 42.667s17.067 42.667 42.667 42.667zM384 683.2h-256c-25.6 0-42.667 17.067-42.667 42.667s17.067 42.667 42.667 42.667h256c25.6 0 42.667-17.067 42.667-42.667s-17.067-42.667-42.667-42.667v0zM789.333 469.867h-661.333c-25.6 0-42.667 17.067-42.667 42.667s17.067 42.667 42.667 42.667h661.333c34.133 0 64 29.867 64 64s-29.867 64-64 64h-119.467c12.8-17.067 17.067-38.4 0-55.467s-42.667-21.333-59.733-4.267l-85.333 72.533-4.267 4.267c-17.067 17.067-12.8 46.933 4.267 59.733l85.333 72.533c8.533 4.267 17.067 8.533 25.6 8.533 12.8 0 25.6-4.267 34.133-17.067 12.8-17.067 12.8-38.4 0-55.467h119.467c81.067 0 149.333-68.267 149.333-149.333s-68.267-149.333-149.333-149.333v0z" ], "attrs": [ {} ], - "isMulticolor": false, - "isMulticolor2": false, "grid": 24, "tags": [ - "heart-outline" - ] - }, - { - "id": 58, - "paths": [ - "M541.257 911.848c-7.313 0-14.629 0-20.724-1.219-65.829-6.095-134.095-37.789-181.639-85.333l-249.905-249.905c-19.505-20.724-30.476-46.324-30.476-75.581 0-28.039 10.971-54.857 30.476-74.361 3.657-3.657 7.315-7.315 12.191-9.752-13.411-17.067-20.724-37.791-21.943-59.733v-3.657c0-29.257 10.971-56.076 30.476-75.581 9.752-9.752 20.724-17.067 32.915-23.163 0-1.219 0-2.437 0-4.876v-3.657c0-29.257 10.971-56.076 30.476-75.581 19.505-20.724 46.324-31.695 75.581-31.695 18.287 0 35.352 4.876 51.2 13.409 2.437-3.657 4.876-7.313 8.533-9.752l2.439-3.657c20.724-20.724 47.543-31.695 75.581-31.695s54.857 10.971 75.581 31.695c46.324 45.105 87.771 84.115 123.124 119.467 0-17.067 1.219-19.505 3.657-24.381l13.409-23.161 2.439-2.439c18.285-18.285 40.229-29.257 63.389-30.476 3.657 0 7.313 0 12.191 0s10.971 0 17.067-1.219c26.819 0 51.2 13.411 68.267 39.009 12.191 18.287 19.505 41.448 21.943 54.857 6.095 34.133 14.629 71.924 24.381 112.152 15.848 68.267 34.133 146.285 39.011 209.676 2.437 54.857-36.571 128-65.829 160.915-14.629 20.724-45.105 53.639-86.552 95.085s-96.305 63.391-157.257 64.611zM248.685 199.924c-12.189 0-23.161 4.876-31.695 13.409s-13.411 19.505-13.411 31.695v2.437c0 4.876 1.219 9.752 3.657 15.848l17.067 39.009-43.887 4.876c-9.752 1.219-19.505 4.876-26.819 13.411-8.533 8.533-13.411 19.505-13.411 31.695v2.439c0 10.971 4.876 20.724 13.411 29.257l62.171 63.389-60.952 9.752c-8.533 1.219-17.067 6.095-23.161 12.189-8.533 8.533-13.411 19.505-13.411 31.695s4.876 23.161 13.411 31.695l249.905 248.685c37.789 37.789 91.429 63.391 143.848 68.267 35.352 2.437 87.771-2.437 129.219-45.105 57.295-58.513 76.8-82.895 80.457-88.991l2.437-3.657c24.381-25.6 52.419-85.333 49.981-115.811-3.657-59.733-21.943-134.095-36.571-198.705-9.752-42.667-19.505-81.676-25.6-117.029-1.219-8.533-6.095-21.943-12.191-30.476s-12.189-12.189-15.848-12.189c0 0 0 0 0 0-6.095 0-10.971 0-15.848 1.219h-4.876c-1.219 0-3.657 0-4.876 0-6.095 0-13.409 2.437-21.943 9.752l-4.876 8.533c1.219 12.191 2.439 35.352 3.657 63.391l6.095 92.648-57.295-56.076c-46.324-46.324-107.276-104.837-180.419-175.543-18.285-18.287-47.543-17.067-63.389 0l-1.219 1.219c-4.876 6.095-8.533 13.411-10.971 21.943l-9.752 60.952-51.2-52.419c-8.533-8.533-19.505-13.411-31.695-13.411z", - "M753.371 917.943c-12.191 0-24.381-7.313-28.039-19.505-6.095-15.848 2.439-32.913 18.287-39.009s30.476-15.848 42.667-28.039c58.513-59.733 70.705-76.8 73.143-81.676l1.219-4.876 4.876-4.876c18.285-19.505 42.667-68.267 41.448-92.648-3.657-56.076-19.505-125.563-34.133-191.391-9.752-41.448-18.287-81.676-24.381-118.248-1.219-4.876-3.657-13.411-7.315-18.287-9.752-13.411-6.095-32.913 7.315-42.667s32.915-6.095 42.667 7.315c9.752 14.629 15.848 32.913 17.067 42.667 6.095 34.133 14.629 73.143 24.381 114.591 15.848 69.485 31.695 140.189 36.571 201.143 2.439 45.105-29.257 106.057-54.857 135.313-6.095 10.971-24.381 34.133-82.895 93.867-18.287 19.505-40.229 34.133-64.611 42.667-7.313 2.437-10.971 3.657-13.411 3.657zM856.991 749.715c0 0 0 0 0 0v0z", - "M314.515 605.867c-6.095 0-13.411-2.439-18.287-7.313l-146.285-158.476c-8.533-9.752-8.533-25.6 1.219-34.133s25.6-8.533 34.133 1.219l146.287 158.476c8.533 9.752 8.533 25.6-1.219 34.133-4.876 4.876-10.971 6.095-15.848 6.095z", - "M412.039 520.533c-6.095 0-12.189-2.439-17.067-7.315l-195.048-195.048c-9.752-9.752-9.752-24.381 0-34.133s24.381-9.752 34.133 0l195.048 195.048c9.752 9.752 9.752 24.381 0 34.133-4.876 6.095-10.971 7.315-17.067 7.315z", - "M497.371 423.009c-6.095 0-12.189-2.439-17.067-7.315l-195.048-195.048c-9.752-9.752-9.752-24.381 0-34.133s24.381-9.752 34.133 0l195.048 195.048c9.752 9.752 9.752 24.381 0 34.133-4.876 6.095-10.971 7.315-17.067 7.315z" - ], - "attrs": [ - {}, - {}, - {}, - {}, - {} + "word-wrap" ], "isMulticolor": false, - "isMulticolor2": false, - "grid": 24, - "tags": [ - "reactions" - ] - }, - { - "id": 57, - "paths": [ - "M829.156 277.333c12.8 0 25.6 27.023 28.444 41.244 15.644 89.6 41.244 147.911 46.933 238.933 2.844 34.133-28.444 146.489-49.779 167.823 2.844 1.423-21.333 25.6-69.689 73.956-28.444 28.444-65.423 41.244-102.4 42.667 5.689-4.267 11.379-8.533 17.067-14.223 48.356-48.356 75.379-78.221 78.223-86.756 21.333-21.333 51.2-76.8 48.356-110.933-5.689-75.379-28.444-159.289-45.511-236.089-1.421-9.956-1.421-24.177-2.844-39.821v-7.111c-1.421-22.756-2.844-42.667-2.844-42.667 1.423-5.689 4.267-11.377 8.533-15.644 5.689-5.689 14.223-9.956 21.333-9.956 0-1.423 11.379-1.423 24.179-1.423zM493.511 204.8c17.067-17.067 44.089-17.067 61.156 0 11.379 11.377 22.756 21.333 32.711 32.711 7.111 46.933 14.223 93.867 9.956 89.6-38.4-39.823-76.8-75.377-112.356-108.089 1.423-5.689 4.267-9.956 8.533-14.223z", - "M140.8 553.244l240.356 237.511c66.844 66.844 189.156 96.711 261.689 22.756 48.356-48.356 75.379-78.221 78.221-86.756 21.333-21.333 51.2-76.8 48.356-110.933-5.689-91.023-44.089-210.489-61.156-301.511-2.844-14.221-14.223-41.244-28.444-41.244-11.379 0-24.177 0-24.177 0-8.533 0-15.644 4.267-21.333 9.956-4.267 4.267-7.111 9.956-8.533 15.644 0 0 11.379 103.823 12.8 116.623s-7.111 17.067-17.067 8.533c-46.933-46.933-118.044-116.621-210.489-204.8-17.067-17.067-44.089-17.067-61.156 0s-17.067 44.089 0 61.156l142.223 142.223-19.911 19.911-190.579-193.421c-17.067-17.067-44.089-17.067-61.156 0s-17.067 44.089 0 61.156l193.421 193.423-19.911 19.911-173.511-173.511c-17.067-17.067-44.089-17.067-61.156 0s-17.067 44.089 0 61.156l173.511 172.089-19.911 21.333-112.356-112.356c-17.067-17.067-44.089-17.067-61.156 0-15.644 17.067-15.644 44.089 1.421 61.156z" - ], - "attrs": [ - {}, - {} - ], - "isMulticolor": false, - "isMulticolor2": false, - "grid": 24, - "tags": [ - "reaction-filled" - ] + "isMulticolor2": false }, { "id": 56, @@ -3093,7 +3060,7 @@ "name": "select", "prevSize": 32, "code": 59744, - "tempChar": "" + "tempChar": "" }, { "order": 480, @@ -3101,7 +3068,7 @@ "name": "folder", "prevSize": 32, "code": 59667, - "tempChar": "" + "tempChar": "" }, { "order": 481, @@ -3109,7 +3076,7 @@ "name": "bots", "prevSize": 32, "code": 59669, - "tempChar": "" + "tempChar": "" }, { "order": 482, @@ -3117,7 +3084,7 @@ "name": "calendar", "prevSize": 32, "code": 59670, - "tempChar": "" + "tempChar": "" }, { "order": 483, @@ -3125,7 +3092,7 @@ "name": "cloud-download", "prevSize": 32, "code": 59671, - "tempChar": "" + "tempChar": "" }, { "order": 484, @@ -3133,7 +3100,7 @@ "name": "colorize", "prevSize": 32, "code": 59672, - "tempChar": "" + "tempChar": "" }, { "order": 651, @@ -3141,7 +3108,7 @@ "name": "forward", "prevSize": 32, "code": 59687, - "tempChar": "" + "tempChar": "" }, { "order": 650, @@ -3149,7 +3116,7 @@ "name": "reply", "prevSize": 32, "code": 59719, - "tempChar": "" + "tempChar": "" }, { "order": 487, @@ -3157,7 +3124,7 @@ "name": "help", "prevSize": 32, "code": 59690, - "tempChar": "" + "tempChar": "" }, { "order": 488, @@ -3165,7 +3132,7 @@ "name": "info", "prevSize": 32, "code": 59691, - "tempChar": "" + "tempChar": "" }, { "order": 489, @@ -3173,7 +3140,7 @@ "name": "info-filled", "prevSize": 32, "code": 59675, - "tempChar": "" + "tempChar": "" }, { "order": 490, @@ -3181,7 +3148,7 @@ "name": "delete-filled", "prevSize": 32, "code": 59676, - "tempChar": "" + "tempChar": "" }, { "order": 491, @@ -3189,7 +3156,7 @@ "name": "delete", "prevSize": 32, "code": 59677, - "tempChar": "" + "tempChar": "" }, { "order": 492, @@ -3197,7 +3164,7 @@ "name": "edit", "prevSize": 32, "code": 59683, - "tempChar": "" + "tempChar": "" }, { "order": 493, @@ -3205,7 +3172,7 @@ "name": "new-chat-filled", "prevSize": 32, "code": 59705, - "tempChar": "" + "tempChar": "" }, { "order": 494, @@ -3213,7 +3180,7 @@ "name": "send", "prevSize": 32, "code": 59722, - "tempChar": "" + "tempChar": "" }, { "order": 495, @@ -3221,7 +3188,7 @@ "name": "send-outline", "prevSize": 32, "code": 59723, - "tempChar": "" + "tempChar": "" }, { "order": 496, @@ -3229,7 +3196,7 @@ "name": "add-user-filled", "prevSize": 32, "code": 59652, - "tempChar": "" + "tempChar": "" }, { "order": 497, @@ -3237,7 +3204,7 @@ "name": "add-user", "prevSize": 32, "code": 59653, - "tempChar": "" + "tempChar": "" }, { "order": 498, @@ -3245,7 +3212,7 @@ "name": "delete-user", "prevSize": 32, "code": 59678, - "tempChar": "" + "tempChar": "" }, { "order": 499, @@ -3253,7 +3220,7 @@ "name": "microphone", "prevSize": 32, "code": 59701, - "tempChar": "" + "tempChar": "" }, { "order": 500, @@ -3261,7 +3228,7 @@ "name": "microphone-alt", "prevSize": 32, "code": 59707, - "tempChar": "" + "tempChar": "" }, { "order": 501, @@ -3269,7 +3236,7 @@ "name": "poll", "prevSize": 32, "code": 59704, - "tempChar": "" + "tempChar": "" }, { "order": 502, @@ -3277,7 +3244,7 @@ "name": "revote", "prevSize": 32, "code": 59706, - "tempChar": "" + "tempChar": "" }, { "order": 503, @@ -3285,7 +3252,7 @@ "name": "photo", "prevSize": 32, "code": 59712, - "tempChar": "" + "tempChar": "" }, { "order": 504, @@ -3293,7 +3260,7 @@ "name": "document", "prevSize": 32, "code": 59679, - "tempChar": "" + "tempChar": "" }, { "order": 505, @@ -3301,7 +3268,7 @@ "name": "camera", "prevSize": 32, "code": 59662, - "tempChar": "" + "tempChar": "" }, { "order": 506, @@ -3309,7 +3276,7 @@ "name": "camera-add", "prevSize": 32, "code": 59663, - "tempChar": "" + "tempChar": "" }, { "order": 507, @@ -3317,7 +3284,7 @@ "name": "logout", "prevSize": 32, "code": 59698, - "tempChar": "" + "tempChar": "" }, { "order": 508, @@ -3325,7 +3292,7 @@ "name": "saved-messages", "prevSize": 32, "code": 59720, - "tempChar": "" + "tempChar": "" }, { "order": 509, @@ -3333,7 +3300,7 @@ "name": "settings", "prevSize": 32, "code": 59726, - "tempChar": "" + "tempChar": "" }, { "order": 652, @@ -3341,7 +3308,7 @@ "name": "phone", "prevSize": 32, "code": 59711, - "tempChar": "" + "tempChar": "" }, { "order": 653, @@ -3349,7 +3316,7 @@ "name": "attach", "prevSize": 32, "code": 59657, - "tempChar": "" + "tempChar": "" }, { "order": 512, @@ -3357,7 +3324,7 @@ "name": "copy", "prevSize": 32, "code": 59674, - "tempChar": "" + "tempChar": "" }, { "order": 513, @@ -3365,7 +3332,7 @@ "name": "channel", "prevSize": 32, "code": 59665, - "tempChar": "" + "tempChar": "" }, { "order": 514, @@ -3373,7 +3340,7 @@ "name": "group", "prevSize": 32, "code": 59689, - "tempChar": "" + "tempChar": "" }, { "order": 515, @@ -3381,7 +3348,7 @@ "name": "user", "prevSize": 32, "code": 59737, - "tempChar": "" + "tempChar": "" }, { "order": 516, @@ -3389,7 +3356,7 @@ "name": "non-contacts", "prevSize": 32, "code": 59688, - "tempChar": "" + "tempChar": "" }, { "order": 517, @@ -3397,7 +3364,7 @@ "name": "active-sessions", "prevSize": 32, "code": 59650, - "tempChar": "" + "tempChar": "" }, { "order": 518, @@ -3405,7 +3372,7 @@ "name": "admin", "prevSize": 32, "code": 59654, - "tempChar": "" + "tempChar": "" }, { "order": 519, @@ -3413,7 +3380,7 @@ "name": "download", "prevSize": 32, "code": 59681, - "tempChar": "" + "tempChar": "" }, { "order": 520, @@ -3421,7 +3388,7 @@ "name": "location", "prevSize": 32, "code": 59696, - "tempChar": "" + "tempChar": "" }, { "order": 521, @@ -3429,7 +3396,7 @@ "name": "stop", "prevSize": 32, "code": 59730, - "tempChar": "" + "tempChar": "" }, { "order": 523, @@ -3437,7 +3404,7 @@ "name": "archive", "prevSize": 32, "code": 59656, - "tempChar": "" + "tempChar": "" }, { "order": 524, @@ -3445,7 +3412,7 @@ "name": "unarchive", "prevSize": 32, "code": 59731, - "tempChar": "" + "tempChar": "" }, { "order": 525, @@ -3453,7 +3420,7 @@ "name": "readchats", "prevSize": 32, "code": 59699, - "tempChar": "" + "tempChar": "" }, { "order": 526, @@ -3461,7 +3428,7 @@ "name": "unread", "prevSize": 32, "code": 59735, - "tempChar": "" + "tempChar": "" }, { "order": 654, @@ -3469,7 +3436,7 @@ "name": "message", "prevSize": 32, "code": 59700, - "tempChar": "" + "tempChar": "" }, { "order": 659, @@ -3477,7 +3444,7 @@ "name": "lock", "prevSize": 32, "code": 59697, - "tempChar": "" + "tempChar": "" }, { "order": 529, @@ -3485,7 +3452,7 @@ "name": "unlock", "prevSize": 32, "code": 59732, - "tempChar": "" + "tempChar": "" }, { "order": 530, @@ -3493,7 +3460,7 @@ "name": "mute", "prevSize": 32, "code": 59703, - "tempChar": "" + "tempChar": "" }, { "order": 531, @@ -3501,7 +3468,7 @@ "name": "unmute", "prevSize": 32, "code": 59733, - "tempChar": "" + "tempChar": "" }, { "order": 532, @@ -3509,7 +3476,7 @@ "name": "pin", "prevSize": 32, "code": 59713, - "tempChar": "" + "tempChar": "" }, { "order": 533, @@ -3517,7 +3484,7 @@ "name": "unpin", "prevSize": 32, "code": 59734, - "tempChar": "" + "tempChar": "" }, { "order": 534, @@ -3525,7 +3492,7 @@ "name": "smallscreen", "prevSize": 32, "code": 59742, - "tempChar": "" + "tempChar": "" }, { "order": 535, @@ -3533,7 +3500,7 @@ "name": "fullscreen", "prevSize": 32, "code": 59743, - "tempChar": "" + "tempChar": "" }, { "order": 536, @@ -3541,7 +3508,7 @@ "name": "large-pause", "prevSize": 32, "code": 59694, - "tempChar": "" + "tempChar": "" }, { "order": 537, @@ -3549,7 +3516,7 @@ "name": "large-play", "prevSize": 32, "code": 59695, - "tempChar": "" + "tempChar": "" }, { "order": 538, @@ -3557,7 +3524,7 @@ "name": "pause", "prevSize": 32, "code": 59709, - "tempChar": "" + "tempChar": "" }, { "order": 539, @@ -3565,7 +3532,7 @@ "name": "play", "prevSize": 32, "code": 59715, - "tempChar": "" + "tempChar": "" }, { "order": 540, @@ -3573,7 +3540,7 @@ "name": "channelviews", "prevSize": 32, "code": 59666, - "tempChar": "" + "tempChar": "" }, { "order": 541, @@ -3581,7 +3548,7 @@ "name": "message-succeeded", "prevSize": 32, "code": 59648, - "tempChar": "" + "tempChar": "" }, { "order": 657, @@ -3589,7 +3556,7 @@ "name": "message-read", "prevSize": 32, "code": 59649, - "tempChar": "" + "tempChar": "" }, { "order": 543, @@ -3597,7 +3564,7 @@ "name": "message-pending", "prevSize": 32, "code": 59724, - "tempChar": "" + "tempChar": "" }, { "order": 544, @@ -3605,7 +3572,7 @@ "name": "message-failed", "prevSize": 32, "code": 59725, - "tempChar": "" + "tempChar": "" }, { "order": 545, @@ -3613,7 +3580,7 @@ "name": "favorite", "prevSize": 32, "code": 59710, - "tempChar": "" + "tempChar": "" }, { "order": 546, @@ -3621,7 +3588,7 @@ "name": "keyboard", "prevSize": 32, "code": 59716, - "tempChar": "" + "tempChar": "" }, { "order": 547, @@ -3629,7 +3596,7 @@ "name": "delete-left", "prevSize": 32, "code": 59717, - "tempChar": "" + "tempChar": "" }, { "order": 548, @@ -3637,7 +3604,7 @@ "name": "recent", "prevSize": 32, "code": 59718, - "tempChar": "" + "tempChar": "" }, { "order": 549, @@ -3645,7 +3612,7 @@ "name": "gifs", "prevSize": 32, "code": 59727, - "tempChar": "" + "tempChar": "" }, { "order": 550, @@ -3653,7 +3620,7 @@ "name": "stickers", "prevSize": 32, "code": 59739, - "tempChar": "" + "tempChar": "" }, { "order": 551, @@ -3661,7 +3628,7 @@ "name": "smile", "prevSize": 32, "code": 59728, - "tempChar": "" + "tempChar": "" }, { "order": 552, @@ -3669,7 +3636,7 @@ "name": "animals", "prevSize": 32, "code": 59655, - "tempChar": "" + "tempChar": "" }, { "order": 553, @@ -3677,7 +3644,7 @@ "name": "eats", "prevSize": 32, "code": 59682, - "tempChar": "" + "tempChar": "" }, { "order": 554, @@ -3685,7 +3652,7 @@ "name": "sport", "prevSize": 32, "code": 59729, - "tempChar": "" + "tempChar": "" }, { "order": 555, @@ -3693,7 +3660,7 @@ "name": "car", "prevSize": 32, "code": 59664, - "tempChar": "" + "tempChar": "" }, { "order": 556, @@ -3701,7 +3668,7 @@ "name": "lamp", "prevSize": 32, "code": 59692, - "tempChar": "" + "tempChar": "" }, { "order": 557, @@ -3709,7 +3676,7 @@ "name": "language", "prevSize": 32, "code": 59693, - "tempChar": "" + "tempChar": "" }, { "order": 558, @@ -3717,7 +3684,7 @@ "name": "flag", "prevSize": 32, "code": 59686, - "tempChar": "" + "tempChar": "" }, { "order": 559, @@ -3725,7 +3692,7 @@ "name": "more", "prevSize": 32, "code": 59702, - "tempChar": "" + "tempChar": "" }, { "order": 560, @@ -3733,7 +3700,7 @@ "name": "search", "prevSize": 32, "code": 59721, - "tempChar": "" + "tempChar": "" }, { "order": 561, @@ -3741,7 +3708,7 @@ "name": "remove", "prevSize": 32, "code": 59740, - "tempChar": "" + "tempChar": "" }, { "order": 562, @@ -3749,7 +3716,7 @@ "name": "add", "prevSize": 32, "code": 59651, - "tempChar": "" + "tempChar": "" }, { "order": 563, @@ -3757,7 +3724,7 @@ "name": "check", "prevSize": 32, "code": 59668, - "tempChar": "" + "tempChar": "" }, { "order": 564, @@ -3765,7 +3732,7 @@ "name": "close", "prevSize": 32, "code": 59673, - "tempChar": "" + "tempChar": "" }, { "order": 610, @@ -3773,7 +3740,7 @@ "name": "arrow-left", "prevSize": 32, "code": 59661, - "tempChar": "" + "tempChar": "" }, { "order": 566, @@ -3781,7 +3748,7 @@ "name": "arrow-right", "prevSize": 32, "code": 59708, - "tempChar": "" + "tempChar": "" }, { "order": 567, @@ -3789,7 +3756,7 @@ "name": "down", "prevSize": 32, "code": 59680, - "tempChar": "" + "tempChar": "" }, { "order": 568, @@ -3797,7 +3764,7 @@ "name": "up", "prevSize": 32, "code": 59736, - "tempChar": "" + "tempChar": "" }, { "order": 569, @@ -3805,7 +3772,7 @@ "name": "eye-closed", "prevSize": 32, "code": 59685, - "tempChar": "" + "tempChar": "" }, { "order": 570, @@ -3813,7 +3780,7 @@ "name": "eye", "prevSize": 32, "code": 59684, - "tempChar": "" + "tempChar": "" }, { "order": 571, @@ -3821,7 +3788,7 @@ "name": "muted", "prevSize": 32, "code": 59741, - "tempChar": "" + "tempChar": "" }, { "order": 572, @@ -3829,7 +3796,7 @@ "name": "avatar-archived-chats", "prevSize": 32, "code": 59658, - "tempChar": "" + "tempChar": "" }, { "order": 573, @@ -3837,7 +3804,7 @@ "name": "avatar-deleted-account", "prevSize": 32, "code": 59659, - "tempChar": "" + "tempChar": "" }, { "order": 574, @@ -3845,7 +3812,7 @@ "name": "avatar-saved-messages", "prevSize": 32, "code": 59660, - "tempChar": "" + "tempChar": "" }, { "order": 575, @@ -3853,7 +3820,7 @@ "name": "pinned-chat", "prevSize": 32, "code": 59714, - "tempChar": "" + "tempChar": "" } ], "prevSize": 32, @@ -3886,7 +3853,9 @@ "showMetadata": false, "showVersion": false, "noie8": true, - "ie7": false + "ie7": false, + "cssVars": false, + "cssVarsFormat": "scss" }, "imagePref": { "prefix": "icon-", diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index ffd0a4735..6a9cde07a 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -165,6 +165,9 @@ $color-message-reaction-own-hover: #b5e0a4; --color-skeleton-background: rgba(33, 33, 33, 0.15); --color-skeleton-foreground: rgba(232, 232, 232, 0.2); + --color-scrollbar: rgba(90, 90, 90, 0.3); + --color-scrollbar-code: rgba(200, 200, 200, 0.3); + --color-telegram-blue: #{$color-primary}; --vh: 1vh; diff --git a/src/styles/icons.scss b/src/styles/icons.scss index b5842c3f9..651f21bde 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -51,11 +51,14 @@ .icon-volume-3:before { content: "\e991"; } -.icon-heart:before { - content: "\e99a"; -} .icon-heart-outline:before { - content: "\e99b"; + content: "\e99e"; +} +.icon-heart:before { + content: "\e99f"; +} +.icon-word-wrap:before { + content: "\e99d"; } .icon-webapp:before { content: "\e993"; @@ -132,6 +135,9 @@ .icon-darkmode:before { content: "\e979"; } +.icon-animations:before { + content: "\e99c"; +} .icon-enter:before { content: "\e97b"; } diff --git a/src/styles/index.scss b/src/styles/index.scss index 809d8710a..386c84ab2 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -12,12 +12,11 @@ html, body { width: 100%; height: 100%; - background: var(--color-background); + background-color: var(--color-background); + font-family: var(--font-family); margin: 0; padding: 0; font-size: 16px; - font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI", Oxygen, Ubuntu, Cantarell, - "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; color: var(--color-text); font-weight: 400; line-height: 1.5; @@ -26,6 +25,10 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + --font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI", Oxygen, Ubuntu, Cantarell, + "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + --font-family-monospace: "Cascadia Mono", "Roboto Mono", "Menlo", "Courier", "Courier New", monospace; + @media (max-width: 600px) { height: calc(var(--vh, 1vh) * 100); } @@ -33,17 +36,17 @@ body { body.is-ios, body.is-macos { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Roboto", "Apple Color Emoji", "Helvetica Neue", sans-serif; + --font-family: system-ui, -apple-system, BlinkMacSystemFont, "Roboto", "Apple Color Emoji", "Helvetica Neue", sans-serif; } html[lang="fa"], html[lang="fa"] body { - font-family: "Vazirmatn", "Roboto", -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI", Oxygen, Ubuntu, + --font-family: "Vazirmatn", "Roboto", -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI", Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; &.is-ios, &.is-macos { - font-family: "Vazirmatn", -apple-system, BlinkMacSystemFont, "Roboto", "Apple Color Emoji", "Segoe UI", Oxygen, Ubuntu, + --font-family: "Vazirmatn", -apple-system, BlinkMacSystemFont, "Roboto", "Apple Color Emoji", "Segoe UI", Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } } @@ -138,7 +141,7 @@ body.cursor-ew-resize { .custom-scroll, .custom-scroll-x { scrollbar-width: thin; - scrollbar-color: rgba(90, 90, 90, 0) transparent; + scrollbar-color: transparent transparent; transition: scrollbar-color 0.3s ease; -webkit-overflow-scrolling: touch; @@ -146,7 +149,7 @@ body.cursor-ew-resize { pointer-events: auto; &::-webkit-scrollbar-thumb { - background-color: rgba(90, 90, 90, 0); + background-color: transparent; border-radius: 0.375rem; // `box-shadow` prevents repaint on macOS when hovering out of scrollable container box-shadow: 0 0 1px rgba(255, 255, 255, 0.01); @@ -155,10 +158,10 @@ body.cursor-ew-resize { &:hover, &:focus, &:focus-within { - scrollbar-color: rgba(90, 90, 90, 0.3) transparent; + scrollbar-color: var(--color-scrollbar) transparent; &::-webkit-scrollbar-thumb { - background-color: rgba(90, 90, 90, 0.3); + background-color: var(--color-scrollbar); } } } diff --git a/src/styles/themes.json b/src/styles/themes.json index 2503be58c..c748fa6a3 100644 --- a/src/styles/themes.json +++ b/src/styles/themes.json @@ -40,8 +40,8 @@ "--color-own-links": ["#3390EC", "#FFFFFF"], "--color-code": ["#4a729a", "#8774E1"], "--color-code-own": ["#3c7940", "#FFFFFF"], - "--color-code-bg": ["#70757914", "#ffffff26"], - "--color-code-own-bg": ["#70757914", "#ffffff26"], + "--color-code-bg": ["#70757914", "#ffffff07"], + "--color-code-own-bg": ["#70757914", "#00000020"], "--color-composer-button": ["#707579CC", "#AAAAAACC"], "--color-message-reaction": ["#ebf3fd", "#2b2a35"], "--color-message-reaction-hover": ["#c5def9", "#343147"], diff --git a/src/util/highlightCode.ts b/src/util/highlightCode.ts new file mode 100644 index 000000000..e7143a10f --- /dev/null +++ b/src/util/highlightCode.ts @@ -0,0 +1,108 @@ +import type { Element, Root } from 'hast'; +import { lowlight } from 'lowlight/lib/core'; +import Teact from '../lib/teact/teact'; + +// First element in alias array MUST BE a language package name +const SUPPORTED_LANGUAGES = { + '1c': ['1c', '1с'], // Allow cyrillic + bash: ['bash', 'sh'], + c: ['c', 'h'], + cpp: ['cpp', 'cc', 'c++', 'h++', 'hpp', 'hh', 'hxx', 'cxx'], + csharp: ['chasp', 'cs', 'c#'], + css: ['css'], + erlang: ['erlang', 'erl'], + elixir: ['elixir', 'ex', 'exs'], + go: ['go', 'golang'], + handlebars: ['handlebars', 'hbs', 'html.hbs', 'html.handlebars', 'htmlbars'], + haskell: ['haskell', 'hs'], + ini: ['ini', 'toml'], + java: ['java', 'jsp'], + javascript: ['javascript', 'js', 'jsx', 'mjs', 'cjs'], + json: ['json'], + kotlin: ['kotlin', 'kt', 'kts'], + lisp: ['lisp'], + lua: ['lua'], + makefile: ['makefile', 'mk', 'mak', 'make'], + markdown: ['markdown', 'md', 'mkdown', 'mkd'], + matlab: ['matlab'], + objectivec: ['objectivec', 'mm', 'objc', 'obj-c', 'obj-c++', 'objective-c++'], + perl: ['perl', 'pl', 'pm'], + php: ['php'], + python: ['python', 'py', 'gyp', 'ipython'], + r: ['r'], + ruby: ['ruby', 'rb', 'gemspec', 'podspec', 'thor', 'irb'], + rust: ['rust', 'rs'], + scss: ['scss'], + sql: ['sql'], + swift: ['swift'], + twig: ['twig', 'craftcms'], + typescript: ['typescript', 'ts', 'tsx'], + xml: ['xml', 'html', 'xhtml', 'rss', 'atom', 'xjb', 'xsd', 'xsl', 'plist', 'wsf', 'svg'], + yaml: ['yaml', 'yml'], +}; + +const languagePromises = new Map>(); + +export default async function highlightCode(text: string, language: string) { + const lowLang = language.toLowerCase(); + const result = await ensureLanguage(lowLang); + if (!result) return undefined; + const tree = lowlight.highlight(lowLang, text); + return treeToElements(tree); +} + +function getLanguageName(alias: string) { + return Object.values(SUPPORTED_LANGUAGES).find((codes) => codes.includes(alias))?.[0]; +} + +async function ensureLanguage(language: string) { + if (lowlight.registered(language)) { + return true; + } + + const langCode = getLanguageName(language); + if (!langCode) { + return false; + } + + if (languagePromises.has(langCode)) { + await languagePromises.get(langCode); + return true; + } + + // Funky webpack bug https://github.com/webpack/webpack/issues/13865 + const languagePromise = import( + /* webpackChunkName: "Highlight for [request]" */ + `../../node_modules/highlight.js/lib/languages/${langCode}` + ); + languagePromises.set(langCode, languagePromise); + // Allow errors to help debugging wrong language names + const syntax = await languagePromise; + lowlight.registerLanguage(langCode, syntax.default); + if (langCode === '1c') { + lowlight.registerAlias('1c', '1с'); // Allow cyrillic + } + return true; +} + +function treeToElements(tree: Element | Root): JSX.Element { + const children = tree.children.map((child) => { + if (child.type === 'text') { + return child.value; + } + if (child.type === 'element') { + return treeToElements(child); + } + return undefined; + }).filter(Boolean); + + if (tree.type === 'root') { + return Teact.createElement('code', { className: 'hljs custom-scroll-x' }, children); + } + + const name = tree.tagName; + const classNameArray = tree.properties?.className as string[]; + const className = classNameArray?.join(' '); + + return Teact.createElement(name, { className }, children); +} diff --git a/src/util/parseMessageInput.ts b/src/util/parseMessageInput.ts index 8b443f54e..2bb7150e3 100644 --- a/src/util/parseMessageInput.ts +++ b/src/util/parseMessageInput.ts @@ -75,7 +75,8 @@ function parseMarkdown(html: string) { parsedHtml = parsedHtml.replace(/<\/div>/g, ''); // Pre - parsedHtml = parsedHtml.replace(/^`{3}(.*[\n\r][^]*?^)`{3}/gm, '
$1
'); + parsedHtml = parsedHtml.replace(/^`{3}(.*?)[\n\r](.*?[\n\r].*?^)`{3}/gms, '
$2
'); + parsedHtml = parsedHtml.replace(/^`{3}[\n\r]?(.*?)[\n\r]?`{3}/gms, '
$1
'); parsedHtml = parsedHtml.replace(/[`]{3}([^`]+)[`]{3}/g, '
$1
'); // Code @@ -131,6 +132,7 @@ function getEntityDataFromNode( let url: string | undefined; let userId: string | undefined; + let language: string | undefined; if (type === ApiMessageEntityTypes.TextUrl) { url = (node as HTMLAnchorElement).href; } @@ -138,6 +140,10 @@ function getEntityDataFromNode( userId = (node as HTMLAnchorElement).dataset.userId; } + if (type === ApiMessageEntityTypes.Pre) { + language = (node as HTMLPreElement).dataset.language; + } + return { index, entity: { @@ -146,6 +152,7 @@ function getEntityDataFromNode( length, ...(url && { url }), ...(userId && { userId }), + ...(language && { language }), }, }; } diff --git a/webpack.config.js b/webpack.config.js index 2daa69d1d..8f7946e17 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,10 +5,10 @@ const { DefinePlugin, EnvironmentPlugin, ProvidePlugin, - + ContextReplacementPlugin, NormalModuleReplacementPlugin, } = require('webpack'); -const HtmlWebackPlugin = require('html-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { GitRevisionPlugin } = require('git-revision-webpack-plugin'); const StatoscopeWebpackPlugin = require('@statoscope/webpack-plugin').default; @@ -119,11 +119,16 @@ module.exports = (env = {}, argv = {}) => { }, }, plugins: [ + // Clearing of the unused files for code highlight for smaller chunk count + new ContextReplacementPlugin( + /highlight\.js\/lib\/languages/, + /^((?!\.js\.js).)*$/ + ), ...(process.env.APP_MOCKED_CLIENT === '1' ? [new NormalModuleReplacementPlugin( /src\/lib\/gramjs\/client\/TelegramClient\.js/, './MockClient.ts' )] : []), - new HtmlWebackPlugin({ + new HtmlWebpackPlugin({ appName: process.env.APP_ENV === 'production' ? 'Telegram Web' : 'Telegram Web Beta', appleIcon: process.env.APP_ENV === 'production' ? 'apple-touch-icon' : './apple-touch-icon-dev', template: 'src/index.html',