diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 3ca572cc41..88797bb4d1 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -307,6 +307,15 @@ importers: '@rush-temp/elastic': specifier: file:./projects/elastic.tgz version: file:projects/elastic.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2) + '@rush-temp/emoji': + specifier: file:./projects/emoji.tgz + version: file:projects/emoji.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) + '@rush-temp/emoji-assets': + specifier: file:./projects/emoji-assets.tgz + version: file:projects/emoji-assets.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) + '@rush-temp/emoji-resources': + specifier: file:./projects/emoji-resources.tgz + version: file:projects/emoji-resources.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) '@rush-temp/export': specifier: file:./projects/export.tgz version: file:projects/export.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(svelte@4.2.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) @@ -511,6 +520,9 @@ importers: '@rush-temp/model-drive': specifier: file:./projects/model-drive.tgz version: file:projects/model-drive.tgz + '@rush-temp/model-emoji': + specifier: file:./projects/model-emoji.tgz + version: file:projects/model-emoji.tgz '@rush-temp/model-export': specifier: file:./projects/model-export.tgz version: file:projects/model-export.tgz @@ -1756,6 +1768,9 @@ importers: emojibase: specifier: ^16.0.0 version: 16.0.0 + emojibase-regex: + specifier: ^16.0.0 + version: 16.0.0 esbuild: specifier: ^0.24.2 version: 0.24.2 @@ -3948,7 +3963,7 @@ packages: version: 0.0.0 '@rush-temp/activity-resources@file:projects/activity-resources.tgz': - resolution: {integrity: sha512-wgkR5K1ITM6du7LR5d/VuWTkwZprdqfkoFs39EtDC86rMhZoJywV3pwrPI7ASlz7cMb1VtzLR/6SKtENmB3FWg==, tarball: file:projects/activity-resources.tgz} + resolution: {integrity: sha512-D/V1RS70B/E7aBNzD2V+DJm7Ncr9DFrOAVXQVF+zU45QoUP98x16mkOfOvYMm72sfkPMZXt/dazzyMfDuidfIQ==, tarball: file:projects/activity-resources.tgz} version: 0.0.0 '@rush-temp/activity@file:projects/activity.tgz': @@ -4164,7 +4179,7 @@ packages: version: 0.0.0 '@rush-temp/desktop@file:projects/desktop.tgz': - resolution: {integrity: sha512-2MuxT1ciufuMIFtWSoBJlO0uNhPmICCXRVnZZOQji4ByNWBvm4RSkPd97BQic8PxYe4H4FlPmIylRsvvVaeU7w==, tarball: file:projects/desktop.tgz} + resolution: {integrity: sha512-mj3Or3RObSw1iM1XVjuK+ecPRc3H/sxwKl5Lu92qg794LRA/tBt+GZaSFomZ27Oi9sU/OUoUbvtdXlgf2b40IQ==, tarball: file:projects/desktop.tgz} version: 0.0.0 '@rush-temp/devmodel-resources@file:projects/devmodel-resources.tgz': @@ -4215,6 +4230,18 @@ packages: resolution: {integrity: sha512-WGiDJdr4on2TaKPfaKntlenxzUJ5uuxtibsd/ZkppUh0BghpFSH37C8WGps7kQRS4oqpb+mB3QhxVhxIz2IvGg==, tarball: file:projects/elastic.tgz} version: 0.0.0 + '@rush-temp/emoji-assets@file:projects/emoji-assets.tgz': + resolution: {integrity: sha512-HQ14d4cwZ0klYdy/wRepltE3uviJX4SNX8fXkyUXonnnQZ226JG0ZQZQjxeTmCBeX9U/fm/+3xT98atXkpGofw==, tarball: file:projects/emoji-assets.tgz} + version: 0.0.0 + + '@rush-temp/emoji-resources@file:projects/emoji-resources.tgz': + resolution: {integrity: sha512-ibOvCN3xgD/JTN5p4ILKI6lDe97Ut9fWdNxRlrl54Xi/lYqSzcR1qA3/9sRANc07QatKeCqiFeZSZUgPAGTv3A==, tarball: file:projects/emoji-resources.tgz} + version: 0.0.0 + + '@rush-temp/emoji@file:projects/emoji.tgz': + resolution: {integrity: sha512-Obbi4ccdBssdxagUm1zLtCGQhj5R0xOFNUqgOoRQDlcEqVvojA6EyQM6G+YtNLBllF+lnHGCqIs96GQ8G+AR4g==, tarball: file:projects/emoji.tgz} + version: 0.0.0 + '@rush-temp/export-assets@file:projects/export-assets.tgz': resolution: {integrity: sha512-PluN0E95WLNoRzQR5AMiB+yM3fIEOqd/pd7Z/iSm/MYtNwlaXvj1W0Y9jDDTGeRDBhG8JlsJx4QQAXrL/wvMrw==, tarball: file:projects/export-assets.tgz} version: 0.0.0 @@ -4420,7 +4447,7 @@ packages: version: 0.0.0 '@rush-temp/model-all@file:projects/model-all.tgz': - resolution: {integrity: sha512-apw/K0//cfSDJtJQ6h8Y9RmfYgpBq67AAr1hxPrSg8kYLMPzhro1u/0Iuj+jJGnbv874rVprV8VWZznoDD5m8A==, tarball: file:projects/model-all.tgz} + resolution: {integrity: sha512-5ggvhrxV+Dhwf4cqX/CLjE+NcDstaIVFrZ0aO9lUcfpX4txUgCCQK0ioAqZ8rL0XHjQ//gq78Fny76aWyuJnLw==, tarball: file:projects/model-all.tgz} version: 0.0.0 '@rush-temp/model-analytics-collector@file:projects/model-analytics-collector.tgz': @@ -4483,6 +4510,10 @@ packages: resolution: {integrity: sha512-JG62skvnh8n7bBwsriHH6TE71W/gVm287OVnmy4VRJVf/tDC0/sCKlg+75qvcSu3CmAjaYqnnJz5q1Lz9EH3xw==, tarball: file:projects/model-drive.tgz} version: 0.0.0 + '@rush-temp/model-emoji@file:projects/model-emoji.tgz': + resolution: {integrity: sha512-o3HzefmauAMUKALu3SpLNEsdNdD/g3zWNT4NvnwXq0UU+l6OVJPOaj40cRZYgbW0J+PYVIA5kLgLmpaTj/3XLw==, tarball: file:projects/model-emoji.tgz} + version: 0.0.0 + '@rush-temp/model-export@file:projects/model-export.tgz': resolution: {integrity: sha512-BvWGwqaQW9aByGimHCl9tUQwzJcflcvXwb7oKLJlERSDthQIbbmiwCbk2hoxoV3JnZvFAP+EIKqU8Gx4JmbBHQ==, tarball: file:projects/model-export.tgz} version: 0.0.0 @@ -4928,7 +4959,7 @@ packages: version: 0.0.0 '@rush-temp/presentation@file:projects/presentation.tgz': - resolution: {integrity: sha512-xbOaATMslHsHV4sUFB+PKbMMWrattdYQi9aAXxuYylBCFMOeNbOD4din6fgYn/MuyerpUXuN/EH4gDoeFv8Udg==, tarball: file:projects/presentation.tgz} + resolution: {integrity: sha512-BIZsM8bDI2fpnw7r71lbLTUZNXk76T2X5Ni9yzy+reE/5/m4+JvCmayE9lOQwIESaKijZSvTJ72rNDYs2+wsoQ==, tarball: file:projects/presentation.tgz} version: 0.0.0 '@rush-temp/print-assets@file:projects/print-assets.tgz': @@ -4956,7 +4987,7 @@ packages: version: 0.0.0 '@rush-temp/prod@file:projects/prod.tgz': - resolution: {integrity: sha512-sH+/06L2vJs2pfZD+n0Lir0YqObICbAlwOUjLMmJGWoQNGn5SrK85M9asNA5w4FbzKSHMLoSarNHebM7Th8ipQ==, tarball: file:projects/prod.tgz} + resolution: {integrity: sha512-GdZBIxyfe90BSJp2zRY77jkeyisNUL2i918qAo93lYsVrw1GFnMwuNxgK/Vw3a2fsLCVw9QIcUq6OLjfJTRDpA==, tarball: file:projects/prod.tgz} version: 0.0.0 '@rush-temp/products-assets@file:projects/products-assets.tgz': @@ -5412,7 +5443,7 @@ packages: version: 0.0.0 '@rush-temp/task-resources@file:projects/task-resources.tgz': - resolution: {integrity: sha512-z+jiv045QZjtK9XdepXuvG3dgv5kZ0N1VVjLUfyzS+23PNChRJ5djF6WQjqvMVVUwtLd5xQmM97KG8U5GlbIVg==, tarball: file:projects/task-resources.tgz} + resolution: {integrity: sha512-hNQPRCjOf5j5O5j7RcOsWBWxGZE5tkrLZlq1zZRhUqK8Ms05g9yv0jZkiN7Ech1+PxGUCvqTJJ4ju6oB3zEghg==, tarball: file:projects/task-resources.tgz} version: 0.0.0 '@rush-temp/task@file:projects/task.tgz': @@ -5472,7 +5503,7 @@ packages: version: 0.0.0 '@rush-temp/text-editor-resources@file:projects/text-editor-resources.tgz': - resolution: {integrity: sha512-MvBheskzm3x8NJCOJv12lYQV2OMPO57KcYvLMTjZ7uVmJyK7iZsBBY7Tuv9kmwVuOT++P+KOTYj1GVd/iwIOjg==, tarball: file:projects/text-editor-resources.tgz} + resolution: {integrity: sha512-graqZ0uS/Iwo3ioLjgR27wpTl0NVQ/IieRuGZFkAuMukFjBrgO3AUZJZETB0cm3Ma/KZ/HotJ0I1mOQBRHeCOw==, tarball: file:projects/text-editor-resources.tgz} version: 0.0.0 '@rush-temp/text-editor@file:projects/text-editor.tgz': @@ -5540,11 +5571,11 @@ packages: version: 0.0.0 '@rush-temp/ui-next@file:projects/ui-next.tgz': - resolution: {integrity: sha512-5ZTDsqNUfXyCIVeeSMGhnxDSj+71eZSnHSSPz4l0QoH2FKtVa7fj01mXV7CXSMcIAz42S38gaXJfH1NB2TOcEw==, tarball: file:projects/ui-next.tgz} + resolution: {integrity: sha512-A3FSIrGhW7OIseHR5XIXglebYVOGmgq40yuGx68bV2XhCmN5KLiTA72MAJYxXmYG2uRS1L7GydCRTmdML+WlNA==, tarball: file:projects/ui-next.tgz} version: 0.0.0 '@rush-temp/ui@file:projects/ui.tgz': - resolution: {integrity: sha512-0U0x2FrKQNlJteRKEqKkDhZNC/qOWO/rL3qK8cuqdYR7Gz5kCaQBnRRHYWGDTVtUKcyYrUN+2RTaB/OG5pgYhA==, tarball: file:projects/ui.tgz} + resolution: {integrity: sha512-1Qa4BqkJrIa/OAlhIk8KQQpejRJa65FHm9MEh0U5o1qy1TnScBVPY0VKtM5hYyBA2L6D7AypJ+K+4nvNTyIB2A==, tarball: file:projects/ui.tgz} version: 0.0.0 '@rush-temp/uploader-assets@file:projects/uploader-assets.tgz': @@ -5564,7 +5595,7 @@ packages: version: 0.0.0 '@rush-temp/view-resources@file:projects/view-resources.tgz': - resolution: {integrity: sha512-7AFAEQlszTmRCzi9xfKegIhjzl76W8lLW4/LLygoH7a8JZPqoJhP1/l99QhNwa0tpUsXFTMg1EYMucFMrXhMdQ==, tarball: file:projects/view-resources.tgz} + resolution: {integrity: sha512-vOueglFyJvW7+13mDOKqFx3TQsMVq+vcIIxAoWQdvkcid8vypVg16TM5WPm1VbL3mF/XZ4Aa0q3DCmQKS+lmYQ==, tarball: file:projects/view-resources.tgz} version: 0.0.0 '@rush-temp/view@file:projects/view.tgz': @@ -8496,6 +8527,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojibase-regex@16.0.0: + resolution: {integrity: sha512-ZMp31BkzBWNW+T73of6NURL6nXQa5GkfKneOkr3cEwBDVllbW/2nuva7NO0J3RjaQ07+SZQNgPTGZ4JlIhmM2Q==} + emojibase@16.0.0: resolution: {integrity: sha512-Nw2m7JLIO4Ou2X/yZPRNscHQXVbbr6SErjkJ7EooG7MbR3yDZszCv9KTizsXFc7yZl0n3WF+qUKIC/Lw6H9xaQ==} engines: {node: '>=18.12.0'} @@ -18599,6 +18633,98 @@ snapshots: - node-notifier - supports-color + '@rush-temp/emoji-assets@file:projects/emoji-assets.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))': + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.7.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.7.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.7.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) + prettier: 3.2.5 + ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + + '@rush-temp/emoji-resources@file:projects/emoji-resources.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))': + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.7.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.7.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.7.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.19) + sass: 1.71.1 + svelte: 4.2.19 + svelte-check: 3.6.9(@babel/core@7.23.9)(postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(postcss@8.5.3)(sass@1.71.1)(svelte@4.2.19) + svelte-eslint-parser: 0.33.1(svelte@4.2.19) + svelte-loader: 3.2.0(svelte@4.2.19) + svelte-preprocess: 5.1.3(@babel/core@7.23.9)(postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(postcss@8.5.3)(sass@1.71.1)(svelte@4.2.19)(typescript@5.7.3) + ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - coffeescript + - esbuild + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + - ts-node + + '@rush-temp/emoji@file:projects/emoji.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))': + dependencies: + '@types/jest': 29.5.12 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.7.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.7.3) + emojibase: 16.0.0 + emojibase-regex: 16.0.0 + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.7.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)) + prettier: 3.2.5 + ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - esbuild + - node-notifier + - supports-color + - ts-node + '@rush-temp/export-assets@file:projects/export-assets.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(svelte@4.2.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))': dependencies: '@types/jest': 29.5.12 @@ -20381,6 +20507,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@rush-temp/model-emoji@file:projects/model-emoji.tgz': + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.7.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.7.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.7.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + prettier: 3.2.5 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + '@rush-temp/model-export@file:projects/model-export.tgz': dependencies: '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.7.3) @@ -27461,6 +27601,7 @@ snapshots: date-fns-tz: 2.0.0(date-fns@2.30.0) dompurify: 3.1.6 emojibase: 16.0.0 + emojibase-regex: 16.0.0 eslint: 8.56.0 eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.3.3) eslint-plugin-import: 2.29.1(eslint@8.56.0) @@ -31438,6 +31579,8 @@ snapshots: emoji-regex@9.2.2: {} + emojibase-regex@16.0.0: {} + emojibase@16.0.0: {} emojis-list@3.0.0: {} diff --git a/desktop/package.json b/desktop/package.json index da35b6cb9b..f1334e2c2b 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -243,6 +243,9 @@ "@hcengineering/communication": "^0.6.0", "@hcengineering/communication-assets": "^0.6.0", "@hcengineering/communication-resources": "^0.6.0", + "@hcengineering/emoji": "^0.6.0", + "@hcengineering/emoji-assets": "^0.6.0", + "@hcengineering/emoji-resources": "^0.6.0", "electron-squirrel-startup": "~1.0.0", "dotenv": "~16.0.0", "electron-context-menu": "^4.0.4", diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index 9dd18973a4..db942e330f 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -67,6 +67,7 @@ import { chatId } from '@hcengineering/chat' import { inboxId } from '@hcengineering/inbox' import { achievementId } from '@hcengineering/achievement' import {communicationId} from '@hcengineering/communication' +import { emojiId } from '@hcengineering/emoji' import '@hcengineering/activity-assets' import '@hcengineering/analytics-collector-assets' @@ -117,6 +118,7 @@ import '@hcengineering/mail-assets' import '@hcengineering/chat-assets' import '@hcengineering/inbox-assets' import '@hcengineering/achievement-assets' +import '@hcengineering/emoji-assets' import '@hcengineering/media-assets' import '@hcengineering/communication-assets' @@ -233,6 +235,7 @@ function configureI18n (): void { addStringsLoader(processId, async (lang: string) => await import(`@hcengineering/process-assets/lang/${lang}.json`)) addStringsLoader(achievementId, async (lang: string) => await import(`@hcengineering/achievement-assets/lang/${lang}.json`)) addStringsLoader(communicationId, async (lang: string) => await import(`@hcengineering/communication-assets/lang/${lang}.json`)) + addStringsLoader(emojiId, async (lang: string) => await import(`@hcengineering/emoji-assets/lang/${lang}.json`)) } export async function configurePlatform (): Promise { @@ -374,6 +377,7 @@ export async function configurePlatform (): Promise { addLocation(processId, () => import(/* webpackChunkName: "process" */ '@hcengineering/process-resources')) addLocation(achievementId, () => import(/* webpackChunkName: "achievement" */ '@hcengineering/achievement-resources')) addLocation(communicationId, () => import(/* webpackChunkName: "communication" */ '@hcengineering/communication-resources')) + addLocation(emojiId, () => import(/* webpackChunkName: "achievement" */ '@hcengineering/emoji-resources')) setMetadata(client.metadata.FilterModel, 'ui') setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin]) diff --git a/dev/prod/package.json b/dev/prod/package.json index faacf40cd3..d399c96bc0 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -274,6 +274,9 @@ "@hcengineering/communication": "^0.6.0", "@hcengineering/communication-assets": "^0.6.0", "@hcengineering/communication-resources": "^0.6.0", + "@hcengineering/emoji": "^0.6.0", + "@hcengineering/emoji-assets": "^0.6.0", + "@hcengineering/emoji-resources": "^0.6.0", "@sentry/svelte": "^8.52.1", "posthog-js": "~1.122.0", "readable-stream": "^4.7.0", diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index f7ef3ef6bd..e25c60d038 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -76,6 +76,7 @@ import { bitrixId } from '@hcengineering/bitrix' import {inboxId} from '@hcengineering/inbox' import {achievementId} from '@hcengineering/achievement' import {communicationId} from '@hcengineering/communication' +import {emojiId} from '@hcengineering/emoji' import '@hcengineering/activity-assets' import '@hcengineering/analytics-collector-assets' @@ -130,6 +131,7 @@ import '@hcengineering/mail-assets' import '@hcengineering/github-assets' import '@hcengineering/achievement-assets' import '@hcengineering/communication-assets' +import '@hcengineering/emoji-assets' import { coreId } from '@hcengineering/core' import presentation, { @@ -365,6 +367,7 @@ function configureI18n(): void { addStringsLoader(inboxId, async (lang: string) => await import(`@hcengineering/inbox-assets/lang/${lang}.json`)) addStringsLoader(achievementId, async (lang: string) => await import(`@hcengineering/achievement-assets/lang/${lang}.json`)) addStringsLoader(communicationId, async (lang: string) => await import(`@hcengineering/communication-assets/lang/${lang}.json`)) + addStringsLoader(emojiId, async (lang: string) => await import(`@hcengineering/emoji-assets/lang/${lang}.json`)) } export async function configurePlatform() { @@ -554,7 +557,7 @@ export async function configurePlatform() { addLocation(uploaderId, () => import(/* webpackChunkName: "uploader" */ '@hcengineering/uploader-resources')) addLocation(recorderId, () => import(/* webpackChunkName: "recorder" */ '@hcengineering/recorder-resources')) addLocation(mediaId, () => import(/* webpackChunkName: "media" */ '@hcengineering/media-resources')) - + addLocation( testManagementId, () => import(/* webpackChunkName: "test-management" */ '@hcengineering/test-management-resources') @@ -568,6 +571,7 @@ export async function configurePlatform() { addLocation(inboxId, () => import(/* webpackChunkName: "inbox" */ '@hcengineering/inbox-resources')) addLocation(achievementId, () => import(/* webpackChunkName: "achievement" */ '@hcengineering/achievement-resources')) addLocation(communicationId, () => import(/* webpackChunkName: "communication" */ '@hcengineering/communication-resources')) + addLocation(emojiId, () => import(/* webpackChunkName: "achievement" */ '@hcengineering/emoji-resources')) setMetadata(client.metadata.FilterModel, 'ui') setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin]) diff --git a/models/all/package.json b/models/all/package.json index 9969abf1bb..15b83cfb3d 100644 --- a/models/all/package.json +++ b/models/all/package.json @@ -124,6 +124,7 @@ "@hcengineering/model-server-process": "^0.6.0", "@hcengineering/model-inbox": "^0.6.0", "@hcengineering/model-achievement": "^0.6.0", - "@hcengineering/model-communication": "^0.6.0" + "@hcengineering/model-communication": "^0.6.0", + "@hcengineering/model-emoji": "^0.6.0" } } diff --git a/models/all/src/index.ts b/models/all/src/index.ts index 3be592815c..bf8d68f8cd 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -117,6 +117,7 @@ import { chatId, createModel as chatModel } from '@hcengineering/model-chat' import processes, { processId, createModel as processModel } from '@hcengineering/model-process' import { createModel as inboxModel, inboxId } from '@hcengineering/model-inbox' import { achievementId, createModel as achievementModel } from '@hcengineering/model-achievement' +import { emojiId, createModel as emojiModel } from '@hcengineering/model-emoji' import { communicationId, createModel as communicationModel } from '@hcengineering/model-communication' import { type Plugin } from '@hcengineering/platform' @@ -458,6 +459,7 @@ export default function buildModel (): Builder { [chatModel, chatId], [inboxModel, inboxId], [achievementModel, achievementId], + [emojiModel, emojiId], [communicationModel, communicationId], [mailModel, mailId], diff --git a/models/emoji/.eslintrc.js b/models/emoji/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/emoji/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/emoji/.npmignore b/models/emoji/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/emoji/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/emoji/config/rig.json b/models/emoji/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/emoji/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/emoji/package.json b/models/emoji/package.json new file mode 100644 index 0000000000..c40806e962 --- /dev/null +++ b/models/emoji/package.json @@ -0,0 +1,44 @@ +{ + "name": "@hcengineering/model-emoji", + "version": "0.6.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Hardcore Engineering Inc.", + "template": "@hcengineering/model-package", + "license": "EPL-2.0", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/core": "^0.6.32", + "@hcengineering/model": "^0.6.11", + "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-presentation": "^0.6.0", + "@hcengineering/platform": "^0.6.11", + "@hcengineering/ui": "^0.6.15", + "@hcengineering/emoji": "^0.6.0", + "@hcengineering/model-workbench": "^0.6.1", + "@hcengineering/setting": "^0.6.17", + "@hcengineering/view": "^0.6.13", + "@hcengineering/model-print": "^0.6.0", + "@hcengineering/model-tracker": "^0.6.0" + } +} diff --git a/models/emoji/src/index.ts b/models/emoji/src/index.ts new file mode 100644 index 0000000000..d6c3dec351 --- /dev/null +++ b/models/emoji/src/index.ts @@ -0,0 +1,59 @@ +// +// Copyright © 2024-2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { type Builder } from '@hcengineering/model' +import core from '@hcengineering/model-core' + +import presentation from '@hcengineering/model-presentation' +import workbench from '@hcengineering/model-workbench' +import setting from '@hcengineering/setting' +import view from '@hcengineering/view' +import print from '@hcengineering/model-print' +import tracker from '@hcengineering/model-tracker' + +import { DOMAIN_EMOJI, TCustomEmoji } from './models' +import emojiPlugin from './plugin' +import { AccountRole } from '@hcengineering/core' + +export { emojiId } from '@hcengineering/emoji' +export { emojiPlugin as default } + +export function createModel (builder: Builder): void { + builder.createModel(TCustomEmoji) + + builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, { + domain: DOMAIN_EMOJI, + disabled: [{ space: 1 }, { modifiedOn: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { createdOn: -1 }] + }) + + builder.createDoc(setting.class.WorkspaceSettingCategory, core.space.Model, { + name: 'emojis', + label: emojiPlugin.string.CustomEmojis, + icon: emojiPlugin.icon.Emoji, + component: emojiPlugin.component.SettingsEmojiTable, + group: 'settings-editor', + role: AccountRole.User, + order: 5000 + }) + + builder.mixin(emojiPlugin.class.CustomEmoji, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Open, view.action.OpenInNewTab, print.action.Print, tracker.action.NewRelatedIssue] + }) + + builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, { + extension: workbench.extensions.WorkbenchExtensions, + component: emojiPlugin.component.WorkbenchExtension + }) +} diff --git a/models/emoji/src/models.ts b/models/emoji/src/models.ts new file mode 100644 index 0000000000..9993f2ec66 --- /dev/null +++ b/models/emoji/src/models.ts @@ -0,0 +1,32 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import emojiPlugin from './plugin' +import { Model, Prop, ReadOnly, TypeRef, TypeString } from '@hcengineering/model' +import core, { TDoc } from '@hcengineering/model-core' +import type { Ref, Blob, Domain } from '@hcengineering/core' +import { type CustomEmoji } from '@hcengineering/emoji' + +export const DOMAIN_EMOJI = 'emoji' as Domain + +@Model(emojiPlugin.class.CustomEmoji, core.class.Doc, DOMAIN_EMOJI) +export class TCustomEmoji extends TDoc implements CustomEmoji { + @Prop(TypeString(), emojiPlugin.string.Shortcode) + @ReadOnly() + shortcode!: string + + @Prop(TypeRef(core.class.Blob), emojiPlugin.string.Image) + @ReadOnly() + image!: Ref +} diff --git a/models/emoji/src/plugin.ts b/models/emoji/src/plugin.ts new file mode 100644 index 0000000000..6e6dbdb081 --- /dev/null +++ b/models/emoji/src/plugin.ts @@ -0,0 +1,9 @@ +import { mergeIds } from '@hcengineering/platform' +import { type AnyComponent } from '@hcengineering/ui/src/types' +import emojiPlugin, { emojiId } from '@hcengineering/emoji' + +export default mergeIds(emojiId, emojiPlugin, { + component: { + WorkbenchExtension: '' as AnyComponent + } +}) diff --git a/models/emoji/tsconfig.json b/models/emoji/tsconfig.json new file mode 100644 index 0000000000..9765b6086d --- /dev/null +++ b/models/emoji/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/packages/ui/src/components/IconWithEmoji.svelte b/packages/presentation/src/components/IconWithEmoji.svelte similarity index 74% rename from packages/ui/src/components/IconWithEmoji.svelte rename to packages/presentation/src/components/IconWithEmoji.svelte index 5043388a4b..307563d9e4 100644 --- a/packages/ui/src/components/IconWithEmoji.svelte +++ b/packages/presentation/src/components/IconWithEmoji.svelte @@ -13,20 +13,29 @@ // limitations under the License. -->
- {value} + {#if typeof icon !== 'string'} + {value} + {:else} + {#await getBlobRef(icon) then iconBlob} + icon + {/await} + {/if}
diff --git a/plugins/emoji-resources/src/components/settings/CustomEmojiPresenter.svelte b/plugins/emoji-resources/src/components/settings/CustomEmojiPresenter.svelte new file mode 100644 index 0000000000..5ffd31cd45 --- /dev/null +++ b/plugins/emoji-resources/src/components/settings/CustomEmojiPresenter.svelte @@ -0,0 +1,20 @@ + + +{#if value !== undefined} + {#await getBlobRef(value) then image} + test + {/await} +{/if} + + diff --git a/plugins/emoji-resources/src/components/settings/SettingsEmojiTable.svelte b/plugins/emoji-resources/src/components/settings/SettingsEmojiTable.svelte new file mode 100644 index 0000000000..b68dc36cf6 --- /dev/null +++ b/plugins/emoji-resources/src/components/settings/SettingsEmojiTable.svelte @@ -0,0 +1,56 @@ + + + +
+
+ + + + +
+
+ +
+
diff --git a/plugins/emoji-resources/src/index.ts b/plugins/emoji-resources/src/index.ts new file mode 100644 index 0000000000..f7a6adbcd3 --- /dev/null +++ b/plugins/emoji-resources/src/index.ts @@ -0,0 +1,20 @@ +import type { Resources } from '@hcengineering/platform' +import EmojiPopup from './components/EmojiPopup.svelte' +import SettingsEmojiTable from './components/settings/SettingsEmojiTable.svelte' +import WorkbenchExtension from './components/WorkbenchExtension.svelte' +import { getCustomEmoji, getEmojiByEmoticon, getEmojiByShortCode } from './utils' + +export * from './utils' + +export default async (): Promise => ({ + component: { + EmojiPopup, + SettingsEmojiTable, + WorkbenchExtension + }, + functions: { + GetEmojiByEmoticon: getEmojiByEmoticon, + GetEmojiByShortCode: getEmojiByShortCode, + GetCustomEmoji: getCustomEmoji + } +}) diff --git a/plugins/emoji-resources/src/store.ts b/plugins/emoji-resources/src/store.ts new file mode 100644 index 0000000000..26e2e7d447 --- /dev/null +++ b/plugins/emoji-resources/src/store.ts @@ -0,0 +1,108 @@ +import { writable, derived, get } from 'svelte/store' +import { isCustomEmoji, type EmojiWithGroup } from '@hcengineering/emoji' +import { getCurrentAccount } from '@hcengineering/core' + +export const unicodeEmojiStore = writable([]) +export const customEmojiStore = writable([]) +export const searchEmoji = writable('') + +export const resultEmojis = derived([unicodeEmojiStore, customEmojiStore, searchEmoji], ([unicode, custom, search]) => { + return search !== '' + ? unicode.concat(custom).filter((emoji) => { + if (isCustomEmoji(emoji)) { + return emoji.shortcode.toLowerCase().includes(search.toLowerCase()) + } + return ( + (emoji.tags?.some((tag: string) => tag.toLowerCase().startsWith(search.toLowerCase())) ?? false) || + emoji.label.toLowerCase().includes(search.toLowerCase()) + ) + }) + : unicode.concat(custom) +}) + +export const setSkinTone = (skinTone: number): void => { + const skinToneKey = getEmojisLocalStorageKey('skinTone') + window.localStorage.setItem(skinToneKey, JSON.stringify(skinTone)) +} +export const getSkinTone = (): number => { + const skinToneKey = getEmojisLocalStorageKey('skinTone') + const skinTone = window.localStorage.getItem(skinToneKey) + if (skinTone == null) return 0 + + try { + return JSON.parse(skinTone) + } catch (e) { + console.error('Failed to parse emojis', e) + return 0 + } +} + +export const removeFrequentlyEmojis = (emoji: EmojiWithGroup): void => { + const hexcode = isCustomEmoji(emoji) ? emoji.shortcode : emoji.hexcode + if (hexcode === undefined) return + + const frequentlyEmojis = getStoredFrequentlyEmojis() + if (frequentlyEmojis === undefined) return + + window.localStorage.setItem( + getEmojisLocalStorageKey(), + JSON.stringify(frequentlyEmojis.filter((pe) => pe.hexcode !== hexcode)) + ) +} +export const addFrequentlyEmojis = (emoji: EmojiWithGroup): void => { + if (emoji === undefined) return + const hexcode = isCustomEmoji(emoji) ? emoji.shortcode : emoji.hexcode + + const frequentlyEmojis = getStoredFrequentlyEmojis() + if (frequentlyEmojis === undefined) { + window.localStorage.setItem(getEmojisLocalStorageKey(), JSON.stringify([{ hexcode, count: 1 }])) + return + } + const index = frequentlyEmojis.findIndex((pe) => pe.hexcode === hexcode) + if (index === -1) frequentlyEmojis.push({ hexcode, count: 1 }) + else frequentlyEmojis[index].count++ + frequentlyEmojis.sort((a, b) => b.count - a.count) + window.localStorage.setItem(getEmojisLocalStorageKey(), JSON.stringify(frequentlyEmojis)) +} +export const getFrequentlyEmojis = (): EmojiWithGroup[] | undefined => { + const frequentlyEmojis = getStoredFrequentlyEmojis() + if (frequentlyEmojis === undefined) return undefined + + const emojis = get(unicodeEmojiStore).concat(get(customEmojiStore)) + const result: EmojiWithGroup[] = [] + emojis.forEach((emoji: EmojiWithGroup) => { + if (isCustomEmoji(emoji)) { + if (frequentlyEmojis.find((pe) => pe.hexcode === emoji.shortcode) !== undefined) result.push(emoji) + } else { + frequentlyEmojis.forEach((parsedEmoji: any) => { + if (parsedEmoji.hexcode === emoji.hexcode) { + result.push(emoji) + return + } + const skinEmoji = emoji.skins?.find((s) => s.hexcode === parsedEmoji.hexcode) + if (skinEmoji === undefined) return + result.push({ ...skinEmoji, key: '' }) + }) + } + }) + return result +} + +function getStoredFrequentlyEmojis (): Array<{ hexcode: string, count: number }> | undefined { + const frequentlyEmojisKey = getEmojisLocalStorageKey() + const frequentlyEmojis = window.localStorage.getItem(frequentlyEmojisKey) + if (frequentlyEmojis === null) return undefined + try { + const parsedEmojis: Array<{ hexcode: string, count: number }> = JSON.parse(frequentlyEmojis) + if (!Array.isArray(parsedEmojis)) return undefined + return parsedEmojis + } catch (e) { + console.error('Failed to parse emojis', e) + return undefined + } +} + +function getEmojisLocalStorageKey (suffix: string = 'frequently'): string { + const me = getCurrentAccount() + return `emojis1.${suffix}.${me.uuid}` +} diff --git a/plugins/emoji-resources/src/types.ts b/plugins/emoji-resources/src/types.ts new file mode 100644 index 0000000000..debca568b0 --- /dev/null +++ b/plugins/emoji-resources/src/types.ts @@ -0,0 +1,88 @@ +import type { Asset, IntlString } from '@hcengineering/platform' +import { type EmojiWithGroup } from '@hcengineering/emoji' +import emojiPlugin from '@hcengineering/emoji' + +export interface EmojiCategory { + id: string + label: IntlString + icon: Asset + categories?: string[] | string + emojisString?: string[] + emojis?: EmojiWithGroup[] +} + +export const emojiCategories: EmojiCategory[] = [ + { id: 'frequently-used', label: emojiPlugin.string.FrequentlyUsed, icon: emojiPlugin.icon.FrequentlyUsed }, + { + id: 'getting-work-done', + label: emojiPlugin.string.GettingWorkDone, + icon: emojiPlugin.icon.GettingWorkDone, + emojisString: [ + '2705', + '1F440', + '1F64C', + '1F64F', + '2795', + '2796', + '1F44F', + '1F4A1', + '1F3AF', + '1F44B', + '1F44D', + '1F389', + '0031-FE0F-20E3', + '0032-FE0F-20E3', + '0033-FE0F-20E3', + '1F4E3', + '26AA', + '1F535', + '1F534', + '1F3CE' + ] + }, + { id: 'custom', label: emojiPlugin.string.CustomEmojis, icon: emojiPlugin.icon.Custom, categories: 'custom' }, + { + id: 'smileys-people', + label: emojiPlugin.string.SmileysAndPeople, + icon: emojiPlugin.icon.SmileysAndPeople, + categories: ['smileys-emotion', 'people-body'] + }, + { + id: 'animals-nature', + label: emojiPlugin.string.AnimalsAndNature, + icon: emojiPlugin.icon.AnimalsAndNature, + categories: 'animals-nature' + }, + { + id: 'food-drink', + label: emojiPlugin.string.FoodAndDrink, + icon: emojiPlugin.icon.FoodAndDrink, + categories: 'food-drink' + }, + { + id: 'travel-places', + label: emojiPlugin.string.TravelAndPlaces, + icon: emojiPlugin.icon.TravelAndPlaces, + categories: 'travel-places' + }, + { + id: 'activities', + label: emojiPlugin.string.Activities, + icon: emojiPlugin.icon.Activities, + categories: 'activities' + }, + { id: 'objects', label: emojiPlugin.string.Objects, icon: emojiPlugin.icon.Objects, categories: 'objects' }, + { id: 'symbols', label: emojiPlugin.string.Symbols, icon: emojiPlugin.icon.Symbols, categories: 'symbols' }, + { id: 'flags', label: emojiPlugin.string.Flags, icon: emojiPlugin.icon.Flags, categories: 'flags' } +] + +export const skinTones: Map = new Map( + [ + emojiPlugin.string.NoTone, + emojiPlugin.string.Light, + emojiPlugin.string.MediumLight, + emojiPlugin.string.Medium, + emojiPlugin.string.MediumDark, + emojiPlugin.string.Dark + ].map((label, index) => [index, label]) +) diff --git a/plugins/emoji-resources/src/utils.ts b/plugins/emoji-resources/src/utils.ts new file mode 100644 index 0000000000..4fb2ec679b --- /dev/null +++ b/plugins/emoji-resources/src/utils.ts @@ -0,0 +1,112 @@ +import { isCustomEmoji, fetchEmojis, fetchMessages } from '@hcengineering/emoji' +import type { EmojiWithGroup, ExtendedEmoji, Locale, Emoji, CustomEmoji } from '@hcengineering/emoji' +import { emojiCategories } from './types' +import { unicodeEmojiStore, customEmojiStore, getSkinTone } from './store' +import { get } from 'svelte/store' +import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui' + +export function updateCustomEmojis (customEmoji: CustomEmoji[]): void { + const emoji = customEmoji.map((e) => { + return { ...e, key: 'custom' } + }) + customEmojiStore.set(emoji as EmojiWithGroup[]) +} + +export async function updateUnicodeEmojis (lang?: string): Promise { + const local = lang ?? get(deviceInfo).language ?? 'en' + const emojis = await loadUnicodeEmojis(local) + unicodeEmojiStore.set(emojis) +} + +export async function loadUnicodeEmojis (lang: string): Promise { + const englishEmojis = + lang === 'en' + ? await fetchEmojis('en', { version: '15.0', shortcodes: ['iamcal'] }) + : await fetchEmojis('en', { compact: true, version: '15.0', shortcodes: ['iamcal'] }) + const languageEmojis = lang === 'en' ? null : await fetchEmojis(lang as Locale, { version: '15.0' }) + const messages = await fetchMessages(lang as Locale) + const groups = messages.groups + const groupKeys = new Map(groups.map((group, index) => [index, group.key])) + + const categories = new Map() + emojiCategories.forEach((cat) => { + if (Array.isArray(cat.categories)) cat.categories.forEach((c) => categories.set(c, cat.id)) + else if (typeof cat.categories === 'string') categories.set(cat.categories, cat.id) + }) + + const emojis = + languageEmojis !== null + ? languageEmojis.map((langEmoji, index) => { + return { + ...langEmoji, + tags: [...(englishEmojis[index]?.tags ?? []), ...(langEmoji?.tags ?? [])], + shortcodes: [...(englishEmojis[index]?.shortcodes ?? []), ...(langEmoji?.shortcodes ?? [])] + } + }) + : (englishEmojis as Emoji.Emoji[]) + + return emojis + .filter((e) => e.group !== 2 && e.group !== undefined) + .map((e) => { + return { ...e, key: categories.get(groupKeys.get(e?.group ?? 0) ?? '') ?? '' } + }) +} + +export function getEmojiByHexcode (hexcode: string): EmojiWithGroup | undefined { + return get(unicodeEmojiStore) + .concat(get(customEmojiStore)) + .find((e) => !isCustomEmoji(e) && e.hexcode === hexcode) +} + +export function getEmojiByEmoticon (emoticon: string | undefined): string | undefined { + if (emoticon === undefined) return undefined + const matchEmoji = findEmoji( + (e) => !isCustomEmoji(e) && (Array.isArray(e.emoticon) ? e.emoticon.includes(emoticon) : e.emoticon === emoticon) + ) + if (matchEmoji === undefined) return undefined + return !isCustomEmoji(matchEmoji) ? matchEmoji.emoji : undefined +} + +export function getUnicodeEmojiByShortCode (shortcode: string | undefined, skinTone?: number): Emoji.Emoji | undefined { + const emoji = getEmojiByShortCode(shortcode, skinTone) + if (emoji === undefined || isCustomEmoji(emoji)) return undefined + return emoji +} + +export function getEmojiByShortCode (shortcode: string | undefined, skinTone?: number): ExtendedEmoji | undefined { + if (shortcode === undefined) return undefined + const pureShortcode = shortcode.replaceAll(':', '') + return findEmoji((e) => { + if (isCustomEmoji(e)) return e.shortcode === pureShortcode + return e.shortcodes?.includes(pureShortcode) + }, skinTone) +} + +export function getCustomEmoji (shortcode: string | undefined): CustomEmoji | undefined { + if (shortcode === undefined) return undefined + const pureShortcode = shortcode.replaceAll(':', '') + const result = findEmoji((e) => { + if (isCustomEmoji(e)) return e.shortcode === pureShortcode + return false + }) as CustomEmoji + console.log('CustomEmoji', shortcode, result) + return result +} + +function findEmoji ( + predicate: (e: EmojiWithGroup) => boolean | undefined, + skinTone?: number +): ExtendedEmoji | undefined { + const emojis = get(unicodeEmojiStore).concat(get(customEmojiStore)) + const matchEmoji = emojis.find(predicate) + if (matchEmoji === undefined) return undefined + if (isCustomEmoji(matchEmoji)) return matchEmoji + if (skinTone === undefined) skinTone = getSkinTone() + if (skinTone === 0 || matchEmoji.skins === undefined) return matchEmoji + return matchEmoji.skins[skinTone - 1] +} + +export function getEmojiSkins (emoji: ExtendedEmoji): Emoji.Emoji[] | undefined { + if (isCustomEmoji(emoji)) return undefined + return emoji.skins +} diff --git a/plugins/emoji-resources/svelte.config.js b/plugins/emoji-resources/svelte.config.js new file mode 100644 index 0000000000..944a06f73e --- /dev/null +++ b/plugins/emoji-resources/svelte.config.js @@ -0,0 +1,5 @@ +const sveltePreprocess = require('svelte-preprocess') + +module.exports = { + preprocess: sveltePreprocess() +}; \ No newline at end of file diff --git a/plugins/emoji-resources/tsconfig.json b/plugins/emoji-resources/tsconfig.json new file mode 100644 index 0000000000..fb6ccb2d18 --- /dev/null +++ b/plugins/emoji-resources/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/ui/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types" + } +} \ No newline at end of file diff --git a/plugins/emoji/.eslintrc.js b/plugins/emoji/.eslintrc.js new file mode 100644 index 0000000000..72235dc283 --- /dev/null +++ b/plugins/emoji/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/plugins/emoji/.npmignore b/plugins/emoji/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/plugins/emoji/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/plugins/emoji/config/rig.json b/plugins/emoji/config/rig.json new file mode 100644 index 0000000000..0110930f55 --- /dev/null +++ b/plugins/emoji/config/rig.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig" +} diff --git a/plugins/emoji/jest.config.js b/plugins/emoji/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/plugins/emoji/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/plugins/emoji/package.json b/plugins/emoji/package.json new file mode 100644 index 0000000000..38782ad2df --- /dev/null +++ b/plugins/emoji/package.json @@ -0,0 +1,50 @@ +{ + "name": "@hcengineering/emoji", + "version": "0.6.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "files": [ + "lib/**/*", + "types/**/*", + "tsconfig.json" + ], + "author": "Hardcore Engineering Inc.", + "license": "EPL-2.0", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "test": "jest --passWithNoTests --silent", + "_phase:build": "compile transpile src", + "_phase:test": "jest --passWithNoTests --silent", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.4.0", + "eslint-plugin-promise": "^6.1.1", + "eslint": "^8.54.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5" + }, + "dependencies": { + "@hcengineering/core": "^0.6.32", + "@hcengineering/platform": "^0.6.11", + "@hcengineering/ui": "^0.6.15", + "emojibase": "^16.0.0", + "emojibase-regex": "^16.0.0" + }, + "repository": "https://github.com/hcengineering/platform", + "publishConfig": { + "registry": "https://npm.pkg.github.com" + } +} diff --git a/plugins/emoji/src/index.ts b/plugins/emoji/src/index.ts new file mode 100644 index 0000000000..e4e0116f5b --- /dev/null +++ b/plugins/emoji/src/index.ts @@ -0,0 +1,21 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import { emojiId, emojiPlugin } from './plugin' + +export * from './types' +export * from './utils' + +export { emojiId } +export default emojiPlugin diff --git a/plugins/emoji/src/plugin.ts b/plugins/emoji/src/plugin.ts new file mode 100644 index 0000000000..27989daf26 --- /dev/null +++ b/plugins/emoji/src/plugin.ts @@ -0,0 +1,97 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import { Asset, type IntlString, plugin, type Plugin, Resource } from '@hcengineering/platform' +import { AnyComponent } from '@hcengineering/ui' +import type { Class, Ref, Doc } from '@hcengineering/core' +import { CustomEmoji, ExtendedEmoji } from './types' + +/** @public */ +export const emojiId = 'emoji' as Plugin + +export const emojiPlugin = plugin(emojiId, { + ids: { + CustomEmoji: '' as Ref + }, + class: { + CustomEmoji: '' as Ref> + }, + component: { + EmojiPopup: '' as AnyComponent, + SettingsEmojiTable: '' as AnyComponent + }, + string: { + Remove: '' as IntlString, + SearchResults: '' as IntlString, + SearchDots: '' as IntlString, + DefaultSkinTone: '' as IntlString, + FrequentlyUsed: '' as IntlString, + GettingWorkDone: '' as IntlString, + SmileysAndPeople: '' as IntlString, + AnimalsAndNature: '' as IntlString, + FoodAndDrink: '' as IntlString, + TravelAndPlaces: '' as IntlString, + Activities: '' as IntlString, + Objects: '' as IntlString, + Symbols: '' as IntlString, + Flags: '' as IntlString, + NoTone: '' as IntlString, + Light: '' as IntlString, + MediumLight: '' as IntlString, + Medium: '' as IntlString, + MediumDark: '' as IntlString, + Dark: '' as IntlString, + CustomEmojis: '' as IntlString, + Shortcode: '' as IntlString, + ShortcodeDescription: '' as IntlString, + Image: '' as IntlString, + ImageDescription: '' as IntlString, + UploadImage: '' as IntlString, + Preview: '' as IntlString, + PreviewTextBegin: '' as IntlString, + PreviewTextEnd: '' as IntlString, + CreateTitle: '' as IntlString, + Create: '' as IntlString, + ImageAspectError: '' as IntlString, + ShortcodeMatchError: '' as IntlString, + ShortcodeExistsError: '' as IntlString, + NewCustomEmojiDialogClose: '' as IntlString, + NewCustomEmojiDialogCloseNote: '' as IntlString + }, + icon: { + Emoji: '' as Asset, + EmojiAdd: '' as Asset, + Activities: '' as Asset, + AnimalsAndNature: '' as Asset, + Flags: '' as Asset, + FoodAndDrink: '' as Asset, + FrequentlyUsed: '' as Asset, + GettingWorkDone: '' as Asset, + Objects: '' as Asset, + Search: '' as Asset, + SmileysAndPeople: '' as Asset, + Symbols: '' as Asset, + TravelAndPlaces: '' as Asset, + Custom: '' as Asset + }, + functions: { + GetEmojiByEmoticon: '' as Resource<(emoticon: string | undefined) => string | undefined>, + GetEmojiByShortCode: '' as Resource< + (shortcode: string | undefined, skinTone?: number) => ExtendedEmoji | undefined + >, + GetCustomEmoji: '' as Resource<(shortcode: string | undefined, skinTone?: number) => CustomEmoji | undefined> + } +}) + +export default emojiPlugin diff --git a/packages/ui/src/components/emoji/types.ts b/plugins/emoji/src/types.ts similarity index 58% rename from packages/ui/src/components/emoji/types.ts rename to plugins/emoji/src/types.ts index 5de9a25255..22f814becc 100644 --- a/packages/ui/src/components/emoji/types.ts +++ b/plugins/emoji/src/types.ts @@ -12,23 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import type { IntlString } from '@hcengineering/platform' -import type { AnySvelteComponent } from '../..' import type { Emoji } from 'emojibase' +import type { Blob, Doc, Ref } from '@hcengineering/core' -export type EmojiWithGroup = Emoji & { key: string } -export type Emojis = Emoji | EmojiWithGroup +export { default as Emoji } from 'emojibase' +export type ExtendedEmoji = Emoji | CustomEmoji +export type EmojiWithGroup = ExtendedEmoji & { key: string } -export interface EmojiCategory { - id: string - label: IntlString - icon: AnySvelteComponent - categories?: string[] | string - emojisString?: string[] - emojis?: EmojiWithGroup[] +/** @public */ +export interface CustomEmoji extends Doc { + shortcode: string + image: Ref } -export interface EmojiHierarchy { - emoji: EmojiWithGroup - parent?: EmojiWithGroup +export function isCustomEmoji (emoji: ExtendedEmoji): emoji is CustomEmoji { + return 'image' in emoji } diff --git a/plugins/emoji/src/utils.ts b/plugins/emoji/src/utils.ts new file mode 100644 index 0000000000..5ae947e4d5 --- /dev/null +++ b/plugins/emoji/src/utils.ts @@ -0,0 +1,28 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import EMOJI_REGEX from 'emojibase-regex' +import EMOTICON_REGEX from 'emojibase-regex/emoticon' +import SHORTCODE_REGEX from 'emojibase-regex/shortcode' + +export { fetchEmojis, fetchMessages, type Locale } from 'emojibase' + +export const emojiRegex = EMOJI_REGEX +export const emojiGlobalRegex = new RegExp(EMOJI_REGEX.source, EMOJI_REGEX.flags + 'g') + +export const emoticonRegex = new RegExp(`(?:^|\\s)(${EMOTICON_REGEX.source})$`) +export const emoticonGlobalRegex = new RegExp(`(? diff --git a/plugins/task-resources/src/components/state/TypeStatesPopup.svelte b/plugins/task-resources/src/components/state/TypeStatesPopup.svelte index d51767dc39..f871768cf8 100644 --- a/plugins/task-resources/src/components/state/TypeStatesPopup.svelte +++ b/plugins/task-resources/src/components/state/TypeStatesPopup.svelte @@ -50,7 +50,9 @@ function getColor (state: Status, type: ProjectType | undefined, categories: IdMap): ColorDefinition { const category = state.category ? categories.get(state.category) : undefined - const targetColor = type?.statuses?.find((p) => p._id === state._id)?.color ?? state.color ?? category?.color + const statusColor = type?.statuses?.find((p) => p._id === state._id)?.color + const targetColor = + statusColor === undefined || typeof statusColor !== 'string' ? statusColor : state.color ?? category?.color return getPlatformColorDef(targetColor ?? getColorNumberByText(state.name), $themeStore.dark) } diff --git a/plugins/task-resources/src/components/taskTypes/TaskTypeEditor.svelte b/plugins/task-resources/src/components/taskTypes/TaskTypeEditor.svelte index efdad48fe3..f39092caac 100644 --- a/plugins/task-resources/src/components/taskTypes/TaskTypeEditor.svelte +++ b/plugins/task-resources/src/components/taskTypes/TaskTypeEditor.svelte @@ -63,7 +63,7 @@ $: taskType = taskTypes.find((tt) => tt._id === objectId) $: name = taskType?.name $: icon = taskType?.icon - $: color = taskType?.color + $: color = taskType?.color !== undefined && typeof taskType?.color !== 'string' ? taskType?.color : undefined $: descriptor = client.getModel().findAllSync(task.class.TaskTypeDescriptor, { _id: taskType?.descriptor }) $: states = (taskType?.statuses.map((p) => $statusStore.byId.get(p)).filter((p) => p !== undefined) as Status[]) ?? [] diff --git a/plugins/task-resources/src/components/taskTypes/TaskTypeIcon.svelte b/plugins/task-resources/src/components/taskTypes/TaskTypeIcon.svelte index f61cd0fbc0..454ffdb6e7 100644 --- a/plugins/task-resources/src/components/taskTypes/TaskTypeIcon.svelte +++ b/plugins/task-resources/src/components/taskTypes/TaskTypeIcon.svelte @@ -15,11 +15,11 @@ -->
- - - {#if selected === 0} -
- - {#if icons.length > 0} -
- {#each icons as obj} - { - _icon = obj - dispatch(!showColor ? 'close' : 'update', { icon: _icon, color: _color }) - }} - /> - {/each} -
-
- {/if} - - {#if showColor} -
- { - _color = evt.detail - dispatch(icons.length === 0 ? 'close' : 'update', { - icon: icons.length === 0 ? null : _icon, - color: _color - }) - }} - /> - {/if} -
- {:else} - + + {#if icons.length > 0} +
+ {#each icons as obj} + { + _icon = obj + dispatch(!showColor ? 'close' : 'update', { icon: _icon, color: _color }) + }} + /> + {/each} +
+
+ {/if} + + {#if showColor} +
+ { - dispatch('close', { icon: iconWithEmoji, color: evt.detail.codes }) + _color = evt.detail + dispatch(icons.length === 0 ? 'close' : 'update', { + icon: icons.length === 0 ? null : _icon, + color: _color + }) }} /> {/if} - - +
+ {:else} + { + dispatch('close', { icon: iconWithEmoji, color: evt.detail.codes }) + }} + /> + {/if}