- Changed image format for agent comments screenshot in README. - Improved table of contents in README for better navigation. - Refactored imports in IPC config and discovery services for consistency. - Added shimmer effect for waiting members in CSS. - Enhanced dashboard view to display active teams with online indicators. - Updated team provisioning components to support message severity. - Improved task detail dialog layout for related tasks and dependencies. - Adjusted team model selector default value and refined member status handling. - Fixed minor styling issues in messages panel and tab bar. These changes aim to improve user experience and maintainability across the application.
90 lines
3.8 KiB
TypeScript
90 lines
3.8 KiB
TypeScript
/**
|
|
* Shared utility for converting @memberName and @teamName mentions in plain text
|
|
* to markdown links with mention:// and team:// protocols.
|
|
*
|
|
* Used by UserChatGroup, TeammateMessageItem, ActivityItem, TaskCommentsSection.
|
|
* MarkdownViewer already handles rendering mention:// and team:// links as colored badges.
|
|
*/
|
|
|
|
/**
|
|
* Convert `@memberName` in plain text to markdown links with mention:// protocol.
|
|
* Encodes color in the URL so MarkdownViewer can render colored badges without extra context.
|
|
* Greedy match: longer names are tried first to avoid partial matches.
|
|
*
|
|
* @param text - The plain text to process
|
|
* @param memberColorMap - Map of member name → color key (e.g. "blue", "red")
|
|
* @returns Text with @mentions replaced by markdown links
|
|
*/
|
|
export function linkifyMentionsInMarkdown(
|
|
text: string,
|
|
memberColorMap: Map<string, string>
|
|
): string {
|
|
if (memberColorMap.size === 0) return text;
|
|
|
|
// Sort by name length descending for greedy matching
|
|
const names = [...memberColorMap.keys()].sort((a, b) => b.length - a.length);
|
|
const escaped = names.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
const pattern = new RegExp(
|
|
// eslint-disable-next-line no-useless-escape -- backslash-quote and backslash-hyphen needed in template literal for RegExp
|
|
`(^|[\\s(\\[{"\'])@(${escaped.join('|')})(?=[\\s,.:;!?)\\]}'\u2019-]|$)`,
|
|
'gi'
|
|
);
|
|
|
|
return text.replace(pattern, (_match: string, prefix: string, name: string) => {
|
|
// Find the canonical name (case-insensitive lookup)
|
|
const canonical = names.find((n) => n.toLowerCase() === name.toLowerCase()) ?? name;
|
|
const color = memberColorMap.get(canonical) ?? '';
|
|
return `${prefix}[@${canonical}](mention://${encodeURIComponent(color)}/${encodeURIComponent(canonical)})`;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Convert `@teamName` in plain text to markdown links with team:// protocol.
|
|
* Greedy match: longer names are tried first to avoid partial matches.
|
|
*
|
|
* @param text - The plain text to process
|
|
* @param teamNames - Set or array of known team names
|
|
* @returns Text with @teamName replaced by markdown links
|
|
*/
|
|
export function linkifyTeamMentionsInMarkdown(
|
|
text: string,
|
|
teamNames: ReadonlySet<string> | readonly string[]
|
|
): string {
|
|
const names: readonly string[] = Array.isArray(teamNames) ? teamNames : [...teamNames];
|
|
if (names.length === 0) return text;
|
|
|
|
// Sort by name length descending for greedy matching
|
|
const sorted = [...names].sort((a, b) => b.length - a.length);
|
|
const escaped = sorted.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
const pattern = new RegExp(
|
|
// eslint-disable-next-line no-useless-escape -- backslash-quote and backslash-hyphen needed in template literal for RegExp
|
|
`(^|[\\s(\\[{"\'])@(${escaped.join('|')})(?=[\\s,.:;!?)\\]}'\u2019-]|$)`,
|
|
'gi'
|
|
);
|
|
|
|
return text.replace(pattern, (_match: string, prefix: string, name: string) => {
|
|
const canonical = sorted.find((n) => n.toLowerCase() === name.toLowerCase()) ?? name;
|
|
return `${prefix}[${canonical}](team://${encodeURIComponent(canonical)})`;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Apply both member and team linkification. Team names are matched first to avoid
|
|
* team names being captured as member mentions when they share similar names.
|
|
*
|
|
* @param text - The plain text to process
|
|
* @param memberColorMap - Map of member name → color key
|
|
* @param teamNames - Known team names
|
|
* @returns Text with both @member and @team mentions replaced by markdown links
|
|
*/
|
|
export function linkifyAllMentionsInMarkdown(
|
|
text: string,
|
|
memberColorMap: Map<string, string>,
|
|
teamNames: ReadonlySet<string> | readonly string[] = []
|
|
): string {
|
|
// Apply team linkification first (team names tend to be longer / more specific)
|
|
let result = linkifyTeamMentionsInMarkdown(text, teamNames);
|
|
// Then member linkification on the remaining text
|
|
result = linkifyMentionsInMarkdown(result, memberColorMap);
|
|
return result;
|
|
}
|