Skip to content
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

Dark theme support #159

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
18 changes: 17 additions & 1 deletion applications/klighd-cli/src/services/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@ export class LSPConnection implements Connection {
async sendInitialize(persistedData: Record<string, any>): Promise<void> {
if (!this.connection) return;

// notify the server about the preferred colors, depending on if the OS prefers light (default) or dark theme.
let foreground = "#000000"
let background = "#ffffff"
let highlight = "#000000"
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// dark mode
foreground = "#cccccc"
background = "#1f1f1f"
highlight = "#cccccc"
}

const method = lsp.InitializeRequest.type.method;
// The standalone view does not really has any LSP capabilities
const initParams: lsp.InitializeParams = {
Expand All @@ -171,7 +182,12 @@ export class LSPConnection implements Connection {
capabilities: {},
initializationOptions: {
clientDiagramOptions: persistedData,
}
clientColorPreferences: {
foreground,
background,
highlight,
}
},
};

console.time("lsp-init");
Expand Down
18 changes: 18 additions & 0 deletions applications/klighd-vscode/src/klighd-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* SPDX-License-Identifier: EPL-2.0
*/
import {
ChangeColorThemeAction,
KlighdFitToScreenAction,
KlighdRequestExportSvgAction,
RefreshDiagramAction,
Expand Down Expand Up @@ -201,6 +202,23 @@ export class KLighDExtension extends SprottyLspVscodeExtension {
* to overwrite commands and would throw an error._
*/
protected override registerCommands(): void {
vscode.window.onDidChangeActiveColorTheme(() => {
// Hook into VS Code's theme change and notify the webview to check the current colors and send them to the server.
// Look for any active KLighD webview, regardless of if it is currently selected.
let webview = this.singleton
if (!webview) {
for (const possibleWebview of this.webviewMap.values()) {
if (possibleWebview.diagramPanel.active) {
webview = possibleWebview;
}
}
}

if (webview) {
webview.dispatch(ChangeColorThemeAction.create());
}
})

this.context.subscriptions.push(
commands.registerCommand(command.diagramOpen, async (...commandArgs: any[]) => {
const identifier = await this.createDiagramIdentifier(commandArgs);
Expand Down
47 changes: 47 additions & 0 deletions packages/klighd-core/src/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,53 @@ export namespace CheckedImagesAction {
}
}

/**
* Sent internally to notify KLighD that the color theme has changed. Will trigger a subsequent
* ClientColorPreferencesAction to be triggered and sent.
*/
export interface ChangeColorThemeAction extends Action {
kind: typeof ChangeColorThemeAction.KIND
}

export namespace ChangeColorThemeAction {
export const KIND = 'changeColorTheme'

export function create(): ChangeColorThemeAction {
return {
kind: KIND,
}
}
}

/**
* Action to notify the server about current color preferences.
*/
export interface ClientColorPreferencesAction extends Action {
kind: typeof ClientColorPreferencesAction.KIND

clientColorPreferences: ColorPreferences
}

export namespace ClientColorPreferencesAction {
export const KIND = 'changeClientColorPreferences'

export function create(clientColorPreferences: ColorPreferences): ClientColorPreferencesAction {
return {
kind: KIND,
clientColorPreferences,
}
}
}

/**
* The color preferences data class, indicating diagram colors to be used by syntheses.
*/
export interface ColorPreferences {
foreground: string,
background: string,
highlight: string,
}

/**
* Sent from the client to the diagram server to perform a klighd action on the model.
* Causes the server to update the diagram accordingly to the action.
Expand Down
32 changes: 32 additions & 0 deletions packages/klighd-core/src/diagram-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@ import {
ActionMessage,
findElement,
generateRequestId,
RequestModelAction,
RequestPopupModelAction,
SelectAction,
SetPopupModelAction,
UpdateModelAction,
} from "sprotty-protocol";
import {
ChangeColorThemeAction,
CheckedImagesAction,
CheckImagesAction,
ClientColorPreferencesAction,
KlighdExportSvgAction,
KlighdFitToScreenAction,
Pair,
Expand Down Expand Up @@ -150,6 +153,10 @@ export class KlighdDiagramServer extends DiagramServerProxy {
// In contract to the name, this should return true, if the actions should be
// sent to the server. Don't know what the Sprotty folks where thinking when they named it...
switch (action.kind) {
case ClientColorPreferencesAction.KIND:
return true;
case ChangeColorThemeAction.KIND:
return false;
case PerformActionAction.KIND:
return true;
case RefreshDiagramAction.KIND:
Expand Down Expand Up @@ -186,6 +193,8 @@ export class KlighdDiagramServer extends DiagramServerProxy {
registry.register(BringToFrontAction.KIND, this);
registry.register(CheckImagesAction.KIND, this);
registry.register(CheckedImagesAction.KIND, this);
registry.register(ClientColorPreferencesAction.KIND, this);
registry.register(ChangeColorThemeAction.KIND, this);
registry.register(DeleteLayerConstraintAction.KIND, this);
registry.register(DeletePositionConstraintAction.KIND, this);
registry.register(DeleteStaticConstraintAction.KIND, this);
Expand All @@ -209,6 +218,18 @@ export class KlighdDiagramServer extends DiagramServerProxy {
}

handle(action: Action): void | ICommand | Action {
if (action.kind === RequestModelAction.KIND && getComputedStyle !== undefined) {
// On any request model action, also send the current colors with the request, so the initial
// syntheses can use the theming of VS Code. Values will be undefined outside of VS Code and should
// be ignored.
(action as RequestModelAction).options = {
...((action as RequestModelAction).options),
clientColorPreferenceForeground: getComputedStyle(document.documentElement).getPropertyValue('--vscode-editor-foreground'),
clientColorPreferenceBackground: getComputedStyle(document.documentElement).getPropertyValue('--vscode-editor-background'),
clientColorPreferenceHighlight: getComputedStyle(document.documentElement).getPropertyValue('--vscode-focusBorder')
}
super.handle(action)
}

if (action.kind === BringToFrontAction.KIND || action.kind === SwitchEditModeAction.KIND) {
// Actions that should be ignored and not further handled by this diagram server
Expand All @@ -217,6 +238,8 @@ export class KlighdDiagramServer extends DiagramServerProxy {

if (action.kind === CheckImagesAction.KIND) {
this.handleCheckImages(action as CheckImagesAction);
} else if (action.kind === ChangeColorThemeAction.KIND) {
this.handleChangeColorTheme();
} else if (action.kind === StoreImagesAction.KIND) {
this.handleStoreImages(action as StoreImagesAction);
} else if (action.kind === RequestPopupModelAction.KIND) {
Expand Down Expand Up @@ -248,6 +271,15 @@ export class KlighdDiagramServer extends DiagramServerProxy {
this.actionDispatcher.dispatch(CheckedImagesAction.create(notCached));
}

handleChangeColorTheme(): void {
if (getComputedStyle === undefined) return
this.actionDispatcher.dispatch(ClientColorPreferencesAction.create({
foreground: getComputedStyle(document.documentElement).getPropertyValue('--vscode-editor-foreground'),
background: getComputedStyle(document.documentElement).getPropertyValue('--vscode-editor-background'),
highlight: getComputedStyle(document.documentElement).getPropertyValue('--vscode-focusBorder'),
}))
}

handleStoreImages(action: StoreImagesAction): void {
// Put the new images in session storage.
for (const imagePair of (action as StoreImagesAction).images) {
Expand Down
Loading