From 500dc2559db997cfd96cafc3f20bee8e7ed41e6f Mon Sep 17 00:00:00 2001 From: cenguanming <975402925@qq.com> Date: Wed, 12 Aug 2026 18:43:06 +0800 Subject: [PATCH 01/13] refactor(genui-sdk-vue): move theme change logic to materials --- packages/core/src/material/index.ts | 2 + packages/core/src/material/materials-theme.ts | 21 +++ packages/core/src/material/materials.ts | 3 + packages/core/src/material/merge-materials.ts | 55 ++++++ .../frameworks/vue/src/chat/GenuiChat.vue | 6 +- .../src/config-provider/ConfigProvider.vue | 158 +++++++++++------- .../src/config-provider/injection-tokens.ts | 12 +- .../src/materials/materials.ts | 2 + .../src/materials/theme/index.ts | 47 ++++++ .../materials/vue-opentiny-vue/package.json | 2 + .../src/materials/materials.ts | 7 +- .../src/materials/theme/ThemeRoot.ts | 24 +++ .../src/materials/theme/index.ts | 53 ++++++ pnpm-lock.yaml | 6 + .../web/src/components/AssistantFooter.vue | 4 +- .../web/src/components/UserFooter.vue | 4 +- .../genui-template/GenuiTemplateChat.vue | 12 +- .../use-monaco-playground-theme.ts | 8 +- 18 files changed, 346 insertions(+), 80 deletions(-) create mode 100644 packages/core/src/material/materials-theme.ts create mode 100644 packages/core/src/material/merge-materials.ts create mode 100644 packages/materials/vue-element-plus/src/materials/theme/index.ts create mode 100644 packages/materials/vue-opentiny-vue/src/materials/theme/ThemeRoot.ts create mode 100644 packages/materials/vue-opentiny-vue/src/materials/theme/index.ts diff --git a/packages/core/src/material/index.ts b/packages/core/src/material/index.ts index 39289cc9..d3c37962 100644 --- a/packages/core/src/material/index.ts +++ b/packages/core/src/material/index.ts @@ -2,3 +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..01d30f82 --- /dev/null +++ b/packages/core/src/material/materials-theme.ts @@ -0,0 +1,21 @@ +export type ThemeColorScheme = 'light' | 'dark'; + +export interface ThemeDescriptor { + id: string; + label?: string; + colorScheme?: ThemeColorScheme; +} + +export interface ThemeApplyContext { + scopeId: string; + rootEl?: HTMLElement | null; + systemColorScheme: ThemeColorScheme; +} + +export type ThemeDisposer = () => void; + +export interface IMaterialsTheme { + themes?: ThemeDescriptor[]; + apply(theme: string, ctx: ThemeApplyContext): void | ThemeDisposer; + Root?: unknown; +} diff --git a/packages/core/src/material/materials.ts b/packages/core/src/material/materials.ts index dded8bf7..4a5cdb4f 100644 --- a/packages/core/src/material/materials.ts +++ b/packages/core/src/material/materials.ts @@ -1,3 +1,5 @@ +import type { IMaterialsTheme } from './materials-theme'; + export type IMaterialComponent = unknown; export type IMaterialsMap = Record; @@ -6,5 +8,6 @@ export interface IMaterials { components?: IMaterialsMap; requiredCompleteFieldSelectors?: string[]; defaultPropsMap?: Record; + theme?: IMaterialsTheme | IMaterialsTheme[]; [key: string]: any; } diff --git a/packages/core/src/material/merge-materials.ts b/packages/core/src/material/merge-materials.ts new file mode 100644 index 00000000..52d45a20 --- /dev/null +++ b/packages/core/src/material/merge-materials.ts @@ -0,0 +1,55 @@ +import type { IMaterials } from './materials'; +import type { IMaterialsTheme } from './materials-theme'; + +const KNOWN_KEYS = ['components', 'requiredCompleteFieldSelectors', 'defaultPropsMap', 'theme']; + +export function mergeMaterials(...sources: (IMaterials | undefined)[]): IMaterials { + const components: Record = {}; + const requiredCompleteFieldSelectors: string[] = []; + const defaultPropsMap: Record = {}; + const themes: IMaterialsTheme[] = []; + 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.theme) { + const arr = Array.isArray(src.theme) ? src.theme : [src.theme]; + for (const theme of arr) { + if (theme && !seenThemes.has(theme)) { + seenThemes.add(theme); + themes.push(theme); + } + } + } + for (const key of Object.keys(src)) { + if (!KNOWN_KEYS.includes(key)) { + extra[key] = (src as Record)[key]; + } + } + } + + const merged: IMaterials = { + components, + requiredCompleteFieldSelectors, + defaultPropsMap, + ...extra, + }; + + if (themes.length === 1) { + merged.theme = themes[0]; + } else if (themes.length > 1) { + merged.theme = themes; + } + + return merged; +} diff --git a/packages/frameworks/vue/src/chat/GenuiChat.vue b/packages/frameworks/vue/src/chat/GenuiChat.vue index c0f2872c..da396be3 100644 --- a/packages/frameworks/vue/src/chat/GenuiChat.vue +++ b/packages/frameworks/vue/src/chat/GenuiChat.vue @@ -15,7 +15,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'; @@ -42,7 +42,7 @@ import { IResponseHandler, defaultResponseHandlers } from './response-handler'; const props = defineProps(); -const genuiConfig: any = inject(GENUI_CONFIG, null); +const genuiConfig = inject(GENUI_CONFIG, null); const { t } = useI18n(); const isAllowFiles = computed(() => { @@ -482,7 +482,7 @@ defineExpose({