├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── package.json
├── postcss.config.js
├── public
└── index.html
├── src
├── App.js
├── Code.js
├── constants.js
├── elements.js
├── index.js
├── logo.js
├── make-theme
│ ├── collectStyles.js
│ ├── highlighterTheme.js
│ ├── index.js
│ ├── minify.js
│ ├── nightOwl.js
│ ├── scopeMapper.js
│ ├── scopeScore.js
│ ├── template.js
│ └── transformSettings.js
└── styles.css
├── tailwind.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "git.ignoreLimitWarning": true
3 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # prism-theme-converter
2 | Created with CodeSandbox
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "prism-theme-generator",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "css": "^3.0.0",
9 | "decomment": "0.9.2",
10 | "is-equal": "1.6.1",
11 | "prism-react-renderer": "1.0.2",
12 | "react": "16.12.0",
13 | "react-dom": "16.12.0",
14 | "react-scripts": "3.0.1",
15 | "react-use-clipboard": "1.0.0",
16 | "styled-components": "5.1.0",
17 | "to-camel-case": "^1.0.0"
18 | },
19 | "devDependencies": {
20 | "typescript": "3.3.3"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test --env=jsdom",
26 | "eject": "react-scripts eject"
27 | },
28 | "browserslist": [
29 | ">0.2%",
30 | "not dead",
31 | "not ie <= 11",
32 | "not op_mini all"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require("tailwindcss"),
4 | require("autoprefixer"),
5 | require("postcss-import")
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
18 |
22 |
23 |
27 |
32 |
33 |
42 | VSCode to Prism Themes
43 |
44 |
45 |
46 |
47 |
48 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useRef } from "react";
2 | import { makeTheme } from "./make-theme";
3 | import decomment from "decomment";
4 | import Code from "./Code";
5 | import Logo from "./logo";
6 | import "styled-components/macro";
7 |
8 | import { InputArea, Main, Textarea } from "./elements";
9 |
10 | import { PLACEHOLDER, GET_THEME, BUTTON_STYLES } from "./constants";
11 |
12 | export default function App() {
13 | const textarea = useRef();
14 | const [tab, setTab] = useState("prism");
15 | const [theme, setTheme] = useState();
16 | const [error, setError] = useState(false);
17 | const [vsCodeTheme, setVSCodeTheme] = useState("");
18 |
19 | const createPrismTheme = () => {
20 | setError(false);
21 | let t = "";
22 | try {
23 | // eslint-disable-next-line
24 | t = eval("(" + decomment(vsCodeTheme) + ")");
25 | } catch (e) {
26 | setError(true);
27 | return setTheme(`
28 | // not a valid VSCode Theme
29 | `);
30 | }
31 |
32 | if (!t || t.$schema !== "vscode://schemas/color-theme") {
33 | setError(true);
34 | return setTheme(`
35 | // not a valid VSCode Theme
36 | `);
37 | } else {
38 | setTheme(makeTheme(t));
39 | }
40 | };
41 |
42 | return (
43 |
44 |
45 |
46 |
47 |
72 |
80 |
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/src/Code.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "styled-components/macro";
3 | import useClipboard from "react-use-clipboard";
4 | import { template } from "./make-theme/template";
5 | import { toSyntaxHighlighter } from "./make-theme/highlighterTheme";
6 | import Highlight, { defaultProps } from "prism-react-renderer";
7 | import { ACTIVE_TAB, RESULT_CODE, GET_THEME, BUTTON_STYLES } from "./constants";
8 | import { Code, Wrapper } from "./elements";
9 |
10 | export default ({ theme, code, language = "json", setTab, tab, error }) => {
11 | const getCode = () => {
12 | if (!theme) return null;
13 | if (error) {
14 | return theme;
15 | }
16 |
17 | if (tab === "prism") return template(theme);
18 | if (tab === "react") return theme;
19 | if (tab === "highlighter")
20 | return `export default ${JSON.stringify(
21 | toSyntaxHighlighter(template(theme)),
22 | null,
23 | 4
24 | )}`;
25 | };
26 |
27 | const ResultCode = RESULT_CODE(theme, getCode(code), tab);
28 | const [isCopied, setCopied] = useClipboard(ResultCode, {
29 | successDuration: 1000,
30 | });
31 |
32 | return (
33 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
64 |
65 |
66 |
67 |
73 | {({ className, style, tokens, getLineProps, getTokenProps }) => (
74 |
75 | {tokens.map((line, i) => (
76 |
77 | {line.map((token, key) => (
78 |
79 | ))}
80 |
81 | ))}
82 |
83 | )}
84 |
85 |
97 |
98 | );
99 | };
100 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | import nightOwl from "./make-theme/nightOwl";
2 |
3 | export const PLACEHOLDER = `
4 | Paste your VSCode theme here.
5 |
6 | To get it:
7 | 1. Open VSCode
8 | 2. Press (CMD or CTRL) + SHIFT + P
9 | 3. Enter: '> Developer: Generate Color Scheme From Current Settings'
10 | 4. Copy the contents and paste them here!
11 | `;
12 |
13 | export const BUTTON_STYLES =
14 | "inline-flex items-center px-4 py-2 border border-gray-300 text-base leading-6 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50 transition ease-in-out duration-150";
15 |
16 | export const ACTIVE_TAB = (boolean) =>
17 | boolean
18 | ? `w-1/2 py-4 px-1 text-center border-b-2 border-indigo-500 font-medium text-sm leading-5 text-indigo-600 focus:outline-none focus:text-indigo-800 focus:border-indigo-700 bg-white`
19 | : `w-1/2 py-4 px-1 text-center border-b-2 border-transparent font-medium text-sm leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 bg-white`;
20 |
21 | export const RESULT_CODE = (theme, code, tab) => {
22 | if (theme) {
23 | if (code.plain) {
24 | return JSON.stringify(code, null, 2);
25 | } else {
26 | return code;
27 | }
28 | }
29 |
30 | if (tab === "react") {
31 | return ` // Your Prism React Render Theme will show up here`;
32 | }
33 |
34 | if (tab === "highlighter") {
35 | return ` // Your React Syntax Highlighter Theme will show up here`;
36 | }
37 |
38 | return "/* Your Prism Theme will show up here */";
39 | };
40 |
41 | export const GET_THEME = (theme) => {
42 | if (theme && theme.plain) return theme;
43 |
44 | return nightOwl;
45 | };
46 |
--------------------------------------------------------------------------------
/src/elements.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const InputArea = styled.div`
4 | display: grid;
5 | grid-template-columns: 50% 50%;
6 | position: relative;
7 | overflow: auto;
8 | `;
9 |
10 | export const Main = styled.main`
11 | width: 100vw;
12 | height: 100vh;
13 | padding: 0;
14 | overflow: hidden;
15 | `;
16 |
17 | export const Textarea = styled.textarea`
18 | width: 100%;
19 | height: 93%;
20 | border: none;
21 | color: #f1f1f1;
22 | background: #252222;
23 | line-height: 1.5;
24 | font-family: monospace;
25 | padding: 20px;
26 | `;
27 |
28 | export const Code = styled.pre`
29 | position: relative;
30 | padding: 20px;
31 | height: calc(100vh - 180px);
32 | overflow: scroll;
33 | margin: 0;
34 | word-break: break-word;
35 | width: 100%;
36 | white-space: pre-wrap;
37 | `;
38 | export const Wrapper = styled.div`
39 | position: relative;
40 | `;
41 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import App from "./App";
5 | import "./styles.css";
6 |
7 | const rootElement = document.getElementById("root");
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | rootElement
13 | );
14 |
--------------------------------------------------------------------------------
/src/logo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "styled-components/macro";
3 |
4 | const Icon = ({ theme }) => (
5 |
65 | );
66 |
67 | export default Icon;
68 |
--------------------------------------------------------------------------------
/src/make-theme/collectStyles.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable-file array-callback-return */
2 |
3 | import { mapScope } from "./scopeMapper";
4 | import { getScoreForScope } from "./scopeScore";
5 | import { transformSettings } from "./transformSettings";
6 | import { minify } from "./minify";
7 |
8 | export const collectAllSettings = (tokenColors) => {
9 | const output = {};
10 |
11 | tokenColors.forEach(({ scope, settings }) => {
12 | // We only care about colouring here
13 | if (!settings.foreground && !settings.fontStyle) {
14 | return;
15 | }
16 |
17 | const normScope = typeof scope === "string" ? [scope] : scope;
18 | // Return when no input scopes are present
19 | if (!normScope || !normScope.length) {
20 | return;
21 | }
22 |
23 | normScope.forEach((scopeName) => {
24 | const mappedScope = mapScope(scopeName);
25 | // Return when no mapping scope has been returned
26 | if (!mappedScope) {
27 | return;
28 | }
29 |
30 | if (Array.isArray(mappedScope)) {
31 | // eslint-disable-next-line
32 | mappedScope.map((scope) => {
33 | if (output[scope] === undefined) {
34 | output[scope] = [];
35 | }
36 | output[scope].push({
37 | scope: scopeName,
38 | settings,
39 | });
40 | });
41 | } else {
42 | if (output[mappedScope] === undefined) {
43 | output[mappedScope] = [];
44 | }
45 | output[mappedScope].push({
46 | scope: scopeName,
47 | settings,
48 | });
49 | }
50 | });
51 | });
52 |
53 | const styles = Object.keys(output).map((mappedScope) => {
54 | const matchesArr = output[mappedScope];
55 |
56 | // Get score for each match
57 | const scored = matchesArr.map((match) => {
58 | const score = getScoreForScope(match.scope, mappedScope);
59 |
60 | return {
61 | score,
62 | scope: mappedScope,
63 | settings: transformSettings(match.settings),
64 | };
65 | });
66 |
67 | // Sort by score asc
68 | const sorted = scored.sort((a, b) => b.score - a.score);
69 |
70 | // Return highest-scored one
71 | return sorted[0];
72 | });
73 |
74 | const themeStyles = minify(styles);
75 |
76 | return {
77 | styles: themeStyles,
78 | };
79 | };
80 |
--------------------------------------------------------------------------------
/src/make-theme/highlighterTheme.js:
--------------------------------------------------------------------------------
1 | import css from "css";
2 | import camel from "to-camel-case";
3 |
4 | export const toSyntaxHighlighter = (data) =>
5 | css.parse(data).stylesheet.rules.reduce((sheet, rule) => {
6 | if (rule.type === "rule") {
7 | const style = rule.selectors.reduce((selectors, selector) => {
8 | const selectorObject = rule.declarations.reduce(
9 | (declarations, declaration) => {
10 | if (declaration.type === "declaration" && declaration.property) {
11 | const camelCaseDeclarationProp = camel(declaration.property);
12 | const key =
13 | camelCaseDeclarationProp.includes("moz") ||
14 | camelCaseDeclarationProp.includes("webkit") ||
15 | (camelCaseDeclarationProp[0] === "o" &&
16 | !camelCaseDeclarationProp.includes("overflow"))
17 | ? `${camelCaseDeclarationProp
18 | .substring(0, 1)
19 | .toUpperCase()}${camelCaseDeclarationProp.substring(1)}`
20 | : camelCaseDeclarationProp;
21 | declarations[key] = declaration.value;
22 | }
23 | return declarations;
24 | },
25 | {}
26 | );
27 |
28 | if (selector.substring(0, 6) === ".token") {
29 | selector = selector.substring(7);
30 | }
31 | selectors[selector] = selectorObject;
32 | return selectors;
33 | }, {});
34 | sheet = Object.keys(style).reduce((stylesheet, selector) => {
35 | if (stylesheet[selector]) {
36 | stylesheet[selector] = Object.assign(
37 | {},
38 | stylesheet[selector],
39 | style[selector]
40 | );
41 | } else {
42 | stylesheet[selector] = style[selector];
43 | }
44 | return stylesheet;
45 | }, sheet);
46 | }
47 | return sheet;
48 | }, {});
49 |
--------------------------------------------------------------------------------
/src/make-theme/index.js:
--------------------------------------------------------------------------------
1 | import { collectAllSettings } from "./collectStyles";
2 |
3 | export const makeTheme = (theme) => {
4 | const prismTheme = collectAllSettings(theme.tokenColors);
5 | const json = {
6 | plain: {
7 | color:
8 | theme.colors["editor.foreground"] ||
9 | theme.tokenColors.find((token) => token.scope === "support").settings
10 | .foreground,
11 | backgroundColor: theme.colors["editor.background"],
12 | selectionBackground: theme.colors["editor.selectionBackground"],
13 | },
14 | ...prismTheme,
15 | };
16 |
17 | return json;
18 | };
19 |
--------------------------------------------------------------------------------
/src/make-theme/minify.js:
--------------------------------------------------------------------------------
1 | import isEqual from "is-equal";
2 |
3 | export const minify = styles => {
4 | const output = [];
5 |
6 | styles.forEach(style => {
7 | const item = output.find(x => isEqual(style.settings, x.style));
8 |
9 | if (!item) {
10 | output.push({
11 | types: [style.scope],
12 | style: style.settings
13 | });
14 | } else {
15 | item.types.push(style.scope);
16 | }
17 | });
18 |
19 | return output;
20 | };
21 |
--------------------------------------------------------------------------------
/src/make-theme/nightOwl.js:
--------------------------------------------------------------------------------
1 | var theme = {
2 | plain: {
3 | color: "#d6deeb",
4 | backgroundColor: "#011627"
5 | },
6 | styles: [
7 | {
8 | types: ["changed"],
9 | style: {
10 | color: "rgb(162, 191, 252)",
11 | fontStyle: "italic"
12 | }
13 | },
14 | {
15 | types: ["deleted"],
16 | style: {
17 | color: "rgba(239, 83, 80, 0.56)",
18 | fontStyle: "italic"
19 | }
20 | },
21 | {
22 | types: ["inserted", "attr-name"],
23 | style: {
24 | color: "rgb(173, 219, 103)",
25 | fontStyle: "italic"
26 | }
27 | },
28 | {
29 | types: ["comment"],
30 | style: {
31 | color: "rgb(99, 119, 119)",
32 | fontStyle: "italic"
33 | }
34 | },
35 | {
36 | types: ["string", "url"],
37 | style: {
38 | color: "rgb(173, 219, 103)"
39 | }
40 | },
41 | {
42 | types: ["variable"],
43 | style: {
44 | color: "rgb(214, 222, 235)"
45 | }
46 | },
47 | {
48 | types: ["number"],
49 | style: {
50 | color: "rgb(247, 140, 108)"
51 | }
52 | },
53 | {
54 | types: ["builtin", "char", "constant", "function"],
55 | style: {
56 | color: "rgb(130, 170, 255)"
57 | }
58 | },
59 | {
60 | // This was manually added after the auto-generation
61 | // so that punctuations are not italicised
62 | types: ["punctuation"],
63 | style: {
64 | color: "rgb(199, 146, 234)"
65 | }
66 | },
67 | {
68 | types: ["selector", "doctype"],
69 | style: {
70 | color: "rgb(199, 146, 234)",
71 | fontStyle: "italic"
72 | }
73 | },
74 | {
75 | types: ["class-name"],
76 | style: {
77 | color: "rgb(255, 203, 139)"
78 | }
79 | },
80 | {
81 | types: ["tag", "operator", "keyword"],
82 | style: {
83 | color: "rgb(127, 219, 202)"
84 | }
85 | },
86 | {
87 | types: ["boolean"],
88 | style: {
89 | color: "rgb(255, 88, 116)"
90 | }
91 | },
92 | {
93 | types: ["property"],
94 | style: {
95 | color: "rgb(128, 203, 196)"
96 | }
97 | },
98 | {
99 | types: ["namespace"],
100 | style: {
101 | color: "rgb(178, 204, 214)"
102 | }
103 | }
104 | ]
105 | };
106 |
107 | export default theme;
108 |
--------------------------------------------------------------------------------
/src/make-theme/scopeMapper.js:
--------------------------------------------------------------------------------
1 | const scopeMap = {
2 | comment: "comment",
3 | punctuation: "punctuation",
4 | "punctuation.section.array": "punctuation",
5 | string: "string",
6 | variable: "variable",
7 | constant: "constant",
8 | header: "prolog",
9 | "support.function.magic": "constant",
10 | "support.variable": "constant",
11 | "entity.name.type.namespace": "namespace",
12 | "keyword.operator": "operator",
13 | "constant.numeric": "number",
14 | "constant.character.numeric": "number",
15 | "support.type.vendor.property-name": "property",
16 | "support.type.property-name": "property",
17 | "meta.property-list": "property",
18 | "entity.name.tag": "tag",
19 | "entity.name.function": "function",
20 | "entity.name.class": "class-name",
21 | "entity.name.tag.doctype": "doctype",
22 | "meta.selector": "selector",
23 | "entity.other.attribute-name": "attr-name",
24 | "meta.attribute-selector": "attr-name",
25 | "constant.other": "constant",
26 | "constant.other.symbol": "symbol",
27 | "constant.language.boolean": "boolean",
28 | "constant.character": "char",
29 | "meta.tag.html": "tag",
30 | "meta.tag.js": "tag",
31 | "support.function": "builtin",
32 | "support.constant": "builtin",
33 | "variable.other.constant": "builtin",
34 | "variable.other.meta.import.js": "imports",
35 | "variable.other.meta.export.js": "exports",
36 | "constant.language": "builtin",
37 | "keyword.control": "keyword",
38 | "keyword.other": "keyword",
39 | "string.regexp": "regex",
40 | "variable.parameter.url": "url",
41 | "meta.at-rule": "at-rule",
42 | "source.css.scss": "at-rule",
43 | "markup.inserted": "inserted",
44 | "markup.deleted": "deleted",
45 | "markup.changed": "changed",
46 | "constant.other.color": "hexcode",
47 | };
48 |
49 | const duplicated = {
50 | "entity.other.attribute-name": "selector",
51 | constant: "hexcode",
52 | };
53 |
54 | export const mapScope = (scope) => {
55 | // If the scope includes a whitespace, it's a specific
56 | // type that we don't support
57 | if (scope.includes(" ")) {
58 | return undefined;
59 | }
60 |
61 | const scopeAccess = scope.split(".");
62 |
63 | for (let i = scopeAccess.length; i >= 0; i--) {
64 | const searchScope = scopeAccess.slice(0, i).join(".");
65 | const outputScope = scopeMap[searchScope];
66 | if (outputScope !== undefined) {
67 | if (duplicated[searchScope]) {
68 | return [outputScope, duplicated[searchScope]];
69 | }
70 | return outputScope;
71 | }
72 | }
73 |
74 | return undefined;
75 | };
76 |
--------------------------------------------------------------------------------
/src/make-theme/scopeScore.js:
--------------------------------------------------------------------------------
1 | const scorePerScope = {
2 | builtin: ['variable.other.constant', 'constant.language'],
3 | punctuation: ['punctuation.accessor'],
4 | tag: ['meta.tag.html', 'entity.name.tag'],
5 | };
6 |
7 | // The higher the better
8 | const score = [
9 | // These are sure matches
10 | 'markup', // diffs
11 | 'comment',
12 | 'punctuation',
13 | 'string',
14 | 'variable',
15 |
16 | // These are more "meta" scopes
17 | 'meta', // good guesses
18 | 'entity',
19 | 'constant',
20 | 'support',
21 | 'variable',
22 | ];
23 |
24 | const baseScoreSize = score.length;
25 |
26 | export const getScoreForScope = (scope, mappedScope) => {
27 | // Get scores for specific mapped scopes first
28 | const scoreForMapped = scorePerScope[mappedScope];
29 | if (scoreForMapped) {
30 | // If the scope is in the specific mapped scope we add the baseScoreSize to this
31 | // score
32 | const mappedIndex = scoreForMapped.findIndex(x => scope.startsWith(x));
33 | if (mappedIndex !== -1) {
34 | return baseScoreSize + (scoreForMapped.length - mappedIndex);
35 | }
36 | }
37 |
38 | const parentScope = scope.split('.')[0];
39 | const index = score.indexOf(parentScope);
40 |
41 | if (index === -1) {
42 | // Otherwise it's a negative score based on length
43 | return -1 * scope.length;
44 | }
45 |
46 | // If it's found we return the score from the main `score` arr
47 | return baseScoreSize - index;
48 | };
49 |
--------------------------------------------------------------------------------
/src/make-theme/template.js:
--------------------------------------------------------------------------------
1 | export const template = (theme) => `code[class*="language-"],
2 | pre[class*="language-"] {
3 | color: ${
4 | theme.plain.color ||
5 | theme.styles.find((style) => style.types.includes("punctuation")).style
6 | .color
7 | };
8 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
9 | text-align: left;
10 | white-space: pre;
11 | word-spacing: normal;
12 | word-break: normal;
13 | word-wrap: normal;
14 | line-height: 1.5;
15 |
16 | -moz-tab-size: 4;
17 | -o-tab-size: 4;
18 | tab-size: 4;
19 |
20 | -webkit-hyphens: none;
21 | -moz-hyphens: none;
22 | -ms-hyphens: none;
23 | hyphens: none;
24 | }
25 |
26 | pre[class*="language-"]::-moz-selection,
27 | pre[class*="language-"] ::-moz-selection,
28 | code[class*="language-"]::-moz-selection,
29 | code[class*="language-"] ::-moz-selection {
30 | text-shadow: none;
31 | background: ${theme.plain.selectionBackground};
32 | }
33 |
34 | pre[class*="language-"]::selection,
35 | pre[class*="language-"] ::selection,
36 | code[class*="language-"]::selection,
37 | code[class*="language-"] ::selection {
38 | text-shadow: none;
39 | background: ${theme.plain.selectionBackground};
40 | }
41 |
42 | @media print {
43 | code[class*="language-"],
44 | pre[class*="language-"] {
45 | text-shadow: none;
46 | }
47 | }
48 |
49 | /* Code blocks */
50 | pre[class*="language-"] {
51 | padding: 1em;
52 | margin: 0.5em 0;
53 | overflow: auto;
54 | }
55 |
56 | :not(pre) > code[class*="language-"],
57 | pre[class*="language-"] {
58 | color: white;
59 | background: ${theme.plain.backgroundColor};
60 | }
61 |
62 | :not(pre) > code[class*="language-"] {
63 | padding: 0.1em;
64 | border-radius: 0.3em;
65 | white-space: normal;
66 | }
67 |
68 | ${theme.styles
69 | .map((style) =>
70 | style.types
71 | .map(
72 | (type) => `
73 | .token.${type} {
74 | color: ${style.style.color};
75 | ${style.style.fontStyle ? `font-style: ${style.style.fontStyle}` : ""}
76 | }
77 | `
78 | )
79 | .join("")
80 | )
81 | .join("")}
82 | .token.important,
83 | .token.bold {
84 | font-weight: bold;
85 | }
86 |
87 | .token.italic {
88 | font-style: italic;
89 | }
90 | `;
91 |
--------------------------------------------------------------------------------
/src/make-theme/transformSettings.js:
--------------------------------------------------------------------------------
1 | export const transformSettings = (settings) => {
2 | const output = {};
3 |
4 | if (settings.foreground) {
5 | output.color = settings.foreground.toString();
6 | }
7 |
8 | if (settings.background) {
9 | output.backgroundColor = settings.background.toString();
10 | }
11 |
12 | if (settings.fontStyle === "italic") {
13 | output.fontStyle = "italic";
14 | }
15 |
16 | return output;
17 | };
18 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 0;
3 | margin: 0;
4 | background: #eaeaea;
5 | overflow: auto;
6 | }
7 |
8 | body * {
9 | box-sizing: border-box;
10 | }
11 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [require("@tailwindcss/ui")]
3 | };
4 |
--------------------------------------------------------------------------------