Skip to content
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

(WIP) feat: initial ui extension framework #3891

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from
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
6 changes: 6 additions & 0 deletions core/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ export * from './tools'
* @module
*/
export * from './models'

/**
* Export all base views.
* @module
*/
export * from './views'
78 changes: 78 additions & 0 deletions core/src/browser/views/UIManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Define an enum for the component types
export enum UIComponent {
RightPanelTabItem = 'right-panel-tab-item-component',
InputChatBox = 'input-chat-box-component',
}

// Define object structures for each component type
interface TabItem {
name: string
value: string
render?: any
}

interface InputChatBox {
name: string
icon: any
render: any
onClick: any
}

// Map each `UIComponent` to a corresponding object type
type TypeObjectMap = {
[UIComponent.RightPanelTabItem]: TabItem
[UIComponent.InputChatBox]: InputChatBox
}

// Utility type to extract the correct object type based on the `UIComponent`
type TypeObject<T extends UIComponent> = TypeObjectMap[T]

export class UIManager {
// Change to store an array of objects for each component type
public view = new Map<UIComponent, TypeObject<UIComponent>[]>()

Check warning on line 32 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

32 line is not covered with tests

/**
* Registers a UI by its type (defined in `UIComponent` enum).
* @param type - The predefined type for the UI from the `UIComponent` enum.
* @param view - The object to register, which depends on the type.
*/
register<T extends UIComponent>(type: T, view: TypeObject<T>) {
// Check if the type already has registered objects
if (!this.view.has(type)) {
this.view.set(type, []) // Initialize with an empty array if not present

Check warning on line 42 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

41-42 lines are not covered with tests
}
// Push the new view object into the array for the given type
this.view.get(type)?.push(view)

Check warning on line 45 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

45 line is not covered with tests
}

/**
* Retrieves all registered UIs by type.
* @param type - The type of the UI to retrieve.
* @returns An array of UIs associated with the specified type, or an empty array if not found.
*/
get<T extends UIComponent>(type: T): TypeObject<T>[] {
return (this.view.get(type) as TypeObject<T>[]) || [] // Return an empty array if no entries are found

Check warning on line 54 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

54 line is not covered with tests
}

/**
* Retrieves all registered UIs.
* @returns An object containing all UIs with their registered component types as keys.
*/
getAll(): Record<UIComponent, TypeObject<UIComponent>[]> {
const ui: Record<UIComponent, TypeObject<UIComponent>[]> = {} as Record<

Check warning on line 62 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

62 line is not covered with tests
UIComponent,
TypeObject<UIComponent>[]
>
this.view.forEach((value, key) => {
ui[key] = value // Map directly to the UI components array

Check warning on line 67 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

66-67 lines are not covered with tests
})
return ui

Check warning on line 69 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

69 line is not covered with tests
}

/**
* The instance of the UI manager.
*/
static instance(): UIManager {
return (window.core?.UIManager as UIManager) ?? new UIManager()

Check warning on line 76 in core/src/browser/views/UIManager.ts

View workflow job for this annotation

GitHub Actions / coverage-check

76 line is not covered with tests
}
}
1 change: 1 addition & 0 deletions core/src/browser/views/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './UIManager'
14 changes: 14 additions & 0 deletions web/screens/Thread/ThreadRightPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { memo, useCallback, useMemo } from 'react'

import { UIComponent, UIManager } from '@janhq/core'

import {
InferenceEngine,
SettingComponentProps,
Expand Down Expand Up @@ -81,7 +83,7 @@
const componentDataRuntimeSetting = getConfigurationsData(
modelRuntimeParams,
selectedModel
).filter((x) => x.key !== 'prompt_template')

Check warning on line 86 in web/screens/Thread/ThreadRightPanel/index.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

86 line is not covered with tests

// engine setting
const modelEngineParams = extractModelLoadParams(
Expand All @@ -94,7 +96,7 @@
const componentDataEngineSetting = getConfigurationsData(
modelEngineParams,
selectedModel
).filter((x) => x.key !== 'prompt_template' && x.key !== 'embedding')

Check warning on line 99 in web/screens/Thread/ThreadRightPanel/index.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

99 line is not covered with tests

// the max value of max token has to follow context length
const maxTokens = componentDataRuntimeSetting.find(
Expand Down Expand Up @@ -214,6 +216,10 @@
[activeThread, resetModel, setEngineParamsUpdate, updateModelParameter]
)

const tabsFromExtension = UIManager.instance().get(
UIComponent.RightPanelTabItem
)

if (!activeThread) {
return null
}
Expand All @@ -234,6 +240,7 @@
},
]
: []),
...(tabsFromExtension.length ? tabsFromExtension : []),
]}
value={activeTabThreadRightPanel as string}
onValueChange={(value) => setActiveTabThreadRightPanel(value)}
Expand Down Expand Up @@ -294,6 +301,13 @@
<TabsContent value="tools">
<Tools />
</TabsContent>

{tabsFromExtension.length > 0 &&
tabsFromExtension.map((tab) => (
<TabsContent key={tab.value} value={tab.value}>
<div className="px-2 py-4" ref={(el) => el && tab.render(el)} />
</TabsContent>
))}
</Tabs>
</RightPanelContainer>
)
Expand Down
Loading