Add conversation reference links to private notes - #475
Conversation
📝 WalkthroughWalkthroughAdds ticket-reference support to the editor. Private notes can search conversations with ChangesTicket reference support
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/apps/main/src/components/editor/TextEditor.vue`:
- Around line 291-315: Update TicketReference to parse its rendered
a.ld-ticket-reference markup on HTML reload: add a parseHTML rule matching the
ticket-reference anchor, map its href into the node’s href attribute, and render
a data-type marker consistent with the parser. Add a round-trip test that
serializes and reloads a ticket reference, preserving its node type, label, and
href.
In `@frontend/apps/main/src/components/editor/TicketReferenceList.vue`:
- Around line 26-28: Move the minimum-query guidance from the hardcoded text in
TicketReferenceList’s query-length hint into the locale messages, defining a
translated message that accepts the minimum length as a count parameter. Update
the span to render that message through $t while passing
MIN_REFERENCE_QUERY_LENGTH, preserving the existing visibility condition.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: da4e04da-985d-408e-b2ef-cd6fa8a9aeb3
📒 Files selected for processing (6)
frontend/apps/main/src/components/editor/TextEditor.vuefrontend/apps/main/src/components/editor/TicketReferenceList.vuefrontend/apps/main/src/components/editor/ticketReference.jsfrontend/apps/main/src/components/editor/ticketReference.test.jsfrontend/apps/main/src/components/editor/ticketReferenceSuggestion.jsfrontend/apps/main/src/features/conversation/ReplyBoxContent.vue
| const TicketReference = Mention.extend({ | ||
| name: 'ticketReference', | ||
|
|
||
| addAttributes() { | ||
| return { | ||
| ...this.parent?.(), | ||
| href: { default: null } | ||
| } | ||
| }, | ||
|
|
||
| renderText({ node }) { | ||
| return `#${node.attrs.label}` | ||
| }, | ||
|
|
||
| renderHTML({ node, HTMLAttributes }) { | ||
| return [ | ||
| 'a', | ||
| { | ||
| ...HTMLAttributes, | ||
| href: node.attrs.href, | ||
| class: [HTMLAttributes.class, 'ld-ticket-reference'].filter(Boolean).join(' ') | ||
| }, | ||
| `#${node.attrs.label}` | ||
| ] | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the declared TipTap versions and existing round-trip coverage.
fd -HI -t f 'package.json' frontend | while IFS= read -r file; do
rg -n -C2 '"`@tiptap/`(extension-mention|extension-link|vue-3)"' "$file" || true
done
# Locate tests and editor paths that should cover HTML serialization and reload.
rg -n -C3 \
--glob '*.{test,spec}.{js,ts,vue}' \
'ticketReference|ld-ticket-reference|setContent|getHTML|handleDOMEvents' \
frontend/apps/main || trueRepository: abhinavxd/libredesk
Length of output: 1549
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== referenced file outline =="
ast-grep outline frontend/apps/main/src/components/editor/TextEditor.vue --match TicketReference --view expanded || true
echo "== TextEditor relevant sections =="
sed -n '260,420p' frontend/apps/main/src/components/editor/TextEditor.vue
echo "== package versions (all package.json) =="
python3 - <<'PY'
import json, pathlib
for p in pathlib.Path('frontend/apps/main').rglob('package.json'):
data=json.loads(p.read_text())
deps={}
for k in ['dependencies','devDependencies','peerDependencies','resolutions?']:
if k in data:
for n,v in data[k].items():
if n.startswith('`@tiptap/`'):
deps[n]=v
if deps:
print(p)
for k,v in deps.items(): print(f' {k}: {v}')
PY
echo "== node_modules mention/link source availability =="
fd 'mention\.js$|link\.js$|mention\.ts$|link\.ts$' . -d 3 | sed -n '1,40p'Repository: abhinavxd/libredesk
Length of output: 4429
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== git status/stat =="
git status --short
git diff --stat || true
echo "== TextEditor around TicketReference with line numbers =="
rg -n -C4 '\bTicketReference\b|CustomMention|handleDOMEvents|ld-ticket-reference' frontend/apps/main/src/components/editor/TextEditor.vue
echo "== editor tests around ticketReference =="
sed -n '1,240p' frontend/apps/main/src/components/editor/ticketReference.test.js
echo "== search for TicketReference tests/usages =="
rg -n -C3 'TicketReference|ticketReference|ld-ticket-reference|ticket-reference' frontend/apps/main || trueRepository: abhinavxd/libredesk
Length of output: 15557
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Fetch `@tiptap/extension-mention` 2.27.1 =="
python3 - <<'PY'
import urllib.request, json, textwrap
url='https://registry.npmjs.org/@tiptap/extension-mention/2.27.1'
try:
data=json.load(urllib.request.urlopen(url, timeout=20))
dist=data['dist']
print('version', data.get('version'))
print('tarball', dist.get('tarball'))
except Exception as e:
print('registry_fetch_error', e)
PY
TARBALL='https://registry.npmjs.org/@tiptap/extension-mention/2.27.1'
tmp="$(mktemp -d)"
cd "$tmp"
curl -Lfs "$TARBALL/package.tgz" -o mention.tgz 2>/dev/null || curl -Lfs "$TARBALL" -o mention.tgz
tar -tzf mention.tgz | sed -n '1,80p'
echo "-- src/mention.ts --"
tar -xOzf mention.tgz package/src/mention.ts | sed -n '1,260p'
echo "-- package/dist/esm/*.d.ts (links) --"
tar -tzf mention.tgz | rg '(^package/dist/esm/|\.d\.ts$)' | rg 'mention|link|node' | sed -n '1,120p'
echo "-- tar export of likely mention/link files --"
for f in package/src/mention.ts package/src/mention-extension.ts package/src/utilities.ts; do
tar -xOzf mention.tgz "$f" 2>/dev/null | sed -n '1,220p' || true
doneRepository: abhinavxd/libredesk
Length of output: 463
🌐 Web query:
@tiptap/extension-mention parseHTML data-type mention node 2.27.1
💡 Result:
In Tiptap, the @tiptap/extension-mention node identifies and parses mentions by looking for a specific HTML structure, primarily the data-type="mention" attribute on a span element [1][2]. When the Tiptap editor parses HTML (e.g., via setContent), it uses the parseHTML method defined in the Mention extension [3][1]. By default, this is implemented as: parseHTML { return [ { tag: 'span[data-type="mention"]', }, ] }, To ensure that your HTML is correctly parsed back into a mention node, your serialized HTML must include this attribute [2]. For example: @John Doe Key implementation details regarding this process include: 1. Identification: The parseHTML rule specifically targets span elements with the attribute data-type="mention" [1][2]. If your custom implementation or older versions do not include this attribute in the rendered output, Tiptap will treat the element as plain text rather than a mention node [2][4]. 2. Attribute Parsing: While the core mention node identifies itself via data-type, individual attributes (like id or label) are handled by the addAttributes method [1]. These are mapped to data-attributes (e.g., data-id, data-label) in the HTML [1]. 3. Customization: If you need to support a different HTML structure (e.g., an anchor tag or different data attributes), you can extend the Mention node and override the parseHTML and renderHTML methods to match your specific requirements [5][3][2]. 4. Versioning Note: Issues regarding data attributes not being correctly included or parsed have been identified in specific version releases (e.g., related to regressions in how attributes are passed to renderHTML) [6][4]. Ensure you are using an up-to-date version of the @tiptap/extension-mention package to avoid these known defects [4]. If you are experiencing issues where mentions are pasted or loaded as plain text, verify that the source HTML contains the data-type="mention" attribute exactly as expected by the extension's parseHTML rule [2].
Citations:
- 1: https://github.com/ueberdosis/tiptap/blob/main/packages/extension-mention/src/mention.ts
- 2: [Bug]: setContent breaks mentions when provided as html ueberdosis/tiptap#4893
- 3: https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing
- 4: [Bug]: Mention extension - copy/pasting mention nodes from same input turns into plain text instead of node ueberdosis/tiptap#4845
- 5: https://tiptap.dev/docs/editor/extensions/nodes/mention
- 6: fix for data attributes not rendering after pr #4082 ueberdosis/tiptap#4980
🌐 Web query:
github ueberdosis/tiptap packages extension-mention src mention.ts 2.27.1
💡 Result:
The file mention.ts located at packages/extension-mention/src/mention.ts in the ueberdosis/tiptap repository is a core component of the Tiptap Mention extension [1][2]. This extension enables users to insert mentions (typically triggered by a character like '@') into the editor [1]. In version 2.27.1 of Tiptap, this file underwent minor updates as part of the package's maintenance [3]. The source code defines the Mention node, which manages attributes such as id, label, and mentionSuggestionChar, and provides methods for rendering these mentions in both text and HTML formats [1][4]. You can view the current version of the source code directly on GitHub [1]. Detailed official documentation and usage instructions for the Mention extension are available on the Tiptap website [2].
Citations:
- 1: https://github.com/ueberdosis/tiptap/blob/main/packages/extension-mention/src/mention.ts
- 2: https://tiptap.dev/docs/editor/extensions/nodes/mention
- 3: ueberdosis/tiptap@ueberdosis:247d277...ueberdosis:4678b54
- 4: https://github.com/ueberdosis/tiptap/blob/9d9cc06a/packages/extension-mention/src/mention.ts
🌐 Web query:
github tip tip extension-mention 2.27.1 mention.ts parseHTML renderHTML
💡 Result:
The Tiptap Mention extension (found in mention.ts) manages how mention nodes are parsed from and rendered to HTML [1][2]. ParseHTML The extension uses a parseHTML method to identify mention nodes in the DOM [1][2]. It looks for span elements that have a data-type attribute matching the extension's name (which defaults to "mention"): parseHTML { return [ { tag: 'span[data-type="${this.name}"]', }, ] }, Specific attributes like id, label, and mentionSuggestionChar are parsed via the addAttributes configuration, which extracts values from corresponding data attributes (e.g., data-id, data-label) [1][2]. RenderHTML The renderHTML method controls how the mention node is converted back to HTML [1][2]. It retrieves the suggestion configuration based on the mentionSuggestionChar, then prioritizes a custom renderHTML function provided via the extension's options if defined [1][2][3]. If no custom renderer is provided, it defaults to rendering a span element [1][2]: renderHTML({ node, HTMLAttributes }) { const suggestion = getSuggestionFromChar(this, node.attrs.mentionSuggestionChar) //... (custom rendering logic) const mergedOptions = {...this.options } //... (attributes merging) const html = this.options.renderHTML({ options: mergedOptions, node, suggestion }) if (typeof html === 'string') { return ['span', mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes), html] } return html }, This architecture allows developers to customize the rendered output (e.g., changing the span to an anchor tag or adding specific CSS classes) by configuring the extension [4]. Note that direct usage of renderLabel is deprecated in favor of this integrated renderHTML and renderText approach [1][2][3].
Citations:
- 1: https://github.com/ueberdosis/tiptap/blob/main/packages/extension-mention/src/mention.ts
- 2: https://github.com/ueberdosis/tiptap/blob/9d9cc06a/packages/extension-mention/src/mention.ts
- 3: ueberdosis/tiptap@9a59985
- 4: https://tiptap.dev/docs/editor/extensions/nodes/mention
🌐 Web query:
@tiptap/extension-link 2.11.2 Link parser href CSS class html
💡 Result:
In Tiptap version 2.11.2, the @tiptap/extension-link extension provides built-in mechanisms to handle HTML attributes, including CSS classes, both globally and per-link [1][2][3]. Global Configuration You can define a default CSS class for all links by configuring the extension during editor initialization. This class will be applied to every rendered tag [1][3]. import Link from '@tiptap/extension-link'; Link.configure({ HTMLAttributes: { class: 'my-custom-class', }, }); Dynamic Per-Link Attributes You can also set or change the CSS class for specific links dynamically when using the setLink or toggleLink commands [1][3][4]. // Example of setting a class dynamically on a specific link editor.chain.focus.setLink({ href: 'https://example.com', class: 'my-specific-class' }).run; HTML Parsing The extension handles HTML parsing via the parseHTML method, which identifies tags in your input HTML [2]. It preserves the href attribute by default [2]. Regarding custom classes, because the class attribute is included in the extension's addAttributes definition, Tiptap automatically parses and maintains the class attribute present on existing tags in your source HTML [2]. Note on TypeScript Support: While the class attribute has been functional in the underlying extension logic for some time, if you encounter issues with TypeScript autocompletion or type errors when passing the class attribute to setLink, ensure you are aware that explicit type definitions for class in the Command interface were formalized in later updates [5]. In version 2.11.2, if type-checking is strict, you may need to cast the attributes object or ensure your project dependencies are correctly aligned with the version's capabilities [5].
Citations:
- 1: https://tiptap.dev/docs/editor/extensions/marks/link
- 2: https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/src/link.ts
- 3: https://github.com/ueberdosis/tiptap-docs/blob/main/src/content/editor/extensions/marks/link.mdx
- 4: How to set link attributes ueberdosis/tiptap#72
- 5: fix: add missing attributes in extension-link ueberdosis/tiptap#4429
🌐 Web query:
tiptap 2.11.2 link extension parseHTML data-type href class
💡 Result:
In Tiptap 2.11.2, the Link extension handles href, class, and other attributes through the addAttributes() method [1][2]. By default, the extension defines these attributes to ensure they are correctly parsed from and rendered to HTML [1][2]. To handle custom attributes such as data-type, you must extend the Link extension and add them to the addAttributes() configuration [3]. ### Managing Attributes in Link Extension The Link extension uses addAttributes() to define how specific attributes are stored in the editor's state and synchronized with the DOM [1][2]. 1. Default Attributes: The href, target, rel, class, and title attributes are included by default [1][2]. The parseHTML function for href specifically extracts the value using element.getAttribute('href') [1][2]. 2. Adding Custom Attributes: To support custom attributes like data-type, you should extend the base Link extension. You must define both parseHTML (to read the attribute from existing HTML) and renderHTML (to write the attribute back to the DOM) [3]. Example of extending the Link extension for custom attributes: javascript import Link from '`@tiptap/extension-link`'; const CustomLink = Link.extend({ addAttributes() { return { // Keep default attributes ...this.parent?.(), // Add custom attribute 'data-type': { default: null, parseHTML: (element) => element.getAttribute('data-type'), renderHTML: (attributes) => { if (!attributes['data-type']) { return {}; } return { 'data-type': attributes['data-type'], }; }, }, }; }, }); ### Important Considerations * Parsing Logic: The main parseHTML() method in the Link extension is primarily responsible for identifying which DOM nodes should be treated as links (using the tag: 'a[href]' rule) and performing XSS validation [1][2]. Attribute-level parsing (like href or class) is handled inside addAttributes() [1][2]. * Attribute Persistence: When adding custom attributes, ensure you include them in both parseHTML and renderHTML within addAttributes to prevent them from being lost during editor updates or content synchronization [3]. * HTMLAttributes Option: For global settings (like a default CSS class for all links), you can use the HTMLAttributes option in the extension configuration [4][2][5], which is merged automatically during rendering [1].
Citations:
- 1: https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/src/link.ts
- 2: https://github.com/ueberdosis/tiptap/blob/5dc935c0/packages/extension-link/src/link.ts
- 3: Custom attributes lost on update ueberdosis/tiptap#3218
- 4: https://tiptap.dev/docs/editor/extensions/marks/link
- 5: https://cdn.jsdelivr.net/npm/@tiptap/extension-link@2.11.5/dist/link.d.ts
Preserve ticket-reference nodes across HTML reload.
TicketReference renders as <a class="ld-ticket-reference" href="...">#<label></a>, but the inherited Mention parser only recognizes custom mentions from span[data-type="<extension-name>"] markup. When saved HTML is reloaded, the anchor can be parsed as a regular link and bypass the closest('a.ld-ticket-reference') click handler.
Add a parseHTML rule for ticket-reference anchors, render a marker such as data-type, parse href into node attributes, and add a reload round-trip test.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/apps/main/src/components/editor/TextEditor.vue` around lines 291 -
315, Update TicketReference to parse its rendered a.ld-ticket-reference markup
on HTML reload: add a parseHTML rule matching the ticket-reference anchor, map
its href into the node’s href attribute, and render a data-type marker
consistent with the parser. Add a round-trip test that serializes and reloads a
ticket reference, preserving its node type, label, and href.
| <span v-if="query.length < MIN_REFERENCE_QUERY_LENGTH" class="text-sm text-muted-foreground"> | ||
| Type at least {{ MIN_REFERENCE_QUERY_LENGTH }} digits to search tickets | ||
| </span> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Move the minimum-query guidance into the locale messages.
Line 27 always renders English text. Add a translated message with a count parameter, then render it through $t. This keeps the private-note editor usable in non-English locales.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/apps/main/src/components/editor/TicketReferenceList.vue` around
lines 26 - 28, Move the minimum-query guidance from the hardcoded text in
TicketReferenceList’s query-length hint into the locale messages, defining a
translated message that accepts the minimum length as a count parameter. Update
the span to render that message through $t while passing
MIN_REFERENCE_QUERY_LENGTH, preserving the existing visibility condition.
Summary
#conversation suggestions to the private-note editorTesting
pnpm exec vitest runpnpm exec vite build --mode mainFixes #468
Summary by CodeRabbit
#search.