mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-12 00:52:05 +03:00
chore: allow toggling recorder/traceviewer color modes (#18718)
Fixes: https://github.com/microsoft/playwright/issues/18700
This commit is contained in:
parent
dfb4ad388a
commit
d5eb74fa5d
@ -15,7 +15,9 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import type { Page } from '../page';
|
||||
import { registryDirectory } from '../registry';
|
||||
import type { CRPage } from './crPage';
|
||||
|
||||
export async function installAppIcon(page: Page) {
|
||||
@ -25,3 +27,25 @@ export async function installAppIcon(page: Page) {
|
||||
image: icon.toString('base64')
|
||||
});
|
||||
}
|
||||
|
||||
export async function syncLocalStorageWithSettings(page: Page, appName: string) {
|
||||
const settingsFile = path.join(registryDirectory, '.settings', `${appName}.json`);
|
||||
await page.exposeBinding('saveSettings', false, (_, settings: any) => {
|
||||
fs.mkdirSync(path.dirname(settingsFile), { recursive: true });
|
||||
fs.writeFileSync(settingsFile, settings);
|
||||
});
|
||||
|
||||
const settings = await fs.promises.readFile(settingsFile, 'utf-8').catch(() => ('{}'));
|
||||
await page.addInitScript(`(${String((settings: any) => {
|
||||
Object.entries(settings).map(([k, v]) => localStorage[k] = v);
|
||||
|
||||
let lastValue = JSON.stringify(localStorage);
|
||||
setInterval(() => {
|
||||
const value = JSON.stringify(localStorage);
|
||||
if (value !== lastValue) {
|
||||
lastValue = value;
|
||||
window.saveSettings(value);
|
||||
}
|
||||
}, 2000);
|
||||
})})(${settings})`);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import { serverSideCallMetadata } from '../instrumentation';
|
||||
import type { CallLog, EventData, Mode, Source } from '@recorder/recorderTypes';
|
||||
import { isUnderTest } from '../../utils';
|
||||
import { mime } from '../../utilsBundle';
|
||||
import { installAppIcon } from '../chromium/crApp';
|
||||
import { installAppIcon, syncLocalStorageWithSettings } from '../chromium/crApp';
|
||||
import { findChromiumChannel } from '../registry';
|
||||
import type { Recorder } from '../recorder';
|
||||
import type { BrowserContext } from '../browserContext';
|
||||
@ -37,6 +37,7 @@ declare global {
|
||||
playwrightSetSelector: (selector: string, focus?: boolean) => void;
|
||||
playwrightUpdateLogs: (callLogs: CallLog[]) => void;
|
||||
dispatch(data: EventData): Promise<void>;
|
||||
saveSettings(data: any): Promise<void>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +80,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
|
||||
|
||||
private async _init() {
|
||||
await installAppIcon(this._page);
|
||||
await syncLocalStorageWithSettings(this._page, 'recorder');
|
||||
|
||||
await this._page._setServerRequestInterceptor(async route => {
|
||||
if (route.request().url().startsWith('https://playwright/')) {
|
||||
|
@ -21,7 +21,7 @@ import { HttpServer } from '../../../utils/httpServer';
|
||||
import { findChromiumChannel } from '../../registry';
|
||||
import { isUnderTest } from '../../../utils';
|
||||
import type { BrowserContext } from '../../browserContext';
|
||||
import { installAppIcon } from '../../chromium/crApp';
|
||||
import { installAppIcon, syncLocalStorageWithSettings } from '../../chromium/crApp';
|
||||
import { serverSideCallMetadata } from '../../instrumentation';
|
||||
import { createPlaywright } from '../../playwright';
|
||||
import { ProgressController } from '../../progress';
|
||||
@ -81,7 +81,7 @@ export async function showTraceViewer(traceUrls: string[], browserName: string,
|
||||
|
||||
if (traceViewerBrowser === 'chromium')
|
||||
await installAppIcon(page);
|
||||
|
||||
await syncLocalStorageWithSettings(page, 'traceviewer');
|
||||
|
||||
const params = traceUrls.map(t => `trace=${t}`);
|
||||
if (isUnderTest()) {
|
||||
|
@ -24,6 +24,7 @@ import * as React from 'react';
|
||||
import { CallLogView } from './callLog';
|
||||
import './recorder.css';
|
||||
import { asLocator } from '@isomorphic/locatorGenerators';
|
||||
import { toggleTheme } from '@web/theme';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -129,6 +130,7 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||
<ToolbarButton icon='clear-all' title='Clear' disabled={!source || !source.text} onClick={() => {
|
||||
window.dispatch({ event: 'clear' });
|
||||
}}></ToolbarButton>
|
||||
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
|
||||
</Toolbar>
|
||||
<SplitView sidebarSize={200} sidebarHidden={mode === 'recording'}>
|
||||
<SourceView text={source.text} language={source.language} highlight={source.highlight} revealLine={source.revealLine}></SourceView>
|
||||
|
@ -85,6 +85,11 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workbench .header .toolbar-button {
|
||||
margin: 12px;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.workbench tab-content {
|
||||
padding: 25px;
|
||||
contain: size;
|
||||
|
@ -17,6 +17,7 @@
|
||||
import type { ActionTraceEvent } from '@trace/trace';
|
||||
import { SplitView } from '@web/components/splitView';
|
||||
import { msToString } from '@web/uiUtils';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import * as React from 'react';
|
||||
import type { ContextEntry } from '../entries';
|
||||
import { ActionList } from './actionList';
|
||||
@ -30,6 +31,7 @@ import { SourceTab } from './sourceTab';
|
||||
import { TabbedPane } from './tabbedPane';
|
||||
import { Timeline } from './timeline';
|
||||
import './workbench.css';
|
||||
import { toggleTheme } from '@web/theme';
|
||||
|
||||
export const Workbench: React.FunctionComponent<{
|
||||
}> = () => {
|
||||
@ -156,6 +158,7 @@ export const Workbench: React.FunctionComponent<{
|
||||
<div className='product'>Playwright</div>
|
||||
{model.title && <div className='title'>{model.title}</div>}
|
||||
<div className='spacer'></div>
|
||||
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
|
||||
</div>
|
||||
<div style={{ paddingLeft: '20px', flex: 'none', borderBottom: '1px solid var(--vscode-panel-border)' }}>
|
||||
<Timeline
|
||||
|
@ -15,6 +15,10 @@
|
||||
*/
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
body {
|
||||
--red: #F44336;
|
||||
--green: #367c39;
|
||||
--purple: #9C27B0;
|
||||
@ -29,14 +33,12 @@
|
||||
--box-shadow: rgba(0, 0, 0, 0.133) 0px 1.6px 3.6px 0px, rgba(0, 0, 0, 0.11) 0px 0.3px 0.9px 0px;
|
||||
}
|
||||
|
||||
@media(prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--green: #28d12f;
|
||||
--yellow: #ff9207;
|
||||
--purple: #dc12ff;
|
||||
--blue: #4dafff;
|
||||
--orange: #ff9800;
|
||||
}
|
||||
body.dark-mode {
|
||||
--green: #28d12f;
|
||||
--yellow: #ff9207;
|
||||
--purple: #dc12ff;
|
||||
--blue: #4dafff;
|
||||
--orange: #ff9800;
|
||||
}
|
||||
|
||||
html, body {
|
||||
|
@ -25,4 +25,23 @@ export function applyTheme() {
|
||||
document!.defaultView!.addEventListener('blur', event => {
|
||||
document.body.classList.add('inactive');
|
||||
}, false);
|
||||
|
||||
const currentTheme = localStorage.getItem('theme');
|
||||
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
if (currentTheme === 'dark-mode' || prefersDarkScheme.matches)
|
||||
document.body.classList.add('dark-mode');
|
||||
}
|
||||
|
||||
export function toggleTheme() {
|
||||
const oldTheme = localStorage.getItem('theme');
|
||||
let newTheme: string;
|
||||
if (oldTheme === 'dark-mode')
|
||||
newTheme = 'light-mode';
|
||||
else
|
||||
newTheme = 'dark-mode';
|
||||
|
||||
if (oldTheme)
|
||||
document.body.classList.remove(oldTheme);
|
||||
document.body.classList.add(newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
}
|
||||
|
1080
packages/web/src/third_party/vscode/colors.css
vendored
1080
packages/web/src/third_party/vscode/colors.css
vendored
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user