Skip to content
Open
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
2 changes: 1 addition & 1 deletion packages/dashboard/src/settingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { GearIcon } from './icons';

export const SettingsButton: React.FC = () => {
const [open, setOpen] = React.useState(false);
const [theme, setTheme] = useThemeSetting();
const [theme, , setTheme] = useThemeSetting();
const containerRef = React.useRef<HTMLDivElement>(null);

React.useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/html-reporter/src/headerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const NavLink: React.FC<{
const SettingsButton: React.FC = () => {
const settingsRef = React.useRef<HTMLDivElement>(null);
const [settingsOpen, setSettingsOpen] = React.useState(false);
const [theme, setTheme] = useThemeSetting();
const [theme, , setTheme] = useThemeSetting();
const [mergeFiles, setMergeFiles] = useSetting('mergeFiles', false);

return <>
Expand Down
58 changes: 58 additions & 0 deletions packages/injected/src/highlight.css
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,61 @@ x-pw-action-item:last-child {
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
}

:host(.dark-mode) {
color: #cccccc;
color-scheme: dark;
}

:host(.dark-mode) x-pw-tooltip,
:host(.dark-mode) x-pw-dialog {
background-color: #252526;
box-shadow: 0 0.5rem 1.2rem rgba(0, 0, 0, 0.6);
}

:host(.dark-mode) x-pw-tooltip-footer {
color: rgba(204, 204, 204, 0.7);
}

:host(.dark-mode) x-pw-tools-list {
border-bottom: 1px solid #3c3c3c;
}

:host(.dark-mode) x-pw-overlay x-pw-tools-list {
background-color: #252526dd;
box-shadow: rgba(0, 0, 0, 0.4) 0px 5px 5px;
}

:host(.dark-mode) x-pw-tool-item:not(.disabled):hover,
:host(.dark-mode) x-pw-tool-item.record.toggled:not(.disabled):hover {
background-color: hsl(0, 0%, 25%);
}

:host(.dark-mode) x-pw-tool-item > x-div {
background-color: #c5c5c5;
}

:host(.dark-mode) x-pw-tool-item.disabled > x-div {
background-color: rgba(197, 197, 197, 0.4);
}

:host(.dark-mode) x-pw-tool-gripper > x-div {
background-color: #969696;
}

:host(.dark-mode) x-pw-tool-item.record.toggled > x-div {
background-color: #f48771;
}

:host(.dark-mode) textarea.text-editor {
color: #cccccc;
background-color: transparent;
}

:host(.dark-mode) x-locator-editor {
border-bottom: 1px solid #3c3c3c;
}

:host(.dark-mode) x-pw-action-item:hover {
background-color: hsl(0, 0%, 20%);
}
10 changes: 10 additions & 0 deletions packages/injected/src/highlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export type HighlightEntry = {
cssStyle?: string,
};

type OverlayTheme = 'dark-mode' | 'light-mode';

export class Highlight {
private _glassPaneElement: HTMLElement;
private _glassPaneShadow: ShadowRoot;
Expand All @@ -64,6 +66,7 @@ export class Highlight {
private _rafRequest: number | undefined;
private _language: Language = 'javascript';
private _elementHighlightSelectors = new Map<string, { selector: ParsedSelector, cssStyle?: string }>();
private _overlayTheme: OverlayTheme = 'light-mode';

constructor(injectedScript: InjectedScript) {
this._injectedScript = injectedScript;
Expand Down Expand Up @@ -106,6 +109,13 @@ export class Highlight {
this._glassPaneShadow.appendChild(this._userOverlayContainer);
}

setOverlayTheme(theme: OverlayTheme) {
if (this._overlayTheme === theme)
return;
this._overlayTheme = theme;
this._glassPaneElement.classList.toggle('dark-mode', theme === 'dark-mode');
}

install() {
// NOTE: document.documentElement can be null: https://github.com/microsoft/TypeScript/issues/50078
if (!this._injectedScript.document.documentElement)
Expand Down
6 changes: 3 additions & 3 deletions packages/injected/src/recorder/pollingRecorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ import { Recorder } from './recorder';
import type { InjectedScript } from '../injectedScript';
import type { RecorderDelegate } from './recorder';
import type * as actions from '@recorder/actions';
import type { ElementInfo, Mode, OverlayState, UIState } from '@recorder/recorderTypes';
import type { ElementInfo, Mode, UIState } from '@recorder/recorderTypes';

interface Embedder {
__pw_recorderPerformAction(action: actions.PerformOnRecordAction): Promise<void>;
__pw_recorderRecordAction(action: actions.Action): Promise<void>;
__pw_recorderState(): Promise<UIState>;
__pw_recorderElementPicked(element: { selector: string, ariaSnapshot?: string }): Promise<void>;
__pw_recorderSetMode(mode: Mode): Promise<void>;
__pw_recorderSetOverlayState(state: OverlayState): Promise<void>;
__pw_recorderSetOverlayState(state: { offsetX: number }): Promise<void>;
__pw_refreshOverlay(): void;
}

Expand Down Expand Up @@ -92,7 +92,7 @@ export class PollingRecorder implements RecorderDelegate {
await this._embedder.__pw_recorderSetMode(mode);
}

async setOverlayState(state: OverlayState): Promise<void> {
async setOverlayState(state: { offsetX: number }): Promise<void> {
await this._embedder.__pw_recorderSetOverlayState(state);
}
}
Expand Down
7 changes: 4 additions & 3 deletions packages/injected/src/recorder/recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type { Highlight, HighlightEntry } from '../highlight';
import type { InjectedScript } from '../injectedScript';
import type { ElementText } from '../selectorUtils';
import type * as actions from '@recorder/actions';
import type { ElementInfo, Mode, OverlayState, UIState } from '@recorder/recorderTypes';
import type { ElementInfo, Mode, UIState } from '@recorder/recorderTypes';
import type { Language } from '@isomorphic/locatorGenerators';

const HighlightColors = {
Expand All @@ -39,7 +39,7 @@ export interface RecorderDelegate {
recordAction?(action: actions.Action): Promise<void>;
elementPicked?(elementInfo: ElementInfo): Promise<void>;
setMode?(mode: Mode): Promise<void>;
setOverlayState?(state: OverlayState): Promise<void>;
setOverlayState?(state: { offsetX: number }): Promise<void>;
highlightUpdated?(): void;
}

Expand Down Expand Up @@ -1383,7 +1383,7 @@ export class Recorder {
mode: 'none',
testIdAttributeName: 'data-testid',
language: 'javascript',
overlay: { offsetX: 0 },
overlay: { offsetX: 0, theme: 'light-mode' },
};
readonly document: Document;
private _delegate: RecorderDelegate = {};
Expand Down Expand Up @@ -1488,6 +1488,7 @@ export class Recorder {

this.state = state;
this.highlight.setLanguage(state.language);
this.highlight.setOverlayTheme(state.overlay.theme);
this._switchCurrentTool();
this.overlay?.setUIState(state);

Expand Down
12 changes: 8 additions & 4 deletions packages/playwright-core/src/server/recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot';
import type { Progress } from './progress';
import type * as channels from '@protocol/channels';
import type * as actions from '@recorder/actions';
import type { CallLog, CallLogStatus, ElementInfo, Mode, OverlayState, Source, UIState } from '@recorder/recorderTypes';
import type { CallLog, CallLogStatus, ElementInfo, Mode, OverlayState, OverlayTheme, Source, UIState } from '@recorder/recorderTypes';
import type { RegisteredListener } from '@utils/eventsHelper';

const recorderSymbol = Symbol('recorderSymbol');
Expand Down Expand Up @@ -79,7 +79,7 @@ export class Recorder extends EventEmitter<RecorderEventMap> implements Instrume
private _params: RecorderParams;
private _mode: Mode;
private _highlightedElement: { selector?: string, ariaTemplate?: AriaTemplateNode } = {};
private _overlayState: OverlayState = { offsetX: 0 };
private _overlayState: OverlayState = { offsetX: 0, theme: 'light-mode' };
private _currentCallsMetadata = new Map<CallMetadata, SdkObject>();
private _actionPoints = new Map<string, Point>();
private _userSources = new Map<string, Source>();
Expand Down Expand Up @@ -207,10 +207,10 @@ export class Recorder extends EventEmitter<RecorderEventMap> implements Instrume
await this.setMode(mode);
});

await this._context.exposeBinding(progress, '__pw_recorderSetOverlayState', async ({ frame }, state: OverlayState) => {
await this._context.exposeBinding(progress, '__pw_recorderSetOverlayState', async ({ frame }, state: { offsetX: number }) => {
if (frame.parentFrame())
return;
this._overlayState = state;
this._overlayState = { ...this._overlayState, ...state };
});

await this._context.exposeBinding(progress, '__pw_resume', () => {
Expand Down Expand Up @@ -257,6 +257,10 @@ export class Recorder extends EventEmitter<RecorderEventMap> implements Instrume
return this._mode;
}

setOverlayTheme(theme: OverlayTheme) {
this._overlayState = { ...this._overlayState, theme };
}

async setMode(mode: Mode) {
if (this._mode === mode)
return;
Expand Down
5 changes: 4 additions & 1 deletion packages/playwright-core/src/server/recorder/recorderApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { BrowserContext } from '../browserContext';

import type { Page } from '../page';
import type * as actions from '@recorder/actions';
import type { CallLog, ElementInfo, Mode, RecorderBackend, RecorderFrontend, Source } from '@recorder/recorderTypes';
import type { CallLog, ElementInfo, Mode, OverlayTheme, RecorderBackend, RecorderFrontend, Source } from '@recorder/recorderTypes';
import type { Language, LanguageGeneratorOptions } from '../codegen/types';
import type * as channels from '@protocol/channels';
import type { Progress } from '../progress';
Expand Down Expand Up @@ -150,6 +150,9 @@ export class RecorderApp {
this._languageGeneratorOptions.generateAutoExpect = params.autoExpect;
this._updateActions();
},
setOverlayTheme: async (params: { theme: OverlayTheme }) => {
this._recorder.setOverlayTheme(params.theme);
},
setMode: async (params: { mode: Mode }) => {
await this._recorder.setMode(params.mode);
},
Expand Down
6 changes: 5 additions & 1 deletion packages/recorder/src/recorder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const Recorder: React.FC = ({}) => {
const [ariaSnapshot, setAriaSnapshot] = React.useState<string | undefined>();
const [ariaSnapshotErrors, setAriaSnapshotErrors] = React.useState<SourceHighlight[]>();
const [settingsOpen, setSettingsOpen] = React.useState(false);
const [theme, setTheme] = useThemeSetting();
const [theme, documentTheme, setTheme] = useThemeSetting();
const [autoExpect, setAutoExpect] = useSetting<boolean>('autoExpect', false);
const settingsButtonRef = React.useRef<HTMLButtonElement>(null);
const backend = React.useMemo(createRecorderBackend, []);
Expand Down Expand Up @@ -104,6 +104,10 @@ export const Recorder: React.FC = ({}) => {
backend.setAutoExpect({ autoExpect });
}, [autoExpect, backend]);

React.useEffect(() => {
backend.setOverlayTheme({ theme: documentTheme });
}, [documentTheme, backend]);

React.useLayoutEffect(() => {
messagesEndRef.current?.scrollIntoView({ block: 'center', inline: 'nearest' });
}, [messagesEndRef]);
Expand Down
4 changes: 4 additions & 0 deletions packages/recorder/src/recorderTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ export type EventData = {
params: any;
};

export type OverlayTheme = 'dark-mode' | 'light-mode';

export type OverlayState = {
offsetX: number;
theme: OverlayTheme;
};

export type UIState = {
Expand Down Expand Up @@ -108,6 +111,7 @@ declare global {
export interface RecorderBackend {
setMode(params: { mode: Mode }): Promise<void>;
setAutoExpect(params: { autoExpect: boolean }): Promise<void>;
setOverlayTheme(params: { theme: OverlayTheme }): Promise<void>;
resume(): Promise<void>;
pause(): Promise<void>;
step(): Promise<void>;
Expand Down
2 changes: 1 addition & 1 deletion packages/trace-viewer/src/ui/defaultSettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const DefaultSettingsView: React.FC<{
shouldPopulateCanvasFromScreenshot,
setShouldPopulateCanvasFromScreenshot,
] = useSetting('shouldPopulateCanvasFromScreenshot', false);
const [theme, setTheme] = useThemeSetting();
const [theme, , setTheme] = useThemeSetting();
const [mergeFiles, setMergeFiles] = useSetting('mergeFiles', false);

return (
Expand Down
2 changes: 1 addition & 1 deletion packages/trace-viewer/src/ui/snapshotTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export const InspectModeController: React.FunctionComponent<{
ariaTemplate,
language: sdkLanguage,
testIdAttributeName,
overlay: { offsetX: 0 },
overlay: { offsetX: 0, theme: 'light-mode' },
}, {
async elementPicked(elementInfo: ElementInfo) {
setHighlightedElement({
Expand Down
12 changes: 9 additions & 3 deletions packages/web/src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ declare global {
}
}

type DocumentTheme = 'dark-mode' | 'light-mode';
export type DocumentTheme = 'dark-mode' | 'light-mode';
export type Theme = DocumentTheme | 'system';

const kDefaultTheme: Theme = 'system';
Expand Down Expand Up @@ -93,13 +93,19 @@ export function currentDocumentTheme(): DocumentTheme | null {
return null;
}

export function useThemeSetting(): [Theme, (value: Theme) => void] {
export function useThemeSetting(): [Theme, DocumentTheme, (value: Theme) => void] {
const [theme, setTheme] = React.useState<Theme>(currentTheme());
const [documentTheme, setDocumentTheme] = React.useState<DocumentTheme>(() => currentDocumentTheme() ?? 'light-mode');

React.useEffect(() => {
settings.setString(kThemeSettingsKey, theme);
updateDocumentTheme(theme);
}, [theme]);

return [theme, setTheme];
React.useEffect(() => {
addThemeListener(setDocumentTheme);
return () => removeThemeListener(setDocumentTheme);
}, []);

return [theme, documentTheme, setTheme];
}
Loading