New card layout (#8796)

This commit is contained in:
Kristina
2025-05-02 09:01:01 +07:00
committed by GitHub
parent 65322e86f7
commit 508cef27d4
130 changed files with 2997 additions and 1465 deletions
+256 -196
View File
@@ -35,29 +35,26 @@ importers:
specifier: ^8.4.1
version: 8.4.1
'@hcengineering/communication-client-query':
specifier: 0.1.172
version: 0.1.172(typescript@5.3.3)
specifier: 0.1.174
version: 0.1.174(typescript@5.3.3)
'@hcengineering/communication-rest-client':
specifier: 0.1.172
version: 0.1.172(typescript@5.3.3)
specifier: 0.1.174
version: 0.1.174(typescript@5.3.3)
'@hcengineering/communication-sdk-types':
specifier: 0.1.172
version: 0.1.172(typescript@5.3.3)
specifier: 0.1.174
version: 0.1.174(typescript@5.3.3)
'@hcengineering/communication-server':
specifier: 0.1.172
version: 0.1.172(typescript@5.3.3)
specifier: 0.1.174
version: 0.1.174(typescript@5.3.3)
'@hcengineering/communication-shared':
specifier: 0.1.172
version: 0.1.172(typescript@5.3.3)
specifier: 0.1.174
version: 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types':
specifier: 0.1.172
version: 0.1.172(typescript@5.3.3)
specifier: 0.1.174
version: 0.1.174(typescript@5.3.3)
'@hcengineering/communication-yaml':
specifier: 0.1.172
version: 0.1.172(typescript@5.3.3)
'@hcengineering/drive':
specifier: ^0.6.32
version: 0.6.490
specifier: 0.1.174
version: 0.1.174(typescript@5.3.3)
'@hocuspocus/provider':
specifier: ^2.15.2
version: 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23)
@@ -226,6 +223,15 @@ importers:
'@rush-temp/collaborator-client':
specifier: file:./projects/collaborator-client.tgz
version: file:projects/collaborator-client.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
'@rush-temp/communication':
specifier: file:./projects/communication.tgz
version: file:projects/communication.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/communication-assets':
specifier: file:./projects/communication-assets.tgz
version: file:projects/communication-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/communication-resources':
specifier: file:./projects/communication-resources.tgz
version: file:projects/communication-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)))(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
'@rush-temp/contact':
specifier: file:./projects/contact.tgz
version: file:projects/contact.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))
@@ -481,6 +487,9 @@ importers:
'@rush-temp/model-chunter':
specifier: file:./projects/model-chunter.tgz
version: file:projects/model-chunter.tgz
'@rush-temp/model-communication':
specifier: file:./projects/model-communication.tgz
version: file:projects/model-communication.tgz
'@rush-temp/model-contact':
specifier: file:./projects/model-contact.tgz
version: file:projects/model-contact.tgz(encoding@0.1.13)
@@ -2985,78 +2994,63 @@ packages:
'@hcengineering/account-client@0.7.28':
resolution: {integrity: sha512-k1dkioX+jYrLyAXLLLsBYVCdZKf3WjhnDHw/yeAQgLQQ/9W2ffLtDMZ8lYWV3XMQ3eWrUCodtTDOiAQgXd/LEQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/account-client/0.7.28/32c9bc0dd297f00f4d5461467f25e072d01b9c76}
'@hcengineering/analytics@0.6.490':
resolution: {integrity: sha512-Jcdj9LXiIUxm75jRIaOrNlvTW48bd59ul2I0WB5NE3WHfAXKmk5FIBUd2dtTDhHMQUOZwUOKzVcxfYWIUs5HFQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/analytics/0.6.490/e2ed2bc32ca04e170e2907e6b0c44a3b725d36bd}
'@hcengineering/analytics@0.7.28':
resolution: {integrity: sha512-+CI/wJ8zgop+3mEcvhlB1UJBU6tIKq2Y7qTichZjGli8BckEIEwbD4j2beu2qXRSoTT2yUGWM9zpS9+fmH/DAw==, tarball: https://npm.pkg.github.com/download/@hcengineering/analytics/0.7.28/a8faf3d575bb28b9682b659495e738f8e0ca596e}
'@hcengineering/card@0.7.28':
resolution: {integrity: sha512-1d3allyx9jTpE/gr9kk/g5qo5848i4FKsRiHgCpJqzNEdRW7TAednoHAr2hKRU5DZC/i3Br56axSkUQzV2Idhg==, tarball: https://npm.pkg.github.com/download/@hcengineering/card/0.7.28/5348996f5b7b4a34517780a5bf324122cbddfbad}
'@hcengineering/communication-client-query@0.1.172':
resolution: {integrity: sha512-LeSVl8zX/rnnVmfvuBN+klI3Jts1OxNdNEOsYJEgFDvGfK+dn3qmcLFUtqwJdh8y5tTGTBfqxn9LjZgCWOYZ8w==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-client-query/0.1.172/0cbf17add61d5932b6ce97c10b8de223e041b453}
'@hcengineering/communication-client-query@0.1.174':
resolution: {integrity: sha512-/JvTp99mkuxfNT853524Cs18kuO6JOF34RAnw/icEgWRAlmeVr0J2HqaQnUhqHiJYg7ZUCpjMVr/iF+rno0CoQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-client-query/0.1.174/a0ba56179280decedb3fb176fbe6f3aa9f5b902f}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-cockroach@0.1.172':
resolution: {integrity: sha512-6RgvzAJ/n7tUjtJIb06jBUdl/k+nUbSuy3sh75ZNBwtNbE93CP+uJG9Wh/8vz416T1Z6hPxJENuVSzNcv9XEwQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-cockroach/0.1.172/cde6de8e8da5d3f239b4f5db4be6a194ffacacae}
'@hcengineering/communication-cockroach@0.1.174':
resolution: {integrity: sha512-+URXcZJ+Xz1r1HrIc2lRjS5a924dKX1g8p/igAJR0TQJO0Vm2k7kWf+8eUd/zqE7v/hiXnEOs5xLvqiwXw+RWA==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-cockroach/0.1.174/849e7081670ea824528e457c891a289d3fc56a9c}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-query@0.1.172':
resolution: {integrity: sha512-EPuYWJ4s4T6ragx/29WpkM5YSqejnYBqwoFWX/2kUCOaV1kIZS2aFiuEz7IW3i6LUjrTt+EKz5EqOJXStqVY7Q==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-query/0.1.172/1c8a30191a4e233d759b01cf39af58531e9b52ca}
'@hcengineering/communication-query@0.1.174':
resolution: {integrity: sha512-KJE/wJYefOuMYjJ9TGKuc5R6WNoqzm60qm13VQAnbWozGttTz2+rhhhcd/kCDjtRyKfa1jBDwNA11ts/vnbKSw==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-query/0.1.174/a950b0c41d561b1a847acd459bf9028e6bde149e}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-rest-client@0.1.172':
resolution: {integrity: sha512-z1rfFpNepm+13Dwaezf+4ni915F1wdocTIxqTVkLwAvfbC2SwxgW9aMSjfxsU/2nEW31XAosvAYFuM21R4uu6w==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-rest-client/0.1.172/4a221f95e64908eddb28aeedd7c7a7caed996aef}
'@hcengineering/communication-rest-client@0.1.174':
resolution: {integrity: sha512-U7Fj57DT30xTInL+JP5eTyhTBXMIL+DwyBaT1XtVH7g135dvNGmC/iGnHjJ1Cp30LYRuHf19ovoNvLbyFewnFw==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-rest-client/0.1.174/5c1b9744648628c532d9798412f7b62001a7d26e}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-sdk-types@0.1.172':
resolution: {integrity: sha512-XeetCHsQjKE0AfqaXA/E1pqClxON6csb27I7bi+CTMOoVH6y3BejEREmI+rj28g/rxB6OdLAlchW4uxsqh+trA==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-sdk-types/0.1.172/5dbccb233f31dd7e8cf93e5d93858d50d0eb0a8a}
'@hcengineering/communication-sdk-types@0.1.174':
resolution: {integrity: sha512-cIiPLY0xM9GsISGBVol6R0kdX2ne50XYXkGKhbdpLyQM7SU9NrwxRwzHi1WV0UjHWmM0N0pGsgMKj8hKrBow4A==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-sdk-types/0.1.174/7a44ace6563f862c5c3479d32643fabdb6dccca9}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-server@0.1.172':
resolution: {integrity: sha512-oOSqWFNNRks3pbYSZQG3oHWPLpBDXugRK47B0xec6aTdVvf+a688XHCdpkErFsbmqvazFwM/HwDIiWq/WfhqdA==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-server/0.1.172/7509a4e876a2f6d0023ed23a1c262baf434399ef}
'@hcengineering/communication-server@0.1.174':
resolution: {integrity: sha512-TT/fBmsVc7bSBGEZ7qxU9LibYqah61taI6ujvTzSDxXRspAvHIb4Jk4TtdyCcU/UzX8i2skUua0XbcPCcwAFpQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-server/0.1.174/5a575aebe3fbdc1a44d5a393cd47171b5c298da1}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-shared@0.1.172':
resolution: {integrity: sha512-gPCcmxV9mlgScKQI7ENXlpxxA3v7gn6RpaL9toF/UmjCb63bvyShIt9Ti+P3rjm2KAZzutAhW5prSZwUlWHGjQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-shared/0.1.172/fc20f730b5bf0e8f7244fc4d2007bc71943c0c82}
'@hcengineering/communication-shared@0.1.174':
resolution: {integrity: sha512-P8lBufpX4psYU0emgjjSpSTWuyxI9FkuCUzdvav7bsDtZ7rAYjwSr/dOx7I51KPdxD2P9x6qHMQhEEsEc5isDg==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-shared/0.1.174/20e4eb0c34d263014cea37f424ed792e3946f6d5}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-types@0.1.172':
resolution: {integrity: sha512-XeU5k09ONUFGPPpbfDOI8+uaUTJcxe+CH5UzpWPfMpOlF4+7Kx+TfCDCBiIc9CYSyedHZbtl+GZokmgGvhdajw==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-types/0.1.172/8a5f620e404dee92505900031dd3e804817f0160}
'@hcengineering/communication-types@0.1.174':
resolution: {integrity: sha512-rydGR+leAkU0ATPf+xMu0FdV6I78r+6mV3oD505fcfc43d59WQcb4VqiIEck9gnVfWezSoXEApenm2uKD7ruDQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-types/0.1.174/226647822d778d1e86bc9c5ac6d8b270684d4994}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/communication-yaml@0.1.172':
resolution: {integrity: sha512-GxWiIEPH+xO1YU1b52uJxuvRrhMnzGiIyxqN7D51TCkqjKEUTl4ZqEPD4uMyiWU1iXjUF3Q8C+MecdcQXuvcjA==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-yaml/0.1.172/2e4ae4b9f3106b5493a1a79b9ff3a0bb41a9641f}
'@hcengineering/communication-yaml@0.1.174':
resolution: {integrity: sha512-iBCC5beUV36ETxpQjCDESU86tk/6Y/oSLvdvQVMG4Q2TtTqODqPxiPJZ8rWUGZv2JomecYE+XcY7541x411HpA==, tarball: https://npm.pkg.github.com/download/@hcengineering/communication-yaml/0.1.174/5b79a08820c011aa58f4897688c6d94d777f6a89}
peerDependencies:
typescript: ^5.6.3
'@hcengineering/core@0.6.490':
resolution: {integrity: sha512-BnYeOeSBxjYcwAwGcXcTcmLbNo0ft2Pt7QoDlHV4KLuDCQkU0Xw6hUvDgMyz6irHKciCa0jBdZ2vOgvBTTe3ug==, tarball: https://npm.pkg.github.com/download/@hcengineering/core/0.6.490/562815e33c0dca6833a43d71745ebb12fa6e7cb9}
'@hcengineering/core@0.7.28':
resolution: {integrity: sha512-qv4QE7mkect6JYlGMljW5CQ2uU80hFGq0E17lZ5rO79h1boWEKLJpINv+w3J2BdR/nNG7taIsdZg3yfl9fY4bg==, tarball: https://npm.pkg.github.com/download/@hcengineering/core/0.7.28/43b84c63262e2b8fe5554c1967812e2ce6bb56e1}
'@hcengineering/drive@0.6.490':
resolution: {integrity: sha512-UCcs/SQeXSP/PinDmyh/F6qMW3Ji4CI1nQYCLsxcMei6XHwXyMqfVppeqKpbmGVV+5HJOjAsbSaJe2EiLy6l5Q==, tarball: https://npm.pkg.github.com/download/@hcengineering/drive/0.6.490/270638c5412bb3cf2ac5f2a207896b069f87adb9}
'@hcengineering/platform@0.6.490':
resolution: {integrity: sha512-jtCHMznMpbPJQaX2RTbKN0tR5e2YgSrIYWHREXgi8VIAIygMLNtzkkNkVX/2FgcOHK+Gs1aF3mf4gsikiKyyKw==, tarball: https://npm.pkg.github.com/download/@hcengineering/platform/0.6.490/45e5e6cbd7fcb12cf241c5ace3c027689052f454}
'@hcengineering/platform@0.7.28':
resolution: {integrity: sha512-0zEifwsLdOs/M6TZ9xnPrQTTlhwW28sooiyL4ZfQsiC2AJUy05vLP5/W8V9eDLta+EiR5bjtlulGsg1GfPjUoQ==, tarball: https://npm.pkg.github.com/download/@hcengineering/platform/0.7.28/2126ff97284b679ceab3c86f24febd73a3a28712}
'@hcengineering/preference@0.6.490':
resolution: {integrity: sha512-gwX9U1T2H3A/yDoT2DLFn4SPD9Ki1mQ4bp3MhbPc6uc/cv8Dm2eD/nFM1viT2j03TrM8caKIpB0epONH3qj12g==, tarball: https://npm.pkg.github.com/download/@hcengineering/preference/0.6.490/9e1a4219a1cbec866faf43f7584abcda8fb9b6c2}
'@hcengineering/preference@0.7.28':
resolution: {integrity: sha512-WwwOfjZ1TLdTfPhQGvSjo8dS63HyuE7BDVhfstxP8D+H4BI5UKYoev/6vlwQf/3d+jI1S4f2ivO//IDJAD7sGg==, tarball: https://npm.pkg.github.com/download/@hcengineering/preference/0.7.28/f1739991cc012d194fa18f99565ae5bd64f0ad07}
@@ -3066,15 +3060,9 @@ packages:
'@hcengineering/tags@0.7.28':
resolution: {integrity: sha512-2KCVUu5N/GAkDsDRbdUi7gnBq4ze1Z3eCLoc8nz2G/zeAjAga8gmWkZEbXqAC8rNVqBQv72EKeJFGJyqnev6Xw==, tarball: https://npm.pkg.github.com/download/@hcengineering/tags/0.7.28/42545cc43e3c61e32ce96be87c245fa929f5cbb6}
'@hcengineering/theme@0.6.490':
resolution: {integrity: sha512-avJpw+/Sx0xZDctrFImANYBGK4TXdOphI158jcWgpC1ys6IBiRHKeeUtquTKud8q9mE06jb7TjOt9Oo/wrCGWg==, tarball: https://npm.pkg.github.com/download/@hcengineering/theme/0.6.490/2f8704cc5a089ce66e7d6dae4aa3ade6f3ea9b7a}
'@hcengineering/theme@0.7.28':
resolution: {integrity: sha512-Hv3wQKHr327TC7wMHCBiG4bMtg8vSlb7+UFwdKI8ztEs3359meqkXu+exqi7Nl5334voXIz5ZFxjwZwOMzNSFA==, tarball: https://npm.pkg.github.com/download/@hcengineering/theme/0.7.28/557640a7ba2ffaa4d6e5411a54577f60dc528300}
'@hcengineering/ui@0.6.490':
resolution: {integrity: sha512-W/7ljW5a/nSfzHQjJRknlnUkKC9vGRkXUjOiBKIjMiJxw7NZwNyfv5quQxNswdARMYRrAfcfdxUOhEmIZYC2Lg==, tarball: https://npm.pkg.github.com/download/@hcengineering/ui/0.6.490/27bb6fcefc46410988abe75404bbd0465d0dd2a1}
'@hcengineering/ui@0.7.28':
resolution: {integrity: sha512-QsUpO0+Idu1n2dC6KtorPJ6SSp30It8FHSuG3Y+Awv4td/ftVocOSFqhskbk+us9Yw9KqOYgkqhGYEHbfRfw4Q==, tarball: https://npm.pkg.github.com/download/@hcengineering/ui/0.7.28/82025fc775702ea8c8569611356662bfbf28eb74}
@@ -3972,7 +3960,7 @@ packages:
version: 0.0.0
'@rush-temp/activity-resources@file:projects/activity-resources.tgz':
resolution: {integrity: sha512-EmICsTg7F8yX7wkZzQhe5L/SCQ+3XJgq4k8VC1Mji+6RPxEhvagBeOKDtQM8Rmg5oJvvQn93unq78kduusuetg==, tarball: file:projects/activity-resources.tgz}
resolution: {integrity: sha512-eC+r253HtclWdPx0PoLIu3zhvQRHvyT10jqAzvO4JOf4hbmNKj8eBLNkNWDnTXmNa0erH2zdPG6gzM08hVoc+g==, tarball: file:projects/activity-resources.tgz}
version: 0.0.0
'@rush-temp/activity@file:projects/activity.tgz':
@@ -4076,7 +4064,7 @@ packages:
version: 0.0.0
'@rush-temp/card-resources@file:projects/card-resources.tgz':
resolution: {integrity: sha512-7714jtYqpTQ+VyWjObiRlGB3X64f9sOfwF1vqYjkJMwi1MWq7xGn294QvOzPDuEjPvOZzfDLJpUd3VVsOn0VsQ==, tarball: file:projects/card-resources.tgz}
resolution: {integrity: sha512-DQsKJ5FhJ6nucCcWic8n9F6ut/bvGbs9+cTHIhv9PmKaKr3f9bxcQY7yjsOlvw9aJw12uOfZpMmhft4XFcK7dg==, tarball: file:projects/card-resources.tgz}
version: 0.0.0
'@rush-temp/card@file:projects/card.tgz':
@@ -4088,7 +4076,7 @@ packages:
version: 0.0.0
'@rush-temp/chat-resources@file:projects/chat-resources.tgz':
resolution: {integrity: sha512-uRaHRIJND7Bkr8M16shYH8YjwyMO7ytjXrogMh3m6yv3acZGCN11sXoeoRpO2+jeUxJ+03o3gBDTFTeTWNfqCw==, tarball: file:projects/chat-resources.tgz}
resolution: {integrity: sha512-0DNiq9IJXQbQsUkcEqaXm/8HrxJJfg1Q+vfDSxMi1wmmBvlMd9QXhhdA53mZRLJ4qa4eP/0Ruaai2+Cb6+q1Ig==, tarball: file:projects/chat-resources.tgz}
version: 0.0.0
'@rush-temp/chat@file:projects/chat.tgz':
@@ -4108,7 +4096,7 @@ packages:
version: 0.0.0
'@rush-temp/client-resources@file:projects/client-resources.tgz':
resolution: {integrity: sha512-oCxf8CrpgoWEHY/wQ32dMlvGt7jEHwf2nSOkOhxuETwbOwZahA/+5MuvSe4bBQ58quLYt1FEMptJsSg3ZP1kkA==, tarball: file:projects/client-resources.tgz}
resolution: {integrity: sha512-KoWLuivv/b8oseSErcCLwes9L3smXAVw7rzqGea7b80UnS4ntBkZD4VIwH7jX41xhNrlIiSjXAxCJf0QPY+LlQ==, tarball: file:projects/client-resources.tgz}
version: 0.0.0
'@rush-temp/client@file:projects/client.tgz':
@@ -4127,6 +4115,18 @@ packages:
resolution: {integrity: sha512-t7F9Hgow7TzMVkYDQW72SgRoeSh5ZQxnvmSycWRrHcSQCtiWqQe0AT7/Sx5oZKWos3hGjUA5GIJi/toJioVmOg==, tarball: file:projects/collaborator.tgz}
version: 0.0.0
'@rush-temp/communication-assets@file:projects/communication-assets.tgz':
resolution: {integrity: sha512-1BcpdOgbhmh+R1rqstyhhHJy0Nw0qd8HN7dlWwEiIjsWtTsQpuFXbPIdSAN71TBLbxVzR0YH+KIFRyE4eSx87g==, tarball: file:projects/communication-assets.tgz}
version: 0.0.0
'@rush-temp/communication-resources@file:projects/communication-resources.tgz':
resolution: {integrity: sha512-LMnZ2ZHr8E8jS3M8c4LAM5KGAGnP1QAD4g1e15pEr+cFvNXzhK5laeMCNaLeJDGy0xF/+lGlRcDuu/w++AVplQ==, tarball: file:projects/communication-resources.tgz}
version: 0.0.0
'@rush-temp/communication@file:projects/communication.tgz':
resolution: {integrity: sha512-Ke3N4mmJ4M3drwvZDvhWMKgvYL+dWpXkIG0DEne45V62XyxJgz+RWTYUqZ5pwNQx3L6Ki6nX6qijaXrHzDXTKA==, tarball: file:projects/communication.tgz}
version: 0.0.0
'@rush-temp/contact-assets@file:projects/contact-assets.tgz':
resolution: {integrity: sha512-PRl7vfRPda3ntMSUZkOEA7qVPSgndwY9vq4bagurqjvhdhvKfoKNRdWXmlVs1eSwAB0dJErw7/AlvU7ryQSIrA==, tarball: file:projects/contact-assets.tgz}
version: 0.0.0
@@ -4176,7 +4176,7 @@ packages:
version: 0.0.0
'@rush-temp/desktop@file:projects/desktop.tgz':
resolution: {integrity: sha512-CP0b8VHDy4SDT6BEqg5fUrc6WG5AVwWslxiyDi6bkGFaLnIG8aSw4HKHj0kkMmZ8WJtZdfvmk3z5JH47A6GkoA==, tarball: file:projects/desktop.tgz}
resolution: {integrity: sha512-2MuxT1ciufuMIFtWSoBJlO0uNhPmICCXRVnZZOQji4ByNWBvm4RSkPd97BQic8PxYe4H4FlPmIylRsvvVaeU7w==, tarball: file:projects/desktop.tgz}
version: 0.0.0
'@rush-temp/devmodel-resources@file:projects/devmodel-resources.tgz':
@@ -4316,7 +4316,7 @@ packages:
version: 0.0.0
'@rush-temp/inbox-resources@file:projects/inbox-resources.tgz':
resolution: {integrity: sha512-nOcoALHs+JLfTFLVFLt3swxNp0ewoxJlfyHJ8rOPYkwDvaTvI7E0G8XKgUoVojZeTX+oR/3KyWqjCrcaf8YuPA==, tarball: file:projects/inbox-resources.tgz}
resolution: {integrity: sha512-VXtnI7lkLa7Oe94ftplqjev46LkmgK05a6YvruXnejgUolNrKjVvSgZIw4l6b3/4JzaI+MdC4P/s0qvajYz76g==, tarball: file:projects/inbox-resources.tgz}
version: 0.0.0
'@rush-temp/inbox@file:projects/inbox.tgz':
@@ -4428,7 +4428,7 @@ packages:
version: 0.0.0
'@rush-temp/model-all@file:projects/model-all.tgz':
resolution: {integrity: sha512-SKstK5NwxlHraDMcD0qfHJru8wR+/XtOzfor1keyKWbVlzbAp65YO8lzua3kd2IP+P9MtsOf8WMMzQX2f5s+iw==, tarball: file:projects/model-all.tgz}
resolution: {integrity: sha512-apw/K0//cfSDJtJQ6h8Y9RmfYgpBq67AAr1hxPrSg8kYLMPzhro1u/0Iuj+jJGnbv874rVprV8VWZznoDD5m8A==, tarball: file:projects/model-all.tgz}
version: 0.0.0
'@rush-temp/model-analytics-collector@file:projects/model-analytics-collector.tgz':
@@ -4456,13 +4456,17 @@ packages:
version: 0.0.0
'@rush-temp/model-chat@file:projects/model-chat.tgz':
resolution: {integrity: sha512-OuLdR05w3mKXbRksmTM4ZC5Cmm757vz4gUH08PHzDVtrOJU/gq4lkAhdqzFgEKnV8nXN4mZ7Cn3Ra/1NDAcKuQ==, tarball: file:projects/model-chat.tgz}
resolution: {integrity: sha512-WBNBH/Nt5693IYF9/RjvGohd42lP6/Pw8E4exrWnJa14QWg4Mdy5sO2/7TGYU9/kPboldNhJjMHg9j25QjIw9Q==, tarball: file:projects/model-chat.tgz}
version: 0.0.0
'@rush-temp/model-chunter@file:projects/model-chunter.tgz':
resolution: {integrity: sha512-es0oJ+MzCyOR75Kdx2An+tBhga3cZCaPV24k7V4LtlxXyxmboZjiDhZgh9/56psQwWgT7qqL6XBPzCR4nZBV1g==, tarball: file:projects/model-chunter.tgz}
version: 0.0.0
'@rush-temp/model-communication@file:projects/model-communication.tgz':
resolution: {integrity: sha512-Fbi79c8xZjCCG892THwn1EHUSA5CecN/DhcyibrsjwWMJP5W4jLnv+2iJhwKHZNZ/bUnRDgKFv5iWUIPu28WDA==, tarball: file:projects/model-communication.tgz}
version: 0.0.0
'@rush-temp/model-contact@file:projects/model-contact.tgz':
resolution: {integrity: sha512-R5QhJlkccqefuHjHaIg/POYsrtnWCYDPE6MwNTQ+1F/CxGYuCJ2kDfEZMlKZiLitiruAkKbvq8xBo/nl2/PNfw==, tarball: file:projects/model-contact.tgz}
version: 0.0.0
@@ -4564,7 +4568,7 @@ packages:
version: 0.0.0
'@rush-temp/model-recorder@file:projects/model-recorder.tgz':
resolution: {integrity: sha512-zYwcCJVba+KhkMPQhgD7+ePk4S+zv9VyJ4h5PdIGgxfafKhugx5hcwVUthjVlgjyTs4merYQoTFl4ys30KatZA==, tarball: file:projects/model-recorder.tgz}
resolution: {integrity: sha512-qFjvdP1IJmVy2FJQ4YvRu/GeQCxxvLJKT39tBv1EIwL7/DV1eENAfSw2HNkanWAUtUKHGnqnmAm348KBVRNQNw==, tarball: file:projects/model-recorder.tgz}
version: 0.0.0
'@rush-temp/model-recruit@file:projects/model-recruit.tgz':
@@ -4864,7 +4868,7 @@ packages:
version: 0.0.0
'@rush-temp/pod-inbound-mail@file:projects/pod-inbound-mail.tgz':
resolution: {integrity: sha512-iC+pqJO5+ZG4SVopo+kXW+ROqVyMddxjDWLWfZGztLTuygK6wjCMNVbyGhJxNAmliTOsNajzEVtmRDIaNA29GQ==, tarball: file:projects/pod-inbound-mail.tgz}
resolution: {integrity: sha512-k2IG0e/Pk2KOW8f/S/hbKPB3Erh2Yiv9KMu/+IyOYVGMhDt/DR+usttRsCD7NOWqFtiy250y2GpaJRq/qQGqBg==, tarball: file:projects/pod-inbound-mail.tgz}
version: 0.0.0
'@rush-temp/pod-love@file:projects/pod-love.tgz':
@@ -4876,7 +4880,7 @@ packages:
version: 0.0.0
'@rush-temp/pod-msg2file@file:projects/pod-msg2file.tgz':
resolution: {integrity: sha512-+Wes98hofxmU3stiB7+xfmPv9zAak7Q2UAhTuC1m++A59iYjnkMwcn9Q/ywRQH+FF45qWeaMYv/AhuMyWnibqw==, tarball: file:projects/pod-msg2file.tgz}
resolution: {integrity: sha512-umk2MHdV65UvS6rIDTcXCjmUc3KBETgY7ojUSirgcvxztmC1JdgQ8g8D4DFzX7StQa0+fAGz5og36007IYP33A==, tarball: file:projects/pod-msg2file.tgz}
version: 0.0.0
'@rush-temp/pod-print@file:projects/pod-print.tgz':
@@ -4884,7 +4888,7 @@ packages:
version: 0.0.0
'@rush-temp/pod-server@file:projects/pod-server.tgz':
resolution: {integrity: sha512-KQSuVq8PFZHfqzogcEEMH3EuVOQZ2AteuXD/5J1FAURYl817QB/zzqC3WtiZ6yt8rwWRTI8NwM/a2VVKTUGZ3Q==, tarball: file:projects/pod-server.tgz}
resolution: {integrity: sha512-NP25MUnqfYgXVn14uYSuhFF/C5tZ3fyutLQIdbghWCNNfa+Cln/vUrmB7F6n7JxthgAg8XCn7HOfjWuLPYxtww==, tarball: file:projects/pod-server.tgz}
version: 0.0.0
'@rush-temp/pod-ses@file:projects/pod-ses.tgz':
@@ -4932,7 +4936,7 @@ packages:
version: 0.0.0
'@rush-temp/presentation@file:projects/presentation.tgz':
resolution: {integrity: sha512-nzzqiGqoRHekqhXV+0wGXdsgwa5PAiehwe+pFzuNFarYRDhS2mzaF4SwKgyAz9A3gN1dAbjLCWaA553AUNFqgQ==, tarball: file:projects/presentation.tgz}
resolution: {integrity: sha512-GzaUoAROzVGtzTqXVF+kJt3ESvx65cESPTpWvhUKjISbwVzfCFDiNwYhYIRWgrKeVxVt6YvqYj3dupaARV/j3Q==, tarball: file:projects/presentation.tgz}
version: 0.0.0
'@rush-temp/print-assets@file:projects/print-assets.tgz':
@@ -4960,7 +4964,7 @@ packages:
version: 0.0.0
'@rush-temp/prod@file:projects/prod.tgz':
resolution: {integrity: sha512-D3+eukzHy1uZ6wlLr+FxaVXntbuYZU/VEmDc1+TBNT6C/1/yB0oUJBDAiJnRsKPOsjSiU1NYw0HKoimvHhdhOw==, tarball: file:projects/prod.tgz}
resolution: {integrity: sha512-sH+/06L2vJs2pfZD+n0Lir0YqObICbAlwOUjLMmJGWoQNGn5SrK85M9asNA5w4FbzKSHMLoSarNHebM7Th8ipQ==, tarball: file:projects/prod.tgz}
version: 0.0.0
'@rush-temp/products-assets@file:projects/products-assets.tgz':
@@ -5012,7 +5016,7 @@ packages:
version: 0.0.0
'@rush-temp/recorder@file:projects/recorder.tgz':
resolution: {integrity: sha512-/Oi8ZzFzINaPy13dn0+UfkU+0VdCVzBOgXmArnYDqAq2ow503KqMf8Zv3/rW8oQOlYeS6Jdy6TlGp+IMX2CQuQ==, tarball: file:projects/recorder.tgz}
resolution: {integrity: sha512-IPl6+cYfRw0rnCL5+4Eajn1zM7YTXPqzON6XGVvNDjEj2PkFJnaENne98ZYdcwYWU/X3kamnteOqx145wlrpJQ==, tarball: file:projects/recorder.tgz}
version: 0.0.0
'@rush-temp/recruit-assets@file:projects/recruit-assets.tgz':
@@ -5060,7 +5064,7 @@ packages:
version: 0.0.0
'@rush-temp/server-activity-resources@file:projects/server-activity-resources.tgz':
resolution: {integrity: sha512-6NGQljoDn5dLuKbCU+CAxoBdJkFhGWbsoLwrZdBisIxEr1hj1xIeeDAtaAMLRld+epgMX+0AcG5EQrLxLp/A7w==, tarball: file:projects/server-activity-resources.tgz}
resolution: {integrity: sha512-O1HkbFlpkESkoXDZUBhWn1wcTUT8v+wsuhMARNbDw4z7FkwwzZnQW+b4dlQz81lWgWvJgNftQiNOcM6oJR6T3w==, tarball: file:projects/server-activity-resources.tgz}
version: 0.0.0
'@rush-temp/server-activity@file:projects/server-activity.tgz':
@@ -5104,7 +5108,7 @@ packages:
version: 0.0.0
'@rush-temp/server-card-resources@file:projects/server-card-resources.tgz':
resolution: {integrity: sha512-700pMI2ppFiIVXIXF712BaTrsbLG2iQpWc+jd0E+CVmMLHvq51t47zqnqkrzsnprQirXka9t6XM1GXiVFTuulA==, tarball: file:projects/server-card-resources.tgz}
resolution: {integrity: sha512-GFpXvTyz3YmI+3M/nu/YLhu1yfNvMszn/n0Hh5M7WQN8Ls8y9ReDpo5mq0O8kxuqVVDiCNmVpUZlMnu3Xgc1Sw==, tarball: file:projects/server-card-resources.tgz}
version: 0.0.0
'@rush-temp/server-card@file:projects/server-card.tgz':
@@ -5148,7 +5152,7 @@ packages:
version: 0.0.0
'@rush-temp/server-core@file:projects/server-core.tgz':
resolution: {integrity: sha512-87zGXHlESgiz1g5FdqHBMQQ5MgqcPhBVoS3x/baKkPSg523llWzGNqQ+BZuCg7eUmPzf2NIVaVXDssq/EQ1CHw==, tarball: file:projects/server-core.tgz}
resolution: {integrity: sha512-HoqF5Rq95I3jae3KoyYrdSu/AuHlM7/vf0t9ZaEiW3peme0lEQ3iO/jA7eIBUaP2D5sMHwZLriYvUGagCq8U5g==, tarball: file:projects/server-core.tgz}
version: 0.0.0
'@rush-temp/server-document-resources@file:projects/server-document-resources.tgz':
@@ -5352,7 +5356,7 @@ packages:
version: 0.0.0
'@rush-temp/server@file:projects/server.tgz':
resolution: {integrity: sha512-Ek116xoAlCP51m2adUYtxibMRAdufFNyxfHLowdOynye2x+tusPaIbjnCUAtBwPx/H6yxH9/wGKNg0jLUE4tpg==, tarball: file:projects/server.tgz}
resolution: {integrity: sha512-DOy7BhJo1N0KhOzdXfmTYTB53NXKztkGwYpJoVgKqqVs0I0y6cIvCt88fR3xuAJC33HwrP72aeZ/UPNhv/qakQ==, tarball: file:projects/server.tgz}
version: 0.0.0
'@rush-temp/setting-assets@file:projects/setting-assets.tgz':
@@ -5544,7 +5548,7 @@ packages:
version: 0.0.0
'@rush-temp/ui-next@file:projects/ui-next.tgz':
resolution: {integrity: sha512-bisoYF6n3HWSicnaOnc1DhInOtgSsIW77QjtWRtuq3lmJMzKQ8u+RF2wqa6gUaw3Qa/qcbE+HyzkJGQyCVB0og==, tarball: file:projects/ui-next.tgz}
resolution: {integrity: sha512-ZzaX4T3iVbJ0L+gUEe0BO+D0weMrHQ7Yyp75BTkDh1UaJIsS/6cQe+qlzkbewxbApcsXqOnCQsYVvahoFCNSzQ==, tarball: file:projects/ui-next.tgz}
version: 0.0.0
'@rush-temp/ui@file:projects/ui.tgz':
@@ -14925,10 +14929,6 @@ snapshots:
'@hcengineering/core': 0.7.28
'@hcengineering/platform': 0.7.28
'@hcengineering/analytics@0.6.490':
dependencies:
'@hcengineering/platform': 0.6.490
'@hcengineering/analytics@0.7.28':
dependencies:
'@hcengineering/platform': 0.7.28
@@ -14939,155 +14939,132 @@ snapshots:
'@hcengineering/platform': 0.7.28
'@hcengineering/ui': 0.7.28
'@hcengineering/communication-client-query@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-client-query@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/communication-query': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-query': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
fast-equals: 5.2.2
typescript: 5.3.3
'@hcengineering/communication-cockroach@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-cockroach@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-shared': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-shared': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
postgres: 3.4.5
typescript: 5.3.3
uuid: 11.1.0
'@hcengineering/communication-query@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-query@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-shared': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-yaml': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-shared': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-yaml': 0.1.174(typescript@5.3.3)
fast-equals: 5.2.2
typescript: 5.3.3
uuid: 11.1.0
'@hcengineering/communication-rest-client@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-rest-client@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-shared': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-shared': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@hcengineering/core': 0.7.28
snappyjs: 0.7.0
typescript: 5.3.3
'@hcengineering/communication-rest-client@0.1.172(typescript@5.7.3)':
'@hcengineering/communication-rest-client@0.1.174(typescript@5.7.3)':
dependencies:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-shared': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-shared': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@hcengineering/core': 0.7.28
snappyjs: 0.7.0
typescript: 5.7.3
'@hcengineering/communication-sdk-types@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-sdk-types@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@hcengineering/core': 0.7.28
typescript: 5.3.3
'@hcengineering/communication-sdk-types@0.1.172(typescript@5.7.3)':
'@hcengineering/communication-sdk-types@0.1.174(typescript@5.7.3)':
dependencies:
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@hcengineering/core': 0.7.28
typescript: 5.7.3
'@hcengineering/communication-server@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-server@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/account-client': 0.7.28
'@hcengineering/communication-cockroach': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-cockroach': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@hcengineering/core': 0.7.28
'@hcengineering/platform': 0.7.28
'@hcengineering/server-token': 0.7.28
typescript: 5.3.3
zod: 3.24.2
'@hcengineering/communication-shared@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-shared@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
typescript: 5.3.3
'@hcengineering/communication-shared@0.1.172(typescript@5.7.3)':
'@hcengineering/communication-shared@0.1.174(typescript@5.7.3)':
dependencies:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
typescript: 5.7.3
'@hcengineering/communication-types@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-types@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/card': 0.7.28
'@hcengineering/core': 0.7.28
'@hcengineering/tags': 0.7.28
typescript: 5.3.3
'@hcengineering/communication-types@0.1.172(typescript@5.6.2)':
'@hcengineering/communication-types@0.1.174(typescript@5.6.2)':
dependencies:
'@hcengineering/card': 0.7.28
'@hcengineering/core': 0.7.28
'@hcengineering/tags': 0.7.28
typescript: 5.6.2
'@hcengineering/communication-types@0.1.172(typescript@5.7.3)':
'@hcengineering/communication-types@0.1.174(typescript@5.7.3)':
dependencies:
'@hcengineering/card': 0.7.28
'@hcengineering/core': 0.7.28
'@hcengineering/tags': 0.7.28
typescript: 5.7.3
'@hcengineering/communication-yaml@0.1.172(typescript@5.3.3)':
'@hcengineering/communication-yaml@0.1.174(typescript@5.3.3)':
dependencies:
'@hcengineering/communication-shared': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-shared': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
js-yaml: 4.1.0
typescript: 5.3.3
'@hcengineering/communication-yaml@0.1.172(typescript@5.7.3)':
'@hcengineering/communication-yaml@0.1.174(typescript@5.7.3)':
dependencies:
'@hcengineering/communication-shared': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-shared': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
js-yaml: 4.1.0
typescript: 5.7.3
'@hcengineering/core@0.6.490':
dependencies:
'@hcengineering/analytics': 0.6.490
'@hcengineering/platform': 0.6.490
fast-equals: 5.2.2
'@hcengineering/core@0.7.28':
dependencies:
'@hcengineering/analytics': 0.7.28
'@hcengineering/platform': 0.7.28
fast-equals: 5.2.2
'@hcengineering/drive@0.6.490':
dependencies:
'@hcengineering/core': 0.6.490
'@hcengineering/platform': 0.6.490
'@hcengineering/preference': 0.6.490
'@hcengineering/ui': 0.6.490
'@hcengineering/platform@0.6.490':
dependencies:
intl-messageformat: 10.7.14
'@hcengineering/platform@0.7.28':
dependencies:
intl-messageformat: 10.7.14
'@hcengineering/preference@0.6.490':
dependencies:
'@hcengineering/core': 0.6.490
'@hcengineering/platform': 0.6.490
'@hcengineering/ui': 0.6.490
'@hcengineering/preference@0.7.28':
dependencies:
'@hcengineering/core': 0.7.28
@@ -15107,35 +15084,12 @@ snapshots:
'@hcengineering/ui': 0.7.28
'@hcengineering/view': 0.7.28
'@hcengineering/theme@0.6.490':
dependencies:
'@hcengineering/analytics': 0.6.490
'@hcengineering/platform': 0.6.490
svelte: 4.2.19
'@hcengineering/theme@0.7.28':
dependencies:
'@hcengineering/analytics': 0.7.28
'@hcengineering/platform': 0.7.28
svelte: 4.2.19
'@hcengineering/ui@0.6.490':
dependencies:
'@hcengineering/analytics': 0.6.490
'@hcengineering/core': 0.6.490
'@hcengineering/platform': 0.6.490
'@hcengineering/theme': 0.6.490
autolinker: 4.0.0
date-fns: 2.30.0
date-fns-tz: 2.0.0(date-fns@2.30.0)
dompurify: 3.2.3
emoji-regex: 10.3.0
emojibase: 16.0.0
fast-equals: 5.2.2
hls.js: 1.5.20
plyr: 3.7.8
svelte: 4.2.19
'@hcengineering/ui@0.7.28':
dependencies:
'@hcengineering/analytics': 0.7.28
@@ -16536,7 +16490,7 @@ snapshots:
'@rush-temp/activity-resources@file:projects/activity-resources.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)(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:
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@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.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
@@ -17360,7 +17314,7 @@ snapshots:
'@rush-temp/card-resources@file:projects/card-resources.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@tiptap/pm@2.11.7)(@types/node@20.11.19)(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:
'@hcengineering/communication-types': 0.1.172(typescript@5.6.2)
'@hcengineering/communication-types': 0.1.174(typescript@5.6.2)
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
'@types/html-to-text': 8.1.1
'@types/jest': 29.5.12
@@ -17455,7 +17409,7 @@ snapshots:
'@rush-temp/chat-resources@file:projects/chat-resources.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)(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:
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@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)
@@ -17615,8 +17569,8 @@ snapshots:
'@rush-temp/client-resources@file:projects/client-resources.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:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@types/jest': 29.5.12
'@types/snappyjs': 0.7.1
'@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)
@@ -17775,6 +17729,99 @@ snapshots:
- y-prosemirror
- y-protocols
'@rush-temp/communication-assets@file:projects/communication-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/communication-resources@file:projects/communication-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)))(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))':
dependencies:
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@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)
autoprefixer: 10.4.17(postcss@8.5.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))
postcss: 8.5.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)
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.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-load-config
- pug
- stylus
- sugarss
- supports-color
- ts-node
'@rush-temp/communication@file:projects/communication.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)
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/contact-assets@file:projects/contact-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
@@ -19282,7 +19329,7 @@ snapshots:
'@rush-temp/inbox-resources@file:projects/inbox-resources.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)(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:
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@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)
@@ -20225,6 +20272,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@rush-temp/model-communication@file:projects/model-communication.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-contact@file:projects/model-contact.tgz(encoding@0.1.13)':
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.3.3)
@@ -22270,8 +22331,8 @@ snapshots:
'@rush-temp/pod-inbound-mail@file:projects/pod-inbound-mail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))':
dependencies:
'@hcengineering/communication-rest-client': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-rest-client': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@tsconfig/node16': 1.0.4
'@types/cors': 2.8.17
'@types/express': 4.17.21
@@ -22395,11 +22456,11 @@ snapshots:
'@rush-temp/pod-msg2file@file:projects/pod-msg2file.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))':
dependencies:
'@hcengineering/communication-rest-client': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-shared': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-yaml': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-rest-client': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-shared': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@hcengineering/communication-yaml': 0.1.174(typescript@5.7.3)
'@tsconfig/node16': 1.0.4
'@types/cors': 2.8.17
'@types/express': 4.17.21
@@ -22481,7 +22542,7 @@ snapshots:
'@rush-temp/pod-server@file:projects/pod-server.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))':
dependencies:
'@hcengineering/communication-server': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-server': 0.1.174(typescript@5.3.3)
'@types/body-parser': 1.19.5
'@types/cors': 2.8.17
'@types/express': 4.17.21
@@ -22934,9 +22995,9 @@ snapshots:
'@rush-temp/presentation@file:projects/presentation.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)(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:
'@hcengineering/communication-client-query': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-client-query': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@types/jest': 29.5.12
'@types/png-chunks-extract': 1.0.2
'@types/uuid': 8.3.4
@@ -23590,7 +23651,6 @@ snapshots:
'@rush-temp/recorder@file:projects/recorder.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:
'@hcengineering/drive': 0.6.490
'@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)
@@ -23943,8 +24003,8 @@ snapshots:
'@rush-temp/server-activity-resources@file:projects/server-activity-resources.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:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@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.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
@@ -24223,7 +24283,7 @@ snapshots:
'@rush-temp/server-card-resources@file:projects/server-card-resources.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:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.7.3)
'@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)
@@ -24506,8 +24566,8 @@ snapshots:
'@rush-temp/server-core@file:projects/server-core.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:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@types/jest': 29.5.12
'@types/node': 20.11.19
'@types/uuid': 8.3.4
@@ -25811,8 +25871,8 @@ snapshots:
'@rush-temp/server@file:projects/server.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:
'@hcengineering/communication-sdk-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.172(typescript@5.3.3)
'@hcengineering/communication-sdk-types': 0.1.174(typescript@5.3.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.3.3)
'@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.3.3)
@@ -27353,7 +27413,7 @@ snapshots:
'@rush-temp/ui-next@file:projects/ui-next.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)(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:
'@hcengineering/communication-types': 0.1.172(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.174(typescript@5.7.3)
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
'@tiptap/pm': 2.11.7
'@types/dompurify': 3.0.5
+3
View File
@@ -240,6 +240,9 @@
"@hcengineering/achievement": "^0.6.0",
"@hcengineering/achievement-assets": "^0.6.0",
"@hcengineering/achievement-resources": "^0.6.0",
"@hcengineering/communication": "^0.6.0",
"@hcengineering/communication-assets": "^0.6.0",
"@hcengineering/communication-resources": "^0.6.0",
"electron-squirrel-startup": "~1.0.0",
"dotenv": "~16.0.0",
"electron-context-menu": "^4.0.4",
+4
View File
@@ -66,6 +66,7 @@ import { mailId } from '@hcengineering/mail'
import { chatId } from '@hcengineering/chat'
import { inboxId } from '@hcengineering/inbox'
import { achievementId } from '@hcengineering/achievement'
import {communicationId} from '@hcengineering/communication'
import '@hcengineering/activity-assets'
import '@hcengineering/analytics-collector-assets'
@@ -117,6 +118,7 @@ import '@hcengineering/chat-assets'
import '@hcengineering/inbox-assets'
import '@hcengineering/achievement-assets'
import '@hcengineering/media-assets'
import '@hcengineering/communication-assets'
import analyticsCollector, { analyticsCollectorId } from '@hcengineering/analytics-collector'
import { coreId } from '@hcengineering/core'
@@ -230,6 +232,7 @@ function configureI18n (): void {
addStringsLoader(inboxId, async (lang: string) => await import(`@hcengineering/inbox-assets/lang/${lang}.json`))
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`))
}
export async function configurePlatform (): Promise<void> {
@@ -370,6 +373,7 @@ export async function configurePlatform (): Promise<void> {
addLocation(mailId, () => import(/* webpackChunkName: "card" */ '@hcengineering/mail-resources'))
addLocation(processId, () => import(/* webpackChunkName: "process" */ '@hcengineering/process-resources'))
addLocation(achievementId, () => import(/* webpackChunkName: "achievement" */ '@hcengineering/achievement-resources'))
addLocation(communicationId, () => import(/* webpackChunkName: "communication" */ '@hcengineering/communication-resources'))
setMetadata(client.metadata.FilterModel, 'ui')
setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin])
+3
View File
@@ -271,6 +271,9 @@
"@hcengineering/process": "^0.6.0",
"@hcengineering/process-assets": "^0.6.0",
"@hcengineering/process-resources": "^0.6.0",
"@hcengineering/communication": "^0.6.0",
"@hcengineering/communication-assets": "^0.6.0",
"@hcengineering/communication-resources": "^0.6.0",
"@sentry/svelte": "^8.52.1",
"posthog-js": "~1.122.0",
"readable-stream": "^4.7.0",
+4
View File
@@ -75,6 +75,7 @@ import github, { githubId } from '@hcengineering/github'
import { bitrixId } from '@hcengineering/bitrix'
import {inboxId} from '@hcengineering/inbox'
import {achievementId} from '@hcengineering/achievement'
import {communicationId} from '@hcengineering/communication'
import '@hcengineering/activity-assets'
import '@hcengineering/analytics-collector-assets'
@@ -128,6 +129,7 @@ import '@hcengineering/inbox-assets'
import '@hcengineering/mail-assets'
import '@hcengineering/github-assets'
import '@hcengineering/achievement-assets'
import '@hcengineering/communication-assets'
import { coreId } from '@hcengineering/core'
import presentation, {
@@ -362,6 +364,7 @@ function configureI18n(): void {
addStringsLoader(processId, async (lang: string) => await import(`@hcengineering/process-assets/lang/${lang}.json`))
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`))
}
export async function configurePlatform() {
@@ -564,6 +567,7 @@ export async function configurePlatform() {
addLocation(processId, () => import(/* webpackChunkName: "process" */ '@hcengineering/process-resources'))
addLocation(inboxId, () => import(/* webpackChunkName: "inbox" */ '@hcengineering/inbox-resources'))
addLocation(achievementId, () => import(/* webpackChunkName: "achievement" */ '@hcengineering/achievement-resources'))
addLocation(communicationId, () => import(/* webpackChunkName: "communication" */ '@hcengineering/communication-resources'))
setMetadata(client.metadata.FilterModel, 'ui')
setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin])
+2 -1
View File
@@ -123,6 +123,7 @@
"@hcengineering/model-process": "^0.6.0",
"@hcengineering/model-server-process": "^0.6.0",
"@hcengineering/model-inbox": "^0.6.0",
"@hcengineering/model-achievement": "^0.6.0"
"@hcengineering/model-achievement": "^0.6.0",
"@hcengineering/model-communication": "^0.6.0"
}
}
+2
View File
@@ -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 { communicationId, createModel as communicationModel } from '@hcengineering/model-communication'
import { type Plugin } from '@hcengineering/platform'
interface ConfigurablePlugin extends Omit<Data<PluginConfiguration>, 'pluginId' | 'transactions'> {}
@@ -458,6 +459,7 @@ export default function buildModel (): Builder {
[chatModel, chatId],
[inboxModel, inboxId],
[achievementModel, achievementId],
[communicationModel, communicationId],
[serverCoreModel, serverCoreId],
[serverAttachmentModel, serverAttachmentId],
+2
View File
@@ -59,6 +59,7 @@ import { aiBotId, aiBotOperation } from '@hcengineering/model-ai-bot'
import { chatId, chatOperation } from '@hcengineering/model-chat'
import { inboxId, inboxOperation } from '@hcengineering/model-inbox'
import { processId, processOperation } from '@hcengineering/model-process'
import { communicationId, communicationOperation } from '@hcengineering/model-communication'
import { recorderId, recorderOperation } from '@hcengineering/model-recorder'
export const migrateOperations: [string, MigrateOperation][] = [
@@ -108,5 +109,6 @@ export const migrateOperations: [string, MigrateOperation][] = [
[chatId, chatOperation],
[inboxId, inboxOperation],
[processId, processOperation],
[communicationId, communicationOperation],
[recorderId, recorderOperation]
]
+118 -7
View File
@@ -20,7 +20,10 @@ import {
type MasterTag,
type ParentInfo,
type Tag,
type Role
type Role,
type CardSection,
type CardViewDefaults,
type CardNavigation
} from '@hcengineering/card'
import chunter from '@hcengineering/chunter'
import core, {
@@ -34,7 +37,8 @@ import core, {
type CollectionSize,
type MarkupBlobRef,
type Rank,
type Ref
type Ref,
type MixinData
} from '@hcengineering/core'
import {
Collection,
@@ -45,14 +49,15 @@ import {
TypeRef,
TypeString,
UX,
type Builder
type Builder,
Mixin
} from '@hcengineering/model'
import attachment from '@hcengineering/model-attachment'
import { TAttachedDoc, TClass, TDoc, TMixin, TSpace } from '@hcengineering/model-core'
import presentation from '@hcengineering/model-presentation'
import setting from '@hcengineering/model-setting'
import view, { createAction } from '@hcengineering/model-view'
import workbench from '@hcengineering/model-workbench'
import workbench, { WidgetType } from '@hcengineering/model-workbench'
import { type Asset, getEmbeddedLabel, type IntlString } from '@hcengineering/platform'
import time, { type ToDo } from '@hcengineering/time'
import { type AnyComponent } from '@hcengineering/ui/src/types'
@@ -116,6 +121,20 @@ export class MasterTagEditorSection extends TDoc implements MasterTagEditorSecti
component!: AnyComponent
}
@Model(card.class.CardSection, core.class.Doc, DOMAIN_MODEL)
export class TCardSection extends TDoc implements CardSection {
label!: IntlString
component!: AnyComponent
order!: number
navigation!: CardNavigation[]
}
@Mixin(card.mixin.CardViewDefaults, card.class.MasterTag)
export class TCardViewDefaults extends TMasterTag implements CardViewDefaults {
defaultSection!: Ref<CardSection>
defaultNavigation?: string
}
@Model(card.class.Role, core.class.AttachedDoc, DOMAIN_MODEL)
export class TRole extends TAttachedDoc implements Role {
name!: string
@@ -154,7 +173,8 @@ export function createSystemType (
type: Ref<MasterTag>,
icon: Asset = card.icon.MasterTag,
label: IntlString,
pluralLabel?: IntlString
pluralLabel?: IntlString,
viewDefaults?: MixinData<MasterTag, CardViewDefaults>
): void {
builder.createDoc(
card.class.MasterTag,
@@ -209,13 +229,30 @@ export function createSystemType (
},
config: listConfig
})
if (viewDefaults !== undefined) {
builder.mixin(type, card.class.MasterTag, card.mixin.CardViewDefaults, viewDefaults)
}
}
export function createModel (builder: Builder): void {
builder.createModel(TMasterTag, TTag, TCard, MasterTagEditorSection, TCardSpace, TRole)
builder.createModel(
TMasterTag,
TTag,
TCard,
MasterTagEditorSection,
TCardSpace,
TRole,
TCardSection,
TCardViewDefaults
)
defineTabs(builder)
createSystemType(builder, card.types.File, card.icon.File, attachment.string.File, attachment.string.Files)
createSystemType(builder, card.types.Document, card.icon.Document, card.string.Document, card.string.Documents)
createSystemType(builder, card.types.Document, card.icon.Document, card.string.Document, card.string.Documents, {
defaultSection: card.section.Content
})
builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: card.class.CardSpace,
@@ -700,6 +737,80 @@ export function createModel (builder: Builder): void {
label: card.string.Views,
component: card.component.ViewsSection
})
builder.createDoc(
workbench.class.Widget,
core.space.Model,
{
label: card.string.Cards,
type: WidgetType.Flexible,
icon: card.icon.Card,
component: card.component.CardWidget
},
card.ids.CardWidget
)
}
function defineTabs (builder: Builder): void {
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: card.string.Properties,
component: card.sectionComponent.PropertiesSection,
order: 100,
navigation: []
},
card.section.Properties
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: card.string.Content,
component: card.sectionComponent.ContentSection,
order: 200,
navigation: []
},
card.section.Content
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: card.string.Children,
component: card.sectionComponent.ChildrenSection,
order: 300,
navigation: []
},
card.section.Children
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: attachment.string.Attachments,
component: card.sectionComponent.AttachmentsSection,
order: 400,
navigation: []
},
card.section.Attachments
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: core.string.Relations,
component: card.sectionComponent.RelationsSection,
order: 500,
navigation: []
},
card.section.Relations
)
}
export default card
+1
View File
@@ -29,6 +29,7 @@
},
"dependencies": {
"@hcengineering/card": "^0.6.0",
"@hcengineering/communication": "^0.6.0",
"@hcengineering/chat": "^0.6.0",
"@hcengineering/chat-resources": "^0.6.0",
"@hcengineering/core": "^0.6.32",
+13 -16
View File
@@ -18,8 +18,8 @@ import { type Builder } from '@hcengineering/model'
import core from '@hcengineering/model-core'
import workbench from '@hcengineering/model-workbench'
import { chatId } from '@hcengineering/chat'
import { WidgetType } from '@hcengineering/workbench'
import { createSystemType } from '@hcengineering/model-card'
import communication, { MessagesNavigationAnchors } from '@hcengineering/communication'
import chat from './plugin'
@@ -37,23 +37,20 @@ export function createModel (builder: Builder): void {
alias: chatId,
accessLevel: AccountRole.User,
hidden: true,
component: chat.component.ChatApplication
component: chat.component.ChatApplication,
locationResolver: chat.resolver.Location,
locationDataResolver: chat.resolver.LocationData
},
chat.app.Chat
)
builder.createDoc(
workbench.class.Widget,
core.space.Model,
{
label: chat.string.Chat,
type: WidgetType.Flexible,
icon: chat.icon.ChatBubble,
component: chat.component.ChatWidget
},
chat.ids.ChatWidget
)
createSystemType(builder, chat.masterTag.Thread, chat.icon.Thread, chat.string.Thread, chat.string.Threads)
createSystemType(builder, chat.masterTag.Channel, chat.icon.Channel, chat.string.Channel, chat.string.Channels)
// TODO: move types to communication-plugin
createSystemType(builder, chat.masterTag.Thread, chat.icon.Thread, chat.string.Thread, chat.string.Threads, {
defaultSection: communication.ids.CardMessagesSection,
defaultNavigation: MessagesNavigationAnchors.LatestMessages
})
createSystemType(builder, chat.masterTag.Channel, chat.icon.Channel, chat.string.Channel, chat.string.Channels, {
defaultSection: communication.ids.CardMessagesSection,
defaultNavigation: MessagesNavigationAnchors.LatestMessages
})
}
+7 -1
View File
@@ -17,10 +17,16 @@ import { chatId } from '@hcengineering/chat'
import chat from '@hcengineering/chat-resources/src/plugin'
import { type Ref } from '@hcengineering/core'
import { type Application } from '@hcengineering/model-workbench'
import { mergeIds } from '@hcengineering/platform'
import { mergeIds, type Resource } from '@hcengineering/platform'
import type { Location, ResolvedLocation } from '@hcengineering/ui'
import type { LocationData } from '@hcengineering/workbench'
export default mergeIds(chatId, chat, {
app: {
Chat: '' as Ref<Application>
},
resolver: {
Location: '' as Resource<(loc: Location) => Promise<ResolvedLocation | undefined>>,
LocationData: '' as Resource<(loc: Location) => Promise<LocationData>>
}
})
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}
+4
View File
@@ -0,0 +1,4 @@
*
!/lib/**
!CHANGELOG.md
/lib/**/__tests__/
+5
View File
@@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "model"
}
+41
View File
@@ -0,0 +1,41 @@
{
"name": "@hcengineering/model-communication",
"version": "0.6.0",
"main": "lib/index.js",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"author": "Copyright © 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/contact": "^0.6.24",
"@hcengineering/core": "^0.6.32",
"@hcengineering/model": "^0.6.11",
"@hcengineering/model-core": "^0.6.0",
"@hcengineering/platform": "^0.6.11",
"@hcengineering/card": "^0.6.0",
"@hcengineering/communication": "^0.6.0",
"@hcengineering/communication-resources": "^0.6.0",
"@hcengineering/ui": "^0.6.15"
}
}
+47
View File
@@ -0,0 +1,47 @@
// 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 { type Builder } from '@hcengineering/model'
import core from '@hcengineering/core'
import card from '@hcengineering/card'
import { MessagesNavigationAnchors } from '@hcengineering/communication'
import communication from './plugin'
export { communicationId } from '@hcengineering/communication'
export * from './migration'
export function createModel (builder: Builder): void {
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: communication.string.Messages,
component: communication.component.CardMessagesSection,
order: 9999,
navigation: [
{
id: MessagesNavigationAnchors.ConversationStart,
label: communication.string.FirstMessages
},
{
id: MessagesNavigationAnchors.LatestMessages,
label: communication.string.LatestMessages
}
]
},
communication.ids.CardMessagesSection
)
}
export default communication
@@ -13,11 +13,9 @@
// limitations under the License.
//
import { type Message } from '@hcengineering/communication-types'
import { type Card } from '@hcengineering/card'
import { type MigrateOperation } from '@hcengineering/model'
import { openThreadInSidebar } from './location'
export async function replyToThread (message: Message, parent: Card): Promise<void> {
await openThreadInSidebar(message, parent)
export const communicationOperation: MigrateOperation = {
async migrate (): Promise<void> {},
async upgrade (): Promise<void> {}
}
+22
View File
@@ -0,0 +1,22 @@
//
// 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 { communicationId } from '@hcengineering/communication'
import communication from '@hcengineering/communication-resources/src/plugin'
import {} from '@hcengineering/core'
import {} from '@hcengineering/ui'
import { mergeIds } from '@hcengineering/platform'
export default mergeIds(communicationId, communication, {})
+10
View File
@@ -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"
}
}
+1 -2
View File
@@ -63,7 +63,6 @@
export let hideActions: boolean = false
export let hideExtra: boolean = false
export let overflowExtra: boolean = false
export let newActivity: boolean = false
const account = getCurrentAccount()
$: isGuest = account.role === AccountRole.DocGuest
@@ -304,7 +303,7 @@
{#if showActivity}
{#key object._id}
<Component
is={newActivity ? activity.component.NewActivity : activity.component.Activity}
is={activity.component.Activity}
bind:innerRef={activityRef}
props={{
object,
+3 -3
View File
@@ -43,9 +43,9 @@
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/client": "^0.6.18",
"@hcengineering/collaborator-client": "^0.6.4",
"@hcengineering/communication-client-query": "0.1.172",
"@hcengineering/communication-sdk-types": "0.1.172",
"@hcengineering/communication-types": "0.1.172",
"@hcengineering/communication-client-query": "0.1.174",
"@hcengineering/communication-sdk-types": "0.1.174",
"@hcengineering/communication-types": "0.1.174",
"@hcengineering/core": "^0.6.32",
"@hcengineering/diffview": "^0.6.0",
"@hcengineering/notification": "^0.6.23",
+3 -1
View File
@@ -28,6 +28,8 @@
"Yesterday": "Yesterday",
"YesterdayAt": "Yesterday at {time}",
"AndMore": "and {count} more",
"IsTyping": "{count, plural, =1 {is typing} other {are typing}}..."
"IsTyping": "{count, plural, =1 {is typing} other {are typing}}...",
"Loading": "Loading...",
"MessageIn": "Message #{title}"
}
}
+1 -1
View File
@@ -42,7 +42,7 @@
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/attachment-resources": "^0.6.0",
"@hcengineering/card": "^0.6.0",
"@hcengineering/communication-types": "0.1.172",
"@hcengineering/communication-types": "0.1.174",
"@hcengineering/contact": "^0.6.24",
"@hcengineering/contact-resources": "^0.6.0",
"@hcengineering/core": "^0.6.32",
@@ -57,8 +57,8 @@
.date-separator {
display: flex;
width: 100%;
padding: 0 2rem;
background: var(--next-panel-color-background);
padding: 0 4rem;
background: var(--theme-panel-color);
&.sticky {
position: sticky;
@@ -231,7 +231,7 @@
.text-input__text {
overflow: auto;
min-height: 2.75rem;
min-height: 2.85rem;
padding: 0 0.25rem;
font-size: 0.875rem;
font-weight: 400;
@@ -14,7 +14,6 @@
-->
<script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { Markup, RateLimiter } from '@hcengineering/core'
import { tick, createEventDispatcher, onDestroy } from 'svelte'
import { uploadFile, deleteFile, getCommunicationClient } from '@hcengineering/presentation'
@@ -32,12 +31,11 @@
import { type TextInputAction, UploadedFile, type PresenceTyping } from '../../types'
import TypingPresenter from '../TypingPresenter.svelte'
export let cardId: CardID | undefined = undefined
export let cardType: CardType | undefined = undefined
export let cardId: CardID
export let cardType: CardType
export let message: Message | undefined = undefined
export let content: Markup | undefined = undefined
export let placeholder: IntlString | undefined = undefined
export let placeholderParams: Record<string, any> = {}
export let title: string = ''
export let onCancel: (() => void) | undefined = undefined
export let onSubmit: ((markdown: string, files: UploadedFile[]) => Promise<void>) | undefined = undefined
@@ -69,13 +67,13 @@
if (message === undefined) {
await createMessage(markdown, filesToLoad)
dispatch('sent')
} else {
await editMessage(message, markdown, filesToLoad)
}
}
async function createMessage (markdown: string, files: UploadedFile[]): Promise<void> {
if (cardId === undefined || cardType === undefined) return
const { id, created } = await communicationClient.createMessage(cardId, cardType, markdown)
for (const file of files) {
@@ -84,7 +82,6 @@
}
async function editMessage (message: Message, markdown: string, files: UploadedFile[]): Promise<void> {
if (cardId === undefined) return
await communicationClient.updateMessage(cardId, message.id, message.created, markdown)
for (const file of files) {
@@ -204,7 +201,6 @@
}
async function onUpdate (event: CustomEvent<Markup>): Promise<void> {
if (cardId === undefined || cardType === undefined) return
if (message !== undefined) return
const markup = event.detail
if (!isEmptyMarkup(markup)) {
@@ -235,8 +231,8 @@
/>
<TextInput
{content}
{placeholder}
{placeholderParams}
placeholder={title !== '' ? uiNext.string.MessageIn : undefined}
placeholderParams={title !== '' ? { title } : undefined}
loading={progress}
hasNonTextContent={files.length > 0}
actions={[...defaultMessageInputActions, attachAction]}
@@ -17,13 +17,14 @@
import { formatName, getPersonBySocialId, Person } from '@hcengineering/contact'
import { createEventDispatcher } from 'svelte'
import { getClient } from '@hcengineering/presentation'
import { Card } from '@hcengineering/card'
import { getCurrentAccount } from '@hcengineering/core'
import cardPlugin, { Card } from '@hcengineering/card'
import { getCurrentAccount, Ref } from '@hcengineering/core'
import { EmojiPopup, getEventPositionElement, showPopup, Action as MenuAction } from '@hcengineering/ui'
import { PersonPreviewProvider, personByPersonIdStore } from '@hcengineering/contact-resources'
import type { SocialID } from '@hcengineering/communication-types'
import { AttachmentPreview } from '@hcengineering/attachment-resources'
import { Message, MessageType } from '@hcengineering/communication-types'
import { openDoc } from '@hcengineering/view-resources'
import MessageContentViewer from './MessageContentViewer.svelte'
import { AvatarSize } from '../../types'
@@ -167,6 +168,16 @@
}
let isActionsOpened = false
async function handleReply () {
const t = message.thread
if (t === undefined) return
const _id = t.thread
const client = getClient()
const c = await client.findOne(cardPlugin.class.Card, { _id: _id as Ref<Card> })
if (c === undefined) return
await openDoc(client.getHierarchy(), c)
}
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
@@ -219,7 +230,8 @@
</div>
{:else}
<MessageInput
cardId={message.card}
cardId={card._id}
cardType={card._class}
{message}
content={toMarkup(message.content)}
onCancel={handleCancelEdit}
@@ -244,11 +256,7 @@
{/if}
{#if replies && message.thread && message.thread.repliesCount > 0}
<div class="message__replies overflow-label">
<MessageReplies
count={message.thread.repliesCount}
lastReply={message.thread.lastReply}
on:click={() => dispatch('reply', message)}
/>
<MessageReplies count={message.thread.repliesCount} lastReply={message.thread.lastReply} on:click={handleReply} />
</div>
{/if}
</div>
@@ -261,7 +269,7 @@
align-self: stretch;
min-width: 0;
position: relative;
padding: 1rem 2rem;
padding: 1rem 4rem;
&:hover:not(.noHover) {
background: var(--next-message-hover-color-background);
@@ -361,6 +369,7 @@
gap: 0.375rem;
overflow-x: auto;
margin-left: 2.75rem;
height: 18.75rem;
}
.message__actions {
@@ -25,9 +25,9 @@
export let card: Card
export let date: Timestamp
export let messages: Message[]
export let showDates = true
export let separatorDate: Date | undefined = undefined
export let separatorDiv: HTMLDivElement | undefined | null = undefined
export let readonly = false
const me = getCurrentAccount()
@@ -40,15 +40,16 @@
</script>
<div class="messages-group" id={date.toString()}>
{#if showDates}
<DateSeparator {date} />
{#if separatorIndex === 0}
<MessagesSeparator bind:element={separatorDiv} />
{/if}
<DateSeparator {date} />
<div class="messages-group__messages">
{#each messages as message, index (message.id)}
{#if index === separatorIndex}
{#if separatorIndex !== 0 && index === separatorIndex}
<MessagesSeparator bind:element={separatorDiv} />
{/if}
<MessagePresenter {message} {card} on:reply />
<MessagePresenter {message} {card} editable={!readonly} on:reply />
{/each}
</div>
</div>
@@ -13,13 +13,12 @@
// limitations under the License.
-->
<script lang="ts">
import { Label } from '@hcengineering/ui-next'
import chat from '../plugin'
import Label from '../Label.svelte'
import uiNext from '../../plugin'
</script>
<div class="loader">
<Label label={chat.string.Loading} />
<Label label={uiNext.string.Loading} />
</div>
<style lang="scss">
@@ -16,11 +16,15 @@
export let element: HTMLDivElement | undefined | null = undefined
</script>
<div class="w-full text-sm flex-center whitespace-nowrap" bind:this={element}>
<div class="w-full text-sm flex-center whitespace-nowrap container" bind:this={element}>
<div class="line mr-3" />
</div>
<style lang="scss">
.container {
padding: 0 4rem;
}
.line {
position: relative;
width: 100%;
+1
View File
@@ -32,3 +32,4 @@ export { default as NavigationList } from './components/NavigationList.svelte'
export { default as Section } from './components/Section.svelte'
export { default as Divider } from './components/Divider.svelte'
export { default as Button } from './components/Button.svelte'
export { default as MessagesLoading } from './components/message/MessagesLoading.svelte'
+3 -1
View File
@@ -47,7 +47,9 @@ export const uiNext = plugin(uiNextId, {
Yesterday: '' as IntlString,
YesterdayAt: '' as IntlString,
AndMore: '' as IntlString,
IsTyping: '' as IntlString
IsTyping: '' as IntlString,
Loading: '' as IntlString,
MessageIn: '' as IntlString
}
})
-9
View File
@@ -21,19 +21,10 @@ import { getCommunicationClient } from '@hcengineering/presentation'
import IconAt from './components/icons/IconAt.svelte'
import IconEmoji from './components/icons/IconEmoji.svelte'
import IconTextFont from './components/icons/IconTextFont.svelte'
import { type TextInputAction } from './types'
import uiNext from './plugin'
export const defaultMessageInputActions: TextInputAction[] = [
{
label: uiNext.string.ShowFormatting,
icon: IconTextFont,
action: (_element, editorHandler) => {
// TODO: implement
},
order: 2000
},
{
label: uiNext.string.Emoji,
icon: IconEmoji,
@@ -120,6 +120,7 @@
on:validate
on:submit
on:select
on:loaded
>
<slot />
</svelte:component>
@@ -140,6 +141,7 @@
on:validate
on:submit
on:select
on:loaded
/>
{/if}
</ErrorBoundary>
+1 -1
View File
@@ -51,7 +51,7 @@
"@hcengineering/ui-next": "^0.6.0",
"@hcengineering/view": "^0.6.13",
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/communication-types": "0.1.172",
"@hcengineering/communication-types": "0.1.174",
"svelte": "^4.2.19"
}
}
@@ -1,131 +0,0 @@
<!--
// 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.
-->
<script lang="ts">
import { SortingOrder } from '@hcengineering/core'
import { createMessagesQuery } from '@hcengineering/presentation'
import { Grid, Section, Spinner } from '@hcengineering/ui'
import { onDestroy, onMount } from 'svelte'
import activity from '@hcengineering/activity'
import { Card } from '@hcengineering/card'
import { MessagePresenter, MessageInput } from '@hcengineering/ui-next'
import { Message, Window } from '@hcengineering/communication-types'
export let object: Card
export let showInput: boolean = true
export let focusIndex: number = -1
export let boundary: HTMLElement | undefined = undefined
const query = createMessagesQuery()
let isLoading = true
let isNextLoading = false
let messages: Message[] = []
let window: Window<Message> | undefined
$: query.query(
{
card: object._id,
replies: true,
files: true,
reactions: true,
order: SortingOrder.Ascending,
limit: 100
},
(res) => {
messages = res.getResult()
isLoading = false
window = res
}
)
function handleScroll (event: Event): void {
const element = event.target as HTMLElement
if (element != null && window != null && window.hasNextPage() && !isNextLoading) {
const isAtBottom = element.scrollTop + element.clientHeight >= element.scrollHeight - 200
if (isAtBottom) {
isNextLoading = true
void window.loadNextPage().finally(() => {
isNextLoading = false
})
}
}
}
onMount(() => {
if (!boundary) return
boundary.addEventListener('scroll', handleScroll)
})
onDestroy(() => {
if (!boundary) return
boundary.removeEventListener('scroll', handleScroll)
})
</script>
<Section label={activity.string.Activity} icon={activity.icon.Activity}>
<svelte:fragment slot="header">
{#if isLoading}
<div class="ml-1">
<Spinner size="small" />
</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="content">
<div class="p-activity select-text" id={activity.string.Activity}>
{#if messages.length}
<Grid column={1} rowGap={1}>
{#each messages as message (message.id)}
<MessagePresenter {message} card={object} />
{/each}
</Grid>
{/if}
</div>
{#if showInput}
<div class="ref-input oldest-first">
<MessageInput cardId={object._id} cardType={object._class} placeholder={activity.string.Message} />
</div>
{/if}
</svelte:fragment>
</Section>
<style lang="scss">
.ref-input {
flex-shrink: 0;
&.newest-first {
margin-bottom: 1rem;
padding-top: 1rem;
}
&.oldest-first {
padding-bottom: 2.5rem;
}
}
.p-activity {
&.newest-first {
padding-bottom: 1.75rem;
}
&:not(.newest-first) {
margin: 1.75rem 0;
}
}
:global(.grid .msgactivity-container.showIcon:last-child::after) {
content: none;
}
</style>
-2
View File
@@ -16,7 +16,6 @@
import { type Resources } from '@hcengineering/platform'
import Activity from './components/Activity.svelte'
import NewActivity from './components/NewActivity.svelte'
import ActivityMessagePresenter from './components/activity-message/ActivityMessagePresenter.svelte'
import DocUpdateMessagePresenter from './components/doc-update-message/DocUpdateMessagePresenter.svelte'
import ActivityInfoMessagePresenter from './components/activity-info-message/ActivityInfoMessagePresenter.svelte'
@@ -68,7 +67,6 @@ export { default as BasePreview } from './components/BasePreview.svelte'
export default async (): Promise<Resources> => ({
component: {
Activity,
NewActivity,
ActivityMessagePresenter,
DocUpdateMessagePresenter,
ReactionPresenter,
-1
View File
@@ -326,7 +326,6 @@ export default plugin(activityId, {
},
component: {
Activity: '' as AnyComponent,
NewActivity: '' as AnyComponent,
ActivityMessagePresenter: '' as AnyComponent,
DocUpdateMessagePresenter: '' as AnyComponent,
ActivityInfoMessagePresenter: '' as AnyComponent,
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "Vybrat typ",
"CreateSpace": "Vytvořit prostor",
"NumberTypes": "{count, plural, one {# typ} few {# typy} other {# typů}}",
"Labels": "Štítky"
"Labels": "Štítky",
"Properties": "Vlastnosti"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "Typ auswählen",
"CreateSpace": "Raum erstellen",
"NumberTypes": "{count, plural, one {# typ} other {# typen}}",
"Labels": "Labels"
"Labels": "Labels",
"Properties": "Eigenschaften"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "Select Type",
"CreateSpace": "Create space",
"NumberTypes": "{count, plural, one {# type} other {# types}}",
"Labels": "Labels"
"Labels": "Labels",
"Properties": "Properties"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "Seleccionar tipo",
"CreateSpace": "Crear espacio",
"NumberTypes": "{count, plural, one {# tipo} other {# tipos}}",
"Labels": "Etiquetas"
"Labels": "Etiquetas",
"Properties": "Propiedades"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "Sélectionner le type",
"CreateSpace": "Créer un espace",
"NumberTypes": "{count, plural, one {# type} other {# types}}",
"Labels": "Étiquettes"
"Labels": "Étiquettes",
"Properties": "Propriétés"
}
}
+2 -1
View File
@@ -35,6 +35,7 @@
"MasterDetailViews": "Viste Master-Detail",
"SelectType": "Seleziona il tipo",
"CreateSpace": "Crea spazio",
"Labels": "Etichette"
"Labels": "Etichette",
"Properties": "Proprietà"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "タイプを選択",
"CreateSpace": "スペースを作成",
"NumberTypes": "{count, plural, one {# タイプ} other {# タイプ}}",
"Labels": "ラベル"
"Labels": "ラベル",
"Properties": "プロパティ"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "Selecionar tipo",
"CreateSpace": "Criar espaço",
"NumberTypes": "{count, plural, one {# tipo} other {# tipos}}",
"Labels": "Etiquetas"
"Labels": "Etiquetas",
"Properties": "Propriedades"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "Выбрать тип",
"CreateSpace": "Создать пространство",
"NumberTypes": "{count, plural, one {# тип} few {# типа} other {# типов}}",
"Labels": "Метки"
"Labels": "Метки",
"Properties": "Свойства"
}
}
+2 -1
View File
@@ -36,6 +36,7 @@
"SelectType": "选择类型",
"CreateSpace": "创建空间",
"NumberTypes": "{count, plural, one {# 类型} other {# 类型}}",
"Labels": "标签"
"Labels": "标签",
"Properties": "属性"
}
}
+2 -1
View File
@@ -39,8 +39,9 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@hcengineering/presence": "^0.6.0",
"@hcengineering/presentation": "^0.6.3",
"@hcengineering/communication-types": "0.1.172",
"@hcengineering/communication-types": "0.1.174",
"@hcengineering/core": "^0.6.32",
"@hcengineering/ui": "^0.6.15",
"@hcengineering/setting": "^0.6.17",
@@ -0,0 +1,104 @@
<!--
// 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.
-->
<script lang="ts">
import { closeWidget, WidgetState } from '@hcengineering/workbench-resources'
import { Widget } from '@hcengineering/workbench'
import { createQuery, createNotificationContextsQuery } from '@hcengineering/presentation'
import { NotificationContext } from '@hcengineering/communication-types'
import { createEventDispatcher } from 'svelte'
import { Card } from '@hcengineering/card'
import { Ref } from '@hcengineering/core'
import { Button, Header, IconClose } from '@hcengineering/ui'
import EditCardTableOfContents from './EditCardTableOfContents.svelte'
import card from '../plugin'
import { CardWidgetData } from '../utils'
import CardPresenter from './CardPresenter.svelte'
export let widget: Widget | undefined
export let widgetState: WidgetState | undefined
export let height: string
export let width: string
const query = createQuery()
const contextsQuery = createNotificationContextsQuery()
const dispatch = createEventDispatcher()
let data: CardWidgetData | undefined = undefined
let doc: Card | undefined = undefined
let context: NotificationContext | undefined = undefined
let isContextLoaded = false
$: data = widgetState?.data as CardWidgetData
$: if (widget === undefined || data?._id === undefined) {
closeWidget(card.ids.CardWidget as Ref<Widget>)
}
$: data._id &&
query.query(
card.class.Card,
{ _id: data._id },
(res) => {
if (res.length === 0) {
closeWidget(card.ids.CardWidget as Ref<Widget>)
} else {
doc = res[0]
}
},
{ limit: 1 }
)
$: data._id &&
contextsQuery.query({ card: data._id, limit: 1 }, (res) => {
context = res.getResult()[0]
isContextLoaded = true
})
</script>
{#if widget && data?._id}
<div class="card-widget" style:width style:height>
<Header type={'type-panel'} noPrint adaptive="disabled">
<svelte:fragment slot="beforeTitle">
<Button
id={'btnPClose'}
focusIndex={10001}
icon={IconClose}
iconProps={{ size: 'medium' }}
kind={'icon'}
noPrint
on:click={() => {
dispatch('close')
}}
/>
</svelte:fragment>
<CardPresenter value={doc} noUnderline />
</Header>
{#if doc}
<EditCardTableOfContents {doc} {context} {isContextLoaded} readonly={false} />
{/if}
</div>
{/if}
<style lang="scss">
.card-widget {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
min-height: 0;
}
</style>
@@ -22,6 +22,7 @@
export let doc: Card
export let readonly: boolean = false
export let content: HTMLElement
export let showToc: boolean = true
const client = getClient()
const hierarchy = client.getHierarchy()
@@ -43,4 +44,4 @@
/>
{/each}
<Description {doc} {readonly} bind:content minHeight={isFile ? '15vh' : '25vh'} />
<Description {doc} {readonly} {showToc} bind:content minHeight={isFile ? '15vh' : '25vh'} on:loaded on:headings />
@@ -23,14 +23,18 @@
import { navigate } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { getObjectLinkFragment } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import ContentEditor from './ContentEditor.svelte'
export let doc: Card
export let readonly: boolean = false
export let content: HTMLElement
export let minHeight: '15vh' | '25vh' = '25vh'
export let showToc: boolean = true
const client = getClient()
const dispatch = createEventDispatcher()
let editor: ContentEditor
@@ -70,18 +74,20 @@
</script>
<div class="content select-text mt-4">
<div class="toc-container">
<div class="toc">
<TableOfContents
items={headings}
on:select={(evt) => {
const heading = evt.detail
const element = window.document.getElementById(heading.id)
element?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}}
/>
{#if showToc}
<div class="toc-container">
<div class="toc">
<TableOfContents
items={headings}
on:select={(evt) => {
const heading = evt.detail
const element = window.document.getElementById(heading.id)
element?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}}
/>
</div>
</div>
</div>
{/if}
{#key doc._id}
<ContentEditor
@@ -96,6 +102,7 @@
}}
on:headings={(evt) => {
headings = evt.detail
dispatch('headings', headings)
}}
on:open-document={async (event) => {
const doc = await client.findOne(event.detail._class, { _id: event.detail._id })
@@ -105,6 +112,7 @@
}
}}
bind:this={editor}
on:loaded
/>
{/key}
</div>
@@ -15,193 +15,25 @@
//
-->
<script lang="ts">
import { Analytics } from '@hcengineering/analytics'
import { Attachments } from '@hcengineering/attachment-resources'
import { Card, CardEvents } from '@hcengineering/card'
import { Doc, Mixin, Ref, WithLookup } from '@hcengineering/core'
import notification from '@hcengineering/notification'
import { Panel } from '@hcengineering/panel'
import { getResource } from '@hcengineering/platform'
import { ComponentExtensions, createQuery, getClient } from '@hcengineering/presentation'
import {
Button,
EditBox,
FocusHandler,
IconMoreH,
createFocusManager,
getCurrentLocation,
navigate
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { ParentsNavigator, RelationsEditor, getDocMixins, showMenu } from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import card from '../plugin'
import CardAttributeEditor from './CardAttributeEditor.svelte'
import CardPresenter from './CardPresenter.svelte'
import Childs from './Childs.svelte'
import Content from './Content.svelte'
import TagsEditor from './TagsEditor.svelte'
import { Ref } from '@hcengineering/core'
import { onMount } from 'svelte'
import { Analytics } from '@hcengineering/analytics'
import EditCardNew from './EditCardNew.svelte'
export let _id: Ref<Card>
export let readonly: boolean = false
export let embedded: boolean = false
let useMaxWidth = getUseMaxWidth()
$: saveUseMaxWidth(useMaxWidth)
export let allowClose: boolean = true
export function canClose (): boolean {
return false
}
let lastId: Ref<Doc> = _id
const query = createQuery()
const dispatch = createEventDispatcher()
const client = getClient()
let doc: WithLookup<Card> | undefined
let title = ''
let innerWidth: number
const notificationClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
$: read(_id)
function read (_id: Ref<Doc>): void {
if (lastId !== _id) {
const prev = lastId
lastId = _id
void notificationClient.then((client) => client.readDoc(prev))
}
}
onDestroy(async () => {
void notificationClient.then((client) => client.readDoc(_id))
})
$: _id !== undefined &&
query.query(card.class.Card, { _id }, async (result) => {
if (result.length > 0) {
;[doc] = result
title = doc?.title ?? ''
} else {
const loc = getCurrentLocation()
loc.path.length = 3
navigate(loc)
}
})
$: canSave = title.trim().length > 0
async function saveTitle (ev: Event): Promise<void> {
ev.preventDefault()
if (doc === undefined || !canSave) {
return
}
const nameTrimmed = title.trim()
if (nameTrimmed.length > 0 && nameTrimmed !== doc.title) {
await client.update(doc, { title: nameTrimmed })
}
}
function getUseMaxWidth (): boolean {
const useMaxWidth = localStorage.getItem('document.useMaxWidth')
return useMaxWidth === 'true'
}
function saveUseMaxWidth (useMaxWidth: boolean): void {
localStorage.setItem('document.useMaxWidth', useMaxWidth.toString())
}
onMount(() => {
Analytics.handleEvent(CardEvents.CardOpened, { id: _id })
})
let content: HTMLElement
const manager = createFocusManager()
let mixins: Array<Mixin<Doc>> = []
$: mixins = doc !== undefined ? getDocMixins(doc) : []
</script>
<FocusHandler {manager} />
{#if doc !== undefined}
<Panel
object={doc}
allowClose={!embedded}
isAside={false}
isHeader={false}
isSub={false}
bind:useMaxWidth
printHeader={false}
{embedded}
adaptive={'default'}
bind:content
bind:innerWidth
floatAside={false}
newActivity
on:open
on:close={() => dispatch('close')}
>
<svelte:fragment slot="title">
<ParentsNavigator element={doc} />
<CardPresenter value={doc} noUnderline />
</svelte:fragment>
<div class="container">
<div class="title flex-row-center">
<EditBox focusIndex={1} bind:value={title} placeholder={card.string.Card} on:blur={(evt) => saveTitle(evt)} />
</div>
<TagsEditor {doc} />
<CardAttributeEditor value={doc} {mixins} {readonly} ignoreKeys={['title', 'content', 'parent']} />
<Content {doc} {readonly} bind:content />
</div>
<ComponentExtensions
extension={card.extensions.EditCardExtension}
props={{
card: doc
}}
/>
<Childs object={doc} {readonly} />
<RelationsEditor object={doc} {readonly} />
<Attachments objectId={doc._id} _class={doc._class} space={doc.space} attachments={doc.attachments ?? 0} />
<svelte:fragment slot="utils">
{#if !readonly}
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
dataId={'btnMoreActions'}
on:click={(e) => {
showMenu(e, { object: doc, excludedActions: [view.action.Open] })
}}
/>
{/if}
</svelte:fragment>
</Panel>
{/if}
<style lang="scss">
.container {
display: flex;
flex-direction: column;
width: 100%;
margin: auto;
}
.title {
font-size: 2.25rem;
margin-top: 1.75rem;
margin-bottom: 1rem;
}
</style>
<EditCardNew {_id} {readonly} {embedded} {allowClose} on:close on:open />
@@ -0,0 +1,197 @@
<!--
//
// 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.
//
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { Ref, WithLookup } from '@hcengineering/core'
import {
Button,
Component,
createFocusManager,
EditBox,
FocusHandler,
getCurrentLocation,
IconDetailsFilled,
IconMoreH,
navigate,
Panel
} from '@hcengineering/ui'
import presence from '@hcengineering/presence'
import { createQuery, createNotificationContextsQuery, getClient } from '@hcengineering/presentation'
import { ParentsNavigator, showMenu } from '@hcengineering/view-resources'
import view from '@hcengineering/view'
import { NotificationContext } from '@hcengineering/communication-types'
import card from '../plugin'
import CardPresenter from './CardPresenter.svelte'
import { openCardInSidebar } from '../utils'
import EditCardTableOfContents from './EditCardTableOfContents.svelte'
import { translate } from '@hcengineering/platform'
import { makeRank } from '@hcengineering/rank'
import TagsEditor from './TagsEditor.svelte'
export let _id: Ref<Card>
export let readonly: boolean = false
export let embedded: boolean = false
export let allowClose: boolean = true
const manager = createFocusManager()
const query = createQuery()
const contextsQuery = createNotificationContextsQuery()
let doc: WithLookup<Card> | undefined
let context: NotificationContext | undefined = undefined
let isContextLoaded = false
let title: string = ''
let prevId: Ref<Card> = _id
$: if (prevId !== _id) {
prevId = _id
context = undefined
isContextLoaded = false
}
$: query.query(card.class.Card, { _id }, async (result) => {
if (result.length > 0) {
;[doc] = result
title = doc.title
} else {
const loc = getCurrentLocation()
loc.path.length = 3
navigate(loc)
}
})
$: contextsQuery.query({ card: _id, limit: 1 }, (res) => {
context = res.getResult()[0]
isContextLoaded = true
})
async function saveTitle (ev: Event): Promise<void> {
ev.preventDefault()
const client = getClient()
const canSave = title.trim().length > 0
if (doc === undefined || !canSave) {
return
}
const nameTrimmed = title.trim()
if (nameTrimmed.length > 0 && nameTrimmed !== doc.title) {
await client.update(doc, { title: nameTrimmed })
}
}
</script>
<FocusHandler {manager} />
{#if doc !== undefined}
<Panel isAside={false} isHeader={false} {embedded} {allowClose} adaptive="disabled" on:open on:close>
<div class="main-content clear-mins">
{#key doc._id}
<EditCardTableOfContents {doc} {readonly} {context} {isContextLoaded} />
{/key}
</div>
<svelte:fragment slot="title">
<ParentsNavigator element={doc} />
<div class="title flex-row-center">
<EditBox focusIndex={1} bind:value={title} placeholder={card.string.Card} on:blur={saveTitle} />
</div>
<TagsEditor {doc} />
</svelte:fragment>
<svelte:fragment slot="presence">
<Component is={presence.component.PresenceAvatars} props={{ object: doc, size: 'x-small', limit: 5 }} />
</svelte:fragment>
<svelte:fragment slot="pre-utils">
<slot name="pre-utils" />
</svelte:fragment>
<svelte:fragment slot="panel-footer">
<slot name="panel-footer" />
</svelte:fragment>
<svelte:fragment slot="utils">
<!-- <Button-->
<!-- icon={IconDetailsFilled}-->
<!-- iconProps={{ size: 'medium' }}-->
<!-- kind="icon"-->
<!-- on:click={() => {-->
<!-- if (doc != null) {-->
<!-- void openCardInSidebar(doc._id, doc)-->
<!-- }-->
<!-- }}-->
<!-- />-->
{#if !readonly}
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind="icon"
dataId="btnMoreActions"
on:click={(e) => {
showMenu(e, { object: doc, excludedActions: [view.action.Open] })
}}
/>
{/if}
</svelte:fragment>
<svelte:fragment slot="header">
{#if $$slots.header}
<div class="header-row between">
{#if $$slots.header}
<slot name="header" />
{/if}
<div class="buttons-group xsmall-gap ml-4" style:align-self={'flex-start'}>
<slot name="tools" />
</div>
</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="post-utils">
<slot name="post-utils" />
</svelte:fragment>
<svelte:fragment slot="extra">
<slot name="extra" />
</svelte:fragment>
<svelte:fragment slot="page-header">
<slot name="page-header" />
</svelte:fragment>
<svelte:fragment slot="page-footer">
<slot name="page-footer" />
</svelte:fragment>
</Panel>
{/if}
<style lang="scss">
.main-content {
display: flex;
flex-direction: column;
flex: 1;
}
.title {
font-size: 1rem;
flex: 1;
min-width: 10rem;
}
</style>
@@ -0,0 +1,198 @@
<!--
//
// 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.
//
-->
<script lang="ts">
import { Attachments } from '@hcengineering/attachment-resources'
import { Card } from '@hcengineering/card'
import { Doc, Mixin, Ref, WithLookup } from '@hcengineering/core'
import notification from '@hcengineering/notification'
import { Panel } from '@hcengineering/panel'
import { getResource } from '@hcengineering/platform'
import { ComponentExtensions, createQuery, getClient } from '@hcengineering/presentation'
import {
Button,
EditBox,
FocusHandler,
IconMoreH,
createFocusManager,
getCurrentLocation,
navigate
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { ParentsNavigator, RelationsEditor, getDocMixins, showMenu } from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy } from 'svelte'
import card from '../plugin'
import CardAttributeEditor from './CardAttributeEditor.svelte'
import CardPresenter from './CardPresenter.svelte'
import Childs from './Childs.svelte'
import Content from './Content.svelte'
import TagsEditor from './TagsEditor.svelte'
export let _id: Ref<Card>
export let readonly: boolean = false
export let embedded: boolean = false
let useMaxWidth = getUseMaxWidth()
$: saveUseMaxWidth(useMaxWidth)
let lastId: Ref<Doc> = _id
const query = createQuery()
const dispatch = createEventDispatcher()
const client = getClient()
let doc: WithLookup<Card> | undefined
let title = ''
let innerWidth: number
const notificationClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
$: read(_id)
function read (_id: Ref<Doc>): void {
if (lastId !== _id) {
const prev = lastId
lastId = _id
void notificationClient.then((client) => client.readDoc(prev))
}
}
onDestroy(async () => {
void notificationClient.then((client) => client.readDoc(_id))
})
$: _id !== undefined &&
query.query(card.class.Card, { _id }, async (result) => {
if (result.length > 0) {
;[doc] = result
title = doc?.title ?? ''
} else {
const loc = getCurrentLocation()
loc.path.length = 3
navigate(loc)
}
})
$: canSave = title.trim().length > 0
async function saveTitle (ev: Event): Promise<void> {
ev.preventDefault()
if (doc === undefined || !canSave) {
return
}
const nameTrimmed = title.trim()
if (nameTrimmed.length > 0 && nameTrimmed !== doc.title) {
await client.update(doc, { title: nameTrimmed })
}
}
function getUseMaxWidth (): boolean {
const useMaxWidth = localStorage.getItem('document.useMaxWidth')
return useMaxWidth === 'true'
}
function saveUseMaxWidth (useMaxWidth: boolean): void {
localStorage.setItem('document.useMaxWidth', useMaxWidth.toString())
}
let content: HTMLElement
const manager = createFocusManager()
let mixins: Array<Mixin<Doc>> = []
$: mixins = doc !== undefined ? getDocMixins(doc) : []
</script>
<FocusHandler {manager} />
{#if doc !== undefined}
<Panel
object={doc}
allowClose={!embedded}
isAside={false}
isHeader={false}
isSub={false}
bind:useMaxWidth
printHeader={false}
{embedded}
adaptive={'default'}
bind:content
bind:innerWidth
floatAside={false}
on:open
on:close={() => dispatch('close')}
>
<svelte:fragment slot="title">
<ParentsNavigator element={doc} />
<CardPresenter value={doc} noUnderline />
</svelte:fragment>
<div class="container">
<div class="title flex-row-center">
<EditBox focusIndex={1} bind:value={title} placeholder={card.string.Card} on:blur={(evt) => saveTitle(evt)} />
</div>
<TagsEditor {doc} />
<CardAttributeEditor value={doc} {mixins} {readonly} ignoreKeys={['title', 'content', 'parent']} />
<Content {doc} {readonly} bind:content />
</div>
<ComponentExtensions
extension={card.extensions.EditCardExtension}
props={{
card: doc
}}
/>
<Childs object={doc} {readonly} />
<RelationsEditor object={doc} {readonly} />
<Attachments objectId={doc._id} _class={doc._class} space={doc.space} attachments={doc.attachments ?? 0} />
<svelte:fragment slot="utils">
{#if !readonly}
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
dataId={'btnMoreActions'}
on:click={(e) => {
showMenu(e, { object: doc, excludedActions: [view.action.Open] })
}}
/>
{/if}
</svelte:fragment>
</Panel>
{/if}
<style lang="scss">
.container {
display: flex;
flex-direction: column;
width: 100%;
margin: auto;
}
.title {
font-size: 2.25rem;
margin-top: 1.75rem;
margin-bottom: 1rem;
}
</style>
@@ -0,0 +1,287 @@
<!--
// 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.
-->
<script lang="ts">
import card, { Card, CardSection, CardViewDefaults } from '@hcengineering/card'
import { Component, Loading, Scroller } from '@hcengineering/ui'
import { getClient } from '@hcengineering/presentation'
import { NotificationContext } from '@hcengineering/communication-types'
import { MessageInput } from '@hcengineering/ui-next'
import { Ref } from '@hcengineering/core'
import { SvelteComponent } from 'svelte'
import { TableOfContents } from '@hcengineering/text-editor-resources'
import { Heading } from '@hcengineering/text-editor'
import { CardSectionAction } from '../types'
export let doc: Card
export let context: NotificationContext | undefined = undefined
export let isContextLoaded: boolean = false
export let readonly: boolean = false
const client = getClient()
const sections: CardSection[] = client.getModel().findAllSync(card.class.CardSection, {})
let defaults: CardViewDefaults | undefined = undefined
$: defaults = client.getHierarchy().classHierarchyMixin(doc._class, card.mixin.CardViewDefaults)
let selectedToc: Heading | undefined = undefined
const sectionElement: Record<Ref<CardSection>, HTMLElement | undefined> = {}
const sectionRef: Record<Ref<CardSection>, SvelteComponent | undefined> = {}
const sectionLoaded: Record<Ref<CardSection>, boolean | undefined> = {}
let sectionOverlays: Record<Ref<CardSection>, boolean> = {}
let toc: Heading[] = []
let subTocBySection: Record<string, Heading[]> = {}
let scrollDiv: HTMLDivElement | undefined | null = undefined
let isScrollInitialized = false
let showOverlay = false
function onNavigationClick (heading: Heading): void {
selectedToc = heading
navigate()
}
function navigate (): void {
if (selectedToc === undefined) return
const sectionId = selectedToc.group as Ref<CardSection>
const ref = sectionRef[sectionId]
const isInnerNavigation = selectedToc.id !== sectionId
if (ref?.navigate && isInnerNavigation) {
ref.navigate(selectedToc.id)
} else {
sectionElement[sectionId]?.scrollIntoView()
}
isScrollInitialized = true
}
function handleSectionLoaded (_id: Ref<CardSection>): void {
const wasLoaded = sectionLoaded[_id] === true
sectionLoaded[_id] = true
if (!wasLoaded && selectedToc?.group === _id) {
navigate()
}
}
function handleChangeNavigation (section: Ref<CardSection>, id: string): void {
if (selectedToc?.group !== section) return
selectedToc = toc.find((it) => it.group === section && it.id === id)
}
function handleAction (section: Ref<CardSection>, action: CardSectionAction): void {
if (action.id === 'toc') {
subTocBySection[section] = action.toc
subTocBySection = subTocBySection
}
if (action.id === 'overlay') {
sectionOverlays[section] = action.show
sectionOverlays = sectionOverlays
}
}
function updateToc (sections: CardSection[], tocBySection: Record<string, Heading[]>): void {
const newToc: Heading[] = []
for (const section of sections) {
const subToc = tocBySection[section._id]
if (section.navigation.length > 0) {
newToc.push(
...section.navigation.map((nav) => ({
id: nav.id,
titleIntl: nav.label,
level: 0,
group: section._id
}))
)
} else {
newToc.push({
id: section._id,
titleIntl: section.label,
level: 0,
group: section._id
})
}
if (subToc !== undefined) {
newToc.push(...subToc.map((it) => ({ ...it, group: section._id })))
}
}
toc = newToc
if (selectedToc == null || newToc.findIndex((it) => it.id === selectedToc?.id) === -1) {
selectedToc =
newToc.find(
(it) =>
it.group === defaults?.defaultSection &&
(defaults?.defaultNavigation === undefined || it.id === defaults?.defaultNavigation)
) ?? newToc[0]
isScrollInitialized = isScrollInitialized ?? selectedToc.id === newToc[0].id
}
}
function onMessageSent (): void {
if (scrollDiv == null) return
const lastTocItem = toc[toc.length - 1]
if (lastTocItem === undefined) return
if (selectedToc?.group === lastTocItem.group && selectedToc?.id === lastTocItem.id) {
scrollDiv?.scroll({ top: scrollDiv.scrollHeight, behavior: 'instant' })
} else {
selectedToc = lastTocItem
navigate()
}
}
function handleScroll (): void {
if (scrollDiv == null || showOverlay) return
const allLoaded = sections.every(({ _id }) => sectionLoaded[_id] === true)
if (!allLoaded) return
const scrollTop = scrollDiv.scrollTop ?? 0
const offsets: Array<{ _id: Ref<CardSection>, offsetTop: number }> = []
sections.forEach(({ _id }) => {
const sectionRef = sectionElement[_id]
if (sectionRef != null && scrollDiv != null) {
offsets.push({ _id, offsetTop: sectionRef.offsetTop })
}
})
offsets.sort((a, b) => a.offsetTop - b.offsetTop)
const selectedSectionId = offsets.filter((it) => it.offsetTop <= scrollTop + 50).pop()?._id
if (selectedSectionId != null && selectedSectionId !== selectedToc?.group) {
selectedToc = toc.find((it) => it.group === selectedSectionId)
}
}
$: updateToc(sections, subTocBySection)
$: showOverlay = !isScrollInitialized || Object.values(sectionOverlays).some((it) => it)
</script>
{#if showOverlay}
<div class="overlay">
<Loading />
</div>
{/if}
<div class="hulyComponent-content__container columns">
<div class="hulyComponent-content__column content">
<div class="toc-container">
<div class="toc">
<TableOfContents
items={toc}
selected={toc.find((it) => it.id === selectedToc?.id)}
on:select={(evt) => {
onNavigationClick(evt.detail)
}}
/>
</div>
</div>
<Scroller
padding="0"
bottomPadding="var(--spacing-3)"
disablePointerEventsOnScroll
bind:divScroll={scrollDiv}
onScroll={handleScroll}
>
<div class="hulyComponent-content gap withoutMaxWidth">
{#each sections as section, i}
{@const isLoadingBefore = sections.slice(0, i).some((section) => sectionLoaded[section._id] === undefined)}
<div id={section._id} bind:this={sectionElement[section._id]} class="section">
<Component
is={section.component}
bind:innerRef={sectionRef[section._id]}
showLoading={false}
props={{
doc,
readonly,
isContextLoaded,
isLoadingBefore,
scrollDiv,
notificationContext: context,
contentDiv: sectionElement[section._id],
active: selectedToc?.group === section._id,
navigation: selectedToc?.id
}}
on:loaded={() => {
handleSectionLoaded(section._id)
}}
on:change={(ev) => {
handleChangeNavigation(section._id, ev.detail)
}}
on:action={(ev) => {
handleAction(section._id, ev.detail)
}}
/>
</div>
{/each}
</div>
</Scroller>
{#if !readonly}
<div class="message-input">
<MessageInput cardId={doc._id} cardType={doc._class} title={doc.title} on:sent={onMessageSent} />
</div>
{/if}
</div>
</div>
<style lang="scss">
.message-input {
display: flex;
width: 100%;
flex-direction: column;
align-items: flex-start;
padding: 0 0.75rem;
min-height: 8.5rem;
max-height: 50%;
}
.overlay {
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--theme-panel-color);
}
.toc-container {
position: absolute;
pointer-events: none;
inset: 0;
z-index: 1;
top: 0;
left: 0.5rem;
}
.toc {
width: 2rem;
pointer-events: all;
position: sticky;
}
.section {
display: flex;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
}
</style>
@@ -53,7 +53,7 @@
const filter = (it: Doc): boolean => {
const card = it as Card
return !card.parentInfo.some((p) => cards.has(p._id))
return !card.parentInfo?.some((p) => cards.has(p._id))
}
</script>
@@ -0,0 +1,51 @@
<!--
// 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.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { Attachments } from '@hcengineering/attachment-resources'
import { createEventDispatcher, onMount } from 'svelte'
export let readonly: boolean = false
export let doc: Card
const dispatch = createEventDispatcher()
onMount(() => {
if ((doc.attachments ?? 0) === 0) {
dispatch('loaded')
}
})
</script>
<div class="section-attachments">
<Attachments
objectId={doc._id}
_class={doc._class}
space={doc.space}
attachments={doc.attachments ?? 0}
{readonly}
on:attachments={() => dispatch('loaded')}
/>
</div>
<style lang="scss">
.section-attachments {
display: flex;
flex-direction: column;
width: 100%;
padding: 0 4rem;
}
</style>
@@ -0,0 +1,45 @@
<!--
// 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.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { onMount, createEventDispatcher } from 'svelte'
import Childs from '../Childs.svelte'
export let readonly: boolean = false
export let doc: Card
const dispatch = createEventDispatcher()
onMount(() => {
if ((doc.children ?? 0) === 0) {
dispatch('loaded')
}
})
</script>
<div class="section-children">
<Childs object={doc} {readonly} on:docs={() => dispatch('loaded')} />
</div>
<style lang="scss">
.section-children {
display: flex;
flex-direction: column;
width: 100%;
padding: 0 4rem;
}
</style>
@@ -0,0 +1,60 @@
<!--
// 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.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { Heading } from '@hcengineering/text-editor'
import { createEventDispatcher, onMount } from 'svelte'
import Content from '../Content.svelte'
import { CardSectionAction } from '../../types'
export let readonly: boolean = false
export let doc: Card
export let contentDiv: HTMLDivElement | undefined | null = undefined
const dispatch = createEventDispatcher()
export function navigate (id: string): void {
const element = window.document.getElementById(id)
element?.scrollIntoView({ behavior: 'instant', block: 'start' })
}
function handleHeadings (ev: CustomEvent<Heading[]>): void {
const headings = ev.detail
const action: CardSectionAction = { id: 'toc', toc: headings }
dispatch('action', action)
}
onMount(() => {
dispatch('action', { id: 'toc', toc: [] })
})
</script>
{#if contentDiv != null}
<div class="content">
<Content {doc} {readonly} content={contentDiv} showToc={false} on:loaded on:headings={handleHeadings} />
</div>
{/if}
<style lang="scss">
.content {
display: flex;
flex-direction: column;
width: 100%;
padding: 0 5rem;
flex: 1;
}
</style>
@@ -0,0 +1,47 @@
<!--
// 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.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { Doc, Mixin } from '@hcengineering/core'
import { getDocMixins } from '@hcengineering/view-resources'
import { createEventDispatcher, onMount } from 'svelte'
import CardAttributeEditor from '../CardAttributeEditor.svelte'
export let readonly: boolean = false
export let doc: Card
const dispatch = createEventDispatcher()
let mixins: Array<Mixin<Doc>> = []
$: mixins = getDocMixins(doc)
onMount(() => {
dispatch('loaded')
})
</script>
<div class="properties">
<CardAttributeEditor value={doc} {mixins} {readonly} ignoreKeys={['title', 'content', 'parent']} />
</div>
<style lang="scss">
.properties {
display: flex;
flex-direction: column;
width: 100%;
padding: 0 4rem;
}
</style>
@@ -14,19 +14,22 @@
-->
<script lang="ts">
import { MessageInput, UploadedFile } from '@hcengineering/ui-next'
import { Card } from '@hcengineering/card'
import { RelationsEditor } from '@hcengineering/view-resources'
import chat from '../plugin'
export let card: Card | undefined
export let onSubmit: ((markdown: string, files: UploadedFile[]) => Promise<void>) | undefined = undefined
export let readonly: boolean = false
export let doc: Card
</script>
<MessageInput
cardId={card?._id}
cardType={card?._class}
placeholder={chat.string.MessageIn}
placeholderParams={{ title: card?.title ?? '' }}
{onSubmit}
/>
<div class="section-relations">
<RelationsEditor object={doc} {readonly} on:loaded />
</div>
<style lang="scss">
.section-relations {
display: flex;
flex-direction: column;
width: 100%;
padding: 0 4rem;
}
</style>
+17 -1
View File
@@ -49,6 +49,14 @@ import SpacePresenter from './components/navigator/SpacePresenter.svelte'
import LabelsPresenter from './components/LabelsPresenter.svelte'
import RolesSection from './components/settings/RolesSection.svelte'
import EditRole from './components/settings/EditRole.svelte'
import CardWidget from './components/CardWidget.svelte'
// Card Sections
import AttachmentsCardSection from './components/sections/AttachmentsSection.svelte'
import ChildrenCardSection from './components/sections/ChildrenSection.svelte'
import ContentCardSection from './components/sections/ContentSection.svelte'
import PropertiesCardSection from './components/sections/PropertiesSection.svelte'
import RelationsCardSection from './components/sections/RelationsSection.svelte'
export { default as CardSelector } from './components/CardSelector.svelte'
export { default as Navigator } from './components/navigator-next/Navigator.svelte'
@@ -83,7 +91,15 @@ export default async (): Promise<Resources> => ({
SpacePresenter,
LabelsPresenter,
RolesSection,
EditRole
EditRole,
CardWidget
},
sectionComponent: {
AttachmentsSection: AttachmentsCardSection,
ChildrenSection: ChildrenCardSection,
ContentSection: ContentCardSection,
PropertiesSection: PropertiesCardSection,
RelationsSection: RelationsCardSection
},
completion: {
CardQuery: queryCard
+11 -2
View File
@@ -48,7 +48,15 @@ export default mergeIds(cardId, card, {
SpacePresenter: '' as AnyComponent,
LabelsPresenter: '' as AnyComponent,
RolesSection: '' as AnyComponent,
EditRole: '' as AnyComponent
EditRole: '' as AnyComponent,
CardWidget: '' as AnyComponent
},
sectionComponent: {
AttachmentsSection: '' as AnyComponent,
ChildrenSection: '' as AnyComponent,
ContentSection: '' as AnyComponent,
PropertiesSection: '' as AnyComponent,
RelationsSection: '' as AnyComponent
},
completion: {
CardQuery: '' as Resource<ObjectSearchFactory>,
@@ -90,6 +98,7 @@ export default mergeIds(cardId, card, {
SelectType: '' as IntlString,
CreateSpace: '' as IntlString,
NumberTypes: '' as IntlString,
Labels: '' as IntlString
Labels: '' as IntlString,
Properties: '' as IntlString
}
})
+13
View File
@@ -14,6 +14,7 @@
import type { Ref } from '@hcengineering/core'
import type { MasterTag } from '@hcengineering/card'
import type { LabelID } from '@hcengineering/communication-types'
import { type Heading } from '@hcengineering/text-editor'
interface BaseNavigatorConfig {
types: Array<Ref<MasterTag>>
@@ -37,3 +38,15 @@ export interface CardsNavigatorConfig extends BaseNavigatorConfig {
}
export type NavigatorConfig = TypesNavigatorConfig | CardsNavigatorConfig
export interface CardTocAction {
id: 'toc'
toc: Heading[]
}
export interface CardOverlayAction {
id: 'overlay'
show: boolean
}
export type CardSectionAction = CardTocAction | CardOverlayAction
+27 -2
View File
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { type Card, CardEvents, cardId, type CardSpace, type MasterTag } from '@hcengineering/card'
import { type Card, CardEvents, cardId, type CardSpace, type CardSection, type MasterTag } from '@hcengineering/card'
import {
type Class,
type Client,
@@ -38,10 +38,11 @@ import {
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { accessDeniedStore } from '@hcengineering/view-resources'
import { type LocationData } from '@hcengineering/workbench'
import workbench, { type LocationData } from '@hcengineering/workbench'
import { translate } from '@hcengineering/platform'
import { makeRank } from '@hcengineering/rank'
import { Analytics } from '@hcengineering/analytics'
import { openWidget } from '@hcengineering/workbench-resources'
import CardSearchItem from './components/CardSearchItem.svelte'
import CreateSpace from './components/navigator/CreateSpace.svelte'
@@ -227,3 +228,27 @@ export function sortNavigatorTypes (types: MasterTag[], config: NavigatorConfig)
return a.label.localeCompare(b.label)
})
}
export interface CardWidgetData {
_id: Ref<Card>
name: string
tab?: Ref<CardSection>
}
export async function openCardInSidebar (cardId: Ref<Card>, doc?: Card, tabId?: Ref<CardSection>): Promise<void> {
const client = getClient()
const widget = client.getModel().findAllSync(workbench.class.Widget, { _id: card.ids.CardWidget as any })[0]
if (widget === undefined) return
const object = doc ?? (await client.findOne(card.class.Card, { _id: cardId }))
if (object === undefined) return
const data: CardWidgetData = {
_id: cardId,
name: object.title,
tab: tabId
}
openWidget(widget, data)
}
+32 -1
View File
@@ -71,6 +71,23 @@ export interface MasterTagEditorSection extends Doc {
masterOnly?: boolean
}
export interface CardNavigation {
id: string
label: IntlString
}
export interface CardSection extends Doc {
label: IntlString
component: AnyComponent
order: number
navigation: CardNavigation[]
}
export interface CardViewDefaults extends MasterTag {
defaultSection: Ref<CardSection>
defaultNavigation?: string
}
/**
* @public
*/
@@ -88,7 +105,11 @@ const cardPlugin = plugin(cardId, {
Tag: '' as Ref<Class<Tag>>,
MasterTagEditorSection: '' as Ref<Class<MasterTagEditorSection>>,
CardSpace: '' as Ref<Class<CardSpace>>,
Role: '' as Ref<Class<Role>>
Role: '' as Ref<Class<Role>>,
CardSection: '' as Ref<Class<CardSection>>
},
mixin: {
CardViewDefaults: '' as Ref<Mixin<CardViewDefaults>>
},
space: {
Default: '' as Ref<CardSpace>
@@ -119,6 +140,16 @@ const cardPlugin = plugin(cardId, {
Cards: '' as IntlString,
CardApplication: '' as IntlString,
Views: '' as IntlString
},
section: {
Attachments: '' as Ref<CardSection>,
Children: '' as Ref<CardSection>,
Content: '' as Ref<CardSection>,
Properties: '' as Ref<CardSection>,
Relations: '' as Ref<CardSection>
},
ids: {
CardWidget: '' as Ref<Doc>
}
})
+1 -1
View File
@@ -42,7 +42,7 @@
"@hcengineering/card": "^0.6.0",
"@hcengineering/card-resources": "^0.6.0",
"@hcengineering/chat": "^0.6.0",
"@hcengineering/communication-types": "0.1.172",
"@hcengineering/communication-types": "0.1.174",
"@hcengineering/contact": "^0.6.24",
"@hcengineering/contact-resources": "^0.6.0",
"@hcengineering/core": "^0.6.32",
@@ -21,21 +21,27 @@
deviceOptionsStore as deviceInfo,
resolvedLocationStore,
Location,
restoreLocation
restoreLocation,
Component
} from '@hcengineering/ui'
import { onDestroy } from 'svelte'
import { getClient } from '@hcengineering/presentation'
import { chatId } from '@hcengineering/chat'
import { Ref } from '@hcengineering/core'
import { Ref, Doc, Class } from '@hcengineering/core'
import view from '@hcengineering/view'
import ChatPanel from './ChatPanel.svelte'
import ChatNavigation from './ChatNavigation.svelte'
import { navigateToCard, getCardIdFromLocation, navigateToType, getTypeIdFromLocation } from '../location'
import ChatNavigationCategoryList from './ChatNavigationCategoryList.svelte'
type Selection = { type: 'card', card: Card } | { type: 'type', ref: Ref<MasterTag> }
interface Selection {
_class: Ref<Class<Doc>>
_id: Ref<Doc>
doc: Doc
}
const client = getClient()
const hierarchy = client.getHierarchy()
let replacedPanelElement: HTMLElement
let selection: Selection | undefined = undefined
@@ -50,9 +56,10 @@
}
const typeId = getTypeIdFromLocation(loc)
const type = typeId != null ? await client.findOne(cardPlugin.class.MasterTag, { _id: typeId }) : undefined
if (typeId != null && typeId !== '') {
selection = { type: 'type', ref: typeId }
if (type != null) {
selection = { _class: cardPlugin.class.MasterTag, _id: type._id, doc: type }
return
}
@@ -71,33 +78,34 @@
if (cardId !== selectedCard?._id) {
const card = await client.findOne(cardPlugin.class.Card, { _id: cardId })
selection = card != null ? { type: 'card', card } : undefined
selection = card != null ? { _class: cardPlugin.class.Card, _id: cardId, doc: card } : undefined
}
}
function selectCard (event: CustomEvent<Card>): void {
if (selection?._id === event.detail._id) return
const card = event.detail
selection = { type: 'card', card }
selection = { _class: cardPlugin.class.Card, _id: card._id, doc: card }
navigateToCard(card._id)
}
function selectType (event: CustomEvent<MasterTag>): void {
if (selection && selection.type === 'type' && selection.ref === event.detail._id) return
if (selection?._id === event.detail._id) return
const type = event.detail
selection = { type: 'type', ref: type._id }
selection = { _class: cardPlugin.class.MasterTag, _id: type._id, doc: type }
navigateToType(type._id)
}
function getSelectedCard (selection: Selection | undefined): Card | undefined {
if (selection == null) return undefined
if (selection.type !== 'card') return undefined
return selection.card
if (selection._class !== cardPlugin.class.Card) return undefined
return selection.doc as Card | undefined
}
function getSelectedType (selection: Selection | undefined): Ref<MasterTag> | undefined {
if (selection == null) return undefined
if (selection.type !== 'type') return undefined
return selection.ref
if (selection._class !== cardPlugin.class.MasterTag) return undefined
return selection._id as Ref<MasterTag>
}
onDestroy(
@@ -146,7 +154,9 @@
{/if}
<div bind:this={replacedPanelElement} class="hulyComponent chat__panel">
{#if selectedCard}
<ChatPanel card={selectedCard} />
{@const panelComponent = hierarchy.classHierarchyMixin(selectedCard._class, view.mixin.ObjectPanel)}
{@const comp = panelComponent?.component ?? view.component.EditDoc}
<Component is={comp} props={{ _id: selectedCard._id, readonly: false, embedded: true, allowClose: false }} />
{:else if selectedType}
<ChatNavigationCategoryList type={selectedType} />
{/if}
@@ -155,18 +165,18 @@
<style lang="scss">
.chat {
background: var(--next-background-color);
border-color: var(--next-border-color);
background: var(--theme-navpanel-color);
border-color: var(--theme-divider-color);
}
.chat__navigator {
background: var(--next-background-color);
border-color: var(--next-border-color);
background: var(--theme-navpanel-color);
border-color: var(--theme-divider-color);
}
.chat__panel {
background: var(--next-panel-color-background);
border-color: var(--next-panel-color-border);
background: var(--theme-panel-color);
border-color: var(--theme-divider-color);
position: relative;
}
</style>
@@ -1,331 +0,0 @@
<!--
// 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.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { type Message, Window, NotificationContext } from '@hcengineering/communication-types'
import { createMessagesQuery, getCommunicationClient } from '@hcengineering/presentation'
import { MessagesGroup as MessagesGroupPresenter } from '@hcengineering/ui-next'
import { Scroller } from '@hcengineering/ui'
import { getCurrentAccount, SortingOrder } from '@hcengineering/core'
import { tick, onDestroy } from 'svelte'
import ReverseScroller from './internal/ReverseScroller.svelte'
import { createMessagesObserver, getGroupDay, groupMessagesByDay, MessagesGroup } from '../ui'
import { replyToThread } from '../actions'
import ChatLoadingFiller from './ChatLoadingFiller.svelte'
export let card: Card | undefined = undefined
export let context: NotificationContext | undefined = undefined
export let bottomStart: boolean = true
export let footerHeight: number | undefined = undefined
export let showDates: boolean = true
export let overlyColor: string | undefined = undefined
const me = getCurrentAccount()
const communicationClient = getCommunicationClient()
const query = createMessagesQuery()
const loadPageThreshold = 200
const scrollToNewThreshold = 50
const initialLastView = context?.lastView
const initialLastUpdate = context?.lastUpdate
let contentDiv: HTMLDivElement | null | undefined = undefined
let scrollDiv: HTMLDivElement | null | undefined = undefined
let scroller: Scroller | undefined | null = undefined
let separatorDiv: HTMLDivElement | null | undefined = undefined
let messages: Message[] = []
let groups: MessagesGroup[] = []
let window: Window<Message> | undefined = undefined
let isLoading = true
let messagesCount = 0
let isScrollInitialized = false
let isPageLoading = false
let shouldScrollToNew = true
let restore: { scrollTop: number, scrollHeight: number } | undefined = undefined
$: if (card != null) {
const limit = 50
const unread = initialLastView != null && initialLastUpdate != null && initialLastUpdate > initialLastView
const order = unread ? SortingOrder.Ascending : SortingOrder.Descending
query.query(
{
card: card._id,
replies: true,
files: true,
reactions: true,
order,
limit,
created:
unread && initialLastView != null
? {
greaterOrEqual: initialLastView
}
: undefined
},
(res: Window<Message>) => {
window = res
messages = order === SortingOrder.Ascending ? res.getResult() : res.getResult().reverse()
if (messages.length < limit && res.hasNextPage()) {
void window.loadNextPage()
} else if (messages.length < limit && res.hasPrevPage()) {
void window.loadPrevPage()
}
groups = groupMessagesByDay(messages)
isLoading = messages.length < limit && (res.hasNextPage() || res.hasPrevPage())
void onUpdate(messages)
}
)
} else {
isLoading = true
window = undefined
groups = []
query.unsubscribe()
}
function updateShouldScrollToNew (): void {
if (scrollDiv != null && contentDiv != null) {
const { scrollTop } = scrollDiv
shouldScrollToNew = Math.abs(scrollTop) < scrollToNewThreshold
}
}
function shouldLoadPrevPage (): boolean {
if (scrollDiv == null) return false
const { scrollHeight, scrollTop, clientHeight } = scrollDiv
return scrollHeight + Math.ceil(scrollTop - clientHeight) <= loadPageThreshold
}
function shouldLoadNextPage (): boolean {
if (scrollDiv == null) return false
return Math.abs(scrollDiv.scrollTop) <= loadPageThreshold
}
function loadMore (): void {
if (window === undefined) return
if (shouldLoadPrevPage() && window.hasPrevPage()) {
void loadPrevPage()
} else if (shouldLoadNextPage() && window.hasNextPage()) {
void loadNextPage()
}
}
async function loadPrevPage (): Promise<void> {
if (window === undefined || isPageLoading || scrollDiv == null) return
if ((restore?.scrollTop ?? 0) !== 0) return
try {
isPageLoading = true
shouldScrollToNew = false
await window.loadPrevPage()
restore = {
scrollTop: scrollDiv?.scrollTop ?? 0,
scrollHeight: 0
}
} finally {
isPageLoading = false
}
}
async function loadNextPage (): Promise<void> {
if (window === undefined || isPageLoading || scrollDiv == null) return
if ((restore?.scrollHeight ?? 0) !== 0) return
try {
isPageLoading = true
shouldScrollToNew = false
await window.loadNextPage()
restore = {
scrollTop: 0,
scrollHeight: scrollDiv?.scrollHeight ?? 0
}
} finally {
isPageLoading = false
}
}
function scrollToBottom (): void {
if (scrollDiv != null) {
scrollDiv.scroll({ top: 0, behavior: 'instant' })
}
}
function restoreScroll (): void {
if (scrollDiv == null || scroller == null || restore == null) return
if (restore.scrollTop !== 0) {
scroller.scroll(restore.scrollTop)
}
if (restore.scrollHeight !== 0) {
const delta = restore.scrollHeight - scrollDiv.scrollHeight
scroller.scroll(delta)
}
restore = undefined
}
function handleScroll (): void {
loadMore()
updateShouldScrollToNew()
}
async function onUpdate (res: Message[]): Promise<void> {
if (messagesCount === res.length) return
const prevCount = messagesCount
messagesCount = res.length
if (prevCount > messagesCount) return
await tick()
restoreScroll()
if (shouldScrollToNew && prevCount > 0) {
scrollToBottom()
}
}
let newLastView: Date | undefined = context?.lastView
let separatorDate: Date | undefined = undefined
let readMessagesTimer: any | undefined = undefined
let unsubscribeObserver: (() => void) | undefined = undefined
function readMessage (date: Date): void {
if (readMessagesTimer != null) {
clearTimeout(readMessagesTimer)
readMessagesTimer = undefined
}
readMessagesTimer = setTimeout(async () => {
if (context == null || context.lastView >= date) return
await communicationClient.updateNotificationContext(context.id, date)
}, 500)
}
$: initMessageObserver(contentDiv, isScrollInitialized, context)
function initMessageObserver (
contentDiv: HTMLDivElement | undefined | null,
isScrollInitialized: boolean,
context: NotificationContext | undefined
): void {
if (contentDiv == null || !isScrollInitialized || context == null) return
if (unsubscribeObserver != null) return
unsubscribeObserver = createMessagesObserver(contentDiv, (messageDiv) => {
const id = messageDiv.id
const message = messages.find((it) => it.id === id)
if (message === undefined) return
const shouldRead = newLastView == null || message.created > newLastView
if (shouldRead) {
newLastView = message.created
readMessage(message.created)
}
})
}
async function handleReply (event: CustomEvent<Message>): Promise<void> {
if (card == null) return
const message = event.detail
await replyToThread(message, card)
}
$: void initializeScroll(isLoading, separatorDiv)
async function initializeScroll (isLoading: boolean, separatorDiv?: HTMLDivElement | null): Promise<void> {
if (isLoading || isScrollInitialized) return
const separatorIndex = initialLastView
? messages.findIndex(({ created, creator }) => !me.socialIds.includes(creator) && created > initialLastView)
: -1
if (separatorIndex === -1) {
isScrollInitialized = true
separatorDate = undefined
return
}
separatorDate = messages[separatorIndex].created
if (separatorDiv != null) {
await tick() // Wait for the DOM to update
separatorDiv.scrollIntoView({ behavior: 'instant', block: 'start' })
if (scrollDiv && scrollDiv.scrollTop < -100) {
scroller?.scrollBy(-50)
}
isScrollInitialized = true
updateShouldScrollToNew()
}
}
onDestroy(() => {
if (unsubscribeObserver != null) {
unsubscribeObserver()
}
})
</script>
<ReverseScroller
bind:scrollDiv
bind:scroller
bind:contentDiv
{overlyColor}
isLoading={card != null && (isLoading || !isScrollInitialized)}
{bottomStart}
onScroll={handleScroll}
>
{#if card}
{#if window !== undefined && window.hasPrevPage()}
<ChatLoadingFiller />
{/if}
{#each groups as group (group.day.toString())}
{@const withSeparator = separatorDate != null && getGroupDay(separatorDate) === group.day}
{#if withSeparator}
<MessagesGroupPresenter
bind:separatorDiv
{card}
date={group.day}
messages={group.messages}
{showDates}
{separatorDate}
on:reply={handleReply}
/>
{:else}
<MessagesGroupPresenter {card} date={group.day} messages={group.messages} {showDates} on:reply={handleReply} />
{/if}
{/each}
{#if window !== undefined && window.hasNextPage()}
<ChatLoadingFiller />
{/if}
{#if footerHeight != null && footerHeight > 0}
<div class="filler" style:height={`${footerHeight}px`} />
{/if}
{/if}
</ReverseScroller>
<style lang="scss">
.filler {
display: flex;
width: 100%;
}
</style>
@@ -1,55 +0,0 @@
<!--
// 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.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { UploadedFile } from '@hcengineering/ui-next'
import ChatInput from './ChatInput.svelte'
export let card: Card | undefined = undefined
export let height: number | undefined = undefined
export let onSubmit: ((markdown: string, files: UploadedFile[]) => Promise<void>) | undefined = undefined
let _height: number | undefined | null
$: if (_height != null) {
// Add 1 for the border
height = _height + 1
}
</script>
<div class="chat__footer" bind:clientHeight={_height}>
<ChatInput {card} {onSubmit} />
</div>
<style lang="scss">
.chat__footer {
display: flex;
width: 100%;
flex-direction: column;
align-items: flex-start;
padding: 1.25rem 0.75rem;
padding-bottom: 0;
border-top: 1px solid var(--next-divider-color);
backdrop-filter: blur(5px);
min-height: 100px;
position: absolute;
bottom: 0;
left: 0;
max-height: 50%;
}
</style>
@@ -1,67 +0,0 @@
<!--
// 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.
-->
<script lang="ts">
import cardPlugin, { Card } from '@hcengineering/card'
import { Header, Icon } from '@hcengineering/ui'
import { getClient } from '@hcengineering/presentation'
import { Asset } from '@hcengineering/platform'
import { PresenceAvatars } from '@hcengineering/presence-resources'
export let card: Card | undefined = undefined
export let canClose: boolean = false
export let title: string | undefined = undefined
export let icon: Asset | undefined = undefined
const client = getClient()
const hierarchy = client.getHierarchy()
$: clazz = card != null ? hierarchy.getClass(card._class) : undefined
</script>
<Header
allowFullsize={false}
type={canClose ? 'type-aside' : 'type-component'}
adaptive="disabled"
hideBefore={false}
closeOnEscape={false}
on:close
>
<div class="hulyHeader-titleGroup">
<div class="content-color mr-2 pl-2">
<Icon icon={clazz?.icon ?? icon ?? cardPlugin.icon.Card} size={'small'} />
</div>
<span class="secondary-textColor overflow-label heading-medium-16 line-height-auto mr-2"
>{card?.title ?? title}</span
>
</div>
<svelte:fragment slot="presence">
{#if card}
<PresenceAvatars object={card} size="x-small" limit={5} />
{/if}
</svelte:fragment>
</Header>
<style lang="scss">
.chat-header {
display: flex;
width: 100%;
height: 4rem;
min-height: 4rem;
max-height: 4rem;
border-bottom: 1px solid var(--next-panel-color-border);
}
</style>
@@ -1,54 +0,0 @@
<!--
// 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.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { NotificationContext } from '@hcengineering/communication-types'
import { createNotificationContextsQuery } from '@hcengineering/presentation'
import { Presence } from '@hcengineering/presence-resources'
import ChatHeader from './ChatHeader.svelte'
import ChatBody from './ChatBody.svelte'
import ChatFooter from './ChatFooter.svelte'
export let card: Card
const contextsQuery = createNotificationContextsQuery()
let footerHeight: number | undefined = undefined
let context: NotificationContext | undefined = undefined
let isLoaded = false
let cardId = card._id
$: if (cardId !== card._id) {
cardId = card._id
context = undefined
isLoaded = false
}
$: contextsQuery.query({ card: cardId, limit: 1 }, (res) => {
context = res.getResult()[0]
isLoaded = true
})
</script>
<Presence object={card} />
<ChatHeader {card} />
{#if isLoaded}
{#key card._id}
<ChatBody {card} {footerHeight} {context} />
{/key}
{/if}
<ChatFooter {card} bind:height={footerHeight} />
@@ -1,186 +0,0 @@
<!--
// 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.
-->
<script lang="ts">
import { closeWidget, WidgetState } from '@hcengineering/workbench-resources'
import { Widget } from '@hcengineering/workbench'
import { getClient, createQuery, getCommunicationClient, createMessagesQuery } from '@hcengineering/presentation'
import cardPlugin, { type Card } from '@hcengineering/card'
import { Message, CardID } from '@hcengineering/communication-types'
import { MessagePresenter, MessageInput, Divider, UploadedFile } from '@hcengineering/ui-next'
import { fillDefaults, MarkupBlobRef, Ref, SortingOrder } from '@hcengineering/core'
import { makeRank } from '@hcengineering/rank'
import { employeeByPersonIdStore } from '@hcengineering/contact-resources'
import { getEmployeeBySocialId } from '@hcengineering/contact'
import { ChatWidgetData } from '../types'
import chat from '../plugin'
import ChatHeader from './ChatHeader.svelte'
import ChatBody from './ChatBody.svelte'
import { createThreadTitle } from '../utils'
import ChatFooter from './ChatFooter.svelte'
export let widget: Widget | undefined
export let widgetState: WidgetState | undefined
export let height: string
export let width: string
const client = getClient()
const hierarchy = client.getHierarchy()
const communicationClient = getCommunicationClient()
const messageQuery = createMessagesQuery()
const threadCardQuery = createQuery()
const parentCardQuery = createQuery()
let data: ChatWidgetData | undefined = undefined
let threadCard: Card | undefined = undefined
let parentCard: Card | undefined = undefined
let message: Message | undefined = undefined
$: data = widgetState?.data as ChatWidgetData
$: if (data !== undefined) {
messageQuery.query(
{
card: data.card,
created: data.created,
id: data.message,
limit: 1,
replies: true,
files: true,
reactions: true
},
(res) => {
message = res.getResult()[0]
}
)
parentCardQuery.query(
cardPlugin.class.Card,
{ _id: data.card as Ref<Card> },
(res) => {
parentCard = res[0]
},
{ limit: 1 }
)
} else {
message = undefined
parentCard = undefined
messageQuery.unsubscribe()
parentCardQuery.unsubscribe()
}
$: if (message?.thread != null) {
threadCardQuery.query(
cardPlugin.class.Card,
{ _id: message.thread.thread as Ref<Card> },
(res) => {
threadCard = res[0]
},
{ limit: 1 }
)
} else {
threadCard = undefined
threadCardQuery.unsubscribe()
}
$: if (widget === undefined || data === undefined) {
closeWidget(chat.ids.ChatWidget)
}
async function handleSubmit (markdown: string, files: UploadedFile[]): Promise<void> {
if (message === undefined || data === undefined) return
const { card } = data
if (card === undefined || parentCard === undefined) return
let threadId: CardID | undefined = message.thread?.thread
if (threadId == null) {
const author =
$employeeByPersonIdStore.get(message.creator) ?? (await getEmployeeBySocialId(client, message.creator))
const lastOne = await client.findOne(cardPlugin.class.Card, {}, { sort: { rank: SortingOrder.Descending } })
const title = createThreadTitle(message, parentCard)
const data = fillDefaults(
hierarchy,
{
title,
rank: makeRank(lastOne?.rank, undefined),
content: '' as MarkupBlobRef,
parent: parentCard._id
},
chat.masterTag.Thread
)
threadId = (await client.createDoc(chat.masterTag.Thread, cardPlugin.space.Default, data)) as CardID
await communicationClient.createThread(card, message.id, message.created, threadId)
if (author?.active === true && author?.personUuid !== undefined) {
await communicationClient.addCollaborators(threadId, chat.masterTag.Thread, [author.personUuid])
}
}
if (threadId != null) {
const { id, created } = await communicationClient.createMessage(threadId, chat.masterTag.Thread, markdown)
for (const file of files) {
await communicationClient.createFile(threadId, id, created, file.blobId, file.type, file.filename, file.size)
}
}
}
let footerHeight: number | undefined = undefined
</script>
{#if widget && data && data.message}
<div class="chat-widget" style:width style:height>
<ChatHeader card={threadCard} icon={chat.icon.Thread} title={data.name} canClose on:close />
{#if message && parentCard}
<div class="mt-4" />
<MessagePresenter {message} card={parentCard} replies={false} />
<Divider />
<div class="messages">
<ChatBody
card={threadCard}
bottomStart={false}
showDates={false}
overlyColor="var(--next-background-color)"
{footerHeight}
/>
</div>
<ChatFooter card={threadCard} bind:height={footerHeight} onSubmit={handleSubmit} />
{/if}
</div>
{/if}
<style lang="scss">
.chat-widget {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
min-height: 0;
background: var(--next-background-color);
}
.messages {
display: flex;
flex-direction: column;
width: 100%;
overflow: hidden;
}
.footer {
border-top: 1px solid var(--next-divider-color);
padding: 1.25rem 1rem 0 1rem;
}
</style>
@@ -1,58 +0,0 @@
<!--
// Copyright © 2024 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.
-->
<script lang="ts">
import { Loading, Scroller } from '@hcengineering/ui'
export let scroller: Scroller | undefined | null = undefined
export let scrollDiv: HTMLDivElement | undefined | null = undefined
export let contentDiv: HTMLDivElement | undefined | null = undefined
export let bottomStart: boolean = true
export let isLoading: boolean = false
export let overlyColor: string = 'var(--next-panel-color-background)'
export let onScroll: () => void = () => {}
export let onResize: () => void = () => {}
</script>
{#if isLoading}
<div class="overlay" style:background-color={overlyColor}>
<Loading />
</div>
{/if}
<Scroller
bind:this={scroller}
bind:divScroll={scrollDiv}
bind:divBox={contentDiv}
scrollDirection="vertical-reverse"
noStretch={true}
{bottomStart}
disableOverscroll
disablePointerEventsOnScroll
{onScroll}
{onResize}
>
<slot />
</Scroller>
<style lang="scss">
.overlay {
width: 100%;
height: 100%;
position: absolute;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
</style>
+6 -3
View File
@@ -16,11 +16,14 @@
import { type Resources } from '@hcengineering/platform'
import ChatApplication from './components/ChatApplication.svelte'
import ChatWidget from './components/ChatWidget.svelte'
import { resolveLocation, resolveLocationData } from './location'
export default async (): Promise<Resources> => ({
component: {
ChatApplication,
ChatWidget
ChatApplication
},
resolver: {
Location: resolveLocation,
LocationData: resolveLocationData
}
})
+76 -39
View File
@@ -13,54 +13,42 @@
// limitations under the License.
//
import { type Card, type MasterTag } from '@hcengineering/card'
import type { Doc, Ref } from '@hcengineering/core'
import { navigate, type Location, getCurrentResolvedLocation } from '@hcengineering/ui'
import cardPlugin, { type Card, type MasterTag } from '@hcengineering/card'
import type { Class, Doc, Ref } from '@hcengineering/core'
import { navigate, getCurrentResolvedLocation, type Location, type ResolvedLocation } from '@hcengineering/ui'
import { chatId } from '@hcengineering/chat'
import { getClient } from '@hcengineering/presentation'
import { type Message } from '@hcengineering/communication-types'
import workbench from '@hcengineering/workbench'
import { openWidget } from '@hcengineering/workbench-resources'
import chat from './plugin'
import { type ChatWidgetData } from './types'
import { createThreadTitle } from './utils'
export function decodeURI (value: string): ['type', Ref<MasterTag>] | ['card', Ref<Card>] {
return decodeURIComponent(value).split('|') as any
}
export function encodeURI (type: 'type' | 'card', ref: Ref<Doc>): string {
return [type, ref].join('|')
}
import { type LocationData } from '@hcengineering/workbench'
import { encodeObjectURI, decodeObjectURI } from '@hcengineering/view'
import { accessDeniedStore } from '@hcengineering/view-resources'
export function getCardIdFromLocation (loc: Location): Ref<Card> | undefined {
if (loc.path[2] !== chatId) {
return undefined
}
const [type, ref] = decodeURI(loc.path[3])
if (type !== 'card') {
const [_id, _class] = decodeObjectURI(loc.path[3])
if (_class !== cardPlugin.class.Card) {
return undefined
}
return ref
return _id as Ref<Card>
}
export function getTypeIdFromLocation (loc: Location): Ref<MasterTag> | undefined {
if (loc.path[2] !== chatId) {
return undefined
}
const [type, ref] = decodeURI(loc.path[3])
if (type !== 'type') {
const [_id, _class] = decodeObjectURI(loc.path[3])
if (_class !== cardPlugin.class.MasterTag) {
return undefined
}
return ref
return _id as Ref<MasterTag>
}
export function navigateToCard (_id: Ref<Card>): void {
const loc = getCurrentResolvedLocation()
loc.path[2] = chatId
loc.path[3] = encodeURI('card', _id)
loc.path[3] = encodeObjectURI(_id, cardPlugin.class.Card)
delete loc.query?.message
navigate(loc)
@@ -70,25 +58,74 @@ export function navigateToType (_id: Ref<MasterTag>): void {
const loc = getCurrentResolvedLocation()
loc.path[2] = chatId
loc.path[3] = encodeURI('type', _id)
loc.path[3] = encodeObjectURI(_id, cardPlugin.class.MasterTag)
delete loc.query?.message
navigate(loc)
}
export async function openThreadInSidebar (message: Message, parent: Card): Promise<void> {
const client = getClient()
const widget = client.getModel().findAllSync(workbench.class.Widget, { _id: chat.ids.ChatWidget })[0]
if (widget === undefined) return
const data: ChatWidgetData = {
id: `${message.card}-${message.id}`,
name: createThreadTitle(message, parent),
message: message.id,
created: message.created,
card: message.card
export async function resolveLocation (loc: Location): Promise<ResolvedLocation | undefined> {
if (loc.path[2] !== chatId) {
return undefined
}
openWidget(widget, data)
const [_id, _class] = decodeObjectURI(loc.path[3])
if (_id != null && _class != null && _id !== '' && _class !== '') {
return await generateLocation(loc, _id, _class)
}
}
async function generateLocation (
loc: Location,
_id: Ref<Doc>,
_class: Ref<Class<Doc>>
): Promise<ResolvedLocation | undefined> {
const client = getClient()
const doc = await client.findOne(_class, { _id })
if (doc === undefined) {
accessDeniedStore.set(true)
return undefined
}
const appComponent = loc.path[0] ?? ''
const workspace = loc.path[1] ?? ''
return {
loc: {
path: [appComponent, workspace, chatId, encodeObjectURI(_id, _class)],
fragment: undefined
},
defaultLocation: {
path: [appComponent, workspace, chatId, encodeObjectURI(_id, _class)],
fragment: undefined
}
}
}
export async function resolveLocationData (loc: Location): Promise<LocationData> {
const cardId = getCardIdFromLocation(loc)
const typeId = getTypeIdFromLocation(loc)
const client = getClient()
if (cardId !== undefined) {
const object = await client.findOne(cardPlugin.class.Card, { _id: cardId })
if (object === undefined) {
return {}
}
return { name: object.title }
}
if (typeId !== undefined) {
const object = await client.findOne(cardPlugin.class.MasterTag, { _id: typeId })
if (object === undefined) {
return {}
}
return { nameIntl: object.label }
}
return {}
}
+2 -2
View File
@@ -40,8 +40,8 @@
"dependencies": {
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/client": "^0.6.18",
"@hcengineering/communication-sdk-types": "0.1.172",
"@hcengineering/communication-types": "0.1.172",
"@hcengineering/communication-sdk-types": "0.1.174",
"@hcengineering/communication-types": "0.1.174",
"@hcengineering/core": "^0.6.32",
"@hcengineering/platform": "^0.6.11",
"@hcengineering/rpc": "^0.6.5",
@@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}
@@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "assets"
}
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Zprávy",
"FirstMessages": "První zprávy",
"LatestMessages": "Poslední zprávy"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Nachrichten",
"FirstMessages": "Erste Nachrichten",
"LatestMessages": "Neueste Nachrichten"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Messages",
"FirstMessages": "First messages",
"LatestMessages": "Latest messages"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Mensajes",
"FirstMessages": "Primeros Mensajes",
"LatestMessages": "Últimos Mensajes"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Messages",
"FirstMessages": "Premiers messages",
"LatestMessages": "Derniers messages"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Messaggi",
"FirstMessages": "Primi Messaggi",
"LatestMessages": "Ultimi Messaggi"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "メッセージ",
"FirstMessages": "最初のメッセージ",
"LatestMessages": "最新のメッセージ"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Mensagens",
"FirstMessages": "Primeiras Mensagens",
"LatestMessages": "Últimas Mensagens"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "Сообщения",
"FirstMessages": "Первые сообщения",
"LatestMessages": "Последние сообщения"
}
}
@@ -0,0 +1,7 @@
{
"string": {
"Messages": "消息",
"FirstMessages": "第一条消息",
"LatestMessages": "最新消息"
}
}
+39
View File
@@ -0,0 +1,39 @@
{
"name": "@hcengineering/communication-assets",
"version": "0.6.0",
"main": "src/index.ts",
"author": "Copyright © Hardcore Engineering Inc.",
"template": "@hcengineering/assets-package",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"test": "jest --passWithNoTests --silent",
"build:docs": "",
"format": "format src",
"build:watch": "compile",
"_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",
"@types/node": "~20.11.16",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"typescript": "^5.3.3"
},
"dependencies": {
"@hcengineering/platform": "^0.6.11",
"@hcengineering/communication": "^0.6.0"
}
}
@@ -0,0 +1,6 @@
import { makeLocalesTest } from '@hcengineering/platform'
it(
'Locales are equale',
makeLocalesTest((lang) => import(`../../lang/${lang}.json`))
)
+19
View File
@@ -0,0 +1,19 @@
//
// 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 { addStringsLoader } from '@hcengineering/platform'
import { communicationId } from '@hcengineering/communication'
addStringsLoader(communicationId, async (lang: string) => await import(`../lang/${lang}.json`))
@@ -0,0 +1,11 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/assets/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"declarationDir": "./types",
"types": ["node", "jest"],
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}
@@ -0,0 +1,4 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'],
parserOptions: { tsconfigRootDir: __dirname }
}
@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"printWidth": 120,
"useTabs": false,
"bracketSpacing": true,
"proseWrap": "preserve",
"plugins": [
"prettier-plugin-svelte"
],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}
@@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "ui"
}
@@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)']
}

Some files were not shown because too many files have changed in this diff Show More