From c91cdb85d89ff3754d47ce5ce6b72596148863d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anton=20M=C3=A5nsson?=
Date: Tue, 14 Oct 2025 11:25:18 +0200
Subject: [PATCH 01/15] ks-201: Add accessibility support for screen readers to
announce the number of matching results in message search immediately after
the search has been performed.
---
.../components/MessageSearch.tsx | 17 ++++++++++++++++-
packages/i18n/src/locales/en.i18n.json | 4 ++++
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
index d07adce8fd586..a77db92f67437 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
@@ -3,6 +3,7 @@ import { MessageTypes } from '@rocket.chat/message-types';
import { useSetting, useTranslation, useUserPreference } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import { Fragment, memo, useState } from 'react';
+import { VisuallyHidden } from 'react-aria';
import { Virtuoso } from 'react-virtuoso';
import { ContextualbarEmptyContent } from '../../../../../components/Contextualbar';
@@ -36,7 +37,21 @@ const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactE
{messageSearchQuery.data && (
<>
- {messageSearchQuery.data.length === 0 && }
+ {messageSearchQuery.data.length === 0 && (
+
+
+
+ )}
+
+ {messageSearchQuery.data.length > 0 && (
+
+ {t('Search_Messages_Count', { count: messageSearchQuery.data.length })}
+
+ )}
+
{messageSearchQuery.data.length > 0 && (
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index dcdbe01b31e34..806ed82ad0820 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -4645,6 +4645,10 @@
"Search_Installed_Apps": "Search installed apps",
"Search_Integrations": "Search Integrations",
"Search_Messages": "Search Messages",
+ "Search_Messages_Count": {
+ "one": "Found {{count}} single result",
+ "other": "Found {{count}} results"
+ },
"Search_Page_Size": "Page Size",
"Search_Premium_Apps": "Search Premium apps",
"Search_Private_Groups": "Search Private Groups",
From 9f5efcccd632d7022046243e7b5938f71d135412 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anton=20M=C3=A5nsson?=
Date: Thu, 16 Oct 2025 12:16:32 +0200
Subject: [PATCH 02/15] ks-201: Fixed tabIndex prop as well as minor spelling
errors.
---
.../MessageSearchTab/components/MessageSearch.tsx | 2 +-
packages/i18n/src/locales/en.i18n.json | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
index a77db92f67437..a32ad4561dd1e 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
@@ -38,7 +38,7 @@ const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactE
{messageSearchQuery.data && (
<>
{messageSearchQuery.data.length === 0 && (
-
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index 806ed82ad0820..7a27c65dab893 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -315,7 +315,7 @@
"Accounts_TwoFactorAuthentication_Enforce_Password_Fallback_Description": "Users will be forced to enter their password, for important actions, if no other Two Factor Authentication method is enabled for that user and a password is set for him.",
"Accounts_TwoFactorAuthentication_MaxDelta": "Maximum Delta",
"Accounts_TwoFactorAuthentication_MaxDelta_Description": "The Maximum Delta determines how many tokens are valid at any given time. Tokens are generated every 30 seconds, and are valid for (30 * Maximum Delta) seconds. \nExample: With a Maximum Delta set to 10, each token can be used up to 300 seconds before or after it's timestamp. This is useful when the client's clock is not properly synced with the server.",
- "Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts": "Maximun Invalid Email OTP Codes Allowed",
+ "Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts": "Maximum Invalid Email OTP Codes Allowed",
"Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts_Description": "The system allows a maximum number of invalid email OTP codes, after which a new code is automatically generated. We highly recommend using this setting along with 'Block failed login attempts by Username'.",
"Accounts_TwoFactorAuthentication_RememberFor": "Remember Two Factor for (seconds)",
"Accounts_TwoFactorAuthentication_RememberFor_Description": "Do not request two factor authorization code if it was already provided before in the given time.",
@@ -581,7 +581,7 @@
"Apps_License_Message_appId": "License hasn't been issued for this app",
"Apps_License_Message_bundle": "License issued for a bundle that does not contain the app",
"Apps_License_Message_expire": "License is no longer valid and needs to be renewed",
- "Apps_License_Message_maxSeats": "License does not accomodate the current amount of active users. Please increase the number of seats",
+ "Apps_License_Message_maxSeats": "License does not accommodate the current amount of active users. Please increase the number of seats",
"Apps_License_Message_publicKey": "There has been an error trying to decrypt the license. Please sync your workspace in the Connectivity Services and try again",
"Apps_License_Message_renewal": "License has expired and needs to be renewed",
"Apps_License_Message_seats": "License does not have enough seats to accommodate the current amount of active users. Please increase the number of seats",
@@ -944,7 +944,7 @@
"Cancel_message_input": "Cancel",
"Cancel_recording": "Cancel recording",
"Cancel_subscription": "Cancel subscription",
- "Cancel_subscription_message": "This workspace will downgrage to Community and lose free access to premium capabilities.
While you can keep using Rocket.Chat, your team will lose access to unlimited mobile push notifications, read receipts, marketplace apps <4>and other capabilities4>.",
+ "Cancel_subscription_message": "This workspace will downgrade to Community and lose free access to premium capabilities.
While you can keep using Rocket.Chat, your team will lose access to unlimited mobile push notifications, read receipts, marketplace apps <4>and other capabilities4>.",
"Canceled": "Canceled",
"Canned_Response_Created": "Canned Response created",
"Canned_Response_Delete_Warning": "Deleting a canned response cannot be undone.",
@@ -985,7 +985,7 @@
"Channel_to_listen_on": "Channel to listen on",
"Channel_what_is_this_channel_about": "What is this channel about?",
"Channels": "Channels",
- "Channels_added": "Channels added sucessfully",
+ "Channels_added": "Channels added successfully",
"Channels_are_where_your_team_communicate": "Channels are where your team communicate",
"Channels_list": "List of public channels",
"Chart": "Chart",
@@ -6594,7 +6594,7 @@
"onboarding.form.registeredServerForm.registrationKeepInformed": "By submitting this form you consent to receive more information about Rocket.Chat products, events and updates, according to our <1>privacy policy1>. You may unsubscribe at any time.",
"onboarding.form.registeredServerForm.title": "Register your workspace",
"onboarding.form.standaloneServerForm.manuallyIntegrate": "Need to manually integrate with external services",
- "onboarding.form.standaloneServerForm.publishOwnApp": "In order to send push notitications you need to compile and publish your own app to Google Play and App Store",
+ "onboarding.form.standaloneServerForm.publishOwnApp": "In order to send push notifications you need to compile and publish your own app to Google Play and App Store",
"onboarding.form.standaloneServerForm.servicesUnavailable": "Some of the services will be unavailable or will require manual setup",
"onboarding.form.standaloneServerForm.title": "Standalone Server Confirmation",
"onboarding.page.alreadyHaveAccount": "Already have an account? <1>Manage your workspaces.1>",
From 214a1130a4d69c2cdcbd24bf99581906e11ef79c Mon Sep 17 00:00:00 2001
From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com>
Date: Mon, 13 Oct 2025 16:40:29 -0300
Subject: [PATCH 03/15] chore: improve voip session handling (#37094)
---
packages/ui-voip/src/v2/useMediaSessionInstance.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/ui-voip/src/v2/useMediaSessionInstance.ts b/packages/ui-voip/src/v2/useMediaSessionInstance.ts
index db55bcfafb181..b72b250e97e4c 100644
--- a/packages/ui-voip/src/v2/useMediaSessionInstance.ts
+++ b/packages/ui-voip/src/v2/useMediaSessionInstance.ts
@@ -97,7 +97,8 @@ class MediaSessionStore extends Emitter<{ change: void }> {
private makeInstance(userId: string) {
if (this.sessionInstance !== null) {
- this.sessionInstance.disableStateReport();
+ this.sessionInstance.endSession();
+ this.sessionInstance = null;
}
if (!this._webrtcProcessorFactory || !this.sendSignalFn) {
From e2580d206f60bc5b65bf8808bd53b99bbc6b9d25 Mon Sep 17 00:00:00 2001
From: Douglas Fabris
Date: Tue, 14 Oct 2025 11:46:02 -0300
Subject: [PATCH 04/15] fix: Pagination not working properly in
`UsersInRoleTable` (#37203)
---
.changeset/slow-tomatoes-try.md | 5 +++++
.../UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
create mode 100644 .changeset/slow-tomatoes-try.md
diff --git a/.changeset/slow-tomatoes-try.md b/.changeset/slow-tomatoes-try.md
new file mode 100644
index 0000000000000..91b762e4f8bf6
--- /dev/null
+++ b/.changeset/slow-tomatoes-try.md
@@ -0,0 +1,5 @@
+---
+'@rocket.chat/meteor': patch
+---
+
+Fixes an issue where pagination is not working properly in “users in role” table
diff --git a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx
index 64d4ae417847d..3695f4ef61490 100644
--- a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx
+++ b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx
@@ -115,7 +115,7 @@ const UsersInRoleTable = ({ rid, roleId, roleName, description }: UsersInRoleTab
divider
current={current}
itemsPerPage={itemsPerPage}
- count={users.length || 0}
+ count={data.total || 0}
onSetItemsPerPage={onSetItemsPerPage}
onSetCurrent={onSetCurrent}
{...paginationProps}
From 53a5aaa1d9749b7410d1a9de828171a83116d920 Mon Sep 17 00:00:00 2001
From: Douglas Fabris
Date: Tue, 14 Oct 2025 14:46:44 -0300
Subject: [PATCH 05/15] fix: Message not being translated immediately in
Omnichannel room (#37200)
---
.changeset/lemon-kings-approve.md | 5 +++++
apps/meteor/app/autotranslate/client/lib/autotranslate.ts | 1 -
2 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 .changeset/lemon-kings-approve.md
diff --git a/.changeset/lemon-kings-approve.md b/.changeset/lemon-kings-approve.md
new file mode 100644
index 0000000000000..1a6999c545273
--- /dev/null
+++ b/.changeset/lemon-kings-approve.md
@@ -0,0 +1,5 @@
+---
+'@rocket.chat/meteor': patch
+---
+
+Fixes an issue where messages are not being translated immediately in omnichannel rooms
diff --git a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts
index 3cea977c8b4e4..76b45d04e18fa 100644
--- a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts
+++ b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts
@@ -148,7 +148,6 @@ export const createAutoTranslateMessageStreamHandler = (): ((message: ITranslate
(record) => record._id === message._id,
({ autoTranslateFetching: _, ...record }) => ({
...record,
- autoTranslateShowInverse: true,
}),
);
delete AutoTranslate.messageIdsToWait[message._id];
From bd0b91735b0b0731545496b30f848b4a6259a12c Mon Sep 17 00:00:00 2001
From: Martin Schoeler
Date: Tue, 14 Oct 2025 15:40:53 -0300
Subject: [PATCH 06/15] feat: Expandable Message Composer (#36920)
Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com>
---
.../room/composer/messageBox/MessageBox.tsx | 43 +++-
.../featurePreview/expandable-composer.png | Bin 0 -> 19912 bytes
packages/i18n/src/locales/en.i18n.json | 2 +
.../src/hooks/useFeaturePreviewList.ts | 12 +-
.../MessageComposer.stories.tsx | 16 ++
.../MessageComposerInputExpandable.spec.tsx | 117 ++++++++++
.../MessageComposerInputExpandable.tsx | 55 +++++
.../MessageComposer.spec.tsx.snap | 205 ++++++++++++++++++
.../ui-composer/src/MessageComposer/index.ts | 2 +
9 files changed, 439 insertions(+), 13 deletions(-)
create mode 100644 apps/meteor/public/images/featurePreview/expandable-composer.png
create mode 100644 packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.spec.tsx
create mode 100644 packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.tsx
diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
index 8baf1cda2d8cf..da788b2051fae 100644
--- a/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
+++ b/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
@@ -1,7 +1,7 @@
/* eslint-disable complexity */
import { isRoomFederated, isRoomNativeFederated, type IMessage, type ISubscription } from '@rocket.chat/core-typings';
import { useContentBoxSize, useEffectEvent } from '@rocket.chat/fuselage-hooks';
-import { useSafeRefCallback } from '@rocket.chat/ui-client';
+import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn, useSafeRefCallback } from '@rocket.chat/ui-client';
import {
MessageComposerAction,
MessageComposerToolbarActions,
@@ -11,6 +11,7 @@ import {
MessageComposerActionsDivider,
MessageComposerToolbarSubmit,
MessageComposerButton,
+ MessageComposerInputExpandable,
} from '@rocket.chat/ui-composer';
import { useTranslation, useUserPreference, useLayout, useSetting } from '@rocket.chat/ui-contexts';
import { useMutation } from '@tanstack/react-query';
@@ -423,17 +424,35 @@ const MessageBox = ({
{isRecordingVideo && }
{isRecordingAudio && }
-
+
+
+
+
+
+
+
+ qic4{KhZ0C|3-0a^
zoXg(d-us-F`*7~VeL4?WnQJ9;ESYnTHRt$^f4Hi$Jl->kXDBErc<1_1-sw7{ppd-&>x24U{ngRqK~!gTd1;hV7}fUU4!VVuk`xL`MJ(
z$x{>*C4={Gr9OI~?x*9Fr%$*pB46~&g}UwMfnrGtMn&5
z_3!V896${1TNk%T_xsJ#s>6jF@3h0_gR2Mg(t`=$qM5{`q4)1Q9dDTCXA_fUekPUH
z+cCS~CI?tUbUw+PwMd)MY7l`m7X9NUjId&9&Y5jvt}1;IbDjmcMQ%}YdpkEb=d5{<
z&epUPOls({KWCM1J!tyr&q*>HwGu9npSnNb>b_hd01tq
z8U0&ZEzgmd-qoA2evUeOcetN^?9}hq(P~t(zBQ($TW<
zxesZutW}Sb{?FsWs)*7ZIREayfX)B3#0aBBslLPF
z-HrydlQ6>z^OBP5x5LA5%6ZRT**`58cA-1sSl4=^nLj6SaI46t(Sm+WYZ!D|Y&-aG
zb1Evk<|#e2uKFztng`}56uSCHrYz>d4vp@I*bY@SGpVbfUlh)VTvz?e-Hb>Pkbd~kzmU~xssrJ9O^e%i>*1q0w#exxc_Ygk>
z_P_PhAUDfnA~8qXcIV`gt)x^-e)s8Rit8Cmg)aWF5YZvA2dB1E@2(cEMutApIf+mF
zn+xFO?Snp;_|V1H|hLvvOpleVOp&ryH)5TiX;
z&EpE-HurnDPR&0DUqD*VDj`>Q7K{9w_Xuyp{nm%3waaJ!b5Y%Nw~G&TOTHUFnC2h0
zZ+jJwxE{s_S`XodceWCgqDqSC`5r5XyVpLq4Z#bQ7XOX^5y0Xh6B+!d*v7AH)%k9H
z@$B&Cu9q0&RYoJRAD@H&!>$?E&7#C2`M0?LJjp&P6Pcc3ho8hat^KxMnc|{awZY%c;fUnSULZ
zWh$*$Q&@+KsF3-^y2j7{hATbeb71>F*67Wj{NM6lBg6db*uOq9tm3$)3_LG(c>{^{
z@vTN8Q!GjywGEFW4{llyz0)mYsEq&ZwG4u{JDBs3`x%IKquXZa!r@!zYV=S4_|@Ki
zl{*Baz-9gvE^*jx{GX2uQvId)~Y702e(~
zWSW%%l>akw6|u5TBU6ky!CELK9pBK{|LO5Wl7m+*XPdsW;TLiL9W;}J9a{cxyKiBK
zTmRdx^y9C<`TqtV(C9i_qIB)n(0!<^zU>&3R5)%=glIWjFcuVWtz9R!)14akPRDTF(JYz3`;_qgZdhO>^I66u+Ii
zLsjA}k~lPdA~KMOePcuR8$0*0fs7lPBhP7MPw4!sfH%vVfATX9ZhYWi{Kl&*qJyo9
zRfW;02NhF8M)HC30h{XzTQm43jSDpVzFBOk`^?{~=~M%9?kBToJgkJpL$0IutWgs!
zYIU=**ssUHvxlqU=3*`z1ZR*2lqY)#7A+4uGzUJ7j`PW20
zI!DaGE;bJo
z{4PQ6>o3|0TAIz?0*=T!uwFeOGF#ly1uJN|X
z&TP?k@0|z9eeBYQcW46{Pznw$KpI=($K~1DG}1|lF*Pm0L7CFQ62;)DV308=`?A@P
zvN+XOG6ghiBZ7%oyo+hIK%C_jurXD*k?y26R+_IDmQXqEi?c%-;ed|Q44)liE~8Y=
z=1g}kgmEc{c2->0>hJ9xeRBWl)2{2PD;tr!inH|$gZu;2N)t%Mw$kR!=0|$1HWRO%
zohsq0!ZG~m+mLBrPFE!&@2vL_)nLYbeno|-kH90tM26DBS#4ZX%AmV!j#J^6|`?~F0X^ZzkjQHj?wICxT&`Aot
zFCN$D8iufTjcYAwTyK8BL2?zY?jKNtdjSJDAkrUZ@uW~Y0w>o%x60KPZLUH}^!X0(
zrO#mtBs>unvfJocQ;w_Z>w&S`{`Dj{%{km|>@0s4I6x!y!2xf8M-h$M7$FcBo1B*T
z=Z`Jzjpev57J_=To43l?=ssq*3^wrThtuN#))QxrjOSz@lVuT$IFW9UY1MJ_06<#+
z4b9oN$1#i*c%8hGy1`Cg5bxNj`}K;b@qYT|;8_L1!nWrtEjHI!6~+(K`%U4t7%
zkG582J=?O{nxT8a@tnm7rjJ?g$%Cx|?397V@gi&153M&zbJk_qgWaErdBb^?o<^3K
z=I8E-Ucj*~!@Vl3@ctxdC%jo3mAgZoshf5RS&elHTc@9JA7nMUnVp(1jlFNY(SA5F
zy0jdk&pN$fkT{W1j=~4Fo3*=5ikFWCZ#G>KF?m(-T>_{l7Q`UoZZ01;K88`@Rl}$6
zj-^dHg$mIiw&{!9c$dD8<0n*QScGe9?Pfh#^tcpa5D?Mu_ULrcn6mNUzW3Jhf~3-=Z#*7#
zL)x)_%md6omd57l8tIPr;Z~WvXVRoPAnizW95@D{ENDNqy8RXPEkxLRYR{M??9ZP`
z+ru+Zyz@p%l8%@p$?NeL899psK;brxPu$>|dsk(x(X`>);
z(FD2DNg(96ecNbKf-6OB^ZHB3{YMGaX1|fE4rfy|fT$QMylQPN%~dB4ih?Pk0hWI8
zGf|CFRa_S~o^RZBO}o`3z|MDJe+%3B9iN_9$x-ld?1NjmPF8!sjj|rQ?7!wd<(ZT&J+D`&!1gug*WqZvN_sxlh93$j(q##%OnT
zYAj+cu-@EA?0+8XvSL#M!880-jBmc)tit3zFt>qe`va=jvViAeC1f4x9(+v+Imc*#
z?U!K=0MEi8>wsD8(V^?GZQdukWI1V=EZI^l=K47EUjt0yClUpX;>RDTnxDS2&|&I1
zj;uS2A&LOQ-IB&08u&a$+O%vLlw2%csI$E-puA%(kJ0bOxUxvugg_2&{06Tk)Z|y?
z`{<$xL{8rZlXSl3@R!Sd8u2579OL~UbFcG^R9<3IZ0H;EPNMM&n4s3EfBc~^@J
zM9v38OB?_3Q8~^4^uqNW79k=uUQj(uMp}(
z(YAb*m)E#@h7m4h(E|;()+WC7`Oj51^~#aY%ge&fnc^(`H+6vJ7t-oUR~J2mWD%Z~*r**&^+~U=N>$tp}orp%l?lniP
ztX1!g6l*WYpR08JHI!I_;!Gz6qa$jOis6E_hb$j%X!6&A8Z5&&ZB@;5BL&!WKt+&f
zl-Why7eiG>7l2ch|CWzaj16G0;05v=e&JM2bGYGDb2KBn6rJA$2l~CA?YM9^`o?Z1
zp>VuW>P)?z3GL?yoj+~DcUi0uVCxf5)kue>z_3peiz(U6D!`xi>W}RP7f^O}k~k12
z3SWq(h@oG&&iz7r+YA~r_?(t<5RwY6W2zXKx^zreKE$rcv%47T=Yx_T`~xh!4h7fa
zC
z)9w92%yPPd#6X$5U(cDgE$#4P{O>dX8zR&wp{UY0X&SBREjhsD`YKWwXk%RhNzPca
zNXyzR`qgUPXqygo!QOwm#+ENXjqm8`5G4g8w?Gj`0mr}8mdi#LetIWg`Y==8YAIsk
zse8p)b^1M^xlzwnvNSq#A4xT0tf@WtR@hfcPyuh@TGf-Pr@%enda|
zN$IDWJq0~9>I80KX@8h^qillOWkO?Kl@;&aj!wT7E=Oku)yD=3dzz#ed)fi!;9%(n
zr>G9$XVK3lSr{xi7ny&C
zQv2*p&qkpr$1(3uVzL+4?PiWMb$UqyzaaV74&n@HBe7EQ&3`1_$s1}xm~GwaYq!ec
zvuL8yLY>ON34yT4{isY2EA)@p1OYIo#gWkYk=0}6AXFz3`PDLJ{`g@}laDE=WJhSP
z8%(;q>?N1dSFyrW1-V>S;$nxAY=*>B`$&WMUV(HrN#u;`KSyAY3U`>&u1i&w6p*oF
z<5A>2wE^FV805MLrs;m@SoP;FZamDAKbdmd&DL7V!*KoaBzuopK>}yaRiFrCot3Wy
z*hNASeM{+T$ba`f+u4<`uP0IPVp{8Utx-$V96LsGH*;1?cD6wLuIZ$shDQ+A=V4ll
zRkUR)XWNmFaz&$V*QBxgtVuZDO6-y!Q$K$5-U^aAmA_ugQieqNe$NCSrS}-O1Lt!~
z@@Tg>Z8ruv(`1C&3h-f^8>X@Z-A@X3+H2>HrY!*Ka;yIG_)_L0_NV=QR4ojoYy%o+
znP(wL&vrviGDh
zcuAWoFAL4|lYO^{ckWCGFz(3BA?mh_EyLKgq6OX1=N^xwD|zlv){Ob{ip#w
z2_UN$0cC^Fj#c+dXSGc<;z5
z9YCd-W*^39t5Q+7Sx(C$+h?#&mTyA1k<0GIyrla>)ABp1u#qU>k8oS$
z&8hL++^F7$MUrnYp%};VRShu2MCkgYwEm$~c}5MkJFfJMRg8{D2
zdOE1KLg(s45$b51?!Jh@3VRNaQD*Eo0!WFE^cFqEvN89cWl;1P42j`9QlMyxF+1K{
zCE7*Ne&1&FY<3v6^y7v3lF2mqeZpD!VvW@@yWtD@*GRx&`$A3qT&g_xOzz5@a=Y&o
zoPr5Mfg=p6=K8vq8|pdCOYy>uKShE+YfS#e7Bs&KL79~smpa&){c;j-Gzs6jGxfUq
zEpngz5TFs2Vp4u8bLjpUkWo;MA_cZHk)Ir#4{(3&Rc|3YI&*t={w@US%=l+>$5
z-ZYRtm--SR*3$EhSu-j8m&Q^<#yX9
zsJ+0j)i60~Z4?+ZHH=NzNI-y{;KSriV?$G|7w4}b;BEXic1({assts3rb$EbELJf)
zoZGM9N}&sN({w^C@-wh!9coo0#?DpG8`eK=u_te+Vf0se9>W|2FV$GN1d_FN4RA-Z
zMjM`TfAJa-P9o!%^}3Dv(EXl~8KJ+nT05E^Xf=;i^^AuxS`|FM%o{jWOKiz5T>uBu
zVs(tj)?+HHLEn)xwHzur@F0|JRSQ|=&biC^UV!*qx;~&m>2ddnyxSzSN7Gt>Hq2cG
zU2`Is;cuavE6pRswwMLhKUlZzdF$0QIsNgdfHs*BXWJ||3qPB)&FqKMv59@r8^IjX
zhDCTu-%UV_8o+lM|G_{&J`GsT@M|Pz^0xJWeZ*z_FMOqQ7Tk$8@mD0A`
ztR6@n8a&d?A;3ISo~Rpn(QvS1bR4svD{;f8;CpoCI&~ZIa5Zgjspa=P?$KNCeG>+v
zQ7ORH``&W1w}{QY#=zHsQxiQmg$vK!9gF;maNn(UZ+wh;ISX7fAqxQ_LJ9zHWdn9D
zozdZOSxAUIN<{ytpxv8lcm52hz
zYp$bPQH7*UGYFSAz5;!|0axnSA+E?L60vEdI&EnyP6CK{-=*Zl7+0>%!t2Sku7B(A
zwVwZY-@uH4My|c`QqaZ;5INy$^4YL0sVNB}!pkEVb9VNA$AetB4jh=DwuFX$d8wg`tk{rmYtJNUa#L(85x=d!mrYztpOLvMs
zSDO2k@XIJqJVmK*LxB@{?VZ*q7yk9|F?@OsO_jJvr8PJSgm0s)3iJ4`q2T*3B0rrF
z9hm%*!3j+2t0Zu%eA8&$9s)JGjtY%2Bc(Wgv*l%hX%3r<49K;6vtUvkk{<4=D4jhI
zFG=wpNVLSKOidc@uW=0k028&xX+>Dt;;me(!opnaNRx_eqx7AXjjCtn2%q%;N30*i
zw?#C!dy@oD`lhSQ?gkCIw=#VK8&T_R<})?y)ddBV+^!f0X7a==&^xfCXgURY-8W&p
zH8F039hKYi)OZZ)PRvYM0YUI@5*-T`WO_#^h3Seoh$VhjcPi@g(;GQAp9GO6r#)SKfvPrn5p!K}
zSSoxf%5Gz5irQ*paP%%;$AB(#_^%L?nT(K&oksS#D4?+3!py3*bn!ULP>g%Ug_2kG
zX+NX+bwhq4FP6`Eo~x&lA2*1q3KvflMnc$`~g_&iJ&+RMFB{v)e#
zH-bJW)F~yUXVFGAmd`UEWv#YILz4Ooe
z#-dsiHEiy9Msu=~zGSRL3E=(wW-5=HrG5RNdRaBZhv^{Srt)x^?n#~Eh?L47@x7PD
z-d9v#eQ^SF8aQ})QU=aXXW7`_ioHP-#W41F4k94l96Z(uv=TaXK4
z4k#oC`zw5AI$cGBJePbuSiBdY88JLM5tZh;u}ed~vDpUN$k`+}#`;TGM9f<9@<)J$4=aqoD|S
z*#-nh+|2gJ?qjMK7wl~|Y{A>1%Pk7~!H(4(QqVAAV7$mq#duCYFqr9vBd_bpO!uh=
zUQ!%Gx=SJy7T$-vX)B+C61(DP(`AVlmBisdi*5B(!gS3Kf`4#v)U&LlT;0Flh1~Yz
z+#?t7Cf)0;g1&twKdueW4$kF{%&xbH9W*xns3<2~-DjrA5Ow*9{r$egiShS}a}+ri
zv{&(~wNSfw*Q(>tg)P4^G7JGfrGcbv3%!btLAgKmE>BZ>1wv9tbI5x=HDrEBVOM#qsuTQ?i+uNM8zvNXYvHs7JT`8Qdwht2Y$VHWxd
z(fe0HP}Ie5rRn0BzR!0|rsc-#cg=@yl_G4S^^_TZM{CYNU@h)}WT|6YXP_)pH+OyFa9?JmRp)zsR0*2px*)xoX
zjy)sJ#-@2*o``IZM4~9w5?xjvYq|^+7lj#YqNrCbCU<9m7;K*3mKZvZIS+#!r*ee3
zFTC%|Z(mfU>3^|An>rHt7AlXu95uUOh4R|_W3Y}Lm9R^Wdn<4j^*j{+<GBp5pk^C*0$_abC@$rS-;k8I~>M@gVWV!lSL
zgHOH*{Fj7v9Uo5q1?Ma}VIko*`SsIcCF%LE{-2VsCP+}}djo$-K3e>n#`Ra~|B__G
zO8f`Sg{4UThw1+REibt+y;T^5LmcE|somvcY22+ROWZbFOw3MZW#ClX26fBl7B`E3
z(_T|gjXpzSQ?hSFW5}GxAH_zHQREf%{)v
zFTKfbyU7j#H`;J(KK$~4)I^l+$0Nq>#pl}e%TLO;J5Q~Fw^fV^tC`Wfd3$z#NRhcm
zQqmRuHC=Fi`4cN>#)=>8}(3emUrc44$~6RtLA={zaXCrjNC(gcvbI}H
zg>YvLfA9S=qkYQlGIG)Ws+N?@mn9|tI8gKvYKkf61n8fitsIIkB@#8+H_b!)eyE7;
z=TS@4BmUq}`8%S5{okl^4L-ZMUs}!(2R?#~QVu8Kl4tSu{k?CvEq&E6_a5Rlr}_TA
zUP@p)Ub`av5hQwLYe;5^t@xGcHBIh6bl#;yS`Lo6B&t6XCr0oz$1qHg(FgOg-y2Gt
z^F1-;C6o28sT3(97alx0O1IP)8#m-`BDnFM^zQ)7bgK+3i5}u@p6%$$tvLAGNm3i<
zfz<=XxVNpSNc9$+DjqoGzcdpoq`;5;vQ;{xeBNn#tGU&Z34D4o@1NZlb2UTKrl?oz(1
z;JQ49jO`L82#%{l)4hZm-JGDLcbhr+j{3&i>{LcuCOt&tTz^r|7H~^NW5E}$0T@Y_
z9H5*Gxcfoaf!x#d6M5)groUBuqE1L4@cb;O2+-V-K7ZZ&7>=Ugeg}}xv00*$YkuauXx6{p?Z;X7
zw_;v!tw*|Finl_2?M_uE8H=hRQs>foK_~IW_Nw{wsHuJnP^Zsr(jf^C!nS$)Z)$%B
z4rH`EVofBNRKb~A45PCVCMI$b_44|tV$y|vw_xCA_Y9K9
zf80}wXTSA4#xOI18yp7flhi~HVC-ypS1anUcn?P2SZog9QQhvCwX~%G$cp}Accf^b
za4>yEE|1Q$V65iCm`=Hm(d#b=eXL%o5MgnKh??NXC&;kX#qr^iWkz*X7PE#j-+Y|O
zYOQ|=l~g`zH4Q(y8}X9*=pasHg%3d;b|V0e9#YQMO(pQeTzK8ZP=YCgZ>3_SO}}wr
ze+KNAlm-O4yi_T?cpllPtcvl-2Qw=U-fh2LLw!UU4A|?uZwlfJ`;}OeRai#_cH5JB
zG)h$_ztPm%ijsol#S&g><_p}#Sr2%Y7-&vF>f4gqmcTxwCp930sqn*U(B2QULN_x=
zv}oUKEWZBOL?QjVhr~KV(eDKt>|?4K1EZ>w2mbq^B{VB7kyF$MW0bY_Ujp^LgKS>n
zn|RysMRyHLf#7m?^rJVow}Rf=fH6fet-K_&eU#~ocvk!^wrVty;m>2!Z@jAa*`87B
zub@DLMxgS#Yi6~21b}w8cNqT;VEL+Baed&A`6Yug;PQjlI0h_Bq}Y5OF8t!b&4*J>5E9ZJl3jQ960z
zAaV8tdmXj%J4JiZlh5DwiQx<7_cliO`Za3f@SV@?JabUX<`Sf<^+O>g#yF*-wW43h
ze&>$LfW#T$ZdL>*a(%P`^Q7gk6##&nVYL^7f(|V$T}pTP-AlGp=G}$N&bao|vc}@!
zp>e1Qg(Sjj22*bj_nCtn!Tx6*KE=Y;G`_rH)VSbsuCgC;llF=#01eE98I~=|$G0Da
zAZO8;(J)Z|2?ED>eZdB$V5M
zD?wkE9T}IA_hE6RPSMdYb!@4Wj{e!EO*?JOxwc0b-U#iRyuQ^1ZA8~mif_Fj#I+)m
z`fMS|A
za0lK8pgjS>xFoN#=2SVgVwgh@}XlC#ufBM{0Cb&E(@mC;``J2SnS|TE&%^hWF9SY2*;S5~#b%b4q35>RPv|(fSGwK+p1@f^
z;)w@$ICB~Aoc>z{ujjg9(&gcS&4t2sd-9@^nQ?c@*1tj_=ZXr3n
z@@5h-FI0~9vGBgwLf-XE%T6{dH~ds#*iLt{&}}#Ai~G6pyj!0xLcQr=QiT!30^(BD
z@3w!nQD2~_cN%f
z+ao+vVkiJm&BU_27jL_47W#@d;G{pTEUYe7bw8G9iYQa{~jeslE2k
z`c;cs)WuEr`JZZ8PFn;yI27B=I8c`y6C<9t=QdoH9*7hQN`A06_SDzlR(+y6#S==v
zs~@lHCCBo*xXXmISfW9~gRIX}RXGyubzCLK14WNmjlae-sYOyBf>o+*XE^A)#1|>7
zuy!dy7Ij)tr&04##*Hm=axM-P)XR7G_TC51dPklf_KKKNF$ICsi`v0%&k*{GQ*`zf
zpsDP3nzrPfHl2$!LQZg=qi>eXu5dwp#(MnhMn!$PjqQE_&(2p{`=&P;i^{1dFk5rk
zr`L_iCDK~Si?!!_;14dZ4exGssm3@P7}`?B0t1j=b?lcQK@KbS;OjsE3$V3?8XJ
zxDNu!UucbNm@m}cb%K0JmTTt)kziP5N53_USuPaS^d^Wgyb;D_ykNTY?ul{W2?_?`
z6T~7|R!f;%9Y-2yLJ)6?ZS-*3(2bMsSpOe+J
z41QB>g`T46^Mz7wws*jn4P8ZYWrpMqNtoov|RCv##NCqOP&QmmY_@N^dA
zB<_p0l8Y)~T=6jG7VYT%aau)5LD8(=3vUJcLrw`eK1vYG{>RMwMLYQZDU<@Z4|Iu1Oin$=$>Jo!pc0Z79K5%ms=Hv%u%u$0@XV**dPumpmqn*cYfE05LB9!79}hF1N*qkeMN-
zNHIAloTJHp6jsz_XwOM0XCb!7fa+l>#%yE?*0OJ~5c+|OHi+rzxnpcE7al^7N!^_7
zplwp^94>+zVCY}#%;cxyhASuA+gkXNc{I9Z^_4L(V6~pS^@r4a1Sq18n2%@vZeMA^
z3UA=#w0V~!_f;JJpgAwMDq+hdd!31lnxPxl{wq}h=EvSbCj8Xadg!N!SCxTVaFl@<
zVQZ4@xV(SsVaJ}xG)8Fd1KZQl?VUu&b19{)Q&i8=*`z656ZNNUXAP_6JRztUDOWq&-|WT|o{x^`d*1=`D}n6A@yzxiylX$0R3YV|u9RV^;w7s4;T
zLZyu2sW}+C*&nmro|VJC=uLxCT#1+Kqww9T16lV?;@6o2P9sOO$v!;JYSU9W`KdAX
zhGmRD^9-j|*OSY{P{POl1sXOlS$%%;~#|(4$X*M0=S8=ywT83luQP;?Dp4`|@rCcY!
zw#bk+@xJWW6!_H|i@x1V+f!4YJN#MUSp{WGelewA5mB*blW`}XCgqCTe~=6#PlP(8
z7_zJ8LwH_$?bj}!SoG5Rui~WpzcJ(X4~+1&Mj$v-i^e#EEFV``L;#W8+6roE)@NC{
zQ{SSj#^ZWbgsOB%DYuHE-c!+oh!rx@Bg5*GUqIr+CRorWV%+!!EDtRi_^O~Orer8v
zner{Sp2^(O)@m4-VBitf_m`}vA_{Ql-#CFyFZxtp9Dc3naW^$5!|Y3J@Vui^v~mj4KI
zC*J*vo;5^XX5XJ>OgIl`iL?OD8y_;uBGstu8M|LGV0uVbM0k@`QRyhZQ{Sw5?qc_`
ze^saV^CF@4w|V{`R$$=iW`PW(n(+&wnupjwuiNjZkQ9a(M
zmX5A>O?duY>-j8~V(GsLU@M&-TO79H?q&{3^hoZ054^N(D?Tq7C#d2GNd+;Wy5%h|
zQd!QujIZD`ax_6UT}4*#x3qF%Lp;$N{vyvd>N%WnS&mpWEx?7)50e2NOdi#P3*vxr
z6jW_P;}_E8U6YZZju-pL<-J!HI?hgM!e7jZr`sdCwxGq|$E27wb~1%;VjYcU9K~}!
zpbVwmTR-=&Qognn{D@ttMV0>%_vBg#2aSEmg0^2WI8qIPiTL^b15sToL22c4)5$or
zIe%8_gZ4x2KD|p&M0gAi#B64CgCl9`U~T`HiUqVy{lPh8xjvC{)_wKpoH&j8S+x2=
zE;ceN#MlV_RHAi8B%Zz}YK(!X3V#SPHYN5F2e4WW>#w$8+guCkYtBiut&l;&_Ra1#
zAD$0@d5Aj9KFwki@RxSr^^g9Ai7FGmDxA&EG9~>m8~qq##k>h)tFaiI?AWb(%ty3>
z*NjPN=S<(I?}u!rCaTp?4P2b!HFhVdO@B*dRTi{(Z~%MQH9mz)gNfNNLb7jefq%l^
zob7(itw7l#=#q-aEsJ3Xn8k7q_#IQNdVimC#O7U-NL|G3^@izkT?yD5P%T0A_Dr=4Z{%wi$<
zUB7zsy)m)gyKbA8nDwZ&T|Mj*<-ZsXJr2FSo{=cL>H?rY$iI<4i2mJV-v_yxBJX)S
z8@Z}`Ew96)H?WzlL1od7E?0s6&O&G>LNxQV+~QuBi~uXM>KPucx5T}n`{Za?A#>zo
zNG)w&M-J~~!dX=UDLENu`+=EBA%=z5=_~BbYbH0<{J6qPMIQO?Vwy3{-jH+ICjUU#
z>T5Y&Kb@C--|mLPvK>`CZLG`qGP=m~lb5%W+Qi0f@&Zm!Ji5OJpA2`Ge^s1bO_fd$
z71cGXDzqVv`S?w|nlhdcZY_JAWzUkmZe_SS_Hz_M=L-965MH(EjDImY?4qdxA6Zd*
zjIvcPecA!AuSZcQJpr4h=M7)6yL8svTg2&V<)mAcs~++iP#sk{7zbQqZy$kIMmnve
z4H7wyKT{8ntlZglI7f}pn4w|ECG6VQkN6$by^?*ofY8Ko9kX;*ot7^@+Ds4+ee5K`lM4c5$d%
z_f^g0cMVsWT>Fx>Lj@)K^&<~rVcB9fB=Gk%rRf~zr2D9LC@_1O>>M79h`@1)<=G%U
zAJGAHY9Lab1bRv$|1~RYm$R&^D$e}i>03in_@-yo>H^b~E`%_yIF_vTjM+suMGueJ
z7!963uWWwQO{AGO$sv+j&!H7EA1K%Nl0?UxsvX6I`!(4&2RcL)b0K-j_-VsnbdF6P-?Y>Uv*iQvC!4&B<@7##F#w^EG?Sje|jZD
zd8AzD$s{REVxc-UNi+RR^s;swFQ=#Wt*1ZxU%@ts-H)WWx5qQXp8CBy=qI1_Gk{wQ
zH3lM!hl^;Y2*&b3!eXU?C{BIMK;C=_=lv&Fl2`Qh{bkO<*;AkG`7p$nrOfM~hr#X7;wh
z{VQKvPxn0N%?b0Hc+y$fBCbutF;=T?z6-zQ`_VVoV$1kqD~IQ3c=KK{+nJ40>D(SB
z`^0M;@$}=Uv#wucBg9w%Q=zWwwsDKCE#JgV-hSR8M)(CcmfJy=kmH^24{=!~?bPg*
zzM7g4^wlDy7%7ExCDS{vbwo|FjJT9ovn#85_I{BsVMwMB1D02CL-_}1(bE?m!&qE1
z@^4>jF$W$GnMZN4Bqaq?bA)T?_|oAOv2A8plYO
zHxbM2K9E()yQy9vj8u{C9(UxkbuvAM77$?jpzrg`5;ai9m;bI#@xzFukn;`seY)oz
zhrd@E}`+u0OA>c|VBg#h;2zfuRrg+4#SAZf?)5Tle#@|CY(U#Zw0yl?0G;u|EWLl%Bqo
zjS>E9p94#OLj-#LRuL20gF5-#@-Ij<9wN`I^o5R^J`Xj+CLrL|qdYJQ<7vn9M-=nc
zww|dhy5(AW@VSmyz5W|Rn$r*OjqWAFw&O}a6HzVT(GC~M@&8D&eZ~p=r3GB
z1)+Zn#eOkH-3k}OXuob-a^)99h#hJX6Apkg%O62cTTQfN*vKzBoxg$49JFk1D76k?g+t
zpZvx1p7wDpj6nol8{=QSFV2F4gWLA%J4YL(!$6q0gJ`kh%MS}K%U28i#86tH7k?8h
zcf|Rl08OR-QJnF8u}sff>$vKG{V|gzeB1a@8dfhNajsjI-Cv6D*KX?x?J*R81VIY&
zav5$S3+^?Di_;_^kJt1hT*Te1Rt$$eFLG>Su47DusKaRc_U`ae?99$*+`QMkZT^6~
z=bGb^H50^0R?m$^|I`qff`uDjQ&4kPiK=4soqkq>{#zdo<%
z^oY5}E;A
z@w0!-4<4EEW6xUjKAbmvu-^VnAYI)T0d$A!_aV>w=!S+`PW1j3Frx(;%Q?hs&+T2!
z1uBQd&5gSI{4Il@T^}z@5L`xXUs_BQX~Nyi10;Czb(h$2KXvK?Df`jWHEqh0T9Cth
zp?59G*zGm8Zg#jAd=e6e53#wL+?`I;?B*5ULMyAys%K;0Vg?u-k;_+-w~TH$kEO!O
zM0Y!!-U`Rj%*BG$Wj)j9Jtv=zh_2qb4)71+KmyhyWQk$UiSbXNBvrtA42996NdphA
z(d5XpKTeV@mp3AO9k;mg^LlTI#Ws@Q^t26LLu#@D9!i`BT5>vd71lQ7{ND`pVvTQ_
z7MFJ(6FW4Jx(&ZI77Hz^Id0gwCWFCcVD~7-b0YPe^LM|UDh|sKFecQk(n!H=`i!OPdfG6BHBY@
zk!>oQpfK+d6oZg9>`FrO~pCJr~d8}25@HtyFBUlNY_{{nR_|J<0(x|
zC-%;IhHo|w9Ivah$X+(>eRVLr)p<(4y9^#2d&E0wCA3edI|N9*F$P2!1#{jTGKJ$&
zJ~N})&JQq3sH|kOo@%J#At(V;*jv-TdUpT
zu)SG_oMfxIE&G%O7CpuBo_nd+dD*dq7$%u_fRV>?u#(+DHJY-r12LVW{I7EdF@g2y2x;VH}tQ?c2&U8+LJeZfAuH8k#
z4fh{v*PnIaRP9|-T}1*vQL&q}I1tb05h??AxZbcbn?>>oSXX9mZi&v2pMHv$0O7W!
z_LB{upXd#K7j4P;vAKzU6+!+)Mf}`F(C7}PsUycSlY9I<5N>k6yO#<`U(Cb6H2-!m
zru8P(5P8M}Ez!FJ=pb@m)2;Sv)^lT)0$FSI97b^f9?KLR&t0v}Id
zQocl=K$Tnb0CdflFtsk3P>J||0#^vA_k79a-qDL0|EaR04lDHE2crgj*su5*=k-P1
zb7qi(G%+VQ^={7M4_a@yAMJpIWrKI*bY7|r)#*O~`=OZb!ZVN_>(C;vENkk{G8>|d
zs{JnNvIxdD=i6G?j&a|iLq~kN+16Q}fY;QCMva+hypuX75vVB(RGlUS8dDbng+?2iI#EF+SzHsuT
zzwZ1jOybX9{{5-GpMP@gH~r5jxp>QE3qN??1q;9ZpFfkl=BrOW?XSVlfB3bV7oLB9
zzVG|5A3fH2&gK65i{E-7h!@ThOL1c{i_|1q~bx4`s1V}scST%jN>{9JI
zr-N+Sp&oO6Pb?4i{#f1HJk_l?=#4TN&0*Vf6`dDi+fZ5mQC2oFy@B^rK2q%+RM9(a
z^t*gUOcp}jl*&)8Jj-BNt+EkwLdALjt@3hOBF|r%JqgZnFp58OUJzaj}zdNdn8u~zKqX`<%hC@@~hidVP+g)>3HnkV*hK=Mi3F(Ql%XfTgb|FYujLU
z8-y#NENqu3({L#~ZG>2#;q8!4ol<9vGG9V0L;TFI_iY1K#I8nAtW{^c>UC~m;U*_)
z!(a_y`@645UdJFQpsoSO>(M!2uVe~z@>IUVG!k=ZsP~*pli$H8FP6Hl&hGIy6tw>Ntp2&T?f_?4`ZEyTp2}N#CNQ
zl9OxbB+6_qeW_rAuc_+*Gkw<4wb;+jJGZ}|sG>uv
z>ejX?&S_4|hz&Rx$_cN>=eaEaxj`x+L^%u2ed3H)uRLc}H|SN!irmesuvvKFmDinG
zSoj?$DpR$i1-(d;e$VG|SCz$meEFF#+Zo4Z^4g+lwBeB4NH(KoR+&w)Q81(LX|O-J
zcW)+*tQYJZ%3&jGHkl=`5quHJGyAS?AfCF8HupLKYp76GIkQW{CCmp6n6NCWmXVd+
zR6W++LpTiQ7o-MDh;{H%GX`0?I>boY>r3;@a&0;TT&I2(<02d}_aEu$vMTz|8@OhV
z9c3F%TGhNZ9Yh)gyc7HM!XNBw#{S&iAgG^>t+qnJmR;x&gizAnl(90M7n3VHAXh3^-PJF-a>_l!zIT{Z?RQ6&o)D`e
zVuMt6wSg_qIy%IDpR2F7=}lfdv~+NmD`*%s-ID4TL+s#9xYA4r5vSwyJ_pCQGwb`h
z?>OUC*WtY1)U~IVJq)wKonKchEG%4i^5n}ueabDo>9o1IA3WvUYv-JMjT1}YXkC?{
zLmM_MUIQgp)u5+BtkMA@fQb(da%l8MVw)T~To^#e%jQeAX0PROP@{yAUO|<*hk?8A
z=);X>2@rINY<>GXT|Qm&z4X-uhGr2I=3vjzpkqbDhQUr?d~j)Q5iG15xFj&-d&eSe
zs3E%v2e=N1#!kZo_0wjA&9eHo256J|rtDYI4}4dX$ix0!HR^}&vId%!vnB&$ET1ZQ
z?aj1x9lKEm!^*8p00>NkS)4knV{F-7^~=>yC5+{r$E&dY6v5Jtl|BpU#eG~_jvoe5
zT*Yf`X*K&t^VT_|600000NkvXXu0mjfy6Ex>
literal 0
HcmV?d00001
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index 7a27c65dab893..56a1d70030b8b 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -2029,6 +2029,8 @@
"Execute_Synchronization_Now": "Execute Synchronization Now",
"Exit_Full_Screen": "Exit Full Screen",
"Expand": "Expand",
+ "Expandable_message_composer": "Expandable Composer",
+ "Expandable_message_composer_description": "Expand the text input area to easily write and review longer messages without having to scroll up and down.",
"Expand_group": "Expand {{group}}",
"Expand_all": "Expand all",
"Expand_view": "Expand view",
diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.ts b/packages/ui-client/src/hooks/useFeaturePreviewList.ts
index 840f3956c6059..f8fef33c53900 100644
--- a/packages/ui-client/src/hooks/useFeaturePreviewList.ts
+++ b/packages/ui-client/src/hooks/useFeaturePreviewList.ts
@@ -5,7 +5,8 @@ export type FeaturesAvailable =
| 'enable-timestamp-message-parser'
| 'contextualbarResizable'
| 'newNavigation'
- | 'secondarySidebar';
+ | 'secondarySidebar'
+ | 'expandableMessageComposer';
export type FeaturePreviewProps = {
name: FeaturesAvailable;
@@ -73,6 +74,15 @@ export const defaultFeaturesPreview: FeaturePreviewProps[] = [
value: true,
},
},
+ {
+ name: 'expandableMessageComposer',
+ i18n: 'Expandable_message_composer',
+ description: 'Expandable_message_composer_description',
+ imageUrl: 'images/featurePreview/expandable-composer.png',
+ group: 'Message',
+ value: false,
+ enabled: true,
+ },
];
export const enabledDefaultFeatures = defaultFeaturesPreview.filter((feature) => feature.enabled);
diff --git a/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx b/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx
index b15ddb1a3ea0f..3c03bed370ea0 100644
--- a/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx
+++ b/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx
@@ -12,6 +12,7 @@ import {
MessageComposerToolbarSubmit,
MessageComposerSkeleton,
MessageComposerHint,
+ MessageComposerInputExpandable,
} from '.';
export default {
@@ -48,6 +49,21 @@ export const Default: StoryFn = () => (
);
+export const Expandable: StoryFn = () => (
+
+
+
+
+
+
+);
+
export const ToolbarActions: StoryFn = () => ;
export const WithHints: StoryFn = () => (
diff --git a/packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.spec.tsx b/packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.spec.tsx
new file mode 100644
index 0000000000000..804c21b9bba3a
--- /dev/null
+++ b/packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.spec.tsx
@@ -0,0 +1,117 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+
+import MessageComposerInputExpandable from './MessageComposerInputExpandable';
+
+test('should show expand button when dimensions.blockSize > 100', () => {
+ render(
+ ,
+ );
+
+ const expandButton = screen.getByRole('button');
+ expect(expandButton).toBeInTheDocument();
+ expect(expandButton).toHaveAttribute('title', 'Expand');
+});
+
+test('should not show expand button when dimensions.blockSize <= 100', () => {
+ render(
+ ,
+ );
+
+ const expandButton = screen.queryByRole('button');
+ expect(expandButton).not.toBeInTheDocument();
+});
+
+test('should expand input when expand button is clicked', () => {
+ render(
+ ,
+ );
+
+ const expandButton = screen.getByRole('button');
+ const textarea = screen.getByRole('textbox');
+
+ // Initially not expanded
+ expect(textarea).not.toHaveStyle({ height: '500px' });
+
+ // Click to expand
+ fireEvent.click(expandButton);
+
+ // Should be expanded now
+ expect(textarea).toHaveStyle({ height: '500px' });
+ expect(textarea).toHaveStyle({ maxHeight: '50vh' });
+ expect(expandButton).toHaveAttribute('title', 'Collapse');
+});
+
+test('should collapse input when collapse button is clicked', () => {
+ render(
+ ,
+ );
+
+ const expandButton = screen.getByRole('button');
+ const textarea = screen.getByRole('textbox');
+
+ // Expand first
+ fireEvent.click(expandButton);
+ expect(textarea).toHaveStyle({ height: '500px' });
+
+ // Click to collapse
+ fireEvent.click(expandButton);
+
+ // Should be collapsed now
+ expect(textarea).not.toHaveStyle({ height: '500px' });
+ expect(textarea).not.toHaveStyle({ maxHeight: '50vh' });
+ expect(expandButton).toHaveAttribute('title', 'Expand');
+});
+
+test('should auto-collapse when input is cleared', () => {
+ render(
+ ,
+ );
+
+ const expandButton = screen.getByRole('button');
+ const textarea = screen.getByRole('textbox');
+
+ // Expand first
+ fireEvent.click(expandButton);
+ expect(textarea).toHaveStyle({ height: '500px' });
+
+ // Type some text
+ fireEvent.change(textarea, { target: { value: 'Some text' } });
+ expect(textarea).toHaveStyle({ height: '500px' });
+
+ // Clear the text
+ fireEvent.change(textarea, { target: { value: '' } });
+
+ // Should auto-collapse
+ expect(textarea).not.toHaveStyle({ height: '500px' });
+ expect(textarea).not.toHaveStyle({ maxHeight: '50vh' });
+});
diff --git a/packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.tsx b/packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.tsx
new file mode 100644
index 0000000000000..86ed216e699b3
--- /dev/null
+++ b/packages/ui-composer/src/MessageComposer/MessageComposerInputExpandable.tsx
@@ -0,0 +1,55 @@
+import { css } from '@rocket.chat/css-in-js';
+import { Box, IconButton } from '@rocket.chat/fuselage';
+import { useState, type ComponentProps, ChangeEvent, forwardRef } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import MessageComposerInput from './MessageComposerInput';
+
+export type ExpandComposerButtonProps = ComponentProps & {
+ dimensions: Readonly<{
+ inlineSize: number;
+ blockSize: number;
+ }>;
+};
+
+const MessageComposerInputExpandable = forwardRef(
+ ({ dimensions, onChange, ...props }, ref) => {
+ const { t } = useTranslation();
+ const [expanded, setExpanded] = useState(false);
+
+ const handleChange = (event: ChangeEvent) => {
+ if (event.target.value.length === 0) {
+ setExpanded(false);
+ }
+
+ onChange?.(event);
+ };
+
+ return (
+ <>
+ {dimensions.blockSize > 100 && (
+
+ setExpanded(!expanded)}
+ />
+
+ )}
+
+ >
+ );
+ },
+);
+
+MessageComposerInputExpandable.displayName = 'MessageComposerInputExpandable';
+
+export default MessageComposerInputExpandable;
diff --git a/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposer.spec.tsx.snap b/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposer.spec.tsx.snap
index 4c69645109556..54d541276250d 100644
--- a/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposer.spec.tsx.snap
+++ b/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposer.spec.tsx.snap
@@ -189,6 +189,211 @@ exports[`renders Default without crashing 1`] = `
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus, eros convallis vulputate cursus, nisi neque eleifend libero, eget lacinia justo purus nec est. In at sodales ipsum. Sed lacinia quis purus eget pulvinar. Aenean eu pretium nunc, at aliquam magna. Praesent dignissim, tortor sed volutpat mattis, mauris diam pulvinar leo, porta commodo risus est non purus.
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus, eros convallis vulputate cursus, nisi neque eleifend libero, eget lacinia justo purus nec est. In at sodales ipsum. Sed lacinia quis purus eget pulvinar. Aenean eu pretium nunc, at aliquam magna. Praesent dignissim, tortor sed volutpat mattis, mauris diam pulvinar leo, porta commodo risus est non purus.
+