[Listing] Search-mode extension seam (SearchModeRegistry) for pluggable smart search - #4076
Merged
Conversation
Generic seam for pluggable search modes on the listing search bar, so bundles (first consumer: backend-power-tools semantic search) can register modes that swap what the search input emits: - SearchModeAbstract + SearchModeRegistry (DynamicTypeRegistryAbstract-based), bound as Element/Listing/SearchModeRegistry and SDK-exported - SearchModeSelect dropdown as a left Compact segment of SearchTermFilter, with per-mode availability, disabled entries with hints, warning line, and a purple collapsed label for active smart modes - searchMode filter value (hidden descriptor, Clear-all resets to full text); the searchTerm descriptor emits the active mode's column filter instead of system.fulltext; registered mode filter types are stripped from restored base filters (duplicates would 422) - blocked modes gate Apply (sidebar) and Enter (search-modal top bars) - opt-in per listing via GeneralFiltersDecoratorConfig.elementType (asset and data-object grids + search-modal Assets/Data Objects tabs); listings without it keep the plain search bar, as does any install with no registered modes - new full-text-search / smart-search icons (generate-icons ride-along rewrites reverted) Part of pimcore/product-management#1369. Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Saved-search restoration, filter cleanup, and blocked-search clearing have unresolved moderate issues.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds an SDK-extensible search-mode registry and integrates smart-search modes into asset and data-object listings.
Changes:
- Adds search-mode contracts, registry, selector UI, icons, and translations.
- Routes search terms through selected modes with availability handling.
- Enables modes for asset/object grids and search-modal tabs.
Unresolved comments:
- Moderate (1 vote) —
search-mode-filter.ts:20: Saved smart searches do not restore their mode/query correctly. Add registry-aware restoration and tests. - Moderate (2 votes) —
element-filter-setup.ts:44: Cleanup excludes hidden or permission-gated modes. Derive cleanup types from all registry entries. - Moderate (1 vote) —
search-mode-abstract.ts:67: The public contract lacks generic deserialization for saved searches. Add a restoration hook, integration, and test. - Nit (2 votes) —
search-mode-select.tsx:74: Use the repository’sIconButtonabstraction. - Moderate (1 vote) —
search-term-filter.tsx:58: Blocked modes prevent clearing the applied search. Allow empty searches through the guard.
File summaries
| File | Reviewed change |
|---|---|
translations/studio.sv.yaml |
Adds Swedish labels. |
translations/studio.no.yaml |
Adds Norwegian labels. |
translations/studio.it.yaml |
Adds Italian labels. |
translations/studio.fr.yaml |
Adds French labels. |
translations/studio.es.yaml |
Adds Spanish labels. |
translations/studio.en.yaml |
Adds English labels. |
translations/studio.de.yaml |
Adds German labels. |
assets/js/src/sdk/modules/element/index.ts |
Exports the search-mode API. |
assets/js/src/core/modules/search/modal/tabs/object/listing/object-search-listing.tsx |
Enables object search modes. |
assets/js/src/core/modules/search/modal/tabs/asset/listing/asset-search-listing.tsx |
Enables asset search modes. |
assets/js/src/core/modules/icon-library/index.ts |
Registers new icons. |
assets/js/src/core/modules/element/listing/decorators/general-filters/view-layer/components/sidebar/tabs/filters/filter-container-inner.tsx |
Applies and gates mode state. |
assets/js/src/core/modules/element/listing/decorators/general-filters/view-layer/components/search/search-term-filter.tsx |
Integrates mode selection with search. |
assets/js/src/core/modules/element/listing/decorators/general-filters/view-layer/components/search/search-mode-select.tsx |
Implements the mode selector. |
assets/js/src/core/modules/element/listing/decorators/general-filters/view-layer/components/search/search-mode-select.styles.ts |
Styles the selector. |
assets/js/src/core/modules/element/listing/decorators/general-filters/search-modes/use-search-mode.ts |
Resolves mode state and availability. |
assets/js/src/core/modules/element/listing/decorators/general-filters/search-modes/search-mode-registry.ts |
Adds the registry. |
assets/js/src/core/modules/element/listing/decorators/general-filters/search-modes/search-mode-abstract.ts |
Defines the extension contract. |
assets/js/src/core/modules/element/listing/decorators/general-filters/search-modes/constants.ts |
Defines the built-in mode ID. |
assets/js/src/core/modules/element/listing/decorators/general-filters/general-filters-decorator.ts |
Adds listing opt-in configuration. |
assets/js/src/core/modules/element/listing/decorators/general-filters/element-filters/use-element-filter-values.ts |
Adds mode filter state. |
assets/js/src/core/modules/element/listing/decorators/general-filters/element-filters/element-filter-types.ts |
Extends filter context types. |
assets/js/src/core/modules/element/listing/decorators/general-filters/element-filters/element-filter-setup.ts |
Builds mode-aware query context. |
assets/js/src/core/modules/element/listing/decorators/general-filters/element-filters/definitions/search-term-filter.tsx |
Emits mode-specific filters. |
assets/js/src/core/modules/element/listing/decorators/general-filters/element-filters/definitions/search-mode-filter.ts |
Defines hidden mode state. |
assets/js/src/core/modules/element/listing/decorators/general-filters/element-filters/definitions/index.ts |
Registers the mode descriptor. |
assets/js/src/core/modules/element/listing/decorators/general-filters/element-filters/build-element-filter-query.ts |
Removes conflicting base filters. |
assets/js/src/core/modules/data-object/listing/index.ts |
Enables object listing modes. |
assets/js/src/core/modules/asset/listing/index.ts |
Enables asset listing modes. |
assets/js/src/core/assets/icons/smart-search.inline.svg |
Adds the smart-search icon. |
assets/js/src/core/assets/icons/full-text-search.inline.svg |
Adds the full-text icon. |
assets/js/src/core/app/config/services/service-ids.ts |
Adds the registry service ID. |
assets/js/src/core/app/config/services/index.ts |
Binds the registry service. |
Review details
- Files reviewed: 31/34 changed files
- Comments generated: 5
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…rch degradation - Derive registeredFilterTypes from every registry entry, not only visible modes, so a hidden mode's filter is still cleaned from restored base filters - Let an empty term through the blocked-mode guard so clearing recovers from a blocked smart search instead of keeping the stale applied query - Saved-search restore: exclude registered mode filter types from the field filters and reset the search mode to full text; a saved smart search degrades cleanly until a mode-level restore contract lands with the first concrete mode Co-Authored-By: Claude <noreply@anthropic.com>
Replace the hardwired SearchModeSelect mount with the component-registry slot 'element.listing.search.slots.prefix' (SlotRenderer, same pattern as asset.tree.node.meta). SearchModeSelect becomes a self-contained default entry registered by the element module: it reads the listing context via hooks, owns the mode-change side effects, and renders nothing when no modes are registered. Any bundle can now put other controls beside the listing search input without touching the search-mode contract. Co-Authored-By: Claude <noreply@anthropic.com>
…mode warning Verified via Playwright in the demo: the blocked-mode warning rendered into the DOM but was invisible in both modal tabs until size='auto'. Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e-seam Conflict resolution: 2026.x moved the service bindings from services/index.ts into the side-effect-free install-core-services.ts — took the refactored index.ts and re-added the SearchModeRegistry binding in the installer. build-dist: kept the branch build; the workflow regenerates it on push. Co-Authored-By: Claude <noreply@anthropic.com>
… Array#push) Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
The search icon/Enter in the filter sidebar applied only the term: a drafted smart mode never reached the applied store, so the search silently ran as full text and the draft re-seeded from applied, visibly reverting the mode selection. The shortcut now behaves like Apply for the control values (searchMode, fieldFilters, directChildren, unreferenced) and respects the blocked gate on every surface; pql still applies only through the Apply button. Found and verified via Playwright gestures in the demo. Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…it fail state The warning Text rendered inline after the Compact row and drifted into the top bar's horizontal-scroll overflow — present in the DOM, invisible on screen (measured at x=1304 in a 1336px-wide modal). It now renders as a block line under the input. A blocked mode additionally shows a warning status on the input, and an attempted search escalates to an error status with the warning in danger color until the block is resolved. Verified via Playwright geometry and state assertions in the demo. Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- warning renders in the darker warn shade (colorWarningTextActive) with the warning-circle icon, matching the design frame - the search-modal top bars pass their selects into SearchTermFilter as prefixControls, so the warning line spans the row and left-aligns with the type select instead of the input column - the input carries no warning border at rest; an attempted search while blocked shows the error border and turns the warning red until resolved Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- trigger uses the shared IconTextButton with the resting border pinned to colorBorder, matching the search button on the other end of the input - the chevron toggles down/up with the dropdown open state and renders at 12px (sizing goes through icon.options — IconTextButton's separate iconOptions prop is dead and worth its own cleanup), vertically centered Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- smart-search-text / smart-search-visual replace the shared smart-search icon (generate-icons ride-alongs reverted) - a mode reporting available: false can never work on the surface (per the backend-computed targets, e.g. image search on data objects) and is now hidden instead of rendered disabled; blocked-but-fixable modes keep their entry, hint and warning Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Feature-specific icons ship with the bundle that registers the modes, via the runtime IconLibrary registration. Core keeps full-text-search for its built-in menu entry. Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the hand-drawn placeholders with the official SVG exports (full-text-search, smart-search-text, smart-search-visual; fills converted to currentColor), owned by the core library like every other bundle-consumed icon — the earlier bundle-side IconLibrary registration is reverted in backend-power-tools. Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ack) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- SearchModeAbstract gains the optional getGlobalSearch() adapter; the All tab renders a mode dropdown and routes smart modes to the adapter's hook via a per-mode keyed GlobalModeSearchResult (SearchResultList extracted). - SearchProvider carries searchMode alongside the term; SearchTermTakeover applies it on tab activation, falling back to full text where unavailable. - SearchModeContext: elementType 'all', explicitTypeSelection and explicitClassSelection so modes gate on explicit choices only. - Availability no longer has a blocked state: a contradicting selection just searches and returns nothing under the informational warning. Removes the red input/text state and the disabled Apply button. - SearchModeWarning and SearchModeDropdown extracted; warning always carries the question-mark-outline icon in the row color. - Compact search bar keeps rounded start corners when the prefix slot renders nothing (antd still counts the empty slot as an item). Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- SearchTermFilter renders SearchModeDropdown directly; the component slot, SearchModeSelect, SearchModeWarning, the corner-radius CSS hack and SearchResultContainer are gone. Shared SearchBar for listing and All tab. - SearchModeContext is elementType, className, selectedTypes, hasExplicitSorting. - ElementFilterContext.searchMode carries the hook result; fallback to full text lives in the searchTerm descriptor. - Sidebar Enter commits searchTerm and searchMode only. - SearchResultList renders plainly (no useMemo). - Comments trimmed; docs page 01_SDK_Overview/10_Search_Modes.md. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…tion SearchModeContext is elementType and hasExplicitSorting; the type-select and class-selection hooks leave useSearchMode. Co-Authored-By: Claude <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Part of pimcore/product-management#1369 (Studio semantic-search UI). PR 1 of 3 — backend-power-tools-bundle registers its Smart search: Text / Image modes against this seam (pimcore/backend-power-tools-bundle#754); pimcore/backend-power-tools-bundle#753 adds the user permission.
What
SearchModeAbstract+SearchModeRegistry(based onDynamicTypeRegistryAbstract), bound asElement/Listing/SearchModeRegistry, SDK-exported fromsdk/modules/element— bundles register modes from their plugin modules, same attach pattern as the existing component/dynamic-type registries.element.listing.search.slots.prefix:SearchModeSelectis the self-contained default entry (dropdown as a leftCompactsegment ofSearchTermFilter), rendered throughSlotRendererso any bundle can occupy the same slot. Mode menu with icons and per-mode hints, purple collapsed label when a smart mode is active ("Full text"/"Default" for the built-in mode). The input keeps rounded start corners when the slot renders nothing.SearchModeWarning,question-mark-outlineicon in the row color) under the input naming the mode's restriction. No blocking: a selection that contradicts the mode simply searches and returns nothing; Apply/Enter are never disabled. Only a restored mode that cannot work on the surface degrades to full text at query build.SearchProvidercarries the chosen mode across tabs (SearchTermTakeoverapplies it, falling back to full text where unavailable, e.g. Documents). The All tab gets the dropdown too — modes may implement the optionalgetGlobalSearch(): GlobalSearchAdapterand the tab routes to that hook via a per-mode keyedGlobalModeSearchResult(SearchResultListextracted fromSearchResult).SearchModeContextcarrieselementType: ElementType | 'all',explicitTypeSelection,explicitClassSelectionandhasExplicitSortingso modes reason about explicit user choices only.searchModevalue (Clear-all resets to full text); thesearchTermdescriptor emits the active mode's column filter instead ofsystem.fulltext; registered mode filter types are stripped from restored base filters (a duplicate simple column filter of the same type is a BE 422).GeneralFiltersDecoratorConfig.elementType— asset grid, data-object grid, search modal Assets/Data Objects tabs. Listings without it, and installs with no registered modes, render today's plain search bar.full-text-search,smart-search-text,smart-search-visualfrom the Figma exports, added to the core icon library (thegenerate-iconsride-along rewrites of unrelated icons were reverted).Verified
tsc --noEmitandeslintclean. Scripted Playwright journeys against the demo with the BPT consumer (Add quantity value data object data types #754): grid sidebar, Quick Search All/Assets/Data Objects/Documents tabs, mode carry-over, network payload assertions (bpt.semanticSearchcolumn filter, global endpoint call), saved-search restore, sort override, clear-all.🤖 Generated with Claude Code