From f25d3650aeea4a47f89ef65cefe83cb2647d366f Mon Sep 17 00:00:00 2001 From: dancer Date: Tue, 26 May 2026 20:24:58 +0100 Subject: [PATCH 1/2] refactor(slack): share provider helpers --- packages/adapter-slack/src/blocks/index.ts | 5 +-- packages/adapter-slack/src/cards.ts | 11 ++---- packages/adapter-slack/src/markdown.ts | 41 +++++----------------- 3 files changed, 13 insertions(+), 44 deletions(-) diff --git a/packages/adapter-slack/src/blocks/index.ts b/packages/adapter-slack/src/blocks/index.ts index 66f98492..3960c474 100644 --- a/packages/adapter-slack/src/blocks/index.ts +++ b/packages/adapter-slack/src/blocks/index.ts @@ -1,3 +1,4 @@ +import { markdownBoldToSlackMrkdwn } from "../format"; import { SlackBlockError } from "./errors"; import { LIMITS } from "./limits"; import type { @@ -453,10 +454,6 @@ function rawText( }; } -function markdownBoldToSlackMrkdwn(text: string): string { - return text.replace(/\*\*(.+?)\*\*/g, "*$1*"); -} - function mapButtonStyle( style: SlackButtonStyle | undefined ): "danger" | "primary" | undefined { diff --git a/packages/adapter-slack/src/cards.ts b/packages/adapter-slack/src/cards.ts index 07380658..ec47eba5 100644 --- a/packages/adapter-slack/src/cards.ts +++ b/packages/adapter-slack/src/cards.ts @@ -27,6 +27,7 @@ import type { TextElement, } from "chat"; import { cardChildToFallbackText, tableElementToAscii } from "chat"; +import { markdownBoldToSlackMrkdwn } from "./format"; /** * Convert emoji placeholders in text to Slack format. @@ -168,14 +169,8 @@ function convertChildToBlocks( } } -/** Convert standard Markdown formatting to Slack mrkdwn */ -function markdownToMrkdwn(text: string): string { - // **bold** → *bold* - return text.replace(/\*\*(.+?)\*\*/g, "*$1*"); -} - export function convertTextToBlock(element: TextElement): SlackBlock { - const text = markdownToMrkdwn(convertEmoji(element.content)); + const text = markdownBoldToSlackMrkdwn(convertEmoji(element.content)); let formattedText = text; // Apply style @@ -437,7 +432,7 @@ export function convertFieldsToBlock(element: FieldsElement): SlackBlock { // Add label and value as separate field items fields.push({ type: "mrkdwn", - text: `*${markdownToMrkdwn(convertEmoji(field.label))}*\n${markdownToMrkdwn(convertEmoji(field.value))}`, + text: `*${markdownBoldToSlackMrkdwn(convertEmoji(field.label))}*\n${markdownBoldToSlackMrkdwn(convertEmoji(field.value))}`, }); } diff --git a/packages/adapter-slack/src/markdown.ts b/packages/adapter-slack/src/markdown.ts index eb92c2bd..74d6b81c 100644 --- a/packages/adapter-slack/src/markdown.ts +++ b/packages/adapter-slack/src/markdown.ts @@ -32,11 +32,9 @@ import { stringifyMarkdown, tableToAscii, } from "chat"; +import { linkBareSlackMentions, slackMrkdwnToMarkdown } from "./format"; -// Match bare @mentions (e.g. "@george") to rewrite as Slack's `<@george>`. -// The lookbehind excludes `<` (already-formatted mentions like `<@U123>`) and -// any word character, so email addresses like `user@example.com` are left alone. -const BARE_MENTION_REGEX = /(? -> @name or <@U123> -> @U123 - markdown = markdown.replace(/<@([A-Z0-9_]+)\|([^<>]+)>/g, "@$2"); - markdown = markdown.replace(/<@([A-Z0-9_]+)>/g, "@$1"); - - // Channel mentions: <#C123|name> -> #name - markdown = markdown.replace(/<#[A-Z0-9_]+\|([^<>]+)>/g, "#$1"); - markdown = markdown.replace(/<#([A-Z0-9_]+)>/g, "#$1"); - - // Links: -> [text](url) - markdown = markdown.replace( - /<(https?:\/\/[^|<>]+)\|([^<>]+)>/g, - "[$2]($1)" - ); - - // Bare links: -> url - markdown = markdown.replace(/<(https?:\/\/[^<>]+)>/g, "$1"); - - // Bold: *text* -> **text** (Slack uses single * for bold) - markdown = markdown.replace(/(? ~~text~~ - markdown = markdown.replace(/(?"), + linkBareMentionNames(linkBareSlackMentions(text)), "slack" ); } @@ -155,7 +128,7 @@ export class SlackFormatConverter extends BaseFormatConverter { } if (isTextNode(node)) { - return node.value.replace(BARE_MENTION_REGEX, "<@$1>"); + return linkBareMentionNames(linkBareSlackMentions(node.value)); } if (isStrongNode(node)) { @@ -219,3 +192,7 @@ export class SlackFormatConverter extends BaseFormatConverter { return this.defaultNodeToText(node, (child) => this.nodeToMrkdwn(child)); } } + +function linkBareMentionNames(text: string): string { + return text.replace(BARE_MENTION_PATTERN, "<@$1>"); +} From 8fee07968050bd9ae393425fe5457b73e625f48e Mon Sep 17 00:00:00 2001 From: dancer Date: Tue, 26 May 2026 20:29:21 +0100 Subject: [PATCH 2/2] chore: add changeset --- .changeset/strict-moons-sneeze.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/strict-moons-sneeze.md diff --git a/.changeset/strict-moons-sneeze.md b/.changeset/strict-moons-sneeze.md new file mode 100644 index 00000000..80a3ef6c --- /dev/null +++ b/.changeset/strict-moons-sneeze.md @@ -0,0 +1,5 @@ +--- +"@chat-adapter/slack": patch +--- + +reuse low-level Slack formatting helpers in the adapter