Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/web/core/hooks/use-parse-editor-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export const useParseEditorContent = (args: TArgs) => {
(htmlContent: string): TCustomComponentsMetaData => {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlContent, "text/html");
const imageMetaData: TCustomComponentsMetaData["file_assets"] = [];
const filesMetaData: TCustomComponentsMetaData["file_assets"] = [];
// process image components
const imageComponents = doc.querySelectorAll("image-component");
imageComponents.forEach((element) => {
Expand All @@ -229,7 +229,7 @@ export const useParseEditorContent = (args: TArgs) => {
workspaceSlug,
});
if (assetSrc) {
imageMetaData.push({
filesMetaData.push({
id: src,
name: src,
url: assetSrc,
Expand Down Expand Up @@ -258,7 +258,7 @@ export const useParseEditorContent = (args: TArgs) => {
});

return {
file_assets: imageMetaData,
file_assets: filesMetaData,
user_mentions: userMentions,
};
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,19 @@ export const parseCustomComponents = (args: TArgs): Record<string, Handle> => {
},
"mention-component": (_state, node) => {
const properties = node.properties || {};
const userId = String(properties.entity_identifier);
const userDetails = metaData.user_mentions.find((user) => user.id === userId);
if (!userDetails) return createTextNode("");
return createTextNode(`[@${userDetails.display_name || "Unknown user"}](${userDetails.url || ""}) `);
const mentionType = String(properties.entity_name);

let url: string = "";
let tag: string = "@";
if (mentionType === "user_mention") {
const userId = String(properties.entity_identifier);
const userDetails = metaData.user_mentions.find((user) => user.id === userId);
if (!userDetails) return createTextNode("");
url = userDetails.url || "";
tag = `@${userDetails.display_name || "Unknown user"}`;
}

return createTextNode(`[${tag}](${url}) `);
Comment on lines +34 to +44
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

When mentionType is not 'user_mention', the function returns invalid markdown with an empty link [@](). Non-user mention types should either be handled with appropriate logic or return an empty text node. Consider adding an else clause that returns createTextNode('') for unhandled mention types, or add a comment explaining why empty links are intentional.

Suggested change
let url: string = "";
let tag: string = "@";
if (mentionType === "user_mention") {
const userId = String(properties.entity_identifier);
const userDetails = metaData.user_mentions.find((user) => user.id === userId);
if (!userDetails) return createTextNode("");
url = userDetails.url || "";
tag = `@${userDetails.display_name || "Unknown user"}`;
}
return createTextNode(`[${tag}](${url}) `);
if (mentionType === "user_mention") {
const userId = String(properties.entity_identifier);
const userDetails = metaData.user_mentions.find((user) => user.id === userId);
if (!userDetails) return createTextNode("");
const url = userDetails.url || "";
const tag = `@${userDetails.display_name || "Unknown user"}`;
return createTextNode(`[${tag}](${url}) `);
}
// For non-user mention types, emit no markdown to avoid invalid empty links.
return createTextNode("");

Copilot uses AI. Check for mistakes.
},
Comment on lines 30 to 45
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Non-user mention types produce broken markdown links.

When mentionType is not "user_mention", the handler returns [@]() — an empty URL and no meaningful display text. This produces unusable markdown output for other entity types.

In contrast, use-parse-editor-content.ts handles non-user mentions by calling parseAdditionalEditorContent to retrieve proper textContent and redirectionPath. The metadata structure (TCustomComponentsMetaData) currently only contains user_mentions, so there's no way to look up details for other mention types here.

Consider either:

  1. Extending TCustomComponentsMetaData to include other mention types, or
  2. Returning an empty text node for unsupported mention types (consistent with the early return pattern used elsewhere)
Suggested minimal fix to avoid broken links
       if (mentionType === "user_mention") {
         const userId = String(properties.entity_identifier);
         const userDetails = metaData.user_mentions.find((user) => user.id === userId);
         if (!userDetails) return createTextNode("");
         url = userDetails.url || "";
         tag = `@${userDetails.display_name || "Unknown user"}`;
+      } else {
+        // Non-user mention types not yet supported in markdown parsing
+        return createTextNode("");
       }
 
       return createTextNode(`[${tag}](${url}) `);
📝 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
"mention-component": (_state, node) => {
const properties = node.properties || {};
const userId = String(properties.entity_identifier);
const userDetails = metaData.user_mentions.find((user) => user.id === userId);
if (!userDetails) return createTextNode("");
return createTextNode(`[@${userDetails.display_name || "Unknown user"}](${userDetails.url || ""}) `);
const mentionType = String(properties.entity_name);
let url: string = "";
let tag: string = "@";
if (mentionType === "user_mention") {
const userId = String(properties.entity_identifier);
const userDetails = metaData.user_mentions.find((user) => user.id === userId);
if (!userDetails) return createTextNode("");
url = userDetails.url || "";
tag = `@${userDetails.display_name || "Unknown user"}`;
}
return createTextNode(`[${tag}](${url}) `);
},
"mention-component": (_state, node) => {
const properties = node.properties || {};
const mentionType = String(properties.entity_name);
let url: string = "";
let tag: string = "@";
if (mentionType === "user_mention") {
const userId = String(properties.entity_identifier);
const userDetails = metaData.user_mentions.find((user) => user.id === userId);
if (!userDetails) return createTextNode("");
url = userDetails.url || "";
tag = `@${userDetails.display_name || "Unknown user"}`;
} else {
// Non-user mention types not yet supported in markdown parsing
return createTextNode("");
}
return createTextNode(`[${tag}](${url}) `);
},
🤖 Prompt for AI Agents
In `@packages/utils/src/editor/markdown-parser/custom-components-handler.ts`
around lines 30 - 45, The "mention-component" handler currently emits an empty
markdown link for non-user mention types (producing "[@]() "), so change the
handler to avoid producing broken links: if mentionType !== "user_mention"
return createTextNode("") early (consistent with the existing early-return
pattern), or alternatively extend TCustomComponentsMetaData and metaData to
include other mention collections and use parseAdditionalEditorContent-like
lookups to populate tag and url; update references to metaData.user_mentions,
the "mention-component" handler, and createTextNode accordingly so unsupported
mention types do not emit empty markdown links.

...parseExtendedCustomComponents({ metaData }),
};
};

export const parseExtendedCustomComponents = (_args: TArgs): Record<string, Handle> => ({});
Loading