Skip to content

Conversation

Salvatoree07
Copy link

@Salvatoree07 Salvatoree07 commented Sep 16, 2025

Proposed changes (including videos or screenshots)

I updated two main files: processSlashCommand.ts and useComposerPopupBox.tsx. The changes ensure that parameters required by slash commands are now displayed in the composer box, making it easier for users to see what inputs are needed.
Additionally, I modified the regex logic to better extract parameters from the input string in the composer box.

User Experience Improvements
Thanks to these changes, new users can use slash commands more easily, even if they are not familiar with Rocket.Chat’s features. These updates also lay the groundwork for adding new elements to the composer box to further improve the slash command user experience. Note: this resolves only part of a broader issue, which will require additional work for complete implementation.

Video.per.issue.su.Slash.Commands.mp4

Issue(s)

965 form feature-request repository
RocketChat/feature-requests#965

Steps to test or reproduce

Further comments

I chose this approach because I believe it’s the most intuitive way to redesign the slash command UX. However, further improvements could be made, such as highlighting parameter names and making them non-editable.
In addition, the system for handling slash commands with multiple parameters can be enhanced in future iterations.

Summary by CodeRabbit

  • New Features

    • Slash commands now support labeled parameters using a “label: value” format.
    • Autocomplete for slash commands pre-fills a parameter label with a trailing colon and space.
    • Mentions and channels in parameters are formatted intelligently (e.g., “username: @” or “channel: #”).
    • Command input parsing aligns with the new labeled format for more consistent command entry.
  • Documentation

    • Added a comprehensive usage guide covering navigation, search, jumps, marks, file switching, options, and line editing.

@Salvatoree07 Salvatoree07 requested a review from a team as a code owner September 16, 2025 10:14
Copy link

changeset-bot bot commented Sep 16, 2025

🦋 Changeset detected

Latest commit: 41c689d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@rocket.chat/meteor Minor
@rocket.chat/core-typings Minor
@rocket.chat/rest-typings Minor
@rocket.chat/uikit-playground Patch
@rocket.chat/api-client Patch
@rocket.chat/apps Patch
@rocket.chat/core-services Patch
@rocket.chat/cron Patch
@rocket.chat/ddp-client Patch
@rocket.chat/freeswitch Patch
@rocket.chat/fuselage-ui-kit Major
@rocket.chat/gazzodown Major
@rocket.chat/http-router Patch
@rocket.chat/livechat Patch
@rocket.chat/model-typings Patch
@rocket.chat/ui-avatar Major
@rocket.chat/ui-client Major
@rocket.chat/ui-contexts Major
@rocket.chat/web-ui-registration Major
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/stream-hub-service Patch
@rocket.chat/license Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/models Patch
@rocket.chat/network-broker Patch
@rocket.chat/omni-core-ee Patch
@rocket.chat/mock-providers Patch
@rocket.chat/ui-video-conf Major
@rocket.chat/ui-voip Major
@rocket.chat/instance-status Patch
@rocket.chat/omni-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

dionisio-bot bot commented Sep 16, 2025

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@CLAassistant
Copy link

CLAassistant commented Sep 16, 2025

CLA assistant check
All committers have signed the CLA.

Copy link

coderabbitai bot commented Sep 16, 2025

Walkthrough

Updated slash-command parsing to require a colon-delimited label before parameters, adjusted composer popup selection/replacement to insert formatted params for slash commands, retained provider logic with whitespace-only edits, and added a standalone textual documentation file unrelated to runtime code.

Changes

Cohort / File(s) Summary
Slash command parsing
apps/meteor/client/lib/chats/flows/processSlashCommand.ts
Replaced regex: from capturing any trailing params to requiring <label>: <params>; added comment; switched to explicit match group access; command resolution unchanged.
Composer popup behavior
apps/meteor/client/views/room/composer/hooks/useComposerBoxPopup.ts
Introduced internal IItem; selection handler adapts to IItem; for '/' trigger, replacement appends formatted params based on item.params with colon labeling rules; minor setFilter computation refactor.
Composer popup provider (whitespace)
apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx
Whitespace-only change near permission check; no functional modifications.
Documentation addition
h Commandq
Added textual help/usage guide for the less command; no code impact.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant C as Composer Box
  participant P as Commands Popup
  participant S as Slash Parser

  U->>C: Type "/cmd ..."
  C->>P: Request suggestions (trigger="/")
  P-->>C: Suggest command items
  U->>P: Select command
  P-->>C: Selected item (with params)
  note over C: If trigger is "/"<br/>append "label: " formatting<br/>based on item.params
  C->>C: Insert "/cmd <label>: "
  U->>C: Fill parameters
  C->>S: Submit message text
  S->>S: Parse with ^\/([^\s]*)\s*[^:]*:\s*(.*)$
  S-->>C: Extract cmd and params
  C-->>U: Proceed with command handling
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I twitch my ears at every slash,
A label’s colon—thump, then dash.
The popup whispers, “Type it right,”
We nibble params, crisp and light.
Regex burrows, finds the trail—
Commands now hop without a fail. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat: Improving the UX of Slash Commands" concisely and accurately captures the primary intent of the changeset (regex and composer updates to improve slash-command UX) as described in the PR summary. It is specific enough for a teammate scanning history, uses concise phrasing, and avoids irrelevant noise.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
apps/meteor/client/views/room/composer/hooks/useComposerBoxPopup.ts (3)

18-24: Avoid widening to a separate IItem; keep generics consistent or use intersection.

Introducing IItem forces casts (as T) and weakens type safety. Prefer T & { params?: string; description?: string; permission?: string } for this hook’s internal usage.

-interface IItem {
-	_id: string;
-	sort?: number;
-	description?: string;
-	params?: string;
-	permission?: string;
-}
+type WithCommandMeta = { params?: string; description?: string; permission?: string };

And then type the handler as (item: T & WithCommandMeta) => void to drop the casts.


95-102: Type cast hides real mismatches.

commandsRef.current.select(item as T) compiles but can mask runtime shape issues. If you adopt the intersection type above, you can pass item directly without casting.


124-126: Cursor placement UX (optional).

Consider placing the caret after the inserted “@”/“#” when present so users can immediately type the identifier.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86c4b1e and 48b405d.

📒 Files selected for processing (4)
  • apps/meteor/client/lib/chats/flows/processSlashCommand.ts (1 hunks)
  • apps/meteor/client/views/room/composer/hooks/useComposerBoxPopup.ts (4 hunks)
  • apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx (1 hunks)
  • h Commandq (1 hunks)
🔇 Additional comments (3)
apps/meteor/client/lib/chats/flows/processSlashCommand.ts (1)

55-64: Behavioral check: unparsed “/…” messages are sent as text.

Given the current early-return on no match, any “/cmd foo” without a colon would bypass slash handling and be sent as plain text. After the parser fix above, this becomes moot, but please confirm there’s no path that still returns undefined for valid commands.

apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx (2)

332-334: No functional changes in this hunk. LGTM.

Whitespace-only tweak around the permission check.


371-386: Slash preview regex still assumes “/cmd ” (space), not “label:”.

matchSelectorRegex: /(?:^)(\/[\w\d\S]+ )[^]*$/ anchors on a trailing space after the command. If the composer now inserts “: …”, ensure previews still trigger as expected.

Comment on lines 13 to 22
// This regex matches strings starting with '/', captures the command name (non-whitespace) skips optional spaces and a label before a colon, and finally captures the parameters after the colon.
const match = msg.match(/^\/([^\s]*)\s*[^:]*:\s*(.*)$/);
if (!match) {
return undefined;
}

const [, cmd, params] = match;
const cmd = match[1];
const params = match[2];

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Colon-required parsing breaks existing slash commands and mis-parses URLs; make label optional and strip only when it matches the command’s declared param label.

Requiring a “label:” after the command means inputs like “/giphy cat” or “/invite @user” no longer parse and will be sent as plain text. It can also eat “http:” in “/topic http://…”. Additionally, ([^\s]*) permits an empty command.

Fix: parse like before, then conditionally drop a leading “:” only if it matches the registered command’s params (handling “@”/“#” forms), keeping full backward compatibility.

Apply this diff within parse:

-	// This regex matches strings starting with '/', captures the command name (non-whitespace) skips optional spaces and a label before a colon, and finally captures the parameters after the colon.
-	const match = msg.match(/^\/([^\s]*)\s*[^:]*:\s*(.*)$/);
+	// Match "/<cmd> <params...>" and keep params intact; we'll strip an optional "<label>:" only
+	// if it matches the command's declared params (e.g., "channel: #", "username: @").
+	const match = msg.match(/^\/(\S+)\s*(.*)$/);
 	if (!match) {
 		return undefined;
 	}
-
-	const cmd = match[1];
-	const params = match[2];
+	const cmd = match[1];
+	let params = match[2] ?? '';
+
+	// If this is a known command with a declared params label, strip a leading "<label>:"
+	// that the composer may have inserted (e.g., "channel: #", "username: @").
+	const meta = slashCommands.commands[cmd];
+	if (meta && typeof meta !== 'string' && typeof meta.params === 'string' && meta.params) {
+		const raw = meta.params;
+		const label = raw.startsWith('@') || raw.startsWith('#') ? raw.slice(1) : raw;
+		if (label) {
+			// escape regex meta chars in label
+			const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+			const re = new RegExp(`^\\s*(?:${esc(label)})\\s*:\\s*`);
+			if (re.test(params)) {
+				params = params.replace(re, '');
+			}
+		}
+	}

Optional (outside this hunk): import escapeRegExp instead of the inline esc helper if preferred.

// at top-level imports
import { escapeHTML /*, escapeRegExp*/ } from '@rocket.chat/string-helpers';
🤖 Prompt for AI Agents
In apps/meteor/client/lib/chats/flows/processSlashCommand.ts around lines 13-21,
the current regex forces a required "label:" and allows an empty command which
breaks commands and mis-parses URLs; change parsing to first match a slash
command with a non-empty name and optional trailing text (no required colon),
then if a registered command exists check its declared params and only strip a
leading "<label>:" when that label exactly matches the command's param label
(handle optional "@" or "#" prefixes and use a safe escaped comparison); ensure
fallback returns the whole trailing text unchanged for backward compatibility
and prevent empty command names by using a non-empty capture for the command;
optionally import escapeRegExp from @rocket.chat/string-helpers to safely build
the label match.

Comment on lines 13 to 14
// This regex matches strings starting with '/', captures the command name (non-whitespace) skips optional spaces and a label before a colon, and finally captures the parameters after the colon.
const match = msg.match(/^\/([^\s]*)\s*[^:]*:\s*(.*)$/);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Require at least one non-space char for the command token.

Change ([^\s]*) to (\S+) to avoid matching “/ label: …” with an empty command.

-const match = msg.match(/^\/([^\s]*)\s*[^:]*:\s*(.*)$/);
+const match = msg.match(/^\/(\S+)\s*(.*)$/);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// This regex matches strings starting with '/', captures the command name (non-whitespace) skips optional spaces and a label before a colon, and finally captures the parameters after the colon.
const match = msg.match(/^\/([^\s]*)\s*[^:]*:\s*(.*)$/);
// This regex matches strings starting with '/', captures the command name (non-whitespace), skips optional spaces and a label before a colon, and finally captures the parameters after the colon.
const match = msg.match(/^\/(\S+)\s*[^:]*:\s*(.*)$/);
🤖 Prompt for AI Agents
In apps/meteor/client/lib/chats/flows/processSlashCommand.ts around lines 13-14,
the regex allows an empty command token because it uses ([^\s]*); update the
pattern to require at least one non-space character by replacing ([^\s]*) with
(\S+), i.e. change the regex to use (\S+) for the command capture so inputs like
"/   label: ..." no longer match as an empty command.

Comment on lines 113 to 128
// formattedParams formats command parameters by detecting '@' or '#' prefixes
const formattedParams =
item.params?.startsWith('@') || item.params?.startsWith('#')
? `${item.params.slice(1)}: ${item.params.charAt(0)}`
: `${item.params}: `;
chat?.composer?.replaceText(
(option.prefix ?? option.trigger ?? '') +
option.getValue(item as T) +
(option.suffix ?? '') +
(option.trigger === '/' ? formattedParams : ''),
{
start: value.lastIndexOf(result[1] + result[2]),
end: chat?.composer?.selection.start,
},
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against “undefined: ” being inserted for commands without params; also trim and handle “@/#” cleanly.

When item.params is absent, the else-branch inserts undefined: into the composer. This is user-facing.

Apply this diff:

-			// formattedParams formats command parameters by detecting '@' or '#' prefixes
-			const formattedParams =
-				item.params?.startsWith('@') || item.params?.startsWith('#')
-					? `${item.params.slice(1)}: ${item.params.charAt(0)}`
-					: `${item.params}: `;
+			// Format an optional "<label>: " or "<label>: @|#"
+			const rawParams = item.params?.trim();
+			const formattedParams =
+				rawParams
+					? (rawParams.startsWith('@') || rawParams.startsWith('#')
+							? `${rawParams.slice(1)}: ${rawParams.charAt(0)}`
+							: `${rawParams}: `)
+					: '';
 			chat?.composer?.replaceText(
 				(option.prefix ?? option.trigger ?? '') +
 					option.getValue(item as T) +
 					(option.suffix ?? '') +
-					(option.trigger === '/' ? formattedParams : ''),
+					(option.trigger === '/' ? formattedParams : ''),

Optional: only append formattedParams when truthy to avoid stray spaces.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// formattedParams formats command parameters by detecting '@' or '#' prefixes
const formattedParams =
item.params?.startsWith('@') || item.params?.startsWith('#')
? `${item.params.slice(1)}: ${item.params.charAt(0)}`
: `${item.params}: `;
chat?.composer?.replaceText(
(option.prefix ?? option.trigger ?? '') +
option.getValue(item as T) +
(option.suffix ?? '') +
(option.trigger === '/' ? formattedParams : ''),
{
start: value.lastIndexOf(result[1] + result[2]),
end: chat?.composer?.selection.start,
},
);
// Format an optional "<label>: " or "<label>: @|#"
const rawParams = item.params?.trim();
const formattedParams =
rawParams
? (rawParams.startsWith('@') || rawParams.startsWith('#')
? `${rawParams.slice(1)}: ${rawParams.charAt(0)}`
: `${rawParams}: `)
: '';
chat?.composer?.replaceText(
(option.prefix ?? option.trigger ?? '') +
option.getValue(item as T) +
(option.suffix ?? '') +
(option.trigger === '/' ? formattedParams : ''),
{
start: value.lastIndexOf(result[1] + result[2]),
end: chat?.composer?.selection.start,
},
);
🤖 Prompt for AI Agents
In apps/meteor/client/views/room/composer/hooks/useComposerBoxPopup.ts around
lines 113–127, the current logic can insert "undefined: " when item.params is
missing and doesn't trim or handle '@'/'#' cleanly; update formattedParams to
first guard that item.params is a truthy string, trim it, then if it starts with
'@' or '#' strip the prefix and produce "stripped: prefix" (or just the stripped
string followed by ': ' if that exact format is required), otherwise produce
"trimmed: " only when trimmed is non-empty; finally, only append formattedParams
to the replacement string when it is a non-empty string to avoid stray spaces or
"undefined" appearing in the composer.

h Commandq Outdated
Comment on lines 1 to 20

SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS

Commands marked with * may be preceded by a number, _N.
Notes in parentheses indicate the behavior if _N is given.
A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.

h H Display this help.
q :q Q :Q ZZ Exit.
---------------------------------------------------------------------------

MMOOVVIINNGG

e ^E j ^N CR * Forward one line (or _N lines).
y ^Y k ^K ^P * Backward one line (or _N lines).
f ^F ^V SPACE * Forward one window (or _N lines).
b ^B ESC-v * Backward one window (or _N lines).
z * Forward one window (and set window to _N).
w * Backward one window (and set window to _N).
ESC-SPACE * Forward one window, but don't stop at end-of-file.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove stray “less” help document from the repository.

This appears unrelated to the PR and may carry licensing/maintenance baggage. Please drop it from the commit or relocate to appropriate docs if truly needed.

🤖 Prompt for AI Agents
In file "h Commandq" lines 1-20, this stray "less" help document should be
removed from the PR: delete the file from the repository (git rm --cached or git
rm as appropriate) or move it into the proper docs directory if it is
intentionally required, update any references and the commit (amend or create a
new commit) so the removal appears in the branch, and run a quick git
status/ls-tree to confirm the file is no longer included in the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants