-
Notifications
You must be signed in to change notification settings - Fork 21
Feature/vscode-theming #833
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
9c4cdd9
c0575fe
4ca0046
c7c082f
a726ee1
57dc5c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { isVSCodeEnv } from '#common/utils/env.utils.ts'; | ||
| import { onMessage } from '#common/utils/vscode-bridge.utils.ts'; | ||
| import { | ||
| HOST_MESSAGE_TYPE, | ||
| type ThemePayload, | ||
| } from '@lemoncode/quickmock-bridge-protocol'; | ||
| import { useEffect } from 'react'; | ||
|
|
||
| const CSS_VAR_MAP: Record<keyof ThemePayload, readonly string[]> = { | ||
| background: ['--primary-100', '--primary-500', '--primary-200'], | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| backgroundSecondary: ['--pure-white'], | ||
| foreground: ['--primary-700'], | ||
| }; | ||
|
|
||
| const applyTheme = (theme: ThemePayload): void => { | ||
| const root = document.documentElement; | ||
| for (const [key, cssVars] of Object.entries(CSS_VAR_MAP)) { | ||
| const value = theme[key as keyof ThemePayload]; | ||
| if (!value) continue; | ||
| for (const cssVar of cssVars) { | ||
| root.style.setProperty(cssVar, value); | ||
| } | ||
| } | ||
| if (theme.background) document.body.style.backgroundColor = theme.background; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (theme.foreground) document.body.style.color = theme.foreground; | ||
| }; | ||
|
|
||
| export const useVSCodeTheme = (): void => { | ||
| useEffect(() => { | ||
| if (!isVSCodeEnv()) return; | ||
| return onMessage(HOST_MESSAGE_TYPE.THEME, applyTheme); | ||
| }, []); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,7 @@ | ||||||
| import { MainLayout } from '#layout/main.layout'; | ||||||
| import classes from './main.module.css'; | ||||||
|
|
||||||
| import { isHeadlessEnv } from '#common/utils/env.utils.ts'; | ||||||
| import { isHeadlessEnv, isVSCodeEnv } from '#common/utils/env.utils.ts'; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| import { useInteractionModeContext } from '#core/providers'; | ||||||
| import { | ||||||
| BasicShapesGalleryPod, | ||||||
|
|
@@ -81,9 +81,11 @@ export const MainScene = () => { | |||||
| <PropertiesPod /> | ||||||
| </div> | ||||||
| )} | ||||||
| <div className={classes.footer}> | ||||||
| <FooterPod /> | ||||||
| </div> | ||||||
| {!isVSCodeEnv() && ( | ||||||
| <div className={classes.footer}> | ||||||
| <FooterPod /> | ||||||
| </div> | ||||||
| )} | ||||||
| </MainLayout> | ||||||
| ); | ||||||
| }; | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import { setupBridge } from './bridge'; | ||
| import { setupThemeSync } from './theme'; | ||
|
|
||
| const appUrl = document.body.dataset.appUrl; | ||
| if (!appUrl) { | ||
|
|
@@ -18,3 +19,4 @@ iframe.title = 'QuickMock Application'; | |
| document.body.appendChild(iframe); | ||
|
|
||
| setupBridge(iframe, appOrigin); | ||
| setupThemeSync(iframe, appOrigin); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. El retorno de const _cleanupTheme = setupThemeSync(iframe, appOrigin); |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { | ||
| APP_MESSAGE_TYPE, | ||
| HOST_MESSAGE_TYPE, | ||
| type ThemePayload, | ||
| } from '@lemoncode/quickmock-bridge-protocol'; | ||
|
|
||
| const readVar = (style: CSSStyleDeclaration, name: string): string => | ||
| style.getPropertyValue(name).trim(); | ||
|
|
||
| export const extractTheme = (): ThemePayload => { | ||
| const style = getComputedStyle(document.documentElement); | ||
| return { | ||
| background: readVar(style, '--vscode-editor-background'), | ||
| backgroundSecondary: readVar(style, '--vscode-sideBar-background'), | ||
| foreground: readVar(style, '--vscode-editor-foreground'), | ||
| }; | ||
| }; | ||
|
|
||
| const IFRAME_READY_TYPES: ReadonlySet<string> = new Set([ | ||
| APP_MESSAGE_TYPE.WEBVIEW_READY, | ||
| APP_MESSAGE_TYPE.READY, | ||
| ]); | ||
|
|
||
| export const setupThemeSync = ( | ||
| iframe: HTMLIFrameElement, | ||
| appOrigin: string | ||
| ): (() => void) => { | ||
| const sendTheme = (): void => { | ||
| iframe.contentWindow?.postMessage( | ||
| { type: HOST_MESSAGE_TYPE.THEME, payload: extractTheme() }, | ||
| appOrigin | ||
| ); | ||
| }; | ||
|
|
||
| const onIframeReady = (event: MessageEvent): void => { | ||
| if (event.origin !== appOrigin) return; | ||
| const type = (event.data as { type?: string } | undefined)?.type; | ||
| if (type && IFRAME_READY_TYPES.has(type)) sendTheme(); | ||
| }; | ||
| window.addEventListener('message', onIframeReady); | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
let rafId = 0;
const sendThemeDebounced = () => {
cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(sendTheme);
};
const observer = new MutationObserver(sendThemeDebounced); |
||
| const observer = new MutationObserver(sendTheme); | ||
| observer.observe(document.body, { | ||
| attributes: true, | ||
| attributeFilter: ['class', 'style'], | ||
| }); | ||
|
|
||
| return () => { | ||
| window.removeEventListener('message', onIframeReady); | ||
| observer.disconnect(); | ||
| }; | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.