From f83b57673c6cf8303a13bf7e71e061563ffe4545 Mon Sep 17 00:00:00 2001 From: david qiu Date: Tue, 23 Jan 2024 15:38:33 -0800 Subject: [PATCH] Backport PR #575: Reflect theme changes without a refresh (#599) * Reflect theme changes without a refresh By leveraging `IThemeManager.themeChanged`, we can listen on theme change signals and rebuild the theme object in response. This allows CSS variable changes to reflect in the MUI theme without having to refresh the page. * update yarn.lock * pass themeManager as a prop instead of using top-level scope * remove theme hack added in #192 --------- Co-authored-by: gchow Co-authored-by: David L. Qiu (cherry picked from commit ca03e2c9a846344c379d50ccbe8323f7e0c7b1ed) Co-authored-by: Garson R Chow <58149459+garsonbyte@users.noreply.github.com> --- packages/jupyter-ai/src/components/chat.tsx | 4 +++- .../src/components/jl-theme-provider.tsx | 6 +++++- packages/jupyter-ai/src/index.ts | 16 +++++++++++----- packages/jupyter-ai/src/theme-provider.ts | 8 +------- packages/jupyter-ai/src/widgets/chat-error.tsx | 9 ++++++--- packages/jupyter-ai/src/widgets/chat-sidebar.tsx | 5 ++++- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/jupyter-ai/src/components/chat.tsx b/packages/jupyter-ai/src/components/chat.tsx index ded339c70..53ba45f1a 100644 --- a/packages/jupyter-ai/src/components/chat.tsx +++ b/packages/jupyter-ai/src/components/chat.tsx @@ -4,6 +4,7 @@ import { Button, IconButton, Stack } from '@mui/material'; import SettingsIcon from '@mui/icons-material/Settings'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import type { Awareness } from 'y-protocols/awareness'; +import type { IThemeManager } from '@jupyterlab/apputils'; import { JlThemeProvider } from './jl-theme-provider'; import { ChatMessages } from './chat-messages'; @@ -178,6 +179,7 @@ export type ChatProps = { selectionWatcher: SelectionWatcher; chatHandler: ChatHandler; globalAwareness: Awareness | null; + themeManager: IThemeManager | null; chatView?: ChatView; }; @@ -190,7 +192,7 @@ export function Chat(props: ChatProps): JSX.Element { const [view, setView] = useState(props.chatView || ChatView.Chat); return ( - + (createTheme()); @@ -12,7 +14,9 @@ export function JlThemeProvider(props: { async function setJlTheme() { setTheme(await getJupyterLabTheme()); } + setJlTheme(); + props.themeManager?.themeChanged.connect(setJlTheme); }, []); return {props.children}; diff --git a/packages/jupyter-ai/src/index.ts b/packages/jupyter-ai/src/index.ts index aefdc8e18..00d493289 100644 --- a/packages/jupyter-ai/src/index.ts +++ b/packages/jupyter-ai/src/index.ts @@ -4,7 +4,11 @@ import { ILayoutRestorer } from '@jupyterlab/application'; -import { IWidgetTracker, ReactWidget } from '@jupyterlab/apputils'; +import { + IWidgetTracker, + ReactWidget, + IThemeManager +} from '@jupyterlab/apputils'; import { IDocumentWidget } from '@jupyterlab/docregistry'; import { IGlobalAwareness } from '@jupyterlab/collaboration'; import type { Awareness } from 'y-protocols/awareness'; @@ -21,11 +25,12 @@ export type DocumentTracker = IWidgetTracker; const plugin: JupyterFrontEndPlugin = { id: 'jupyter_ai:plugin', autoStart: true, - optional: [IGlobalAwareness, ILayoutRestorer], + optional: [IGlobalAwareness, ILayoutRestorer, IThemeManager], activate: async ( app: JupyterFrontEnd, globalAwareness: Awareness | null, - restorer: ILayoutRestorer + restorer: ILayoutRestorer | null, + themeManager: IThemeManager | null ) => { /** * Initialize selection watcher singleton @@ -43,10 +48,11 @@ const plugin: JupyterFrontEndPlugin = { chatWidget = buildChatSidebar( selectionWatcher, chatHandler, - globalAwareness + globalAwareness, + themeManager ); } catch (e) { - chatWidget = buildErrorWidget(); + chatWidget = buildErrorWidget(themeManager); } /** diff --git a/packages/jupyter-ai/src/theme-provider.ts b/packages/jupyter-ai/src/theme-provider.ts index 405f08198..02db8d369 100644 --- a/packages/jupyter-ai/src/theme-provider.ts +++ b/packages/jupyter-ai/src/theme-provider.ts @@ -13,7 +13,6 @@ export async function pollUntilReady(): Promise { export async function getJupyterLabTheme(): Promise { await pollUntilReady(); const light = document.body.getAttribute('data-jp-theme-light'); - const primaryFontColor = getCSSVariable('--jp-ui-font-color1'); return createTheme({ spacing: 4, components: { @@ -113,7 +112,7 @@ export async function getJupyterLabTheme(): Promise { dark: getCSSVariable('--jp-success-color0') }, text: { - primary: primaryFontColor, + primary: getCSSVariable('--jp-ui-font-color1'), secondary: getCSSVariable('--jp-ui-font-color2'), disabled: getCSSVariable('--jp-ui-font-color3') } @@ -127,11 +126,6 @@ export async function getJupyterLabTheme(): Promise { htmlFontSize: 16, button: { textTransform: 'capitalize' - }, - // this is undocumented as of the time of writing. - // https://stackoverflow.com/a/62950304/12548458 - allVariants: { - color: primaryFontColor } } }); diff --git a/packages/jupyter-ai/src/widgets/chat-error.tsx b/packages/jupyter-ai/src/widgets/chat-error.tsx index 3b8f8ef95..8ae9cbb44 100644 --- a/packages/jupyter-ai/src/widgets/chat-error.tsx +++ b/packages/jupyter-ai/src/widgets/chat-error.tsx @@ -1,13 +1,16 @@ import React from 'react'; import { ReactWidget } from '@jupyterlab/apputils'; +import type { IThemeManager } from '@jupyterlab/apputils'; +import { Alert, Box } from '@mui/material'; import { chatIcon } from '../icons'; -import { Alert, Box } from '@mui/material'; import { JlThemeProvider } from '../components/jl-theme-provider'; -export function buildErrorWidget(): ReactWidget { +export function buildErrorWidget( + themeManager: IThemeManager | null +): ReactWidget { const ErrorWidget = ReactWidget.create( - + ); ChatWidget.id = 'jupyter-ai::chat';