snake_case theme (#2661)

This PR moves the theme / `/styles` typescript app to use snake_case to
better align with the rust app and make it easier to reference things
across both apps.

It also configures ESLint in the styles app and fixes many ESLint
errors.

Going forward from this PR we will use `snake_case` throughout the
theme.

Release Notes:

- N/A (No public facing changes)
This commit is contained in:
Nate Butler 2023-06-29 12:47:15 -04:00 committed by GitHub
commit c7c38c7da2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
144 changed files with 6276 additions and 4510 deletions

33
styles/.eslintrc.js Normal file
View File

@ -0,0 +1,33 @@
module.exports = {
env: {
node: true,
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/typescript",
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["@typescript-eslint", "import"],
globals: {
module: true,
},
settings: {
"import/parsers": {
"@typescript-eslint/parser": [".ts"],
},
"import/resolver": {
typescript: true,
node: true,
},
"import/extensions": [".ts"],
},
rules: {
"linebreak-style": ["error", "unix"],
semi: ["error", "never"],
},
}

6
styles/.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"semi": false,
"printWidth": 80,
"htmlWhitespaceSensitivity": "strict",
"tabWidth": 4
}

2682
styles/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,22 @@
{
"name": "styles",
"version": "1.0.0",
"description": "",
"main": "index.js",
"description": "Typescript app that builds Zed's themes",
"main": "./src/build_themes.ts",
"scripts": {
"build": "ts-node ./src/buildThemes.ts",
"build-licenses": "ts-node ./src/buildLicenses.ts",
"build-tokens": "ts-node ./src/buildTokens.ts",
"build-types": "ts-node ./src/buildTypes.ts",
"build": "ts-node ./src/build_themes.ts",
"build-licenses": "ts-node ./src/build_licenses.ts",
"build-tokens": "ts-node ./src/build_tokens.ts",
"build-types": "ts-node ./src/build_types.ts",
"test": "vitest"
},
"author": "",
"author": "Zed Industries (https://github.com/zed-industries/)",
"license": "ISC",
"dependencies": {
"@tokens-studio/types": "^0.2.3",
"@types/chroma-js": "^2.4.0",
"@types/node": "^18.14.1",
"ayu": "^8.0.1",
"bezier-easing": "^2.1.0",
"case-anything": "^2.1.10",
"chroma-js": "^2.4.2",
"deepmerge": "^4.3.0",
"json-schema-to-typescript": "^13.0.2",
@ -26,15 +24,13 @@
"ts-deepmerge": "^6.0.3",
"ts-node": "^10.9.1",
"utility-types": "^3.10.0",
"vitest": "^0.32.0"
},
"prettier": {
"semi": false,
"printWidth": 80,
"htmlWhitespaceSensitivity": "strict",
"tabWidth": 4
},
"devDependencies": {
"@vitest/coverage-v8": "^0.32.0"
"vitest": "^0.32.0",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"@vitest/coverage-v8": "^0.32.0",
"eslint": "^8.43.0",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.27.5",
"typescript": "^5.1.5"
}
}

View File

@ -1,50 +0,0 @@
import * as fs from "fs"
import toml from "toml"
import { themes } from "./themes"
import { ThemeConfig } from "./common"
const ACCEPTED_LICENSES_FILE = `${__dirname}/../../script/licenses/zed-licenses.toml`
// Use the cargo-about configuration file as the source of truth for supported licenses.
function parseAcceptedToml(file: string): string[] {
let buffer = fs.readFileSync(file).toString()
let obj = toml.parse(buffer)
if (!Array.isArray(obj.accepted)) {
throw Error("Accepted license source is malformed")
}
return obj.accepted
}
function checkLicenses(themes: ThemeConfig[]) {
for (const theme of themes) {
if (!theme.licenseFile) {
throw Error(`Theme ${theme.name} should have a LICENSE file`)
}
}
}
function generateLicenseFile(themes: ThemeConfig[]) {
checkLicenses(themes)
for (const theme of themes) {
const licenseText = fs.readFileSync(theme.licenseFile).toString()
writeLicense(theme.name, licenseText, theme.licenseUrl)
}
}
function writeLicense(
themeName: string,
licenseText: string,
licenseUrl?: string
) {
process.stdout.write(
licenseUrl
? `## [${themeName}](${licenseUrl})\n\n${licenseText}\n********************************************************************************\n\n`
: `## ${themeName}\n\n${licenseText}\n********************************************************************************\n\n`
)
}
const acceptedLicenses = parseAcceptedToml(ACCEPTED_LICENSES_FILE)
generateLicenseFile(themes)

View File

@ -1,43 +0,0 @@
import * as fs from "fs"
import { tmpdir } from "os"
import * as path from "path"
import app from "./styleTree/app"
import { ColorScheme, createColorScheme } from "./theme/colorScheme"
import snakeCase from "./utils/snakeCase"
import { themes } from "./themes"
const assetsDirectory = `${__dirname}/../../assets`
const tempDirectory = fs.mkdtempSync(path.join(tmpdir(), "build-themes"))
// Clear existing themes
function clearThemes(themeDirectory: string) {
if (!fs.existsSync(themeDirectory)) {
fs.mkdirSync(themeDirectory, { recursive: true })
} else {
for (const file of fs.readdirSync(themeDirectory)) {
if (file.endsWith(".json")) {
fs.unlinkSync(path.join(themeDirectory, file))
}
}
}
}
function writeThemes(colorSchemes: ColorScheme[], outputDirectory: string) {
clearThemes(outputDirectory)
for (let colorScheme of colorSchemes) {
let styleTree = snakeCase(app(colorScheme))
let styleTreeJSON = JSON.stringify(styleTree, null, 2)
let tempPath = path.join(tempDirectory, `${colorScheme.name}.json`)
let outPath = path.join(outputDirectory, `${colorScheme.name}.json`)
fs.writeFileSync(tempPath, styleTreeJSON)
fs.renameSync(tempPath, outPath)
console.log(`- ${outPath} created`)
}
}
const colorSchemes: ColorScheme[] = themes.map((theme) =>
createColorScheme(theme)
)
// Write new themes to theme directory
writeThemes(colorSchemes, `${assetsDirectory}/themes`)

View File

@ -1,87 +0,0 @@
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_FILE = path.join(TOKENS_DIRECTORY, "$themes.json")
const METADATA_FILE = path.join(TOKENS_DIRECTORY, "$metadata.json")
function clearTokens(tokensDirectory: string) {
if (!fs.existsSync(tokensDirectory)) {
fs.mkdirSync(tokensDirectory, { recursive: true })
} else {
for (const file of fs.readdirSync(tokensDirectory)) {
if (file.endsWith(".json")) {
fs.unlinkSync(path.join(tokensDirectory, file))
}
}
}
}
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)
for (const colorScheme of colorSchemes) {
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)

View File

@ -1,64 +0,0 @@
import * as fs from "fs/promises"
import * as fsSync from "fs"
import * as path from "path"
import { compile } from "json-schema-to-typescript"
const BANNER = `/*
* This file is autogenerated
*/\n\n`
const dirname = __dirname
async function main() {
let schemasPath = path.join(dirname, "../../", "crates/theme/schemas")
let schemaFiles = (await fs.readdir(schemasPath)).filter((x) =>
x.endsWith(".json")
)
let compiledTypes = new Set()
for (let filename of schemaFiles) {
let filePath = path.join(schemasPath, filename)
const fileContents = await fs.readFile(filePath)
let schema = JSON.parse(fileContents.toString())
let compiled = await compile(schema, schema.title, {
bannerComment: "",
})
let eachType = compiled.split("export")
for (let type of eachType) {
if (!type) {
continue
}
compiledTypes.add("export " + type.trim())
}
}
let output = BANNER + Array.from(compiledTypes).join("\n\n")
let outputPath = path.join(dirname, "../../styles/src/types/zed.ts")
try {
let existing = await fs.readFile(outputPath)
if (existing.toString() == output) {
// Skip writing if it hasn't changed
console.log("Schemas are up to date")
return
}
} catch (e) {
// It's fine if there's no output from a previous run.
// @ts-ignore
if (e.code !== "ENOENT") {
throw e
}
}
const typesDic = path.dirname(outputPath)
if (!fsSync.existsSync(typesDic)) {
await fs.mkdir(typesDic)
}
await fs.writeFile(outputPath, output)
console.log(`Wrote Typescript types to ${outputPath}`)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})

View File

@ -0,0 +1,50 @@
import * as fs from "fs"
import toml from "toml"
import { themes } from "./themes"
import { ThemeConfig } from "./common"
const ACCEPTED_LICENSES_FILE = `${__dirname}/../../script/licenses/zed-licenses.toml`
// Use the cargo-about configuration file as the source of truth for supported licenses.
function parse_accepted_toml(file: string): string[] {
const buffer = fs.readFileSync(file).toString()
const obj = toml.parse(buffer)
if (!Array.isArray(obj.accepted)) {
throw Error("Accepted license source is malformed")
}
return obj.accepted
}
function check_licenses(themes: ThemeConfig[]) {
for (const theme of themes) {
if (!theme.license_file) {
throw Error(`Theme ${theme.name} should have a LICENSE file`)
}
}
}
function generate_license_file(themes: ThemeConfig[]) {
check_licenses(themes)
for (const theme of themes) {
const license_text = fs.readFileSync(theme.license_file).toString()
write_license(theme.name, license_text, theme.license_url)
}
}
function write_license(
theme_name: string,
license_text: string,
license_url?: string
) {
process.stdout.write(
license_url
? `## [${theme_name}](${license_url})\n\n${license_text}\n********************************************************************************\n\n`
: `## ${theme_name}\n\n${license_text}\n********************************************************************************\n\n`
)
}
const accepted_licenses = parse_accepted_toml(ACCEPTED_LICENSES_FILE)
generate_license_file(themes)

View File

@ -0,0 +1,43 @@
import * as fs from "fs"
import { tmpdir } from "os"
import * as path from "path"
import app from "./style_tree/app"
import { ColorScheme, create_color_scheme } from "./theme/color_scheme"
import { themes } from "./themes"
const assets_directory = `${__dirname}/../../assets`
const temp_directory = fs.mkdtempSync(path.join(tmpdir(), "build-themes"))
function clear_themes(theme_directory: string) {
if (!fs.existsSync(theme_directory)) {
fs.mkdirSync(theme_directory, { recursive: true })
} else {
for (const file of fs.readdirSync(theme_directory)) {
if (file.endsWith(".json")) {
fs.unlinkSync(path.join(theme_directory, file))
}
}
}
}
function write_themes(themes: ColorScheme[], output_directory: string) {
clear_themes(output_directory)
for (const color_scheme of themes) {
const style_tree = app(color_scheme)
const style_tree_json = JSON.stringify(style_tree, null, 2)
const temp_path = path.join(temp_directory, `${color_scheme.name}.json`)
const out_path = path.join(
output_directory,
`${color_scheme.name}.json`
)
fs.writeFileSync(temp_path, style_tree_json)
fs.renameSync(temp_path, out_path)
console.log(`- ${out_path} created`)
}
}
const all_themes: ColorScheme[] = themes.map((theme) =>
create_color_scheme(theme)
)
write_themes(all_themes, `${assets_directory}/themes`)

View File

@ -0,0 +1,87 @@
import * as fs from "fs"
import * as path from "path"
import { ColorScheme, create_color_scheme } from "./common"
import { themes } from "./themes"
import { slugify } from "./utils/slugify"
import { theme_tokens } from "./theme/tokens/color_scheme"
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 clear_tokens(tokens_directory: string) {
if (!fs.existsSync(tokens_directory)) {
fs.mkdirSync(tokens_directory, { recursive: true })
} else {
for (const file of fs.readdirSync(tokens_directory)) {
if (file.endsWith(".json")) {
fs.unlinkSync(path.join(tokens_directory, file))
}
}
}
}
type TokenSet = {
id: string
name: string
selected_token_sets: { [key: string]: "enabled" }
}
function build_token_set_order(theme: ColorScheme[]): {
token_set_order: string[]
} {
const token_set_order: string[] = theme.map((scheme) =>
scheme.name.toLowerCase().replace(/\s+/g, "_")
)
return { token_set_order }
}
function build_themes_index(theme: ColorScheme[]): TokenSet[] {
const themes_index: TokenSet[] = theme.map((scheme, index) => {
const id = `${scheme.is_light ? "light" : "dark"}_${scheme.name
.toLowerCase()
.replace(/\s+/g, "_")}_${index}`
const selected_token_sets: { [key: string]: "enabled" } = {}
const token_set = scheme.name.toLowerCase().replace(/\s+/g, "_")
selected_token_sets[token_set] = "enabled"
return {
id,
name: `${scheme.name} - ${scheme.is_light ? "Light" : "Dark"}`,
selected_token_sets,
}
})
return themes_index
}
function write_tokens(themes: ColorScheme[], tokens_directory: string) {
clear_tokens(tokens_directory)
for (const theme of themes) {
const file_name = slugify(theme.name) + ".json"
const tokens = theme_tokens(theme)
const tokens_json = JSON.stringify(tokens, null, 2)
const out_path = path.join(tokens_directory, file_name)
fs.writeFileSync(out_path, tokens_json, { mode: 0o644 })
console.log(`- ${out_path} created`)
}
const theme_index_data = build_themes_index(themes)
const themes_json = JSON.stringify(theme_index_data, null, 2)
fs.writeFileSync(TOKENS_FILE, themes_json, { mode: 0o644 })
console.log(`- ${TOKENS_FILE} created`)
const token_set_order_data = build_token_set_order(themes)
const metadata_json = JSON.stringify(token_set_order_data, null, 2)
fs.writeFileSync(METADATA_FILE, metadata_json, { mode: 0o644 })
console.log(`- ${METADATA_FILE} created`)
}
const all_themes: ColorScheme[] = themes.map((theme) =>
create_color_scheme(theme)
)
write_tokens(all_themes, TOKENS_DIRECTORY)

62
styles/src/build_types.ts Normal file
View File

@ -0,0 +1,62 @@
import * as fs from "fs/promises"
import * as fsSync from "fs"
import * as path from "path"
import { compile } from "json-schema-to-typescript"
const BANNER = `/*
* This file is autogenerated
*/\n\n`
const dirname = __dirname
async function main() {
const schemas_path = path.join(dirname, "../../", "crates/theme/schemas")
const schema_files = (await fs.readdir(schemas_path)).filter((x) =>
x.endsWith(".json")
)
const compiled_types = new Set()
for (const filename of schema_files) {
const file_path = path.join(schemas_path, filename)
const file_contents = await fs.readFile(file_path)
const schema = JSON.parse(file_contents.toString())
const compiled = await compile(schema, schema.title, {
bannerComment: "",
})
const each_type = compiled.split("export")
for (const type of each_type) {
if (!type) {
continue
}
compiled_types.add("export " + type.trim())
}
}
const output = BANNER + Array.from(compiled_types).join("\n\n")
const output_path = path.join(dirname, "../../styles/src/types/zed.ts")
try {
const existing = await fs.readFile(output_path)
if (existing.toString() == output) {
// Skip writing if it hasn't changed
console.log("Schemas are up to date")
return
}
} catch (e) {
if (e.code !== "ENOENT") {
throw e
}
}
const types_dic = path.dirname(output_path)
if (!fsSync.existsSync(types_dic)) {
await fs.mkdir(types_dic)
}
await fs.writeFile(output_path, output)
console.log(`Wrote Typescript types to ${output_path}`)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})

View File

@ -2,42 +2,24 @@ import chroma from "chroma-js"
export * from "./theme"
export { chroma }
export const fontFamilies = {
export const font_families = {
sans: "Zed Sans",
mono: "Zed Mono",
}
export const fontSizes = {
"3xs": 8,
export const font_sizes = {
"2xs": 10,
xs: 12,
sm: 14,
md: 16,
lg: 18,
xl: 20,
}
export type FontWeight =
| "thin"
| "extra_light"
| "light"
| "normal"
| "medium"
| "semibold"
| "bold"
| "extra_bold"
| "black"
export type FontWeight = "normal" | "bold"
export const fontWeights: { [key: string]: FontWeight } = {
thin: "thin",
extra_light: "extra_light",
light: "light",
export const font_weights: { [key: string]: FontWeight } = {
normal: "normal",
medium: "medium",
semibold: "semibold",
bold: "bold",
extra_bold: "extra_bold",
black: "black",
}
export const sizes = {

View File

@ -1,6 +1,6 @@
import { ColorScheme } from "../common"
import { interactive, toggleable } from "../element"
import { background, foreground } from "../styleTree/components"
import { background, foreground } from "../style_tree/components"
import { ColorScheme } from "../theme/color_scheme"
export type Margin = {
top: number

View File

@ -1,11 +1,11 @@
import { ColorScheme } from "../common"
import { interactive, toggleable } from "../element"
import {
TextProperties,
background,
foreground,
text,
} from "../styleTree/components"
} from "../style_tree/components"
import { ColorScheme } from "../theme/color_scheme"
import { Margin } from "./icon_button"
interface TextButtonOptions {

View File

@ -8,7 +8,7 @@ import { describe, it, expect } from "vitest"
describe("interactive", () => {
it("creates an Interactive<Element> with base properties and states", () => {
const result = interactive({
base: { fontSize: 10, color: "#FFFFFF" },
base: { font_size: 10, color: "#FFFFFF" },
state: {
hovered: { color: "#EEEEEE" },
clicked: { color: "#CCCCCC" },
@ -16,25 +16,25 @@ describe("interactive", () => {
})
expect(result).toEqual({
default: { color: "#FFFFFF", fontSize: 10 },
hovered: { color: "#EEEEEE", fontSize: 10 },
clicked: { color: "#CCCCCC", fontSize: 10 },
default: { color: "#FFFFFF", font_size: 10 },
hovered: { color: "#EEEEEE", font_size: 10 },
clicked: { color: "#CCCCCC", font_size: 10 },
})
})
it("creates an Interactive<Element> with no base properties", () => {
const result = interactive({
state: {
default: { color: "#FFFFFF", fontSize: 10 },
default: { color: "#FFFFFF", font_size: 10 },
hovered: { color: "#EEEEEE" },
clicked: { color: "#CCCCCC" },
},
})
expect(result).toEqual({
default: { color: "#FFFFFF", fontSize: 10 },
hovered: { color: "#EEEEEE", fontSize: 10 },
clicked: { color: "#CCCCCC", fontSize: 10 },
default: { color: "#FFFFFF", font_size: 10 },
hovered: { color: "#EEEEEE", font_size: 10 },
clicked: { color: "#CCCCCC", font_size: 10 },
})
})
@ -48,7 +48,7 @@ describe("interactive", () => {
it("throws error when no other state besides default is present", () => {
const state = {
default: { fontSize: 10 },
default: { font_size: 10 },
}
expect(() => interactive({ state })).toThrow(NOT_ENOUGH_STATES_ERROR)

View File

@ -37,61 +37,61 @@ interface InteractiveProps<T> {
* @param state Object containing optional modified fields to be included in the resulting object for each state.
* @returns Interactive<T> object with fields from `base` and `state`.
*/
export function interactive<T extends Object>({
export function interactive<T extends object>({
base,
state,
}: InteractiveProps<T>): Interactive<T> {
if (!base && !state.default) throw new Error(NO_DEFAULT_OR_BASE_ERROR)
let defaultState: T
let default_state: T
if (state.default && base) {
defaultState = merge(base, state.default) as T
default_state = merge(base, state.default) as T
} else {
defaultState = base ? base : (state.default as T)
default_state = base ? base : (state.default as T)
}
let interactiveObj: Interactive<T> = {
default: defaultState,
const interactive_obj: Interactive<T> = {
default: default_state,
}
let stateCount = 0
let state_count = 0
if (state.hovered !== undefined) {
interactiveObj.hovered = merge(
interactiveObj.default,
interactive_obj.hovered = merge(
interactive_obj.default,
state.hovered
) as T
stateCount++
state_count++
}
if (state.clicked !== undefined) {
interactiveObj.clicked = merge(
interactiveObj.default,
interactive_obj.clicked = merge(
interactive_obj.default,
state.clicked
) as T
stateCount++
state_count++
}
if (state.selected !== undefined) {
interactiveObj.selected = merge(
interactiveObj.default,
interactive_obj.selected = merge(
interactive_obj.default,
state.selected
) as T
stateCount++
state_count++
}
if (state.disabled !== undefined) {
interactiveObj.disabled = merge(
interactiveObj.default,
interactive_obj.disabled = merge(
interactive_obj.default,
state.disabled
) as T
stateCount++
state_count++
}
if (stateCount < 1) {
if (state_count < 1) {
throw new Error(NOT_ENOUGH_STATES_ERROR)
}
return interactiveObj
return interactive_obj
}

View File

@ -35,13 +35,13 @@ export function toggleable<T extends object>(
if (!base && !state.inactive) throw new Error(NO_INACTIVE_OR_BASE_ERROR)
if (!state.active) throw new Error(NO_ACTIVE_ERROR)
const inactiveState = base
const inactive_state = base
? ((state.inactive ? merge(base, state.inactive) : base) as T)
: (state.inactive as T)
const toggleObj: Toggleable<T> = {
inactive: inactiveState,
const toggle_obj: Toggleable<T> = {
inactive: inactive_state,
active: merge(base ?? {}, state.active) as T,
}
return toggleObj
return toggle_obj
}

View File

@ -1,75 +0,0 @@
import contactFinder from "./contactFinder"
import contactsPopover from "./contactsPopover"
import commandPalette from "./commandPalette"
import editor from "./editor"
import projectPanel from "./projectPanel"
import search from "./search"
import picker from "./picker"
import workspace from "./workspace"
import contextMenu from "./contextMenu"
import sharedScreen from "./sharedScreen"
import projectDiagnostics from "./projectDiagnostics"
import contactNotification from "./contactNotification"
import updateNotification from "./updateNotification"
import simpleMessageNotification from "./simpleMessageNotification"
import projectSharedNotification from "./projectSharedNotification"
import tooltip from "./tooltip"
import terminal from "./terminal"
import contactList from "./contactList"
import toolbarDropdownMenu from "./toolbarDropdownMenu"
import incomingCallNotification from "./incomingCallNotification"
import { ColorScheme } from "../theme/colorScheme"
import feedback from "./feedback"
import welcome from "./welcome"
import copilot from "./copilot"
import assistant from "./assistant"
import { titlebar } from "./titlebar"
export default function app(colorScheme: ColorScheme): Object {
return {
meta: {
name: colorScheme.name,
isLight: colorScheme.isLight,
},
commandPalette: commandPalette(colorScheme),
contactNotification: contactNotification(colorScheme),
projectSharedNotification: projectSharedNotification(colorScheme),
incomingCallNotification: incomingCallNotification(colorScheme),
picker: picker(colorScheme),
workspace: workspace(colorScheme),
titlebar: titlebar(colorScheme),
copilot: copilot(colorScheme),
welcome: welcome(colorScheme),
contextMenu: contextMenu(colorScheme),
editor: editor(colorScheme),
projectDiagnostics: projectDiagnostics(colorScheme),
projectPanel: projectPanel(colorScheme),
contactsPopover: contactsPopover(colorScheme),
contactFinder: contactFinder(colorScheme),
contactList: contactList(colorScheme),
toolbarDropdownMenu: toolbarDropdownMenu(colorScheme),
search: search(colorScheme),
sharedScreen: sharedScreen(colorScheme),
updateNotification: updateNotification(colorScheme),
simpleMessageNotification: simpleMessageNotification(colorScheme),
tooltip: tooltip(colorScheme),
terminal: terminal(colorScheme),
assistant: assistant(colorScheme),
feedback: feedback(colorScheme),
colorScheme: {
...colorScheme,
players: Object.values(colorScheme.players),
ramps: {
neutral: colorScheme.ramps.neutral.colors(100, "hex"),
red: colorScheme.ramps.red.colors(100, "hex"),
orange: colorScheme.ramps.orange.colors(100, "hex"),
yellow: colorScheme.ramps.yellow.colors(100, "hex"),
green: colorScheme.ramps.green.colors(100, "hex"),
cyan: colorScheme.ramps.cyan.colors(100, "hex"),
blue: colorScheme.ramps.blue.colors(100, "hex"),
violet: colorScheme.ramps.violet.colors(100, "hex"),
magenta: colorScheme.ramps.magenta.colors(100, "hex"),
},
},
}
}

View File

@ -1,70 +0,0 @@
import picker from "./picker"
import { ColorScheme } from "../theme/colorScheme"
import { background, border, foreground, text } from "./components"
export default function contactFinder(colorScheme: ColorScheme): any {
let layer = colorScheme.middle
const sideMargin = 6
const contactButton = {
background: background(layer, "variant"),
color: foreground(layer, "variant"),
iconWidth: 8,
buttonWidth: 16,
cornerRadius: 8,
}
const pickerStyle = picker(colorScheme)
const pickerInput = {
background: background(layer, "on"),
cornerRadius: 6,
text: text(layer, "mono"),
placeholderText: text(layer, "mono", "on", "disabled", { size: "xs" }),
selection: colorScheme.players[0],
border: border(layer),
padding: {
bottom: 4,
left: 8,
right: 8,
top: 4,
},
margin: {
left: sideMargin,
right: sideMargin,
},
}
return {
picker: {
emptyContainer: {},
item: {
...pickerStyle.item,
margin: { left: sideMargin, right: sideMargin },
},
noMatches: pickerStyle.noMatches,
inputEditor: pickerInput,
emptyInputEditor: pickerInput,
},
rowHeight: 28,
contactAvatar: {
cornerRadius: 10,
width: 18,
},
contactUsername: {
padding: {
left: 8,
},
},
contactButton: {
...contactButton,
hover: {
background: background(layer, "variant", "hovered"),
},
},
disabledContactButton: {
...contactButton,
background: background(layer, "disabled"),
color: foreground(layer, "disabled"),
},
}
}

View File

@ -1,53 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, foreground, text } from "./components"
import { interactive } from "../element"
const avatarSize = 12
const headerPadding = 8
export default function contactNotification(colorScheme: ColorScheme): Object {
let layer = colorScheme.lowest
return {
headerAvatar: {
height: avatarSize,
width: avatarSize,
cornerRadius: 6,
},
headerMessage: {
...text(layer, "sans", { size: "xs" }),
margin: { left: headerPadding, right: headerPadding },
},
headerHeight: 18,
bodyMessage: {
...text(layer, "sans", { size: "xs" }),
margin: { left: avatarSize + headerPadding, top: 6, bottom: 6 },
},
button: interactive({
base: {
...text(layer, "sans", "on", { size: "xs" }),
background: background(layer, "on"),
padding: 4,
cornerRadius: 6,
margin: { left: 6 },
},
state: {
hovered: {
background: background(layer, "on", "hovered"),
},
},
}),
dismissButton: {
default: {
color: foreground(layer, "variant"),
iconWidth: 8,
iconHeight: 8,
buttonWidth: 8,
buttonHeight: 8,
hover: {
color: foreground(layer, "hovered"),
},
},
},
}
}

View File

@ -1,16 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, text } from "./components"
export default function contactsPopover(colorScheme: ColorScheme) {
let layer = colorScheme.middle
const sidePadding = 12
return {
background: background(layer),
cornerRadius: 6,
padding: { top: 6, bottom: 6 },
shadow: colorScheme.popoverShadow,
border: border(layer),
width: 300,
height: 400,
}
}

View File

@ -1,67 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, borderColor, text } from "./components"
import { interactive, toggleable } from "../element"
export default function contextMenu(colorScheme: ColorScheme) {
let layer = colorScheme.middle
return {
background: background(layer),
cornerRadius: 10,
padding: 4,
shadow: colorScheme.popoverShadow,
border: border(layer),
keystrokeMargin: 30,
item: toggleable({
base: interactive({
base: {
iconSpacing: 8,
iconWidth: 14,
padding: { left: 6, right: 6, top: 2, bottom: 2 },
cornerRadius: 6,
label: text(layer, "sans", { size: "sm" }),
keystroke: {
...text(layer, "sans", "variant", {
size: "sm",
weight: "bold",
}),
padding: { left: 3, right: 3 },
},
},
state: {
hovered: {
background: background(layer, "hovered"),
label: text(layer, "sans", "hovered", { size: "sm" }),
keystroke: {
...text(layer, "sans", "hovered", {
size: "sm",
weight: "bold",
}),
padding: { left: 3, right: 3 },
},
},
clicked: {
background: background(layer, "pressed"),
},
},
}),
state: {
active: {
default: {
background: background(layer, "active"),
},
hovered: {
background: background(layer, "hovered"),
},
clicked: {
background: background(layer, "pressed"),
},
},
},
}),
separator: {
background: borderColor(layer),
margin: { top: 2, bottom: 2 },
},
}
}

View File

@ -1,314 +0,0 @@
import { withOpacity } from "../theme/color"
import { ColorScheme, Layer, StyleSets } from "../theme/colorScheme"
import { background, border, borderColor, foreground, text } from "./components"
import hoverPopover from "./hoverPopover"
import { buildSyntax } from "../theme/syntax"
import { interactive, toggleable } from "../element"
export default function editor(colorScheme: ColorScheme) {
const { isLight } = colorScheme
let layer = colorScheme.highest
const autocompleteItem = {
cornerRadius: 6,
padding: {
bottom: 2,
left: 6,
right: 6,
top: 2,
},
}
function diagnostic(layer: Layer, styleSet: StyleSets) {
return {
textScaleFactor: 0.857,
header: {
border: border(layer, {
top: true,
}),
},
message: {
text: text(layer, "sans", styleSet, "default", { size: "sm" }),
highlightText: text(layer, "sans", styleSet, "default", {
size: "sm",
weight: "bold",
}),
},
}
}
const syntax = buildSyntax(colorScheme)
return {
textColor: syntax.primary.color,
background: background(layer),
activeLineBackground: withOpacity(background(layer, "on"), 0.75),
highlightedLineBackground: background(layer, "on"),
// Inline autocomplete suggestions, Co-pilot suggestions, etc.
suggestion: syntax.predictive,
codeActions: {
indicator: toggleable({
base: interactive({
base: {
color: foreground(layer, "variant"),
},
state: {
hovered: {
color: foreground(layer, "variant", "hovered"),
},
clicked: {
color: foreground(layer, "variant", "pressed"),
},
},
}),
state: {
active: {
default: {
color: foreground(layer, "accent"),
},
hovered: {
color: foreground(layer, "accent", "hovered"),
},
clicked: {
color: foreground(layer, "accent", "pressed"),
},
},
},
}),
verticalScale: 0.55,
},
folds: {
iconMarginScale: 2.5,
foldedIcon: "icons/chevron_right_8.svg",
foldableIcon: "icons/chevron_down_8.svg",
indicator: toggleable({
base: interactive({
base: {
color: foreground(layer, "variant"),
},
state: {
hovered: {
color: foreground(layer, "on"),
},
clicked: {
color: foreground(layer, "base"),
},
},
}),
state: {
active: {
default: {
color: foreground(layer, "default"),
},
hovered: {
color: foreground(layer, "variant"),
},
},
},
}),
ellipses: {
textColor: colorScheme.ramps.neutral(0.71).hex(),
cornerRadiusFactor: 0.15,
background: {
// Copied from hover_popover highlight
default: {
color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex(),
},
hovered: {
color: colorScheme.ramps.neutral(0.5).alpha(0.5).hex(),
},
clicked: {
color: colorScheme.ramps.neutral(0.5).alpha(0.7).hex(),
},
},
},
foldBackground: foreground(layer, "variant"),
},
diff: {
deleted: isLight
? colorScheme.ramps.red(0.5).hex()
: colorScheme.ramps.red(0.4).hex(),
modified: isLight
? colorScheme.ramps.yellow(0.5).hex()
: colorScheme.ramps.yellow(0.5).hex(),
inserted: isLight
? colorScheme.ramps.green(0.4).hex()
: colorScheme.ramps.green(0.5).hex(),
removedWidthEm: 0.275,
widthEm: 0.15,
cornerRadius: 0.05,
},
/** Highlights matching occurrences of what is under the cursor
* as well as matched brackets
*/
documentHighlightReadBackground: withOpacity(
foreground(layer, "accent"),
0.1
),
documentHighlightWriteBackground: colorScheme.ramps
.neutral(0.5)
.alpha(0.4)
.hex(), // TODO: This was blend * 2
errorColor: background(layer, "negative"),
gutterBackground: background(layer),
gutterPaddingFactor: 3.5,
lineNumber: withOpacity(foreground(layer), 0.35),
lineNumberActive: foreground(layer),
renameFade: 0.6,
unnecessaryCodeFade: 0.5,
selection: colorScheme.players[0],
whitespace: colorScheme.ramps.neutral(0.5).hex(),
guestSelections: [
colorScheme.players[1],
colorScheme.players[2],
colorScheme.players[3],
colorScheme.players[4],
colorScheme.players[5],
colorScheme.players[6],
colorScheme.players[7],
],
autocomplete: {
background: background(colorScheme.middle),
cornerRadius: 8,
padding: 4,
margin: {
left: -14,
},
border: border(colorScheme.middle),
shadow: colorScheme.popoverShadow,
matchHighlight: foreground(colorScheme.middle, "accent"),
item: autocompleteItem,
hoveredItem: {
...autocompleteItem,
matchHighlight: foreground(
colorScheme.middle,
"accent",
"hovered"
),
background: background(colorScheme.middle, "hovered"),
},
selectedItem: {
...autocompleteItem,
matchHighlight: foreground(
colorScheme.middle,
"accent",
"active"
),
background: background(colorScheme.middle, "active"),
},
},
diagnosticHeader: {
background: background(colorScheme.middle),
iconWidthFactor: 1.5,
textScaleFactor: 0.857,
border: border(colorScheme.middle, {
bottom: true,
top: true,
}),
code: {
...text(colorScheme.middle, "mono", { size: "sm" }),
margin: {
left: 10,
},
},
source: {
text: text(colorScheme.middle, "sans", {
size: "sm",
weight: "bold",
}),
},
message: {
highlightText: text(colorScheme.middle, "sans", {
size: "sm",
weight: "bold",
}),
text: text(colorScheme.middle, "sans", { size: "sm" }),
},
},
diagnosticPathHeader: {
background: background(colorScheme.middle),
textScaleFactor: 0.857,
filename: text(colorScheme.middle, "mono", { size: "sm" }),
path: {
...text(colorScheme.middle, "mono", { size: "sm" }),
margin: {
left: 12,
},
},
},
errorDiagnostic: diagnostic(colorScheme.middle, "negative"),
warningDiagnostic: diagnostic(colorScheme.middle, "warning"),
informationDiagnostic: diagnostic(colorScheme.middle, "accent"),
hintDiagnostic: diagnostic(colorScheme.middle, "warning"),
invalidErrorDiagnostic: diagnostic(colorScheme.middle, "base"),
invalidHintDiagnostic: diagnostic(colorScheme.middle, "base"),
invalidInformationDiagnostic: diagnostic(colorScheme.middle, "base"),
invalidWarningDiagnostic: diagnostic(colorScheme.middle, "base"),
hoverPopover: hoverPopover(colorScheme),
linkDefinition: {
color: syntax.linkUri.color,
underline: syntax.linkUri.underline,
},
jumpIcon: interactive({
base: {
color: foreground(layer, "on"),
iconWidth: 20,
buttonWidth: 20,
cornerRadius: 6,
padding: {
top: 6,
bottom: 6,
left: 6,
right: 6,
},
},
state: {
hovered: {
background: background(layer, "on", "hovered"),
},
},
}),
scrollbar: {
width: 12,
minHeightFactor: 1.0,
track: {
border: border(layer, "variant", { left: true }),
},
thumb: {
background: withOpacity(background(layer, "inverted"), 0.3),
border: {
width: 1,
color: borderColor(layer, "variant"),
top: false,
right: true,
left: true,
bottom: false,
},
},
git: {
deleted: isLight
? withOpacity(colorScheme.ramps.red(0.5).hex(), 0.8)
: withOpacity(colorScheme.ramps.red(0.4).hex(), 0.8),
modified: isLight
? withOpacity(colorScheme.ramps.yellow(0.5).hex(), 0.8)
: withOpacity(colorScheme.ramps.yellow(0.4).hex(), 0.8),
inserted: isLight
? withOpacity(colorScheme.ramps.green(0.5).hex(), 0.8)
: withOpacity(colorScheme.ramps.green(0.4).hex(), 0.8),
},
},
compositionMark: {
underline: {
thickness: 1.0,
color: borderColor(layer),
},
},
syntax,
}
}

View File

@ -1,49 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, text } from "./components"
import { interactive } from "../element"
export default function feedback(colorScheme: ColorScheme) {
let layer = colorScheme.highest
return {
submit_button: interactive({
base: {
...text(layer, "mono", "on"),
background: background(layer, "on"),
cornerRadius: 6,
border: border(layer, "on"),
margin: {
right: 4,
},
padding: {
bottom: 2,
left: 10,
right: 10,
top: 2,
},
},
state: {
clicked: {
...text(layer, "mono", "on", "pressed"),
background: background(layer, "on", "pressed"),
border: border(layer, "on", "pressed"),
},
hovered: {
...text(layer, "mono", "on", "hovered"),
background: background(layer, "on", "hovered"),
border: border(layer, "on", "hovered"),
},
},
}),
button_margin: 8,
info_text_default: text(layer, "sans", "default", { size: "xs" }),
link_text_default: text(layer, "sans", "default", {
size: "xs",
underline: true,
}),
link_text_hover: text(layer, "sans", "hovered", {
size: "xs",
underline: true,
}),
}
}

View File

@ -1,46 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, foreground, text } from "./components"
export default function HoverPopover(colorScheme: ColorScheme) {
let layer = colorScheme.middle
let baseContainer = {
background: background(layer),
cornerRadius: 8,
padding: {
left: 8,
right: 8,
top: 4,
bottom: 4,
},
shadow: colorScheme.popoverShadow,
border: border(layer),
margin: {
left: -8,
},
}
return {
container: baseContainer,
infoContainer: {
...baseContainer,
background: background(layer, "accent"),
border: border(layer, "accent"),
},
warningContainer: {
...baseContainer,
background: background(layer, "warning"),
border: border(layer, "warning"),
},
errorContainer: {
...baseContainer,
background: background(layer, "negative"),
border: border(layer, "negative"),
},
blockStyle: {
padding: { top: 4 },
},
prose: text(layer, "sans", { size: "sm" }),
diagnosticSourceHighlight: { color: foreground(layer, "accent") },
highlight: colorScheme.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better
}
}

View File

@ -1,53 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, text } from "./components"
export default function incomingCallNotification(
colorScheme: ColorScheme
): Object {
let layer = colorScheme.middle
const avatarSize = 48
return {
windowHeight: 74,
windowWidth: 380,
background: background(layer),
callerContainer: {
padding: 12,
},
callerAvatar: {
height: avatarSize,
width: avatarSize,
cornerRadius: avatarSize / 2,
},
callerMetadata: {
margin: { left: 10 },
},
callerUsername: {
...text(layer, "sans", { size: "sm", weight: "bold" }),
margin: { top: -3 },
},
callerMessage: {
...text(layer, "sans", "variant", { size: "xs" }),
margin: { top: -3 },
},
worktreeRoots: {
...text(layer, "sans", "variant", { size: "xs", weight: "bold" }),
margin: { top: -3 },
},
buttonWidth: 96,
acceptButton: {
background: background(layer, "accent"),
border: border(layer, { left: true, bottom: true }),
...text(layer, "sans", "positive", {
size: "xs",
weight: "extra_bold",
}),
},
declineButton: {
border: border(layer, { left: true }),
...text(layer, "sans", "negative", {
size: "xs",
weight: "extra_bold",
}),
},
}
}

View File

@ -1,13 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, text } from "./components"
export default function projectDiagnostics(colorScheme: ColorScheme) {
let layer = colorScheme.highest
return {
background: background(layer),
tabIconSpacing: 4,
tabIconWidth: 13,
tabSummarySpacing: 10,
emptyMessage: text(layer, "sans", "variant", { size: "md" }),
}
}

View File

@ -1,188 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import {
Border,
TextStyle,
background,
border,
foreground,
text,
} from "./components"
import { interactive, toggleable } from "../element"
import merge from "ts-deepmerge"
export default function projectPanel(colorScheme: ColorScheme) {
const { isLight } = colorScheme
let layer = colorScheme.middle
type EntryStateProps = {
background?: string
border?: Border
text?: TextStyle
iconColor?: string
}
type EntryState = {
default: EntryStateProps
hovered?: EntryStateProps
clicked?: EntryStateProps
}
const entry = (unselected?: EntryState, selected?: EntryState) => {
const git_status = {
git: {
modified: isLight
? colorScheme.ramps.yellow(0.6).hex()
: colorScheme.ramps.yellow(0.5).hex(),
inserted: isLight
? colorScheme.ramps.green(0.45).hex()
: colorScheme.ramps.green(0.5).hex(),
conflict: isLight
? colorScheme.ramps.red(0.6).hex()
: colorScheme.ramps.red(0.5).hex(),
},
}
const base_properties = {
height: 22,
background: background(layer),
iconColor: foreground(layer, "variant"),
iconSize: 7,
iconSpacing: 5,
text: text(layer, "mono", "variant", { size: "sm" }),
status: {
...git_status,
},
}
const selectedStyle: EntryState | undefined = selected
? selected
: unselected
const unselected_default_style = merge(
base_properties,
unselected?.default ?? {},
{}
)
const unselected_hovered_style = merge(
base_properties,
unselected?.hovered ?? {},
{ background: background(layer, "variant", "hovered") }
)
const unselected_clicked_style = merge(
base_properties,
unselected?.clicked ?? {},
{ background: background(layer, "variant", "pressed") }
)
const selected_default_style = merge(
base_properties,
selectedStyle?.default ?? {},
{ background: background(layer) }
)
const selected_hovered_style = merge(
base_properties,
selectedStyle?.hovered ?? {},
{ background: background(layer, "variant", "hovered") }
)
const selected_clicked_style = merge(
base_properties,
selectedStyle?.clicked ?? {},
{ background: background(layer, "variant", "pressed") }
)
return toggleable({
state: {
inactive: interactive({
state: {
default: unselected_default_style,
hovered: unselected_hovered_style,
clicked: unselected_clicked_style,
},
}),
active: interactive({
state: {
default: selected_default_style,
hovered: selected_hovered_style,
clicked: selected_clicked_style,
},
}),
},
})
}
const defaultEntry = entry()
return {
openProjectButton: interactive({
base: {
background: background(layer),
border: border(layer, "active"),
cornerRadius: 4,
margin: {
top: 16,
left: 16,
right: 16,
},
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
...text(layer, "sans", "default", { size: "sm" }),
},
state: {
hovered: {
...text(layer, "sans", "default", { size: "sm" }),
background: background(layer, "hovered"),
border: border(layer, "active"),
},
clicked: {
...text(layer, "sans", "default", { size: "sm" }),
background: background(layer, "pressed"),
border: border(layer, "active"),
},
},
}),
background: background(layer),
padding: { left: 6, right: 6, top: 0, bottom: 6 },
indentWidth: 12,
entry: defaultEntry,
draggedEntry: {
...defaultEntry.inactive.default,
text: text(layer, "mono", "on", { size: "sm" }),
background: withOpacity(background(layer, "on"), 0.9),
border: border(layer),
},
ignoredEntry: entry(
{
default: {
text: text(layer, "mono", "disabled"),
},
},
{
default: {
iconColor: foreground(layer, "variant"),
},
}
),
cutEntry: entry(
{
default: {
text: text(layer, "mono", "disabled"),
},
},
{
default: {
background: background(layer, "active"),
text: text(layer, "mono", "disabled", { size: "sm" }),
},
}
),
filenameEditor: {
background: background(layer, "on"),
text: text(layer, "mono", "on", { size: "sm" }),
selection: colorScheme.players[0],
},
}
}

View File

@ -1,54 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, text } from "./components"
export default function projectSharedNotification(
colorScheme: ColorScheme
): Object {
let layer = colorScheme.middle
const avatarSize = 48
return {
windowHeight: 74,
windowWidth: 380,
background: background(layer),
ownerContainer: {
padding: 12,
},
ownerAvatar: {
height: avatarSize,
width: avatarSize,
cornerRadius: avatarSize / 2,
},
ownerMetadata: {
margin: { left: 10 },
},
ownerUsername: {
...text(layer, "sans", { size: "sm", weight: "bold" }),
margin: { top: -3 },
},
message: {
...text(layer, "sans", "variant", { size: "xs" }),
margin: { top: -3 },
},
worktreeRoots: {
...text(layer, "sans", "variant", { size: "xs", weight: "bold" }),
margin: { top: -3 },
},
buttonWidth: 96,
openButton: {
background: background(layer, "accent"),
border: border(layer, { left: true, bottom: true }),
...text(layer, "sans", "accent", {
size: "xs",
weight: "extra_bold",
}),
},
dismissButton: {
border: border(layer, { left: true }),
...text(layer, "sans", "variant", {
size: "xs",
weight: "extra_bold",
}),
},
}
}

View File

@ -1,135 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import { background, border, foreground, text } from "./components"
import { interactive, toggleable } from "../element"
export default function search(colorScheme: ColorScheme) {
let layer = colorScheme.highest
// Search input
const editor = {
background: background(layer),
cornerRadius: 8,
minWidth: 200,
maxWidth: 500,
placeholderText: text(layer, "mono", "disabled"),
selection: colorScheme.players[0],
text: text(layer, "mono", "default"),
border: border(layer),
margin: {
right: 12,
},
padding: {
top: 3,
bottom: 3,
left: 12,
right: 8,
},
}
const includeExcludeEditor = {
...editor,
minWidth: 100,
maxWidth: 250,
}
return {
// TODO: Add an activeMatchBackground on the rust side to differentiate between active and inactive
matchBackground: withOpacity(foreground(layer, "accent"), 0.4),
optionButton: toggleable({
base: interactive({
base: {
...text(layer, "mono", "on"),
background: background(layer, "on"),
cornerRadius: 6,
border: border(layer, "on"),
margin: {
right: 4,
},
padding: {
bottom: 2,
left: 10,
right: 10,
top: 2,
},
},
state: {
hovered: {
...text(layer, "mono", "on", "hovered"),
background: background(layer, "on", "hovered"),
border: border(layer, "on", "hovered"),
},
clicked: {
...text(layer, "mono", "on", "pressed"),
background: background(layer, "on", "pressed"),
border: border(layer, "on", "pressed"),
},
},
}),
state: {
active: {
default: {
...text(layer, "mono", "accent"),
},
hovered: {
...text(layer, "mono", "accent", "hovered"),
},
clicked: {
...text(layer, "mono", "accent", "pressed"),
},
},
},
}),
editor,
invalidEditor: {
...editor,
border: border(layer, "negative"),
},
includeExcludeEditor,
invalidIncludeExcludeEditor: {
...includeExcludeEditor,
border: border(layer, "negative"),
},
matchIndex: {
...text(layer, "mono", "variant"),
padding: {
left: 6,
},
},
optionButtonGroup: {
padding: {
left: 12,
right: 12,
},
},
includeExcludeInputs: {
...text(layer, "mono", "variant"),
padding: {
right: 6,
},
},
resultsStatus: {
...text(layer, "mono", "on"),
size: 18,
},
dismissButton: interactive({
base: {
color: foreground(layer, "variant"),
iconWidth: 12,
buttonWidth: 14,
padding: {
left: 10,
right: 10,
},
},
state: {
hovered: {
color: foreground(layer, "hovered"),
},
clicked: {
color: foreground(layer, "pressed"),
},
},
}),
}
}

View File

@ -1,9 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background } from "./components"
export default function sharedScreen(colorScheme: ColorScheme) {
let layer = colorScheme.highest
return {
background: background(layer),
}
}

View File

@ -1,53 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, foreground, text } from "./components"
import { interactive } from "../element"
const headerPadding = 8
export default function simpleMessageNotification(
colorScheme: ColorScheme
): Object {
let layer = colorScheme.middle
return {
message: {
...text(layer, "sans", { size: "xs" }),
margin: { left: headerPadding, right: headerPadding },
},
actionMessage: interactive({
base: {
...text(layer, "sans", { size: "xs" }),
border: border(layer, "active"),
cornerRadius: 4,
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
margin: { left: headerPadding, top: 6, bottom: 6 },
},
state: {
hovered: {
...text(layer, "sans", "default", { size: "xs" }),
background: background(layer, "hovered"),
border: border(layer, "active"),
},
},
}),
dismissButton: interactive({
base: {
color: foreground(layer),
iconWidth: 8,
iconHeight: 8,
buttonWidth: 8,
buttonHeight: 8,
},
state: {
hovered: {
color: foreground(layer, "hovered"),
},
},
}),
}
}

View File

@ -1,52 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
export default function terminal(colorScheme: ColorScheme) {
/**
* Colors are controlled per-cell in the terminal grid.
* Cells can be set to any of these more 'theme-capable' colors
* or can be set directly with RGB values.
* Here are the common interpretations of these names:
* https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
*/
return {
black: colorScheme.ramps.neutral(0).hex(),
red: colorScheme.ramps.red(0.5).hex(),
green: colorScheme.ramps.green(0.5).hex(),
yellow: colorScheme.ramps.yellow(0.5).hex(),
blue: colorScheme.ramps.blue(0.5).hex(),
magenta: colorScheme.ramps.magenta(0.5).hex(),
cyan: colorScheme.ramps.cyan(0.5).hex(),
white: colorScheme.ramps.neutral(1).hex(),
brightBlack: colorScheme.ramps.neutral(0.4).hex(),
brightRed: colorScheme.ramps.red(0.25).hex(),
brightGreen: colorScheme.ramps.green(0.25).hex(),
brightYellow: colorScheme.ramps.yellow(0.25).hex(),
brightBlue: colorScheme.ramps.blue(0.25).hex(),
brightMagenta: colorScheme.ramps.magenta(0.25).hex(),
brightCyan: colorScheme.ramps.cyan(0.25).hex(),
brightWhite: colorScheme.ramps.neutral(1).hex(),
/**
* Default color for characters
*/
foreground: colorScheme.ramps.neutral(1).hex(),
/**
* Default color for the rectangle background of a cell
*/
background: colorScheme.ramps.neutral(0).hex(),
modalBackground: colorScheme.ramps.neutral(0.1).hex(),
/**
* Default color for the cursor
*/
cursor: colorScheme.players[0].cursor,
dimBlack: colorScheme.ramps.neutral(1).hex(),
dimRed: colorScheme.ramps.red(0.75).hex(),
dimGreen: colorScheme.ramps.green(0.75).hex(),
dimYellow: colorScheme.ramps.yellow(0.75).hex(),
dimBlue: colorScheme.ramps.blue(0.75).hex(),
dimMagenta: colorScheme.ramps.magenta(0.75).hex(),
dimCyan: colorScheme.ramps.cyan(0.75).hex(),
dimWhite: colorScheme.ramps.neutral(0.6).hex(),
brightForeground: colorScheme.ramps.neutral(1).hex(),
dimForeground: colorScheme.ramps.neutral(0).hex(),
}
}

View File

@ -1,47 +0,0 @@
import merge from "ts-deepmerge"
type ToggleState = "inactive" | "active"
type Toggleable<T> = Record<ToggleState, T>
const NO_INACTIVE_OR_BASE_ERROR =
"A toggleable object must have an inactive state, or a base property."
const NO_ACTIVE_ERROR = "A toggleable object must have an active state."
interface ToggleableProps<T> {
base?: T
state: Partial<Record<ToggleState, T>>
}
/**
* Helper function for creating Toggleable objects.
* @template T The type of the object being toggled.
* @param props Object containing the base (inactive) state and state modifications to create the active state.
* @returns A Toggleable object containing both the inactive and active states.
* @example
* ```
* toggleable({
* base: { background: "#000000", text: "#CCCCCC" },
* state: { active: { text: "#CCCCCC" } },
* })
* ```
*/
export function toggleable<T extends object>(
props: ToggleableProps<T>
): Toggleable<T> {
const { base, state } = props
if (!base && !state.inactive) throw new Error(NO_INACTIVE_OR_BASE_ERROR)
if (!state.active) throw new Error(NO_ACTIVE_ERROR)
const inactiveState = base
? ((state.inactive ? merge(base, state.inactive) : base) as T)
: (state.inactive as T)
const toggleObj: Toggleable<T> = {
inactive: inactiveState,
active: merge(base ?? {}, state.active) as T,
}
return toggleObj
}

View File

@ -1,64 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, text } from "./components"
import { interactive, toggleable } from "../element"
export default function dropdownMenu(colorScheme: ColorScheme) {
let layer = colorScheme.middle
return {
rowHeight: 30,
background: background(layer),
border: border(layer),
shadow: colorScheme.popoverShadow,
header: interactive({
base: {
...text(layer, "sans", { size: "sm" }),
secondaryText: text(layer, "sans", {
size: "sm",
color: "#aaaaaa",
}),
secondaryTextSpacing: 10,
padding: { left: 8, right: 8, top: 2, bottom: 2 },
cornerRadius: 6,
background: background(layer, "on"),
},
state: {
hovered: {
background: background(layer, "hovered"),
},
clicked: {
background: background(layer, "pressed"),
},
},
}),
sectionHeader: {
...text(layer, "sans", { size: "sm" }),
padding: { left: 8, right: 8, top: 8, bottom: 8 },
},
item: toggleable({
base: interactive({
base: {
...text(layer, "sans", { size: "sm" }),
secondaryTextSpacing: 10,
secondaryText: text(layer, "sans", { size: "sm" }),
padding: { left: 18, right: 18, top: 2, bottom: 2 },
},
state: {
hovered: {
background: background(layer, "hovered"),
...text(layer, "sans", "hovered", { size: "sm" }),
},
},
}),
state: {
active: {
default: {
background: background(layer, "active"),
},
hovered: {
background: background(layer, "hovered"),
},
},
},
}),
}
}

View File

@ -1,23 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, text } from "./components"
export default function tooltip(colorScheme: ColorScheme) {
let layer = colorScheme.middle
return {
background: background(layer),
border: border(layer),
padding: { top: 4, bottom: 4, left: 8, right: 8 },
margin: { top: 6, left: 6 },
shadow: colorScheme.popoverShadow,
cornerRadius: 6,
text: text(layer, "sans", { size: "xs" }),
keystroke: {
background: background(layer, "on"),
cornerRadius: 4,
margin: { left: 6 },
padding: { left: 4, right: 4 },
...text(layer, "mono", "on", { size: "xs", weight: "bold" }),
},
maxTextWidth: 200,
}
}

View File

@ -1,40 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { foreground, text } from "./components"
import { interactive } from "../element"
const headerPadding = 8
export default function updateNotification(colorScheme: ColorScheme): Object {
let layer = colorScheme.middle
return {
message: {
...text(layer, "sans", { size: "xs" }),
margin: { left: headerPadding, right: headerPadding },
},
actionMessage: interactive({
base: {
...text(layer, "sans", { size: "xs" }),
margin: { left: headerPadding, top: 6, bottom: 6 },
},
state: {
hovered: {
color: foreground(layer, "hovered"),
},
},
}),
dismissButton: interactive({
base: {
color: foreground(layer),
iconWidth: 8,
iconHeight: 8,
buttonWidth: 8,
buttonHeight: 8,
},
state: {
hovered: {
color: foreground(layer, "hovered"),
},
},
}),
}
}

View File

@ -1,134 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import {
border,
background,
foreground,
text,
TextProperties,
svg,
} from "./components"
import { interactive } from "../element"
export default function welcome(colorScheme: ColorScheme) {
let layer = colorScheme.highest
let checkboxBase = {
cornerRadius: 4,
padding: {
left: 3,
right: 3,
top: 3,
bottom: 3,
},
// shadow: colorScheme.popoverShadow,
border: border(layer),
margin: {
right: 8,
top: 5,
bottom: 5,
},
}
let interactive_text_size: TextProperties = { size: "sm" }
return {
pageWidth: 320,
logo: svg(foreground(layer, "default"), "icons/logo_96.svg", 64, 64),
logoSubheading: {
...text(layer, "sans", "variant", { size: "md" }),
margin: {
top: 10,
bottom: 7,
},
},
buttonGroup: {
margin: {
top: 8,
bottom: 16,
},
},
headingGroup: {
margin: {
top: 8,
bottom: 12,
},
},
checkboxGroup: {
border: border(layer, "variant"),
background: withOpacity(background(layer, "hovered"), 0.25),
cornerRadius: 4,
padding: {
left: 12,
top: 2,
bottom: 2,
},
},
button: interactive({
base: {
background: background(layer),
border: border(layer, "active"),
cornerRadius: 4,
margin: {
top: 4,
bottom: 4,
},
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
...text(layer, "sans", "default", interactive_text_size),
},
state: {
hovered: {
...text(layer, "sans", "default", interactive_text_size),
background: background(layer, "hovered"),
},
},
}),
usageNote: {
...text(layer, "sans", "variant", { size: "2xs" }),
padding: {
top: -4,
},
},
checkboxContainer: {
margin: {
top: 4,
},
padding: {
bottom: 8,
},
},
checkbox: {
label: {
...text(layer, "sans", interactive_text_size),
// Also supports margin, container, border, etc.
},
icon: svg(foreground(layer, "on"), "icons/check_12.svg", 12, 12),
default: {
...checkboxBase,
background: background(layer, "default"),
border: border(layer, "active"),
},
checked: {
...checkboxBase,
background: background(layer, "hovered"),
border: border(layer, "active"),
},
hovered: {
...checkboxBase,
background: background(layer, "hovered"),
border: border(layer, "active"),
},
hoveredAndChecked: {
...checkboxBase,
background: background(layer, "hovered"),
border: border(layer, "active"),
},
},
}
}

View File

@ -1,200 +0,0 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import {
background,
border,
borderColor,
foreground,
svg,
text,
} from "./components"
import statusBar from "./statusBar"
import tabBar from "./tabBar"
import { interactive } from "../element"
import { titlebar } from "./titlebar"
export default function workspace(colorScheme: ColorScheme) {
const layer = colorScheme.lowest
const isLight = colorScheme.isLight
return {
background: background(colorScheme.lowest),
blankPane: {
logoContainer: {
width: 256,
height: 256,
},
logo: svg(
withOpacity("#000000", colorScheme.isLight ? 0.6 : 0.8),
"icons/logo_96.svg",
256,
256
),
logoShadow: svg(
withOpacity(
colorScheme.isLight
? "#FFFFFF"
: colorScheme.lowest.base.default.background,
colorScheme.isLight ? 1 : 0.6
),
"icons/logo_96.svg",
256,
256
),
keyboardHints: {
margin: {
top: 96,
},
cornerRadius: 4,
},
keyboardHint: interactive({
base: {
...text(layer, "sans", "variant", { size: "sm" }),
padding: {
top: 3,
left: 8,
right: 8,
bottom: 3,
},
cornerRadius: 8,
},
state: {
hovered: {
...text(layer, "sans", "active", { size: "sm" }),
},
},
}),
keyboardHintWidth: 320,
},
joiningProjectAvatar: {
cornerRadius: 40,
width: 80,
},
joiningProjectMessage: {
padding: 12,
...text(layer, "sans", { size: "lg" }),
},
externalLocationMessage: {
background: background(colorScheme.middle, "accent"),
border: border(colorScheme.middle, "accent"),
cornerRadius: 6,
padding: 12,
margin: { bottom: 8, right: 8 },
...text(colorScheme.middle, "sans", "accent", { size: "xs" }),
},
leaderBorderOpacity: 0.7,
leaderBorderWidth: 2.0,
tabBar: tabBar(colorScheme),
modal: {
margin: {
bottom: 52,
top: 52,
},
cursor: "Arrow",
},
zoomedBackground: {
cursor: "Arrow",
background: isLight
? withOpacity(background(colorScheme.lowest), 0.8)
: withOpacity(background(colorScheme.highest), 0.6),
},
zoomedPaneForeground: {
margin: 16,
shadow: colorScheme.modalShadow,
border: border(colorScheme.lowest, { overlay: true }),
},
zoomedPanelForeground: {
margin: 16,
border: border(colorScheme.lowest, { overlay: true }),
},
dock: {
left: {
border: border(layer, { right: true }),
},
bottom: {
border: border(layer, { top: true }),
},
right: {
border: border(layer, { left: true }),
},
},
paneDivider: {
color: borderColor(layer),
width: 1,
},
statusBar: statusBar(colorScheme),
titlebar: titlebar(colorScheme),
toolbar: {
height: 34,
background: background(colorScheme.highest),
border: border(colorScheme.highest, { bottom: true }),
itemSpacing: 8,
navButton: interactive({
base: {
color: foreground(colorScheme.highest, "on"),
iconWidth: 12,
buttonWidth: 24,
cornerRadius: 6,
},
state: {
hovered: {
color: foreground(colorScheme.highest, "on", "hovered"),
background: background(
colorScheme.highest,
"on",
"hovered"
),
},
disabled: {
color: foreground(
colorScheme.highest,
"on",
"disabled"
),
},
},
}),
padding: { left: 8, right: 8, top: 4, bottom: 4 },
},
breadcrumbHeight: 24,
breadcrumbs: interactive({
base: {
...text(colorScheme.highest, "sans", "variant"),
cornerRadius: 6,
padding: {
left: 6,
right: 6,
},
},
state: {
hovered: {
color: foreground(colorScheme.highest, "on", "hovered"),
background: background(
colorScheme.highest,
"on",
"hovered"
),
},
},
}),
disconnectedOverlay: {
...text(layer, "sans"),
background: withOpacity(background(layer), 0.8),
},
notification: {
margin: { top: 10 },
background: background(colorScheme.middle),
cornerRadius: 6,
padding: 12,
border: border(colorScheme.middle),
shadow: colorScheme.popoverShadow,
},
notifications: {
width: 400,
margin: { right: 10, bottom: 10 },
},
dropTargetOverlayColor: withOpacity(foreground(layer, "variant"), 0.5),
}
}

View File

@ -0,0 +1,75 @@
import contact_finder from "./contact_finder"
import contacts_popover from "./contacts_popover"
import command_palette from "./command_palette"
import project_panel from "./project_panel"
import search from "./search"
import picker from "./picker"
import workspace from "./workspace"
import context_menu from "./context_menu"
import shared_screen from "./shared_screen"
import project_diagnostics from "./project_diagnostics"
import contact_notification from "./contact_notification"
import update_notification from "./update_notification"
import simple_message_notification from "./simple_message_notification"
import project_shared_notification from "./project_shared_notification"
import tooltip from "./tooltip"
import terminal from "./terminal"
import contact_list from "./contact_list"
import toolbar_dropdown_menu from "./toolbar_dropdown_menu"
import incoming_call_notification from "./incoming_call_notification"
import { ColorScheme } from "../theme/color_scheme"
import welcome from "./welcome"
import copilot from "./copilot"
import assistant from "./assistant"
import { titlebar } from "./titlebar"
import editor from "./editor"
import feedback from "./feedback"
export default function app(theme: ColorScheme): any {
return {
meta: {
name: theme.name,
is_light: theme.is_light,
},
command_palette: command_palette(theme),
contact_notification: contact_notification(theme),
project_shared_notification: project_shared_notification(theme),
incoming_call_notification: incoming_call_notification(theme),
picker: picker(theme),
workspace: workspace(theme),
titlebar: titlebar(theme),
copilot: copilot(theme),
welcome: welcome(theme),
context_menu: context_menu(theme),
editor: editor(theme),
project_diagnostics: project_diagnostics(theme),
project_panel: project_panel(theme),
contacts_popover: contacts_popover(theme),
contact_finder: contact_finder(theme),
contact_list: contact_list(theme),
toolbar_dropdown_menu: toolbar_dropdown_menu(theme),
search: search(theme),
shared_screen: shared_screen(theme),
update_notification: update_notification(theme),
simple_message_notification: simple_message_notification(theme),
tooltip: tooltip(theme),
terminal: terminal(theme),
assistant: assistant(theme),
feedback: feedback(theme),
color_scheme: {
...theme,
players: Object.values(theme.players),
ramps: {
neutral: theme.ramps.neutral.colors(100, "hex"),
red: theme.ramps.red.colors(100, "hex"),
orange: theme.ramps.orange.colors(100, "hex"),
yellow: theme.ramps.yellow.colors(100, "hex"),
green: theme.ramps.green.colors(100, "hex"),
cyan: theme.ramps.cyan.colors(100, "hex"),
blue: theme.ramps.blue.colors(100, "hex"),
violet: theme.ramps.violet.colors(100, "hex"),
magenta: theme.ramps.magenta.colors(100, "hex"),
},
},
}
}

View File

@ -1,23 +1,21 @@
import { ColorScheme } from "../theme/colorScheme"
import { ColorScheme } from "../theme/color_scheme"
import { text, border, background, foreground } from "./components"
import editor from "./editor"
import { interactive } from "../element"
export default function assistant(colorScheme: ColorScheme) {
const layer = colorScheme.highest
export default function assistant(theme: ColorScheme): any {
return {
container: {
background: editor(colorScheme).background,
background: background(theme.highest),
padding: { left: 12 },
},
messageHeader: {
message_header: {
margin: { bottom: 6, top: 6 },
background: editor(colorScheme).background,
background: background(theme.highest),
},
hamburgerButton: interactive({
hamburger_button: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
color: foreground(theme.highest, "variant"),
asset: "icons/hamburger_15.svg",
dimensions: {
width: 15,
@ -31,15 +29,15 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.highest, "hovered"),
},
},
},
}),
splitButton: interactive({
split_button: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
color: foreground(theme.highest, "variant"),
asset: "icons/split_message_15.svg",
dimensions: {
width: 15,
@ -53,15 +51,15 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.highest, "hovered"),
},
},
},
}),
quoteButton: interactive({
quote_button: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
color: foreground(theme.highest, "variant"),
asset: "icons/quote_15.svg",
dimensions: {
width: 15,
@ -75,15 +73,15 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.highest, "hovered"),
},
},
},
}),
assistButton: interactive({
assist_button: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
color: foreground(theme.highest, "variant"),
asset: "icons/assist_15.svg",
dimensions: {
width: 15,
@ -97,15 +95,15 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.highest, "hovered"),
},
},
},
}),
zoomInButton: interactive({
zoom_in_button: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
color: foreground(theme.highest, "variant"),
asset: "icons/maximize_8.svg",
dimensions: {
width: 12,
@ -119,15 +117,15 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.highest, "hovered"),
},
},
},
}),
zoomOutButton: interactive({
zoom_out_button: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
color: foreground(theme.highest, "variant"),
asset: "icons/minimize_8.svg",
dimensions: {
width: 12,
@ -141,15 +139,15 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.highest, "hovered"),
},
},
},
}),
plusButton: interactive({
plus_button: interactive({
base: {
icon: {
color: foreground(layer, "variant"),
color: foreground(theme.highest, "variant"),
asset: "icons/plus_12.svg",
dimensions: {
width: 12,
@ -163,109 +161,109 @@ export default function assistant(colorScheme: ColorScheme) {
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.highest, "hovered"),
},
},
},
}),
title: {
...text(layer, "sans", "default", { size: "sm" }),
...text(theme.highest, "sans", "default", { size: "sm" }),
},
savedConversation: {
saved_conversation: {
container: interactive({
base: {
background: background(layer, "on"),
background: background(theme.highest, "on"),
padding: { top: 4, bottom: 4 },
},
state: {
hovered: {
background: background(layer, "on", "hovered"),
background: background(theme.highest, "on", "hovered"),
},
},
}),
savedAt: {
saved_at: {
margin: { left: 8 },
...text(layer, "sans", "default", { size: "xs" }),
...text(theme.highest, "sans", "default", { size: "xs" }),
},
title: {
margin: { left: 16 },
...text(layer, "sans", "default", {
...text(theme.highest, "sans", "default", {
size: "sm",
weight: "bold",
}),
},
},
userSender: {
user_sender: {
default: {
...text(layer, "sans", "default", {
...text(theme.highest, "sans", "default", {
size: "sm",
weight: "bold",
}),
},
},
assistantSender: {
assistant_sender: {
default: {
...text(layer, "sans", "accent", {
...text(theme.highest, "sans", "accent", {
size: "sm",
weight: "bold",
}),
},
},
systemSender: {
system_sender: {
default: {
...text(layer, "sans", "variant", {
...text(theme.highest, "sans", "variant", {
size: "sm",
weight: "bold",
}),
},
},
sentAt: {
sent_at: {
margin: { top: 2, left: 8 },
...text(layer, "sans", "default", { size: "2xs" }),
...text(theme.highest, "sans", "default", { size: "2xs" }),
},
model: interactive({
base: {
background: background(layer, "on"),
background: background(theme.highest, "on"),
margin: { left: 12, right: 12, top: 12 },
padding: 4,
cornerRadius: 4,
...text(layer, "sans", "default", { size: "xs" }),
corner_radius: 4,
...text(theme.highest, "sans", "default", { size: "xs" }),
},
state: {
hovered: {
background: background(layer, "on", "hovered"),
border: border(layer, "on", { overlay: true }),
background: background(theme.highest, "on", "hovered"),
border: border(theme.highest, "on", { overlay: true }),
},
},
}),
remainingTokens: {
background: background(layer, "on"),
remaining_tokens: {
background: background(theme.highest, "on"),
margin: { top: 12, right: 24 },
padding: 4,
cornerRadius: 4,
...text(layer, "sans", "positive", { size: "xs" }),
corner_radius: 4,
...text(theme.highest, "sans", "positive", { size: "xs" }),
},
noRemainingTokens: {
background: background(layer, "on"),
no_remaining_tokens: {
background: background(theme.highest, "on"),
margin: { top: 12, right: 24 },
padding: 4,
cornerRadius: 4,
...text(layer, "sans", "negative", { size: "xs" }),
corner_radius: 4,
...text(theme.highest, "sans", "negative", { size: "xs" }),
},
errorIcon: {
error_icon: {
margin: { left: 8 },
color: foreground(layer, "negative"),
color: foreground(theme.highest, "negative"),
width: 12,
},
apiKeyEditor: {
background: background(layer, "on"),
cornerRadius: 6,
text: text(layer, "mono", "on"),
placeholderText: text(layer, "mono", "on", "disabled", {
api_key_editor: {
background: background(theme.highest, "on"),
corner_radius: 6,
text: text(theme.highest, "mono", "on"),
placeholder_text: text(theme.highest, "mono", "on", "disabled", {
size: "xs",
}),
selection: colorScheme.players[0],
border: border(layer, "on"),
selection: theme.players[0],
border: border(theme.highest, "on"),
padding: {
bottom: 4,
left: 8,
@ -273,9 +271,9 @@ export default function assistant(colorScheme: ColorScheme) {
top: 4,
},
},
apiKeyPrompt: {
api_key_prompt: {
padding: 10,
...text(layer, "sans", "default", { size: "xs" }),
...text(theme.highest, "sans", "default", { size: "xs" }),
},
}
}

View File

@ -1,16 +1,16 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import { ColorScheme } from "../theme/color_scheme"
import { with_opacity } from "../theme/color"
import { text, background } from "./components"
import { toggleable } from "../element"
export default function commandPalette(colorScheme: ColorScheme) {
let layer = colorScheme.highest
export default function command_palette(theme: ColorScheme): any {
const key = toggleable({
base: {
text: text(layer, "mono", "variant", "default", { size: "xs" }),
cornerRadius: 2,
background: background(layer, "on"),
text: text(theme.highest, "mono", "variant", "default", {
size: "xs",
}),
corner_radius: 2,
background: background(theme.highest, "on"),
padding: {
top: 1,
bottom: 1,
@ -25,14 +25,16 @@ export default function commandPalette(colorScheme: ColorScheme) {
},
state: {
active: {
text: text(layer, "mono", "on", "default", { size: "xs" }),
background: withOpacity(background(layer, "on"), 0.2),
text: text(theme.highest, "mono", "on", "default", {
size: "xs",
}),
background: with_opacity(background(theme.highest, "on"), 0.2),
},
},
})
return {
keystrokeSpacing: 8,
keystroke_spacing: 8,
// TODO: This should be a Toggle<ContainedText> on the rust side so we don't have to do this
key: {
inactive: { ...key.inactive },

View File

@ -1,7 +1,7 @@
import { fontFamilies, fontSizes, FontWeight } from "../common"
import { Layer, Styles, StyleSets, Style } from "../theme/colorScheme"
import { font_families, font_sizes, FontWeight } from "../common"
import { Layer, Styles, StyleSets, Style } from "../theme/color_scheme"
function isStyleSet(key: any): key is StyleSets {
function is_style_set(key: any): key is StyleSets {
return [
"base",
"variant",
@ -13,7 +13,7 @@ function isStyleSet(key: any): key is StyleSets {
].includes(key)
}
function isStyle(key: any): key is Styles {
function is_style(key: any): key is Styles {
return [
"default",
"active",
@ -23,78 +23,70 @@ function isStyle(key: any): key is Styles {
"inverted",
].includes(key)
}
function getStyle(
function get_style(
layer: Layer,
possibleStyleSetOrStyle?: any,
possibleStyle?: any
possible_style_set_or_style?: any,
possible_style?: any
): Style {
let styleSet: StyleSets = "base"
let style_set: StyleSets = "base"
let style: Styles = "default"
if (isStyleSet(possibleStyleSetOrStyle)) {
styleSet = possibleStyleSetOrStyle
} else if (isStyle(possibleStyleSetOrStyle)) {
style = possibleStyleSetOrStyle
if (is_style_set(possible_style_set_or_style)) {
style_set = possible_style_set_or_style
} else if (is_style(possible_style_set_or_style)) {
style = possible_style_set_or_style
}
if (isStyle(possibleStyle)) {
style = possibleStyle
if (is_style(possible_style)) {
style = possible_style
}
return layer[styleSet][style]
return layer[style_set][style]
}
export function background(layer: Layer, style?: Styles): string
export function background(
layer: Layer,
styleSet?: StyleSets,
style_set?: StyleSets,
style?: Styles
): string
export function background(
layer: Layer,
styleSetOrStyles?: StyleSets | Styles,
style_set_or_styles?: StyleSets | Styles,
style?: Styles
): string {
return getStyle(layer, styleSetOrStyles, style).background
return get_style(layer, style_set_or_styles, style).background
}
export function borderColor(layer: Layer, style?: Styles): string
export function borderColor(
export function border_color(layer: Layer, style?: Styles): string
export function border_color(
layer: Layer,
styleSet?: StyleSets,
style_set?: StyleSets,
style?: Styles
): string
export function borderColor(
export function border_color(
layer: Layer,
styleSetOrStyles?: StyleSets | Styles,
style_set_or_styles?: StyleSets | Styles,
style?: Styles
): string {
return getStyle(layer, styleSetOrStyles, style).border
return get_style(layer, style_set_or_styles, style).border
}
export function foreground(layer: Layer, style?: Styles): string
export function foreground(
layer: Layer,
styleSet?: StyleSets,
style_set?: StyleSets,
style?: Styles
): string
export function foreground(
layer: Layer,
styleSetOrStyles?: StyleSets | Styles,
style_set_or_styles?: StyleSets | Styles,
style?: Styles
): string {
return getStyle(layer, styleSetOrStyles, style).foreground
}
interface Text extends Object {
family: keyof typeof fontFamilies
color: string
size: number
weight?: FontWeight
underline?: boolean
return get_style(layer, style_set_or_styles, style).foreground
}
export interface TextStyle extends Object {
family: keyof typeof fontFamilies
family: keyof typeof font_families
color: string
size: number
weight?: FontWeight
@ -102,7 +94,7 @@ export interface TextStyle extends Object {
}
export interface TextProperties {
size?: keyof typeof fontSizes
size?: keyof typeof font_sizes
weight?: FontWeight
underline?: boolean
color?: string
@ -182,49 +174,53 @@ interface FontFeatures {
export function text(
layer: Layer,
fontFamily: keyof typeof fontFamilies,
styleSet: StyleSets,
font_family: keyof typeof font_families,
style_set: StyleSets,
style: Styles,
properties?: TextProperties
): Text
): TextStyle
export function text(
layer: Layer,
fontFamily: keyof typeof fontFamilies,
styleSet: StyleSets,
font_family: keyof typeof font_families,
style_set: StyleSets,
properties?: TextProperties
): Text
): TextStyle
export function text(
layer: Layer,
fontFamily: keyof typeof fontFamilies,
font_family: keyof typeof font_families,
style: Styles,
properties?: TextProperties
): Text
): TextStyle
export function text(
layer: Layer,
fontFamily: keyof typeof fontFamilies,
font_family: keyof typeof font_families,
properties?: TextProperties
): Text
): TextStyle
export function text(
layer: Layer,
fontFamily: keyof typeof fontFamilies,
styleSetStyleOrProperties?: StyleSets | Styles | TextProperties,
styleOrProperties?: Styles | TextProperties,
font_family: keyof typeof font_families,
style_set_style_or_properties?: StyleSets | Styles | TextProperties,
style_or_properties?: Styles | TextProperties,
properties?: TextProperties
) {
let style = getStyle(layer, styleSetStyleOrProperties, styleOrProperties)
const style = get_style(
layer,
style_set_style_or_properties,
style_or_properties
)
if (typeof styleSetStyleOrProperties === "object") {
properties = styleSetStyleOrProperties
if (typeof style_set_style_or_properties === "object") {
properties = style_set_style_or_properties
}
if (typeof styleOrProperties === "object") {
properties = styleOrProperties
if (typeof style_or_properties === "object") {
properties = style_or_properties
}
let size = fontSizes[properties?.size || "sm"]
let color = properties?.color || style.foreground
const size = font_sizes[properties?.size || "sm"]
const color = properties?.color || style.foreground
return {
family: fontFamilies[fontFamily],
family: font_families[font_family],
...properties,
color,
size,
@ -252,13 +248,13 @@ export interface BorderProperties {
export function border(
layer: Layer,
styleSet: StyleSets,
style_set: StyleSets,
style: Styles,
properties?: BorderProperties
): Border
export function border(
layer: Layer,
styleSet: StyleSets,
style_set: StyleSets,
properties?: BorderProperties
): Border
export function border(
@ -269,17 +265,17 @@ export function border(
export function border(layer: Layer, properties?: BorderProperties): Border
export function border(
layer: Layer,
styleSetStyleOrProperties?: StyleSets | Styles | BorderProperties,
styleOrProperties?: Styles | BorderProperties,
style_set_or_properties?: StyleSets | Styles | BorderProperties,
style_or_properties?: Styles | BorderProperties,
properties?: BorderProperties
): Border {
let style = getStyle(layer, styleSetStyleOrProperties, styleOrProperties)
const style = get_style(layer, style_set_or_properties, style_or_properties)
if (typeof styleSetStyleOrProperties === "object") {
properties = styleSetStyleOrProperties
if (typeof style_set_or_properties === "object") {
properties = style_set_or_properties
}
if (typeof styleOrProperties === "object") {
properties = styleOrProperties
if (typeof style_or_properties === "object") {
properties = style_or_properties
}
return {
@ -291,9 +287,9 @@ export function border(
export function svg(
color: string,
asset: String,
width: Number,
height: Number
asset: string,
width: number,
height: number
) {
return {
color,

View File

@ -0,0 +1,70 @@
import picker from "./picker"
import { ColorScheme } from "../theme/color_scheme"
import { background, border, foreground, text } from "./components"
export default function contact_finder(theme: ColorScheme): any {
const side_margin = 6
const contact_button = {
background: background(theme.middle, "variant"),
color: foreground(theme.middle, "variant"),
icon_width: 8,
button_width: 16,
corner_radius: 8,
}
const picker_style = picker(theme)
const picker_input = {
background: background(theme.middle, "on"),
corner_radius: 6,
text: text(theme.middle, "mono"),
placeholder_text: text(theme.middle, "mono", "on", "disabled", {
size: "xs",
}),
selection: theme.players[0],
border: border(theme.middle),
padding: {
bottom: 4,
left: 8,
right: 8,
top: 4,
},
margin: {
left: side_margin,
right: side_margin,
},
}
return {
picker: {
empty_container: {},
item: {
...picker_style.item,
margin: { left: side_margin, right: side_margin },
},
no_matches: picker_style.no_matches,
input_editor: picker_input,
empty_input_editor: picker_input,
},
row_height: 28,
contact_avatar: {
corner_radius: 10,
width: 18,
},
contact_username: {
padding: {
left: 8,
},
},
contact_button: {
...contact_button,
hover: {
background: background(theme.middle, "variant", "hovered"),
},
},
disabled_contact_button: {
...contact_button,
background: background(theme.middle, "disabled"),
color: foreground(theme.middle, "disabled"),
},
}
}

View File

@ -1,56 +1,62 @@
import { ColorScheme } from "../theme/colorScheme"
import { background, border, borderColor, foreground, text } from "./components"
import { ColorScheme } from "../theme/color_scheme"
import {
background,
border,
border_color,
foreground,
text,
} from "./components"
import { interactive, toggleable } from "../element"
export default function contactsPanel(colorScheme: ColorScheme) {
const nameMargin = 8
const sidePadding = 12
export default function contacts_panel(theme: ColorScheme): any {
const name_margin = 8
const side_padding = 12
let layer = colorScheme.middle
const layer = theme.middle
const contactButton = {
const contact_button = {
background: background(layer, "on"),
color: foreground(layer, "on"),
iconWidth: 8,
buttonWidth: 16,
cornerRadius: 8,
icon_width: 8,
button_width: 16,
corner_radius: 8,
}
const projectRow = {
guestAvatarSpacing: 4,
const project_row = {
guest_avatar_spacing: 4,
height: 24,
guestAvatar: {
cornerRadius: 8,
guest_avatar: {
corner_radius: 8,
width: 14,
},
name: {
...text(layer, "mono", { size: "sm" }),
margin: {
left: nameMargin,
left: name_margin,
right: 6,
},
},
guests: {
margin: {
left: nameMargin,
right: nameMargin,
left: name_margin,
right: name_margin,
},
},
padding: {
left: sidePadding,
right: sidePadding,
left: side_padding,
right: side_padding,
},
}
return {
background: background(layer),
padding: { top: 12 },
userQueryEditor: {
user_query_editor: {
background: background(layer, "on"),
cornerRadius: 6,
corner_radius: 6,
text: text(layer, "mono", "on"),
placeholderText: text(layer, "mono", "on", "disabled", {
placeholder_text: text(layer, "mono", "on", "disabled", {
size: "xs",
}),
selection: colorScheme.players[0],
selection: theme.players[0],
border: border(layer, "on"),
padding: {
bottom: 4,
@ -62,23 +68,23 @@ export default function contactsPanel(colorScheme: ColorScheme) {
left: 6,
},
},
userQueryEditorHeight: 33,
addContactButton: {
user_query_editor_height: 33,
add_contact_button: {
margin: { left: 6, right: 12 },
color: foreground(layer, "on"),
buttonWidth: 28,
iconWidth: 16,
button_width: 28,
icon_width: 16,
},
rowHeight: 28,
sectionIconSize: 8,
headerRow: toggleable({
row_height: 28,
section_icon_size: 8,
header_row: toggleable({
base: interactive({
base: {
...text(layer, "mono", { size: "sm" }),
margin: { top: 14 },
padding: {
left: sidePadding,
right: sidePadding,
left: side_padding,
right: side_padding,
},
background: background(layer, "default"), // posiewic: breaking change
},
@ -106,11 +112,11 @@ export default function contactsPanel(colorScheme: ColorScheme) {
},
},
}),
leaveCall: interactive({
leave_call: interactive({
base: {
background: background(layer),
border: border(layer),
cornerRadius: 6,
corner_radius: 6,
margin: {
top: 1,
},
@ -130,12 +136,12 @@ export default function contactsPanel(colorScheme: ColorScheme) {
},
},
}),
contactRow: {
contact_row: {
inactive: {
default: {
padding: {
left: sidePadding,
right: sidePadding,
left: side_padding,
right: side_padding,
},
},
},
@ -143,84 +149,83 @@ export default function contactsPanel(colorScheme: ColorScheme) {
default: {
background: background(layer, "active"),
padding: {
left: sidePadding,
right: sidePadding,
left: side_padding,
right: side_padding,
},
},
},
},
contactAvatar: {
cornerRadius: 10,
contact_avatar: {
corner_radius: 10,
width: 18,
},
contactStatusFree: {
cornerRadius: 4,
contact_status_free: {
corner_radius: 4,
padding: 4,
margin: { top: 12, left: 12 },
background: foreground(layer, "positive"),
},
contactStatusBusy: {
cornerRadius: 4,
contact_status_busy: {
corner_radius: 4,
padding: 4,
margin: { top: 12, left: 12 },
background: foreground(layer, "negative"),
},
contactUsername: {
contact_username: {
...text(layer, "mono", { size: "sm" }),
margin: {
left: nameMargin,
left: name_margin,
},
},
contactButtonSpacing: nameMargin,
contactButton: interactive({
base: { ...contactButton },
contact_button_spacing: name_margin,
contact_button: interactive({
base: { ...contact_button },
state: {
hovered: {
background: background(layer, "hovered"),
},
},
}),
disabledButton: {
...contactButton,
disabled_button: {
...contact_button,
background: background(layer, "on"),
color: foreground(layer, "on"),
},
callingIndicator: {
calling_indicator: {
...text(layer, "mono", "variant", { size: "xs" }),
},
treeBranch: toggleable({
tree_branch: toggleable({
base: interactive({
base: {
color: borderColor(layer),
color: border_color(layer),
width: 1,
},
state: {
hovered: {
color: borderColor(layer),
color: border_color(layer),
},
},
}),
state: {
active: {
default: {
color: borderColor(layer),
color: border_color(layer),
},
},
},
}),
projectRow: toggleable({
project_row: toggleable({
base: interactive({
base: {
...projectRow,
...project_row,
background: background(layer),
icon: {
margin: { left: nameMargin },
margin: { left: name_margin },
color: foreground(layer, "variant"),
width: 12,
},
name: {
...projectRow.name,
...project_row.name,
...text(layer, "mono", { size: "sm" }),
},
},

View File

@ -0,0 +1,53 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, foreground, text } from "./components"
import { interactive } from "../element"
export default function contact_notification(theme: ColorScheme): any {
const avatar_size = 12
const header_padding = 8
return {
header_avatar: {
height: avatar_size,
width: avatar_size,
corner_radius: 6,
},
header_message: {
...text(theme.lowest, "sans", { size: "xs" }),
margin: { left: header_padding, right: header_padding },
},
header_height: 18,
body_message: {
...text(theme.lowest, "sans", { size: "xs" }),
margin: { left: avatar_size + header_padding, top: 6, bottom: 6 },
},
button: interactive({
base: {
...text(theme.lowest, "sans", "on", { size: "xs" }),
background: background(theme.lowest, "on"),
padding: 4,
corner_radius: 6,
margin: { left: 6 },
},
state: {
hovered: {
background: background(theme.lowest, "on", "hovered"),
},
},
}),
dismiss_button: {
default: {
color: foreground(theme.lowest, "variant"),
icon_width: 8,
icon_height: 8,
button_width: 8,
button_height: 8,
hover: {
color: foreground(theme.lowest, "hovered"),
},
},
},
}
}

View File

@ -0,0 +1,14 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border } from "./components"
export default function contacts_popover(theme: ColorScheme): any {
return {
background: background(theme.middle),
corner_radius: 6,
padding: { top: 6, bottom: 6 },
shadow: theme.popover_shadow,
border: border(theme.middle),
width: 300,
height: 400,
}
}

View File

@ -0,0 +1,68 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, border_color, text } from "./components"
import { interactive, toggleable } from "../element"
export default function context_menu(theme: ColorScheme): any {
return {
background: background(theme.middle),
corner_radius: 10,
padding: 4,
shadow: theme.popover_shadow,
border: border(theme.middle),
keystroke_margin: 30,
item: toggleable({
base: interactive({
base: {
icon_spacing: 8,
icon_width: 14,
padding: { left: 6, right: 6, top: 2, bottom: 2 },
corner_radius: 6,
label: text(theme.middle, "sans", { size: "sm" }),
keystroke: {
...text(theme.middle, "sans", "variant", {
size: "sm",
weight: "bold",
}),
padding: { left: 3, right: 3 },
},
},
state: {
hovered: {
background: background(theme.middle, "hovered"),
label: text(theme.middle, "sans", "hovered", {
size: "sm",
}),
keystroke: {
...text(theme.middle, "sans", "hovered", {
size: "sm",
weight: "bold",
}),
padding: { left: 3, right: 3 },
},
},
clicked: {
background: background(theme.middle, "pressed"),
},
},
}),
state: {
active: {
default: {
background: background(theme.middle, "active"),
},
hovered: {
background: background(theme.middle, "hovered"),
},
clicked: {
background: background(theme.middle, "pressed"),
},
},
},
}),
separator: {
background: border_color(theme.middle),
margin: { top: 2, bottom: 2 },
},
}
}

View File

@ -1,18 +1,16 @@
import { ColorScheme } from "../theme/colorScheme"
import { ColorScheme } from "../theme/color_scheme"
import { background, border, foreground, svg, text } from "./components"
import { interactive } from "../element"
export default function copilot(colorScheme: ColorScheme) {
let layer = colorScheme.middle
export default function copilot(theme: ColorScheme): any {
const content_width = 264
let content_width = 264
let ctaButton =
const cta_button =
// Copied from welcome screen. FIXME: Move this into a ZDS component
interactive({
base: {
background: background(layer),
border: border(layer, "default"),
cornerRadius: 4,
background: background(theme.middle),
border: border(theme.middle, "default"),
corner_radius: 4,
margin: {
top: 4,
bottom: 4,
@ -25,49 +23,52 @@ export default function copilot(colorScheme: ColorScheme) {
left: 7,
right: 7,
},
...text(layer, "sans", "default", { size: "sm" }),
...text(theme.middle, "sans", "default", { size: "sm" }),
},
state: {
hovered: {
...text(layer, "sans", "default", { size: "sm" }),
background: background(layer, "hovered"),
border: border(layer, "active"),
...text(theme.middle, "sans", "default", { size: "sm" }),
background: background(theme.middle, "hovered"),
border: border(theme.middle, "active"),
},
},
})
return {
outLinkIcon: interactive({
out_link_icon: interactive({
base: {
icon: svg(
foreground(layer, "variant"),
foreground(theme.middle, "variant"),
"icons/link_out_12.svg",
12,
12
),
container: {
cornerRadius: 6,
corner_radius: 6,
padding: { left: 6 },
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered"),
color: foreground(theme.middle, "hovered"),
},
},
},
}),
modal: {
titleText: {
title_text: {
default: {
...text(layer, "sans", { size: "xs", weight: "bold" }),
...text(theme.middle, "sans", {
size: "xs",
weight: "bold",
}),
},
},
titlebar: {
background: background(colorScheme.lowest),
border: border(layer, "active"),
background: background(theme.lowest),
border: border(theme.middle, "active"),
padding: {
top: 4,
bottom: 4,
@ -76,7 +77,7 @@ export default function copilot(colorScheme: ColorScheme) {
},
},
container: {
background: background(colorScheme.lowest),
background: background(theme.lowest),
padding: {
top: 0,
left: 0,
@ -84,16 +85,16 @@ export default function copilot(colorScheme: ColorScheme) {
bottom: 8,
},
},
closeIcon: interactive({
close_icon: interactive({
base: {
icon: svg(
foreground(layer, "variant"),
foreground(theme.middle, "variant"),
"icons/x_mark_8.svg",
8,
8
),
container: {
cornerRadius: 2,
corner_radius: 2,
padding: {
top: 4,
bottom: 4,
@ -108,7 +109,7 @@ export default function copilot(colorScheme: ColorScheme) {
state: {
hovered: {
icon: svg(
foreground(layer, "on"),
foreground(theme.middle, "on"),
"icons/x_mark_8.svg",
8,
8
@ -116,7 +117,7 @@ export default function copilot(colorScheme: ColorScheme) {
},
clicked: {
icon: svg(
foreground(layer, "base"),
foreground(theme.middle, "base"),
"icons/x_mark_8.svg",
8,
8
@ -133,11 +134,11 @@ export default function copilot(colorScheme: ColorScheme) {
auth: {
content_width,
ctaButton,
cta_button,
header: {
icon: svg(
foreground(layer, "default"),
foreground(theme.middle, "default"),
"icons/zed_plus_copilot_32.svg",
92,
32
@ -154,7 +155,7 @@ export default function copilot(colorScheme: ColorScheme) {
prompting: {
subheading: {
...text(layer, "sans", { size: "xs" }),
...text(theme.middle, "sans", { size: "xs" }),
margin: {
top: 6,
bottom: 12,
@ -164,19 +165,22 @@ export default function copilot(colorScheme: ColorScheme) {
},
hint: {
...text(layer, "sans", { size: "xs", color: "#838994" }),
...text(theme.middle, "sans", {
size: "xs",
color: "#838994",
}),
margin: {
top: 6,
bottom: 2,
},
},
deviceCode: {
text: text(layer, "mono", { size: "sm" }),
device_code: {
text: text(theme.middle, "mono", { size: "sm" }),
cta: {
...ctaButton,
background: background(colorScheme.lowest),
border: border(colorScheme.lowest, "inverted"),
...cta_button,
background: background(theme.lowest),
border: border(theme.lowest, "inverted"),
padding: {
top: 0,
bottom: 0,
@ -189,7 +193,7 @@ export default function copilot(colorScheme: ColorScheme) {
},
},
left: content_width / 2,
leftContainer: {
left_container: {
padding: {
top: 3,
bottom: 3,
@ -198,9 +202,9 @@ export default function copilot(colorScheme: ColorScheme) {
},
},
right: (content_width * 1) / 3,
rightContainer: interactive({
right_container: interactive({
base: {
border: border(colorScheme.lowest, "inverted", {
border: border(theme.lowest, "inverted", {
bottom: false,
right: false,
top: false,
@ -215,7 +219,7 @@ export default function copilot(colorScheme: ColorScheme) {
},
state: {
hovered: {
border: border(layer, "active", {
border: border(theme.middle, "active", {
bottom: false,
right: false,
top: false,
@ -227,9 +231,9 @@ export default function copilot(colorScheme: ColorScheme) {
},
},
notAuthorized: {
not_authorized: {
subheading: {
...text(layer, "sans", { size: "xs" }),
...text(theme.middle, "sans", { size: "xs" }),
margin: {
top: 16,
@ -240,13 +244,13 @@ export default function copilot(colorScheme: ColorScheme) {
},
warning: {
...text(layer, "sans", {
...text(theme.middle, "sans", {
size: "xs",
color: foreground(layer, "warning"),
color: foreground(theme.middle, "warning"),
}),
border: border(layer, "warning"),
background: background(layer, "warning"),
cornerRadius: 2,
border: border(theme.middle, "warning"),
background: background(theme.middle, "warning"),
corner_radius: 2,
padding: {
top: 4,
left: 4,
@ -263,7 +267,7 @@ export default function copilot(colorScheme: ColorScheme) {
authorized: {
subheading: {
...text(layer, "sans", { size: "xs" }),
...text(theme.middle, "sans", { size: "xs" }),
margin: {
top: 16,
@ -272,7 +276,10 @@ export default function copilot(colorScheme: ColorScheme) {
},
hint: {
...text(layer, "sans", { size: "xs", color: "#838994" }),
...text(theme.middle, "sans", {
size: "xs",
color: "#838994",
}),
margin: {
top: 24,
bottom: 4,

View File

@ -0,0 +1,312 @@
import { with_opacity } from "../theme/color"
import { ColorScheme, Layer, StyleSets } from "../theme/color_scheme"
import {
background,
border,
border_color,
foreground,
text,
} from "./components"
import hover_popover from "./hover_popover"
import { build_syntax } from "../theme/syntax"
import { interactive, toggleable } from "../element"
export default function editor(theme: ColorScheme): any {
const { is_light } = theme
const layer = theme.highest
const autocomplete_item = {
corner_radius: 6,
padding: {
bottom: 2,
left: 6,
right: 6,
top: 2,
},
}
function diagnostic(layer: Layer, style_set: StyleSets) {
return {
text_scale_factor: 0.857,
header: {
border: border(layer, {
top: true,
}),
},
message: {
text: text(layer, "sans", style_set, "default", { size: "sm" }),
highlight_text: text(layer, "sans", style_set, "default", {
size: "sm",
weight: "bold",
}),
},
}
}
const syntax = build_syntax(theme)
return {
text_color: syntax.primary.color,
background: background(layer),
active_line_background: with_opacity(background(layer, "on"), 0.75),
highlighted_line_background: background(layer, "on"),
// Inline autocomplete suggestions, Co-pilot suggestions, etc.
suggestion: syntax.predictive,
code_actions: {
indicator: toggleable({
base: interactive({
base: {
color: foreground(layer, "variant"),
},
state: {
hovered: {
color: foreground(layer, "variant", "hovered"),
},
clicked: {
color: foreground(layer, "variant", "pressed"),
},
},
}),
state: {
active: {
default: {
color: foreground(layer, "accent"),
},
hovered: {
color: foreground(layer, "accent", "hovered"),
},
clicked: {
color: foreground(layer, "accent", "pressed"),
},
},
},
}),
vertical_scale: 0.55,
},
folds: {
icon_margin_scale: 2.5,
folded_icon: "icons/chevron_right_8.svg",
foldable_icon: "icons/chevron_down_8.svg",
indicator: toggleable({
base: interactive({
base: {
color: foreground(layer, "variant"),
},
state: {
hovered: {
color: foreground(layer, "on"),
},
clicked: {
color: foreground(layer, "base"),
},
},
}),
state: {
active: {
default: {
color: foreground(layer, "default"),
},
hovered: {
color: foreground(layer, "variant"),
},
},
},
}),
ellipses: {
text_color: theme.ramps.neutral(0.71).hex(),
corner_radius_factor: 0.15,
background: {
// Copied from hover_popover highlight
default: {
color: theme.ramps.neutral(0.5).alpha(0.0).hex(),
},
hovered: {
color: theme.ramps.neutral(0.5).alpha(0.5).hex(),
},
clicked: {
color: theme.ramps.neutral(0.5).alpha(0.7).hex(),
},
},
},
fold_background: foreground(layer, "variant"),
},
diff: {
deleted: is_light
? theme.ramps.red(0.5).hex()
: theme.ramps.red(0.4).hex(),
modified: is_light
? theme.ramps.yellow(0.5).hex()
: theme.ramps.yellow(0.5).hex(),
inserted: is_light
? theme.ramps.green(0.4).hex()
: theme.ramps.green(0.5).hex(),
removed_width_em: 0.275,
width_em: 0.15,
corner_radius: 0.05,
},
/** Highlights matching occurrences of what is under the cursor
* as well as matched brackets
*/
document_highlight_read_background: with_opacity(
foreground(layer, "accent"),
0.1
),
document_highlight_write_background: theme.ramps
.neutral(0.5)
.alpha(0.4)
.hex(), // TODO: This was blend * 2
error_color: background(layer, "negative"),
gutter_background: background(layer),
gutter_padding_factor: 3.5,
line_number: with_opacity(foreground(layer), 0.35),
line_number_active: foreground(layer),
rename_fade: 0.6,
unnecessary_code_fade: 0.5,
selection: theme.players[0],
whitespace: theme.ramps.neutral(0.5).hex(),
guest_selections: [
theme.players[1],
theme.players[2],
theme.players[3],
theme.players[4],
theme.players[5],
theme.players[6],
theme.players[7],
],
autocomplete: {
background: background(theme.middle),
corner_radius: 8,
padding: 4,
margin: {
left: -14,
},
border: border(theme.middle),
shadow: theme.popover_shadow,
match_highlight: foreground(theme.middle, "accent"),
item: autocomplete_item,
hovered_item: {
...autocomplete_item,
match_highlight: foreground(theme.middle, "accent", "hovered"),
background: background(theme.middle, "hovered"),
},
selected_item: {
...autocomplete_item,
match_highlight: foreground(theme.middle, "accent", "active"),
background: background(theme.middle, "active"),
},
},
diagnostic_header: {
background: background(theme.middle),
icon_width_factor: 1.5,
text_scale_factor: 0.857,
border: border(theme.middle, {
bottom: true,
top: true,
}),
code: {
...text(theme.middle, "mono", { size: "sm" }),
margin: {
left: 10,
},
},
source: {
text: text(theme.middle, "sans", {
size: "sm",
weight: "bold",
}),
},
message: {
highlight_text: text(theme.middle, "sans", {
size: "sm",
weight: "bold",
}),
text: text(theme.middle, "sans", { size: "sm" }),
},
},
diagnostic_path_header: {
background: background(theme.middle),
text_scale_factor: 0.857,
filename: text(theme.middle, "mono", { size: "sm" }),
path: {
...text(theme.middle, "mono", { size: "sm" }),
margin: {
left: 12,
},
},
},
error_diagnostic: diagnostic(theme.middle, "negative"),
warning_diagnostic: diagnostic(theme.middle, "warning"),
information_diagnostic: diagnostic(theme.middle, "accent"),
hint_diagnostic: diagnostic(theme.middle, "warning"),
invalid_error_diagnostic: diagnostic(theme.middle, "base"),
invalid_hint_diagnostic: diagnostic(theme.middle, "base"),
invalid_information_diagnostic: diagnostic(theme.middle, "base"),
invalid_warning_diagnostic: diagnostic(theme.middle, "base"),
hover_popover: hover_popover(theme),
link_definition: {
color: syntax.link_uri.color,
underline: syntax.link_uri.underline,
},
jump_icon: interactive({
base: {
color: foreground(layer, "on"),
icon_width: 20,
button_width: 20,
corner_radius: 6,
padding: {
top: 6,
bottom: 6,
left: 6,
right: 6,
},
},
state: {
hovered: {
background: background(layer, "on", "hovered"),
},
},
}),
scrollbar: {
width: 12,
min_height_factor: 1.0,
track: {
border: border(layer, "variant", { left: true }),
},
thumb: {
background: with_opacity(background(layer, "inverted"), 0.3),
border: {
width: 1,
color: border_color(layer, "variant"),
top: false,
right: true,
left: true,
bottom: false,
},
},
git: {
deleted: is_light
? with_opacity(theme.ramps.red(0.5).hex(), 0.8)
: with_opacity(theme.ramps.red(0.4).hex(), 0.8),
modified: is_light
? with_opacity(theme.ramps.yellow(0.5).hex(), 0.8)
: with_opacity(theme.ramps.yellow(0.4).hex(), 0.8),
inserted: is_light
? with_opacity(theme.ramps.green(0.5).hex(), 0.8)
: with_opacity(theme.ramps.green(0.4).hex(), 0.8),
},
},
composition_mark: {
underline: {
thickness: 1.0,
color: border_color(layer),
},
},
syntax,
}
}

View File

@ -0,0 +1,49 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, text } from "./components"
import { interactive } from "../element"
export default function feedback(theme: ColorScheme): any {
return {
submit_button: interactive({
base: {
...text(theme.highest, "mono", "on"),
background: background(theme.highest, "on"),
corner_radius: 6,
border: border(theme.highest, "on"),
margin: {
right: 4,
},
padding: {
bottom: 2,
left: 10,
right: 10,
top: 2,
},
},
state: {
clicked: {
...text(theme.highest, "mono", "on", "pressed"),
background: background(theme.highest, "on", "pressed"),
border: border(theme.highest, "on", "pressed"),
},
hovered: {
...text(theme.highest, "mono", "on", "hovered"),
background: background(theme.highest, "on", "hovered"),
border: border(theme.highest, "on", "hovered"),
},
},
}),
button_margin: 8,
info_text_default: text(theme.highest, "sans", "default", {
size: "xs",
}),
link_text_default: text(theme.highest, "sans", "default", {
size: "xs",
underline: true,
}),
link_text_hover: text(theme.highest, "sans", "hovered", {
size: "xs",
underline: true,
}),
}
}

View File

@ -0,0 +1,47 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, foreground, text } from "./components"
export default function hover_popover(theme: ColorScheme): any {
const base_container = {
background: background(theme.middle),
corner_radius: 8,
padding: {
left: 8,
right: 8,
top: 4,
bottom: 4,
},
shadow: theme.popover_shadow,
border: border(theme.middle),
margin: {
left: -8,
},
}
return {
container: base_container,
info_container: {
...base_container,
background: background(theme.middle, "accent"),
border: border(theme.middle, "accent"),
},
warning_container: {
...base_container,
background: background(theme.middle, "warning"),
border: border(theme.middle, "warning"),
},
error_container: {
...base_container,
background: background(theme.middle, "negative"),
border: border(theme.middle, "negative"),
},
block_style: {
padding: { top: 4 },
},
prose: text(theme.middle, "sans", { size: "sm" }),
diagnostic_source_highlight: {
color: foreground(theme.middle, "accent"),
},
highlight: theme.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better
}
}

View File

@ -0,0 +1,55 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, text } from "./components"
export default function incoming_call_notification(
theme: ColorScheme
): unknown {
const avatar_size = 48
return {
window_height: 74,
window_width: 380,
background: background(theme.middle),
caller_container: {
padding: 12,
},
caller_avatar: {
height: avatar_size,
width: avatar_size,
corner_radius: avatar_size / 2,
},
caller_metadata: {
margin: { left: 10 },
},
caller_username: {
...text(theme.middle, "sans", { size: "sm", weight: "bold" }),
margin: { top: -3 },
},
caller_message: {
...text(theme.middle, "sans", "variant", { size: "xs" }),
margin: { top: -3 },
},
worktree_roots: {
...text(theme.middle, "sans", "variant", {
size: "xs",
weight: "bold",
}),
margin: { top: -3 },
},
button_width: 96,
accept_button: {
background: background(theme.middle, "accent"),
border: border(theme.middle, { left: true, bottom: true }),
...text(theme.middle, "sans", "positive", {
size: "xs",
weight: "bold",
}),
},
decline_button: {
border: border(theme.middle, { left: true }),
...text(theme.middle, "sans", "negative", {
size: "xs",
weight: "bold",
}),
},
}
}

View File

@ -1,24 +1,23 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import { ColorScheme } from "../theme/color_scheme"
import { with_opacity } from "../theme/color"
import { background, border, text } from "./components"
import { interactive, toggleable } from "../element"
export default function picker(colorScheme: ColorScheme): any {
let layer = colorScheme.lowest
export default function picker(theme: ColorScheme): any {
const container = {
background: background(layer),
border: border(layer),
shadow: colorScheme.modalShadow,
cornerRadius: 12,
background: background(theme.lowest),
border: border(theme.lowest),
shadow: theme.modal_shadow,
corner_radius: 12,
padding: {
bottom: 4,
},
}
const inputEditor = {
placeholderText: text(layer, "sans", "on", "disabled"),
selection: colorScheme.players[0],
text: text(layer, "mono", "on"),
border: border(layer, { bottom: true }),
const input_editor = {
placeholder_text: text(theme.lowest, "sans", "on", "disabled"),
selection: theme.players[0],
text: text(theme.lowest, "mono", "on"),
border: border(theme.lowest, { bottom: true }),
padding: {
bottom: 8,
left: 16,
@ -29,13 +28,13 @@ export default function picker(colorScheme: ColorScheme): any {
bottom: 4,
},
}
const emptyInputEditor: any = { ...inputEditor }
delete emptyInputEditor.border
delete emptyInputEditor.margin
const empty_input_editor: any = { ...input_editor }
delete empty_input_editor.border
delete empty_input_editor.margin
return {
...container,
emptyContainer: {
empty_container: {
...container,
padding: {},
},
@ -53,22 +52,22 @@ export default function picker(colorScheme: ColorScheme): any {
left: 4,
right: 4,
},
cornerRadius: 8,
text: text(layer, "sans", "variant"),
highlightText: text(layer, "sans", "accent", {
corner_radius: 8,
text: text(theme.lowest, "sans", "variant"),
highlight_text: text(theme.lowest, "sans", "accent", {
weight: "bold",
}),
},
state: {
hovered: {
background: withOpacity(
background(layer, "hovered"),
background: with_opacity(
background(theme.lowest, "hovered"),
0.5
),
},
clicked: {
background: withOpacity(
background(layer, "pressed"),
background: with_opacity(
background(theme.lowest, "pressed"),
0.5
),
},
@ -77,20 +76,20 @@ export default function picker(colorScheme: ColorScheme): any {
state: {
active: {
default: {
background: withOpacity(
background(layer, "base", "active"),
background: with_opacity(
background(theme.lowest, "base", "active"),
0.5
),
},
hovered: {
background: withOpacity(
background(layer, "hovered"),
background: with_opacity(
background(theme.lowest, "hovered"),
0.5
),
},
clicked: {
background: withOpacity(
background(layer, "pressed"),
background: with_opacity(
background(theme.lowest, "pressed"),
0.5
),
},
@ -98,10 +97,10 @@ export default function picker(colorScheme: ColorScheme): any {
},
}),
inputEditor,
emptyInputEditor,
noMatches: {
text: text(layer, "sans", "variant"),
input_editor,
empty_input_editor,
no_matches: {
text: text(theme.lowest, "sans", "variant"),
padding: {
bottom: 8,
left: 16,

View File

@ -0,0 +1,12 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, text } from "./components"
export default function project_diagnostics(theme: ColorScheme): any {
return {
background: background(theme.highest),
tab_icon_spacing: 4,
tab_icon_width: 13,
tab_summary_spacing: 10,
empty_message: text(theme.highest, "sans", "variant", { size: "md" }),
}
}

View File

@ -0,0 +1,188 @@
import { ColorScheme } from "../theme/color_scheme"
import { with_opacity } from "../theme/color"
import {
Border,
TextStyle,
background,
border,
foreground,
text,
} from "./components"
import { interactive, toggleable } from "../element"
import merge from "ts-deepmerge"
export default function project_panel(theme: ColorScheme): any {
const { is_light } = theme
type EntryStateProps = {
background?: string
border?: Border
text?: TextStyle
icon_color?: string
}
type EntryState = {
default: EntryStateProps
hovered?: EntryStateProps
clicked?: EntryStateProps
}
const entry = (unselected?: EntryState, selected?: EntryState) => {
const git_status = {
git: {
modified: is_light
? theme.ramps.yellow(0.6).hex()
: theme.ramps.yellow(0.5).hex(),
inserted: is_light
? theme.ramps.green(0.45).hex()
: theme.ramps.green(0.5).hex(),
conflict: is_light
? theme.ramps.red(0.6).hex()
: theme.ramps.red(0.5).hex(),
},
}
const base_properties = {
height: 22,
background: background(theme.middle),
icon_color: foreground(theme.middle, "variant"),
icon_size: 7,
icon_spacing: 5,
text: text(theme.middle, "mono", "variant", { size: "sm" }),
status: {
...git_status,
},
}
const selected_style: EntryState | undefined = selected
? selected
: unselected
const unselected_default_style = merge(
base_properties,
unselected?.default ?? {},
{}
)
const unselected_hovered_style = merge(
base_properties,
unselected?.hovered ?? {},
{ background: background(theme.middle, "variant", "hovered") }
)
const unselected_clicked_style = merge(
base_properties,
unselected?.clicked ?? {},
{ background: background(theme.middle, "variant", "pressed") }
)
const selected_default_style = merge(
base_properties,
selected_style?.default ?? {},
{ background: background(theme.middle) }
)
const selected_hovered_style = merge(
base_properties,
selected_style?.hovered ?? {},
{ background: background(theme.middle, "variant", "hovered") }
)
const selected_clicked_style = merge(
base_properties,
selected_style?.clicked ?? {},
{ background: background(theme.middle, "variant", "pressed") }
)
return toggleable({
state: {
inactive: interactive({
state: {
default: unselected_default_style,
hovered: unselected_hovered_style,
clicked: unselected_clicked_style,
},
}),
active: interactive({
state: {
default: selected_default_style,
hovered: selected_hovered_style,
clicked: selected_clicked_style,
},
}),
},
})
}
const default_entry = entry()
return {
open_project_button: interactive({
base: {
background: background(theme.middle),
border: border(theme.middle, "active"),
corner_radius: 4,
margin: {
top: 16,
left: 16,
right: 16,
},
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
...text(theme.middle, "sans", "default", { size: "sm" }),
},
state: {
hovered: {
...text(theme.middle, "sans", "default", { size: "sm" }),
background: background(theme.middle, "hovered"),
border: border(theme.middle, "active"),
},
clicked: {
...text(theme.middle, "sans", "default", { size: "sm" }),
background: background(theme.middle, "pressed"),
border: border(theme.middle, "active"),
},
},
}),
background: background(theme.middle),
padding: { left: 6, right: 6, top: 0, bottom: 6 },
indent_width: 12,
entry: default_entry,
dragged_entry: {
...default_entry.inactive.default,
text: text(theme.middle, "mono", "on", { size: "sm" }),
background: with_opacity(background(theme.middle, "on"), 0.9),
border: border(theme.middle),
},
ignored_entry: entry(
{
default: {
text: text(theme.middle, "mono", "disabled"),
},
},
{
default: {
icon_color: foreground(theme.middle, "variant"),
},
}
),
cut_entry: entry(
{
default: {
text: text(theme.middle, "mono", "disabled"),
},
},
{
default: {
background: background(theme.middle, "active"),
text: text(theme.middle, "mono", "disabled", {
size: "sm",
}),
},
}
),
filename_editor: {
background: background(theme.middle, "on"),
text: text(theme.middle, "mono", "on", { size: "sm" }),
selection: theme.players[0],
},
}
}

View File

@ -0,0 +1,55 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, text } from "./components"
export default function project_shared_notification(
theme: ColorScheme
): unknown {
const avatar_size = 48
return {
window_height: 74,
window_width: 380,
background: background(theme.middle),
owner_container: {
padding: 12,
},
owner_avatar: {
height: avatar_size,
width: avatar_size,
corner_radius: avatar_size / 2,
},
owner_metadata: {
margin: { left: 10 },
},
owner_username: {
...text(theme.middle, "sans", { size: "sm", weight: "bold" }),
margin: { top: -3 },
},
message: {
...text(theme.middle, "sans", "variant", { size: "xs" }),
margin: { top: -3 },
},
worktree_roots: {
...text(theme.middle, "sans", "variant", {
size: "xs",
weight: "bold",
}),
margin: { top: -3 },
},
button_width: 96,
open_button: {
background: background(theme.middle, "accent"),
border: border(theme.middle, { left: true, bottom: true }),
...text(theme.middle, "sans", "accent", {
size: "xs",
weight: "bold",
}),
},
dismiss_button: {
border: border(theme.middle, { left: true }),
...text(theme.middle, "sans", "variant", {
size: "xs",
weight: "bold",
}),
},
}
}

View File

@ -0,0 +1,136 @@
import { ColorScheme } from "../theme/color_scheme"
import { with_opacity } from "../theme/color"
import { background, border, foreground, text } from "./components"
import { interactive, toggleable } from "../element"
export default function search(theme: ColorScheme): any {
// Search input
const editor = {
background: background(theme.highest),
corner_radius: 8,
min_width: 200,
max_width: 500,
placeholder_text: text(theme.highest, "mono", "disabled"),
selection: theme.players[0],
text: text(theme.highest, "mono", "default"),
border: border(theme.highest),
margin: {
right: 12,
},
padding: {
top: 3,
bottom: 3,
left: 12,
right: 8,
},
}
const include_exclude_editor = {
...editor,
min_width: 100,
max_width: 250,
}
return {
// TODO: Add an activeMatchBackground on the rust side to differentiate between active and inactive
match_background: with_opacity(
foreground(theme.highest, "accent"),
0.4
),
option_button: toggleable({
base: interactive({
base: {
...text(theme.highest, "mono", "on"),
background: background(theme.highest, "on"),
corner_radius: 6,
border: border(theme.highest, "on"),
margin: {
right: 4,
},
padding: {
bottom: 2,
left: 10,
right: 10,
top: 2,
},
},
state: {
hovered: {
...text(theme.highest, "mono", "on", "hovered"),
background: background(theme.highest, "on", "hovered"),
border: border(theme.highest, "on", "hovered"),
},
clicked: {
...text(theme.highest, "mono", "on", "pressed"),
background: background(theme.highest, "on", "pressed"),
border: border(theme.highest, "on", "pressed"),
},
},
}),
state: {
active: {
default: {
...text(theme.highest, "mono", "accent"),
},
hovered: {
...text(theme.highest, "mono", "accent", "hovered"),
},
clicked: {
...text(theme.highest, "mono", "accent", "pressed"),
},
},
},
}),
editor,
invalid_editor: {
...editor,
border: border(theme.highest, "negative"),
},
include_exclude_editor,
invalid_include_exclude_editor: {
...include_exclude_editor,
border: border(theme.highest, "negative"),
},
match_index: {
...text(theme.highest, "mono", "variant"),
padding: {
left: 6,
},
},
option_button_group: {
padding: {
left: 12,
right: 12,
},
},
include_exclude_inputs: {
...text(theme.highest, "mono", "variant"),
padding: {
right: 6,
},
},
results_status: {
...text(theme.highest, "mono", "on"),
size: 18,
},
dismiss_button: interactive({
base: {
color: foreground(theme.highest, "variant"),
icon_width: 12,
button_width: 14,
padding: {
left: 10,
right: 10,
},
},
state: {
hovered: {
color: foreground(theme.highest, "hovered"),
},
clicked: {
color: foreground(theme.highest, "pressed"),
},
},
}),
}
}

View File

@ -0,0 +1,8 @@
import { ColorScheme } from "../theme/color_scheme"
import { background } from "./components"
export default function sharedScreen(theme: ColorScheme) {
return {
background: background(theme.highest),
}
}

View File

@ -0,0 +1,50 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, foreground, text } from "./components"
import { interactive } from "../element"
export default function simple_message_notification(theme: ColorScheme): any {
const header_padding = 8
return {
message: {
...text(theme.middle, "sans", { size: "xs" }),
margin: { left: header_padding, right: header_padding },
},
action_message: interactive({
base: {
...text(theme.middle, "sans", { size: "xs" }),
border: border(theme.middle, "active"),
corner_radius: 4,
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
margin: { left: header_padding, top: 6, bottom: 6 },
},
state: {
hovered: {
...text(theme.middle, "sans", "default", { size: "xs" }),
background: background(theme.middle, "hovered"),
border: border(theme.middle, "active"),
},
},
}),
dismiss_button: interactive({
base: {
color: foreground(theme.middle),
icon_width: 8,
icon_height: 8,
button_width: 8,
button_height: 8,
},
state: {
hovered: {
color: foreground(theme.middle, "hovered"),
},
},
}),
}
}

View File

@ -1,22 +1,22 @@
import { ColorScheme } from "../theme/colorScheme"
import { ColorScheme } from "../theme/color_scheme"
import { background, border, foreground, text } from "./components"
import { interactive, toggleable } from "../element"
export default function statusBar(colorScheme: ColorScheme) {
let layer = colorScheme.lowest
export default function status_bar(theme: ColorScheme): any {
const layer = theme.lowest
const statusContainer = {
cornerRadius: 6,
const status_container = {
corner_radius: 6,
padding: { top: 3, bottom: 3, left: 6, right: 6 },
}
const diagnosticStatusContainer = {
cornerRadius: 6,
const diagnostic_status_container = {
corner_radius: 6,
padding: { top: 1, bottom: 1, left: 6, right: 6 },
}
return {
height: 30,
itemSpacing: 8,
item_spacing: 8,
padding: {
top: 1,
bottom: 1,
@ -24,8 +24,8 @@ export default function statusBar(colorScheme: ColorScheme) {
right: 6,
},
border: border(layer, { top: true, overlay: true }),
cursorPosition: text(layer, "sans", "variant"),
activeLanguage: interactive({
cursor_position: text(layer, "sans", "variant"),
active_language: interactive({
base: {
padding: { left: 6, right: 6 },
...text(layer, "sans", "variant"),
@ -36,83 +36,83 @@ export default function statusBar(colorScheme: ColorScheme) {
},
},
}),
autoUpdateProgressMessage: text(layer, "sans", "variant"),
autoUpdateDoneMessage: text(layer, "sans", "variant"),
lspStatus: interactive({
auto_update_progress_message: text(layer, "sans", "variant"),
auto_update_done_message: text(layer, "sans", "variant"),
lsp_status: interactive({
base: {
...diagnosticStatusContainer,
iconSpacing: 4,
iconWidth: 14,
...diagnostic_status_container,
icon_spacing: 4,
icon_width: 14,
height: 18,
message: text(layer, "sans"),
iconColor: foreground(layer),
icon_color: foreground(layer),
},
state: {
hovered: {
message: text(layer, "sans"),
iconColor: foreground(layer),
icon_color: foreground(layer),
background: background(layer, "hovered"),
},
},
}),
diagnosticMessage: interactive({
diagnostic_message: interactive({
base: {
...text(layer, "sans"),
},
state: { hovered: text(layer, "sans", "hovered") },
}),
diagnosticSummary: interactive({
diagnostic_summary: interactive({
base: {
height: 20,
iconWidth: 16,
iconSpacing: 2,
summarySpacing: 6,
icon_width: 16,
icon_spacing: 2,
summary_spacing: 6,
text: text(layer, "sans", { size: "sm" }),
iconColorOk: foreground(layer, "variant"),
iconColorWarning: foreground(layer, "warning"),
iconColorError: foreground(layer, "negative"),
containerOk: {
cornerRadius: 6,
icon_color_ok: foreground(layer, "variant"),
icon_color_warning: foreground(layer, "warning"),
icon_color_error: foreground(layer, "negative"),
container_ok: {
corner_radius: 6,
padding: { top: 3, bottom: 3, left: 7, right: 7 },
},
containerWarning: {
...diagnosticStatusContainer,
container_warning: {
...diagnostic_status_container,
background: background(layer, "warning"),
border: border(layer, "warning"),
},
containerError: {
...diagnosticStatusContainer,
container_error: {
...diagnostic_status_container,
background: background(layer, "negative"),
border: border(layer, "negative"),
},
},
state: {
hovered: {
iconColorOk: foreground(layer, "on"),
containerOk: {
icon_color_ok: foreground(layer, "on"),
container_ok: {
background: background(layer, "on", "hovered"),
},
containerWarning: {
container_warning: {
background: background(layer, "warning", "hovered"),
border: border(layer, "warning", "hovered"),
},
containerError: {
container_error: {
background: background(layer, "negative", "hovered"),
border: border(layer, "negative", "hovered"),
},
},
},
}),
panelButtons: {
groupLeft: {},
groupBottom: {},
groupRight: {},
panel_buttons: {
group_left: {},
group_bottom: {},
group_right: {},
button: toggleable({
base: interactive({
base: {
...statusContainer,
iconSize: 16,
iconColor: foreground(layer, "variant"),
...status_container,
icon_size: 16,
icon_color: foreground(layer, "variant"),
label: {
margin: { left: 6 },
...text(layer, "sans", { size: "sm" }),
@ -120,7 +120,7 @@ export default function statusBar(colorScheme: ColorScheme) {
},
state: {
hovered: {
iconColor: foreground(layer, "hovered"),
icon_color: foreground(layer, "hovered"),
background: background(layer, "variant"),
},
},
@ -128,22 +128,22 @@ export default function statusBar(colorScheme: ColorScheme) {
state: {
active: {
default: {
iconColor: foreground(layer, "active"),
icon_color: foreground(layer, "active"),
background: background(layer, "active"),
},
hovered: {
iconColor: foreground(layer, "hovered"),
icon_color: foreground(layer, "hovered"),
background: background(layer, "hovered"),
},
clicked: {
iconColor: foreground(layer, "pressed"),
icon_color: foreground(layer, "pressed"),
background: background(layer, "pressed"),
},
},
},
}),
badge: {
cornerRadius: 3,
corner_radius: 3,
padding: 2,
margin: { bottom: -1, right: -1 },
border: border(layer),

View File

@ -1,13 +1,13 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import { ColorScheme } from "../theme/color_scheme"
import { with_opacity } from "../theme/color"
import { text, border, background, foreground } from "./components"
import { interactive, toggleable } from "../element"
export default function tabBar(colorScheme: ColorScheme) {
export default function tab_bar(theme: ColorScheme): any {
const height = 32
let activeLayer = colorScheme.highest
let layer = colorScheme.middle
const active_layer = theme.highest
const layer = theme.middle
const tab = {
height,
@ -25,16 +25,16 @@ export default function tabBar(colorScheme: ColorScheme) {
spacing: 8,
// Tab type icons (e.g. Project Search)
typeIconWidth: 14,
type_icon_width: 14,
// Close icons
closeIconWidth: 8,
iconClose: foreground(layer, "variant"),
iconCloseActive: foreground(layer, "hovered"),
close_icon_width: 8,
icon_close: foreground(layer, "variant"),
icon_close_active: foreground(layer, "hovered"),
// Indicators
iconConflict: foreground(layer, "warning"),
iconDirty: foreground(layer, "accent"),
icon_conflict: foreground(layer, "warning"),
icon_dirty: foreground(layer, "accent"),
// When two tabs of the same name are open, a label appears next to them
description: {
@ -43,25 +43,25 @@ export default function tabBar(colorScheme: ColorScheme) {
},
}
const activePaneActiveTab = {
const active_pane_active_tab = {
...tab,
background: background(activeLayer),
text: text(activeLayer, "sans", "active", { size: "sm" }),
background: background(active_layer),
text: text(active_layer, "sans", "active", { size: "sm" }),
border: {
...tab.border,
bottom: false,
},
}
const inactivePaneInactiveTab = {
const inactive_pane_inactive_tab = {
...tab,
background: background(layer),
text: text(layer, "sans", "variant", { size: "sm" }),
}
const inactivePaneActiveTab = {
const inactive_pane_active_tab = {
...tab,
background: background(activeLayer),
background: background(active_layer),
text: text(layer, "sans", "variant", { size: "sm" }),
border: {
...tab.border,
@ -69,31 +69,31 @@ export default function tabBar(colorScheme: ColorScheme) {
},
}
const draggedTab = {
...activePaneActiveTab,
background: withOpacity(tab.background, 0.9),
const dragged_tab = {
...active_pane_active_tab,
background: with_opacity(tab.background, 0.9),
border: undefined as any,
shadow: colorScheme.popoverShadow,
shadow: theme.popover_shadow,
}
return {
height,
background: background(layer),
activePane: {
activeTab: activePaneActiveTab,
inactiveTab: tab,
active_pane: {
active_tab: active_pane_active_tab,
inactive_tab: tab,
},
inactivePane: {
activeTab: inactivePaneActiveTab,
inactiveTab: inactivePaneInactiveTab,
inactive_pane: {
active_tab: inactive_pane_active_tab,
inactive_tab: inactive_pane_inactive_tab,
},
draggedTab,
paneButton: toggleable({
dragged_tab,
pane_button: toggleable({
base: interactive({
base: {
color: foreground(layer, "variant"),
iconWidth: 12,
buttonWidth: activePaneActiveTab.height,
icon_width: 12,
button_width: active_pane_active_tab.height,
},
state: {
hovered: {
@ -118,7 +118,7 @@ export default function tabBar(colorScheme: ColorScheme) {
},
},
}),
paneButtonContainer: {
pane_button_container: {
background: tab.background,
border: {
...tab.border,

View File

@ -0,0 +1,52 @@
import { ColorScheme } from "../theme/color_scheme"
export default function terminal(theme: ColorScheme) {
/**
* Colors are controlled per-cell in the terminal grid.
* Cells can be set to any of these more 'theme-capable' colors
* or can be set directly with RGB values.
* Here are the common interpretations of these names:
* https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
*/
return {
black: theme.ramps.neutral(0).hex(),
red: theme.ramps.red(0.5).hex(),
green: theme.ramps.green(0.5).hex(),
yellow: theme.ramps.yellow(0.5).hex(),
blue: theme.ramps.blue(0.5).hex(),
magenta: theme.ramps.magenta(0.5).hex(),
cyan: theme.ramps.cyan(0.5).hex(),
white: theme.ramps.neutral(1).hex(),
bright_black: theme.ramps.neutral(0.4).hex(),
bright_red: theme.ramps.red(0.25).hex(),
bright_green: theme.ramps.green(0.25).hex(),
bright_yellow: theme.ramps.yellow(0.25).hex(),
bright_blue: theme.ramps.blue(0.25).hex(),
bright_magenta: theme.ramps.magenta(0.25).hex(),
bright_cyan: theme.ramps.cyan(0.25).hex(),
bright_white: theme.ramps.neutral(1).hex(),
/**
* Default color for characters
*/
foreground: theme.ramps.neutral(1).hex(),
/**
* Default color for the rectangle background of a cell
*/
background: theme.ramps.neutral(0).hex(),
modal_background: theme.ramps.neutral(0.1).hex(),
/**
* Default color for the cursor
*/
cursor: theme.players[0].cursor,
dim_black: theme.ramps.neutral(1).hex(),
dim_red: theme.ramps.red(0.75).hex(),
dim_green: theme.ramps.green(0.75).hex(),
dim_yellow: theme.ramps.yellow(0.75).hex(),
dim_blue: theme.ramps.blue(0.75).hex(),
dim_magenta: theme.ramps.magenta(0.75).hex(),
dim_cyan: theme.ramps.cyan(0.75).hex(),
dim_white: theme.ramps.neutral(0.6).hex(),
bright_foreground: theme.ramps.neutral(1).hex(),
dim_foreground: theme.ramps.neutral(0).hex(),
}
}

View File

@ -2,7 +2,7 @@ import { ColorScheme } from "../common"
import { icon_button, toggleable_icon_button } from "../component/icon_button"
import { toggleable_text_button } from "../component/text_button"
import { interactive, toggleable } from "../element"
import { withOpacity } from "../theme/color"
import { with_opacity } from "../theme/color"
import { background, border, foreground, text } from "./components"
const ITEM_SPACING = 8
@ -17,8 +17,8 @@ function build_spacing(
group: spacing,
item: spacing / 2,
half_item: spacing / 4,
marginY: (container_height - element_height) / 2,
marginX: (container_height - element_height) / 2,
margin_y: (container_height - element_height) / 2,
margin_x: (container_height - element_height) / 2,
}
}
@ -26,15 +26,15 @@ function call_controls(theme: ColorScheme) {
const button_height = 18
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
const marginY = {
top: space.marginY,
bottom: space.marginY,
const margin_y = {
top: space.margin_y,
bottom: space.margin_y,
}
return {
toggle_microphone_button: toggleable_icon_button(theme, {
margin: {
...marginY,
...margin_y,
left: space.group,
right: space.half_item,
},
@ -43,7 +43,7 @@ function call_controls(theme: ColorScheme) {
toggle_speakers_button: toggleable_icon_button(theme, {
margin: {
...marginY,
...margin_y,
left: space.half_item,
right: space.half_item,
},
@ -51,7 +51,7 @@ function call_controls(theme: ColorScheme) {
screen_share_button: toggleable_icon_button(theme, {
margin: {
...marginY,
...margin_y,
left: space.half_item,
right: space.group,
},
@ -78,7 +78,7 @@ function user_menu(theme: ColorScheme) {
const button = toggleable({
base: interactive({
base: {
cornerRadius: 6,
corner_radius: 6,
height: button_height,
width: online ? 37 : 24,
padding: {
@ -150,20 +150,20 @@ function user_menu(theme: ColorScheme) {
}
}
return {
userMenuButtonOnline: build_button({ online: true }),
userMenuButtonOffline: build_button({ online: false }),
user_menu_button_online: build_button({ online: true }),
user_menu_button_offline: build_button({ online: false }),
}
}
export function titlebar(theme: ColorScheme) {
const avatarWidth = 15
const avatarOuterWidth = avatarWidth + 4
const followerAvatarWidth = 14
const followerAvatarOuterWidth = followerAvatarWidth + 4
export function titlebar(theme: ColorScheme): any {
const avatar_width = 15
const avatar_outer_width = avatar_width + 4
const follower_avatar_width = 14
const follower_avatar_outer_width = follower_avatar_width + 4
return {
item_spacing: ITEM_SPACING,
facePileSpacing: 2,
face_pile_spacing: 2,
height: TITLEBAR_HEIGHT,
background: background(theme.lowest),
border: border(theme.lowest, { bottom: true }),
@ -177,21 +177,21 @@ export function titlebar(theme: ColorScheme) {
highlight_color: text(theme.lowest, "sans", "active").color,
// Collaborators
leaderAvatar: {
width: avatarWidth,
outerWidth: avatarOuterWidth,
cornerRadius: avatarWidth / 2,
outerCornerRadius: avatarOuterWidth / 2,
leader_avatar: {
width: avatar_width,
outer_width: avatar_outer_width,
corner_radius: avatar_width / 2,
outer_corner_radius: avatar_outer_width / 2,
},
followerAvatar: {
width: followerAvatarWidth,
outerWidth: followerAvatarOuterWidth,
cornerRadius: followerAvatarWidth / 2,
outerCornerRadius: followerAvatarOuterWidth / 2,
follower_avatar: {
width: follower_avatar_width,
outer_width: follower_avatar_outer_width,
corner_radius: follower_avatar_width / 2,
outer_corner_radius: follower_avatar_outer_width / 2,
},
inactiveAvatarGrayscale: true,
followerAvatarOverlap: 8,
leaderSelection: {
inactive_avatar_grayscale: true,
follower_avatar_overlap: 8,
leader_selection: {
margin: {
top: 4,
bottom: 4,
@ -202,16 +202,16 @@ export function titlebar(theme: ColorScheme) {
top: 2,
bottom: 2,
},
cornerRadius: 6,
corner_radius: 6,
},
avatarRibbon: {
avatar_ribbon: {
height: 3,
width: 14,
// TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded.
},
sign_in_button: toggleable_text_button(theme, {}),
offlineIcon: {
offline_icon: {
color: foreground(theme.lowest, "variant"),
width: 16,
margin: {
@ -223,9 +223,9 @@ export function titlebar(theme: ColorScheme) {
},
// When the collaboration server is out of date, show a warning
outdatedWarning: {
outdated_warning: {
...text(theme.lowest, "sans", "warning", { size: "xs" }),
background: withOpacity(background(theme.lowest, "warning"), 0.3),
background: with_opacity(background(theme.lowest, "warning"), 0.3),
border: border(theme.lowest, "warning"),
margin: {
left: ITEM_SPACING,
@ -234,7 +234,7 @@ export function titlebar(theme: ColorScheme) {
left: 8,
right: 8,
},
cornerRadius: 6,
corner_radius: 6,
},
leave_call_button: icon_button(theme, {
@ -253,14 +253,14 @@ export function titlebar(theme: ColorScheme) {
}),
// Jewel that notifies you that there are new contact requests
toggleContactsBadge: {
cornerRadius: 3,
toggle_contacts_badge: {
corner_radius: 3,
padding: 2,
margin: { top: 3, left: 3 },
border: border(theme.lowest),
background: foreground(theme.lowest, "accent"),
},
shareButton: toggleable_text_button(theme, {}),
share_button: toggleable_text_button(theme, {}),
user_menu: user_menu(theme),
}
}

View File

@ -0,0 +1,64 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, text } from "./components"
import { interactive, toggleable } from "../element"
export default function dropdown_menu(theme: ColorScheme): any {
return {
row_height: 30,
background: background(theme.middle),
border: border(theme.middle),
shadow: theme.popover_shadow,
header: interactive({
base: {
...text(theme.middle, "sans", { size: "sm" }),
secondary_text: text(theme.middle, "sans", {
size: "sm",
color: "#aaaaaa",
}),
secondary_text_spacing: 10,
padding: { left: 8, right: 8, top: 2, bottom: 2 },
corner_radius: 6,
background: background(theme.middle, "on"),
},
state: {
hovered: {
background: background(theme.middle, "hovered"),
},
clicked: {
background: background(theme.middle, "pressed"),
},
},
}),
section_header: {
...text(theme.middle, "sans", { size: "sm" }),
padding: { left: 8, right: 8, top: 8, bottom: 8 },
},
item: toggleable({
base: interactive({
base: {
...text(theme.middle, "sans", { size: "sm" }),
secondary_text_spacing: 10,
secondary_text: text(theme.middle, "sans", { size: "sm" }),
padding: { left: 18, right: 18, top: 2, bottom: 2 },
},
state: {
hovered: {
background: background(theme.middle, "hovered"),
...text(theme.middle, "sans", "hovered", {
size: "sm",
}),
},
},
}),
state: {
active: {
default: {
background: background(theme.middle, "active"),
},
hovered: {
background: background(theme.middle, "hovered"),
},
},
},
}),
}
}

View File

@ -0,0 +1,22 @@
import { ColorScheme } from "../theme/color_scheme"
import { background, border, text } from "./components"
export default function tooltip(theme: ColorScheme): any {
return {
background: background(theme.middle),
border: border(theme.middle),
padding: { top: 4, bottom: 4, left: 8, right: 8 },
margin: { top: 6, left: 6 },
shadow: theme.popover_shadow,
corner_radius: 6,
text: text(theme.middle, "sans", { size: "xs" }),
keystroke: {
background: background(theme.middle, "on"),
corner_radius: 4,
margin: { left: 6 },
padding: { left: 4, right: 4 },
...text(theme.middle, "mono", "on", { size: "xs", weight: "bold" }),
},
max_text_width: 200,
}
}

View File

@ -0,0 +1,39 @@
import { ColorScheme } from "../theme/color_scheme"
import { foreground, text } from "./components"
import { interactive } from "../element"
export default function update_notification(theme: ColorScheme): any {
const header_padding = 8
return {
message: {
...text(theme.middle, "sans", { size: "xs" }),
margin: { left: header_padding, right: header_padding },
},
action_message: interactive({
base: {
...text(theme.middle, "sans", { size: "xs" }),
margin: { left: header_padding, top: 6, bottom: 6 },
},
state: {
hovered: {
color: foreground(theme.middle, "hovered"),
},
},
}),
dismiss_button: interactive({
base: {
color: foreground(theme.middle),
icon_width: 8,
icon_height: 8,
button_width: 8,
button_height: 8,
},
state: {
hovered: {
color: foreground(theme.middle, "hovered"),
},
},
}),
}
}

View File

@ -0,0 +1,155 @@
import { ColorScheme } from "../theme/color_scheme"
import { with_opacity } from "../theme/color"
import {
border,
background,
foreground,
text,
TextProperties,
svg,
} from "./components"
import { interactive } from "../element"
export default function welcome(theme: ColorScheme): any {
const checkbox_base = {
corner_radius: 4,
padding: {
left: 3,
right: 3,
top: 3,
bottom: 3,
},
// shadow: theme.popover_shadow,
border: border(theme.highest),
margin: {
right: 8,
top: 5,
bottom: 5,
},
}
const interactive_text_size: TextProperties = { size: "sm" }
return {
page_width: 320,
logo: svg(
foreground(theme.highest, "default"),
"icons/logo_96.svg",
64,
64
),
logo_subheading: {
...text(theme.highest, "sans", "variant", { size: "md" }),
margin: {
top: 10,
bottom: 7,
},
},
button_group: {
margin: {
top: 8,
bottom: 16,
},
},
heading_group: {
margin: {
top: 8,
bottom: 12,
},
},
checkbox_group: {
border: border(theme.highest, "variant"),
background: with_opacity(
background(theme.highest, "hovered"),
0.25
),
corner_radius: 4,
padding: {
left: 12,
top: 2,
bottom: 2,
},
},
button: interactive({
base: {
background: background(theme.highest),
border: border(theme.highest, "active"),
corner_radius: 4,
margin: {
top: 4,
bottom: 4,
},
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
...text(
theme.highest,
"sans",
"default",
interactive_text_size
),
},
state: {
hovered: {
...text(
theme.highest,
"sans",
"default",
interactive_text_size
),
background: background(theme.highest, "hovered"),
},
},
}),
usage_note: {
...text(theme.highest, "sans", "variant", { size: "2xs" }),
padding: {
top: -4,
},
},
checkbox_container: {
margin: {
top: 4,
},
padding: {
bottom: 8,
},
},
checkbox: {
label: {
...text(theme.highest, "sans", interactive_text_size),
// Also supports margin, container, border, etc.
},
icon: svg(
foreground(theme.highest, "on"),
"icons/check_12.svg",
12,
12
),
default: {
...checkbox_base,
background: background(theme.highest, "default"),
border: border(theme.highest, "active"),
},
checked: {
...checkbox_base,
background: background(theme.highest, "hovered"),
border: border(theme.highest, "active"),
},
hovered: {
...checkbox_base,
background: background(theme.highest, "hovered"),
border: border(theme.highest, "active"),
},
hovered_and_checked: {
...checkbox_base,
background: background(theme.highest, "hovered"),
border: border(theme.highest, "active"),
},
},
}
}

View File

@ -0,0 +1,190 @@
import { ColorScheme } from "../theme/color_scheme"
import { with_opacity } from "../theme/color"
import {
background,
border,
border_color,
foreground,
svg,
text,
} from "./components"
import statusBar from "./status_bar"
import tabBar from "./tab_bar"
import { interactive } from "../element"
import { titlebar } from "./titlebar"
export default function workspace(theme: ColorScheme): any {
const { is_light } = theme
return {
background: background(theme.lowest),
blank_pane: {
logo_container: {
width: 256,
height: 256,
},
logo: svg(
with_opacity("#000000", theme.is_light ? 0.6 : 0.8),
"icons/logo_96.svg",
256,
256
),
logo_shadow: svg(
with_opacity(
theme.is_light
? "#FFFFFF"
: theme.lowest.base.default.background,
theme.is_light ? 1 : 0.6
),
"icons/logo_96.svg",
256,
256
),
keyboard_hints: {
margin: {
top: 96,
},
corner_radius: 4,
},
keyboard_hint: interactive({
base: {
...text(theme.lowest, "sans", "variant", { size: "sm" }),
padding: {
top: 3,
left: 8,
right: 8,
bottom: 3,
},
corner_radius: 8,
},
state: {
hovered: {
...text(theme.lowest, "sans", "active", { size: "sm" }),
},
},
}),
keyboard_hint_width: 320,
},
joining_project_avatar: {
corner_radius: 40,
width: 80,
},
joining_project_message: {
padding: 12,
...text(theme.lowest, "sans", { size: "lg" }),
},
external_location_message: {
background: background(theme.middle, "accent"),
border: border(theme.middle, "accent"),
corner_radius: 6,
padding: 12,
margin: { bottom: 8, right: 8 },
...text(theme.middle, "sans", "accent", { size: "xs" }),
},
leader_border_opacity: 0.7,
leader_border_width: 2.0,
tab_bar: tabBar(theme),
modal: {
margin: {
bottom: 52,
top: 52,
},
cursor: "Arrow",
},
zoomed_background: {
cursor: "Arrow",
background: is_light
? with_opacity(background(theme.lowest), 0.8)
: with_opacity(background(theme.highest), 0.6),
},
zoomed_pane_foreground: {
margin: 16,
shadow: theme.modal_shadow,
border: border(theme.lowest, { overlay: true }),
},
zoomed_panel_foreground: {
margin: 16,
border: border(theme.lowest, { overlay: true }),
},
dock: {
left: {
border: border(theme.lowest, { right: true }),
},
bottom: {
border: border(theme.lowest, { top: true }),
},
right: {
border: border(theme.lowest, { left: true }),
},
},
pane_divider: {
color: border_color(theme.lowest),
width: 1,
},
status_bar: statusBar(theme),
titlebar: titlebar(theme),
toolbar: {
height: 34,
background: background(theme.highest),
border: border(theme.highest, { bottom: true }),
item_spacing: 8,
nav_button: interactive({
base: {
color: foreground(theme.highest, "on"),
icon_width: 12,
button_width: 24,
corner_radius: 6,
},
state: {
hovered: {
color: foreground(theme.highest, "on", "hovered"),
background: background(theme.highest, "on", "hovered"),
},
disabled: {
color: foreground(theme.highest, "on", "disabled"),
},
},
}),
padding: { left: 8, right: 8, top: 4, bottom: 4 },
},
breadcrumb_height: 24,
breadcrumbs: interactive({
base: {
...text(theme.highest, "sans", "variant"),
corner_radius: 6,
padding: {
left: 6,
right: 6,
},
},
state: {
hovered: {
color: foreground(theme.highest, "on", "hovered"),
background: background(theme.highest, "on", "hovered"),
},
},
}),
disconnected_overlay: {
...text(theme.lowest, "sans"),
background: with_opacity(background(theme.lowest), 0.8),
},
notification: {
margin: { top: 10 },
background: background(theme.middle),
corner_radius: 6,
padding: 12,
border: border(theme.middle),
shadow: theme.popover_shadow,
},
notifications: {
width: 400,
margin: { right: 10, bottom: 10 },
},
drop_target_overlay_color: with_opacity(
foreground(theme.lowest, "variant"),
0.5
),
}
}

View File

@ -1,11 +0,0 @@
/** Converts a percentage scale value (0-100) to normalized scale (0-1) value. */
export function percentageToNormalized(value: number) {
const normalized = value / 100
return normalized
}
/** Converts a normalized scale (0-1) value to a percentage scale (0-100) value. */
export function normalizedToPercetage(value: number) {
const percentage = value * 100
return percentage
}

View File

@ -1,26 +0,0 @@
import bezier from "bezier-easing"
import { Curve } from "../ref/curves"
/**
* Formats our Curve data structure into a bezier easing function.
* @param {Curve} curve - The curve to format.
* @param {Boolean} inverted - Whether or not to invert the curve.
* @returns {EasingFunction} The formatted easing function.
*/
export function curve(curve: Curve, inverted?: Boolean) {
if (inverted) {
return bezier(
curve.value[3],
curve.value[2],
curve.value[1],
curve.value[0]
)
}
return bezier(
curve.value[0],
curve.value[1],
curve.value[2],
curve.value[3]
)
}

View File

@ -1,159 +0,0 @@
import bezier from "bezier-easing"
import chroma from "chroma-js"
import { Color, ColorFamily, ColorFamilyConfig, ColorScale } from "../types"
import { percentageToNormalized } from "./convert"
import { curve } from "./curve"
// Re-export interface in a more standard format
export type EasingFunction = bezier.EasingFunction
/**
* Generates a color, outputs it in multiple formats, and returns a variety of useful metadata.
*
* @param {EasingFunction} hueEasing - An easing function for the hue component of the color.
* @param {EasingFunction} saturationEasing - An easing function for the saturation component of the color.
* @param {EasingFunction} lightnessEasing - An easing function for the lightness component of the color.
* @param {ColorFamilyConfig} family - Configuration for the color family.
* @param {number} step - The current step.
* @param {number} steps - The total number of steps in the color scale.
*
* @returns {Color} The generated color, with its calculated contrast against black and white, as well as its LCH values, RGBA array, hexadecimal representation, and a flag indicating if it is light or dark.
*/
function generateColor(
hueEasing: EasingFunction,
saturationEasing: EasingFunction,
lightnessEasing: EasingFunction,
family: ColorFamilyConfig,
step: number,
steps: number
) {
const { hue, saturation, lightness } = family.color
const stepHue = hueEasing(step / steps) * (hue.end - hue.start) + hue.start
const stepSaturation =
saturationEasing(step / steps) * (saturation.end - saturation.start) +
saturation.start
const stepLightness =
lightnessEasing(step / steps) * (lightness.end - lightness.start) +
lightness.start
const color = chroma.hsl(
stepHue,
percentageToNormalized(stepSaturation),
percentageToNormalized(stepLightness)
)
const contrast = {
black: {
value: chroma.contrast(color, "black"),
aaPass: chroma.contrast(color, "black") >= 4.5,
aaaPass: chroma.contrast(color, "black") >= 7,
},
white: {
value: chroma.contrast(color, "white"),
aaPass: chroma.contrast(color, "white") >= 4.5,
aaaPass: chroma.contrast(color, "white") >= 7,
},
}
const lch = color.lch()
const rgba = color.rgba()
const hex = color.hex()
// 55 is a magic number. It's the lightness value at which we consider a color to be "light".
// It was picked by eye with some testing. We might want to use a more scientific approach in the future.
const isLight = lch[0] > 55
const result: Color = {
step,
lch,
hex,
rgba,
contrast,
isLight,
}
return result
}
/**
* Generates a color scale based on a color family configuration.
*
* @param {ColorFamilyConfig} config - The configuration for the color family.
* @param {Boolean} inverted - Specifies whether the color scale should be inverted or not.
*
* @returns {ColorScale} The generated color scale.
*
* @example
* ```ts
* const colorScale = generateColorScale({
* name: "blue",
* color: {
* hue: {
* start: 210,
* end: 240,
* curve: "easeInOut"
* },
* saturation: {
* start: 100,
* end: 100,
* curve: "easeInOut"
* },
* lightness: {
* start: 50,
* end: 50,
* curve: "easeInOut"
* }
* }
* });
* ```
*/
export function generateColorScale(
config: ColorFamilyConfig,
inverted: Boolean = false
) {
const { hue, saturation, lightness } = config.color
// 101 steps means we get values from 0-100
const NUM_STEPS = 101
const hueEasing = curve(hue.curve, inverted)
const saturationEasing = curve(saturation.curve, inverted)
const lightnessEasing = curve(lightness.curve, inverted)
let scale: ColorScale = {
colors: [],
values: [],
}
for (let i = 0; i < NUM_STEPS; i++) {
const color = generateColor(
hueEasing,
saturationEasing,
lightnessEasing,
config,
i,
NUM_STEPS
)
scale.colors.push(color)
scale.values.push(color.hex)
}
return scale
}
/** Generates a color family with a scale and an inverted scale. */
export function generateColorFamily(config: ColorFamilyConfig) {
const scale = generateColorScale(config, false)
const invertedScale = generateColorScale(config, true)
const family: ColorFamily = {
name: config.name,
scale,
invertedScale,
}
return family
}

View File

@ -1,445 +0,0 @@
import { generateColorFamily } from "../lib/generate"
import { curve } from "./curves"
// These are the source colors for the color scales in the system.
// These should never directly be used directly in components or themes as they generate thousands of lines of code.
// Instead, use the outputs from the reference palette which exports a smaller subset of colors.
// Token or user-facing colors should use short, clear names and a 100-900 scale to match the font weight scale.
// Light Gray ======================================== //
export const lightgray = generateColorFamily({
name: "lightgray",
color: {
hue: {
start: 210,
end: 210,
curve: curve.linear,
},
saturation: {
start: 10,
end: 15,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 50,
curve: curve.linear,
},
},
})
// Light Dark ======================================== //
export const darkgray = generateColorFamily({
name: "darkgray",
color: {
hue: {
start: 210,
end: 210,
curve: curve.linear,
},
saturation: {
start: 15,
end: 20,
curve: curve.saturation,
},
lightness: {
start: 55,
end: 8,
curve: curve.linear,
},
},
})
// Red ======================================== //
export const red = generateColorFamily({
name: "red",
color: {
hue: {
start: 0,
end: 0,
curve: curve.linear,
},
saturation: {
start: 95,
end: 75,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 25,
curve: curve.lightness,
},
},
})
// Sunset ======================================== //
export const sunset = generateColorFamily({
name: "sunset",
color: {
hue: {
start: 15,
end: 15,
curve: curve.linear,
},
saturation: {
start: 100,
end: 90,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 25,
curve: curve.lightness,
},
},
})
// Orange ======================================== //
export const orange = generateColorFamily({
name: "orange",
color: {
hue: {
start: 25,
end: 25,
curve: curve.linear,
},
saturation: {
start: 100,
end: 95,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 20,
curve: curve.lightness,
},
},
})
// Amber ======================================== //
export const amber = generateColorFamily({
name: "amber",
color: {
hue: {
start: 38,
end: 38,
curve: curve.linear,
},
saturation: {
start: 100,
end: 100,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 18,
curve: curve.lightness,
},
},
})
// Yellow ======================================== //
export const yellow = generateColorFamily({
name: "yellow",
color: {
hue: {
start: 48,
end: 48,
curve: curve.linear,
},
saturation: {
start: 90,
end: 100,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 15,
curve: curve.lightness,
},
},
})
// Lemon ======================================== //
export const lemon = generateColorFamily({
name: "lemon",
color: {
hue: {
start: 55,
end: 55,
curve: curve.linear,
},
saturation: {
start: 85,
end: 95,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 15,
curve: curve.lightness,
},
},
})
// Citron ======================================== //
export const citron = generateColorFamily({
name: "citron",
color: {
hue: {
start: 70,
end: 70,
curve: curve.linear,
},
saturation: {
start: 85,
end: 90,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 15,
curve: curve.lightness,
},
},
})
// Lime ======================================== //
export const lime = generateColorFamily({
name: "lime",
color: {
hue: {
start: 85,
end: 85,
curve: curve.linear,
},
saturation: {
start: 85,
end: 80,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 18,
curve: curve.lightness,
},
},
})
// Green ======================================== //
export const green = generateColorFamily({
name: "green",
color: {
hue: {
start: 108,
end: 108,
curve: curve.linear,
},
saturation: {
start: 60,
end: 70,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 18,
curve: curve.lightness,
},
},
})
// Mint ======================================== //
export const mint = generateColorFamily({
name: "mint",
color: {
hue: {
start: 142,
end: 142,
curve: curve.linear,
},
saturation: {
start: 60,
end: 75,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 20,
curve: curve.lightness,
},
},
})
// Cyan ======================================== //
export const cyan = generateColorFamily({
name: "cyan",
color: {
hue: {
start: 179,
end: 179,
curve: curve.linear,
},
saturation: {
start: 70,
end: 80,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 20,
curve: curve.lightness,
},
},
})
// Sky ======================================== //
export const sky = generateColorFamily({
name: "sky",
color: {
hue: {
start: 195,
end: 205,
curve: curve.linear,
},
saturation: {
start: 85,
end: 90,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 15,
curve: curve.lightness,
},
},
})
// Blue ======================================== //
export const blue = generateColorFamily({
name: "blue",
color: {
hue: {
start: 218,
end: 218,
curve: curve.linear,
},
saturation: {
start: 85,
end: 70,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 15,
curve: curve.lightness,
},
},
})
// Indigo ======================================== //
export const indigo = generateColorFamily({
name: "indigo",
color: {
hue: {
start: 245,
end: 245,
curve: curve.linear,
},
saturation: {
start: 60,
end: 50,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 22,
curve: curve.lightness,
},
},
})
// Purple ======================================== //
export const purple = generateColorFamily({
name: "purple",
color: {
hue: {
start: 260,
end: 270,
curve: curve.linear,
},
saturation: {
start: 65,
end: 55,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 20,
curve: curve.lightness,
},
},
})
// Pink ======================================== //
export const pink = generateColorFamily({
name: "pink",
color: {
hue: {
start: 320,
end: 330,
curve: curve.linear,
},
saturation: {
start: 70,
end: 65,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 32,
curve: curve.lightness,
},
},
})
// Rose ======================================== //
export const rose = generateColorFamily({
name: "rose",
color: {
hue: {
start: 345,
end: 345,
curve: curve.linear,
},
saturation: {
start: 90,
end: 70,
curve: curve.saturation,
},
lightness: {
start: 97,
end: 32,
curve: curve.lightness,
},
},
})

View File

@ -1,25 +0,0 @@
export interface Curve {
name: string
value: number[]
}
export interface Curves {
lightness: Curve
saturation: Curve
linear: Curve
}
export const curve: Curves = {
lightness: {
name: "lightnessCurve",
value: [0.2, 0, 0.75, 1.0],
},
saturation: {
name: "saturationCurve",
value: [0.67, 0.6, 0.55, 1.0],
},
linear: {
name: "linear",
value: [0.5, 0.5, 0.5, 0.5],
},
}

View File

@ -1,32 +0,0 @@
import chroma from "chroma-js"
import * as colorFamily from "./ref/color"
const color = {
lightgray: chroma
.scale(colorFamily.lightgray.scale.values)
.mode("lch")
.colors(9),
darkgray: chroma
.scale(colorFamily.darkgray.scale.values)
.mode("lch")
.colors(9),
red: chroma.scale(colorFamily.red.scale.values).mode("lch").colors(9),
sunset: chroma.scale(colorFamily.sunset.scale.values).mode("lch").colors(9),
orange: chroma.scale(colorFamily.orange.scale.values).mode("lch").colors(9),
amber: chroma.scale(colorFamily.amber.scale.values).mode("lch").colors(9),
yellow: chroma.scale(colorFamily.yellow.scale.values).mode("lch").colors(9),
lemon: chroma.scale(colorFamily.lemon.scale.values).mode("lch").colors(9),
citron: chroma.scale(colorFamily.citron.scale.values).mode("lch").colors(9),
lime: chroma.scale(colorFamily.lime.scale.values).mode("lch").colors(9),
green: chroma.scale(colorFamily.green.scale.values).mode("lch").colors(9),
mint: chroma.scale(colorFamily.mint.scale.values).mode("lch").colors(9),
cyan: chroma.scale(colorFamily.cyan.scale.values).mode("lch").colors(9),
sky: chroma.scale(colorFamily.sky.scale.values).mode("lch").colors(9),
blue: chroma.scale(colorFamily.blue.scale.values).mode("lch").colors(9),
indigo: chroma.scale(colorFamily.indigo.scale.values).mode("lch").colors(9),
purple: chroma.scale(colorFamily.purple.scale.values).mode("lch").colors(9),
pink: chroma.scale(colorFamily.pink.scale.values).mode("lch").colors(9),
rose: chroma.scale(colorFamily.rose.scale.values).mode("lch").colors(9),
}
export { color }

View File

@ -1,66 +0,0 @@
import { Curve } from "./ref/curves"
export interface ColorAccessibilityValue {
value: number
aaPass: boolean
aaaPass: boolean
}
/**
* Calculates the color contrast between a specified color and its corresponding background and foreground colors.
*
* @note This implementation is currently basic Currently we only calculate contrasts against black and white, in the future will allow for dynamic color contrast calculation based on the colors present in a given palette.
* @note The goal is to align with WCAG3 accessibility standards as they become stabilized. See the [WCAG 3 Introduction](https://www.w3.org/WAI/standards-guidelines/wcag/wcag3-intro/) for more information.
*/
export interface ColorAccessibility {
black: ColorAccessibilityValue
white: ColorAccessibilityValue
}
export type Color = {
step: number
contrast: ColorAccessibility
hex: string
lch: number[]
rgba: number[]
isLight: boolean
}
export interface ColorScale {
colors: Color[]
// An array of hex values for each color in the scale
values: string[]
}
export type ColorFamily = {
name: string
scale: ColorScale
invertedScale: ColorScale
}
export interface ColorFamilyHue {
start: number
end: number
curve: Curve
}
export interface ColorFamilySaturation {
start: number
end: number
curve: Curve
}
export interface ColorFamilyLightness {
start: number
end: number
curve: Curve
}
export interface ColorFamilyConfig {
name: string
color: {
hue: ColorFamilyHue
saturation: ColorFamilySaturation
lightness: ColorFamilyLightness
}
}

View File

@ -1,5 +1,5 @@
import chroma from "chroma-js"
export function withOpacity(color: string, opacity: number): string {
export function with_opacity(color: string, opacity: number): string {
return chroma(color).alpha(opacity).hex()
}

View File

@ -1,286 +0,0 @@
import { Scale, Color } from "chroma-js"
import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax"
export { Syntax, ThemeSyntax, SyntaxHighlightStyle }
import {
ThemeConfig,
ThemeAppearance,
ThemeConfigInputColors,
} from "./themeConfig"
import { getRamps } from "./ramps"
export interface ColorScheme {
name: string
isLight: boolean
lowest: Layer
middle: Layer
highest: Layer
ramps: RampSet
popoverShadow: Shadow
modalShadow: Shadow
players: Players
syntax?: Partial<ThemeSyntax>
}
export interface Meta {
name: string
author: string
url: string
license: License
}
export interface License {
SPDX: SPDXExpression
}
// License name -> License text
export interface Licenses {
[key: string]: string
}
// FIXME: Add support for the SPDX expression syntax
export type SPDXExpression = "MIT"
export interface Player {
cursor: string
selection: string
}
export interface Players {
"0": Player
"1": Player
"2": Player
"3": Player
"4": Player
"5": Player
"6": Player
"7": Player
}
export interface Shadow {
blur: number
color: string
offset: number[]
}
export type StyleSets = keyof Layer
export interface Layer {
base: StyleSet
variant: StyleSet
on: StyleSet
accent: StyleSet
positive: StyleSet
warning: StyleSet
negative: StyleSet
}
export interface RampSet {
neutral: Scale
red: Scale
orange: Scale
yellow: Scale
green: Scale
cyan: Scale
blue: Scale
violet: Scale
magenta: Scale
}
export type Styles = keyof StyleSet
export interface StyleSet {
default: Style
active: Style
disabled: Style
hovered: Style
pressed: Style
inverted: Style
}
export interface Style {
background: string
border: string
foreground: string
}
export function createColorScheme(theme: ThemeConfig): ColorScheme {
const {
name,
appearance,
inputColor,
override: { syntax },
} = theme
const isLight = appearance === ThemeAppearance.Light
const colorRamps: ThemeConfigInputColors = inputColor
// Chromajs scales from 0 to 1 flipped if isLight is true
const ramps = getRamps(isLight, colorRamps)
const lowest = lowestLayer(ramps)
const middle = middleLayer(ramps)
const highest = highestLayer(ramps)
const popoverShadow = {
blur: 4,
color: ramps
.neutral(isLight ? 7 : 0)
.darken()
.alpha(0.2)
.hex(), // TODO used blend previously. Replace with something else
offset: [1, 2],
}
const modalShadow = {
blur: 16,
color: ramps
.neutral(isLight ? 7 : 0)
.darken()
.alpha(0.2)
.hex(), // TODO used blend previously. Replace with something else
offset: [0, 2],
}
const players = {
"0": player(ramps.blue),
"1": player(ramps.green),
"2": player(ramps.magenta),
"3": player(ramps.orange),
"4": player(ramps.violet),
"5": player(ramps.cyan),
"6": player(ramps.red),
"7": player(ramps.yellow),
}
return {
name,
isLight,
ramps,
lowest,
middle,
highest,
popoverShadow,
modalShadow,
players,
syntax,
}
}
function player(ramp: Scale): Player {
return {
selection: ramp(0.5).alpha(0.24).hex(),
cursor: ramp(0.5).hex(),
}
}
function lowestLayer(ramps: RampSet): Layer {
return {
base: buildStyleSet(ramps.neutral, 0.2, 1),
variant: buildStyleSet(ramps.neutral, 0.2, 0.7),
on: buildStyleSet(ramps.neutral, 0.1, 1),
accent: buildStyleSet(ramps.blue, 0.1, 0.5),
positive: buildStyleSet(ramps.green, 0.1, 0.5),
warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
negative: buildStyleSet(ramps.red, 0.1, 0.5),
}
}
function middleLayer(ramps: RampSet): Layer {
return {
base: buildStyleSet(ramps.neutral, 0.1, 1),
variant: buildStyleSet(ramps.neutral, 0.1, 0.7),
on: buildStyleSet(ramps.neutral, 0, 1),
accent: buildStyleSet(ramps.blue, 0.1, 0.5),
positive: buildStyleSet(ramps.green, 0.1, 0.5),
warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
negative: buildStyleSet(ramps.red, 0.1, 0.5),
}
}
function highestLayer(ramps: RampSet): Layer {
return {
base: buildStyleSet(ramps.neutral, 0, 1),
variant: buildStyleSet(ramps.neutral, 0, 0.7),
on: buildStyleSet(ramps.neutral, 0.1, 1),
accent: buildStyleSet(ramps.blue, 0.1, 0.5),
positive: buildStyleSet(ramps.green, 0.1, 0.5),
warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
negative: buildStyleSet(ramps.red, 0.1, 0.5),
}
}
function buildStyleSet(
ramp: Scale,
backgroundBase: number,
foregroundBase: number,
step: number = 0.08
): StyleSet {
let styleDefinitions = buildStyleDefinition(
backgroundBase,
foregroundBase,
step
)
function colorString(indexOrColor: number | Color): string {
if (typeof indexOrColor === "number") {
return ramp(indexOrColor).hex()
} else {
return indexOrColor.hex()
}
}
function buildStyle(style: Styles): Style {
return {
background: colorString(styleDefinitions.background[style]),
border: colorString(styleDefinitions.border[style]),
foreground: colorString(styleDefinitions.foreground[style]),
}
}
return {
default: buildStyle("default"),
hovered: buildStyle("hovered"),
pressed: buildStyle("pressed"),
active: buildStyle("active"),
disabled: buildStyle("disabled"),
inverted: buildStyle("inverted"),
}
}
function buildStyleDefinition(
bgBase: number,
fgBase: number,
step: number = 0.08
) {
return {
background: {
default: bgBase,
hovered: bgBase + step,
pressed: bgBase + step * 1.5,
active: bgBase + step * 2.2,
disabled: bgBase,
inverted: fgBase + step * 6,
},
border: {
default: bgBase + step * 1,
hovered: bgBase + step,
pressed: bgBase + step,
active: bgBase + step * 3,
disabled: bgBase + step * 0.5,
inverted: bgBase - step * 3,
},
foreground: {
default: fgBase,
hovered: fgBase,
pressed: fgBase,
active: fgBase + step * 6,
disabled: bgBase + step * 4,
inverted: bgBase + step * 2,
},
}
}

View File

@ -0,0 +1,282 @@
import { Scale, Color } from "chroma-js"
import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax"
export { Syntax, ThemeSyntax, SyntaxHighlightStyle }
import {
ThemeConfig,
ThemeAppearance,
ThemeConfigInputColors,
} from "./theme_config"
import { get_ramps } from "./ramps"
export interface ColorScheme {
name: string
is_light: boolean
lowest: Layer
middle: Layer
highest: Layer
ramps: RampSet
popover_shadow: Shadow
modal_shadow: Shadow
players: Players
syntax?: Partial<ThemeSyntax>
}
export interface Meta {
name: string
author: string
url: string
license: License
}
export interface License {
SPDX: SPDXExpression
}
// License name -> License text
export interface Licenses {
[key: string]: string
}
// FIXME: Add support for the SPDX expression syntax
export type SPDXExpression = "MIT"
export interface Player {
cursor: string
selection: string
}
export interface Players {
"0": Player
"1": Player
"2": Player
"3": Player
"4": Player
"5": Player
"6": Player
"7": Player
}
export interface Shadow {
blur: number
color: string
offset: number[]
}
export type StyleSets = keyof Layer
export interface Layer {
base: StyleSet
variant: StyleSet
on: StyleSet
accent: StyleSet
positive: StyleSet
warning: StyleSet
negative: StyleSet
}
export interface RampSet {
neutral: Scale
red: Scale
orange: Scale
yellow: Scale
green: Scale
cyan: Scale
blue: Scale
violet: Scale
magenta: Scale
}
export type Styles = keyof StyleSet
export interface StyleSet {
default: Style
active: Style
disabled: Style
hovered: Style
pressed: Style
inverted: Style
}
export interface Style {
background: string
border: string
foreground: string
}
export function create_color_scheme(theme: ThemeConfig): ColorScheme {
const {
name,
appearance,
input_color,
override: { syntax },
} = theme
const is_light = appearance === ThemeAppearance.Light
const color_ramps: ThemeConfigInputColors = input_color
// Chromajs scales from 0 to 1 flipped if is_light is true
const ramps = get_ramps(is_light, color_ramps)
const lowest = lowest_layer(ramps)
const middle = middle_layer(ramps)
const highest = highest_layer(ramps)
const popover_shadow = {
blur: 4,
color: ramps
.neutral(is_light ? 7 : 0)
.darken()
.alpha(0.2)
.hex(), // TODO used blend previously. Replace with something else
offset: [1, 2],
}
const modal_shadow = {
blur: 16,
color: ramps
.neutral(is_light ? 7 : 0)
.darken()
.alpha(0.2)
.hex(), // TODO used blend previously. Replace with something else
offset: [0, 2],
}
const players = {
"0": player(ramps.blue),
"1": player(ramps.green),
"2": player(ramps.magenta),
"3": player(ramps.orange),
"4": player(ramps.violet),
"5": player(ramps.cyan),
"6": player(ramps.red),
"7": player(ramps.yellow),
}
return {
name,
is_light,
ramps,
lowest,
middle,
highest,
popover_shadow,
modal_shadow,
players,
syntax,
}
}
function player(ramp: Scale): Player {
return {
selection: ramp(0.5).alpha(0.24).hex(),
cursor: ramp(0.5).hex(),
}
}
function lowest_layer(ramps: RampSet): Layer {
return {
base: build_style_set(ramps.neutral, 0.2, 1),
variant: build_style_set(ramps.neutral, 0.2, 0.7),
on: build_style_set(ramps.neutral, 0.1, 1),
accent: build_style_set(ramps.blue, 0.1, 0.5),
positive: build_style_set(ramps.green, 0.1, 0.5),
warning: build_style_set(ramps.yellow, 0.1, 0.5),
negative: build_style_set(ramps.red, 0.1, 0.5),
}
}
function middle_layer(ramps: RampSet): Layer {
return {
base: build_style_set(ramps.neutral, 0.1, 1),
variant: build_style_set(ramps.neutral, 0.1, 0.7),
on: build_style_set(ramps.neutral, 0, 1),
accent: build_style_set(ramps.blue, 0.1, 0.5),
positive: build_style_set(ramps.green, 0.1, 0.5),
warning: build_style_set(ramps.yellow, 0.1, 0.5),
negative: build_style_set(ramps.red, 0.1, 0.5),
}
}
function highest_layer(ramps: RampSet): Layer {
return {
base: build_style_set(ramps.neutral, 0, 1),
variant: build_style_set(ramps.neutral, 0, 0.7),
on: build_style_set(ramps.neutral, 0.1, 1),
accent: build_style_set(ramps.blue, 0.1, 0.5),
positive: build_style_set(ramps.green, 0.1, 0.5),
warning: build_style_set(ramps.yellow, 0.1, 0.5),
negative: build_style_set(ramps.red, 0.1, 0.5),
}
}
function build_style_set(
ramp: Scale,
background_base: number,
foreground_base: number,
step = 0.08
): StyleSet {
const style_definitions = build_style_definition(
background_base,
foreground_base,
step
)
function color_string(index_or_color: number | Color): string {
if (typeof index_or_color === "number") {
return ramp(index_or_color).hex()
} else {
return index_or_color.hex()
}
}
function build_style(style: Styles): Style {
return {
background: color_string(style_definitions.background[style]),
border: color_string(style_definitions.border[style]),
foreground: color_string(style_definitions.foreground[style]),
}
}
return {
default: build_style("default"),
hovered: build_style("hovered"),
pressed: build_style("pressed"),
active: build_style("active"),
disabled: build_style("disabled"),
inverted: build_style("inverted"),
}
}
function build_style_definition(bg_base: number, fg_base: number, step = 0.08) {
return {
background: {
default: bg_base,
hovered: bg_base + step,
pressed: bg_base + step * 1.5,
active: bg_base + step * 2.2,
disabled: bg_base,
inverted: fg_base + step * 6,
},
border: {
default: bg_base + step * 1,
hovered: bg_base + step,
pressed: bg_base + step,
active: bg_base + step * 3,
disabled: bg_base + step * 0.5,
inverted: bg_base - step * 3,
},
foreground: {
default: fg_base,
hovered: fg_base,
pressed: fg_base,
active: fg_base + step * 6,
disabled: bg_base + step * 4,
inverted: bg_base + step * 2,
},
}
}

View File

@ -1,4 +1,4 @@
export * from "./colorScheme"
export * from "./color_scheme"
export * from "./ramps"
export * from "./syntax"
export * from "./themeConfig"
export * from "./theme_config"

View File

@ -1,14 +1,14 @@
import chroma, { Color, Scale } from "chroma-js"
import { RampSet } from "./colorScheme"
import { RampSet } from "./color_scheme"
import {
ThemeConfigInputColors,
ThemeConfigInputColorsKeys,
} from "./themeConfig"
} from "./theme_config"
export function colorRamp(color: Color): Scale {
let endColor = color.desaturate(1).brighten(5)
let startColor = color.desaturate(1).darken(4)
return chroma.scale([startColor, color, endColor]).mode("lab")
export function color_ramp(color: Color): Scale {
const end_color = color.desaturate(1).brighten(5)
const start_color = color.desaturate(1).darken(4)
return chroma.scale([start_color, color, end_color]).mode("lab")
}
/**
@ -18,29 +18,29 @@ export function colorRamp(color: Color): Scale {
theme so that we don't modify the passed in ramps.
This combined with an error in the type definitions for chroma js means we have to cast the colors
function to any in order to get the colors back out from the original ramps.
* @param isLight
* @param colorRamps
* @returns
* @param is_light
* @param color_ramps
* @returns
*/
export function getRamps(
isLight: boolean,
colorRamps: ThemeConfigInputColors
export function get_ramps(
is_light: boolean,
color_ramps: ThemeConfigInputColors
): RampSet {
const ramps: RampSet = {} as any
const colorsKeys = Object.keys(colorRamps) as ThemeConfigInputColorsKeys[]
const ramps: RampSet = {} as any // eslint-disable-line @typescript-eslint/no-explicit-any
const color_keys = Object.keys(color_ramps) as ThemeConfigInputColorsKeys[]
if (isLight) {
for (const rampName of colorsKeys) {
ramps[rampName] = chroma.scale(
colorRamps[rampName].colors(100).reverse()
if (is_light) {
for (const ramp_name of color_keys) {
ramps[ramp_name] = chroma.scale(
color_ramps[ramp_name].colors(100).reverse()
)
}
ramps.neutral = chroma.scale(colorRamps.neutral.colors(100).reverse())
ramps.neutral = chroma.scale(color_ramps.neutral.colors(100).reverse())
} else {
for (const rampName of colorsKeys) {
ramps[rampName] = chroma.scale(colorRamps[rampName].colors(100))
for (const ramp_name of color_keys) {
ramps[ramp_name] = chroma.scale(color_ramps[ramp_name].colors(100))
}
ramps.neutral = chroma.scale(colorRamps.neutral.colors(100))
ramps.neutral = chroma.scale(color_ramps.neutral.colors(100))
}
return ramps

View File

@ -1,6 +1,6 @@
import deepmerge from "deepmerge"
import { FontWeight, fontWeights } from "../common"
import { ColorScheme } from "./colorScheme"
import { FontWeight, font_weights } from "../common"
import { ColorScheme } from "./color_scheme"
import chroma from "chroma-js"
export interface SyntaxHighlightStyle {
@ -22,8 +22,8 @@ export interface Syntax {
emphasis: SyntaxHighlightStyle
"emphasis.strong": SyntaxHighlightStyle
title: SyntaxHighlightStyle
linkUri: SyntaxHighlightStyle
linkText: SyntaxHighlightStyle
link_uri: SyntaxHighlightStyle
link_text: SyntaxHighlightStyle
/** md: indented_code_block, fenced_code_block, code_span */
"text.literal": SyntaxHighlightStyle
@ -56,7 +56,7 @@ export interface Syntax {
// == Types ====== /
// We allow Function here because all JS objects literals have this property
constructor: SyntaxHighlightStyle | Function
constructor: SyntaxHighlightStyle | Function // eslint-disable-line @typescript-eslint/ban-types
variant: SyntaxHighlightStyle
type: SyntaxHighlightStyle
// js: predefined_type
@ -116,13 +116,13 @@ export interface Syntax {
export type ThemeSyntax = Partial<Syntax>
const defaultSyntaxHighlightStyle: Omit<SyntaxHighlightStyle, "color"> = {
const default_syntax_highlight_style: Omit<SyntaxHighlightStyle, "color"> = {
weight: "normal",
underline: false,
italic: false,
}
function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
function build_default_syntax(color_scheme: ColorScheme): Syntax {
// Make a temporary object that is allowed to be missing
// the "color" property for each style
const syntax: {
@ -132,7 +132,7 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
// then spread the default to each style
for (const key of Object.keys({} as Syntax)) {
syntax[key as keyof Syntax] = {
...defaultSyntaxHighlightStyle,
...default_syntax_highlight_style,
}
}
@ -140,35 +140,35 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
// predictive color distinct from any other color in the theme
const predictive = chroma
.mix(
colorScheme.ramps.neutral(0.4).hex(),
colorScheme.ramps.blue(0.4).hex(),
color_scheme.ramps.neutral(0.4).hex(),
color_scheme.ramps.blue(0.4).hex(),
0.45,
"lch"
)
.hex()
const color = {
primary: colorScheme.ramps.neutral(1).hex(),
comment: colorScheme.ramps.neutral(0.71).hex(),
punctuation: colorScheme.ramps.neutral(0.86).hex(),
primary: color_scheme.ramps.neutral(1).hex(),
comment: color_scheme.ramps.neutral(0.71).hex(),
punctuation: color_scheme.ramps.neutral(0.86).hex(),
predictive: predictive,
emphasis: colorScheme.ramps.blue(0.5).hex(),
string: colorScheme.ramps.orange(0.5).hex(),
function: colorScheme.ramps.yellow(0.5).hex(),
type: colorScheme.ramps.cyan(0.5).hex(),
constructor: colorScheme.ramps.blue(0.5).hex(),
variant: colorScheme.ramps.blue(0.5).hex(),
property: colorScheme.ramps.blue(0.5).hex(),
enum: colorScheme.ramps.orange(0.5).hex(),
operator: colorScheme.ramps.orange(0.5).hex(),
number: colorScheme.ramps.green(0.5).hex(),
boolean: colorScheme.ramps.green(0.5).hex(),
constant: colorScheme.ramps.green(0.5).hex(),
keyword: colorScheme.ramps.blue(0.5).hex(),
emphasis: color_scheme.ramps.blue(0.5).hex(),
string: color_scheme.ramps.orange(0.5).hex(),
function: color_scheme.ramps.yellow(0.5).hex(),
type: color_scheme.ramps.cyan(0.5).hex(),
constructor: color_scheme.ramps.blue(0.5).hex(),
variant: color_scheme.ramps.blue(0.5).hex(),
property: color_scheme.ramps.blue(0.5).hex(),
enum: color_scheme.ramps.orange(0.5).hex(),
operator: color_scheme.ramps.orange(0.5).hex(),
number: color_scheme.ramps.green(0.5).hex(),
boolean: color_scheme.ramps.green(0.5).hex(),
constant: color_scheme.ramps.green(0.5).hex(),
keyword: color_scheme.ramps.blue(0.5).hex(),
}
// Then assign colors and use Syntax to enforce each style getting it's own color
const defaultSyntax: Syntax = {
const default_syntax: Syntax = {
...syntax,
comment: {
color: color.comment,
@ -188,18 +188,18 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
},
"emphasis.strong": {
color: color.emphasis,
weight: fontWeights.bold,
weight: font_weights.bold,
},
title: {
color: color.primary,
weight: fontWeights.bold,
weight: font_weights.bold,
},
linkUri: {
color: colorScheme.ramps.green(0.5).hex(),
link_uri: {
color: color_scheme.ramps.green(0.5).hex(),
underline: true,
},
linkText: {
color: colorScheme.ramps.orange(0.5).hex(),
link_text: {
color: color_scheme.ramps.orange(0.5).hex(),
italic: true,
},
"text.literal": {
@ -215,7 +215,7 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
color: color.punctuation,
},
"punctuation.special": {
color: colorScheme.ramps.neutral(0.86).hex(),
color: color_scheme.ramps.neutral(0.86).hex(),
},
"punctuation.list_marker": {
color: color.punctuation,
@ -236,10 +236,10 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
color: color.string,
},
constructor: {
color: colorScheme.ramps.blue(0.5).hex(),
color: color_scheme.ramps.blue(0.5).hex(),
},
variant: {
color: colorScheme.ramps.blue(0.5).hex(),
color: color_scheme.ramps.blue(0.5).hex(),
},
type: {
color: color.type,
@ -248,16 +248,16 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
color: color.primary,
},
label: {
color: colorScheme.ramps.blue(0.5).hex(),
color: color_scheme.ramps.blue(0.5).hex(),
},
tag: {
color: colorScheme.ramps.blue(0.5).hex(),
color: color_scheme.ramps.blue(0.5).hex(),
},
attribute: {
color: colorScheme.ramps.blue(0.5).hex(),
color: color_scheme.ramps.blue(0.5).hex(),
},
property: {
color: colorScheme.ramps.blue(0.5).hex(),
color: color_scheme.ramps.blue(0.5).hex(),
},
constant: {
color: color.constant,
@ -288,17 +288,20 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
},
}
return defaultSyntax
return default_syntax
}
function mergeSyntax(defaultSyntax: Syntax, colorScheme: ColorScheme): Syntax {
if (!colorScheme.syntax) {
return defaultSyntax
function merge_syntax(
default_syntax: Syntax,
color_scheme: ColorScheme
): Syntax {
if (!color_scheme.syntax) {
return default_syntax
}
return deepmerge<Syntax, Partial<ThemeSyntax>>(
defaultSyntax,
colorScheme.syntax,
default_syntax,
color_scheme.syntax,
{
arrayMerge: (destinationArray, sourceArray) => [
...destinationArray,
@ -308,10 +311,10 @@ function mergeSyntax(defaultSyntax: Syntax, colorScheme: ColorScheme): Syntax {
)
}
export function buildSyntax(colorScheme: ColorScheme): Syntax {
const defaultSyntax: Syntax = buildDefaultSyntax(colorScheme)
export function build_syntax(color_scheme: ColorScheme): Syntax {
const default_syntax: Syntax = build_default_syntax(color_scheme)
const syntax = mergeSyntax(defaultSyntax, colorScheme)
const syntax = merge_syntax(default_syntax, color_scheme)
return syntax
}

View File

@ -17,15 +17,15 @@ interface ThemeMeta {
*
* Example: `MIT`
*/
licenseType?: string | ThemeLicenseType
licenseUrl?: string
licenseFile: string
themeUrl?: string
license_type?: string | ThemeLicenseType
license_url?: string
license_file: string
theme_url?: string
}
export type ThemeFamilyMeta = Pick<
ThemeMeta,
"name" | "author" | "licenseType" | "licenseUrl"
"name" | "author" | "license_type" | "license_url"
>
export interface ThemeConfigInputColors {
@ -62,7 +62,7 @@ interface ThemeConfigOverrides {
}
type ThemeConfigProperties = ThemeMeta & {
inputColor: ThemeConfigInputColors
input_color: ThemeConfigInputColors
override: ThemeConfigOverrides
}

View File

@ -1,99 +0,0 @@
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 {
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 {
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),
}
}

View File

@ -0,0 +1,97 @@
import {
SingleBoxShadowToken,
SingleColorToken,
SingleOtherToken,
TokenTypes,
} from "@tokens-studio/types"
import {
ColorScheme,
Shadow,
SyntaxHighlightStyle,
ThemeSyntax,
} from "../color_scheme"
import { LayerToken, layer_token } from "./layer"
import { PlayersToken, players_token } from "./players"
import { color_token } from "./token"
import { Syntax } from "../syntax"
import editor from "../../style_tree/editor"
interface ColorSchemeTokens {
name: SingleOtherToken
appearance: SingleOtherToken
lowest: LayerToken
middle: LayerToken
highest: LayerToken
players: PlayersToken
popover_shadow: SingleBoxShadowToken
modal_shadow: SingleBoxShadowToken
syntax?: Partial<ThemeSyntaxColorTokens>
}
const create_shadow_token = (
shadow: Shadow,
token_name: string
): SingleBoxShadowToken => {
return {
name: token_name,
type: TokenTypes.BOX_SHADOW,
value: `${shadow.offset[0]}px ${shadow.offset[1]}px ${shadow.blur}px 0px ${shadow.color}`,
}
}
const popover_shadow_token = (theme: ColorScheme): SingleBoxShadowToken => {
const shadow = theme.popover_shadow
return create_shadow_token(shadow, "popover_shadow")
}
const modal_shadow_token = (theme: ColorScheme): SingleBoxShadowToken => {
const shadow = theme.modal_shadow
return create_shadow_token(shadow, "modal_shadow")
}
type ThemeSyntaxColorTokens = Record<keyof ThemeSyntax, SingleColorToken>
function syntax_highlight_style_color_tokens(
syntax: Syntax
): ThemeSyntaxColorTokens {
const style_keys = Object.keys(syntax) as (keyof Syntax)[]
return style_keys.reduce((acc, style_key) => {
// 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[style_key] || typeof syntax[style_key] === "function")
return acc
const { color } = syntax[style_key] as Required<SyntaxHighlightStyle>
return { ...acc, [style_key]: color_token(style_key, color) }
}, {} as ThemeSyntaxColorTokens)
}
const syntax_tokens = (theme: ColorScheme): ColorSchemeTokens["syntax"] => {
const syntax = editor(theme).syntax
return syntax_highlight_style_color_tokens(syntax)
}
export function theme_tokens(theme: ColorScheme): ColorSchemeTokens {
return {
name: {
name: "themeName",
value: theme.name,
type: TokenTypes.OTHER,
},
appearance: {
name: "themeAppearance",
value: theme.is_light ? "light" : "dark",
type: TokenTypes.OTHER,
},
lowest: layer_token(theme.lowest, "lowest"),
middle: layer_token(theme.middle, "middle"),
highest: layer_token(theme.highest, "highest"),
popover_shadow: popover_shadow_token(theme),
modal_shadow: modal_shadow_token(theme),
players: players_token(theme),
syntax: syntax_tokens(theme),
}
}

View File

@ -1,6 +1,6 @@
import { SingleColorToken } from "@tokens-studio/types"
import { Layer, Style, StyleSet } from "../colorScheme"
import { colorToken } from "./token"
import { Layer, Style, StyleSet } from "../color_scheme"
import { color_token } from "./token"
interface StyleToken {
background: SingleColorToken
@ -27,36 +27,36 @@ export interface LayerToken {
negative: StyleSetToken
}
export const styleToken = (style: Style, name: string): StyleToken => {
export const style_token = (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),
background: color_token(`${name}Background`, style.background),
border: color_token(`${name}Border`, style.border),
foreground: color_token(`${name}Foreground`, style.foreground),
}
return token
}
export const styleSetToken = (
styleSet: StyleSet,
export const style_set_token = (
style_set: StyleSet,
name: string
): StyleSetToken => {
const token: StyleSetToken = {} as StyleSetToken
for (const style in styleSet) {
for (const style in style_set) {
const s = style as keyof StyleSet
token[s] = styleToken(styleSet[s], `${name}${style}`)
token[s] = style_token(style_set[s], `${name}${style}`)
}
return token
}
export const layerToken = (layer: Layer, name: string): LayerToken => {
export const layer_token = (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}`)
for (const style_set in layer) {
const s = style_set as keyof Layer
token[s] = style_set_token(layer[s], `${name}${style_set}`)
}
return token

View File

@ -1,36 +1,33 @@
import { SingleColorToken } from "@tokens-studio/types"
import { ColorScheme, Players } from "../../common"
import { colorToken } from "./token"
import { color_token } from "./token"
import { ColorScheme, Players } from "../color_scheme"
export type PlayerToken = Record<"selection" | "cursor", SingleColorToken>
export type PlayersToken = Record<keyof Players, PlayerToken>
function buildPlayerToken(
colorScheme: ColorScheme,
index: number
): PlayerToken {
const playerNumber = index.toString() as keyof Players
function build_player_token(theme: ColorScheme, index: number): PlayerToken {
const player_number = index.toString() as keyof Players
return {
selection: colorToken(
selection: color_token(
`player${index}Selection`,
colorScheme.players[playerNumber].selection
theme.players[player_number].selection
),
cursor: colorToken(
cursor: color_token(
`player${index}Cursor`,
colorScheme.players[playerNumber].cursor
theme.players[player_number].cursor
),
}
}
export const playersToken = (colorScheme: ColorScheme): PlayersToken => ({
"0": buildPlayerToken(colorScheme, 0),
"1": buildPlayerToken(colorScheme, 1),
"2": buildPlayerToken(colorScheme, 2),
"3": buildPlayerToken(colorScheme, 3),
"4": buildPlayerToken(colorScheme, 4),
"5": buildPlayerToken(colorScheme, 5),
"6": buildPlayerToken(colorScheme, 6),
"7": buildPlayerToken(colorScheme, 7),
export const players_token = (theme: ColorScheme): PlayersToken => ({
"0": build_player_token(theme, 0),
"1": build_player_token(theme, 1),
"2": build_player_token(theme, 2),
"3": build_player_token(theme, 3),
"4": build_player_token(theme, 4),
"5": build_player_token(theme, 5),
"6": build_player_token(theme, 6),
"7": build_player_token(theme, 7),
})

View File

@ -1,6 +1,6 @@
import { SingleColorToken, TokenTypes } from "@tokens-studio/types"
export function colorToken(
export function color_token(
name: string,
value: string,
description?: string

View File

@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@ -1,6 +1,6 @@
import {
chroma,
colorRamp,
color_ramp,
ThemeAppearance,
ThemeLicenseType,
ThemeConfig,
@ -10,10 +10,10 @@ export const dark: ThemeConfig = {
name: "Andromeda",
author: "EliverLara",
appearance: ThemeAppearance.Dark,
licenseType: ThemeLicenseType.MIT,
licenseUrl: "https://github.com/EliverLara/Andromeda",
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: ThemeLicenseType.MIT,
license_url: "https://github.com/EliverLara/Andromeda",
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma
.scale([
"#1E2025",
@ -26,14 +26,14 @@ export const dark: ThemeConfig = {
"#F7F7F8",
])
.domain([0, 0.15, 0.25, 0.35, 0.7, 0.8, 0.9, 1]),
red: colorRamp(chroma("#F92672")),
orange: colorRamp(chroma("#F39C12")),
yellow: colorRamp(chroma("#FFE66D")),
green: colorRamp(chroma("#96E072")),
cyan: colorRamp(chroma("#00E8C6")),
blue: colorRamp(chroma("#0CA793")),
violet: colorRamp(chroma("#8A3FA6")),
magenta: colorRamp(chroma("#C74DED")),
red: color_ramp(chroma("#F92672")),
orange: color_ramp(chroma("#F39C12")),
yellow: color_ramp(chroma("#FFE66D")),
green: color_ramp(chroma("#96E072")),
cyan: color_ramp(chroma("#00E8C6")),
blue: color_ramp(chroma("#0CA793")),
violet: color_ramp(chroma("#8A3FA6")),
magenta: color_ramp(chroma("#C74DED")),
},
override: { syntax: {} },
}

View File

@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Cave Dark`,
author: meta.author,
appearance: ThemeAppearance.Dark,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale([
colors.base00,
colors.base01,
@ -45,17 +45,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base06,
colors.base07,
]),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Cave Light`,
author: meta.author,
appearance: ThemeAppearance.Light,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale(
[
colors.base00,
@ -47,17 +47,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base07,
].reverse()
),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Dune Dark`,
author: meta.author,
appearance: ThemeAppearance.Dark,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale([
colors.base00,
colors.base01,
@ -45,17 +45,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base06,
colors.base07,
]),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Dune Light`,
author: meta.author,
appearance: ThemeAppearance.Light,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale(
[
colors.base00,
@ -47,17 +47,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base07,
].reverse()
),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Estuary Dark`,
author: meta.author,
appearance: ThemeAppearance.Dark,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale([
colors.base00,
colors.base01,
@ -45,17 +45,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base06,
colors.base07,
]),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Estuary Light`,
author: meta.author,
appearance: ThemeAppearance.Light,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale(
[
colors.base00,
@ -47,17 +47,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base07,
].reverse()
),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Forest Dark`,
author: meta.author,
appearance: ThemeAppearance.Dark,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale([
colors.base00,
colors.base01,
@ -45,17 +45,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base06,
colors.base07,
]),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

View File

@ -1,5 +1,5 @@
import { chroma, ThemeAppearance, ThemeConfig, colorRamp } from "../../common"
import { meta, buildSyntax, Variant } from "./common"
import { chroma, ThemeAppearance, ThemeConfig, color_ramp } from "../../common"
import { meta, build_syntax, Variant } from "./common"
const variant: Variant = {
colors: {
@ -22,19 +22,19 @@ const variant: Variant = {
},
}
const syntax = buildSyntax(variant)
const syntax = build_syntax(variant)
const getTheme = (variant: Variant): ThemeConfig => {
const get_theme = (variant: Variant): ThemeConfig => {
const { colors } = variant
return {
name: `${meta.name} Forest Light`,
author: meta.author,
appearance: ThemeAppearance.Light,
licenseType: meta.licenseType,
licenseUrl: meta.licenseUrl,
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
license_type: meta.license_type,
license_url: meta.license_url,
license_file: `${__dirname}/LICENSE`,
input_color: {
neutral: chroma.scale(
[
colors.base00,
@ -47,17 +47,17 @@ const getTheme = (variant: Variant): ThemeConfig => {
colors.base07,
].reverse()
),
red: colorRamp(chroma(colors.base08)),
orange: colorRamp(chroma(colors.base09)),
yellow: colorRamp(chroma(colors.base0A)),
green: colorRamp(chroma(colors.base0B)),
cyan: colorRamp(chroma(colors.base0C)),
blue: colorRamp(chroma(colors.base0D)),
violet: colorRamp(chroma(colors.base0E)),
magenta: colorRamp(chroma(colors.base0F)),
red: color_ramp(chroma(colors.base08)),
orange: color_ramp(chroma(colors.base09)),
yellow: color_ramp(chroma(colors.base0A)),
green: color_ramp(chroma(colors.base0B)),
cyan: color_ramp(chroma(colors.base0C)),
blue: color_ramp(chroma(colors.base0D)),
violet: color_ramp(chroma(colors.base0E)),
magenta: color_ramp(chroma(colors.base0F)),
},
override: { syntax },
}
}
export const theme = getTheme(variant)
export const theme = get_theme(variant)

Some files were not shown because too many files have changed in this diff Show More