From 542baae5a44a9345f7e3e640232ea030dc370c50 Mon Sep 17 00:00:00 2001 From: Benjamin Moser Date: Wed, 17 Dec 2025 11:42:53 +0100 Subject: [PATCH 01/11] generated code --- .../src/api/gateway-api/generated-api.ts | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/org.knime.ui.js/src/api/gateway-api/generated-api.ts b/org.knime.ui.js/src/api/gateway-api/generated-api.ts index 5022eb915..d2383494a 100644 --- a/org.knime.ui.js/src/api/gateway-api/generated-api.ts +++ b/org.knime.ui.js/src/api/gateway-api/generated-api.ts @@ -1212,6 +1212,112 @@ export interface ComponentPortDescription { } +/** + * ... + * @export + * @interface ComponentSearchItem + */ +export interface ComponentSearchItem { + + /** + * The name of the component as given by the component creator + * @type {string} + * @memberof ComponentSearchItem + */ + name?: string; + /** + * The description of the component as given by the component creator + * @type {string} + * @memberof ComponentSearchItem + */ + description?: string; + /** + * serialised data of component icon + * @type {string} + * @memberof ComponentSearchItem + */ + icon?: string; + /** + * The type (a.k.a. \"kind\") of the component + * @type {string} + * @memberof ComponentSearchItem + */ + type?: ComponentSearchItem.TypeEnum; + /** + * The components's input ports. + * @type {Array} + * @memberof ComponentSearchItem + */ + inPorts?: Array; + /** + * The node's output ports. + * @type {Array} + * @memberof ComponentSearchItem + */ + outPorts?: Array; + +} + + +/** + * @export + * @namespace ComponentSearchItem + */ +export namespace ComponentSearchItem { + /** + * @export + * @enum {string} + */ + export enum TypeEnum { + Source = 'source', + Manipulator = 'manipulator', + Visualizer = 'visualizer', + Learner = 'learner', + Sink = 'sink', + Other = 'other' + } +} +/** + * + * @export + * @interface ComponentSearchItemPort + */ +export interface ComponentSearchItemPort { + + /** + * The name of the port as given by the component creator. Not to be confused with the human-readable name of the port _type_. + * @type {string} + * @memberof ComponentSearchItemPort + */ + name: string; + /** + * The description of the component as given by the component creator. + * @type {string} + * @memberof ComponentSearchItemPort + */ + description?: string; + /** + * The human-readable name of the port type. This is given by the port type implementation. + * @type {string} + * @memberof ComponentSearchItemPort + */ + portTypeName?: string; + /** + * Hex string of port type color + * @type {string} + * @memberof ComponentSearchItemPort + */ + color?: string; + /** + * Whether the port is optional + * @type {boolean} + * @memberof ComponentSearchItemPort + */ + optional?: boolean; + +} + + /** * Event that can consist of multiple generic events. * @export @@ -6923,6 +7029,29 @@ const space = function(rpcClient: RPCClient) { return rpcClient.call('SpaceService.renameSpace', { ...defaultParams, ...params }); }, + /** + * Search among components in this provider + * @param {string} params.spaceProviderId Identifies a space-provider. + * @param {string} [params.query] + * @param {number} [params.limit] + * @param {number} [params.offset] + * @param {*} [params.options] Override http request option. + * @throws {RequiredError} + * @throws {ServiceCallException} If a Gateway service call failed for some reason. + * @throws {LoggedOutException} If a web request could not be authorized because the space provider isn't logged in + * @throws {NetworkException} If a Gateway service call failed due to a network error. + */ + async searchComponents( + params: { spaceProviderId: string, query?: string, limit?: number, offset?: number } + ): Promise> { + const defaultParams = { + query: null, + limit: null, + offset: null, + } + + return rpcClient.call('SpaceService.searchComponents', { ...defaultParams, ...params }); + }, } }; From 86f8bc2cabb12435ded15235913993bad7cfd62f Mon Sep 17 00:00:00 2001 From: Benjamin Moser Date: Wed, 17 Dec 2025 16:32:40 +0100 Subject: [PATCH 02/11] required props --- org.knime.ui.js/src/api/gateway-api/generated-api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.knime.ui.js/src/api/gateway-api/generated-api.ts b/org.knime.ui.js/src/api/gateway-api/generated-api.ts index d2383494a..5d17283b6 100644 --- a/org.knime.ui.js/src/api/gateway-api/generated-api.ts +++ b/org.knime.ui.js/src/api/gateway-api/generated-api.ts @@ -1224,7 +1224,7 @@ export interface ComponentSearchItem { * @type {string} * @memberof ComponentSearchItem */ - name?: string; + name: string; /** * The description of the component as given by the component creator * @type {string} @@ -1242,7 +1242,7 @@ export interface ComponentSearchItem { * @type {string} * @memberof ComponentSearchItem */ - type?: ComponentSearchItem.TypeEnum; + type: ComponentSearchItem.TypeEnum; /** * The components's input ports. * @type {Array} From 4958bcd56c8ac2dca42cc0d61625bcc278ca1d08 Mon Sep 17 00:00:00 2001 From: Benjamin Moser Date: Wed, 17 Dec 2025 16:34:00 +0100 Subject: [PATCH 03/11] capitalisation of enum values --- org.knime.ui.js/src/api/gateway-api/generated-api.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/org.knime.ui.js/src/api/gateway-api/generated-api.ts b/org.knime.ui.js/src/api/gateway-api/generated-api.ts index 5d17283b6..b7e5c891b 100644 --- a/org.knime.ui.js/src/api/gateway-api/generated-api.ts +++ b/org.knime.ui.js/src/api/gateway-api/generated-api.ts @@ -1269,12 +1269,12 @@ export namespace ComponentSearchItem { * @enum {string} */ export enum TypeEnum { - Source = 'source', - Manipulator = 'manipulator', - Visualizer = 'visualizer', - Learner = 'learner', - Sink = 'sink', - Other = 'other' + Source = 'Source', + Manipulator = 'Manipulator', + Visualizer = 'Visualizer', + Learner = 'Learner', + Sink = 'Sink', + Other = 'Other' } } /** From 5ea9854492edb00f8463743c966ac0f6d7c6dc22 Mon Sep 17 00:00:00 2001 From: Helian Rivera Date: Wed, 17 Dec 2025 18:38:40 +0100 Subject: [PATCH 04/11] NXT-4296: improve component composition and reusability NXT-4296 (Gateway API for component search (use HubClient SDK to call Search Service; aggregate and map entities for FE; error handling)) --- .../InfiniteLoadingList.vue | 102 ++++++++++++ .../common/NodeList/InfiniteNodeList.vue | 122 ++++++++++++++ .../components/common/NodeList/NodeList.vue | 10 +- .../common/NodeTemplate/NodeTemplate.vue | 5 +- .../common/skeleton-loader/SkeletonNodes.vue | 2 +- .../SidebarSearchResults.vue | 4 +- .../NodesGroupedByTag/NodesGroupedByTag.vue | 6 +- .../components/nodeSearch/SearchResults.vue | 151 +++++------------- .../quickAdd/QuickAddNodeSearchResults.vue | 2 +- 9 files changed, 275 insertions(+), 129 deletions(-) create mode 100644 org.knime.ui.js/src/components/common/InfiniteLoadingList/InfiniteLoadingList.vue create mode 100644 org.knime.ui.js/src/components/common/NodeList/InfiniteNodeList.vue diff --git a/org.knime.ui.js/src/components/common/InfiniteLoadingList/InfiniteLoadingList.vue b/org.knime.ui.js/src/components/common/InfiniteLoadingList/InfiniteLoadingList.vue new file mode 100644 index 000000000..ebf2a60ef --- /dev/null +++ b/org.knime.ui.js/src/components/common/InfiniteLoadingList/InfiniteLoadingList.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/org.knime.ui.js/src/components/common/NodeList/InfiniteNodeList.vue b/org.knime.ui.js/src/components/common/NodeList/InfiniteNodeList.vue new file mode 100644 index 000000000..b635499c4 --- /dev/null +++ b/org.knime.ui.js/src/components/common/NodeList/InfiniteNodeList.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/org.knime.ui.js/src/components/common/NodeList/NodeList.vue b/org.knime.ui.js/src/components/common/NodeList/NodeList.vue index dd4136aaf..32f1513e7 100644 --- a/org.knime.ui.js/src/components/common/NodeList/NodeList.vue +++ b/org.knime.ui.js/src/components/common/NodeList/NodeList.vue @@ -17,7 +17,7 @@ type Props = { hasMoreNodes?: boolean; displayMode?: NodeRepositoryDisplayModesType; selectedNode?: NodeTemplateWithExtendedPorts | null; - showDescriptionForNode?: NodeTemplateWithExtendedPorts | null; + showDetailsFor?: NodeTemplateWithExtendedPorts | null; highlightFirst?: boolean; }; @@ -35,13 +35,13 @@ const props = withDefaults(defineProps(), { hasMoreNodes: false, displayMode: "icon", selectedNode: null, - showDescriptionForNode: null, + showDetailsFor: null, highlightFirst: false, }); const emit = defineEmits<{ enterKey: [node: NodeTemplateWithExtendedPorts]; - helpKey: [node: NodeTemplateWithExtendedPorts]; + showNodeDetails: [node: NodeTemplateWithExtendedPorts]; showMore: []; "update:selectedNode": [node: NodeTemplateWithExtendedPorts | null]; navReachedTop: [event: NavReachedEvent]; @@ -73,7 +73,7 @@ const nodeTemplateProps = ( isHighlighted: props.selectedNode === null && index === 0 && props.highlightFirst, isSelected: props.selectedNode?.id === node.id, - isDescriptionActive: props.showDescriptionForNode?.id === node.id, + isDescriptionActive: props.showDetailsFor?.id === node.id, displayMode: props.displayMode, }; }; @@ -261,7 +261,7 @@ defineExpose({ focusFirst, focusLast }); :aria-label="`Select node ${node.name}`" :data-index="index" @keydown.enter.stop.prevent="$emit('enterKey', node)" - @keydown.i.stop.prevent="$emit('helpKey', node)" + @keydown.i.stop.prevent="$emit('showNodeDetails', node)" > diff --git a/org.knime.ui.js/src/components/common/NodeTemplate/NodeTemplate.vue b/org.knime.ui.js/src/components/common/NodeTemplate/NodeTemplate.vue index 402d063b8..ed1b37998 100644 --- a/org.knime.ui.js/src/components/common/NodeTemplate/NodeTemplate.vue +++ b/org.knime.ui.js/src/components/common/NodeTemplate/NodeTemplate.vue @@ -12,7 +12,7 @@ import NodeTemplateListMode from "./NodeTemplateListMode.vue"; /** * Basic NodeTemplate without any drag or insert features. This component should stay reusable. */ -export interface Props { +export type Props = { /** * Additional to the properties of the NodeTemplate from the gateway API, this object * contains the port information (color and kind) which was mapped from the store @@ -23,7 +23,7 @@ export interface Props { isDescriptionActive?: boolean; isHighlighted?: boolean; showFloatingHelpIcon?: boolean; -} +}; const props = withDefaults(defineProps(), { displayMode: "icon", @@ -86,6 +86,7 @@ defineExpose({ getNodePreview }); :in-ports="nodeTemplate.inPorts" :out-ports="nodeTemplate.outPorts" :icon="nodeTemplate.icon" + :is-component="nodeTemplate.component" /> diff --git a/org.knime.ui.js/src/components/common/skeleton-loader/SkeletonNodes.vue b/org.knime.ui.js/src/components/common/skeleton-loader/SkeletonNodes.vue index 825bcdd8d..d7cf353cf 100644 --- a/org.knime.ui.js/src/components/common/skeleton-loader/SkeletonNodes.vue +++ b/org.knime.ui.js/src/components/common/skeleton-loader/SkeletonNodes.vue @@ -3,7 +3,7 @@ import SkeletonItem from "./SkeletonItem.vue"; type Props = { numberOfNodes?: number; - displayMode?: string; + displayMode?: "icon" | "list"; }; withDefaults(defineProps(), { diff --git a/org.knime.ui.js/src/components/nodeRepository/NodeSearchResults/SidebarSearchResults.vue b/org.knime.ui.js/src/components/nodeRepository/NodeSearchResults/SidebarSearchResults.vue index dd56386d1..f4f68a8a7 100644 --- a/org.knime.ui.js/src/components/nodeRepository/NodeSearchResults/SidebarSearchResults.vue +++ b/org.knime.ui.js/src/components/nodeRepository/NodeSearchResults/SidebarSearchResults.vue @@ -56,7 +56,7 @@ const focusFirst = () => { searchResults.value?.focusFirst(); }; -const onHelpKey = (node: NodeTemplateWithExtendedPorts) => { +const onShowNodeDetails = (node: NodeTemplateWithExtendedPorts) => { emit("showNodeDescription", { nodeTemplate: node, isDescriptionActive: showDescriptionForNode.value?.id === node.id, @@ -87,7 +87,7 @@ const displayModeSupported = computed(() => { :num-filtered-out-nodes="totalNumFilteredNodesFound" :is-loading-search-results="isLoadingSearchResults" @item-enter-key="addNodeToWorkflow({ nodeFactory: $event.nodeFactory! })" - @help-key="onHelpKey" + @show-node-details="onShowNodeDetails" @nav-reached-top="$emit('navReachedTop', $event)" >