Detailed changes
@@ -1,11 +1,13 @@
-import * as fs from "fs"
-import * as path from "path"
-import { ColorScheme, createColorScheme } from "./common"
-import { themes } from "./themes"
-import { slugify } from "./utils/slugify"
-import { colorSchemeTokens } from "./theme/tokens/colorScheme"
+import * as fs from "fs";
+import * as path from "path";
+import { ColorScheme, createColorScheme } from "./common";
+import { themes } from "./themes";
+import { slugify } from "./utils/slugify";
+import { colorSchemeTokens } from "./theme/tokens/colorScheme";
-const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens")
+const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens");
+const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json");
+const METADATA_FILE = path.join(TOKENS_DIRECTORY, "$metadata.json");
function clearTokens(tokensDirectory: string) {
if (!fs.existsSync(tokensDirectory)) {
@@ -19,21 +21,65 @@ function clearTokens(tokensDirectory: string) {
}
}
+type TokenSet = {
+ id: string;
+ name: string;
+ selectedTokenSets: { [key: string]: "enabled" };
+};
+
+function buildTokenSetOrder(colorSchemes: ColorScheme[]): { tokenSetOrder: string[] } {
+ const tokenSetOrder: string[] = colorSchemes.map(
+ (scheme) => scheme.name.toLowerCase().replace(/\s+/g, "_")
+ );
+ return { tokenSetOrder };
+}
+
+function buildThemesIndex(colorSchemes: ColorScheme[]): TokenSet[] {
+ const themesIndex: TokenSet[] = colorSchemes.map((scheme, index) => {
+ const id = `${scheme.isLight ? "light" : "dark"}_${scheme.name
+ .toLowerCase()
+ .replace(/\s+/g, "_")}_${index}`;
+ const selectedTokenSets: { [key: string]: "enabled" } = {};
+ const tokenSet = scheme.name.toLowerCase().replace(/\s+/g, "_");
+ selectedTokenSets[tokenSet] = "enabled";
+
+ return {
+ id,
+ name: `${scheme.name} - ${scheme.isLight ? "Light" : "Dark"}`,
+ selectedTokenSets,
+ };
+ });
+
+ return themesIndex;
+}
+
function writeTokens(colorSchemes: ColorScheme[], tokensDirectory: string) {
- clearTokens(tokensDirectory)
+ clearTokens(tokensDirectory);
for (const colorScheme of colorSchemes) {
- const fileName = slugify(colorScheme.name)
- const tokens = colorSchemeTokens(colorScheme)
- const tokensJSON = JSON.stringify(tokens, null, 2)
- const outPath = path.join(tokensDirectory, `${fileName}.json`)
- fs.writeFileSync(outPath, tokensJSON)
- console.log(`- ${outPath} created`)
+ const fileName = slugify(colorScheme.name) + ".json";
+ const tokens = colorSchemeTokens(colorScheme);
+ const tokensJSON = JSON.stringify(tokens, null, 2);
+ const outPath = path.join(tokensDirectory, fileName);
+ fs.writeFileSync(outPath, tokensJSON, { mode: 0o644 });
+ console.log(`- ${outPath} created`);
}
+
+ const themeIndexData = buildThemesIndex(colorSchemes);
+
+ const themesJSON = JSON.stringify(themeIndexData, null, 2);
+ fs.writeFileSync(TOKENS_FILE, themesJSON, { mode: 0o644 });
+ console.log(`- ${TOKENS_FILE} created`);
+
+ const tokenSetOrderData = buildTokenSetOrder(colorSchemes);
+
+ const metadataJSON = JSON.stringify(tokenSetOrderData, null, 2);
+ fs.writeFileSync(METADATA_FILE, metadataJSON, { mode: 0o644 });
+ console.log(`- ${METADATA_FILE} created`);
}
const colorSchemes: ColorScheme[] = themes.map((theme) =>
createColorScheme(theme)
-)
+);
-writeTokens(colorSchemes, TOKENS_DIRECTORY)
+writeTokens(colorSchemes, TOKENS_DIRECTORY);
@@ -1,12 +1,81 @@
-import { ColorScheme } from "../colorScheme"
-import { PlayerTokens, players } from "./players"
+import { SingleBoxShadowToken, SingleColorToken, SingleOtherToken, TokenTypes } from "@tokens-studio/types"
+import { ColorScheme, Shadow, SyntaxHighlightStyle, ThemeSyntax } from "../colorScheme"
+import { LayerToken, layerToken } from "./layer"
+import { PlayersToken, playersToken } from "./players"
+import { colorToken } from "./token"
+import { Syntax } from "../syntax";
+import editor from "../../styleTree/editor"
interface ColorSchemeTokens {
- players: PlayerTokens
+ name: SingleOtherToken
+ appearance: SingleOtherToken
+ lowest: LayerToken
+ middle: LayerToken
+ highest: LayerToken
+ players: PlayersToken
+ popoverShadow: SingleBoxShadowToken
+ modalShadow: SingleBoxShadowToken
+ syntax?: Partial<ThemeSyntaxColorTokens>
+}
+
+const createShadowToken = (shadow: Shadow, tokenName: string): SingleBoxShadowToken => {
+ return {
+ name: tokenName,
+ type: TokenTypes.BOX_SHADOW,
+ value: `${shadow.offset[0]}px ${shadow.offset[1]}px ${shadow.blur}px 0px ${shadow.color}`
+ };
+};
+
+const popoverShadowToken = (colorScheme: ColorScheme): SingleBoxShadowToken => {
+ const shadow = colorScheme.popoverShadow;
+ return createShadowToken(shadow, "popoverShadow");
+};
+
+const modalShadowToken = (colorScheme: ColorScheme): SingleBoxShadowToken => {
+ const shadow = colorScheme.modalShadow;
+ return createShadowToken(shadow, "modalShadow");
+};
+
+type ThemeSyntaxColorTokens = Record<keyof ThemeSyntax, SingleColorToken>
+
+function syntaxHighlightStyleColorTokens(syntax: Syntax): ThemeSyntaxColorTokens {
+ const styleKeys = Object.keys(syntax) as (keyof Syntax)[]
+
+ return styleKeys.reduce((acc, styleKey) => {
+ // Hack: The type of a style could be "Function"
+ // This can happen because we have a "constructor" property on the syntax object
+ // and a "constructor" property on the prototype of the syntax object
+ // To work around this just assert that the type of the style is not a function
+ if (!syntax[styleKey] || typeof syntax[styleKey] === 'function') return acc;
+ const { color } = syntax[styleKey] as Required<SyntaxHighlightStyle>;
+ return { ...acc, [styleKey]: colorToken(styleKey, color) };
+ }, {} as ThemeSyntaxColorTokens);
+}
+
+const syntaxTokens = (colorScheme: ColorScheme): ColorSchemeTokens['syntax'] => {
+ const syntax = editor(colorScheme).syntax
+
+ return syntaxHighlightStyleColorTokens(syntax)
}
export function colorSchemeTokens(colorScheme: ColorScheme): ColorSchemeTokens {
return {
- players: players(colorScheme),
+ name: {
+ name: "themeName",
+ value: colorScheme.name,
+ type: TokenTypes.OTHER,
+ },
+ appearance: {
+ name: "themeAppearance",
+ value: colorScheme.isLight ? "light" : "dark",
+ type: TokenTypes.OTHER,
+ },
+ lowest: layerToken(colorScheme.lowest, "lowest"),
+ middle: layerToken(colorScheme.middle, "middle"),
+ highest: layerToken(colorScheme.highest, "highest"),
+ popoverShadow: popoverShadowToken(colorScheme),
+ modalShadow: modalShadowToken(colorScheme),
+ players: playersToken(colorScheme),
+ syntax: syntaxTokens(colorScheme),
}
}
@@ -0,0 +1,60 @@
+import { SingleColorToken } from "@tokens-studio/types";
+import { Layer, Style, StyleSet } from "../colorScheme";
+import { colorToken } from "./token";
+
+interface StyleToken {
+ background: SingleColorToken,
+ border: SingleColorToken,
+ foreground: SingleColorToken,
+}
+
+interface StyleSetToken {
+ default: StyleToken
+ active: StyleToken
+ disabled: StyleToken
+ hovered: StyleToken
+ pressed: StyleToken
+ inverted: StyleToken
+}
+
+export interface LayerToken {
+ base: StyleSetToken
+ variant: StyleSetToken
+ on: StyleSetToken
+ accent: StyleSetToken
+ positive: StyleSetToken
+ warning: StyleSetToken
+ negative: StyleSetToken
+}
+
+export const styleToken = (style: Style, name: string): StyleToken => {
+ const token = {
+ background: colorToken(`${name}Background`, style.background),
+ border: colorToken(`${name}Border`, style.border),
+ foreground: colorToken(`${name}Foreground`, style.foreground),
+ }
+
+ return token
+}
+
+export const styleSetToken = (styleSet: StyleSet, name: string): StyleSetToken => {
+ const token: StyleSetToken = {} as StyleSetToken;
+
+ for (const style in styleSet) {
+ const s = style as keyof StyleSet;
+ token[s] = styleToken(styleSet[s], `${name}${style}`);
+ }
+
+ return token;
+}
+
+export const layerToken = (layer: Layer, name: string): LayerToken => {
+ const token: LayerToken = {} as LayerToken;
+
+ for (const styleSet in layer) {
+ const s = styleSet as keyof Layer;
+ token[s] = styleSetToken(layer[s], `${name}${styleSet}`);
+ }
+
+ return token;
+}
@@ -4,7 +4,7 @@ import { colorToken } from "./token"
export type PlayerToken = Record<"selection" | "cursor", SingleColorToken>
-export type PlayerTokens = Record<keyof Players, PlayerToken>
+export type PlayersToken = Record<keyof Players, PlayerToken>
function buildPlayerToken(colorScheme: ColorScheme, index: number): PlayerToken {
@@ -16,7 +16,7 @@ function buildPlayerToken(colorScheme: ColorScheme, index: number): PlayerToken
}
}
-export const players = (colorScheme: ColorScheme): PlayerTokens => ({
+export const playersToken = (colorScheme: ColorScheme): PlayersToken => ({
"0": buildPlayerToken(colorScheme, 0),
"1": buildPlayerToken(colorScheme, 1),
"2": buildPlayerToken(colorScheme, 2),