Skip to content

Commit

Permalink
Reflect theme changes without a refresh (#575)
Browse files Browse the repository at this point in the history
* 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 <garson.chow@factset.com>
Co-authored-by: David L. Qiu <david@qiu.dev>
  • Loading branch information
3 people authored Jan 23, 2024
1 parent 71227d9 commit ca03e2c
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 20 deletions.
4 changes: 3 additions & 1 deletion packages/jupyter-ai/src/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -178,6 +179,7 @@ export type ChatProps = {
selectionWatcher: SelectionWatcher;
chatHandler: ChatHandler;
globalAwareness: Awareness | null;
themeManager: IThemeManager | null;
chatView?: ChatView;
};

Expand All @@ -190,7 +192,7 @@ export function Chat(props: ChatProps): JSX.Element {
const [view, setView] = useState<ChatView>(props.chatView || ChatView.Chat);

return (
<JlThemeProvider>
<JlThemeProvider themeManager={props.themeManager}>
<SelectionContextProvider selectionWatcher={props.selectionWatcher}>
<CollaboratorsContextProvider globalAwareness={props.globalAwareness}>
<Box
Expand Down
6 changes: 5 additions & 1 deletion packages/jupyter-ai/src/components/jl-theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useState, useEffect } from 'react';

import type { IThemeManager } from '@jupyterlab/apputils';
import { Theme, ThemeProvider, createTheme } from '@mui/material/styles';

import { getJupyterLabTheme } from '../theme-provider';

export function JlThemeProvider(props: {
themeManager: IThemeManager | null;
children: React.ReactNode;
}): JSX.Element {
const [theme, setTheme] = useState<Theme>(createTheme());
Expand All @@ -12,7 +14,9 @@ export function JlThemeProvider(props: {
async function setJlTheme() {
setTheme(await getJupyterLabTheme());
}

setJlTheme();
props.themeManager?.themeChanged.connect(setJlTheme);
}, []);

return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>;
Expand Down
16 changes: 11 additions & 5 deletions packages/jupyter-ai/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 '@jupyter/collaboration';
import type { Awareness } from 'y-protocols/awareness';
Expand All @@ -23,11 +27,12 @@ export type DocumentTracker = IWidgetTracker<IDocumentWidget>;
const plugin: JupyterFrontEndPlugin<void> = {
id: 'jupyter_ai:plugin',
autoStart: true,
optional: [IGlobalAwareness, ILayoutRestorer],
optional: [IGlobalAwareness, ILayoutRestorer, IThemeManager],
activate: async (
app: JupyterFrontEnd,
globalAwareness: Awareness | null,
restorer: ILayoutRestorer | null
restorer: ILayoutRestorer | null,
themeManager: IThemeManager | null
) => {
/**
* Initialize selection watcher singleton
Expand All @@ -45,10 +50,11 @@ const plugin: JupyterFrontEndPlugin<void> = {
chatWidget = buildChatSidebar(
selectionWatcher,
chatHandler,
globalAwareness
globalAwareness,
themeManager
);
} catch (e) {
chatWidget = buildErrorWidget();
chatWidget = buildErrorWidget(themeManager);
}

/**
Expand Down
8 changes: 1 addition & 7 deletions packages/jupyter-ai/src/theme-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export async function pollUntilReady(): Promise<void> {
export async function getJupyterLabTheme(): Promise<Theme> {
await pollUntilReady();
const light = document.body.getAttribute('data-jp-theme-light');
const primaryFontColor = getCSSVariable('--jp-ui-font-color1');
return createTheme({
spacing: 4,
components: {
Expand Down Expand Up @@ -113,7 +112,7 @@ export async function getJupyterLabTheme(): Promise<Theme> {
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')
}
Expand All @@ -127,11 +126,6 @@ export async function getJupyterLabTheme(): Promise<Theme> {
htmlFontSize: 16,
button: {
textTransform: 'capitalize'
},
// this is undocumented as of the time of writing.
// https://stackoverflow.com/a/62950304/12548458
allVariants: {
color: primaryFontColor
}
}
});
Expand Down
9 changes: 6 additions & 3 deletions packages/jupyter-ai/src/widgets/chat-error.tsx
Original file line number Diff line number Diff line change
@@ -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(
<JlThemeProvider>
<JlThemeProvider themeManager={themeManager}>
<Box
sx={{
width: '100%',
Expand Down
5 changes: 4 additions & 1 deletion packages/jupyter-ai/src/widgets/chat-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { ReactWidget } from '@jupyterlab/apputils';
import type { IThemeManager } from '@jupyterlab/apputils';
import type { Awareness } from 'y-protocols/awareness';

import { Chat } from '../components/chat';
Expand All @@ -10,13 +11,15 @@ import { ChatHandler } from '../chat_handler';
export function buildChatSidebar(
selectionWatcher: SelectionWatcher,
chatHandler: ChatHandler,
globalAwareness: Awareness | null
globalAwareness: Awareness | null,
themeManager: IThemeManager | null
): ReactWidget {
const ChatWidget = ReactWidget.create(
<Chat
selectionWatcher={selectionWatcher}
chatHandler={chatHandler}
globalAwareness={globalAwareness}
themeManager={themeManager}
/>
);
ChatWidget.id = 'jupyter-ai::chat';
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14822,11 +14822,11 @@ __metadata:

"typescript@patch:typescript@^3 || ^4#~builtin<compat/typescript>, typescript@patch:typescript@~4.9.0#~builtin<compat/typescript>":
version: 4.9.5
resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin<compat/typescript>::version=4.9.5&hash=23ec76"
resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin<compat/typescript>::version=4.9.5&hash=289587"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: ab417a2f398380c90a6cf5a5f74badd17866adf57f1165617d6a551f059c3ba0a3e4da0d147b3ac5681db9ac76a303c5876394b13b3de75fdd5b1eaa06181c9d
checksum: 1f8f3b6aaea19f0f67cba79057674ba580438a7db55057eb89cc06950483c5d632115c14077f6663ea76fd09fce3c190e6414bb98582ec80aa5a4eaf345d5b68
languageName: node
linkType: hard

Expand Down

0 comments on commit ca03e2c

Please sign in to comment.