diff --git a/packages/core/src/material/index.ts b/packages/core/src/material/index.ts index 251e84c4..d3c37962 100644 --- a/packages/core/src/material/index.ts +++ b/packages/core/src/material/index.ts @@ -2,4 +2,5 @@ export * from './default-props-map'; export * from './materials'; export * from './materials-meta'; export * from './materials-protocol'; +export * from './materials-theme'; export * from './merge-materials'; diff --git a/packages/core/src/material/materials-theme.ts b/packages/core/src/material/materials-theme.ts new file mode 100644 index 00000000..3fd8f144 --- /dev/null +++ b/packages/core/src/material/materials-theme.ts @@ -0,0 +1,25 @@ +export type ThemeColorScheme = 'light' | 'dark'; + +export interface IThemeDescriptor { + id: string; + colorScheme?: ThemeColorScheme; +} + +export interface IThemeApplyContext { + systemColorScheme: ThemeColorScheme; +} + +export type ThemeDisposer = () => void; + +export interface IThemeApplyResult { + descriptor: IThemeDescriptor; + dispose?: ThemeDisposer; + root?: unknown; +} + +export interface IMaterialsTheme { + themes?: IThemeDescriptor[]; + apply(theme: string, ctx: IThemeApplyContext): IThemeApplyResult; +} + +export type MaterialsThemeFactory = () => IMaterialsTheme; diff --git a/packages/core/src/material/materials.ts b/packages/core/src/material/materials.ts index dded8bf7..33c4b61d 100644 --- a/packages/core/src/material/materials.ts +++ b/packages/core/src/material/materials.ts @@ -1,3 +1,5 @@ +import type { MaterialsThemeFactory } from './materials-theme'; + export type IMaterialComponent = unknown; export type IMaterialsMap = Record; @@ -6,5 +8,10 @@ export interface IMaterials { components?: IMaterialsMap; requiredCompleteFieldSelectors?: string[]; defaultPropsMap?: Record; + themeFactory?: MaterialsThemeFactory; [key: string]: any; } + +export type MergedMaterials = Omit & { + themeFactory?: MaterialsThemeFactory | MaterialsThemeFactory[]; +}; diff --git a/packages/core/src/material/merge-materials.ts b/packages/core/src/material/merge-materials.ts index 448742f6..6b407be6 100644 --- a/packages/core/src/material/merge-materials.ts +++ b/packages/core/src/material/merge-materials.ts @@ -1,20 +1,53 @@ -import type { IMaterials } from './materials'; +import type { IMaterials, MergedMaterials } from './materials'; +import type { MaterialsThemeFactory } from './materials-theme'; -export function mergeMaterials(...list: IMaterials[]): IMaterials { - const result: IMaterials = { - components: {}, - defaultPropsMap: {}, - requiredCompleteFieldSelectors: [], +const handleKeys = ['components', 'requiredCompleteFieldSelectors', 'defaultPropsMap', 'themeFactory']; + +export function mergeMaterials(...sources: (IMaterials | undefined)[]): MergedMaterials { + const components: Record = {}; + const requiredCompleteFieldSelectors: string[] = []; + const defaultPropsMap: Record = {}; + const themes: MaterialsThemeFactory[] = []; + const seenThemes = new Set(); + const extra: Record = {}; + + for (const src of sources) { + if (!src) { + continue; + } + Object.assign(components, src.components ?? {}); + for (const selector of src.requiredCompleteFieldSelectors ?? []) { + if (!requiredCompleteFieldSelectors.includes(selector)) { + requiredCompleteFieldSelectors.push(selector); + } + } + Object.assign(defaultPropsMap, src.defaultPropsMap ?? {}); + if (src.themeFactory) { + const factories = Array.isArray(src.themeFactory) ? src.themeFactory : [src.themeFactory]; + for (const factory of factories) { + if (factory && !seenThemes.has(factory)) { + seenThemes.add(factory); + themes.push(factory); + } + } + } + for (const key of Object.keys(src)) { + if (!handleKeys.includes(key)) { + extra[key] = (src as Record)[key]; + } + } + } + + const merged: MergedMaterials = { + components, + requiredCompleteFieldSelectors, + defaultPropsMap, + ...extra, }; - for (const item of list) { - if (!item) continue; - Object.assign(result.components!, item.components ?? {}); - Object.assign(result.defaultPropsMap!, item.defaultPropsMap ?? {}); - result.requiredCompleteFieldSelectors!.push( - ...(item.requiredCompleteFieldSelectors ?? []), - ); + if (themes.length > 0) { + merged.themeFactory = themes; } - return result; + return merged; } diff --git a/packages/frameworks/vue/src/chat/GenuiChat.vue b/packages/frameworks/vue/src/chat/GenuiChat.vue index 9546c841..a4dd1cbf 100644 --- a/packages/frameworks/vue/src/chat/GenuiChat.vue +++ b/packages/frameworks/vue/src/chat/GenuiChat.vue @@ -10,7 +10,7 @@ import type { UserItem, UserTextItem, } from '@opentiny/tiny-robot'; -import { ref, watch, computed, h, inject, provide } from 'vue'; +import { ref, watch, computed, h, inject } from 'vue'; import type { Ref, Component } from 'vue'; import { CustomModelProvider } from './CustomModelProvider'; import { scrollEnd, throttle, toSlotFunction } from './chat-utils'; @@ -37,7 +37,7 @@ import { IResponseHandler, defaultResponseHandlers } from './response-handler'; const props = defineProps(); -const genuiConfig: any = inject(GENUI_CONFIG, null); +const genuiConfig = inject(GENUI_CONFIG); const { t } = useI18n(); const isAllowFiles = computed(() => { @@ -484,7 +484,7 @@ defineExpose({