carbon-app / carbon

:black_heart: Create and share beautiful images of your source code
https://carbon.now.sh
MIT License
34.43k stars 1.91k forks source link

Support custom colour scheme + save/load schemes. #181

Closed minacle closed 5 years ago

minacle commented 6 years ago

I'd like to use my own colour scheme on my codes.

Also, it is pain to customise every time, so behaviour to save and load schemes is needed.

mfix22 commented 6 years ago

@minacle how do you propose we format a theme? How do you currently defined your "theme". Is it just an array of colors? Or do you have a more precise idea in mind?

We could definitely support an array of colors (much like Slack does with their sidebar themes) and just fill in the code mirror CSS (example here: https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.34.0/theme/blackboard.css)

minacle commented 6 years ago

@mfix22 Absolutely, it is an array of colours, but I want more useful features.

  1. Customise colours based on one of predefined scheme.
  2. Save the scheme to cookie or something.
  3. Export the scheme to save it to gist or file.
  4. Exported schemes can be imported.
noudadrichem commented 6 years ago

Hi @mfix22

I wanted to make an issue out of this myself. But I see there already is one. I have spit through the code how you guys rock these themes, and I saw you guys are using highlightjs. As a solution of approach I was thinking of something like a directory with themes where people could upload their own hljs themes. At @yummygum we made a theme called city lights and I use carbon a lot for nice previews of code. We also made a HLJS theme.

So, this could be a solution. If you guys can easily add themes yourself I request to add the City Lights theme 😊 .

I'll be happy to help!

Sincerely

mfix22 commented 6 years ago

@noudadrichem I absolutely love City Lights! It's my current theme + syntax in Atom!

  1. Please go ahead and submit a PR to add the necessary changes to add City Lights to Carbon.

  2. We are definitely going to add a way to add custom themes (although if you wanted to make a PR for that, go right ahead 😄)

GavinRay97 commented 6 years ago

@mfix22 I was actually poking around at loading custom themes into Carbon when I found this issue.

What do you think about the following?

In the short term, the easiest way would be to add support for pre-existing CodeMirror themes. I see two straightforward ways of doing this:

In the long term, maybe a dat.gui toolbox hooked up to each of the CodeMirror syntax CSS classes (cm-keyword, cm-def, cm-operator, etc). This would give you fine-grained and incredibly easy control over syntax theming. Eventually it could be neat to be able to save/share custom themes.

There's a React dat.gui available that seems like it would be fairly straightforward (albeit tedious) to implement

I would be happy to play around with this if you'd think it's something people would like. I was going to clone the repo and experiment later this evening.

mfix22 commented 6 years ago

@GavinRay97 i always imagined a UI similar to the screenshot you posted, that had a subset of the definable colors (background, keyword, def, operator, etc.) that people can choose the colors for (using our colorpicker component) to start.

If you have ever used Slack, they let you choose like 8 colors to create a theme, and I think that would be a good place to start for Carbon. After that we can add more fine grained controls. I don't want the controls to be too overwhelming at the start.

Also, i think creating a custom theme, and saving/loading them are two separate issues. Would it be helpful to separate them in the issues?

GavinRay97 commented 6 years ago

@mfix22 I would think so, since the saving/loading isn't directly tied to ability to create a theme.

I'm about to finish up my work for the evening, I'll try to get a basic stylesheet link loaded, then mess with a simple modal with text editor to inject CSS, and then I'll see about the dat.gui stuff.

One minor hump with the dat.gui approach is that you're programmatically controlling styles instead of directly via CSS, but I'm sure it can be worked around.

mfix22 commented 6 years ago

I don't think we need to use dat.gui, unless i am mistaken. We can probably just inject global stylesheets for each "Theme" that is created, and have one for the current theme that is being edited.

GavinRay97 commented 6 years ago

@mfix22 What do you think? Had to keep the video short because of compression but you get the idea.

Video Preview: https://i.imgur.com/lV061Ky.gifv

Screenshot:

I'm not the best with React, I primarily write Vue + Ruby. Here's most of the code to accomplish this, I'm sure this isn't proper and needs work:

In state:

customThemeStyle: {
  "background": "#151718",
  "color": "#cfd2d1",
  "cm-keyword": "#e6cd69",
  "cm-operator": "#9fca56",
  "cm-def": "#55b5db",
  "cm-meta": "#55b5db",
  "cm-string": "#55b5db",
  "cm-variable": "#a074c4",
  "cm-variable-2": "#a074c4",
  "cm-property": "#a074c4",
  "cm-number": "#cd3f45"
}

Custom style node:

<style id="custom-styles" />

dat.gui reactive overlay:

<DatGui data={this.state.customThemeStyle} onUpdate={this.colorsChanged}>
  {Object.keys(this.state.customThemeStyle).map(key => <DatColor path={key} label={key} />)}
</DatGui>

Functionality:

colorsChanged(newColors) {
  this.setState({customThemeStyle: newColors})
  this.setCustomStyles()
}

setCustomStyles() {
  // Convert customThemeStyle to iterable object
  const iterableStyles = Object.entries(this.state.customThemeStyle)

  // The editor background and text color properties need to be handled differently
  const windowProps = ["background", "color"]
  const isWindowProp = (selector) => windowProps.includes(selector)

  // Return CSS string of custom theme styles
  const generateCustomStyleCSS = () => {
    return iterableStyles.map(([cssSelector, colorCode]) => {
      return isWindowProp(cssSelector)
        ? `.cm-s-custom.CodeMirror {${cssSelector}: ${colorCode};}`
        : `.cm-s-custom .${cssSelector} {color: ${colorCode};}`
    }).join("\n")
  }

  // Get the custom-styles node and remove it's previous CSS text
  const style = document.getElementById("custom-styles")
  style.firstChild && style.removeChild(style.firstChild);

  // Set the style element content to our generated CSS
  style.appendChild(document.createTextNode(generateCustomStyleCSS()))
}
mfix22 commented 6 years ago

@GavinRay97 this looks great!! 😄 How about this, can you push this up on a branch that isn't master and allow us to edit it, and we can take a look at styling it according to our style (@jakedex) and we can also try and React-ify it as well.

mfix22 commented 6 years ago

@GavinRay97 You can also just create the stylesheet, and just next/head (search for <Head>) component and React will keep that up to date when you call setState()

GavinRay97 commented 6 years ago

@mfix22

up on a branch that isn't master

Awww damn, that's straight where I was intending on trying to go with this :joy:

But sure, I'll get everything up on a branch and then link you here as soon as I've got a spare moment.

and we can take a look at styling it according to our style (@jakedex) and we can also try and React-ify it as well.

Could you drop a mention to the commit when you do this? I'm always interested in seeing how things are better done.

You can also just create the stylesheet, and just next/head (search for ) component and React will keep that up to date when you call setState()

Right over my head haha.

mfix22 commented 6 years ago

@GavinRay97 haha no worries. Thanks for your help. We will make sure you see the updates on your PR once you open it 😄

GavinRay97 commented 6 years ago

@mfix22 https://github.com/GavinRay97/carbon/tree/custom-themes

So I built the foundation for a "preset" type architecture and cleaned up the dat.gui layout and styles a bit.

I think the presets thing is really nice.

Again, the code is probably awful. I tried to follow the patterns I saw as best I could.

charlie-volpe commented 5 years ago

In this same vein, I would love to see "gloom" in here. So +1

mfix22 commented 5 years ago

We just released this feature! Try it out in the "Themes" dropdown menu

zeit-theme-over