feat: Tag formatting options#2504
Conversation
…r to tag without ID The current feature allow to add a custom tag "userid-username" upon request to either radarr ou sonarr. With this new feature, when Tag Request is enabled and this option is also enable, the tag will only be "username" instead of "userid-username" seerr-team#727
…sed tag format Created a dropdown to give the user control of what tag style is used. This change means its no longer mandatory to have the internal userID in the tag. The dropdown is also automatically hidden if the tagging is disabled to decrease UI clutter. This commit refactors work from commit bbf0f06. Resolves issue seerr-team#727 Builds on and obsoletes PR seerr-team#2282
…tag formatting options Changed the hard-coded strings to an enum to decrease the possibility of spelling mistakes causing broken code.
1d9e817 to
86e98dc
Compare
|
No actionable comments were generated in the recent review. 🎉 📝 WalkthroughWalkthroughAdds an exported enum Changes
Sequence Diagram(s)sequenceDiagram
actor Admin
participant UI as RadarrModal/SonarrModal
participant API as Settings API
participant Config as DVRSettings
participant Subscriber as MediaRequestSubscriber
participant Arr as Arr API
Admin->>UI: Choose tagRequestsFormat
UI->>API: Save settings (include tagRequestsFormat)
API->>Config: Persist tagRequestsFormat
Arr->>Subscriber: New media request event
Subscriber->>Config: Read tagRequestsFormat
alt userid
Subscriber->>Subscriber: tagLabel = "123"
else userid-username
Subscriber->>Subscriber: tagLabel = "123-tomjones"
else username
Subscriber->>Subscriber: tagLabel = "tomjones"
end
Subscriber->>Arr: create/update tag (`tagLabel`) and attach to request
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/i18n/locale/fr.json (1)
1204-1213:⚠️ Potential issue | 🟡 MinorFrench locale still uses legacy tag keys and misses new tag-format strings.
The UI now references
tagRequestsFormat+tagFormatUserId/UserIdUsername/Username, but fr.json only adds the oldtagRequestsNoIDkeys and keeps the old info text. This will surface missing keys in French and misdescribe the feature.✏️ Suggested i18n updates
- "components.Settings.RadarrModal.tagRequestsNoID": "Pas de ID dans le tag", - "components.Settings.RadarrModal.tagRequestsNoIDInfo": "Ne pas ajouter le préfixe ID utilisateur au tag. Requis que l'option Tager les demandes soit active.", + "components.Settings.RadarrModal.tagRequestsFormat": "Format du tag", + "components.Settings.RadarrModal.tagFormatUserId": "ID utilisateur", + "components.Settings.RadarrModal.tagFormatUserIdUsername": "ID utilisateur - Nom d'utilisateur", + "components.Settings.RadarrModal.tagFormatUsername": "Nom d'utilisateur", - "components.Settings.SonarrModal.tagRequestsNoID": "Pas de ID dans le tag", - "components.Settings.SonarrModal.tagRequestsNoIDInfo": "Ne pas ajouter le préfixe ID utilisateur au tag. Requis que l'option Tager les demandes soit active.", + "components.Settings.SonarrModal.tagRequestsFormat": "Format du tag", + "components.Settings.SonarrModal.tagFormatUserId": "ID utilisateur", + "components.Settings.SonarrModal.tagFormatUserIdUsername": "ID utilisateur - Nom d'utilisateur", + "components.Settings.SonarrModal.tagFormatUsername": "Nom d'utilisateur", - "components.Settings.SonarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'identifiant et le nom du demandeur", + "components.Settings.SonarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'identifiant utilisateur et/ou le nom d'affichage du demandeur", - "components.Settings.RadarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'identifiant et le nom du demandeur", + "components.Settings.RadarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'identifiant utilisateur et/ou le nom d'affichage du demandeur",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/i18n/locale/fr.json` around lines 1204 - 1213, The French locale is missing the new tag-format keys and still uses legacy keys; add translations for the new keys used by the UI: components.Settings.RadarrModal.tagRequestsFormat, components.Settings.RadarrModal.tagFormatUserId, components.Settings.RadarrModal.tagFormatUserIdUsername, components.Settings.RadarrModal.tagFormatUsername (and the corresponding components.Settings.SonarrModal.* keys), and update components.Settings.*.tagRequestsInfo to reflect the new format text (replace the old "tagRequestsNoID"/"tagRequestsNoIDInfo" wording). Ensure each new key has an accurate French string equivalent to the English originals so the UI no longer shows missing keys or incorrect descriptions.
🧹 Nitpick comments (1)
server/subscriber/MediaRequestSubscriber.ts (1)
652-687: Consider extracting tag handling logic to reduce duplication.The tag format handling logic at lines 652-687 is nearly identical to the Radarr logic at lines 306-341. While the current implementation is correct and follows the existing pattern in this file, consider extracting this into a shared helper method to reduce duplication.
♻️ Suggested helper extraction
private async resolveOrCreateUserTag( api: RadarrAPI | SonarrAPI, tagFormat: TagRequestsFormat, requestedBy: { id: number; displayName: string }, entity: MediaRequest ): Promise<number | undefined> { const tags = await api.getTags(); let tagLabel = requestedBy.id + '-' + sanitizeDisplayName(requestedBy.displayName); let userTag = tags.find((v) => v.label.startsWith(requestedBy.id + '-')); if (tagFormat === TagRequestsFormat.USERID) { tagLabel = String(requestedBy.id); userTag = tags.find((v) => v.label === tagLabel); } else if (tagFormat === TagRequestsFormat.USERNAME) { tagLabel = sanitizeDisplayName(requestedBy.displayName.toLowerCase()); userTag = tags.find((v) => v.label === tagLabel); } else if (!userTag) { userTag = tags.find((v) => v.label.startsWith(requestedBy.id + ' - ')); } if (!userTag) { userTag = await api.createTag({ label: tagLabel }); } return userTag?.id; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/subscriber/MediaRequestSubscriber.ts` around lines 652 - 687, Extract the duplicated Sonarr/Radarr tag-resolution and creation logic in MediaRequestSubscriber.ts into a shared helper (e.g., resolveOrCreateUserTag) that accepts the API client (RadarrAPI | SonarrAPI), TagRequestsFormat, requestedBy ({id, displayName}) and the MediaRequest entity, then moves the tag label construction, lookups (startsWith id+'-' and legacy ' - '), format branches (USERID, USERNAME) and createTag call into that helper and return the resolved tag id; replace the in-place logic in the Sonarr block (the code handling tagFormat, tagLabel, userTag, and createTag) with a call to this helper to remove duplication with the Radarr block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/i18n/locale/fr.json`:
- Around line 1204-1213: The French locale is missing the new tag-format keys
and still uses legacy keys; add translations for the new keys used by the UI:
components.Settings.RadarrModal.tagRequestsFormat,
components.Settings.RadarrModal.tagFormatUserId,
components.Settings.RadarrModal.tagFormatUserIdUsername,
components.Settings.RadarrModal.tagFormatUsername (and the corresponding
components.Settings.SonarrModal.* keys), and update
components.Settings.*.tagRequestsInfo to reflect the new format text (replace
the old "tagRequestsNoID"/"tagRequestsNoIDInfo" wording). Ensure each new key
has an accurate French string equivalent to the English originals so the UI no
longer shows missing keys or incorrect descriptions.
---
Nitpick comments:
In `@server/subscriber/MediaRequestSubscriber.ts`:
- Around line 652-687: Extract the duplicated Sonarr/Radarr tag-resolution and
creation logic in MediaRequestSubscriber.ts into a shared helper (e.g.,
resolveOrCreateUserTag) that accepts the API client (RadarrAPI | SonarrAPI),
TagRequestsFormat, requestedBy ({id, displayName}) and the MediaRequest entity,
then moves the tag label construction, lookups (startsWith id+'-' and legacy ' -
'), format branches (USERID, USERNAME) and createTag call into that helper and
return the resolved tag id; replace the in-place logic in the Sonarr block (the
code handling tagFormat, tagLabel, userTag, and createTag) with a call to this
helper to remove duplication with the Radarr block.
|
Ah, Coderabbit is of course rightfully mad about the French translation now. Is there maybe a French speaker who can confirm the AI translations Coderabbit gave? I also agree with the code duplication nitpick, but since this seemed to be the pattern in this file I ended up duplicating the code anyway. |
|
I speak french, what do you need? |
gauthier-th
left a comment
There was a problem hiding this comment.
Also please change the description of User ID - Username to UserID-Username to avoid confusion with spaces in tags.
… handled through Weblate
Translations (other than English) don't belong to the PR. As said above, they are handled by our Weblate. |
3450dcd to
11670fa
Compare
…ect actual generated tag
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
server/subscriber/MediaRequestSubscriber.ts (1)
304-356: Consider extracting shared tagging logic.The Radarr (lines 304-356) and Sonarr (lines 650-702) tagging blocks are nearly identical. While the PR author noted following existing patterns, a helper function could reduce duplication and ensure consistent behavior across both integrations.
This is not blocking since the current implementation works correctly.
Example helper extraction
async function resolveOrCreateUserTag( api: RadarrAPI | SonarrAPI, tagFormat: TagRequestsFormat, userId: number, displayName: string ): Promise<{ id?: number; label: string }> { const tags = await api.getTags(); const sanitized = sanitizeDisplayName(displayName.toLowerCase()); let tagLabel: string; let userTag: typeof tags[number] | undefined; switch (tagFormat) { case TagRequestsFormat.USERID: tagLabel = String(userId); userTag = tags.find((v) => v.label === tagLabel); break; case TagRequestsFormat.USERNAME: tagLabel = sanitized; userTag = tags.find((v) => v.label === tagLabel); break; default: // USERID_USERNAME tagLabel = `${userId}-${sanitized}`; userTag = tags.find((v) => v.label.startsWith(`${userId}-`)) ?? tags.find((v) => v.label.startsWith(`${userId} - `)); break; } return userTag ?? await api.createTag({ label: tagLabel }); }Also applies to: 650-702
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/subscriber/MediaRequestSubscriber.ts` around lines 304 - 356, The repeated tagging logic in MediaRequestSubscriber.ts (the Radarr block around the Radarr tag handling and the Sonarr block at lines ~650-702) should be extracted into a shared helper (e.g., resolveOrCreateUserTag) that accepts the API client (RadarrAPI|SonarrAPI), TagRequestsFormat, userId, displayName and returns the resolved/created tag object/label; replace both inline blocks with calls to that helper and then push the returned tag.id into the existing tags array if present, preserving existing logging and radarr/sonarr.createTag behavior and types used by functions like radarr.getTags()/createTag and sanitizeDisplayName so imports and types align.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/subscriber/MediaRequestSubscriber.ts`:
- Around line 310-326: The tag casing is inconsistent: when tagFormat is
TagRequestsFormat.USERNAME the code lowercases entity.requestedBy.displayName
before calling sanitizeDisplayName but for the USERID_USERNAME branch it does
not, producing mixed-case tags; update the USERID_USERNAME construction (the
tagLabel and the radarrTags lookup for userTag) to lowercase the display name
before sanitization just like the USERNAME branch (apply to variables tagLabel,
userTag, sanitizeDisplayName and the radarrTags.find calls that use
entity.requestedBy.displayName) so both formats produce lowercase sanitized
tags.
---
Nitpick comments:
In `@server/subscriber/MediaRequestSubscriber.ts`:
- Around line 304-356: The repeated tagging logic in MediaRequestSubscriber.ts
(the Radarr block around the Radarr tag handling and the Sonarr block at lines
~650-702) should be extracted into a shared helper (e.g.,
resolveOrCreateUserTag) that accepts the API client (RadarrAPI|SonarrAPI),
TagRequestsFormat, userId, displayName and returns the resolved/created tag
object/label; replace both inline blocks with calls to that helper and then push
the returned tag.id into the existing tags array if present, preserving existing
logging and radarr/sonarr.createTag behavior and types used by functions like
radarr.getTags()/createTag and sanitizeDisplayName so imports and types align.
| let tagLabel = | ||
| entity.requestedBy.id + | ||
| '-' + | ||
| sanitizeDisplayName(entity.requestedBy.displayName); | ||
| // new tags do not have spaces around the hyphen, since spaces are not allowed anymore | ||
| // note: this can theoretically collide if a USERNAME-format tag starts with this user's id | ||
| let userTag = radarrTags.find((v) => | ||
| v.label.startsWith(entity.requestedBy.id + ' - ') | ||
| v.label.startsWith(entity.requestedBy.id + '-') | ||
| ); | ||
| // new tags do not have spaces around the hyphen, since spaces are not allowed anymore | ||
| if (!userTag) { | ||
| if (tagFormat === TagRequestsFormat.USERID) { | ||
| tagLabel = String(entity.requestedBy.id); | ||
| userTag = radarrTags.find((v) => v.label === tagLabel); | ||
| } else if (tagFormat === TagRequestsFormat.USERNAME) { | ||
| tagLabel = sanitizeDisplayName( | ||
| entity.requestedBy.displayName.toLowerCase() | ||
| ); | ||
| userTag = radarrTags.find((v) => v.label === tagLabel); |
There was a problem hiding this comment.
Inconsistent case handling between tag formats.
For USERNAME format (line 323-324), the display name is lowercased before sanitization. For USERID_USERNAME format (line 310-313), it is not. This creates inconsistent tag casing:
USERID_USERNAME→1-MagicLegendUSERNAME→magiclegend
If consistent lowercase tags are desired (as shown in PR screenshots), consider lowercasing the display name for USERID_USERNAME as well:
Proposed fix for consistent lowercase
let tagLabel =
entity.requestedBy.id +
'-' +
- sanitizeDisplayName(entity.requestedBy.displayName);
+ sanitizeDisplayName(entity.requestedBy.displayName.toLowerCase());📝 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.
| let tagLabel = | |
| entity.requestedBy.id + | |
| '-' + | |
| sanitizeDisplayName(entity.requestedBy.displayName); | |
| // new tags do not have spaces around the hyphen, since spaces are not allowed anymore | |
| // note: this can theoretically collide if a USERNAME-format tag starts with this user's id | |
| let userTag = radarrTags.find((v) => | |
| v.label.startsWith(entity.requestedBy.id + ' - ') | |
| v.label.startsWith(entity.requestedBy.id + '-') | |
| ); | |
| // new tags do not have spaces around the hyphen, since spaces are not allowed anymore | |
| if (!userTag) { | |
| if (tagFormat === TagRequestsFormat.USERID) { | |
| tagLabel = String(entity.requestedBy.id); | |
| userTag = radarrTags.find((v) => v.label === tagLabel); | |
| } else if (tagFormat === TagRequestsFormat.USERNAME) { | |
| tagLabel = sanitizeDisplayName( | |
| entity.requestedBy.displayName.toLowerCase() | |
| ); | |
| userTag = radarrTags.find((v) => v.label === tagLabel); | |
| let tagLabel = | |
| entity.requestedBy.id + | |
| '-' + | |
| sanitizeDisplayName(entity.requestedBy.displayName.toLowerCase()); | |
| // new tags do not have spaces around the hyphen, since spaces are not allowed anymore | |
| // note: this can theoretically collide if a USERNAME-format tag starts with this user's id | |
| let userTag = radarrTags.find((v) => | |
| v.label.startsWith(entity.requestedBy.id + '-') | |
| ); | |
| if (tagFormat === TagRequestsFormat.USERID) { | |
| tagLabel = String(entity.requestedBy.id); | |
| userTag = radarrTags.find((v) => v.label === tagLabel); | |
| } else if (tagFormat === TagRequestsFormat.USERNAME) { | |
| tagLabel = sanitizeDisplayName( | |
| entity.requestedBy.displayName.toLowerCase() | |
| ); | |
| userTag = radarrTags.find((v) => v.label === tagLabel); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@server/subscriber/MediaRequestSubscriber.ts` around lines 310 - 326, The tag
casing is inconsistent: when tagFormat is TagRequestsFormat.USERNAME the code
lowercases entity.requestedBy.displayName before calling sanitizeDisplayName but
for the USERID_USERNAME branch it does not, producing mixed-case tags; update
the USERID_USERNAME construction (the tagLabel and the radarrTags lookup for
userTag) to lowercase the display name before sanitization just like the
USERNAME branch (apply to variables tagLabel, userTag, sanitizeDisplayName and
the radarrTags.find calls that use entity.requestedBy.displayName) so both
formats produce lowercase sanitized tags.
There was a problem hiding this comment.
Ah crap, this is correct. I missed this because the 1 - magiclegend label already existed on my *arrs, so it didn't create a new one.
@gauthier-th Would the preference be to keep the user-supplied capitalization, or to standardize it to lowercase? Personally I would do the latter to ensure consistency.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
11670fa to
6385feb
Compare
I considered this as well, but ended up not doing it for readability. I've changed the labels. I've also updated the |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
server/subscriber/MediaRequestSubscriber.ts (1)
304-356: Consider extracting shared tag-handling logic.The tagging logic for Radarr (lines 304-356) and Sonarr (lines 650-702) is nearly identical. A shared helper function could reduce duplication and ensure both flows stay in sync.
That said, I acknowledge your PR comment about matching existing file patterns—this is a reasonable tradeoff and can be addressed in a future cleanup.
Example helper extraction
interface TagHandler { getTags: () => Promise<{ id: number; label: string }[]>; createTag: (options: { label: string }) => Promise<{ id: number; label: string }>; } async function resolveUserTag( handler: TagHandler, tagFormat: TagRequestsFormat, userId: number, displayName: string ): Promise<{ id: number; label: string } | undefined> { const tags = await handler.getTags(); const sanitizedName = sanitizeDisplayName(displayName.toLowerCase()); let tagLabel: string; let userTag: { id: number; label: string } | undefined; switch (tagFormat) { case TagRequestsFormat.USERID: tagLabel = String(userId); userTag = tags.find((v) => v.label === tagLabel); break; case TagRequestsFormat.USERNAME: tagLabel = sanitizedName; userTag = tags.find((v) => v.label === tagLabel); break; default: // USERID_USERNAME tagLabel = `${userId}-${sanitizedName}`; userTag = tags.find((v) => v.label.startsWith(`${userId}-`)) ?? tags.find((v) => v.label.startsWith(`${userId} - `)); break; } if (!userTag) { userTag = await handler.createTag({ label: tagLabel }); } return userTag; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/subscriber/MediaRequestSubscriber.ts` around lines 304 - 356, Extract the duplicated tag resolution/creation logic into a shared helper (e.g., resolveUserTag) that accepts an adapter implementing a small TagHandler interface (methods: getTags and createTag), the TagRequestsFormat, userId and displayName, and returns the resolved tag object or undefined; replace the Radarr block (radarr.getTags, radarr.createTag, tagLabel/userTag logic) and the Sonarr block with calls to this helper, reusing existing utilities TagRequestsFormat and sanitizeDisplayName to build labels and to preserve the old-space fallback (startsWith(userId + ' - ')) for USERID_USERNAME mode. Ensure callers push the returned tag.id into their tags array and keep the same logging paths (logger.info/warn) when createTag fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@server/subscriber/MediaRequestSubscriber.ts`:
- Around line 310-326: The tag generation is inconsistent: in the branch that
builds USERID_USERNAME tags it calls
sanitizeDisplayName(entity.requestedBy.displayName) without lowercasing, while
the USERNAME branch lowercases before sanitizing; update the USERID_USERNAME
branch so tagLabel uses
sanitizeDisplayName(entity.requestedBy.displayName.toLowerCase()) and ensure
userTag lookup uses the same lowercased/sanitized label (references: tagLabel,
userTag, sanitizeDisplayName, and TagRequestsFormat.USERNAME/USERID_USERNAME) so
all display-name-based tags are consistently lowercase.
- Around line 656-672: The Sonarr tag-label logic in MediaRequestSubscriber.ts
is inconsistent: when tagFormat === TagRequestsFormat.USERID_USERNAME you build
tagLabel using sanitizeDisplayName(entity.requestedBy.displayName) (not
lowercased) but when tagFormat === TagRequestsFormat.USERNAME you use
sanitizeDisplayName(entity.requestedBy.displayName.toLowerCase()), causing case
mismatches; update the USERID_USERNAME branch to lowercase the display name
before sanitizing (use the same .toLowerCase() call used in the USERNAME branch)
and ensure the userTag lookups (sonarrTags.find) compare against the
normalized/lowercased sanitized label consistently so sanitizeDisplayName and
TagRequestsFormat branches produce matching case.
---
Nitpick comments:
In `@server/subscriber/MediaRequestSubscriber.ts`:
- Around line 304-356: Extract the duplicated tag resolution/creation logic into
a shared helper (e.g., resolveUserTag) that accepts an adapter implementing a
small TagHandler interface (methods: getTags and createTag), the
TagRequestsFormat, userId and displayName, and returns the resolved tag object
or undefined; replace the Radarr block (radarr.getTags, radarr.createTag,
tagLabel/userTag logic) and the Sonarr block with calls to this helper, reusing
existing utilities TagRequestsFormat and sanitizeDisplayName to build labels and
to preserve the old-space fallback (startsWith(userId + ' - ')) for
USERID_USERNAME mode. Ensure callers push the returned tag.id into their tags
array and keep the same logging paths (logger.info/warn) when createTag fails.
Forgot to run i18n:extract, oops
Description
Took the liberty of implementing the proposed dropdown, indentation and hiding changes proposed by fallenbagel in #2282 (comment).
This creates an option for the user to choose how they want the tags formatted.
Note that in my Radarr instance migration 0007 did not actually update the tags to the new format. Wasn't it supposed to do this automatically? (See screenshot below) I'm aware that the current matching logic could have collisions if the user changes to a different format and has an username starting with a number followed by a dash that also matches an existing label. (e.g.
1-the-man).AI was used for this PR. Please open me for more details.
Model used: Anthropic Claude Sonnet 4.6.
Prompt:
Context files given:
src/components/Settings/RadarrModal/index.tsxsrc/components/PermissionOption/index.tsxserver/subscriber/MediaRequestSubscriber.tsNote that it took the changes to the French i18n file and updated those, and created a migration to accommodate the changed config value. Given that the change this was built upon has not reached GA yet, I removed the migration. I also did not include the changes it made to the French i18n file because I could not validate the translation myself.
Complete reasoning output of the initial changes:
I then got it to change to an enum:
Prompt:
Then I prompted for a refactor to better match the coding style of the rest of the file:
Prompt:
How Has This Been Tested?
I tested this by connecting the development instance running with
pnpmto my 'production' Radarr and Sonarr instances. Then, I tried all three configurations and confirmed that the tag was either created or set correctly. I also validated the logic by attaching a Node debugger toMediaRequestSubscriber.tsto verify the logic.Node Debugger stuff in case somebody is interested
Got Claude Sonnet 4.5 to figure out the configs, so they are indeed garbage, hence I'm not committing any of it. But they do work. Would be great if somebody who is more familiar with these tools could tell me which settings are indeed required for debugging and source maps so this can be documented properly.
package.json:Added a debug command to start in development mode with the debug server:
Then, created a
launch.json:{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "attach", "name": "Attach to Debug Server", "port": 9229, "restart": true, "skipFiles": ["<node_internals>/**", "**/node_modules/**"], "protocol": "inspector", "sourceMaps": true, "localRoot": "${workspaceFolder}", "remoteRoot": "${workspaceFolder}", "resolveSourceMapLocations": [ "${workspaceFolder}/**", "!**/node_modules/**" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } ] }This should be enough to get a debugger working. Try placing a breakpoint in
server/index.tsat line 1 or 2 and it should bind (= become red).Then, to also get React Devtools working I added configs for sourcemapping:
tsconfig.json, add:{ "compilerOptions": { "sourceMap": true } }server/tsconfig.json, replace:{ "extends": "../tsconfig.json", "compilerOptions": { "target": "ES2020", "module": "commonjs", "outDir": "../dist", "noEmit": false, "incremental": true, "baseUrl": ".", "sourceMap": false, "inlineSourceMap": true, "inlineSources": true, "paths": { "@server/*": ["*"] } }, "include": ["**/*.ts"], "ts-node": { "files": true, "transpileOnly": false, "compilerOptions": { "sourceMap": false, "inlineSourceMap": true, "inlineSources": true } } }No, not all settings are probably needed, but I couldn't be arsed with figuring out which ones did the trick, I just wanted it to work.
Screenshots / Logs (if applicable)
No dropdown if the tag option is disabled:

Added dropdown:

Result from tests:
Old tags with spaces:

Just username tag:

Just ID:

Checklist:
pnpm buildpnpm i18n:extractSummary by CodeRabbit
New Features
Localization