Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion org.knime.ui.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@knime/rich-text-editor": "^1.7.11",
"@knime/styles": "^1.14.3",
"@knime/ui-extension-renderer": "2.3.0",
"@knime/utils": "^1.7.1",
"@knime/utils": "^1.9.1",
"@knime/virtual-tree": "^1.6.11",
"@open-rpc/client-js": "^1.8.1",
"@tiptap/extension-heading": "^2.26.1",
Expand Down
21 changes: 7 additions & 14 deletions org.knime.ui.js/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 0 additions & 18 deletions org.knime.ui.js/src/api/custom-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import type {
EditableMetadata,
MetaNode,
NativeNode,
NodeCategory,
NodeDescription,
NodeTemplate,
PortGroup,
PortType,
ProjectMetadata,
Expand Down Expand Up @@ -152,18 +150,6 @@ export interface Schedule {
user: string;
workflowPath: string;
}

export type ExtendedPortType = PortType & {
typeId: string;
type?: string;
description: string;
};

export type NodeTemplateWithExtendedPorts = NodeTemplate & {
inPorts: ExtendedPortType[];
outPorts: ExtendedPortType[];
};

export type WorkflowObject = XY & {
id: string;
type: "node" | "annotation" | "componentPlaceholder";
Expand All @@ -187,10 +173,6 @@ export type ExampleProject = {

export type NodeRelation = "PREDECESSORS" | "SUCCESSORS";

export type NodeCategoryWithExtendedPorts = NodeCategory & {
nodes?: NodeTemplateWithExtendedPorts[];
};

export type AncestorInfo = {
ancestorItemIds: string[];
itemName: string | null;
Expand Down
134 changes: 134 additions & 0 deletions org.knime.ui.js/src/api/gateway-api/generated-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,118 @@ export interface ComponentPortDescription {
}


/**
* ...
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

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

The JSDoc comment for ComponentSearchItem contains only '...' as a description. Please provide a meaningful description explaining what a ComponentSearchItem represents, such as 'Represents a component in search results with metadata including name, type, ports, and visual information.'

Suggested change
* ...
* Represents a component entry returned in search results, including its name, description, type, ports, and icon information.

Copilot uses AI. Check for mistakes.
* @export
* @interface ComponentSearchItem
*/
export interface ComponentSearchItem {

/**
* Space item ID of this component
* @type {string}
* @memberof ComponentSearchItem
*/
id: string;
/**
* 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<ComponentSearchItemPort>}
* @memberof ComponentSearchItem
*/
inPorts?: Array<ComponentSearchItemPort>;
/**
* The node&#39;s output ports.
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

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

The comment refers to 'node's output ports' but this is in the ComponentSearchItem interface which represents a component, not a node. The comment should say 'The component's output ports.' for consistency with the inPorts comment on line 1247.

Suggested change
* The node&#39;s output ports.
* The component&#39;s output ports.

Copilot uses AI. Check for mistakes.
* @type {Array<ComponentSearchItemPort>}
* @memberof ComponentSearchItem
*/
outPorts?: Array<ComponentSearchItemPort>;

}


/**
* @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.
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

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

The comment says 'The description of the component' but this field is in ComponentSearchItemPort and should refer to the port description, not the component description. It should say 'The description of the port as given by the component creator.'

Suggested change
* The description of the component as given by the component creator.
* The description of the port as given by the component creator.

Copilot uses AI. Check for mistakes.
* @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
Expand Down Expand Up @@ -6923,6 +7035,28 @@ const space = function(rpcClient: RPCClient) {

return rpcClient.call('SpaceService.renameSpace', { ...defaultParams, ...params });
},
/**
* Search among components in this 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&#39;t logged in
* @throws {NetworkException} If a Gateway service call failed due to a network error.
*/
async searchComponents(
params: { query?: string, limit?: number, offset?: number }
): Promise<Array<ComponentSearchItem>> {
const defaultParams = {
query: null,
limit: null,
offset: null,
}

return rpcClient.call('SpaceService.searchComponents', { ...defaultParams, ...params });
},
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<script setup lang="ts">
import { nextTick, ref, toRefs, useTemplateRef, watch } from "vue";

import ScrollViewContainer from "@/components/common/ScrollViewContainer/ScrollViewContainer.vue";
import { createStaggeredLoader } from "@/util/createStaggeredLoader";

type Props = {
isLoading: boolean;
fetchMore: () => Promise<any>;
};
const props = defineProps<Props>();

const searchScrollPosition = defineModel<number>({ default: 0 });

const { isLoading } = toRefs(props);

const isLoadingNextPage = ref(false);

/**
* State used to mirror the loading state but with a small
* time offset, so as not to immediately show the loading state
* if the results arrive quickly
* */
const isLoadingDeferred = ref(false);

const setIsLoadingNextPage = createStaggeredLoader({
firstStageCallback: () => {
isLoadingNextPage.value = true;
},
resetCallback: () => {
isLoadingNextPage.value = false;
},
});

const setIsLoadingDeferred = createStaggeredLoader({
firstStageCallback: () => {
isLoadingDeferred.value = true;
},
resetCallback: () => {
isLoadingDeferred.value = false;
},
});

watch(
isLoading,
(value) => {
setIsLoadingDeferred(value);
},
{ immediate: true },
);

const onSaveScrollPosition = (position: number) => {
searchScrollPosition.value = position;
};

const scroller = useTemplateRef("scroller");
const scrollToTop = async () => {
// wait for new content to be displayed, then scroll to top
await nextTick();
if (scroller.value) {
scroller.value.$el.scrollTop = 0;
}
};

const loadMoreSearchResults = async () => {
setIsLoadingNextPage(true);
await props.fetchMore();
setIsLoadingNextPage(false);
};

defineExpose({ scrollToTop });
</script>

<template>
<ScrollViewContainer
ref="scroller"
class="results"
:initial-position="searchScrollPosition"
@save-position="onSaveScrollPosition"
@scroll-bottom="loadMoreSearchResults"
>
<div class="content">
<slot
:is-loading-next-page="isLoadingNextPage"
:is-loading-deferred="isLoadingDeferred"
/>
</div>
</ScrollViewContainer>
</template>

<style lang="postcss" scoped>
.results {
& .content {
padding: 0 var(--space-16) var(--space-16);

& .node-list {
margin-bottom: -11px;
}

& .node-list-skeleton {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
}
}
}
</style>
Loading
Loading