Skip to content

[Listing] Search-mode extension seam (SearchModeRegistry) for pluggable smart search - #4076

Merged
xIrusux merged 35 commits into
2026.xfrom
semantic-search-mode-seam
Sep 9, 2026
Merged

[Listing] Search-mode extension seam (SearchModeRegistry) for pluggable smart search#4076
xIrusux merged 35 commits into
2026.xfrom
semantic-search-mode-seam

Conversation

@xIrusux

@xIrusux xIrusux commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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 on DynamicTypeRegistryAbstract), bound as Element/Listing/SearchModeRegistry, SDK-exported from sdk/modules/element — bundles register modes from their plugin modules, same attach pattern as the existing component/dynamic-type registries.
  • Component slot element.listing.search.slots.prefix: SearchModeSelect is the self-contained default entry (dropdown as a left Compact segment of SearchTermFilter), rendered through SlotRenderer so 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.
  • Informational warning line (SearchModeWarning, question-mark-outline icon 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.
  • Quick Search: SearchProvider carries the chosen mode across tabs (SearchTermTakeover applies it, falling back to full text where unavailable, e.g. Documents). The All tab gets the dropdown too — modes may implement the optional getGlobalSearch(): GlobalSearchAdapter and the tab routes to that hook via a per-mode keyed GlobalModeSearchResult (SearchResultList extracted from SearchResult).
  • SearchModeContext carries elementType: ElementType | 'all', explicitTypeSelection, explicitClassSelection and hasExplicitSorting so modes reason about explicit user choices only.
  • Filter pipeline: hidden searchMode value (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 (a duplicate simple column filter of the same type is a BE 422).
  • Opt-in per listing via 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.
  • Icons full-text-search, smart-search-text, smart-search-visual from the Figma exports, added to the core icon library (the generate-icons ride-along rewrites of unrelated icons were reverted).
  • i18n keys synced to de/fr/it/es/sv/no.

Verified

  • tsc --noEmit and eslint clean. 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.semanticSearch column filter, global endpoint call), saved-search restore, sort override, clear-all.

🤖 Generated with Claude Code

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>
Copilot AI balanced review requested due to automatic review settings September 3, 2026 07:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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’s IconButton abstraction.
  • 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.

@xIrusux xIrusux added this to the 2026.3.0 milestone Sep 3, 2026
@xIrusux xIrusux self-assigned this Sep 3, 2026
xIrusux and others added 23 commits September 3, 2026 10:11
…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>
xIrusux and others added 10 commits September 3, 2026 20:02
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>
…tion

SearchModeContext is elementType and hasExplicitSorting; the type-select and
class-selection hooks leave useSearchMode.

Co-Authored-By: Claude <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Sep 9, 2026

Copy link
Copy Markdown

@xIrusux
xIrusux merged commit 7aed350 into 2026.x Sep 9, 2026
1 check passed
@xIrusux
xIrusux deleted the semantic-search-mode-seam branch September 9, 2026 13:07
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants