Message / Code: Support highlight (#1842)

This commit is contained in:
Alexander Zinchuk 2022-05-16 13:34:06 +02:00
parent 4dfbc58976
commit 69fbf47d7a
30 changed files with 1096 additions and 308 deletions

336
package-lock.json generated
View File

@ -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": {

View File

@ -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",

View File

@ -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 }),
};
}

View File

@ -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:

View File

@ -241,6 +241,7 @@ export interface ApiMessageEntity {
length: number;
userId?: string;
url?: string;
language?: string;
}
export enum ApiMessageEntityTypes {

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-cycle
export { default as MediaViewer } from '../components/mediaViewer/MediaViewer';
export { default as ForwardPicker } from '../components/main/ForwardPicker';
@ -52,11 +53,13 @@ export { default as EmojiTooltip } from '../components/middle/composer/EmojiTool
export { default as InlineBotTooltip } from '../components/middle/composer/InlineBotTooltip';
export { default as SendAsMenu } from '../components/middle/composer/SendAsMenu';
// eslint-disable-next-line import/no-cycle
export { default as RightSearch } from '../components/right/RightSearch';
// eslint-disable-next-line import/no-cycle
export { default as StickerSearch } from '../components/right/StickerSearch';
// eslint-disable-next-line import/no-cycle
export { default as GifSearch } from '../components/right/GifSearch';
// eslint-disable-next-line import/no-cycle
export { default as Statistics } from '../components/right/statistics/Statistics';
export { default as MessageStatistics } from '../components/right/statistics/MessageStatistics';
export { default as PollResults } from '../components/right/PollResults';

View File

@ -0,0 +1,158 @@
.code-block {
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;
}
html.theme-light & {
--color-type: #0053d4;
--color-keyword: #388e22;
--color-class: #3e6c20;
--color-string: #9a1111;
--color-template: #9A5334;
--color-selector: #9A5334;
--color-function: #a753b7;
--color-comment: #616161;
--color-section: #9a1111;
--color-variable: #BD63C5;
--color-attribute: #276b8f;
--color-link: #276b8f;
--color-tag: #000000;
}
html.theme-dark :not(.own) & {
--color-type: #56b6c2;
--color-keyword: #c678dd;
--color-class: #e06c75;
--color-string: #98c379;
--color-template: #d19a66;
--color-selector: #e06c75;
--color-function: #61aeee;
--color-comment: #5c6370;
--color-section: #e06c75;
--color-variable: #d19a66;
--color-attribute: #d19a66;
--color-link: #d19a66;
--color-tag: #e06c75;
}
html.theme-dark .own & {
--color-type: #9EFFFF;
--color-keyword: #ffe900;
--color-class: #b2f5ff;
--color-string: #fedcad;
--color-template: #ffe900;
--color-selector: #b2f5ff;
--color-function: #87ff91;
--color-comment: #cbcbcb;
--color-section: #b2f5ff;
--color-variable: #ffe900;
--color-attribute: #ffe900;
--color-link: #ffe900;
--color-tag: #b2f5ff;
}
.hljs {
display: block;
overflow-x: auto;
color: var(--color-text);
}
}
.hljs-keyword,
.hljs-literal,
.hljs-symbol,
.hljs-name {
color: var(--color-keyword);
}
.hljs-link {
color: var(--color-link);
text-decoration: underline;
}
.hljs-built_in,
.hljs-type {
color: var(--color-type);
}
.hljs-number,
.hljs-class {
color: var(--color-class);
}
.hljs-string,
.hljs-meta .hljs-string {
color: var(--color-string);
}
.hljs-regexp,
.hljs-template-tag {
color: var(--color-template);
}
.hljs-subst,
.hljs-function,
.hljs-title,
.hljs-params,
.hljs-formula {
color: var(--color-function);
}
.hljs-comment,
.hljs-quote {
color: var(--color-comment);
font-style: italic;
}
.hljs-meta,
.hljs-meta .hljs-keyword,
.hljs-tag, .hljs-doctag {
color: var(--color-tag);
}
.hljs-variable,
.hljs-template-variable {
color: var(--color-variable);
}
.hljs-attr,
.hljs-attribute {
color: var(--color-attribute);
}
.hljs-section {
color: var(--color-section);
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-bullet,
.hljs-selector-tag,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: var(--color-selector);
}

View File

@ -0,0 +1,52 @@
import React, {
FC, memo, useCallback, useState,
} from '../../../lib/teact/teact';
import buildClassName from '../../../util/buildClassName';
import useAsync from '../../../hooks/useAsync';
import PreBlock from './PreBlock';
import CodeOverlay from './CodeOverlay';
import './CodeBlock.scss';
export type OwnProps = {
text: string;
language?: string;
noCopy?: boolean;
};
const CodeBlock: FC<OwnProps> = ({ 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 <PreBlock text={text} noCopy={noCopy} />;
}
const blockClass = buildClassName('code-block', !isWordWrap && 'no-word-wrap');
return (
<pre className={blockClass}>
{highlighted}
<CodeOverlay
text={text}
className="code-overlay"
onWordWrapToggle={handleWordWrapToggle}
noCopy={noCopy}
/>
</pre>
);
};
export default memo(CodeBlock);

View File

@ -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;
}
}

View File

@ -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<OwnProps> = ({
text, className, noCopy, onWordWrapToggle,
}) => {
const { showNotification } = getActions();
// eslint-disable-next-line no-null/no-null
const ref = useRef<HTMLDivElement>(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 (
<div className={overlayClass} ref={ref}>
<div className={contentClass}>
{withWordWrapButton && (
<div className={wrapClass} onClick={handleWordWrapClick} title="Word Wrap">
<i className="icon-word-wrap" />
</div>
)}
{!noCopy && (
<div className={styles.copy} onClick={handleCopy} title={lang('Copy')}>
<i className="icon-copy" />
</div>
)}
</div>
</div>
);
};
export default memo(CodeOverlay);

View File

@ -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<OwnProps> = ({ text, noCopy }) => {
const [isWordWrap, setWordWrap] = useState(true);
const handleWordWrapToggle = useCallback((wrap) => {
setWordWrap(wrap);
}, []);
const blockClass = buildClassName('text-entity-pre', !isWordWrap && 'no-word-wrap');
return (
<pre className={blockClass}>
<div className="pre-code custom-scroll-x">{text}</div>
<CodeOverlay
text={text}
className="code-overlay"
onWordWrapToggle={handleWordWrapToggle}
noCopy={noCopy}
/>
</pre>
);
};
export default memo(PreBlock);

View File

@ -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,
);
}

View File

@ -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;
}

View File

@ -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 (
<code className="text-entity-code" onClick={handleCodeClick} role="textbox" tabIndex={0}>
<code
className={buildClassName('text-entity-code', !isProtected && 'clickable')}
onClick={!isProtected ? handleCodeClick : undefined}
role="textbox"
tabIndex={0}
>
{renderNestedMessagePart()}
</code>
);
@ -376,7 +385,7 @@ function processEntity(
</a>
);
case ApiMessageEntityTypes.Pre:
return <pre className="text-entity-pre">{renderNestedMessagePart()}</pre>;
return <CodeBlock text={entityText} language={entity.language} noCopy={isProtected} />;
case ApiMessageEntityTypes.Strike:
return <del>{renderNestedMessagePart()}</del>;
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 `<code class="text-entity-code">${renderedContent}</code>`;
case ApiMessageEntityTypes.Pre:
return `\`\`\`<br/>${renderedContent}<br/>\`\`\``;
return `\`\`\`${entity.language || ''}<br/>${renderedContent}<br/>\`\`\`<br/>`;
case ApiMessageEntityTypes.Strike:
return `<del>${renderedContent}</del>`;
case ApiMessageEntityTypes.MentionName:

View File

@ -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') {

View File

@ -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;

View File

@ -488,7 +488,9 @@ const Message: FC<OwnProps & StateProps> = ({
});
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) {

View File

@ -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;
}

View File

@ -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';

View File

@ -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) {

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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";
}

View File

@ -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);
}
}
}

View File

@ -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"],

108
src/util/highlightCode.ts Normal file
View File

@ -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<string, Promise<void>>();
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);
}

View File

@ -75,7 +75,8 @@ function parseMarkdown(html: string) {
parsedHtml = parsedHtml.replace(/<\/div>/g, '');
// Pre
parsedHtml = parsedHtml.replace(/^`{3}(.*[\n\r][^]*?^)`{3}/gm, '<pre>$1</pre>');
parsedHtml = parsedHtml.replace(/^`{3}(.*?)[\n\r](.*?[\n\r].*?^)`{3}/gms, '<pre data-language="$1">$2</pre>');
parsedHtml = parsedHtml.replace(/^`{3}[\n\r]?(.*?)[\n\r]?`{3}/gms, '<pre>$1</pre>');
parsedHtml = parsedHtml.replace(/[`]{3}([^`]+)[`]{3}/g, '<pre>$1</pre>');
// 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 }),
},
};
}

View File

@ -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',