-
Notifications
You must be signed in to change notification settings - Fork 30
View Annotations and Datasets from Command Palette #9087
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughExtracts dataset view URL generation into a public accessor and replaces inline URL construction across several components; adds dynamic dataset/annotation commands and navigation, view-mode entries, keyboard shortcuts, HTML sanitization, and scrolling to the command palette. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related issues
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 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 |
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
This reverts commit 4fd2ecb.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
frontend/javascripts/viewer/view/components/command_palette.tsx (3)
73-75: Remove leftover debugconsole.logstatementsThe
console.log("Rendering Command Palette")andconsole.log("h")logs look like temporary debugging output and were already flagged to be removed in a previous review. They should be dropped before merging to avoid noisy consoles in production.- console.log("Rendering Command Palette"); ... - console.log("h");Also applies to: 117-119
269-285: View mode entries and tool shortcuts integration are well structured
getViewModeEntries(including “Reset layout”) andshortCutDictForTools/getToolEntriesare wired cleanly:
- View mode commands correctly dispatch
setViewModeActionand emitLayoutEvents.resetLayout.- Tool commands include readable shortcut labels while still dispatching
setToolAction.Once the typing for
shortcutis adjusted as suggested above, this section is good to go.Also applies to: 286-297, 299-315
31-38: Fix command typing forshortcutandhighlightto unblock TypeScript/CIThe new
shortcutfield on commands (line 310 ingetToolEntries) and use ofhighlightinrenderCommand(line 366) conflict with theCommand/CommandWithoutIdtypings with strict TypeScript enabled:
- Object literal with
shortcutcannot be assigned toCommandWithoutId[]type.- Destructuring
highlightandshortcutinrenderCommandaccesses fields not present onCommandtype.Fix by extending the local
CommandWithoutIdtype to include optional fields and casting inrenderCommand:- type CommandWithoutId = Omit<Command, "id">; + type CommandWithoutId = Omit<Command, "id"> & { + shortcut?: string; + };And in
renderCommand:- renderCommand={(command) => { - const { name, shortcut, highlight: maybeDirtyString } = command; + renderCommand={(command) => { + const { name, shortcut, highlight: maybeDirtyString } = command as Command & { + shortcut?: string; + highlight?: string; + };This allows your local commands to carry shortcuts and access the library's
highlightfield without TS errors.Also remove debug
console.logstatements at lines 74 and 118.
🧹 Nitpick comments (2)
frontend/javascripts/dashboard/advanced_dataset/dataset_action_view.tsx (1)
23-24: UsegetViewDatasetURLconsistently for “View Dataset” navigationThe new use of
getViewDatasetURL(dataset)for the inline “View Dataset” link looks good and keeps URL construction centralized.To avoid future drift, you could also switch the context‑menu “View” action to use the same helper:
- onClick: () => { - window.location.href = `/datasets/${getReadableURLPart(dataset)}/view`; - }, + onClick: () => { + window.location.href = getViewDatasetURL(dataset); + },This keeps all dataset view URLs in sync.
Also applies to: 262-265, 313-320
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
69-72: Reconsider manual HTML sanitization beforedangerouslySetInnerHTML
cleanStringOfMostHTMLplusdangerouslySetInnerHTMLis a reasonable first defense, but it’s still a hand‑rolled sanitizer around user‑influenced strings (e.g. dataset/annotation names viahighlight). This is brittle and easy to get wrong over time.If you keep
dangerouslySetInnerHTML, consider:
- Using a dedicated sanitizer (e.g. DOMPurify) with a strict allowlist (only
<b>).- Or avoiding HTML altogether by rendering plain text and letting React handle highlighting via JSX instead of raw HTML.
That would fully address the XSS warning and make the code easier to reason about.
Also applies to: 365-381
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/javascripts/dashboard/advanced_dataset/dataset_action_view.tsx(2 hunks)frontend/javascripts/viewer/model/accessors/dataset_accessor.ts(1 hunks)frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🧬 Code graph analysis (2)
frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
frontend/javascripts/types/api_types.ts (2)
APIDataset(243-246)APIDatasetCompact(271-276)
frontend/javascripts/dashboard/advanced_dataset/dataset_action_view.tsx (1)
frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-755)
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 374-374: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Actions: CI Pipeline
frontend/javascripts/viewer/view/components/command_palette.tsx
[error] 310-310: TypeScript error TS2353: Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
🪛 GitHub Check: backend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 310-310:
Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
[failure] 366-366:
Property 'highlight' does not exist on type 'Command'.
[failure] 366-366:
Property 'shortcut' does not exist on type 'Command'.
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 310-310:
Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
[failure] 366-366:
Property 'highlight' does not exist on type 'Command'.
[failure] 366-366:
Property 'shortcut' does not exist on type 'Command'.
🔇 Additional comments (2)
frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
743-755: New URL helper is consistent and type‑safe
getViewDatasetURLcorrectly reusesgetReadableURLPartand matches the existing/datasets/{readable}/viewpattern for bothAPIDatasetandAPIDatasetCompact. No issues from a typing or correctness perspective.frontend/javascripts/viewer/view/components/command_palette.tsx (1)
117-145: Dynamic dataset/annotation commands and palette reset logic look soundThe dynamic commands (
viewDataset,viewAnnotation) plushandleSelect/closePaletteand the static command aggregation inallStaticCommandsare coherent:
- Selecting “> View Dataset...” / “> View Annotation...” expands into concrete items via
getDatasetItems/getAnnotationItems.- Empty lists are handled with a Toast message.
- Final selections close the palette via
closePaletteand reset commands ononRequestClose.No functional issues stand out here.
Also applies to: 146-157, 164-175, 177-181, 327-341
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 376-376: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 368-368:
Property 'highlight' does not exist on type 'Command'.
[failure] 368-368:
Property 'shortcut' does not exist on type 'Command'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: backend-tests
- GitHub Check: build-smoketest-push
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
34-36: Fix extended command types forshortcut/highlightto satisfy TypeScript.
frontend-testsis failing becauseCommandWithoutIddoesn’t declareshortcut, andrenderCommanddestructuresshortcut/highlightfromCommand, which doesn’t include those fields in the lib typings. Extending the type locally and casting insiderenderCommandkeeps things type-safe while allowing the extra props.-// duplicate fields because otherwise, optional fields of Command yield errors -type CommandWithoutId = Omit<Command, "id">; +// Extend react-command-palette's Command with our own optional fields. +type ExtendedCommand = Command & { + shortcut?: string; + highlight?: string; +}; + +// duplicate fields because otherwise, optional fields of Command yield errors +type CommandWithoutId = Omit<ExtendedCommand, "id">; @@ - renderCommand={(command) => { - const { name, shortcut, highlight: maybeDirtyString } = command; + renderCommand={(command) => { + const { name, shortcut = "", highlight: maybeDirtyString } = + command as ExtendedCommand;This also resolves the object-literal error in
getToolEntries, sinceshortcutbecomes part ofCommandWithoutId.Also applies to: 285-297, 309-309, 365-367
🧹 Nitpick comments (2)
frontend/javascripts/viewer/view/components/command_palette.tsx (2)
71-74: Custom HTML cleaning +dangerouslySetInnerHTMLis brittle; consider a safer approach.
cleanStringOfMostHTMLplusdangerouslySetInnerHTMLis narrowly scoped to allow only<b>tags, but regex-based HTML sanitizers are easy to get wrong and static analysis flags this as a potential XSS vector. If feasible, consider either using a small sanitizer (e.g., DOMPurify) onhighlight, or parsing the<b>markers and rebuilding JSX instead of injecting raw HTML.Also applies to: 373-377
117-145: TightenhandleSelecttyping and add error handling for async dataset/annotation loads.
handleSelectis typed asRecord<string, unknown>, and failures ingetDatasets/getReadableAnnotationswould currently bubble up and break the palette. You can leverage the lib’sCommandtype and surface backend failures via Toast instead.- const handleSelect = useCallback(async (command: Record<string, unknown>) => { - if (typeof command === "string") { - return; - } - - if (command.name === DynamicCommands.viewDataset) { - const items = await getDatasetItems(); - if (items.length > 0) { - setCommands(items); - } else { - Toast.info("No datasets available."); - } - return; - } - - if (command.name === DynamicCommands.viewAnnotation) { - const items = await getAnnotationItems(); - if (items.length > 0) { - setCommands(items); - } else { - Toast.info("No annotations available."); - } - return; - } - - closePalette(); - }, []); + const handleSelect = useCallback( + async (command: Command | string) => { + if (typeof command === "string") { + return; + } + + if (command.name === DynamicCommands.viewDataset) { + try { + const items = await getDatasetItems(); + if (items.length > 0) { + setCommands(items); + } else { + Toast.info("No datasets available."); + } + } catch { + Toast.info("Failed to load datasets."); + } + return; + } + + if (command.name === DynamicCommands.viewAnnotation) { + try { + const items = await getAnnotationItems(); + if (items.length > 0) { + setCommands(items); + } else { + Toast.info("No annotations available."); + } + } catch { + Toast.info("Failed to load annotations."); + } + return; + } + + closePalette(); + }, + [], + );This makes the callback type-accurate and avoids uncaught errors when the dataset/annotation requests fail.
Also applies to: 145-155, 163-175, 176-180
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🧬 Code graph analysis (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (4)
frontend/javascripts/admin/rest_api.ts (2)
getDatasets(995-1024)getReadableAnnotations(472-479)frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-757)frontend/javascripts/viewer/constants.ts (1)
ViewModeValues(4-4)frontend/javascripts/viewer/model/actions/settings_actions.ts (1)
setViewModeAction(115-119)
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 373-373: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 309-309:
Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
[failure] 365-365:
Property 'highlight' does not exist on type 'Command'.
[failure] 365-365:
Property 'shortcut' does not exist on type 'Command'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (2)
frontend/javascripts/viewer/view/components/command_palette.tsx (2)
268-283: View-mode and tool commands + shortcuts look good; keep them aligned with actual keybindings.
getViewModeEntriesandshortCutDictForToolsmake the palette much more discoverable and give users a clear mapping from tools to"Ctrl + K, …"combos. Just ensure these strings stay in sync with whatever actually binds those shortcuts so the palette doesn’t drift out of date.Also applies to: 285-297, 298-314
326-339: Palette state andpaletteKeyhandling are consistent with the dynamic submenu UX.Using
allStaticCommandsas the baseline pluscommandsstate, apaletteKey-driven remount,closeOnSelect={false}, andonRequestClose={() => setCommands(allStaticCommands)}matches the intended behaviour (expand datasets/annotations in-place, but reset to the static set whenever the palette is closed). Given the palette blocks interaction with the rest of the UI, this should keep the visible commands in sync with the viewer state on each open.Also applies to: 341-349, 353-364
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
frontend/javascripts/viewer/view/components/command_palette.tsx (2)
367-384: Cast command to extended type to fix TypeScript errors.The
renderCommandcallback receives aCommandtype from react-command-palette, which doesn't includeshortcutorhighlightproperties, causing build failures.Apply this diff to cast the command to your extended type:
renderCommand={(command) => { - const { shortcut, highlight: maybeDirtyString, name } = command; + const { shortcut, highlight: maybeDirtyString, name } = + command as Command & { shortcut?: string; highlight?: string }; const cleanString = cleanStringOfMostHTML(maybeDirtyString);As per the static analysis hints. This mirrors the fix needed for the type definition at line 34.
34-34: Fix TypeScript build errors by extending the Command type.The
CommandWithoutIdtype doesn't include theshortcutandhighlightproperties you're using throughout the file, causing build failures at lines 312 and 368.Apply this diff to extend the type:
-// duplicate fields because otherwise, optional fields of Command yield errors -type CommandWithoutId = Omit<Command, "id">; +// Extend Command type to include optional shortcut and highlight fields +type CommandWithoutId = Omit<Command, "id"> & { + shortcut?: string; + highlight?: string; +};As per the static analysis hints.
🧹 Nitpick comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
329-339: Consider memoizing allStaticCommands for performance.The
allStaticCommandsarray is recreated on every render, which calls multiple getter functions. While the performance impact is likely minor, memoizing it could avoid unnecessary work.- const allStaticCommands = [ + const allStaticCommands = useMemo(() => [ viewDatasetsItem, viewAnnotationItems, ...getNavigationEntries(), ...getThemeEntries(), ...getToolEntries(), ...getViewModeEntries(), ...mapMenuActionsToCommands(menuActions), ...getTabsAndSettingsMenuItems(), ...getSuperUserItems(), - ]; + ], [ + activeUser, + isInTracingView, + isViewMode, + userConfig, + restrictions, + menuActions, + ]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)frontend/stylesheets/command_palette.less(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🧬 Code graph analysis (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (4)
frontend/javascripts/admin/rest_api.ts (2)
getDatasets(995-1024)getReadableAnnotations(472-479)frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-757)frontend/javascripts/viewer/constants.ts (1)
ViewModeValues(4-4)frontend/javascripts/viewer/model/actions/settings_actions.ts (1)
setViewModeAction(115-119)
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 376-376: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 312-312:
Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
[failure] 368-368:
Property 'highlight' does not exist on type 'Command'.
[failure] 368-368:
Property 'shortcut' does not exist on type 'Command'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (2)
frontend/stylesheets/command_palette.less (1)
103-110: LGTM! Proper scrolling support for expanded suggestions.The additions of
overflow-y: scroll;andscrollbar-width: thin;correctly enable vertical scrolling in the suggestions container while maintaining the fixed 315px max-height. The CSS cascade is sound—overflow-y: scroll;properly overrides the y-axis behavior from the earlieroverflow: hidden;shorthand, keeping the x-axis hidden to prevent horizontal scroll. This aligns well with the PR's goal of dynamically populating the palette with datasets and annotations, which can exceed the container height.frontend/javascripts/viewer/view/components/command_palette.tsx (1)
288-299: Nice addition!The shortcut dictionary provides helpful keyboard shortcut hints for users, improving discoverability of tool switching commands.
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
34-36: Fix TypeScript typings for extended command fields (shortcut/highlight)Current typing causes the frontend TS build to fail:
CommandWithoutId = Omit<Command, "id">doesn’t declareshortcut, yet several commands specifyshortcut.renderCommanddestructuresshortcutandhighlightfrom the incomingcommand, which is typed asCommandbyreact-command-paletteand doesn’t expose those properties.Define an extended command type and assert before destructuring so both object literals and
renderCommandcompile cleanly.Suggested diff:
@@ -// duplicate fields because otherwise, optional fields of Command yield errors -type CommandWithoutId = Omit<Command, "id">; +type ExtendedCommand = Command & { + // Extra optional fields used in our UI but not declared on `react-command-palette`'s Command. + shortcut?: string; + highlight?: string; +}; + +type CommandWithoutId = Omit<ExtendedCommand, "id">; @@ const shortCutDictForTools: Record<string, string> = { @@ commands.push({ name: `Switch to ${tool.readableName}`, command: () => Store.dispatch(setToolAction(tool)), - shortcut: shortCutDictForTools[tool.id] || "", + shortcut: shortCutDictForTools[tool.id] || "", color: commandEntryColor, }); @@ - renderCommand={(command) => { - const { shortcut, highlight: maybeDirtyString, name } = command; + renderCommand={(command) => { + const { name, shortcut = "", highlight: maybeDirtyString } = + command as ExtendedCommand; const cleanString = cleanStringOfMostHTML(maybeDirtyString); @@ - <span>{shortcut}</span> + <span>{shortcut}</span>After this change, rerun
yarn typecheck/yarn test-frontendto confirm the TS errors aboutshortcut/highlightare gone.#!/bin/bash yarn typecheckAlso applies to: 298-323, 378-392
🧹 Nitpick comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
118-149: Dynamic dataset/annotation commands: behaviour and UX look solid; consider optional limitsThe async handling for
> View Dataset…and> View Annotation…looks good:
- Errors are surfaced via
Toast.error, empty results viaToast.info.- Annotations are sorted by
modifieddescending, matching the dashboard behaviour.- Dataset/annotation items navigate directly via
getViewDatasetURL(dataset)and/annotations/${annotation.id}, which aligns with the rest of the UI.One optional refinement:
getDatasets()without alimitmight become heavy in very large installations. If you start seeing performance issues, consider passing a reasonablelimit(e.g., 100–200) togetDatasetsand/or adding simple client-side filtering, but that’s not required for correctness now.Also applies to: 154-193
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (2)
frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)package.json(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🧬 Code graph analysis (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (4)
frontend/javascripts/admin/rest_api.ts (2)
getDatasets(995-1024)getReadableAnnotations(472-479)frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-757)frontend/javascripts/viewer/constants.ts (1)
ViewModeValues(4-4)frontend/javascripts/viewer/model/actions/settings_actions.ts (1)
setViewModeAction(115-119)
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 386-386: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 322-322:
Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
[failure] 378-378:
Property 'highlight' does not exist on type 'Command'.
[failure] 378-378:
Property 'shortcut' does not exist on type 'Command'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (3)
package.json (1)
124-155: DOMPurify as a runtime dependency looks appropriateUsing DOMPurify from the viewer at runtime justifies adding it under
dependencies(and only there). No issues spotted here.If you want to double‑check compatibility, verify that
dompurify@^3.3.0is supported by your bundler/TS setup (e.g.,yarn add dompurify@^3.3.0in a throwaway branch and runyarn typecheck).frontend/javascripts/viewer/view/components/command_palette.tsx (2)
281-297: View‑mode/layout commands and palette lifecycle wiring look consistentThe added view-mode entries (
ViewModeValues+setViewModeAction) and “Reset layout” (layoutEmitter.emit(LayoutEvents.resetLayout)) integrate cleanly with the existing viewer state.The palette lifecycle wiring (
commands+commandsWithIds,paletteKey-based remount,closeOnSelect={false},onRequestClose={() => setCommands(allStaticCommands)}) matches the dynamic-lists example fromreact-command-paletteand should behave as intended (static commands on open, dynamic lists only after explicit selection).No changes needed here from my side.
Also applies to: 339-357, 358-367, 372-377
71-75: Now let me check the web search results about DOMPurify 3.3.0 support forALLOWED_ATTR:
DOMPurify 3.3.0 documentation ALLOWED_ATTR
And let me also check the official DOMPurify repository or changelog:
DOMPurify version 3.3.0 release notes ALLOWED_ATTR support
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 394-394: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 386-386:
Property 'highlight' does not exist on type 'Command'.
[failure] 386-386:
Property 'shortcut' does not exist on type 'Command'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (3)
frontend/javascripts/viewer/view/components/command_palette.tsx (3)
31-41: Command modeling and tool shortcut wiring look solidDefining a local
Command/CommandWithoutIdshape with optionalshortcut/highlightand usingshortCutDictForToolsto populateshortcutintogetToolEntries, then aggregating everything viaallStaticCommands, is clear and maintainable. The structure should make it easy to extend with more commands or shortcuts later without surprises.Also applies to: 306-317, 330-330, 347-357
79-82: DOMPurify sanitization makesdangerouslySetInnerHTMLacceptable hereUsing
cleanStringOfMostHTMLwithDOMPurify.sanitizeandALLOWED_TAGS: ["b"]before passing content intodangerouslySetInnerHTMLis a reasonable mitigation against XSS in this context. The static warning aboutdangerouslySetInnerHTMLcan be considered addressed, since only sanitized markup with a single safe tag is rendered, and the fallback to plain{name}handles the non-highlighted case cleanly.Also applies to: 393-399
386-401: Verify the proposed TypeScript fix by running the actual TypeScript compiler on the codebase after applying the change.The review comment proposes casting the
commandparameter in therenderCommandcallback to resolve TypeScript errors. However, based on examination of the codebase:
Current code state: The file defines its own local
Commandtype (lines 33–40) that includesshortcut?andhighlight?as optional fields.Objects passed: The
commandsWithIds(lines 366–370) constructs objects with{ id: index, ...command }wherecommandisCommandWithoutId, giving them all required properties includingshortcutandhighlight.Parameter typing issue: The
renderCommandcallback parameter at line 385 is not explicitly typed, so TypeScript infers its type from theReactCommandPaletteprop definition. Ifreact-command-paletteexports aCommandtype that doesn't include these optional fields, then destructuring them would indeed cause type errors.Proposed fix: Casting to
CommandWithoutId & { highlight?: string }as shown in the diff would make the type explicit and safe.However, I could not definitively verify:
- Whether TypeScript errors actually exist (the "frontend-tests" mentioned are not located in the repository)
- The exact exported
Commandtype from@types/react-command-palette(node_modules not available in sandbox)The fix appears reasonable for type safety, but confirmation requires running
tscor the actual test pipeline to verify the errors are resolved.
| onRequestClose={() => setCommands(allStaticCommands)} | ||
| closeOnSelect={false} | ||
| renderCommand={(command) => { | ||
| const { shortcut, highlight: maybeDirtyString, name } = command; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@philippotto do you have any idea why this fails and how to fix it? no matter if use the import form command_palette.d.ts or whether I redefine the type in the module, this error occurs. Furthermore, it only occurs in the pipeline, not locally when running yarn typecheck.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll take over :D
Yes, I see whats going on here: The type definition of the method passed to renderCommand says that the callback has
export interface Command {
id: number;
color: string;
name: string;
command(this: Command): void;
}
as a parameter. So shortcut & highlight does not actually exist.
So the library does not seem to allow adding additional fields to the command type. Maybe enforced type casting is a solution here.
Maybe this stuff from the example only works because of the propType definitions or so 🤔?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe before diving too deep into this making a type case could be helpful? The additional properties are possibly undefined as well. So typescript should still enforce that we should be able to handle the cases were some of them are not defined.
So:
| const { shortcut, highlight: maybeDirtyString, name } = command; | |
| const { shortcut, highlight: maybeDirtyString, name } = command as Command; |
So no perfect solution but regarding working time maybe the wise one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
frontend/javascripts/viewer/view/components/command_palette.tsx (2)
363-365: Optional: Reset commands when closing to prevent stuck dynamic list (repeat suggestion).
closePalette()bumps the key to remountReactCommandPalettebut doesn't resetcommandsback toallStaticCommands. If a future change adds dynamic items that don't navigate (currently all items usewindow.location.hrefwhich reloads the page), users could get stuck viewing only the dynamic list on subsequent opens.For correctness and future-proofing, consider adding
setCommands(allStaticCommands)before bumping the key, as previously suggested.const closePalette = () => { + setCommands(allStaticCommands); setPaletteKey((prevKey) => prevKey + 1); };
387-387: TypeScript error: Cast command to extended type before destructuring.The
frontend-testspipeline fails becauserenderCommandreceives aCommandtype from the library (react-command-palette) which lacksshortcutandhighlightproperties. The code attempts to destructure these properties without casting, causing TypeScript errors.Apply this fix:
- renderCommand={(command) => { - const { shortcut, highlight: maybeDirtyString, name } = command; + renderCommand={(command) => { + const { shortcut, highlight: maybeDirtyString, name } = command as Command;Note: This was flagged in a previous review and marked as addressed, but the fix is not present in the current code.
🧹 Nitpick comments (3)
frontend/stylesheets/command_palette.less (1)
103-110: Scrolling behavior correctly added; consider consolidating overflow properties.The addition of
overflow-y: auto(Line 108) correctly overrides the y-axis behavior ofoverflow: hidden(Line 104), enabling vertical scrolling withscrollbar-width: thin. This change is appropriate for supporting an expanded suggestions list with datasets and annotations.For clarity, consider replacing
overflow: hiddenwithoverflow-x: hiddento make the intent explicit, reducing cognitive load when reading the ruleset..command-palette-suggestionsContainerOpen { - overflow: hidden; + overflow-x: hidden; border: 1px solid var(--color-bg); max-height: 315px; margin-top: 10px; overflow-y: auto; scrollbar-width: thin; }frontend/javascripts/viewer/view/components/command_palette.tsx (2)
33-41: Consider addressing the TODO comment about the Command type definition.The custom
Commandtype redefines properties from the library to addshortcutandhighlight. While this works, the TODO suggests exploring alternatives. However, given that the library doesn't export an extensible type and TypeScript doesn't allow easy augmentation of third-party types without module augmentation, this approach is pragmatic.If you want to keep this pattern, consider removing the TODO and adding a brief comment explaining why the redefinition is necessary (e.g., "Extends react-command-palette's Command type with optional fields").
163-173: Consider using React Router navigation instead of full page reload.The dataset navigation uses
window.location.href = getViewDatasetURL(dataset)which triggers a full page reload. If the app uses React Router, consider usingnavigate()orhistory.push()to maintain SPA benefits (faster navigation, preserved state).Same applies to annotation navigation at line 189.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)frontend/stylesheets/command_palette.less(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 395-395: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 387-387:
Property 'highlight' does not exist on type 'Command'.
[failure] 387-387:
Property 'shortcut' does not exist on type 'Command'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (5)
frontend/javascripts/viewer/view/components/command_palette.tsx (5)
79-82: LGTM! Proper HTML sanitization.The use of DOMPurify with a strict allowlist (
ALLOWED_TAGS: ["b"]) is the correct approach for sanitizing the highlighted text while preserving bold formatting. This effectively mitigates XSS risks.
182-195: LGTM! Annotation sorting and error handling.The annotations are correctly sorted by
modifieddate in descending order (most recent first), and error handling with Toast notifications provides good user feedback.
290-305: LGTM! View mode switching and layout reset.The view mode entries are correctly guarded for tracing view, and the layout reset integration using
layoutEmitteris appropriate.
367-371: LGTM! Proper command transformation with IDs.The
useMemoensures commands are efficiently transformed with IDs only when the commands array changes.
394-396: LGTM! Safe use of dangerouslySetInnerHTML with DOMPurify.The HTML content is sanitized with DOMPurify before rendering, with a strict allowlist of only
<b>tags. This effectively prevents XSS while preserving the highlighted text formatting. The static analysis warning can be safely ignored in this case.
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
MichaelBuessemeyer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making the command palette even more capable 💪
Sorry for the long text 🙈
First part
Sadly, during testing I noticed some bugs which might be unreladed to your changes but maybe should be fixed here:
When I opened an annotation and then opened the command palette enter somethings but then closed it via escape without triggering any command, I got an react error:
installHook.js:1 Warning: Cannot update a component (`Row`) while rendering a different component (`CommandPalette`). To locate the bad setState() call inside `CommandPalette`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render Error Component Stack
at CommandPalette (command_palette.tsx:84:34)
at ShortcutsInfo (statusbar.tsx:181:35)
at span (<anonymous>)
at Statusbar (<anonymous>)
at footer (<anonymous>)
at FlexLayoutWrapper (flex_layout_wrapper.tsx:85:5)
at NmlUploadZoneContainer (nml_upload_zone_container.tsx:76:1)
at TracingLayoutView (tracing_layout_view.tsx:94:5)
at ComponentWithRouterProp (with_router_hoc.tsx:22:33)
at TracingViewRouteWrapper (route_wrappers.tsx:177:33)
at SecuredRoute (secured_route.tsx:23:3)
at ErrorBoundary (error_boundary.tsx:15:5)
at main (<anonymous>)
at RootLayout (router.tsx:85:40)
at GlobalThemeProvider (theme.tsx:122:3)
at ErrorBoundary (error_boundary.tsx:15:5)
But after retrying to produce this error again, I failed. So maybe this was some kind of edge case? Anyway, could you give this a quick look whether there might be a setState in the rendering of the command palette that is not part of an useEffect or a callback happening upon e.g. a user interaction?
Second part
Besides this, I noticed that there is a bug with the tool that are available in the command palette, when opening a shared annotation. Reasoning:
Initially, when opening a shared annotation restrictions.allowUpdate is false until the mutex of the annotation is fetched successfully.
Thus, in the first rendering of the command palette
const getToolEntries = () => {
if (!isInTracingView) return [];
const commands: CommandWithoutId[] = [];
let availableTools = Object.values(AnnotationTool);
if (isViewMode || !restrictions.allowUpdate) {
availableTools = Toolkits.READ_ONLY_TOOLS;
}
availableTools.forEach((tool) => {
commands.push({
name: `Switch to ${tool.readableName}`,
command: () => Store.dispatch(setToolAction(tool)),
shortcut: shortCutDictForTools[tool.id] || "",
color: commandEntryColor,
});
});
return commands;
};
runs with restrictions.allowUpdate = false and therefore the availableTools = Toolkits.READ_ONLY_TOOLS; path will be executed.
And below
const allStaticCommands = [
viewDatasetsItem,
viewAnnotationItems,
...getNavigationEntries(),
...getThemeEntries(),
...getToolEntries(),
...getViewModeEntries(),
...mapMenuActionsToCommands(menuActions),
...getTabsAndSettingsMenuItems(),
...getSuperUserItems(),
];
const [commands, setCommands] = useState<CommandWithoutId[]>(allStaticCommands);
creates the state variable commands which contains all allStaticCommands. !!!But!! only the commands which are present in allStaticCommands during the first rendering. If allStaticCommands changes afterwards, these changes won't affect commands. for this a used effect would be necessary watching for changes of restrictions.allowUpdate.
Thus, some kind of update mechanism to the state variable commands would be needed upon restrictions.allowUpdate changes. Could you please implement this? It should be relatively easy to test whether your solution works:
- create a annotation
- open sharing settings & share it
- reload the annotation
- open the command palette: Skeleton Tool switching should be an option in the command palette. (Currently it is not due to the described bug)
frontend/javascripts/admin/dataset/composition_wizard/04_configure_new_dataset.tsx
Show resolved
Hide resolved
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
| command: () => { | ||
| window.location.href = getViewDatasetURL(dataset); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using useNavigate might be more react-router agnostic 🤔: https://reactrouter.com/api/hooks/useNavigate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to use it, but somehow in the annotation view, opening datasets wont do anything with useNavigate. So I left it the way it is. Same below for annotations. But I did update other paths, somehow the problems wont occur there (neither from admin nor annotation views)
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
| onRequestClose={() => setCommands(allStaticCommands)} | ||
| closeOnSelect={false} | ||
| renderCommand={(command) => { | ||
| const { shortcut, highlight: maybeDirtyString, name } = command; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll take over :D
Yes, I see whats going on here: The type definition of the method passed to renderCommand says that the callback has
export interface Command {
id: number;
color: string;
name: string;
command(this: Command): void;
}
as a parameter. So shortcut & highlight does not actually exist.
So the library does not seem to allow adding additional fields to the command type. Maybe enforced type casting is a solution here.
Maybe this stuff from the example only works because of the propType definitions or so 🤔?
| onRequestClose={() => setCommands(allStaticCommands)} | ||
| closeOnSelect={false} | ||
| renderCommand={(command) => { | ||
| const { shortcut, highlight: maybeDirtyString, name } = command; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe before diving too deep into this making a type case could be helpful? The additional properties are possibly undefined as well. So typescript should still enforce that we should be able to handle the cases were some of them are not defined.
So:
| const { shortcut, highlight: maybeDirtyString, name } = command; | |
| const { shortcut, highlight: maybeDirtyString, name } = command as Command; |
So no perfect solution but regarding working time maybe the wise one
| > | ||
| {cleanString ? ( | ||
| // biome-ignore lint/security/noDangerouslySetInnerHtml: modified from https://github.com/asabaylus/react-command-palette/blob/main/src/default-command.js | ||
| <span dangerouslySetInnerHTML={{ __html: cleanString }} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, but why is this needed? Cant we avoid this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I see. It is for the text highlighting. I do not see an immediate problem with that besides adding a new dependency and using dangerouslySetInnerHTML. But in this context it should kinda be save as long as the get the command palette to contain a certain string when e.g. opening a specifically crafted link. But the command palette does not support such a thing right now. So it should be okisch.
But I honestly do not want to decide this on my own. @philippotto do you have an opinion on this/ can we talk about this? Maybe the fuzzy highlighting isn't that important 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, and the library does this, too, I just modified the code 😅 Of course if we find a solution without it it would be perfect! I didnt manage to find an alternative
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (7)
frontend/javascripts/viewer/view/components/command_palette.tsx (7)
33-33: Address or remove the TODO comment.This TODO has been flagged previously. Please either resolve it or remove the comment if the current implementation is acceptable.
167-167: Consider adding a separator for clarity.As previously suggested, adding a separator like
:or-would improve readability:name: `View Dataset: ${dataset.name} (id ${dataset.id})`,
168-170: Consider using React Router's navigation instead of full page reload.Using
window.location.hrefcauses a full page reload. For a better user experience with client-side navigation, consider using React Router'suseNavigatehook:const navigate = useNavigate(); // ... command: () => { navigate(getViewDatasetURL(dataset)); }This would provide faster navigation and preserve React state where appropriate.
188-188: Consider adding a separator for clarity.As previously suggested, adding a separator would improve readability:
name: `View Annotation: ${annotation.name.length > 0 ? `${annotation.name} (id ${annotation.id})` : annotation.id}`,
189-191: Consider using React Router's navigation instead of full page reload.Using
window.location.hrefcauses a full page reload. Consider using React Router'suseNavigatehook for client-side navigation as suggested for the dataset items.
308-319: Note: Tool shortcuts are display-only.As previously discussed, these shortcuts are shown in the UI but are not currently functional (no keyboard handlers registered). This is acceptable per the time constraints noted in earlier comments.
364-366: Reset commands when closing the palette.As previously noted,
closePaletteshould resetcommandsback toallStaticCommandsto avoid showing only the last dynamic list (datasets or annotations) on the next open:const closePalette = () => { + setCommands(allStaticCommands); setPaletteKey((prevKey) => prevKey + 1); };This ensures every new palette open starts with the full command list.
🧹 Nitpick comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
349-359: Memoize allStaticCommands to avoid unnecessary recalculations.
allStaticCommandsis rebuilt on every render, which is inefficient since it includes multiple function calls and array spreads. Wrap it inuseMemowith appropriate dependencies:- const allStaticCommands = [ - viewDatasetsItem, - viewAnnotationItems, + const allStaticCommands = useMemo(() => [ + viewDatasetsItem, + viewAnnotationItems, ...getNavigationEntries(), ...getThemeEntries(), ...getToolEntries(), ...getViewModeEntries(), ...mapMenuActionsToCommands(menuActions), ...getTabsAndSettingsMenuItems(), ...getSuperUserItems(), - ]; + ], [ + activeUser, + annotationId, + annotationOwner, + annotationType, + isAnnotationLockedByUser, + isInTracingView, + isViewMode, + menuActions, + restrictions, + task, + userConfig, + ]);
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/javascripts/admin/job/job_list_view.tsx(3 hunks)frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx(4 hunks)frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/javascripts/admin/job/job_list_view.tsx
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-06-25T13:16:26.966Z
Learnt from: philippotto
Repo: scalableminds/webknossos PR: 8672
File: frontend/javascripts/admin/account/account_settings_view.tsx:50-50
Timestamp: 2025-06-25T13:16:26.966Z
Learning: When parsing URL pathnames in React Router applications, avoid using `pathname.split("/").pop()` as it fails with trailing slashes. Instead, use `pathname.split("/").filter(Boolean)` to remove empty segments and explicitly find the desired segment by index for more robust URL parsing.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:25:53.526Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/statistic/time_tracking_overview.tsx:261-279
Timestamp: 2025-12-11T15:25:53.526Z
Learning: Ant Design v6 Select: when using the Select component, consider supplying a prefix prop to render an icon or element before the input for better visual context. Apply this guideline to TS and TSX files across the codebase where Ant Design Select is used; ensure prefix usage is accessible (e.g., provide meaningful aria-label if needed) and avoid unnecessary prefixes on simple inputs.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsxfrontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx
📚 Learning: 2025-12-11T15:33:06.880Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/task/task_search_form.tsx:151-151
Timestamp: 2025-12-11T15:33:06.880Z
Learning: In Ant Design v6, do not use optionFilterProp as a standalone prop on Select. Instead, pass it inside showSearch as optionFilterProp, e.g. showSearch={{ optionFilterProp: 'label' }}. showSearch accepts boolean or an object with keys like optionFilterProp, filterOption, autoClearSearchValue. Update all Select components that use optionFilterProp to adopt the new pattern and adjust types accordingly to maintain compatibility and avoid deprecation warnings.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsxfrontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx
📚 Learning: 2025-12-11T15:54:47.778Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:131-134
Timestamp: 2025-12-11T15:54:47.778Z
Learning: In Ant Design v6, Dropdown uses a flat classNames structure: classNames={{ root: '…' }}. Other components (Select, AutoComplete, Cascader, TreeSelect) use a nested structure. The deprecated overlayClassName prop for Dropdown should be replaced with classNames.root. In reviews, flag Dropdown usage that relies on overlayClassName and replace it with classNames={{ root: '…' }}. If you encounter related components, verify the correct classNames shape (flat for Dropdown, nested for others) and update accordingly. This guideline covers TSX files under the frontend codebase where Ant Design components are used.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsxfrontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx
🧬 Code graph analysis (2)
frontend/javascripts/viewer/view/components/command_palette.tsx (4)
frontend/javascripts/admin/rest_api.ts (2)
getDatasets(1001-1030)getReadableAnnotations(472-479)frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-757)frontend/javascripts/viewer/constants.ts (1)
ViewModeValues(5-5)frontend/javascripts/viewer/model/actions/settings_actions.ts (1)
setViewModeAction(115-119)
frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx (1)
frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-757)
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 396-396: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: backend-tests
- GitHub Check: build-smoketest-push
🔇 Additional comments (7)
frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx (1)
43-43: LGTM! Clean refactor to centralize dataset URL construction.The import and usage of
getViewDatasetURLcorrectly replaces manual URL construction across all three call sites (thumbnail link, name link, and double-click navigation). Type compatibility is maintained since all call sites passAPIDatasetCompact, which matches the helper's signature. This refactor improves maintainability by providing a single source of truth for dataset view URLs, which aligns well with the PR's objective of enabling dataset navigation from the command palette.Also applies to: 335-335, 345-345, 810-810
frontend/javascripts/viewer/view/components/command_palette.tsx (6)
79-82: LGTM: HTML sanitization properly implemented.The DOMPurify usage correctly sanitizes HTML with an allowlist of only
<b>tags, effectively preventing XSS attacks. The null check is also appropriate.
127-162: LGTM: Well-structured async command handling.The error handling with try-catch blocks and user feedback via Toast notifications is well implemented. The logic clearly handles both dynamic commands (datasets/annotations) and falls through to close for regular commands.
185-185: LGTM: Sorting annotations by most recently modified.The sorting logic correctly places the most recently modified annotations first, which matches the dashboard behavior as mentioned in the PR objectives.
291-306: LGTM: View mode switching and layout reset.The implementation correctly dispatches view mode actions and uses the layout emitter for reset functionality. The commands array construction is clean and appropriate.
368-372: LGTM: Proper memoization of commands with IDs.The
useMemohook correctly memoizes the transformation of commands to include IDs, with appropriate dependencies.
387-404: LGTM: Safe custom command rendering with proper sanitization.The custom
renderCommandimplementation correctly:
- Uses DOMPurify to sanitize HTML before rendering with
dangerouslySetInnerHTML- Falls back to plain text when no highlight is present
- Displays shortcuts in a clean layout
The static analysis warning about
dangerouslySetInnerHTMLis a false positive here because the content is properly sanitized with DOMPurify's allowlist.
|
Thank you so much for taking the time to have a look at my PR! To your point
I couldnt find anything in the code and wasnt able to reproduce it either 😅 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
364-366: Reset commands in closePalette to avoid stale dynamic lists.After selecting a dataset or annotation command,
closePalette()is called (line 161) but only incrementspaletteKeywithout resettingcommands. This means the next time the palette opens, it may still show the last dynamic list (datasets or annotations) instead of the full static command list. This creates a confusing user experience.The
onRequestClosecallback (line 385) does reset commands, but it's only triggered when closing via ESC or overlay click, not when closing after a selection.Apply this fix:
const closePalette = () => { + setCommands(allStaticCommands); setPaletteKey((prevKey) => prevKey + 1); };
🧹 Nitpick comments (3)
frontend/javascripts/viewer/view/components/command_palette.tsx (3)
164-174: Prefer client-side navigation over full page reload.Using
window.location.hreftriggers a full page reload, which is slower and loses React state. Consider using React Router'suseNavigatehook for client-side navigation instead.Based on learnings from past reviews, using
useNavigatealigns better with React Router patterns.Apply this pattern:
+import { useNavigate } from "react-router-dom"; + export const CommandPalette = ({ label }: { label: string | JSX.Element | null }) => { + const navigate = useNavigate(); // ... other hooks ... const getDatasetItems = useCallback(async () => { const datasets = await getDatasets(); return datasets.map((dataset) => ({ name: `View Dataset ${dataset.name} (id ${dataset.id})`, command: () => { - window.location.href = getViewDatasetURL(dataset); + navigate(getViewDatasetURL(dataset)); }, color: commandEntryColor, id: dataset.id, })); - }, []); + }, [navigate]);
183-196: Prefer client-side navigation over full page reload.Similar to the dataset navigation, using
window.location.hreftriggers a full page reload. Use React Router'suseNavigatehook instead.Apply this pattern:
const getAnnotationItems = useCallback(async () => { const annotations = await getReadableAnnotations(false); const sortedAnnotations = _.sortBy(annotations, (a) => a.modified).reverse(); return sortedAnnotations.map((annotation) => { return { name: `View Annotation ${annotation.name.length > 0 ? `${annotation.name} (id ${annotation.id})` : annotation.id}`, command: () => { - window.location.href = `/annotations/${annotation.id}`; + navigate(`/annotations/${annotation.id}`); }, color: commandEntryColor, id: annotation.id, }; }); - }, []); + }, [navigate]);
291-292: Consider renaming for consistency with store naming.The store property is named
isInAnnotationView(line 87), but the local variable is namedisInTracingView. This naming inconsistency could cause confusion. Consider renaming the local variable to match the store property.- const isInTracingView = useWkSelector((state) => state.uiInformation.isInAnnotationView); + const isInAnnotationView = useWkSelector((state) => state.uiInformation.isInAnnotationView);Then update all usages throughout the file accordingly.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/javascripts/admin/job/job_list_view.tsx(2 hunks)frontend/javascripts/viewer/view/components/command_palette.tsx(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/javascripts/admin/job/job_list_view.tsx
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-06-25T13:16:26.966Z
Learnt from: philippotto
Repo: scalableminds/webknossos PR: 8672
File: frontend/javascripts/admin/account/account_settings_view.tsx:50-50
Timestamp: 2025-06-25T13:16:26.966Z
Learning: When parsing URL pathnames in React Router applications, avoid using `pathname.split("/").pop()` as it fails with trailing slashes. Instead, use `pathname.split("/").filter(Boolean)` to remove empty segments and explicitly find the desired segment by index for more robust URL parsing.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:25:53.526Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/statistic/time_tracking_overview.tsx:261-279
Timestamp: 2025-12-11T15:25:53.526Z
Learning: Ant Design v6 Select: when using the Select component, consider supplying a prefix prop to render an icon or element before the input for better visual context. Apply this guideline to TS and TSX files across the codebase where Ant Design Select is used; ensure prefix usage is accessible (e.g., provide meaningful aria-label if needed) and avoid unnecessary prefixes on simple inputs.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:33:06.880Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/task/task_search_form.tsx:151-151
Timestamp: 2025-12-11T15:33:06.880Z
Learning: In Ant Design v6, do not use optionFilterProp as a standalone prop on Select. Instead, pass it inside showSearch as optionFilterProp, e.g. showSearch={{ optionFilterProp: 'label' }}. showSearch accepts boolean or an object with keys like optionFilterProp, filterOption, autoClearSearchValue. Update all Select components that use optionFilterProp to adopt the new pattern and adjust types accordingly to maintain compatibility and avoid deprecation warnings.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:54:47.778Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:131-134
Timestamp: 2025-12-11T15:54:47.778Z
Learning: In Ant Design v6, Dropdown uses a flat classNames structure: classNames={{ root: '…' }}. Other components (Select, AutoComplete, Cascader, TreeSelect) use a nested structure. The deprecated overlayClassName prop for Dropdown should be replaced with classNames.root. In reviews, flag Dropdown usage that relies on overlayClassName and replace it with classNames={{ root: '…' }}. If you encounter related components, verify the correct classNames shape (flat for Dropdown, nested for others) and update accordingly. This guideline covers TSX files under the frontend codebase where Ant Design components are used.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 396-396: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build-smoketest-push
- GitHub Check: frontend-tests
- GitHub Check: backend-tests
🔇 Additional comments (2)
frontend/javascripts/viewer/view/components/command_palette.tsx (2)
79-82: LGTM: Proper HTML sanitization with DOMPurify.The sanitization correctly uses DOMPurify with a strict allowlist (only
<b>tags), which provides adequate XSS protection for the command palette's fuzzy-match highlighting.
387-404: LGTM: Custom rendering with proper sanitization.The custom
renderCommandimplementation correctly:
- Casts to the extended Command type to access custom properties
- Sanitizes HTML content using DOMPurify before injecting it
- Falls back to plain text rendering when no highlight is present
- Displays shortcuts in a justified layout
The use of
dangerouslySetInnerHTMLis acceptable here because the content is sanitized with DOMPurify's strict allowlist (lines 79-82).
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
frontend/javascripts/viewer/view/components/command_palette.tsx (2)
380-397: Update type cast after fixing CommandWithoutId definition.Once you extend
CommandWithoutIdto includeshortcutandhighlightproperties (as suggested in the earlier comment), update the type cast here to use the extended type for consistency:renderCommand={(command) => { - const { shortcut, highlight: maybeDirtyString, name } = command as Command; + const { shortcut, highlight: maybeDirtyString, name } = command as CommandWithoutId; const cleanString = cleanStringOfMostHTML(maybeDirtyString);Note: The
dangerouslySetInnerHTMLusage is safe here because the content is sanitized throughcleanStringOfMostHTMLusing DOMPurify.
34-34: Extend CommandWithoutId to include custom properties.The base
Commandtype fromreact-command-palettedoesn't includeshortcutorhighlightproperties, causing TypeScript build failures on lines 325 and 381. Extend the type definition to support these custom fields.Apply this diff to fix the type definition:
-type CommandWithoutId = Omit<Command, "id">; +type CommandWithoutId = Omit<Command, "id"> & { + shortcut?: string; + highlight?: string; +};
🧹 Nitpick comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
357-359: Consider resetting commands in closePalette for consistent UX.After selecting a dataset or annotation from a dynamic list,
closePalette()only incrementspaletteKeywithout resettingcommandstoallStaticCommands. This means the next time the palette opens, it might still show the dynamic list instead of the full menu. WhileonRequestClosehandles ESC/overlay closes properly, programmatic closes via selection don't reset.Apply this diff to ensure every palette open starts fresh:
const closePalette = () => { + setCommands(allStaticCommands); setPaletteKey((prevKey) => prevKey + 1); };
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
conf/application.conf(1 hunks)frontend/javascripts/viewer/view/components/command_palette.tsx(10 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-06-25T13:16:26.966Z
Learnt from: philippotto
Repo: scalableminds/webknossos PR: 8672
File: frontend/javascripts/admin/account/account_settings_view.tsx:50-50
Timestamp: 2025-06-25T13:16:26.966Z
Learning: When parsing URL pathnames in React Router applications, avoid using `pathname.split("/").pop()` as it fails with trailing slashes. Instead, use `pathname.split("/").filter(Boolean)` to remove empty segments and explicitly find the desired segment by index for more robust URL parsing.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:25:53.526Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/statistic/time_tracking_overview.tsx:261-279
Timestamp: 2025-12-11T15:25:53.526Z
Learning: Ant Design v6 Select: when using the Select component, consider supplying a prefix prop to render an icon or element before the input for better visual context. Apply this guideline to TS and TSX files across the codebase where Ant Design Select is used; ensure prefix usage is accessible (e.g., provide meaningful aria-label if needed) and avoid unnecessary prefixes on simple inputs.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:33:06.880Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/task/task_search_form.tsx:151-151
Timestamp: 2025-12-11T15:33:06.880Z
Learning: In Ant Design v6, do not use optionFilterProp as a standalone prop on Select. Instead, pass it inside showSearch as optionFilterProp, e.g. showSearch={{ optionFilterProp: 'label' }}. showSearch accepts boolean or an object with keys like optionFilterProp, filterOption, autoClearSearchValue. Update all Select components that use optionFilterProp to adopt the new pattern and adjust types accordingly to maintain compatibility and avoid deprecation warnings.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:54:47.778Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:131-134
Timestamp: 2025-12-11T15:54:47.778Z
Learning: In Ant Design v6, Dropdown uses a flat classNames structure: classNames={{ root: '…' }}. Other components (Select, AutoComplete, Cascader, TreeSelect) use a nested structure. The deprecated overlayClassName prop for Dropdown should be replaced with classNames.root. In reviews, flag Dropdown usage that relies on overlayClassName and replace it with classNames={{ root: '…' }}. If you encounter related components, verify the correct classNames shape (flat for Dropdown, nested for others) and update accordingly. This guideline covers TSX files under the frontend codebase where Ant Design components are used.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2024-11-25T10:11:52.591Z
Learnt from: frcroth
Repo: scalableminds/webknossos PR: 8193
File: conf/application.conf:119-119
Timestamp: 2024-11-25T10:11:52.591Z
Learning: In `conf/application.conf`, the `termsOfService.enabled` setting should remain `false` by default and be enabled (`true`) only in specific deployed instances.
Applied to files:
conf/application.conf
📚 Learning: 2025-01-13T09:06:12.362Z
Learnt from: MichaelBuessemeyer
Repo: scalableminds/webknossos PR: 8221
File: frontend/javascripts/oxalis/view/action-bar/starting_job_modals.tsx:530-535
Timestamp: 2025-01-13T09:06:12.362Z
Learning: In the webknossos codebase, the evaluation settings for split-merger evaluation in neuron inference tasks (`useSparseTracing`, `maxEdgeLength`, `sparseTubeThresholdInNm`, `minimumMergerPathLengthInNm`) are designed to be optional parameters.
Applied to files:
conf/application.conf
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 389-389: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 325-325:
Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
[failure] 381-381:
Property 'highlight' does not exist on type 'Command'.
[failure] 381-381:
Property 'shortcut' does not exist on type 'Command'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: backend-tests
- GitHub Check: build-smoketest-push
🔇 Additional comments (6)
conf/application.conf (1)
161-162: Verify if these feature flag changes are intentional and related to command palette functionality.These feature flag changes enable
jobsEnabledandvoxelyticsEnabledby default, which appear disconnected from the stated PR purpose of adding command palette entries for viewing datasets and annotations. Line 160 indicates that enabling jobs requires database activation usingyarn enable-jobs.Please clarify:
- Are these feature flags related to the new command palette functionality?
- Should these flags be enabled by default for all environments?
- Has the database setup requirement for jobs been considered?
frontend/javascripts/viewer/view/components/command_palette.tsx (5)
70-73: LGTM! Proper sanitization implemented.The function correctly uses DOMPurify to sanitize HTML content, allowing only
<b>tags. This properly mitigates XSS risks when rendering user-generated content withdangerouslySetInnerHTML.
121-155: LGTM! Dynamic command handling is well-structured.The callback properly distinguishes between dynamic entry commands (which load dataset/annotation lists) and concrete commands (which close the palette). Error handling with Toast notifications provides good user feedback.
157-167: LGTM! Dataset command loading is clean and well-structured.The function properly fetches datasets via the API and formats them into navigable commands using the
getViewDatasetURLhelper. The display format includes both dataset name and ID for clarity.
176-189: LGTM! Annotation sorting matches dashboard behavior.The function correctly sorts annotations by modified date (most recent first) and handles the case where annotation names are empty by falling back to the ID. This provides a consistent user experience with the dashboard.
284-299: LGTM! View mode commands are properly implemented.The function correctly generates commands for all view modes and includes layout reset functionality. The guard against non-annotation views prevents showing irrelevant commands.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (1)
343-360: Consider memoizing allStaticCommands for clarity and performance.
allStaticCommandsis recalculated on every render, but theuseEffectonly updatescommandsstate whenallowUpdatechanges. While this works in practice (because the palette blocks interaction and resets on close), memoizingallStaticCommandswould make the data flow more explicit and prevent unnecessary recalculations.- const allStaticCommands = [ + const allStaticCommands = useMemo(() => [ viewDatasetsItem, viewAnnotationItems, ...getNavigationEntries(), ...getThemeEntries(), ...getToolEntries(), ...getViewModeEntries(), ...mapMenuActionsToCommands(menuActions), ...getTabsAndSettingsMenuItems(), ...getSuperUserItems(), - ]; + ], [ + viewDatasetsItem, + viewAnnotationItems, + activeUser, + isInAnnotationView, + isViewMode, + allowUpdate, + menuActions, + userConfig, + ]); const [commands, setCommands] = useState<CommandWithoutId[]>(allStaticCommands); - // biome-ignore lint/correctness/useExhaustiveDependencies: only rerun if allowUpdate changes useEffect(() => { setCommands(allStaticCommands); - }, [allowUpdate]); + }, [allStaticCommands]);This makes the dependencies explicit and ensures commands update whenever any relevant state changes.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/javascripts/viewer/view/components/command_palette.tsx(9 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-06-25T13:16:26.966Z
Learnt from: philippotto
Repo: scalableminds/webknossos PR: 8672
File: frontend/javascripts/admin/account/account_settings_view.tsx:50-50
Timestamp: 2025-06-25T13:16:26.966Z
Learning: When parsing URL pathnames in React Router applications, avoid using `pathname.split("/").pop()` as it fails with trailing slashes. Instead, use `pathname.split("/").filter(Boolean)` to remove empty segments and explicitly find the desired segment by index for more robust URL parsing.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:25:53.526Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/statistic/time_tracking_overview.tsx:261-279
Timestamp: 2025-12-11T15:25:53.526Z
Learning: Ant Design v6 Select: when using the Select component, consider supplying a prefix prop to render an icon or element before the input for better visual context. Apply this guideline to TS and TSX files across the codebase where Ant Design Select is used; ensure prefix usage is accessible (e.g., provide meaningful aria-label if needed) and avoid unnecessary prefixes on simple inputs.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:33:06.880Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/task/task_search_form.tsx:151-151
Timestamp: 2025-12-11T15:33:06.880Z
Learning: In Ant Design v6, do not use optionFilterProp as a standalone prop on Select. Instead, pass it inside showSearch as optionFilterProp, e.g. showSearch={{ optionFilterProp: 'label' }}. showSearch accepts boolean or an object with keys like optionFilterProp, filterOption, autoClearSearchValue. Update all Select components that use optionFilterProp to adopt the new pattern and adjust types accordingly to maintain compatibility and avoid deprecation warnings.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:54:47.778Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:131-134
Timestamp: 2025-12-11T15:54:47.778Z
Learning: In Ant Design v6, Dropdown uses a flat classNames structure: classNames={{ root: '…' }}. Other components (Select, AutoComplete, Cascader, TreeSelect) use a nested structure. The deprecated overlayClassName prop for Dropdown should be replaced with classNames.root. In reviews, flag Dropdown usage that relies on overlayClassName and replace it with classNames={{ root: '…' }}. If you encounter related components, verify the correct classNames shape (flat for Dropdown, nested for others) and update accordingly. This guideline covers TSX files under the frontend codebase where Ant Design components are used.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🧬 Code graph analysis (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (5)
frontend/javascripts/libs/react_hooks.ts (1)
useWkSelector(299-301)frontend/javascripts/admin/rest_api.ts (2)
getDatasets(1001-1030)getReadableAnnotations(472-479)frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-757)frontend/javascripts/viewer/constants.ts (1)
ViewModeValues(5-5)frontend/javascripts/viewer/model/actions/settings_actions.ts (1)
setViewModeAction(115-119)
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 394-394: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/view/components/command_palette.tsx
[failure] 386-386:
Property 'highlight' does not exist on type 'Command'.
[failure] 386-386:
Property 'shortcut' does not exist on type 'Command'.
[failure] 326-326:
Object literal may only specify known properties, and 'shortcut' does not exist in type 'CommandWithoutId'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: backend-tests
- GitHub Check: build-smoketest-push
🔇 Additional comments (10)
frontend/javascripts/viewer/view/components/command_palette.tsx (10)
1-30: LGTM!The new imports are appropriate for the enhanced command palette functionality. The addition of DOMPurify properly addresses the XSS sanitization requirements, and the use of
useNavigatefollows React Router 6 best practices.
70-73: LGTM!The sanitization implementation using DOMPurify is correct and secure. Restricting to only
<b>tags is appropriate for command palette highlighting, and the null handling is proper.
88-114: LGTM!Component setup is clean. The use of
isInAnnotationViewis consistent with the store naming, anduseNavigatefollows React Router 6 patterns correctly.
135-169: LGTM!The
handleSelectcallback correctly implements the dynamic loading pattern with appropriate error handling via Toast messages. The empty dependency array is safe because all referenced functions are stable (setState callbacks and memoized functions).
171-188: LGTM!Dataset loading implementation is clean and follows best practices. The use of
getViewDatasetURLhelper andnavigatefor routing is correct.
190-210: LGTM!Annotation loading correctly implements the sorting by most recently modified first (matching dashboard behavior as intended). Navigation and callback memoization are properly implemented.
225-269: LGTM!Navigation entries are well-structured with proper permission filtering. The use of
navigateinstead ofwindow.location.hrefcorrectly implements React Router 6 navigation patterns.
298-313: LGTM!The view mode switching implementation is clean and properly integrates with the layout reset functionality via
layoutEmitter. The conditional rendering ensures these commands only appear in annotation views.
315-331: LGTM!Tool entries are properly memoized with correct dependencies and implement appropriate filtering based on view mode and edit permissions.
372-384: LGTM!The
ReactCommandPaletteconfiguration is well thought out. The combination ofcloseOnSelect={false}(to keep palette open while browsing dynamic lists),onRequestClose(to reset on ESC), andpaletteKey-based remounting creates a smooth UX for the dynamic dataset/annotation viewing workflow.
frontend/javascripts/viewer/view/components/command_palette.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
frontend/javascripts/viewer/view/components/command_palette.tsx (3)
140-174: Consider logging errors for debugging.The error handling provides good user feedback via Toast messages, but the caught errors are silently discarded. For debugging purposes, consider logging the actual error while still showing the user-friendly message:
🔎 Suggested enhancement
if (command.name === DynamicCommands.viewDataset) { try { const items = await getDatasetItems(); if (items.length > 0) { setCommands(items); } else { Toast.info("No datasets available."); } - } catch (_e) { + } catch (error) { + console.error("Failed to load datasets:", error); Toast.error("Failed to load datasets."); } return; } if (command.name === DynamicCommands.viewAnnotation) { try { const items = await getAnnotationItems(); if (items.length > 0) { setCommands(items); } else { Toast.info("No annotations available."); } - } catch (_e) { + } catch (error) { + console.error("Failed to load annotations:", error); Toast.error("Failed to load annotations."); } return; }
195-208: Optional: Consider a helper function for annotation URLs.The dataset commands use
getViewDatasetURL()for URL generation (line 181), while annotation commands use a hardcoded path template (line 202:`/annotations/${annotation.id}`). For consistency and maintainability, consider creating a similar helper function likegetViewAnnotationURL()if one doesn't already exist.
348-365: Optional: Consider memoizing allStaticCommands for performance.
allStaticCommandsis recreated on every render since it's not wrapped inuseMemo. While this isn't a critical issue for a command palette component, memoizing it could improve performance, especially since it involves multiple function calls and depends on several state slices:🔎 Optional optimization
- const allStaticCommands = [ + const allStaticCommands = useMemo(() => [ viewDatasetsItem, viewAnnotationItems, ...getNavigationEntries(), ...getThemeEntries(), ...getToolEntries(), ...getViewModeEntries(), ...mapMenuActionsToCommands(menuActions), ...getTabsAndSettingsMenuItems(), ...getSuperUserItems(), - ]; + ], [ + viewDatasetsItem, + viewAnnotationItems, + activeUser, + navigate, + isInAnnotationView, + isViewMode, + allowUpdate, + menuActions, + userConfig, + ]); - // biome-ignore lint/correctness/useExhaustiveDependencies: only rerun if allowUpdate changes useEffect(() => { setCommands(allStaticCommands); - }, [allowUpdate]); + }, [allStaticCommands]);Note: The current approach works correctly due to the
paletteKeyreset mechanism, but memoization would reduce unnecessary recreations.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
conf/application.conf(1 hunks)frontend/javascripts/viewer/view/components/command_palette.tsx(9 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2024-11-25T10:11:52.591Z
Learnt from: frcroth
Repo: scalableminds/webknossos PR: 8193
File: conf/application.conf:119-119
Timestamp: 2024-11-25T10:11:52.591Z
Learning: In `conf/application.conf`, the `termsOfService.enabled` setting should remain `false` by default and be enabled (`true`) only in specific deployed instances.
Applied to files:
conf/application.conf
📚 Learning: 2025-10-21T10:54:16.468Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8961
File: frontend/javascripts/viewer/model/actions/annotation_actions.ts:80-82
Timestamp: 2025-10-21T10:54:16.468Z
Learning: In frontend/javascripts/viewer/model/actions/annotation_actions.ts, the ShowManyBucketUpdatesWarningAction is intentionally not included in the AnnotationActionTypes union because it's a UI-only action that doesn't modify the annotation state through reducers.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-09-04T10:01:56.727Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 8895
File: frontend/javascripts/viewer/view/action_bar_view.tsx:0-0
Timestamp: 2025-09-04T10:01:56.727Z
Learning: In frontend/javascripts/viewer/view/action_bar_view.tsx, knollengewaechs prefers not to use onKeyUpCapture for preventing modal close on modifier keys because: 1) holding ctrl while opening modal will still close on keyup, 2) normal case of opening modal then pressing ctrl doesn't cause issues with just onKeyDownCapture.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-06-25T13:16:26.966Z
Learnt from: philippotto
Repo: scalableminds/webknossos PR: 8672
File: frontend/javascripts/admin/account/account_settings_view.tsx:50-50
Timestamp: 2025-06-25T13:16:26.966Z
Learning: When parsing URL pathnames in React Router applications, avoid using `pathname.split("/").pop()` as it fails with trailing slashes. Instead, use `pathname.split("/").filter(Boolean)` to remove empty segments and explicitly find the desired segment by index for more robust URL parsing.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:25:53.526Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/statistic/time_tracking_overview.tsx:261-279
Timestamp: 2025-12-11T15:25:53.526Z
Learning: Ant Design v6 Select: when using the Select component, consider supplying a prefix prop to render an icon or element before the input for better visual context. Apply this guideline to TS and TSX files across the codebase where Ant Design Select is used; ensure prefix usage is accessible (e.g., provide meaningful aria-label if needed) and avoid unnecessary prefixes on simple inputs.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:33:06.880Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/admin/task/task_search_form.tsx:151-151
Timestamp: 2025-12-11T15:33:06.880Z
Learning: In Ant Design v6, do not use optionFilterProp as a standalone prop on Select. Instead, pass it inside showSearch as optionFilterProp, e.g. showSearch={{ optionFilterProp: 'label' }}. showSearch accepts boolean or an object with keys like optionFilterProp, filterOption, autoClearSearchValue. Update all Select components that use optionFilterProp to adopt the new pattern and adjust types accordingly to maintain compatibility and avoid deprecation warnings.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
📚 Learning: 2025-12-11T15:54:47.778Z
Learnt from: knollengewaechs
Repo: scalableminds/webknossos PR: 9117
File: frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx:131-134
Timestamp: 2025-12-11T15:54:47.778Z
Learning: In Ant Design v6, Dropdown uses a flat classNames structure: classNames={{ root: '…' }}. Other components (Select, AutoComplete, Cascader, TreeSelect) use a nested structure. The deprecated overlayClassName prop for Dropdown should be replaced with classNames.root. In reviews, flag Dropdown usage that relies on overlayClassName and replace it with classNames={{ root: '…' }}. If you encounter related components, verify the correct classNames shape (flat for Dropdown, nested for others) and update accordingly. This guideline covers TSX files under the frontend codebase where Ant Design components are used.
Applied to files:
frontend/javascripts/viewer/view/components/command_palette.tsx
🧬 Code graph analysis (1)
frontend/javascripts/viewer/view/components/command_palette.tsx (6)
frontend/javascripts/libs/react_hooks.ts (1)
useWkSelector(299-301)frontend/javascripts/admin/rest_api.ts (2)
getDatasets(1001-1030)getReadableAnnotations(472-479)frontend/javascripts/viewer/model/accessors/dataset_accessor.ts (1)
getViewDatasetURL(753-757)frontend/javascripts/viewer/constants.ts (1)
ViewModeValues(5-5)frontend/javascripts/viewer/model/actions/settings_actions.ts (1)
setViewModeAction(115-119)frontend/javascripts/viewer/view/action-bar/view_dataset_actions_view.tsx (1)
viewDatasetMenu(57-62)
🪛 ast-grep (0.40.0)
frontend/javascripts/viewer/view/components/command_palette.tsx
[warning] 399-399: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: frontend-tests
- GitHub Check: backend-tests
- GitHub Check: build-smoketest-push
🔇 Additional comments (4)
frontend/javascripts/viewer/view/components/command_palette.tsx (4)
34-39: LGTM! Type definitions properly extended.The
ExtendedCommandtype correctly extends the library'sCommandtype with optionalshortcutandhighlightproperties, andCommandWithoutIdproperly derives from it. This addresses the previous TypeScript errors while maintaining type safety.
75-78: Excellent: Secure HTML sanitization implemented.DOMPurify with
ALLOWED_TAGS: ["b"]provides robust XSS protection while supporting the command highlighting feature. This properly addresses the previous security concerns.
303-318: LGTM! Clean view mode switching implementation.The view mode entries are well-structured, properly gated by
isInAnnotationView, and include the useful "Reset layout" action. The integration withlayoutEmittermaintains good separation of concerns.
390-407: LGTM! Safe and well-structured custom rendering.The custom
renderCommandimplementation properly:
- Casts to
ExtendedCommandto access the extended properties- Sanitizes HTML content via DOMPurify before using
dangerouslySetInnerHTML- Falls back to plain text when no highlight is present
- Displays shortcuts in a clean flex layout
The use of
dangerouslySetInnerHTMLis acceptable here since the content is properly sanitized with a restrictive tag whitelist.
|
@MichaelBuessemeyer I had a look at your comments and incorporated your feedback, unless I had some remaining questions (see threads). I also fixed the
-> so the scenario where multiple users open an annotation and allowUpdate changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/common_edge_cases.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
.github/common_edge_cases.md
12-12: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: backend-tests
- GitHub Check: frontend-tests
- GitHub Check: build-smoketest-push
| - The user is logged out and views a public dataset or annotation | ||
| - User uses dark mode / light mode | ||
| - There is no local datastore/tracingstore module (Compare [instructions to test this locally](https://github.com/scalableminds/webknossos/wiki/Set-up-a-standalone-datastore-locally)) | ||
| - An annotation is opened by multiple users at the same time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix markdown indentation to comply with linting rules.
The newly added edge case has incorrect indentation. According to the markdownlint-cli2 rule MD007 (ul-indent), the list item should have 0 leading spaces instead of 1.
🔎 Apply this diff to fix the indentation:
- - An annotation is opened by multiple users at the same time
+- An annotation is opened by multiple users at the same timeNote: If this is intentional formatting to nest the list under "Will this also work if", consider applying the same indentation change to all list items (lines 4–10) for consistency.
📝 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.
| - An annotation is opened by multiple users at the same time | |
| - An annotation is opened by multiple users at the same time |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
12-12: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
🤖 Prompt for AI Agents
.in .github/common_edge_cases.md around line 12: the list item "- An annotation
is opened by multiple users at the same time" has one leading space which
violates MD007 (ul-indent); remove the single leading space so the list item
starts at column 0, and if the intent was to nest the whole block under "Will
this also work if" then instead adjust all sibling list items (lines 4–10) to
the same indentation for consistency.


URL of deployed dev instance (used for testing):
Steps to test:
Notes
TODOs:
omit dangerouslySetInnerHTMLIssues:
(Please delete unneeded items, merge only when none are left open)
$PR_NUMBER.mdfile inunreleased_changesor use./tools/create-changelog-entry.py)