Merge branch 'main' into Z-1292/show_search_results_in_scrollbar
This commit is contained in:
@@ -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"],
|
||||
},
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"semi": false,
|
||||
"printWidth": 80,
|
||||
"htmlWhitespaceSensitivity": "strict",
|
||||
"tabWidth": 4
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Folder-specific settings
|
||||
//
|
||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||
// see the documentation: https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings
|
||||
{
|
||||
"languages": {
|
||||
"TypeScript": {
|
||||
"tab_size": 4
|
||||
},
|
||||
"TSX": {
|
||||
"tab_size": 4
|
||||
},
|
||||
"JavaScript": {
|
||||
"tab_size": 4
|
||||
},
|
||||
"JSON": {
|
||||
"tab_size": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+4136
-186
File diff suppressed because it is too large
Load Diff
+21
-15
@@ -1,31 +1,37 @@
|
||||
{
|
||||
"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": "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",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||
"@typescript-eslint/parser": "^5.60.1",
|
||||
"@vitest/coverage-v8": "^0.32.0",
|
||||
"ayu": "^8.0.1",
|
||||
"bezier-easing": "^2.1.0",
|
||||
"case-anything": "^2.1.10",
|
||||
"chroma-js": "^2.4.2",
|
||||
"deepmerge": "^4.3.0",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.5",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"json-schema-to-typescript": "^13.0.2",
|
||||
"toml": "^3.0.0",
|
||||
"ts-node": "^10.9.1"
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"printWidth": 80,
|
||||
"htmlWhitespaceSensitivity": "strict",
|
||||
"tabWidth": 4
|
||||
"ts-deepmerge": "^6.0.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.1.5",
|
||||
"utility-types": "^3.10.0",
|
||||
"vitest": "^0.32.0",
|
||||
"zustand": "^4.3.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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`)
|
||||
@@ -1,85 +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);
|
||||
@@ -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)
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as fs from "fs"
|
||||
import { tmpdir } from "os"
|
||||
import * as path from "path"
|
||||
import app from "./style_tree/app"
|
||||
import { Theme, create_theme } from "./theme/create_theme"
|
||||
import { themes } from "./themes"
|
||||
import { useThemeStore } from "./theme"
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const all_themes: Theme[] = themes.map((theme) =>
|
||||
create_theme(theme)
|
||||
)
|
||||
|
||||
function write_themes(themes: Theme[], output_directory: string) {
|
||||
clear_themes(output_directory)
|
||||
for (const theme of themes) {
|
||||
const { setTheme } = useThemeStore.getState()
|
||||
setTheme(theme)
|
||||
|
||||
const style_tree = app()
|
||||
const style_tree_json = JSON.stringify(style_tree, null, 2)
|
||||
const temp_path = path.join(temp_directory, `${theme.name}.json`)
|
||||
const out_path = path.join(
|
||||
output_directory,
|
||||
`${theme.name}.json`
|
||||
)
|
||||
fs.writeFileSync(temp_path, style_tree_json)
|
||||
fs.renameSync(temp_path, out_path)
|
||||
console.log(`- ${out_path} created`)
|
||||
}
|
||||
}
|
||||
|
||||
write_themes(all_themes, `${assets_directory}/themes`)
|
||||
@@ -0,0 +1,90 @@
|
||||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import { Theme, create_theme, useThemeStore } from "./common"
|
||||
import { themes } from "./themes"
|
||||
import { slugify } from "./utils/slugify"
|
||||
import { theme_tokens } from "./theme/tokens/theme"
|
||||
|
||||
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: Theme[]): {
|
||||
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: Theme[]): 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: Theme[], tokens_directory: string) {
|
||||
clear_tokens(tokens_directory)
|
||||
|
||||
for (const theme of themes) {
|
||||
const { setTheme } = useThemeStore.getState()
|
||||
setTheme(theme)
|
||||
|
||||
const file_name = slugify(theme.name) + ".json"
|
||||
const tokens = theme_tokens()
|
||||
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: Theme[] = themes.map((theme) =>
|
||||
create_theme(theme)
|
||||
)
|
||||
|
||||
write_tokens(all_themes, TOKENS_DIRECTORY)
|
||||
@@ -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)
|
||||
})
|
||||
+4
-22
@@ -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 = {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { background, foreground } from "../style_tree/components"
|
||||
import { useTheme, Theme } from "../theme"
|
||||
|
||||
export type Margin = {
|
||||
top: number
|
||||
bottom: number
|
||||
left: number
|
||||
right: number
|
||||
}
|
||||
|
||||
interface IconButtonOptions {
|
||||
layer?:
|
||||
| Theme["lowest"]
|
||||
| Theme["middle"]
|
||||
| Theme["highest"]
|
||||
color?: keyof Theme["lowest"]
|
||||
margin?: Partial<Margin>
|
||||
}
|
||||
|
||||
type ToggleableIconButtonOptions = IconButtonOptions & {
|
||||
active_color?: keyof Theme["lowest"]
|
||||
}
|
||||
|
||||
export function icon_button({ color, margin, layer }: IconButtonOptions) {
|
||||
const theme = useTheme()
|
||||
|
||||
if (!color) color = "base"
|
||||
|
||||
const m = {
|
||||
top: margin?.top ?? 0,
|
||||
bottom: margin?.bottom ?? 0,
|
||||
left: margin?.left ?? 0,
|
||||
right: margin?.right ?? 0,
|
||||
}
|
||||
|
||||
return interactive({
|
||||
base: {
|
||||
corner_radius: 6,
|
||||
padding: {
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
left: 4,
|
||||
right: 4,
|
||||
},
|
||||
margin: m,
|
||||
icon_width: 14,
|
||||
icon_height: 14,
|
||||
button_width: 20,
|
||||
button_height: 16,
|
||||
},
|
||||
state: {
|
||||
default: {
|
||||
background: background(layer ?? theme.lowest, color),
|
||||
color: foreground(layer ?? theme.lowest, color),
|
||||
},
|
||||
hovered: {
|
||||
background: background(layer ?? theme.lowest, color, "hovered"),
|
||||
color: foreground(layer ?? theme.lowest, color, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
background: background(layer ?? theme.lowest, color, "pressed"),
|
||||
color: foreground(layer ?? theme.lowest, color, "pressed"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function toggleable_icon_button(
|
||||
theme: Theme,
|
||||
{ color, active_color, margin }: ToggleableIconButtonOptions
|
||||
) {
|
||||
if (!color) color = "base"
|
||||
|
||||
return toggleable({
|
||||
state: {
|
||||
inactive: icon_button({ color, margin }),
|
||||
active: icon_button({
|
||||
color: active_color ? active_color : color,
|
||||
margin,
|
||||
layer: theme.middle,
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { interactive, toggleable } from "../element"
|
||||
import {
|
||||
TextProperties,
|
||||
background,
|
||||
foreground,
|
||||
text,
|
||||
} from "../style_tree/components"
|
||||
import { useTheme, Theme } from "../theme"
|
||||
import { Margin } from "./icon_button"
|
||||
|
||||
interface TextButtonOptions {
|
||||
layer?:
|
||||
| Theme["lowest"]
|
||||
| Theme["middle"]
|
||||
| Theme["highest"]
|
||||
color?: keyof Theme["lowest"]
|
||||
margin?: Partial<Margin>
|
||||
text_properties?: TextProperties
|
||||
}
|
||||
|
||||
type ToggleableTextButtonOptions = TextButtonOptions & {
|
||||
active_color?: keyof Theme["lowest"]
|
||||
}
|
||||
|
||||
export function text_button({
|
||||
color,
|
||||
layer,
|
||||
margin,
|
||||
text_properties,
|
||||
}: TextButtonOptions) {
|
||||
const theme = useTheme()
|
||||
if (!color) color = "base"
|
||||
|
||||
const text_options: TextProperties = {
|
||||
size: "xs",
|
||||
weight: "normal",
|
||||
...text_properties,
|
||||
}
|
||||
|
||||
const m = {
|
||||
top: margin?.top ?? 0,
|
||||
bottom: margin?.bottom ?? 0,
|
||||
left: margin?.left ?? 0,
|
||||
right: margin?.right ?? 0,
|
||||
}
|
||||
|
||||
return interactive({
|
||||
base: {
|
||||
corner_radius: 6,
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
margin: m,
|
||||
button_height: 22,
|
||||
...text(layer ?? theme.lowest, "sans", color, text_options),
|
||||
},
|
||||
state: {
|
||||
default: {
|
||||
background: background(layer ?? theme.lowest, color),
|
||||
color: foreground(layer ?? theme.lowest, color),
|
||||
},
|
||||
hovered: {
|
||||
background: background(layer ?? theme.lowest, color, "hovered"),
|
||||
color: foreground(layer ?? theme.lowest, color, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
background: background(layer ?? theme.lowest, color, "pressed"),
|
||||
color: foreground(layer ?? theme.lowest, color, "pressed"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function toggleable_text_button(
|
||||
theme: Theme,
|
||||
{ color, active_color, margin }: ToggleableTextButtonOptions
|
||||
) {
|
||||
if (!color) color = "base"
|
||||
|
||||
return toggleable({
|
||||
state: {
|
||||
inactive: text_button({ color, margin }),
|
||||
active: text_button({
|
||||
color: active_color ? active_color : color,
|
||||
margin,
|
||||
layer: theme.middle,
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { interactive, Interactive } from "./interactive"
|
||||
import { toggleable } from "./toggle"
|
||||
|
||||
export { interactive, Interactive, toggleable }
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
NOT_ENOUGH_STATES_ERROR,
|
||||
NO_DEFAULT_OR_BASE_ERROR,
|
||||
interactive,
|
||||
} from "./interactive"
|
||||
import { describe, it, expect } from "vitest"
|
||||
|
||||
describe("interactive", () => {
|
||||
it("creates an Interactive<Element> with base properties and states", () => {
|
||||
const result = interactive({
|
||||
base: { font_size: 10, color: "#FFFFFF" },
|
||||
state: {
|
||||
hovered: { color: "#EEEEEE" },
|
||||
clicked: { color: "#CCCCCC" },
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
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", font_size: 10 },
|
||||
hovered: { color: "#EEEEEE" },
|
||||
clicked: { color: "#CCCCCC" },
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
default: { color: "#FFFFFF", font_size: 10 },
|
||||
hovered: { color: "#EEEEEE", font_size: 10 },
|
||||
clicked: { color: "#CCCCCC", font_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it("throws error when both default and base are missing", () => {
|
||||
const state = {
|
||||
hovered: { color: "blue" },
|
||||
}
|
||||
|
||||
expect(() => interactive({ state })).toThrow(NO_DEFAULT_OR_BASE_ERROR)
|
||||
})
|
||||
|
||||
it("throws error when no other state besides default is present", () => {
|
||||
const state = {
|
||||
default: { font_size: 10 },
|
||||
}
|
||||
|
||||
expect(() => interactive({ state })).toThrow(NOT_ENOUGH_STATES_ERROR)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,97 @@
|
||||
import merge from "ts-deepmerge"
|
||||
import { DeepPartial } from "utility-types"
|
||||
|
||||
export type InteractiveState =
|
||||
| "default"
|
||||
| "hovered"
|
||||
| "clicked"
|
||||
| "selected"
|
||||
| "disabled"
|
||||
|
||||
export type Interactive<T> = {
|
||||
default: T
|
||||
hovered?: T
|
||||
clicked?: T
|
||||
selected?: T
|
||||
disabled?: T
|
||||
}
|
||||
|
||||
export const NO_DEFAULT_OR_BASE_ERROR =
|
||||
"An interactive object must have a default state, or a base property."
|
||||
export const NOT_ENOUGH_STATES_ERROR =
|
||||
"An interactive object must have a default and at least one other state."
|
||||
|
||||
interface InteractiveProps<T> {
|
||||
base?: T
|
||||
state: Partial<Record<InteractiveState, DeepPartial<T>>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for creating Interactive<T> objects that works with Toggle<T>-like behavior.
|
||||
* It takes a default object to be used as the value for `default` field and fills out other fields
|
||||
* with fields from either `base` or from the `state` object which contains values for specific states.
|
||||
* Notably, it does not touch `hover`, `clicked`, `selected` and `disabled` states if there are no modifications for them.
|
||||
*
|
||||
* @param defaultObj Object to be used as the value for the `default` field.
|
||||
* @param base Optional object containing base fields to be included in the resulting object.
|
||||
* @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>({
|
||||
base,
|
||||
state,
|
||||
}: InteractiveProps<T>): Interactive<T> {
|
||||
if (!base && !state.default) throw new Error(NO_DEFAULT_OR_BASE_ERROR)
|
||||
|
||||
let default_state: T
|
||||
|
||||
if (state.default && base) {
|
||||
default_state = merge(base, state.default) as T
|
||||
} else {
|
||||
default_state = base ? base : (state.default as T)
|
||||
}
|
||||
|
||||
const interactive_obj: Interactive<T> = {
|
||||
default: default_state,
|
||||
}
|
||||
|
||||
let state_count = 0
|
||||
|
||||
if (state.hovered !== undefined) {
|
||||
interactive_obj.hovered = merge(
|
||||
interactive_obj.default,
|
||||
state.hovered
|
||||
) as T
|
||||
state_count++
|
||||
}
|
||||
|
||||
if (state.clicked !== undefined) {
|
||||
interactive_obj.clicked = merge(
|
||||
interactive_obj.default,
|
||||
state.clicked
|
||||
) as T
|
||||
state_count++
|
||||
}
|
||||
|
||||
if (state.selected !== undefined) {
|
||||
interactive_obj.selected = merge(
|
||||
interactive_obj.default,
|
||||
state.selected
|
||||
) as T
|
||||
state_count++
|
||||
}
|
||||
|
||||
if (state.disabled !== undefined) {
|
||||
interactive_obj.disabled = merge(
|
||||
interactive_obj.default,
|
||||
state.disabled
|
||||
) as T
|
||||
state_count++
|
||||
}
|
||||
|
||||
if (state_count < 1) {
|
||||
throw new Error(NOT_ENOUGH_STATES_ERROR)
|
||||
}
|
||||
|
||||
return interactive_obj
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
NO_ACTIVE_ERROR,
|
||||
NO_INACTIVE_OR_BASE_ERROR,
|
||||
toggleable,
|
||||
} from "./toggle"
|
||||
import { describe, it, expect } from "vitest"
|
||||
|
||||
describe("toggleable", () => {
|
||||
it("creates a Toggleable<Element> with base properties and states", () => {
|
||||
const result = toggleable({
|
||||
base: { background: "#000000", color: "#CCCCCC" },
|
||||
state: {
|
||||
active: { color: "#FFFFFF" },
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||
active: { background: "#000000", color: "#FFFFFF" },
|
||||
})
|
||||
})
|
||||
|
||||
it("creates a Toggleable<Element> with no base properties", () => {
|
||||
const result = toggleable({
|
||||
state: {
|
||||
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||
active: { background: "#000000", color: "#FFFFFF" },
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||
active: { background: "#000000", color: "#FFFFFF" },
|
||||
})
|
||||
})
|
||||
|
||||
it("throws error when both inactive and base are missing", () => {
|
||||
const state = {
|
||||
active: { background: "#000000", color: "#FFFFFF" },
|
||||
}
|
||||
|
||||
expect(() => toggleable({ state })).toThrow(NO_INACTIVE_OR_BASE_ERROR)
|
||||
})
|
||||
|
||||
it("throws error when no active state is present", () => {
|
||||
const state = {
|
||||
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||
}
|
||||
|
||||
expect(() => toggleable({ state })).toThrow(NO_ACTIVE_ERROR)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
import merge from "ts-deepmerge"
|
||||
import { DeepPartial } from "utility-types"
|
||||
|
||||
type ToggleState = "inactive" | "active"
|
||||
|
||||
type Toggleable<T> = Record<ToggleState, T>
|
||||
|
||||
export const NO_INACTIVE_OR_BASE_ERROR =
|
||||
"A toggleable object must have an inactive state, or a base property."
|
||||
export const NO_ACTIVE_ERROR = "A toggleable object must have an active state."
|
||||
|
||||
interface ToggleableProps<T> {
|
||||
base?: T
|
||||
state: Partial<Record<ToggleState, DeepPartial<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 inactive_state = base
|
||||
? ((state.inactive ? merge(base, state.inactive) : base) as T)
|
||||
: (state.inactive as T)
|
||||
|
||||
const toggle_obj: Toggleable<T> = {
|
||||
inactive: inactive_state,
|
||||
active: merge(base ?? {}, state.active) as T,
|
||||
}
|
||||
return toggle_obj
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
import { text } from "./components"
|
||||
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"
|
||||
|
||||
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),
|
||||
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"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { text, border, background, foreground } from "./components"
|
||||
import editor from "./editor"
|
||||
|
||||
export default function assistant(colorScheme: ColorScheme) {
|
||||
const layer = colorScheme.highest
|
||||
return {
|
||||
container: {
|
||||
background: editor(colorScheme).background,
|
||||
padding: { left: 12 },
|
||||
},
|
||||
header: {
|
||||
border: border(layer, "default", { bottom: true, top: true }),
|
||||
margin: { bottom: 6, top: 6 },
|
||||
background: editor(colorScheme).background,
|
||||
},
|
||||
userSender: {
|
||||
...text(layer, "sans", "default", { size: "sm", weight: "bold" }),
|
||||
},
|
||||
assistantSender: {
|
||||
...text(layer, "sans", "accent", { size: "sm", weight: "bold" }),
|
||||
},
|
||||
systemSender: {
|
||||
...text(layer, "sans", "variant", { size: "sm", weight: "bold" }),
|
||||
},
|
||||
sentAt: {
|
||||
margin: { top: 2, left: 8 },
|
||||
...text(layer, "sans", "default", { size: "2xs" }),
|
||||
},
|
||||
modelInfoContainer: {
|
||||
margin: { right: 16, top: 4 },
|
||||
},
|
||||
model: {
|
||||
background: background(layer, "on"),
|
||||
border: border(layer, "on", { overlay: true }),
|
||||
padding: 4,
|
||||
cornerRadius: 4,
|
||||
...text(layer, "sans", "default", { size: "xs" }),
|
||||
hover: {
|
||||
background: background(layer, "on", "hovered"),
|
||||
},
|
||||
},
|
||||
remainingTokens: {
|
||||
background: background(layer, "on"),
|
||||
border: border(layer, "on", { overlay: true }),
|
||||
padding: 4,
|
||||
margin: { left: 4 },
|
||||
cornerRadius: 4,
|
||||
...text(layer, "sans", "positive", { size: "xs" }),
|
||||
},
|
||||
noRemainingTokens: {
|
||||
background: background(layer, "on"),
|
||||
border: border(layer, "on", { overlay: true }),
|
||||
padding: 4,
|
||||
margin: { left: 4 },
|
||||
cornerRadius: 4,
|
||||
...text(layer, "sans", "negative", { size: "xs" }),
|
||||
},
|
||||
errorIcon: {
|
||||
margin: { left: 8 },
|
||||
color: foreground(layer, "negative"),
|
||||
width: 12,
|
||||
},
|
||||
apiKeyEditor: {
|
||||
background: background(layer, "on"),
|
||||
cornerRadius: 6,
|
||||
text: text(layer, "mono", "on"),
|
||||
placeholderText: text(layer, "mono", "on", "disabled", {
|
||||
size: "xs",
|
||||
}),
|
||||
selection: colorScheme.players[0],
|
||||
border: border(layer, "on"),
|
||||
padding: {
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
top: 4,
|
||||
},
|
||||
},
|
||||
apiKeyPrompt: {
|
||||
padding: 10,
|
||||
...text(layer, "sans", "default", { size: "xs" }),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { withOpacity } from "../theme/color"
|
||||
import { text, background } from "./components"
|
||||
|
||||
export default function commandPalette(colorScheme: ColorScheme) {
|
||||
let layer = colorScheme.highest
|
||||
return {
|
||||
keystrokeSpacing: 8,
|
||||
key: {
|
||||
text: text(layer, "mono", "variant", "default", { size: "xs" }),
|
||||
cornerRadius: 2,
|
||||
background: background(layer, "on"),
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
margin: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 2,
|
||||
},
|
||||
active: {
|
||||
text: text(layer, "mono", "on", "default", { size: "xs" }),
|
||||
background: withOpacity(background(layer, "on"), 0.2),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, border, borderColor, foreground, text } from "./components"
|
||||
|
||||
export default function contactsPanel(colorScheme: ColorScheme) {
|
||||
const nameMargin = 8
|
||||
const sidePadding = 12
|
||||
|
||||
let layer = colorScheme.middle
|
||||
|
||||
const contactButton = {
|
||||
background: background(layer, "on"),
|
||||
color: foreground(layer, "on"),
|
||||
iconWidth: 8,
|
||||
buttonWidth: 16,
|
||||
cornerRadius: 8,
|
||||
}
|
||||
const projectRow = {
|
||||
guestAvatarSpacing: 4,
|
||||
height: 24,
|
||||
guestAvatar: {
|
||||
cornerRadius: 8,
|
||||
width: 14,
|
||||
},
|
||||
name: {
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
margin: {
|
||||
left: nameMargin,
|
||||
right: 6,
|
||||
},
|
||||
},
|
||||
guests: {
|
||||
margin: {
|
||||
left: nameMargin,
|
||||
right: nameMargin,
|
||||
},
|
||||
},
|
||||
padding: {
|
||||
left: sidePadding,
|
||||
right: sidePadding,
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
background: background(layer),
|
||||
padding: { top: 12 },
|
||||
userQueryEditor: {
|
||||
background: background(layer, "on"),
|
||||
cornerRadius: 6,
|
||||
text: text(layer, "mono", "on"),
|
||||
placeholderText: text(layer, "mono", "on", "disabled", {
|
||||
size: "xs",
|
||||
}),
|
||||
selection: colorScheme.players[0],
|
||||
border: border(layer, "on"),
|
||||
padding: {
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
top: 4,
|
||||
},
|
||||
margin: {
|
||||
left: 6,
|
||||
},
|
||||
},
|
||||
userQueryEditorHeight: 33,
|
||||
addContactButton: {
|
||||
margin: { left: 6, right: 12 },
|
||||
color: foreground(layer, "on"),
|
||||
buttonWidth: 28,
|
||||
iconWidth: 16,
|
||||
},
|
||||
rowHeight: 28,
|
||||
sectionIconSize: 8,
|
||||
headerRow: {
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
margin: { top: 14 },
|
||||
padding: {
|
||||
left: sidePadding,
|
||||
right: sidePadding,
|
||||
},
|
||||
active: {
|
||||
...text(layer, "mono", "active", { size: "sm" }),
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
},
|
||||
leaveCall: {
|
||||
background: background(layer),
|
||||
border: border(layer),
|
||||
cornerRadius: 6,
|
||||
margin: {
|
||||
top: 1,
|
||||
},
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 7,
|
||||
right: 7,
|
||||
},
|
||||
...text(layer, "sans", "variant", { size: "xs" }),
|
||||
hover: {
|
||||
...text(layer, "sans", "hovered", { size: "xs" }),
|
||||
background: background(layer, "hovered"),
|
||||
border: border(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
contactRow: {
|
||||
padding: {
|
||||
left: sidePadding,
|
||||
right: sidePadding,
|
||||
},
|
||||
active: {
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
},
|
||||
contactAvatar: {
|
||||
cornerRadius: 10,
|
||||
width: 18,
|
||||
},
|
||||
contactStatusFree: {
|
||||
cornerRadius: 4,
|
||||
padding: 4,
|
||||
margin: { top: 12, left: 12 },
|
||||
background: foreground(layer, "positive"),
|
||||
},
|
||||
contactStatusBusy: {
|
||||
cornerRadius: 4,
|
||||
padding: 4,
|
||||
margin: { top: 12, left: 12 },
|
||||
background: foreground(layer, "negative"),
|
||||
},
|
||||
contactUsername: {
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
margin: {
|
||||
left: nameMargin,
|
||||
},
|
||||
},
|
||||
contactButtonSpacing: nameMargin,
|
||||
contactButton: {
|
||||
...contactButton,
|
||||
hover: {
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
disabledButton: {
|
||||
...contactButton,
|
||||
background: background(layer, "on"),
|
||||
color: foreground(layer, "on"),
|
||||
},
|
||||
callingIndicator: {
|
||||
...text(layer, "mono", "variant", { size: "xs" }),
|
||||
},
|
||||
treeBranch: {
|
||||
color: borderColor(layer),
|
||||
width: 1,
|
||||
hover: {
|
||||
color: borderColor(layer),
|
||||
},
|
||||
active: {
|
||||
color: borderColor(layer),
|
||||
},
|
||||
},
|
||||
projectRow: {
|
||||
...projectRow,
|
||||
background: background(layer),
|
||||
icon: {
|
||||
margin: { left: nameMargin },
|
||||
color: foreground(layer, "variant"),
|
||||
width: 12,
|
||||
},
|
||||
name: {
|
||||
...projectRow.name,
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
},
|
||||
hover: {
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
active: {
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, foreground, text } from "./components"
|
||||
|
||||
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: {
|
||||
...text(layer, "sans", "on", { size: "xs" }),
|
||||
background: background(layer, "on"),
|
||||
padding: 4,
|
||||
cornerRadius: 6,
|
||||
margin: { left: 6 },
|
||||
hover: {
|
||||
background: background(layer, "on", "hovered"),
|
||||
},
|
||||
},
|
||||
dismissButton: {
|
||||
color: foreground(layer, "variant"),
|
||||
iconWidth: 8,
|
||||
iconHeight: 8,
|
||||
buttonWidth: 8,
|
||||
buttonHeight: 8,
|
||||
hover: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, border, borderColor, text } from "./components"
|
||||
|
||||
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: {
|
||||
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 },
|
||||
},
|
||||
hover: {
|
||||
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 },
|
||||
},
|
||||
},
|
||||
active: {
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
activeHover: {
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
},
|
||||
separator: {
|
||||
background: borderColor(layer),
|
||||
margin: { top: 2, bottom: 2 },
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, border, foreground, svg, text } from "./components"
|
||||
|
||||
export default function copilot(colorScheme: ColorScheme) {
|
||||
let layer = colorScheme.middle
|
||||
|
||||
let content_width = 264
|
||||
|
||||
let ctaButton = {
|
||||
// Copied from welcome screen. FIXME: Move this into a ZDS component
|
||||
background: background(layer),
|
||||
border: border(layer, "default"),
|
||||
cornerRadius: 4,
|
||||
margin: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
padding: {
|
||||
top: 3,
|
||||
bottom: 3,
|
||||
left: 7,
|
||||
right: 7,
|
||||
},
|
||||
...text(layer, "sans", "default", { size: "sm" }),
|
||||
hover: {
|
||||
...text(layer, "sans", "default", { size: "sm" }),
|
||||
background: background(layer, "hovered"),
|
||||
border: border(layer, "active"),
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
outLinkIcon: {
|
||||
icon: svg(
|
||||
foreground(layer, "variant"),
|
||||
"icons/link_out_12.svg",
|
||||
12,
|
||||
12
|
||||
),
|
||||
container: {
|
||||
cornerRadius: 6,
|
||||
padding: { left: 6 },
|
||||
},
|
||||
hover: {
|
||||
icon: svg(
|
||||
foreground(layer, "hovered"),
|
||||
"icons/link_out_12.svg",
|
||||
12,
|
||||
12
|
||||
),
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
titleText: {
|
||||
...text(layer, "sans", { size: "xs", weight: "bold" }),
|
||||
},
|
||||
titlebar: {
|
||||
background: background(colorScheme.lowest),
|
||||
border: border(layer, "active"),
|
||||
padding: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
background: background(colorScheme.lowest),
|
||||
padding: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 8,
|
||||
},
|
||||
},
|
||||
closeIcon: {
|
||||
icon: svg(
|
||||
foreground(layer, "variant"),
|
||||
"icons/x_mark_8.svg",
|
||||
8,
|
||||
8
|
||||
),
|
||||
container: {
|
||||
cornerRadius: 2,
|
||||
padding: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 4,
|
||||
right: 4,
|
||||
},
|
||||
margin: {
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
hover: {
|
||||
icon: svg(
|
||||
foreground(layer, "on"),
|
||||
"icons/x_mark_8.svg",
|
||||
8,
|
||||
8
|
||||
),
|
||||
},
|
||||
clicked: {
|
||||
icon: svg(
|
||||
foreground(layer, "base"),
|
||||
"icons/x_mark_8.svg",
|
||||
8,
|
||||
8
|
||||
),
|
||||
},
|
||||
},
|
||||
dimensions: {
|
||||
width: 280,
|
||||
height: 280,
|
||||
},
|
||||
},
|
||||
|
||||
auth: {
|
||||
content_width,
|
||||
|
||||
ctaButton,
|
||||
|
||||
header: {
|
||||
icon: svg(
|
||||
foreground(layer, "default"),
|
||||
"icons/zed_plus_copilot_32.svg",
|
||||
92,
|
||||
32
|
||||
),
|
||||
container: {
|
||||
margin: {
|
||||
top: 35,
|
||||
bottom: 5,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
prompting: {
|
||||
subheading: {
|
||||
...text(layer, "sans", { size: "xs" }),
|
||||
margin: {
|
||||
top: 6,
|
||||
bottom: 12,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
|
||||
hint: {
|
||||
...text(layer, "sans", { size: "xs", color: "#838994" }),
|
||||
margin: {
|
||||
top: 6,
|
||||
bottom: 2,
|
||||
},
|
||||
},
|
||||
|
||||
deviceCode: {
|
||||
text: text(layer, "mono", { size: "sm" }),
|
||||
cta: {
|
||||
...ctaButton,
|
||||
background: background(colorScheme.lowest),
|
||||
border: border(colorScheme.lowest, "inverted"),
|
||||
padding: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 16,
|
||||
right: 16,
|
||||
},
|
||||
margin: {
|
||||
left: 16,
|
||||
right: 16,
|
||||
},
|
||||
},
|
||||
left: content_width / 2,
|
||||
leftContainer: {
|
||||
padding: {
|
||||
top: 3,
|
||||
bottom: 3,
|
||||
left: 0,
|
||||
right: 6,
|
||||
},
|
||||
},
|
||||
right: (content_width * 1) / 3,
|
||||
rightContainer: {
|
||||
border: border(colorScheme.lowest, "inverted", {
|
||||
bottom: false,
|
||||
right: false,
|
||||
top: false,
|
||||
left: true,
|
||||
}),
|
||||
padding: {
|
||||
top: 3,
|
||||
bottom: 5,
|
||||
left: 8,
|
||||
right: 0,
|
||||
},
|
||||
hover: {
|
||||
border: border(layer, "active", {
|
||||
bottom: false,
|
||||
right: false,
|
||||
top: false,
|
||||
left: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
notAuthorized: {
|
||||
subheading: {
|
||||
...text(layer, "sans", { size: "xs" }),
|
||||
|
||||
margin: {
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
|
||||
warning: {
|
||||
...text(layer, "sans", {
|
||||
size: "xs",
|
||||
color: foreground(layer, "warning"),
|
||||
}),
|
||||
border: border(layer, "warning"),
|
||||
background: background(layer, "warning"),
|
||||
cornerRadius: 2,
|
||||
padding: {
|
||||
top: 4,
|
||||
left: 4,
|
||||
bottom: 4,
|
||||
right: 4,
|
||||
},
|
||||
margin: {
|
||||
bottom: 16,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
authorized: {
|
||||
subheading: {
|
||||
...text(layer, "sans", { size: "xs" }),
|
||||
|
||||
margin: {
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
},
|
||||
},
|
||||
|
||||
hint: {
|
||||
...text(layer, "sans", { size: "xs", color: "#838994" }),
|
||||
margin: {
|
||||
top: 24,
|
||||
bottom: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,281 +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"
|
||||
|
||||
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: {
|
||||
color: foreground(layer, "variant"),
|
||||
|
||||
clicked: {
|
||||
color: foreground(layer, "base"),
|
||||
},
|
||||
hover: {
|
||||
color: foreground(layer, "on"),
|
||||
},
|
||||
active: {
|
||||
color: foreground(layer, "on"),
|
||||
},
|
||||
},
|
||||
verticalScale: 0.55,
|
||||
},
|
||||
folds: {
|
||||
iconMarginScale: 2.5,
|
||||
foldedIcon: "icons/chevron_right_8.svg",
|
||||
foldableIcon: "icons/chevron_down_8.svg",
|
||||
indicator: {
|
||||
color: foreground(layer, "variant"),
|
||||
|
||||
clicked: {
|
||||
color: foreground(layer, "base"),
|
||||
},
|
||||
hover: {
|
||||
color: foreground(layer, "on"),
|
||||
},
|
||||
active: {
|
||||
color: foreground(layer, "on"),
|
||||
},
|
||||
},
|
||||
ellipses: {
|
||||
textColor: colorScheme.ramps.neutral(0.71).hex(),
|
||||
cornerRadiusFactor: 0.15,
|
||||
background: {
|
||||
// Copied from hover_popover highlight
|
||||
color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex(),
|
||||
|
||||
hover: {
|
||||
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: {
|
||||
color: foreground(layer, "on"),
|
||||
iconWidth: 20,
|
||||
buttonWidth: 20,
|
||||
cornerRadius: 6,
|
||||
padding: {
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
hover: {
|
||||
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),
|
||||
},
|
||||
selections: isLight
|
||||
? withOpacity(colorScheme.ramps.blue(0.5).hex(), 0.8)
|
||||
: withOpacity(colorScheme.ramps.blue(0.4).hex(), 0.8)
|
||||
},
|
||||
compositionMark: {
|
||||
underline: {
|
||||
thickness: 1.0,
|
||||
color: borderColor(layer),
|
||||
},
|
||||
},
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, border, text } from "./components"
|
||||
|
||||
export default function feedback(colorScheme: ColorScheme) {
|
||||
let layer = colorScheme.highest
|
||||
|
||||
return {
|
||||
submit_button: {
|
||||
...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,
|
||||
},
|
||||
clicked: {
|
||||
...text(layer, "mono", "on", "pressed"),
|
||||
background: background(layer, "on", "pressed"),
|
||||
border: border(layer, "on", "pressed"),
|
||||
},
|
||||
hover: {
|
||||
...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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { withOpacity } from "../theme/color"
|
||||
import { background, border, text } from "./components"
|
||||
|
||||
export default function picker(colorScheme: ColorScheme): any {
|
||||
let layer = colorScheme.lowest
|
||||
const container = {
|
||||
background: background(layer),
|
||||
border: border(layer),
|
||||
shadow: colorScheme.modalShadow,
|
||||
cornerRadius: 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 }),
|
||||
padding: {
|
||||
bottom: 8,
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
},
|
||||
margin: {
|
||||
bottom: 4,
|
||||
},
|
||||
}
|
||||
const emptyInputEditor: any = { ...inputEditor }
|
||||
delete emptyInputEditor.border
|
||||
delete emptyInputEditor.margin
|
||||
|
||||
return {
|
||||
...container,
|
||||
emptyContainer: {
|
||||
...container,
|
||||
padding: {},
|
||||
},
|
||||
item: {
|
||||
padding: {
|
||||
bottom: 4,
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 4,
|
||||
},
|
||||
margin: {
|
||||
top: 1,
|
||||
left: 4,
|
||||
right: 4,
|
||||
},
|
||||
cornerRadius: 8,
|
||||
text: text(layer, "sans", "variant"),
|
||||
highlightText: text(layer, "sans", "accent", { weight: "bold" }),
|
||||
active: {
|
||||
background: withOpacity(
|
||||
background(layer, "base", "active"),
|
||||
0.5
|
||||
),
|
||||
text: text(layer, "sans", "base", "active"),
|
||||
highlightText: text(layer, "sans", "accent", {
|
||||
weight: "bold",
|
||||
}),
|
||||
},
|
||||
hover: {
|
||||
background: withOpacity(background(layer, "hovered"), 0.5),
|
||||
},
|
||||
},
|
||||
inputEditor,
|
||||
emptyInputEditor,
|
||||
noMatches: {
|
||||
text: text(layer, "sans", "variant"),
|
||||
padding: {
|
||||
bottom: 8,
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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" }),
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { withOpacity } from "../theme/color"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
|
||||
export default function projectPanel(colorScheme: ColorScheme) {
|
||||
const { isLight } = colorScheme
|
||||
|
||||
let layer = colorScheme.middle
|
||||
|
||||
let baseEntry = {
|
||||
height: 22,
|
||||
iconColor: foreground(layer, "variant"),
|
||||
iconSize: 7,
|
||||
iconSpacing: 5,
|
||||
}
|
||||
|
||||
let 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(),
|
||||
},
|
||||
}
|
||||
|
||||
let entry = {
|
||||
...baseEntry,
|
||||
text: text(layer, "mono", "variant", { size: "sm" }),
|
||||
hover: {
|
||||
background: background(layer, "variant", "hovered"),
|
||||
},
|
||||
active: {
|
||||
background: colorScheme.isLight
|
||||
? withOpacity(background(layer, "active"), 0.5)
|
||||
: background(layer, "active"),
|
||||
text: text(layer, "mono", "active", { size: "sm" }),
|
||||
},
|
||||
activeHover: {
|
||||
background: background(layer, "active"),
|
||||
text: text(layer, "mono", "active", { size: "sm" }),
|
||||
},
|
||||
status,
|
||||
}
|
||||
|
||||
return {
|
||||
openProjectButton: {
|
||||
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" }),
|
||||
hover: {
|
||||
...text(layer, "sans", "default", { size: "sm" }),
|
||||
background: background(layer, "hovered"),
|
||||
border: border(layer, "active"),
|
||||
},
|
||||
},
|
||||
background: background(layer),
|
||||
padding: { left: 6, right: 6, top: 0, bottom: 6 },
|
||||
indentWidth: 12,
|
||||
entry,
|
||||
draggedEntry: {
|
||||
...baseEntry,
|
||||
text: text(layer, "mono", "on", { size: "sm" }),
|
||||
background: withOpacity(background(layer, "on"), 0.9),
|
||||
border: border(layer),
|
||||
status,
|
||||
},
|
||||
ignoredEntry: {
|
||||
...entry,
|
||||
iconColor: foreground(layer, "disabled"),
|
||||
text: text(layer, "mono", "disabled"),
|
||||
active: {
|
||||
...entry.active,
|
||||
iconColor: foreground(layer, "variant"),
|
||||
},
|
||||
},
|
||||
cutEntry: {
|
||||
...entry,
|
||||
text: text(layer, "mono", "disabled"),
|
||||
active: {
|
||||
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],
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { withOpacity } from "../theme/color"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
|
||||
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: {
|
||||
...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,
|
||||
},
|
||||
active: {
|
||||
...text(layer, "mono", "on", "inverted"),
|
||||
background: background(layer, "on", "inverted"),
|
||||
border: border(layer, "on", "inverted"),
|
||||
},
|
||||
clicked: {
|
||||
...text(layer, "mono", "on", "pressed"),
|
||||
background: background(layer, "on", "pressed"),
|
||||
border: border(layer, "on", "pressed"),
|
||||
},
|
||||
hover: {
|
||||
...text(layer, "mono", "on", "hovered"),
|
||||
background: background(layer, "on", "hovered"),
|
||||
border: border(layer, "on", "hovered"),
|
||||
},
|
||||
},
|
||||
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: {
|
||||
color: foreground(layer, "variant"),
|
||||
iconWidth: 12,
|
||||
buttonWidth: 14,
|
||||
padding: {
|
||||
left: 10,
|
||||
right: 10,
|
||||
},
|
||||
hover: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
|
||||
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: {
|
||||
...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 },
|
||||
hover: {
|
||||
...text(layer, "sans", "default", { size: "xs" }),
|
||||
background: background(layer, "hovered"),
|
||||
border: border(layer, "active"),
|
||||
},
|
||||
},
|
||||
dismissButton: {
|
||||
color: foreground(layer),
|
||||
iconWidth: 8,
|
||||
iconHeight: 8,
|
||||
buttonWidth: 8,
|
||||
buttonHeight: 8,
|
||||
hover: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
|
||||
export default function statusBar(colorScheme: ColorScheme) {
|
||||
let layer = colorScheme.lowest
|
||||
|
||||
const statusContainer = {
|
||||
cornerRadius: 6,
|
||||
padding: { top: 3, bottom: 3, left: 6, right: 6 },
|
||||
}
|
||||
|
||||
const diagnosticStatusContainer = {
|
||||
cornerRadius: 6,
|
||||
padding: { top: 1, bottom: 1, left: 6, right: 6 },
|
||||
}
|
||||
|
||||
return {
|
||||
height: 30,
|
||||
itemSpacing: 8,
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
border: border(layer, { top: true, overlay: true }),
|
||||
cursorPosition: text(layer, "sans", "variant"),
|
||||
activeLanguage: {
|
||||
padding: { left: 6, right: 6 },
|
||||
...text(layer, "sans", "variant"),
|
||||
hover: {
|
||||
...text(layer, "sans", "on"),
|
||||
},
|
||||
},
|
||||
autoUpdateProgressMessage: text(layer, "sans", "variant"),
|
||||
autoUpdateDoneMessage: text(layer, "sans", "variant"),
|
||||
lspStatus: {
|
||||
...diagnosticStatusContainer,
|
||||
iconSpacing: 4,
|
||||
iconWidth: 14,
|
||||
height: 18,
|
||||
message: text(layer, "sans"),
|
||||
iconColor: foreground(layer),
|
||||
hover: {
|
||||
message: text(layer, "sans"),
|
||||
iconColor: foreground(layer),
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
diagnosticMessage: {
|
||||
...text(layer, "sans"),
|
||||
hover: text(layer, "sans", "hovered"),
|
||||
},
|
||||
diagnosticSummary: {
|
||||
height: 20,
|
||||
iconWidth: 16,
|
||||
iconSpacing: 2,
|
||||
summarySpacing: 6,
|
||||
text: text(layer, "sans", { size: "sm" }),
|
||||
iconColorOk: foreground(layer, "variant"),
|
||||
iconColorWarning: foreground(layer, "warning"),
|
||||
iconColorError: foreground(layer, "negative"),
|
||||
containerOk: {
|
||||
cornerRadius: 6,
|
||||
padding: { top: 3, bottom: 3, left: 7, right: 7 },
|
||||
},
|
||||
containerWarning: {
|
||||
...diagnosticStatusContainer,
|
||||
background: background(layer, "warning"),
|
||||
border: border(layer, "warning"),
|
||||
},
|
||||
containerError: {
|
||||
...diagnosticStatusContainer,
|
||||
background: background(layer, "negative"),
|
||||
border: border(layer, "negative"),
|
||||
},
|
||||
hover: {
|
||||
iconColorOk: foreground(layer, "on"),
|
||||
containerOk: {
|
||||
cornerRadius: 6,
|
||||
padding: { top: 3, bottom: 3, left: 7, right: 7 },
|
||||
background: background(layer, "on", "hovered"),
|
||||
},
|
||||
containerWarning: {
|
||||
...diagnosticStatusContainer,
|
||||
background: background(layer, "warning", "hovered"),
|
||||
border: border(layer, "warning", "hovered"),
|
||||
},
|
||||
containerError: {
|
||||
...diagnosticStatusContainer,
|
||||
background: background(layer, "negative", "hovered"),
|
||||
border: border(layer, "negative", "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
panelButtons: {
|
||||
groupLeft: {},
|
||||
groupBottom: {},
|
||||
groupRight: {},
|
||||
button: {
|
||||
...statusContainer,
|
||||
iconSize: 16,
|
||||
iconColor: foreground(layer, "variant"),
|
||||
label: {
|
||||
margin: { left: 6 },
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
},
|
||||
hover: {
|
||||
iconColor: foreground(layer, "hovered"),
|
||||
background: background(layer, "variant"),
|
||||
},
|
||||
active: {
|
||||
iconColor: foreground(layer, "active"),
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
},
|
||||
badge: {
|
||||
cornerRadius: 3,
|
||||
padding: 2,
|
||||
margin: { bottom: -1, right: -1 },
|
||||
border: border(layer),
|
||||
background: background(layer, "accent"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { withOpacity } from "../theme/color"
|
||||
import { text, border, background, foreground } from "./components"
|
||||
|
||||
export default function tabBar(colorScheme: ColorScheme) {
|
||||
const height = 32
|
||||
|
||||
let activeLayer = colorScheme.highest
|
||||
let layer = colorScheme.middle
|
||||
|
||||
const tab = {
|
||||
height,
|
||||
text: text(layer, "sans", "variant", { size: "sm" }),
|
||||
background: background(layer),
|
||||
border: border(layer, {
|
||||
right: true,
|
||||
bottom: true,
|
||||
overlay: true,
|
||||
}),
|
||||
padding: {
|
||||
left: 8,
|
||||
right: 12,
|
||||
},
|
||||
spacing: 8,
|
||||
|
||||
// Tab type icons (e.g. Project Search)
|
||||
typeIconWidth: 14,
|
||||
|
||||
// Close icons
|
||||
closeIconWidth: 8,
|
||||
iconClose: foreground(layer, "variant"),
|
||||
iconCloseActive: foreground(layer, "hovered"),
|
||||
|
||||
// Indicators
|
||||
iconConflict: foreground(layer, "warning"),
|
||||
iconDirty: foreground(layer, "accent"),
|
||||
|
||||
// When two tabs of the same name are open, a label appears next to them
|
||||
description: {
|
||||
margin: { left: 8 },
|
||||
...text(layer, "sans", "disabled", { size: "2xs" }),
|
||||
},
|
||||
}
|
||||
|
||||
const activePaneActiveTab = {
|
||||
...tab,
|
||||
background: background(activeLayer),
|
||||
text: text(activeLayer, "sans", "active", { size: "sm" }),
|
||||
border: {
|
||||
...tab.border,
|
||||
bottom: false,
|
||||
},
|
||||
}
|
||||
|
||||
const inactivePaneInactiveTab = {
|
||||
...tab,
|
||||
background: background(layer),
|
||||
text: text(layer, "sans", "variant", { size: "sm" }),
|
||||
}
|
||||
|
||||
const inactivePaneActiveTab = {
|
||||
...tab,
|
||||
background: background(activeLayer),
|
||||
text: text(layer, "sans", "variant", { size: "sm" }),
|
||||
border: {
|
||||
...tab.border,
|
||||
bottom: false,
|
||||
},
|
||||
}
|
||||
|
||||
const draggedTab = {
|
||||
...activePaneActiveTab,
|
||||
background: withOpacity(tab.background, 0.9),
|
||||
border: undefined as any,
|
||||
shadow: colorScheme.popoverShadow,
|
||||
}
|
||||
|
||||
return {
|
||||
height,
|
||||
background: background(layer),
|
||||
activePane: {
|
||||
activeTab: activePaneActiveTab,
|
||||
inactiveTab: tab,
|
||||
},
|
||||
inactivePane: {
|
||||
activeTab: inactivePaneActiveTab,
|
||||
inactiveTab: inactivePaneInactiveTab,
|
||||
},
|
||||
draggedTab,
|
||||
paneButton: {
|
||||
color: foreground(layer, "variant"),
|
||||
iconWidth: 12,
|
||||
buttonWidth: activePaneActiveTab.height,
|
||||
hover: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
active: {
|
||||
color: foreground(layer, "accent"),
|
||||
},
|
||||
},
|
||||
paneButtonContainer: {
|
||||
background: tab.background,
|
||||
border: {
|
||||
...tab.border,
|
||||
right: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { background, border, text } from "./components"
|
||||
|
||||
export default function dropdownMenu(colorScheme: ColorScheme) {
|
||||
let layer = colorScheme.middle
|
||||
|
||||
return {
|
||||
rowHeight: 30,
|
||||
background: background(layer),
|
||||
border: border(layer),
|
||||
shadow: colorScheme.popoverShadow,
|
||||
header: {
|
||||
...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"),
|
||||
border: border(layer, "on", { overlay: true }),
|
||||
hover: {
|
||||
background: background(layer, "hovered"),
|
||||
...text(layer, "sans", "hovered", { size: "sm" }),
|
||||
}
|
||||
},
|
||||
sectionHeader: {
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
padding: { left: 8, right: 8, top: 8, bottom: 8 },
|
||||
},
|
||||
item: {
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
secondaryTextSpacing: 10,
|
||||
secondaryText: text(layer, "sans", { size: "sm" }),
|
||||
padding: { left: 18, right: 18, top: 2, bottom: 2 },
|
||||
hover: {
|
||||
background: background(layer, "hovered"),
|
||||
...text(layer, "sans", "hovered", { size: "sm" }),
|
||||
},
|
||||
active: {
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
activeHover: {
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { foreground, text } from "./components"
|
||||
|
||||
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: {
|
||||
...text(layer, "sans", { size: "xs" }),
|
||||
margin: { left: headerPadding, top: 6, bottom: 6 },
|
||||
hover: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
dismissButton: {
|
||||
color: foreground(layer),
|
||||
iconWidth: 8,
|
||||
iconHeight: 8,
|
||||
buttonWidth: 8,
|
||||
buttonHeight: 8,
|
||||
hover: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import { ColorScheme } from "../theme/colorScheme"
|
||||
import { withOpacity } from "../theme/color"
|
||||
import {
|
||||
border,
|
||||
background,
|
||||
foreground,
|
||||
text,
|
||||
TextProperties,
|
||||
svg,
|
||||
} from "./components"
|
||||
|
||||
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: {
|
||||
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),
|
||||
hover: {
|
||||
...text(layer, "sans", "default", interactive_text_size),
|
||||
background: background(layer, "hovered"),
|
||||
border: border(layer, "active"),
|
||||
},
|
||||
},
|
||||
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"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,338 +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"
|
||||
|
||||
export default function workspace(colorScheme: ColorScheme) {
|
||||
const layer = colorScheme.lowest
|
||||
const isLight = colorScheme.isLight
|
||||
const itemSpacing = 8
|
||||
const titlebarButton = {
|
||||
cornerRadius: 6,
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
...text(layer, "sans", "variant", { size: "xs" }),
|
||||
background: background(layer, "variant"),
|
||||
border: border(layer),
|
||||
hover: {
|
||||
...text(layer, "sans", "variant", "hovered", { size: "xs" }),
|
||||
background: background(layer, "variant", "hovered"),
|
||||
border: border(layer, "variant", "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
...text(layer, "sans", "variant", "pressed", { size: "xs" }),
|
||||
background: background(layer, "variant", "pressed"),
|
||||
border: border(layer, "variant", "pressed"),
|
||||
},
|
||||
active: {
|
||||
...text(layer, "sans", "variant", "active", { size: "xs" }),
|
||||
background: background(layer, "variant", "active"),
|
||||
border: border(layer, "variant", "active"),
|
||||
},
|
||||
}
|
||||
const avatarWidth = 18
|
||||
const avatarOuterWidth = avatarWidth + 4
|
||||
const followerAvatarWidth = 14
|
||||
const followerAvatarOuterWidth = followerAvatarWidth + 4
|
||||
|
||||
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: {
|
||||
...text(layer, "sans", "variant", { size: "sm" }),
|
||||
padding: {
|
||||
top: 3,
|
||||
left: 8,
|
||||
right: 8,
|
||||
bottom: 3,
|
||||
},
|
||||
cornerRadius: 8,
|
||||
hover: {
|
||||
...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: {
|
||||
itemSpacing,
|
||||
facePileSpacing: 2,
|
||||
height: 33, // 32px + 1px border. It's important the content area of the titlebar is evenly sized to vertically center avatar images.
|
||||
background: background(layer),
|
||||
border: border(layer, { bottom: true }),
|
||||
padding: {
|
||||
left: 80,
|
||||
right: itemSpacing,
|
||||
},
|
||||
|
||||
// Project
|
||||
title: text(layer, "sans", "variant"),
|
||||
highlight_color: text(layer, "sans", "active").color,
|
||||
|
||||
// Collaborators
|
||||
leaderAvatar: {
|
||||
width: avatarWidth,
|
||||
outerWidth: avatarOuterWidth,
|
||||
cornerRadius: avatarWidth / 2,
|
||||
outerCornerRadius: avatarOuterWidth / 2,
|
||||
},
|
||||
followerAvatar: {
|
||||
width: followerAvatarWidth,
|
||||
outerWidth: followerAvatarOuterWidth,
|
||||
cornerRadius: followerAvatarWidth / 2,
|
||||
outerCornerRadius: followerAvatarOuterWidth / 2,
|
||||
},
|
||||
inactiveAvatarGrayscale: true,
|
||||
followerAvatarOverlap: 8,
|
||||
leaderSelection: {
|
||||
margin: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
},
|
||||
padding: {
|
||||
left: 2,
|
||||
right: 2,
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
},
|
||||
cornerRadius: 6,
|
||||
},
|
||||
avatarRibbon: {
|
||||
height: 3,
|
||||
width: 12,
|
||||
// TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded.
|
||||
},
|
||||
|
||||
// Sign in buttom
|
||||
// FlatButton, Variant
|
||||
signInPrompt: {
|
||||
margin: {
|
||||
left: itemSpacing,
|
||||
},
|
||||
...titlebarButton,
|
||||
},
|
||||
|
||||
// Offline Indicator
|
||||
offlineIcon: {
|
||||
color: foreground(layer, "variant"),
|
||||
width: 16,
|
||||
margin: {
|
||||
left: itemSpacing,
|
||||
},
|
||||
padding: {
|
||||
right: 4,
|
||||
},
|
||||
},
|
||||
|
||||
// Notice that the collaboration server is out of date
|
||||
outdatedWarning: {
|
||||
...text(layer, "sans", "warning", { size: "xs" }),
|
||||
background: withOpacity(background(layer, "warning"), 0.3),
|
||||
border: border(layer, "warning"),
|
||||
margin: {
|
||||
left: itemSpacing,
|
||||
},
|
||||
padding: {
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
cornerRadius: 6,
|
||||
},
|
||||
callControl: {
|
||||
cornerRadius: 6,
|
||||
color: foreground(layer, "variant"),
|
||||
iconWidth: 12,
|
||||
buttonWidth: 20,
|
||||
hover: {
|
||||
background: background(layer, "variant", "hovered"),
|
||||
color: foreground(layer, "variant", "hovered"),
|
||||
},
|
||||
},
|
||||
toggleContactsButton: {
|
||||
margin: { left: itemSpacing },
|
||||
cornerRadius: 6,
|
||||
color: foreground(layer, "variant"),
|
||||
iconWidth: 14,
|
||||
buttonWidth: 20,
|
||||
active: {
|
||||
background: background(layer, "variant", "active"),
|
||||
color: foreground(layer, "variant", "active"),
|
||||
},
|
||||
clicked: {
|
||||
background: background(layer, "variant", "pressed"),
|
||||
color: foreground(layer, "variant", "pressed"),
|
||||
},
|
||||
hover: {
|
||||
background: background(layer, "variant", "hovered"),
|
||||
color: foreground(layer, "variant", "hovered"),
|
||||
},
|
||||
},
|
||||
userMenuButton: {
|
||||
buttonWidth: 20,
|
||||
iconWidth: 12,
|
||||
...titlebarButton,
|
||||
},
|
||||
toggleContactsBadge: {
|
||||
cornerRadius: 3,
|
||||
padding: 2,
|
||||
margin: { top: 3, left: 3 },
|
||||
border: border(layer),
|
||||
background: foreground(layer, "accent"),
|
||||
},
|
||||
shareButton: {
|
||||
...titlebarButton,
|
||||
},
|
||||
},
|
||||
|
||||
toolbar: {
|
||||
height: 34,
|
||||
background: background(colorScheme.highest),
|
||||
border: border(colorScheme.highest, { bottom: true }),
|
||||
itemSpacing: 8,
|
||||
navButton: {
|
||||
color: foreground(colorScheme.highest, "on"),
|
||||
iconWidth: 12,
|
||||
buttonWidth: 24,
|
||||
cornerRadius: 6,
|
||||
hover: {
|
||||
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: {
|
||||
...text(colorScheme.highest, "sans", "variant"),
|
||||
cornerRadius: 6,
|
||||
padding: {
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
hover: {
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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 welcome from "./welcome"
|
||||
import copilot from "./copilot"
|
||||
import assistant from "./assistant"
|
||||
import { titlebar } from "./titlebar"
|
||||
import editor from "./editor"
|
||||
import feedback from "./feedback"
|
||||
import { useTheme } from "../common"
|
||||
|
||||
export default function app(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
return {
|
||||
meta: {
|
||||
name: theme.name,
|
||||
is_light: theme.is_light,
|
||||
},
|
||||
command_palette: command_palette(),
|
||||
contact_notification: contact_notification(),
|
||||
project_shared_notification: project_shared_notification(),
|
||||
incoming_call_notification: incoming_call_notification(),
|
||||
picker: picker(),
|
||||
workspace: workspace(),
|
||||
titlebar: titlebar(),
|
||||
copilot: copilot(),
|
||||
welcome: welcome(),
|
||||
context_menu: context_menu(),
|
||||
editor: editor(),
|
||||
project_diagnostics: project_diagnostics(),
|
||||
project_panel: project_panel(),
|
||||
contacts_popover: contacts_popover(),
|
||||
contact_finder: contact_finder(),
|
||||
contact_list: contact_list(),
|
||||
toolbar_dropdown_menu: toolbar_dropdown_menu(),
|
||||
search: search(),
|
||||
shared_screen: shared_screen(),
|
||||
update_notification: update_notification(),
|
||||
simple_message_notification: simple_message_notification(),
|
||||
tooltip: tooltip(),
|
||||
terminal: terminal(),
|
||||
assistant: assistant(),
|
||||
feedback: feedback()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
import { text, border, background, foreground } from "./components"
|
||||
import { interactive } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function assistant(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
return {
|
||||
container: {
|
||||
background: background(theme.highest),
|
||||
padding: { left: 12 },
|
||||
},
|
||||
message_header: {
|
||||
margin: { bottom: 6, top: 6 },
|
||||
background: background(theme.highest),
|
||||
},
|
||||
hamburger_button: interactive({
|
||||
base: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "variant"),
|
||||
asset: "icons/hamburger_15.svg",
|
||||
dimensions: {
|
||||
width: 15,
|
||||
height: 15,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
padding: { left: 12, right: 8.5 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
split_button: interactive({
|
||||
base: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "variant"),
|
||||
asset: "icons/split_message_15.svg",
|
||||
dimensions: {
|
||||
width: 15,
|
||||
height: 15,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
padding: { left: 8.5, right: 8.5 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
quote_button: interactive({
|
||||
base: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "variant"),
|
||||
asset: "icons/quote_15.svg",
|
||||
dimensions: {
|
||||
width: 15,
|
||||
height: 15,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
padding: { left: 8.5, right: 8.5 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
assist_button: interactive({
|
||||
base: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "variant"),
|
||||
asset: "icons/assist_15.svg",
|
||||
dimensions: {
|
||||
width: 15,
|
||||
height: 15,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
padding: { left: 8.5, right: 8.5 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
zoom_in_button: interactive({
|
||||
base: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "variant"),
|
||||
asset: "icons/maximize_8.svg",
|
||||
dimensions: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
padding: { left: 10, right: 10 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
zoom_out_button: interactive({
|
||||
base: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "variant"),
|
||||
asset: "icons/minimize_8.svg",
|
||||
dimensions: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
padding: { left: 10, right: 10 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
plus_button: interactive({
|
||||
base: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "variant"),
|
||||
asset: "icons/plus_12.svg",
|
||||
dimensions: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
padding: { left: 10, right: 10 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.highest, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
title: {
|
||||
...text(theme.highest, "sans", "default", { size: "sm" }),
|
||||
},
|
||||
saved_conversation: {
|
||||
container: interactive({
|
||||
base: {
|
||||
background: background(theme.highest, "on"),
|
||||
padding: { top: 4, bottom: 4 },
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
background: background(theme.highest, "on", "hovered"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
saved_at: {
|
||||
margin: { left: 8 },
|
||||
...text(theme.highest, "sans", "default", { size: "xs" }),
|
||||
},
|
||||
title: {
|
||||
margin: { left: 16 },
|
||||
...text(theme.highest, "sans", "default", {
|
||||
size: "sm",
|
||||
weight: "bold",
|
||||
}),
|
||||
},
|
||||
},
|
||||
user_sender: {
|
||||
default: {
|
||||
...text(theme.highest, "sans", "default", {
|
||||
size: "sm",
|
||||
weight: "bold",
|
||||
}),
|
||||
},
|
||||
},
|
||||
assistant_sender: {
|
||||
default: {
|
||||
...text(theme.highest, "sans", "accent", {
|
||||
size: "sm",
|
||||
weight: "bold",
|
||||
}),
|
||||
},
|
||||
},
|
||||
system_sender: {
|
||||
default: {
|
||||
...text(theme.highest, "sans", "variant", {
|
||||
size: "sm",
|
||||
weight: "bold",
|
||||
}),
|
||||
},
|
||||
},
|
||||
sent_at: {
|
||||
margin: { top: 2, left: 8 },
|
||||
...text(theme.highest, "sans", "default", { size: "2xs" }),
|
||||
},
|
||||
model: interactive({
|
||||
base: {
|
||||
background: background(theme.highest, "on"),
|
||||
margin: { left: 12, right: 12, top: 12 },
|
||||
padding: 4,
|
||||
corner_radius: 4,
|
||||
...text(theme.highest, "sans", "default", { size: "xs" }),
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
background: background(theme.highest, "on", "hovered"),
|
||||
border: border(theme.highest, "on", { overlay: true }),
|
||||
},
|
||||
},
|
||||
}),
|
||||
remaining_tokens: {
|
||||
background: background(theme.highest, "on"),
|
||||
margin: { top: 12, right: 24 },
|
||||
padding: 4,
|
||||
corner_radius: 4,
|
||||
...text(theme.highest, "sans", "positive", { size: "xs" }),
|
||||
},
|
||||
no_remaining_tokens: {
|
||||
background: background(theme.highest, "on"),
|
||||
margin: { top: 12, right: 24 },
|
||||
padding: 4,
|
||||
corner_radius: 4,
|
||||
...text(theme.highest, "sans", "negative", { size: "xs" }),
|
||||
},
|
||||
error_icon: {
|
||||
margin: { left: 8 },
|
||||
color: foreground(theme.highest, "negative"),
|
||||
width: 12,
|
||||
},
|
||||
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: theme.players[0],
|
||||
border: border(theme.highest, "on"),
|
||||
padding: {
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
top: 4,
|
||||
},
|
||||
},
|
||||
api_key_prompt: {
|
||||
padding: 10,
|
||||
...text(theme.highest, "sans", "default", { size: "xs" }),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { with_opacity } from "../theme/color"
|
||||
import { text, background } from "./components"
|
||||
import { toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function command_palette(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
const key = toggleable({
|
||||
base: {
|
||||
text: text(theme.highest, "mono", "variant", "default", {
|
||||
size: "xs",
|
||||
}),
|
||||
corner_radius: 2,
|
||||
background: background(theme.highest, "on"),
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
margin: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 2,
|
||||
},
|
||||
},
|
||||
state: {
|
||||
active: {
|
||||
text: text(theme.highest, "mono", "on", "default", {
|
||||
size: "xs",
|
||||
}),
|
||||
background: with_opacity(background(theme.highest, "on"), 0.2),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
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 },
|
||||
active: key.active,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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/create_theme"
|
||||
|
||||
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,70 +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
|
||||
return get_style(layer, style_set_or_styles, style).foreground
|
||||
}
|
||||
|
||||
interface Text {
|
||||
family: keyof typeof fontFamilies
|
||||
export interface TextStyle extends Object {
|
||||
family: keyof typeof font_families
|
||||
color: string
|
||||
size: number
|
||||
weight?: FontWeight
|
||||
@@ -94,7 +94,7 @@ interface Text {
|
||||
}
|
||||
|
||||
export interface TextProperties {
|
||||
size?: keyof typeof fontSizes
|
||||
size?: keyof typeof font_sizes
|
||||
weight?: FontWeight
|
||||
underline?: boolean
|
||||
color?: string
|
||||
@@ -174,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,
|
||||
@@ -244,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(
|
||||
@@ -261,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 {
|
||||
@@ -283,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,
|
||||
@@ -0,0 +1,74 @@
|
||||
import picker from "./picker"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function contact_finder(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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()
|
||||
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,
|
||||
header: picker_style.header,
|
||||
footer: picker_style.footer,
|
||||
},
|
||||
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"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
import {
|
||||
background,
|
||||
border,
|
||||
border_color,
|
||||
foreground,
|
||||
text,
|
||||
} from "./components"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
export default function contacts_panel(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
const name_margin = 8
|
||||
const side_padding = 12
|
||||
|
||||
const layer = theme.middle
|
||||
|
||||
const contact_button = {
|
||||
background: background(layer, "on"),
|
||||
color: foreground(layer, "on"),
|
||||
icon_width: 8,
|
||||
button_width: 16,
|
||||
corner_radius: 8,
|
||||
}
|
||||
const project_row = {
|
||||
guest_avatar_spacing: 4,
|
||||
height: 24,
|
||||
guest_avatar: {
|
||||
corner_radius: 8,
|
||||
width: 14,
|
||||
},
|
||||
name: {
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
margin: {
|
||||
left: name_margin,
|
||||
right: 6,
|
||||
},
|
||||
},
|
||||
guests: {
|
||||
margin: {
|
||||
left: name_margin,
|
||||
right: name_margin,
|
||||
},
|
||||
},
|
||||
padding: {
|
||||
left: side_padding,
|
||||
right: side_padding,
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
background: background(layer),
|
||||
padding: { top: 12 },
|
||||
user_query_editor: {
|
||||
background: background(layer, "on"),
|
||||
corner_radius: 6,
|
||||
text: text(layer, "mono", "on"),
|
||||
placeholder_text: text(layer, "mono", "on", "disabled", {
|
||||
size: "xs",
|
||||
}),
|
||||
selection: theme.players[0],
|
||||
border: border(layer, "on"),
|
||||
padding: {
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
top: 4,
|
||||
},
|
||||
margin: {
|
||||
left: 6,
|
||||
},
|
||||
},
|
||||
user_query_editor_height: 33,
|
||||
add_contact_button: {
|
||||
margin: { left: 6, right: 12 },
|
||||
color: foreground(layer, "on"),
|
||||
button_width: 28,
|
||||
icon_width: 16,
|
||||
},
|
||||
row_height: 28,
|
||||
section_icon_size: 8,
|
||||
header_row: toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
margin: { top: 14 },
|
||||
padding: {
|
||||
left: side_padding,
|
||||
right: side_padding,
|
||||
},
|
||||
background: background(layer, "default"), // posiewic: breaking change
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
background: background(layer, "pressed"),
|
||||
},
|
||||
}, // hack, we want headerRow to be interactive for whatever reason. It probably shouldn't be interactive in the first place.
|
||||
}),
|
||||
state: {
|
||||
active: {
|
||||
default: {
|
||||
...text(layer, "mono", "active", { size: "sm" }),
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
hovered: {
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
background: background(layer, "pressed"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
leave_call: interactive({
|
||||
base: {
|
||||
background: background(layer),
|
||||
border: border(layer),
|
||||
corner_radius: 6,
|
||||
margin: {
|
||||
top: 1,
|
||||
},
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 7,
|
||||
right: 7,
|
||||
},
|
||||
...text(layer, "sans", "variant", { size: "xs" }),
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
...text(layer, "sans", "hovered", { size: "xs" }),
|
||||
background: background(layer, "hovered"),
|
||||
border: border(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
contact_row: {
|
||||
inactive: {
|
||||
default: {
|
||||
padding: {
|
||||
left: side_padding,
|
||||
right: side_padding,
|
||||
},
|
||||
},
|
||||
},
|
||||
active: {
|
||||
default: {
|
||||
background: background(layer, "active"),
|
||||
padding: {
|
||||
left: side_padding,
|
||||
right: side_padding,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
contact_avatar: {
|
||||
corner_radius: 10,
|
||||
width: 18,
|
||||
},
|
||||
contact_status_free: {
|
||||
corner_radius: 4,
|
||||
padding: 4,
|
||||
margin: { top: 12, left: 12 },
|
||||
background: foreground(layer, "positive"),
|
||||
},
|
||||
contact_status_busy: {
|
||||
corner_radius: 4,
|
||||
padding: 4,
|
||||
margin: { top: 12, left: 12 },
|
||||
background: foreground(layer, "negative"),
|
||||
},
|
||||
contact_username: {
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
margin: {
|
||||
left: name_margin,
|
||||
},
|
||||
},
|
||||
contact_button_spacing: name_margin,
|
||||
contact_button: interactive({
|
||||
base: { ...contact_button },
|
||||
state: {
|
||||
hovered: {
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
disabled_button: {
|
||||
...contact_button,
|
||||
background: background(layer, "on"),
|
||||
color: foreground(layer, "on"),
|
||||
},
|
||||
calling_indicator: {
|
||||
...text(layer, "mono", "variant", { size: "xs" }),
|
||||
},
|
||||
tree_branch: toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
color: border_color(layer),
|
||||
width: 1,
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
color: border_color(layer),
|
||||
},
|
||||
},
|
||||
}),
|
||||
state: {
|
||||
active: {
|
||||
default: {
|
||||
color: border_color(layer),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
project_row: toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
...project_row,
|
||||
background: background(layer),
|
||||
icon: {
|
||||
margin: { left: name_margin },
|
||||
color: foreground(layer, "variant"),
|
||||
width: 12,
|
||||
},
|
||||
name: {
|
||||
...project_row.name,
|
||||
...text(layer, "mono", { size: "sm" }),
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
state: {
|
||||
active: {
|
||||
default: { background: background(layer, "active") },
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { background, foreground, text } from "./components"
|
||||
import { interactive } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function contact_notification(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { useTheme } from "../theme"
|
||||
import { background, border } from "./components"
|
||||
|
||||
export default function contacts_popover(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { background, border, border_color, text } from "./components"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function context_menu(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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 },
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
import { background, border, foreground, svg, text } from "./components"
|
||||
import { interactive } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
export default function copilot(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
const content_width = 264
|
||||
|
||||
const cta_button =
|
||||
// Copied from welcome screen. FIXME: Move this into a ZDS component
|
||||
interactive({
|
||||
base: {
|
||||
background: background(theme.middle),
|
||||
border: border(theme.middle, "default"),
|
||||
corner_radius: 4,
|
||||
margin: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
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"),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
out_link_icon: interactive({
|
||||
base: {
|
||||
icon: svg(
|
||||
foreground(theme.middle, "variant"),
|
||||
"icons/link_out_12.svg",
|
||||
12,
|
||||
12
|
||||
),
|
||||
container: {
|
||||
corner_radius: 6,
|
||||
padding: { left: 6 },
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: {
|
||||
color: foreground(theme.middle, "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
modal: {
|
||||
title_text: {
|
||||
default: {
|
||||
...text(theme.middle, "sans", {
|
||||
size: "xs",
|
||||
weight: "bold",
|
||||
}),
|
||||
},
|
||||
},
|
||||
titlebar: {
|
||||
background: background(theme.lowest),
|
||||
border: border(theme.middle, "active"),
|
||||
padding: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
background: background(theme.lowest),
|
||||
padding: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 8,
|
||||
},
|
||||
},
|
||||
close_icon: interactive({
|
||||
base: {
|
||||
icon: svg(
|
||||
foreground(theme.middle, "variant"),
|
||||
"icons/x_mark_8.svg",
|
||||
8,
|
||||
8
|
||||
),
|
||||
container: {
|
||||
corner_radius: 2,
|
||||
padding: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
left: 4,
|
||||
right: 4,
|
||||
},
|
||||
margin: {
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon: svg(
|
||||
foreground(theme.middle, "on"),
|
||||
"icons/x_mark_8.svg",
|
||||
8,
|
||||
8
|
||||
),
|
||||
},
|
||||
clicked: {
|
||||
icon: svg(
|
||||
foreground(theme.middle, "base"),
|
||||
"icons/x_mark_8.svg",
|
||||
8,
|
||||
8
|
||||
),
|
||||
},
|
||||
},
|
||||
}),
|
||||
dimensions: {
|
||||
width: 280,
|
||||
height: 280,
|
||||
},
|
||||
},
|
||||
|
||||
auth: {
|
||||
content_width,
|
||||
|
||||
cta_button,
|
||||
|
||||
header: {
|
||||
icon: svg(
|
||||
foreground(theme.middle, "default"),
|
||||
"icons/zed_plus_copilot_32.svg",
|
||||
92,
|
||||
32
|
||||
),
|
||||
container: {
|
||||
margin: {
|
||||
top: 35,
|
||||
bottom: 5,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
prompting: {
|
||||
subheading: {
|
||||
...text(theme.middle, "sans", { size: "xs" }),
|
||||
margin: {
|
||||
top: 6,
|
||||
bottom: 12,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
|
||||
hint: {
|
||||
...text(theme.middle, "sans", {
|
||||
size: "xs",
|
||||
color: "#838994",
|
||||
}),
|
||||
margin: {
|
||||
top: 6,
|
||||
bottom: 2,
|
||||
},
|
||||
},
|
||||
|
||||
device_code: {
|
||||
text: text(theme.middle, "mono", { size: "sm" }),
|
||||
cta: {
|
||||
...cta_button,
|
||||
background: background(theme.lowest),
|
||||
border: border(theme.lowest, "inverted"),
|
||||
padding: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 16,
|
||||
right: 16,
|
||||
},
|
||||
margin: {
|
||||
left: 16,
|
||||
right: 16,
|
||||
},
|
||||
},
|
||||
left: content_width / 2,
|
||||
left_container: {
|
||||
padding: {
|
||||
top: 3,
|
||||
bottom: 3,
|
||||
left: 0,
|
||||
right: 6,
|
||||
},
|
||||
},
|
||||
right: (content_width * 1) / 3,
|
||||
right_container: interactive({
|
||||
base: {
|
||||
border: border(theme.lowest, "inverted", {
|
||||
bottom: false,
|
||||
right: false,
|
||||
top: false,
|
||||
left: true,
|
||||
}),
|
||||
padding: {
|
||||
top: 3,
|
||||
bottom: 5,
|
||||
left: 8,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
border: border(theme.middle, "active", {
|
||||
bottom: false,
|
||||
right: false,
|
||||
top: false,
|
||||
left: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
not_authorized: {
|
||||
subheading: {
|
||||
...text(theme.middle, "sans", { size: "xs" }),
|
||||
|
||||
margin: {
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
|
||||
warning: {
|
||||
...text(theme.middle, "sans", {
|
||||
size: "xs",
|
||||
color: foreground(theme.middle, "warning"),
|
||||
}),
|
||||
border: border(theme.middle, "warning"),
|
||||
background: background(theme.middle, "warning"),
|
||||
corner_radius: 2,
|
||||
padding: {
|
||||
top: 4,
|
||||
left: 4,
|
||||
bottom: 4,
|
||||
right: 4,
|
||||
},
|
||||
margin: {
|
||||
bottom: 16,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
authorized: {
|
||||
subheading: {
|
||||
...text(theme.middle, "sans", { size: "xs" }),
|
||||
|
||||
margin: {
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
},
|
||||
},
|
||||
|
||||
hint: {
|
||||
...text(theme.middle, "sans", {
|
||||
size: "xs",
|
||||
color: "#838994",
|
||||
}),
|
||||
margin: {
|
||||
top: 24,
|
||||
bottom: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
import { with_opacity } from "../theme/color"
|
||||
import { Layer, StyleSets } from "../theme/create_theme"
|
||||
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"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function editor(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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()
|
||||
|
||||
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.
|
||||
hint: syntax.hint,
|
||||
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(),
|
||||
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),
|
||||
},
|
||||
selections: is_light
|
||||
? with_opacity(theme.ramps.blue(0.5).hex(), 0.8)
|
||||
: with_opacity(theme.ramps.blue(0.4).hex(), 0.8)
|
||||
},
|
||||
composition_mark: {
|
||||
underline: {
|
||||
thickness: 1.0,
|
||||
color: border_color(layer),
|
||||
},
|
||||
},
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { background, border, text } from "./components"
|
||||
import { interactive } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function feedback(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useTheme } from "../theme"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
|
||||
export default function hover_popover(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useTheme } from "../theme"
|
||||
import { background, border, text } from "./components"
|
||||
|
||||
export default function incoming_call_notification(): unknown {
|
||||
const theme = useTheme()
|
||||
|
||||
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",
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import { with_opacity } from "../theme/color"
|
||||
import { background, border, text } from "./components"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function picker(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
const container = {
|
||||
background: background(theme.lowest),
|
||||
border: border(theme.lowest),
|
||||
shadow: theme.modal_shadow,
|
||||
corner_radius: 12,
|
||||
padding: {
|
||||
bottom: 4,
|
||||
},
|
||||
}
|
||||
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,
|
||||
right: 16,
|
||||
top: 8,
|
||||
},
|
||||
margin: {
|
||||
bottom: 4,
|
||||
},
|
||||
}
|
||||
const empty_input_editor: any = { ...input_editor }
|
||||
delete empty_input_editor.border
|
||||
delete empty_input_editor.margin
|
||||
|
||||
return {
|
||||
...container,
|
||||
empty_container: {
|
||||
...container,
|
||||
padding: {},
|
||||
},
|
||||
item: toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
padding: {
|
||||
bottom: 4,
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 4,
|
||||
},
|
||||
margin: {
|
||||
top: 1,
|
||||
left: 4,
|
||||
right: 4,
|
||||
},
|
||||
corner_radius: 8,
|
||||
text: text(theme.lowest, "sans", "variant"),
|
||||
highlight_text: text(theme.lowest, "sans", "accent", {
|
||||
weight: "bold",
|
||||
}),
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "hovered"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
clicked: {
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "pressed"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
},
|
||||
}),
|
||||
state: {
|
||||
active: {
|
||||
default: {
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "base", "active"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
hovered: {
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "hovered"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
clicked: {
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "pressed"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
input_editor,
|
||||
empty_input_editor,
|
||||
no_matches: {
|
||||
text: text(theme.lowest, "sans", "variant"),
|
||||
padding: {
|
||||
bottom: 8,
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
},
|
||||
},
|
||||
header: {
|
||||
text: text(theme.lowest, "sans", "variant", { size: "xs" }),
|
||||
|
||||
margin: {
|
||||
top: 1,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
text: text(theme.lowest, "sans", "variant", { size: "xs" }),
|
||||
margin: {
|
||||
top: 1,
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useTheme } from "../theme"
|
||||
import { background, text } from "./components"
|
||||
|
||||
export default function project_diagnostics(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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" }),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
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"
|
||||
import { useTheme } from "../theme"
|
||||
export default function project_panel(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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, "sans", "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,
|
||||
{ background: background(theme.middle, "hovered") },
|
||||
unselected?.hovered ?? {}
|
||||
)
|
||||
const unselected_clicked_style = merge(
|
||||
base_properties,
|
||||
{ background: background(theme.middle, "pressed") },
|
||||
unselected?.clicked ?? {}
|
||||
)
|
||||
const selected_default_style = merge(
|
||||
base_properties,
|
||||
{
|
||||
background: background(theme.lowest),
|
||||
text: text(theme.lowest, "sans", { size: "sm" }),
|
||||
},
|
||||
selected_style?.default ?? {}
|
||||
)
|
||||
const selected_hovered_style = merge(
|
||||
base_properties,
|
||||
{
|
||||
background: background(theme.lowest, "hovered"),
|
||||
text: text(theme.lowest, "sans", { size: "sm" }),
|
||||
},
|
||||
selected_style?.hovered ?? {}
|
||||
)
|
||||
const selected_clicked_style = merge(
|
||||
base_properties,
|
||||
{
|
||||
background: background(theme.lowest, "pressed"),
|
||||
text: text(theme.lowest, "sans", { size: "sm" }),
|
||||
},
|
||||
selected_style?.clicked ?? {}
|
||||
)
|
||||
|
||||
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, "sans", "on", { size: "sm" }),
|
||||
background: with_opacity(background(theme.middle, "on"), 0.9),
|
||||
border: border(theme.middle),
|
||||
},
|
||||
ignored_entry: entry(
|
||||
{
|
||||
default: {
|
||||
text: text(theme.middle, "sans", "disabled"),
|
||||
},
|
||||
},
|
||||
{
|
||||
default: {
|
||||
icon_color: foreground(theme.middle, "variant"),
|
||||
},
|
||||
}
|
||||
),
|
||||
cut_entry: entry(
|
||||
{
|
||||
default: {
|
||||
text: text(theme.middle, "sans", "disabled"),
|
||||
},
|
||||
},
|
||||
{
|
||||
default: {
|
||||
background: background(theme.middle, "active"),
|
||||
text: text(theme.middle, "sans", "disabled", {
|
||||
size: "sm",
|
||||
}),
|
||||
},
|
||||
}
|
||||
),
|
||||
filename_editor: {
|
||||
background: background(theme.middle, "on"),
|
||||
text: text(theme.middle, "sans", "on", { size: "sm" }),
|
||||
selection: theme.players[0],
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useTheme } from "../theme"
|
||||
import { background, border, text } from "./components"
|
||||
|
||||
export default function project_shared_notification(): unknown {
|
||||
const theme = useTheme()
|
||||
|
||||
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",
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { with_opacity } from "../theme/color"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function search(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
// 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"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { useTheme } from "../theme"
|
||||
import { background } from "./components"
|
||||
|
||||
export default function sharedScreen() {
|
||||
const theme = useTheme()
|
||||
|
||||
return {
|
||||
background: background(theme.highest),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { background, border, foreground, text } from "./components"
|
||||
import { interactive } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function simple_message_notification(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { background, border, foreground, text } from "./components"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../common"
|
||||
export default function status_bar(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
const layer = theme.lowest
|
||||
|
||||
const status_container = {
|
||||
corner_radius: 6,
|
||||
padding: { top: 3, bottom: 3, left: 6, right: 6 },
|
||||
}
|
||||
|
||||
const diagnostic_status_container = {
|
||||
corner_radius: 6,
|
||||
padding: { top: 1, bottom: 1, left: 6, right: 6 },
|
||||
}
|
||||
|
||||
return {
|
||||
height: 30,
|
||||
item_spacing: 8,
|
||||
padding: {
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
border: border(layer, { top: true, overlay: true }),
|
||||
cursor_position: text(layer, "sans", "variant"),
|
||||
active_language: interactive({
|
||||
base: {
|
||||
padding: { left: 6, right: 6 },
|
||||
...text(layer, "sans", "variant"),
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
...text(layer, "sans", "on"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
auto_update_progress_message: text(layer, "sans", "variant"),
|
||||
auto_update_done_message: text(layer, "sans", "variant"),
|
||||
lsp_status: interactive({
|
||||
base: {
|
||||
...diagnostic_status_container,
|
||||
icon_spacing: 4,
|
||||
icon_width: 14,
|
||||
height: 18,
|
||||
message: text(layer, "sans"),
|
||||
icon_color: foreground(layer),
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
message: text(layer, "sans"),
|
||||
icon_color: foreground(layer),
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
diagnostic_message: interactive({
|
||||
base: {
|
||||
...text(layer, "sans"),
|
||||
},
|
||||
state: { hovered: text(layer, "sans", "hovered") },
|
||||
}),
|
||||
diagnostic_summary: interactive({
|
||||
base: {
|
||||
height: 20,
|
||||
icon_width: 16,
|
||||
icon_spacing: 2,
|
||||
summary_spacing: 6,
|
||||
text: text(layer, "sans", { size: "sm" }),
|
||||
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 },
|
||||
},
|
||||
container_warning: {
|
||||
...diagnostic_status_container,
|
||||
background: background(layer, "warning"),
|
||||
border: border(layer, "warning"),
|
||||
},
|
||||
container_error: {
|
||||
...diagnostic_status_container,
|
||||
background: background(layer, "negative"),
|
||||
border: border(layer, "negative"),
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon_color_ok: foreground(layer, "on"),
|
||||
container_ok: {
|
||||
background: background(layer, "on", "hovered"),
|
||||
},
|
||||
container_warning: {
|
||||
background: background(layer, "warning", "hovered"),
|
||||
border: border(layer, "warning", "hovered"),
|
||||
},
|
||||
container_error: {
|
||||
background: background(layer, "negative", "hovered"),
|
||||
border: border(layer, "negative", "hovered"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
panel_buttons: {
|
||||
group_left: {},
|
||||
group_bottom: {},
|
||||
group_right: {},
|
||||
button: toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
...status_container,
|
||||
icon_size: 16,
|
||||
icon_color: foreground(layer, "variant"),
|
||||
label: {
|
||||
margin: { left: 6 },
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
icon_color: foreground(layer, "hovered"),
|
||||
background: background(layer, "variant"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
state: {
|
||||
active: {
|
||||
default: {
|
||||
icon_color: foreground(layer, "active"),
|
||||
background: background(layer, "active"),
|
||||
},
|
||||
hovered: {
|
||||
icon_color: foreground(layer, "hovered"),
|
||||
background: background(layer, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
icon_color: foreground(layer, "pressed"),
|
||||
background: background(layer, "pressed"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
badge: {
|
||||
corner_radius: 3,
|
||||
padding: 2,
|
||||
margin: { bottom: -1, right: -1 },
|
||||
border: border(layer),
|
||||
background: background(layer, "accent"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { with_opacity } from "../theme/color"
|
||||
import { text, border, background, foreground } from "./components"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../common"
|
||||
|
||||
export default function tab_bar(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
const height = 32
|
||||
|
||||
const active_layer = theme.highest
|
||||
const layer = theme.middle
|
||||
|
||||
const tab = {
|
||||
height,
|
||||
text: text(layer, "sans", "variant", { size: "sm" }),
|
||||
background: background(layer),
|
||||
border: border(layer, {
|
||||
right: true,
|
||||
bottom: true,
|
||||
overlay: true,
|
||||
}),
|
||||
padding: {
|
||||
left: 8,
|
||||
right: 12,
|
||||
},
|
||||
spacing: 8,
|
||||
|
||||
// Tab type icons (e.g. Project Search)
|
||||
type_icon_width: 14,
|
||||
|
||||
// Close icons
|
||||
close_icon_width: 8,
|
||||
icon_close: foreground(layer, "variant"),
|
||||
icon_close_active: foreground(layer, "hovered"),
|
||||
|
||||
// Indicators
|
||||
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: {
|
||||
margin: { left: 8 },
|
||||
...text(layer, "sans", "disabled", { size: "2xs" }),
|
||||
},
|
||||
}
|
||||
|
||||
const active_pane_active_tab = {
|
||||
...tab,
|
||||
background: background(active_layer),
|
||||
text: text(active_layer, "sans", "active", { size: "sm" }),
|
||||
border: {
|
||||
...tab.border,
|
||||
bottom: false,
|
||||
},
|
||||
}
|
||||
|
||||
const inactive_pane_inactive_tab = {
|
||||
...tab,
|
||||
background: background(layer),
|
||||
text: text(layer, "sans", "variant", { size: "sm" }),
|
||||
}
|
||||
|
||||
const inactive_pane_active_tab = {
|
||||
...tab,
|
||||
background: background(active_layer),
|
||||
text: text(layer, "sans", "variant", { size: "sm" }),
|
||||
border: {
|
||||
...tab.border,
|
||||
bottom: false,
|
||||
},
|
||||
}
|
||||
|
||||
const dragged_tab = {
|
||||
...active_pane_active_tab,
|
||||
background: with_opacity(tab.background, 0.9),
|
||||
border: undefined as any,
|
||||
shadow: theme.popover_shadow,
|
||||
}
|
||||
|
||||
return {
|
||||
height,
|
||||
background: background(layer),
|
||||
active_pane: {
|
||||
active_tab: active_pane_active_tab,
|
||||
inactive_tab: tab,
|
||||
},
|
||||
inactive_pane: {
|
||||
active_tab: inactive_pane_active_tab,
|
||||
inactive_tab: inactive_pane_inactive_tab,
|
||||
},
|
||||
dragged_tab,
|
||||
pane_button: toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
color: foreground(layer, "variant"),
|
||||
icon_width: 12,
|
||||
button_width: active_pane_active_tab.height,
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
color: foreground(layer, "pressed"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
state: {
|
||||
active: {
|
||||
default: {
|
||||
color: foreground(layer, "accent"),
|
||||
},
|
||||
hovered: {
|
||||
color: foreground(layer, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
color: foreground(layer, "pressed"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
pane_button_container: {
|
||||
background: tab.background,
|
||||
border: {
|
||||
...tab.border,
|
||||
right: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function terminal() {
|
||||
const theme = useTheme()
|
||||
|
||||
/**
|
||||
* 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(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
import { icon_button, toggleable_icon_button } from "../component/icon_button"
|
||||
import { toggleable_text_button } from "../component/text_button"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
import { with_opacity } from "../theme/color"
|
||||
import { background, border, foreground, text } from "./components"
|
||||
|
||||
const ITEM_SPACING = 8
|
||||
const TITLEBAR_HEIGHT = 32
|
||||
|
||||
function build_spacing(
|
||||
container_height: number,
|
||||
element_height: number,
|
||||
spacing: number
|
||||
) {
|
||||
return {
|
||||
group: spacing,
|
||||
item: spacing / 2,
|
||||
half_item: spacing / 4,
|
||||
margin_y: (container_height - element_height) / 2,
|
||||
margin_x: (container_height - element_height) / 2,
|
||||
}
|
||||
}
|
||||
|
||||
function call_controls() {
|
||||
const theme = useTheme()
|
||||
|
||||
const button_height = 18
|
||||
|
||||
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
|
||||
const margin_y = {
|
||||
top: space.margin_y,
|
||||
bottom: space.margin_y,
|
||||
}
|
||||
|
||||
return {
|
||||
toggle_microphone_button: toggleable_icon_button(theme, {
|
||||
margin: {
|
||||
...margin_y,
|
||||
left: space.group,
|
||||
right: space.half_item,
|
||||
},
|
||||
active_color: "negative",
|
||||
}),
|
||||
|
||||
toggle_speakers_button: toggleable_icon_button(theme, {
|
||||
margin: {
|
||||
...margin_y,
|
||||
left: space.half_item,
|
||||
right: space.half_item,
|
||||
},
|
||||
}),
|
||||
|
||||
screen_share_button: toggleable_icon_button(theme, {
|
||||
margin: {
|
||||
...margin_y,
|
||||
left: space.half_item,
|
||||
right: space.group,
|
||||
},
|
||||
active_color: "accent",
|
||||
}),
|
||||
|
||||
muted: foreground(theme.lowest, "negative"),
|
||||
speaking: foreground(theme.lowest, "accent"),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the User Menu when toggled
|
||||
*
|
||||
* When logged in shows the user's avatar and a chevron,
|
||||
* When logged out only shows a chevron.
|
||||
*/
|
||||
function user_menu() {
|
||||
const theme = useTheme()
|
||||
|
||||
const button_height = 18
|
||||
|
||||
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
|
||||
|
||||
const build_button = ({ online }: { online: boolean }) => {
|
||||
const button = toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
corner_radius: 6,
|
||||
height: button_height,
|
||||
width: online ? 37 : 24,
|
||||
padding: {
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
left: 6,
|
||||
right: 6,
|
||||
},
|
||||
margin: {
|
||||
left: space.item,
|
||||
right: space.item,
|
||||
},
|
||||
...text(theme.lowest, "sans", { size: "xs" }),
|
||||
background: background(theme.lowest),
|
||||
},
|
||||
state: {
|
||||
hovered: {
|
||||
...text(theme.lowest, "sans", "hovered", {
|
||||
size: "xs",
|
||||
}),
|
||||
background: background(theme.lowest, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
...text(theme.lowest, "sans", "pressed", {
|
||||
size: "xs",
|
||||
}),
|
||||
background: background(theme.lowest, "pressed"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
state: {
|
||||
active: {
|
||||
default: {
|
||||
...text(theme.lowest, "sans", "active", { size: "xs" }),
|
||||
background: background(theme.middle),
|
||||
},
|
||||
hovered: {
|
||||
...text(theme.lowest, "sans", "active", { size: "xs" }),
|
||||
background: background(theme.middle, "hovered"),
|
||||
},
|
||||
clicked: {
|
||||
...text(theme.lowest, "sans", "active", { size: "xs" }),
|
||||
background: background(theme.middle, "pressed"),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
user_menu: button,
|
||||
avatar: {
|
||||
icon_width: 16,
|
||||
icon_height: 16,
|
||||
corner_radius: 4,
|
||||
outer_width: 16,
|
||||
outer_corner_radius: 16,
|
||||
},
|
||||
icon: {
|
||||
margin: {
|
||||
top: 2,
|
||||
left: online ? space.item : 0,
|
||||
right: space.group,
|
||||
bottom: 2,
|
||||
},
|
||||
width: 11,
|
||||
height: 11,
|
||||
color: foreground(theme.lowest),
|
||||
},
|
||||
}
|
||||
}
|
||||
return {
|
||||
user_menu_button_online: build_button({ online: true }),
|
||||
user_menu_button_offline: build_button({ online: false }),
|
||||
}
|
||||
}
|
||||
|
||||
export function titlebar(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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,
|
||||
face_pile_spacing: 2,
|
||||
height: TITLEBAR_HEIGHT,
|
||||
background: background(theme.lowest),
|
||||
border: border(theme.lowest, { bottom: true }),
|
||||
padding: {
|
||||
left: 80,
|
||||
right: 0,
|
||||
},
|
||||
|
||||
// Project
|
||||
project_name_divider: text(theme.lowest, "sans", "variant"),
|
||||
|
||||
project_menu_button: toggleable_text_button(theme, {
|
||||
color: 'base',
|
||||
}),
|
||||
git_menu_button: toggleable_text_button(theme, {
|
||||
color: 'variant',
|
||||
}),
|
||||
|
||||
// Collaborators
|
||||
leader_avatar: {
|
||||
width: avatar_width,
|
||||
outer_width: avatar_outer_width,
|
||||
corner_radius: avatar_width / 2,
|
||||
outer_corner_radius: avatar_outer_width / 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,
|
||||
},
|
||||
inactive_avatar_grayscale: true,
|
||||
follower_avatar_overlap: 8,
|
||||
leader_selection: {
|
||||
margin: {
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
},
|
||||
padding: {
|
||||
left: 2,
|
||||
right: 2,
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
},
|
||||
corner_radius: 6,
|
||||
},
|
||||
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, {}),
|
||||
offline_icon: {
|
||||
color: foreground(theme.lowest, "variant"),
|
||||
width: 16,
|
||||
margin: {
|
||||
left: ITEM_SPACING,
|
||||
},
|
||||
padding: {
|
||||
right: 4,
|
||||
},
|
||||
},
|
||||
|
||||
// When the collaboration server is out of date, show a warning
|
||||
outdated_warning: {
|
||||
...text(theme.lowest, "sans", "warning", { size: "xs" }),
|
||||
background: with_opacity(background(theme.lowest, "warning"), 0.3),
|
||||
border: border(theme.lowest, "warning"),
|
||||
margin: {
|
||||
left: ITEM_SPACING,
|
||||
},
|
||||
padding: {
|
||||
left: 8,
|
||||
right: 8,
|
||||
},
|
||||
corner_radius: 6,
|
||||
},
|
||||
|
||||
leave_call_button: icon_button({
|
||||
margin: {
|
||||
left: ITEM_SPACING / 2,
|
||||
right: ITEM_SPACING,
|
||||
},
|
||||
}),
|
||||
|
||||
...call_controls(),
|
||||
|
||||
toggle_contacts_button: toggleable_icon_button(theme, {
|
||||
margin: {
|
||||
left: ITEM_SPACING,
|
||||
},
|
||||
}),
|
||||
|
||||
// Jewel that notifies you that there are new contact requests
|
||||
toggle_contacts_badge: {
|
||||
corner_radius: 3,
|
||||
padding: 2,
|
||||
margin: { top: 3, left: 3 },
|
||||
border: border(theme.lowest),
|
||||
background: foreground(theme.lowest, "accent"),
|
||||
},
|
||||
share_button: toggleable_text_button(theme, {}),
|
||||
user_menu: user_menu(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { background, border, text } from "./components"
|
||||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
export default function dropdown_menu(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { useTheme } from "../theme"
|
||||
import { background, border, text } from "./components"
|
||||
|
||||
export default function tooltip(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { foreground, text } from "./components"
|
||||
import { interactive } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function update_notification(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import { with_opacity } from "../theme/color"
|
||||
import {
|
||||
border,
|
||||
background,
|
||||
foreground,
|
||||
text,
|
||||
TextProperties,
|
||||
svg,
|
||||
} from "./components"
|
||||
import { interactive } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function welcome(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
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"
|
||||
import { useTheme } from "../theme"
|
||||
|
||||
export default function workspace(): any {
|
||||
const theme = useTheme()
|
||||
|
||||
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(),
|
||||
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(),
|
||||
titlebar: titlebar(),
|
||||
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
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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]
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -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],
|
||||
},
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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 Theme {
|
||||
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_theme(theme: ThemeConfig): Theme {
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,25 @@
|
||||
export * from "./colorScheme"
|
||||
import { create } from "zustand"
|
||||
import { Theme } from "./create_theme"
|
||||
|
||||
type ThemeState = {
|
||||
theme: Theme | undefined
|
||||
setTheme: (theme: Theme) => void
|
||||
}
|
||||
|
||||
export const useThemeStore = create<ThemeState>((set) => ({
|
||||
theme: undefined,
|
||||
setTheme: (theme) => set(() => ({ theme })),
|
||||
}))
|
||||
|
||||
export const useTheme = (): Theme => {
|
||||
const { theme } = useThemeStore.getState()
|
||||
|
||||
if (!theme) throw new Error("Tried to use theme before it was loaded")
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
export * from "./create_theme"
|
||||
export * from "./ramps"
|
||||
export * from "./syntax"
|
||||
export * from "./themeConfig"
|
||||
export * from "./theme_config"
|
||||
|
||||
+22
-22
@@ -1,14 +1,14 @@
|
||||
import chroma, { Color, Scale } from "chroma-js"
|
||||
import { RampSet } from "./colorScheme"
|
||||
import { RampSet } from "./create_theme"
|
||||
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
|
||||
|
||||
+68
-55
@@ -1,6 +1,5 @@
|
||||
import deepmerge from "deepmerge"
|
||||
import { FontWeight, fontWeights } from "../common"
|
||||
import { ColorScheme } from "./colorScheme"
|
||||
import { FontWeight, font_weights, useTheme } from "../common"
|
||||
import chroma from "chroma-js"
|
||||
|
||||
export interface SyntaxHighlightStyle {
|
||||
@@ -17,13 +16,14 @@ export interface Syntax {
|
||||
"comment.doc": SyntaxHighlightStyle
|
||||
primary: SyntaxHighlightStyle
|
||||
predictive: SyntaxHighlightStyle
|
||||
hint: SyntaxHighlightStyle
|
||||
|
||||
// === Formatted Text ====== /
|
||||
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,25 +116,25 @@ 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(): Syntax {
|
||||
const theme = useTheme()
|
||||
|
||||
// Make a temporary object that is allowed to be missing
|
||||
// the "color" property for each style
|
||||
const syntax: {
|
||||
[key: string]: Omit<SyntaxHighlightStyle, "color">
|
||||
} = {}
|
||||
|
||||
const light = colorScheme.isLight
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,35 +142,46 @@ 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(),
|
||||
theme.ramps.neutral(0.4).hex(),
|
||||
theme.ramps.blue(0.4).hex(),
|
||||
0.45,
|
||||
"lch"
|
||||
)
|
||||
.hex()
|
||||
// Mix the neutral and green colors to get a
|
||||
// hint color distinct from any other color in the theme
|
||||
const hint = chroma
|
||||
.mix(
|
||||
theme.ramps.neutral(0.6).hex(),
|
||||
theme.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: theme.ramps.neutral(1).hex(),
|
||||
comment: theme.ramps.neutral(0.71).hex(),
|
||||
punctuation: theme.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(),
|
||||
hint: hint,
|
||||
emphasis: theme.ramps.blue(0.5).hex(),
|
||||
string: theme.ramps.orange(0.5).hex(),
|
||||
function: theme.ramps.yellow(0.5).hex(),
|
||||
type: theme.ramps.cyan(0.5).hex(),
|
||||
constructor: theme.ramps.blue(0.5).hex(),
|
||||
variant: theme.ramps.blue(0.5).hex(),
|
||||
property: theme.ramps.blue(0.5).hex(),
|
||||
enum: theme.ramps.orange(0.5).hex(),
|
||||
operator: theme.ramps.orange(0.5).hex(),
|
||||
number: theme.ramps.green(0.5).hex(),
|
||||
boolean: theme.ramps.green(0.5).hex(),
|
||||
constant: theme.ramps.green(0.5).hex(),
|
||||
keyword: theme.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,
|
||||
@@ -185,23 +196,27 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
|
||||
color: color.predictive,
|
||||
italic: true,
|
||||
},
|
||||
hint: {
|
||||
color: color.hint,
|
||||
weight: font_weights.bold,
|
||||
},
|
||||
emphasis: {
|
||||
color: color.emphasis,
|
||||
},
|
||||
"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: theme.ramps.green(0.5).hex(),
|
||||
underline: true,
|
||||
},
|
||||
linkText: {
|
||||
color: colorScheme.ramps.orange(0.5).hex(),
|
||||
link_text: {
|
||||
color: theme.ramps.orange(0.5).hex(),
|
||||
italic: true,
|
||||
},
|
||||
"text.literal": {
|
||||
@@ -217,7 +232,7 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
|
||||
color: color.punctuation,
|
||||
},
|
||||
"punctuation.special": {
|
||||
color: colorScheme.ramps.neutral(0.86).hex(),
|
||||
color: theme.ramps.neutral(0.86).hex(),
|
||||
},
|
||||
"punctuation.list_marker": {
|
||||
color: color.punctuation,
|
||||
@@ -238,10 +253,10 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
|
||||
color: color.string,
|
||||
},
|
||||
constructor: {
|
||||
color: colorScheme.ramps.blue(0.5).hex(),
|
||||
color: theme.ramps.blue(0.5).hex(),
|
||||
},
|
||||
variant: {
|
||||
color: colorScheme.ramps.blue(0.5).hex(),
|
||||
color: theme.ramps.blue(0.5).hex(),
|
||||
},
|
||||
type: {
|
||||
color: color.type,
|
||||
@@ -250,16 +265,16 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
|
||||
color: color.primary,
|
||||
},
|
||||
label: {
|
||||
color: colorScheme.ramps.blue(0.5).hex(),
|
||||
color: theme.ramps.blue(0.5).hex(),
|
||||
},
|
||||
tag: {
|
||||
color: colorScheme.ramps.blue(0.5).hex(),
|
||||
color: theme.ramps.blue(0.5).hex(),
|
||||
},
|
||||
attribute: {
|
||||
color: colorScheme.ramps.blue(0.5).hex(),
|
||||
color: theme.ramps.blue(0.5).hex(),
|
||||
},
|
||||
property: {
|
||||
color: colorScheme.ramps.blue(0.5).hex(),
|
||||
color: theme.ramps.blue(0.5).hex(),
|
||||
},
|
||||
constant: {
|
||||
color: color.constant,
|
||||
@@ -290,17 +305,21 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
|
||||
},
|
||||
}
|
||||
|
||||
return defaultSyntax
|
||||
return default_syntax
|
||||
}
|
||||
|
||||
function mergeSyntax(defaultSyntax: Syntax, colorScheme: ColorScheme): Syntax {
|
||||
if (!colorScheme.syntax) {
|
||||
return defaultSyntax
|
||||
export function build_syntax(): Syntax {
|
||||
const theme = useTheme()
|
||||
|
||||
const default_syntax: Syntax = build_default_syntax()
|
||||
|
||||
if (!theme.syntax) {
|
||||
return default_syntax
|
||||
}
|
||||
|
||||
return deepmerge<Syntax, Partial<ThemeSyntax>>(
|
||||
defaultSyntax,
|
||||
colorScheme.syntax,
|
||||
const syntax = deepmerge<Syntax, Partial<ThemeSyntax>>(
|
||||
default_syntax,
|
||||
theme.syntax,
|
||||
{
|
||||
arrayMerge: (destinationArray, sourceArray) => [
|
||||
...destinationArray,
|
||||
@@ -308,12 +327,6 @@ function mergeSyntax(defaultSyntax: Syntax, colorScheme: ColorScheme): Syntax {
|
||||
],
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function buildSyntax(colorScheme: ColorScheme): Syntax {
|
||||
const defaultSyntax: Syntax = buildDefaultSyntax(colorScheme)
|
||||
|
||||
const syntax = mergeSyntax(defaultSyntax, colorScheme)
|
||||
|
||||
return syntax
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import { Scale, Color } from "chroma-js"
|
||||
import { Syntax } from "./syntax"
|
||||
|
||||
interface ThemeMeta {
|
||||
/** The name of the theme */
|
||||
name: string
|
||||
/** The theme's appearance. Either `light` or `dark`. */
|
||||
appearance: ThemeAppearance
|
||||
/** The author of the theme
|
||||
*
|
||||
* Ideally formatted as `Full Name <email>`
|
||||
*
|
||||
* Example: `John Doe <john@doe.com>`
|
||||
*/
|
||||
author: string
|
||||
/** SPDX License string
|
||||
*
|
||||
* Example: `MIT`
|
||||
*/
|
||||
licenseType?: string | ThemeLicenseType
|
||||
licenseUrl?: string
|
||||
licenseFile: string
|
||||
themeUrl?: string
|
||||
}
|
||||
|
||||
export type ThemeFamilyMeta = Pick<
|
||||
ThemeMeta,
|
||||
"name" | "author" | "licenseType" | "licenseUrl"
|
||||
>
|
||||
|
||||
export interface ThemeConfigInputColors {
|
||||
neutral: Scale<Color>
|
||||
red: Scale<Color>
|
||||
orange: Scale<Color>
|
||||
yellow: Scale<Color>
|
||||
green: Scale<Color>
|
||||
cyan: Scale<Color>
|
||||
blue: Scale<Color>
|
||||
violet: Scale<Color>
|
||||
magenta: Scale<Color>
|
||||
}
|
||||
|
||||
export type ThemeConfigInputColorsKeys = keyof ThemeConfigInputColors
|
||||
|
||||
/** Allow any part of a syntax highlight style to be overriden by the theme
|
||||
*
|
||||
* Example:
|
||||
* ```ts
|
||||
* override: {
|
||||
* syntax: {
|
||||
* boolean: {
|
||||
* underline: true,
|
||||
* },
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export type ThemeConfigInputSyntax = Partial<Syntax>
|
||||
|
||||
interface ThemeConfigOverrides {
|
||||
syntax: ThemeConfigInputSyntax
|
||||
}
|
||||
|
||||
type ThemeConfigProperties = ThemeMeta & {
|
||||
inputColor: ThemeConfigInputColors
|
||||
override: ThemeConfigOverrides
|
||||
}
|
||||
|
||||
// This should be the format a theme is defined as
|
||||
export type ThemeConfig = {
|
||||
[K in keyof ThemeConfigProperties]: ThemeConfigProperties[K]
|
||||
}
|
||||
|
||||
interface ThemeColors {
|
||||
neutral: string[]
|
||||
red: string[]
|
||||
orange: string[]
|
||||
yellow: string[]
|
||||
green: string[]
|
||||
cyan: string[]
|
||||
blue: string[]
|
||||
violet: string[]
|
||||
magenta: string[]
|
||||
}
|
||||
|
||||
type ThemeSyntax = Required<Syntax>
|
||||
|
||||
export type ThemeProperties = ThemeMeta & {
|
||||
color: ThemeColors
|
||||
syntax: ThemeSyntax
|
||||
}
|
||||
|
||||
// This should be a theme after all its properties have been resolved
|
||||
export type Theme = {
|
||||
[K in keyof ThemeProperties]: ThemeProperties[K]
|
||||
}
|
||||
|
||||
export enum ThemeAppearance {
|
||||
Light = "light",
|
||||
Dark = "dark",
|
||||
}
|
||||
|
||||
export enum ThemeLicenseType {
|
||||
MIT = "MIT",
|
||||
Apache2 = "Apache License 2.0",
|
||||
}
|
||||
|
||||
export type ThemeFamilyItem =
|
||||
| ThemeConfig
|
||||
| { light: ThemeConfig; dark: ThemeConfig }
|
||||
|
||||
type ThemeFamilyProperties = Partial<Omit<ThemeMeta, "name" | "appearance">> & {
|
||||
name: string
|
||||
default: ThemeFamilyItem
|
||||
variants: {
|
||||
[key: string]: ThemeFamilyItem
|
||||
}
|
||||
}
|
||||
|
||||
// Idea: A theme family is a collection of themes that share the same name
|
||||
// For example, a theme family could be `One Dark` and have a `light` and `dark` variant
|
||||
// The Ayu family could have `light`, `mirage`, and `dark` variants
|
||||
|
||||
type ThemeFamily = {
|
||||
[K in keyof ThemeFamilyProperties]: ThemeFamilyProperties[K]
|
||||
}
|
||||
|
||||
/** The collection of all themes
|
||||
*
|
||||
* Example:
|
||||
* ```ts
|
||||
* {
|
||||
* one_dark,
|
||||
* one_light,
|
||||
* ayu: {
|
||||
* name: 'Ayu',
|
||||
* default: 'ayu_mirage',
|
||||
* variants: {
|
||||
* light: 'ayu_light',
|
||||
* mirage: 'ayu_mirage',
|
||||
* dark: 'ayu_dark',
|
||||
* },
|
||||
* },
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export type ThemeIndex = Record<string, ThemeFamily | ThemeConfig>
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Scale, Color } from "chroma-js"
|
||||
import { Syntax } from "./syntax"
|
||||
|
||||
interface ThemeMeta {
|
||||
/** The name of the theme */
|
||||
name: string
|
||||
/** The theme's appearance. Either `light` or `dark`. */
|
||||
appearance: ThemeAppearance
|
||||
/** The author of the theme
|
||||
*
|
||||
* Ideally formatted as `Full Name <email>`
|
||||
*
|
||||
* Example: `John Doe <john@doe.com>`
|
||||
*/
|
||||
author: string
|
||||
/** SPDX License string
|
||||
*
|
||||
* Example: `MIT`
|
||||
*/
|
||||
license_type?: string | ThemeLicenseType
|
||||
license_url?: string
|
||||
license_file: string
|
||||
theme_url?: string
|
||||
}
|
||||
|
||||
export type ThemeFamilyMeta = Pick<
|
||||
ThemeMeta,
|
||||
"name" | "author" | "license_type" | "license_url"
|
||||
>
|
||||
|
||||
export interface ThemeConfigInputColors {
|
||||
neutral: Scale<Color>
|
||||
red: Scale<Color>
|
||||
orange: Scale<Color>
|
||||
yellow: Scale<Color>
|
||||
green: Scale<Color>
|
||||
cyan: Scale<Color>
|
||||
blue: Scale<Color>
|
||||
violet: Scale<Color>
|
||||
magenta: Scale<Color>
|
||||
}
|
||||
|
||||
export type ThemeConfigInputColorsKeys = keyof ThemeConfigInputColors
|
||||
|
||||
/** Allow any part of a syntax highlight style to be overriden by the theme
|
||||
*
|
||||
* Example:
|
||||
* ```ts
|
||||
* override: {
|
||||
* syntax: {
|
||||
* boolean: {
|
||||
* underline: true,
|
||||
* },
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export type ThemeConfigInputSyntax = Partial<Syntax>
|
||||
|
||||
interface ThemeConfigOverrides {
|
||||
syntax: ThemeConfigInputSyntax
|
||||
}
|
||||
|
||||
type ThemeConfigProperties = ThemeMeta & {
|
||||
input_color: ThemeConfigInputColors
|
||||
override: ThemeConfigOverrides
|
||||
}
|
||||
|
||||
export type ThemeConfig = {
|
||||
[K in keyof ThemeConfigProperties]: ThemeConfigProperties[K]
|
||||
}
|
||||
|
||||
export enum ThemeAppearance {
|
||||
Light = "light",
|
||||
Dark = "dark",
|
||||
}
|
||||
|
||||
export enum ThemeLicenseType {
|
||||
MIT = "MIT",
|
||||
Apache2 = "Apache License 2.0",
|
||||
}
|
||||
@@ -1,81 +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),
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { SingleColorToken } from "@tokens-studio/types";
|
||||
import { Layer, Style, StyleSet } from "../colorScheme";
|
||||
import { colorToken } from "./token";
|
||||
import { SingleColorToken } from "@tokens-studio/types"
|
||||
import { Layer, Style, StyleSet } from "../create_theme"
|
||||
import { color_token } from "./token"
|
||||
|
||||
interface StyleToken {
|
||||
background: SingleColorToken,
|
||||
border: SingleColorToken,
|
||||
foreground: SingleColorToken,
|
||||
background: SingleColorToken
|
||||
border: SingleColorToken
|
||||
foreground: SingleColorToken
|
||||
}
|
||||
|
||||
interface StyleSetToken {
|
||||
@@ -27,34 +27,37 @@ 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, name: string): StyleSetToken => {
|
||||
const token: StyleSetToken = {} as StyleSetToken;
|
||||
export const style_set_token = (
|
||||
style_set: StyleSet,
|
||||
name: string
|
||||
): StyleSetToken => {
|
||||
const token: StyleSetToken = {} as StyleSetToken
|
||||
|
||||
for (const style in styleSet) {
|
||||
const s = style as keyof StyleSet;
|
||||
token[s] = styleToken(styleSet[s], `${name}${style}`);
|
||||
for (const style in style_set) {
|
||||
const s = style as keyof StyleSet
|
||||
token[s] = style_token(style_set[s], `${name}${style}`)
|
||||
}
|
||||
|
||||
return token;
|
||||
return token
|
||||
}
|
||||
|
||||
export const layerToken = (layer: Layer, name: string): LayerToken => {
|
||||
const token: LayerToken = {} as 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;
|
||||
return token
|
||||
}
|
||||
|
||||
@@ -1,28 +1,37 @@
|
||||
import { SingleColorToken } from "@tokens-studio/types"
|
||||
import { ColorScheme, Players } from "../../common"
|
||||
import { colorToken } from "./token"
|
||||
import { color_token } from "./token"
|
||||
import { Players } from "../create_theme"
|
||||
import { useTheme } from "../../../src/common"
|
||||
|
||||
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(index: number): PlayerToken {
|
||||
const theme = useTheme()
|
||||
const player_number = index.toString() as keyof Players
|
||||
|
||||
return {
|
||||
selection: colorToken(`player${index}Selection`, colorScheme.players[playerNumber].selection),
|
||||
cursor: colorToken(`player${index}Cursor`, colorScheme.players[playerNumber].cursor),
|
||||
selection: color_token(
|
||||
`player${index}Selection`,
|
||||
theme.players[player_number].selection
|
||||
),
|
||||
cursor: color_token(
|
||||
`player${index}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 = (): PlayersToken => {
|
||||
return {
|
||||
"0": build_player_token(0),
|
||||
"1": build_player_token(1),
|
||||
"2": build_player_token(2),
|
||||
"3": build_player_token(3),
|
||||
"4": build_player_token(4),
|
||||
"5": build_player_token(5),
|
||||
"6": build_player_token(6),
|
||||
"7": build_player_token(7),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import {
|
||||
SingleBoxShadowToken,
|
||||
SingleColorToken,
|
||||
SingleOtherToken,
|
||||
TokenTypes,
|
||||
} from "@tokens-studio/types"
|
||||
import {
|
||||
Shadow,
|
||||
SyntaxHighlightStyle,
|
||||
ThemeSyntax,
|
||||
} from "../create_theme"
|
||||
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"
|
||||
import { useTheme } from "../../../src/common"
|
||||
|
||||
interface ThemeTokens {
|
||||
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 = (): SingleBoxShadowToken => {
|
||||
const theme = useTheme()
|
||||
const shadow = theme.popover_shadow
|
||||
return create_shadow_token(shadow, "popover_shadow")
|
||||
}
|
||||
|
||||
const modal_shadow_token = (): SingleBoxShadowToken => {
|
||||
const theme = useTheme()
|
||||
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 = (): ThemeTokens["syntax"] => {
|
||||
const syntax = editor().syntax
|
||||
|
||||
return syntax_highlight_style_color_tokens(syntax)
|
||||
}
|
||||
|
||||
export function theme_tokens(): ThemeTokens {
|
||||
const theme = useTheme()
|
||||
|
||||
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(),
|
||||
modal_shadow: modal_shadow_token(),
|
||||
players: players_token(),
|
||||
syntax: syntax_tokens(),
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import { SingleColorToken, TokenTypes } from "@tokens-studio/types"
|
||||
|
||||
export function colorToken(name: string, value: string, description?: string): SingleColorToken {
|
||||
export function color_token(
|
||||
name: string,
|
||||
value: string,
|
||||
description?: string
|
||||
): SingleColorToken {
|
||||
const token: SingleColorToken = {
|
||||
name,
|
||||
type: TokenTypes.COLOR,
|
||||
@@ -8,7 +12,8 @@ export function colorToken(name: string, value: string, description?: string): S
|
||||
description,
|
||||
}
|
||||
|
||||
if (!token.value || token.value === '') throw new Error("Color token must have a value")
|
||||
if (!token.value || token.value === "")
|
||||
throw new Error("Color token must have a value")
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user