uiwjs / react-codemirror

CodeMirror 6 component for React. @codemirror https://uiwjs.github.io/react-codemirror/
https://uiwjs.github.io/react-codemirror/
MIT License
1.69k stars 135 forks source link

Can you apply a theme using CodeMirrorMerge? #515

Closed kimfucious closed 1 year ago

kimfucious commented 1 year ago

I'm trying to compare two markdown files, using CodeMirrorMerge.

Question: Is there a way to apply a theme in the below?

import { EditorState } from "@codemirror/state";
import { EditorView } from "codemirror";
import { Scene } from "../../../types";
import CodeMirrorMerge from "react-codemirror-merge";

const Original = CodeMirrorMerge.Original;
const Modified = CodeMirrorMerge.Modified;

interface Props {
    changedText: string;
    setChangedText: (s: string) => void;
    originalScene: Scene | undefined;
    original: string | undefined;
    modified: string | undefined;
}
// https://uiwjs.github.io/react-codemirror/#/merge/onchange
export default function MergeEditor({
    original,
    modified,
    changedText,
    setChangedText,
}: Props) {
    function handleChange(str: string) {
        console.log("Changed Text", str);
        setChangedText(str);
    }

    return (
        <div className="container d-flex flex-column align-items-center w-100">
            <CodeMirrorMerge
                className="w-100"
                highlightChanges
                orientation="a-b"
                revertControls="b-to-a"
            >
                <Original
                    value={original}
                    onChange={(str) => handleChange(str)}
                />
                <Modified
                    value={modified}
                    extensions={[
                        EditorView.editable.of(false),
                        EditorState.readOnly.of(true),
                    ]}
                />
            </CodeMirrorMerge>

            <div className="d-flex justify-content-between w-100">
                <small>Current Scene</small>
                <small>Unsaved Changes</small>
            </div>
            {changedText !== modified && (
                <div className="d-flex justify-content-center mt-3 w-100">
                    You can merge the unsaved changes by clicking the little
                    arrow
                </div>
            )}
        </div>
    );
}
jaywcjlove commented 1 year ago

@kimfucious https://codesandbox.io/embed/react-codemirror-merge-https-github-com-uiwjs-react-codemirror-issues-515-0888v2?fontsize=14&hidenavigation=1&theme=dark

import React from "react";
import CodeMirrorMerge from "react-codemirror-merge";
import { EditorView } from "@codemirror/view";
import { draculaInit } from "@uiw/codemirror-theme-dracula";
import { javascript } from "@codemirror/lang-javascript";

export const defaultThemeOption = EditorView.theme(
  {
    "&": {
      backgroundColor: "#333 !important"
    }
  },
  {
    dark: true
  }
);

const Original = CodeMirrorMerge.Original;
const Modified = CodeMirrorMerge.Modified;
let doc = `function examle() {

}`;

export default function App() {
  return (
    <div>
      <CodeMirrorMerge orientation="a-b">
        <Original
          extensions={[
            defaultLightThemeOption,
            draculaInit({
              theme: "dark"
            }),
            javascript({ jsx: true }),
            EditorView.lineWrapping
          ]}
          value={doc}
        />
        <Modified
          extensions={[EditorView.lineWrapping]}
          value={doc.replace(/e/g, "T")}
        />
      </CodeMirrorMerge>
    </div>
  );
}
kimfucious commented 1 year ago

Thanks for this @jaywcjlove.

I actually prefer your default "dark" mode. Not sure how to use that. If I can't I'll default to one of the other dark themes.

BTW, I noticed this link seems to lead nowhere: https://uiwjs.github.io/react-codemirror/#/theme/data/vscode/dark

kimfucious commented 1 year ago

Thanks for your help on this, @jaywcjlove. It's appreciated.

I worked through things and got to the below.

I couldn't find the default "dark" theme settings for defaultThemeOptions so I went with materialDark.

import { useContext, useMemo } from "react";
import { EditorState } from "@codemirror/state";
import { EditorView } from "codemirror";
import { markdown } from "@codemirror/lang-markdown";
import { materialDarkInit } from "@uiw/codemirror-theme-material";
import { Scene } from "../../../types";
import { solarizedLightInit } from "@uiw/codemirror-theme-solarized";
import { ThemeContext } from "../../../contexts/ThemeProvider";
import { vim } from "@replit/codemirror-vim";
import CodeMirrorMerge from "react-codemirror-merge";

const Original = CodeMirrorMerge.Original;
const Modified = CodeMirrorMerge.Modified;

interface Props {
    changedText: string;
    setChangedText: (s: string) => void;
    originalScene: Scene | undefined;
    original: string | undefined;
    modified: string | undefined;
}
// https://uiwjs.github.io/react-codemirror/#/merge/onchange
export default function MergeEditor({
    original,
    modified,
    changedText,
    setChangedText,
}: Props) {
    const { isDark } = useContext(ThemeContext);
    function handleChange(str: string) {
        setChangedText(str);
    }
    const { defaultThemeOptions, theme } = useMemo(() => {
        const defaultThemeOptions = EditorView.theme(
            {
                "&": {
                    background: isDark
                        ? "#2e3235 !important"
                        : "#2e3235 !important",
                    backgroundColor: isDark
                        ? "#2e3235 !important"
                        : "#fdf6e3 !important",
                    foreground: isDark
                        ? "#bdbdbd !important"
                        : "#657b83 !important",
                    caret: isDark ? "#a0a4ae !important" : "#586e75 !important",
                    selection: isDark
                        ? "#d7d4f0 !important"
                        : "#dfd9c8 !important",
                    selectionMatch: isDark
                        ? "#d7d4f0 !important"
                        : "#dfd9c8 !important",
                    gutterBackground: isDark
                        ? "#2e3235 !important"
                        : "#00000010 !important",
                    gutterActiveBackground: isDark
                        ? "#4f5b66 !important"
                        : "#00000010 !important",
                    gutterActiveForeground: isDark
                        ? "#000 !important"
                        : "#657b83 !important",
                    gutterForeground: isDark
                        ? // ? "#999 !important"
                          "#ff69b4 !important"
                        : "#657b83 !important",
                    lineHighlight: isDark
                        ? "#545b61 !important"
                        : "#dfd9c8 !important",
                },
            },
            {
                dark: isDark,
            }
        );
        const theme = isDark
            ? materialDarkInit({ theme: "dark" })
            : solarizedLightInit({ theme: "light" });
        return { defaultThemeOptions, theme };
    }, [isDark]);

    return (
        <div className="container d-flex flex-column align-items-center w-100">
            <CodeMirrorMerge
                className="w-100"
                gutter={false}
                highlightChanges
                orientation="a-b"
                revertControls="b-to-a"
            >
                <Original
                    extensions={[
                        vim({ status: false }),
                        defaultThemeOptions,
                        theme,
                        markdown(),
                        EditorView.lineWrapping,
                        EditorView.contentAttributes.of({ spellcheck: "true" }),
                    ]}
                    value={original}
                    onChange={(str) => handleChange(str)}
                />
                <Modified
                    value={modified}
                    extensions={[
                        vim({ status: false }),
                        defaultThemeOptions,
                        theme,
                        markdown(),
                        EditorView.lineWrapping,
                        EditorView.editable.of(false),
                        EditorState.readOnly.of(true),
                    ]}
                />
            </CodeMirrorMerge>

            <div className="d-flex justify-content-between w-100">
                <small>Current Scene</small>
                <small>Unsaved Changes (read only)</small>
            </div>
            {changedText !== modified && (
                <div className="d-flex justify-content-center mt-3 w-100">
                    You can merge the unsaved changes by clicking the little
                    arrow
                </div>
            )}
        </div>
    );
}
kimfucious commented 1 year ago

Hi @jaywcjlove,

Before I close this, where can I find these values for the default "dark" theme?

Oddly, I like it better than all of the other packaged dark themes 😄

var defaultSettingsSolarizedLight = {
  background: '#fdf6e3',
  foreground: '#657b83',
  caret: '#586e75',
  selection: '#dfd9c8',
  selectionMatch: '#dfd9c8',
  gutterBackground: '#00000010',
  gutterForeground: '#657b83',
  lineHighlight: '#dfd9c8'
};
jaywcjlove commented 1 year ago

@kimfucious

import { solarizedLight, solarizedLightInit, solarizedDark, solarizedDarkInit } from '@uiw/codemirror-theme-solarized';

<CodeMirror theme={solarizedLight} />
<CodeMirror
  theme={solarizedLightInit({
    settings: {
      caret: '#c6c6c6',
      fontFamily: 'monospace',
    }
  })}
/>

@kimfucious https://github.com/uiwjs/react-codemirror/blob/f01d52bd5aa8024f4df221c9050916c15b3c31ec/themes/solarized/src/index.ts#L133-L236

jaywcjlove commented 1 year ago

https://github.com/uiwjs/react-codemirror/blob/f01d52bd5aa8024f4df221c9050916c15b3c31ec/themes/solarized/src/index.ts#L4-L13

kimfucious commented 1 year ago

Hi @jaywcjlove,

Sorry for the confusion.

I'm looking for the values for the DEFAULT dark mode theme, not solarizedLight.

This is the theme that you can get with CodeMirror when you use theme={"dark"}.

Thanks.

jaywcjlove commented 1 year ago

Oh, the theme is light by default, you don't need to get it, you set it to dark, you should log it.

kimfucious commented 1 year ago

Hi @jaywcjlove,

Sorry again for the confusion.

What I want is for the dark values in defaultThemeOptions to match the default "dark" theme's settings.

const { defaultThemeOptions, theme } = useMemo(() => {
        const defaultThemeOptions = EditorView.theme(
            {
                "&": {
                    background: isDark
                        ? "#2e3235 !important" // value from "dark"
                        : "#2e3235 !important",
                    backgroundColor: isDark
                        ? "#2e3235 !important" // value from "dark"
                        : "#fdf6e3 !important",
                    foreground: isDark
                        ? "#bdbdbd !important" // value from "dark"
                        : "#657b83 !important",
                    //...etc
                },
            },
            {
                dark: isDark,
            }
        );

Then I want

const theme = isDark
    ? materialDarkInit({ theme: "dark" }) // I want this "dark", not materialDark.
    : solarizedLightInit({ theme: "light" });

Thanks.

jaywcjlove commented 1 year ago

@kimfucious https://codesandbox.io/embed/react-codemirror-merge-https-github-com-uiwjs-react-codemirror-issues-515-forked-lm3kgo?fontsize=14&hidenavigation=1&theme=dark

import React, { useState } from "react";
import CodeMirrorMerge from "react-codemirror-merge";
import { EditorView } from "@codemirror/view";
import { javascript } from "@codemirror/lang-javascript";

const Original = CodeMirrorMerge.Original;
const Modified = CodeMirrorMerge.Modified;
let doc = `function examle() {

}`;

export default function App() {
  const [theme, setTheme] = useState("light");
  return (
    <div>
      <CodeMirrorMerge theme={theme} orientation="a-b">
        <Original
          extensions={[javascript({ jsx: true }), EditorView.lineWrapping]}
          value={doc}
        />
        <Modified
          extensions={[EditorView.lineWrapping]}
          value={doc.replace(/e/g, "T")}
        />
      </CodeMirrorMerge>
      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
        Change Theme {theme}
      </button>
    </div>
  );
}
import React, { useState } from "react";
import CodeMirrorMerge from "react-codemirror-merge";
import { EditorView } from "@codemirror/view";
import { javascript } from "@codemirror/lang-javascript";
import { githubLight, githubDark } from "@uiw/codemirror-theme-github";

const Original = CodeMirrorMerge.Original;
const Modified = CodeMirrorMerge.Modified;
let doc = `function examle() {

}`;

function Example() {
  const [theme, setTheme] = useState("light");
  return (
    <div>
      <CodeMirrorMerge
        theme={theme === "light" ? githubLight : githubDark}
        orientation="a-b"
      >
        <Original
          extensions={[javascript({ jsx: true }), EditorView.lineWrapping]}
          value={doc}
        />
        <Modified
          extensions={[EditorView.lineWrapping, javascript({ jsx: true })]}
          value={doc.replace(/e/g, "T")}
        />
      </CodeMirrorMerge>
      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
        Change Theme {theme}
      </button>
    </div>
  );
}
kimfucious commented 1 year ago

Thanks @jaywcjlove.

Let me try to explain what I want once more.

I want the values for the below from the default dark theme:

settings: {
    background: '#ffffff',
    foreground: '#4D4D4C',
    caret: '#AEAFAD',
    selection: '#D6D6D6',
    selectionMatch: '#D6D6D6',
    gutterBackground: '#FFFFFF',
    gutterForeground: '#4D4D4C',
    gutterBorder: '#ddd',
    gutterActiveForeground: '',
    lineHighlight: '#EFEFEF',
  },

When I say "default dark theme", I mean the one that you get when using CodeMirror, like the below when you use "dark" without a packaged theme, but I want to use it with CodeMirrorMerge.

<CodeMirror
    autoFocus
    basicSetup={{
        foldGutter: false,
        highlightActiveLine: false,
    }}
    value={app.draftActiveSceneText}
    theme={isDark ? "dark" : "light"} // <= I want "dark" here.
    height={"500px"}
    width={`calc(36em + 74px)`}
    extensions={[
        vim({ status: true }),
        markdown(),
        EditorView.lineWrapping,
        EditorView.contentAttributes.of({ spellcheck: "true" }),
    ]}
    onChange={onChange}
/>
jaywcjlove commented 1 year ago

@kimfucious I tried to help you out, but not sure if it's what you want. https://codesandbox.io/embed/react-codemirror-merge-https-github-com-uiwjs-react-codemirror-issues-515-forked-dqrcgt?fontsize=14&hidenavigation=1&theme=dark

import React, { useState } from "react";
import CodeMirrorMerge from "react-codemirror-merge";
import { EditorView } from "@codemirror/view";
import { javascript } from "@codemirror/lang-javascript";
import {
  githubLight,
  githubDark,
  githubDarkInit
} from "@uiw/codemirror-theme-github";
import { createTheme } from "@uiw/codemirror-themes";
import { tags as t } from "@lezer/highlight";

const myDarkTheme = createTheme({
  theme: "dark",
  settings: {
    background: "#fdf6e3",
    foreground: "#657b83",
    caret: "#586e75",
    selection: "#dfd9c8",
    selectionMatch: "#dfd9c8",
    gutterBackground: "#00000010",
    gutterForeground: "#657b83",
    lineHighlight: "#dfd9c8"
  },
  styles: [
    { tag: t.comment, color: "#787b8099" },
    { tag: t.variableName, color: "#0080ff" },
    { tag: [t.string, t.special(t.brace)], color: "#5c6166" },
    { tag: t.number, color: "#5c6166" },
    { tag: t.bool, color: "#5c6166" },
    { tag: t.null, color: "#5c6166" },
    { tag: t.keyword, color: "#5c6166" },
    { tag: t.operator, color: "#5c6166" },
    { tag: t.className, color: "#5c6166" },
    { tag: t.definition(t.typeName), color: "#5c6166" },
    { tag: t.typeName, color: "#5c6166" },
    { tag: t.angleBracket, color: "#5c6166" },
    { tag: t.tagName, color: "#5c6166" },
    { tag: t.attributeName, color: "#5c6166" }
  ]
});

const Original = CodeMirrorMerge.Original;
const Modified = CodeMirrorMerge.Modified;
let doc = `function examle() {

}`;

export default function Example() {
  const [theme, setTheme] = useState("dark");
  return (
    <div>
      <CodeMirrorMerge
        theme={theme === "dark" ? myDarkTheme : githubLight}
        orientation="a-b"
      >
        <Original
          extensions={[javascript({ jsx: true }), EditorView.lineWrapping]}
          value={doc}
        />
        <Modified
          extensions={[EditorView.lineWrapping, javascript({ jsx: true })]}
          value={doc.replace(/e/g, "T")}
        />
      </CodeMirrorMerge>
      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
        Change Theme {theme}
      </button>
    </div>
  );
}
kimfucious commented 1 year ago

Hi @jaywcjlove.

I'm sorry for the continued confusion. I am really not communicating clearly, it seems.

I want the theme that looks like the below to use in CodeMergeView so that it will match what I'm using in CodeMirror.

It is NOT any of the themes that are listed here.

It is the theme that is used in CodeMirror with the below. Note that I am not importing any theme. I am only using "dark" (a string) as the value.

<CodeMirror
    autoFocus
    basicSetup={{
        foldGutter: false,
        highlightActiveLine: false,
    }}
    value={app.draftActiveSceneText}
    // themes: https://uiwjs.github.io/react-codemirror/#/theme/home
    theme="dark" // <= No imported theme.  Just "dark"
    height={"500px"}
    width={`calc(36em + 74px)`}
    extensions={[
        vim({ status: true }),
        markdown(),
        EditorView.lineWrapping,
        EditorView.contentAttributes.of({ spellcheck: "true" }),
    ]}
    onChange={onChange}
/>

I don't need a Code Sandbox example. I just need the values that match what looks like below.

I cannot find these values buried anywhere in the code, so I'm asking.

Thanks for your patience.

image

jaywcjlove commented 1 year ago

@kimfucious If you don't want to use import { createTheme } from "@uiw/codemirror-themes"; to define your theme, you can only use the official method definition.

https://github.com/uiwjs/react-codemirror/blob/159444d825293732d55389ce54367e6d7f7c0cdf/themes/theme/src/index.tsx#L46-L118

createTheme is the method that simplifies official theme definitions

jaywcjlove commented 1 year ago

@kimfucious For custom styles, you must encapsulate an Extension yourself.

kimfucious commented 1 year ago

Hi @jaywcjlove ,

If I knew the Settings and Styles for the "default dark theme", I'd be happy to create a custom theme from that, but I don't know what they are, which is why I'm asking.

jaywcjlove commented 1 year ago

https://github.com/uiwjs/react-codemirror/blob/159444d825293732d55389ce54367e6d7f7c0cdf/core/src/getDefaultExtensions.ts#L5

This is the built-in official theme, it does not support customization.

https://github.com/uiwjs/react-codemirror/blob/159444d825293732d55389ce54367e6d7f7c0cdf/core/src/getDefaultExtensions.ts#L44-L56

@kimfucious If you want to know what styles it defines, you can check here. Currently, the built-in dark theme can only be changed by overriding.

kimfucious commented 1 year ago

@jaywcjlove, Yay!

I did not know that oneDark was the default dark theme. That helped!

To implement, I used the following.

const { defaultThemeOptions, theme } = useMemo(() => {
    const defaultThemeOptions = EditorView.theme(
        {
            "&": {
                backgroundColor: isDark
                    ? "#282c34 !important"
                    : "#fdf6e3 !important",
            },
        },
        {
            dark: isDark,
        }
    );
    const theme = isDark ? [oneDark] : [solarizedLight];
    return { defaultThemeOptions, theme };
}, [isDark]);
<CodeMirrorMerge
    className="w-100"
    highlightChanges
    orientation="a-b"
    revertControls="b-to-a"
>
    <Original
        extensions={[
            vim({ status: false }),
            defaultThemeOptions,
            theme,
            markdown(),
            EditorView.lineWrapping,
            EditorView.contentAttributes.of({ spellcheck: "true" }),
        ]}
        value={original}
        onChange={(str) => handleChange(str)}
    />
    <Modified
        value={modified}
        extensions={[
            vim({ status: false }),
            defaultThemeOptions,
            theme,
            markdown(),
            EditorView.lineWrapping,
            EditorView.editable.of(false),
            EditorState.readOnly.of(true),
        ]}
    />
</CodeMirrorMerge>

Thank you very much for your patience on this.