import { IRawThemeSetting } from 'vscode-textmate'; import { Deferred, Event, IDisposable, IThemeColor, MaybePromise, URI } from '@opensumi/ide-core-common'; import { Color } from './color'; import { hc_black, hc_light, vs, vs_dark } from './default-themes'; import { IconContribution, IconDefinition } from './icon-registry'; export const ThemeServicePath = 'themeServicePath'; export const DEFAULT_THEME_ID = 'ide-dark'; export const DEFAULT_PRODUCT_ICON_THEME_ID = 'opensumi-icons'; export const DEFAULT_PRODUCT_ICON_THEME_LABEL = 'OpenSumi Icons'; export const PRODUCT_ICON_STYLE_ID = 'product-icon-style'; // codiconStyles 为 monaco 内置样式表 export const PRODUCT_ICON_CODICON_STYLE_ID = 'codiconStyles'; // from vscode export const colorIdPattern = '^\\w+[.\\w+]*$'; export const IIconService = Symbol('IIconTheme'); export const IThemeService = Symbol('IThemeService'); export const IProductIconService = Symbol('IProductIconService'); export interface IIconTheme { hasFileIcons: boolean; hasFolderIcons: boolean; hidesExplorerArrows: boolean; styleSheetContent: string; load(location?: URI): Promise; } export interface IProductIconTheme { /** * Resolves the definition for the given icon as defined by the theme. * * @param iconContribution The icon */ readonly id: string; readonly label: string; readonly extensionData?: ExtensionData; readonly description?: string; readonly settingsId: string | null; styleSheetContent?: string; getIcon(iconContribution: IconContribution): IconDefinition | undefined; } export interface ExtensionData { extensionId: string; extensionPublisher: string; extensionName: string; extensionIsBuiltin: boolean; } export enum IconType { Mask = 'mask', Background = 'background', Base64 = 'base64', } export enum IconShape { Circle, Square, } export interface IIconService { currentThemeId: string; currentTheme: IIconTheme; iconThemeLoaded: Deferred; onThemeChange: Event; /** * 应用主题(外部需要改主题请直接修改preference) * @param themeId 主题ID * */ applyTheme(themeId: string): Promise; /** * 将 Base64 路径进行转义,便于在 `background: url("${iconPath}")` 结构中使用 * @param iconPath Base64 路径 */ encodeBase64Path(iconPath: string): string; /** * 将 codicon 的 id 转换为 codicon 的 class * @param str codicon id eg. $(add), $(add~sync) */ fromString(str: string): string | undefined; /** * 将一个url地址或插件主题url转换为icon的class * @param basePath 路径前缀 * @param icon iconUrl地址,可以是直接的字符串,或者和主题类型有关的 object 字符串对象 * @param type 选择采用Mask或者Background的方式渲染icon资源, 默认值为IconType.Mask * @returns icon的className */ fromIcon( basePath: string, icon?: { [index in IconThemeType]: string } | string, type?: IconType, shape?: IconShape, fromExtension?: boolean, ): string | undefined; registerIconThemes(iconThemesContribution: IThemeContribution[], extPath: URI): void; getAvailableThemeInfos(): IconThemeInfo[]; } export const IThemeData = Symbol('IThemeData'); export interface IThemeData extends IStandaloneThemeData { name: string; id: string; colorMap: IColorMap; themeSettings: IRawThemeSetting[]; settings: IRawThemeSetting[]; initializeFromData(data): void; initializeThemeData(id, name, base, themeLocation: URI): Promise; loadCustomTokens(customTokenColors: ITokenColorizationRule[]): unknown; } export const IThemeStore = Symbol('IThemeStore'); export interface IThemeStore { getThemeData(contribution?: IThemeContribution, basePath?: URI): Promise; getDefaultThemeID(): string; } export interface IThemeService { currentThemeId: string; colorThemeLoaded: Deferred; onThemeChange: Event; registerThemes(themeContributions: IThemeContribution[], extPath: URI): IDisposable; ensureValidTheme(defaultThemeId?: string): Promise; /** * 应用主题(外部需要改主题请直接修改preference) * @param id 主题ID */ applyTheme(id: string): Promise; getAvailableThemeInfos(): ThemeInfo[]; getCurrentTheme(): Promise; getCurrentThemeSync(): ITheme; getColor(id: string | IThemeColor | undefined): string | undefined; getColorVar(id: string | IThemeColor | undefined): string | undefined; /** * 获取指定 color token 的 className */ getColorClassNameByColorToken(colorId: string | IThemeColor | undefined): string | undefined; registerColor(contribution: ExtColorContribution): void; } export interface IProductIconService { currentThemeId: string; currentTheme: IProductIconTheme; productIconThemeLoaded: Deferred; updateProductIconThemes(): MaybePromise; onDidProductIconThemeChange: Event; applyTheme(themeId: string): Promise; registerProductIconThemes(productIconThemesContribution: IThemeContribution[], extPath: URI): void; getAvailableThemeInfos(): IconThemeInfo[]; } export interface ITokenColorizationRule { name?: string; scope?: string | string[]; settings: ITokenColorizationSetting; } export interface ITokenColorizationSetting { foreground?: string; background?: string; fontStyle?: string; // italic, underline, bold } export interface ISemanticTokenColorizationSetting { foreground?: string; fontStyle?: string /* [italic|underline|bold] */; bold?: boolean; underline?: boolean; italic?: boolean; } export interface IColorMap { [id: string]: Color; } /** * Color scheme used by the OS and by color themes. */ export enum ColorScheme { DARK = 'dark', LIGHT = 'light', HIGH_CONTRAST_DARK = 'hcDark', HIGH_CONTRAST_LIGHT = 'hcLight', } export type BuiltinTheme = 'vs' | 'vs-dark' | 'hc-black' | 'hc-light'; export function getThemeTypeName(base: BuiltinTheme) { const map = { vs: 'theme.base.vs', 'vs-dark': 'theme.base.vs-dark', 'hc-black': 'theme.base.hc', 'hc-light': 'theme.base.hc', }; return map[base]; } export enum BuiltinThemeComparator { 'vs', 'vs-dark', 'hc-black', 'hc-light', } export interface IStandaloneThemeData { base: BuiltinTheme; inherit: boolean; rules: ITokenThemeRule[]; encodedTokensColors?: string[]; colors: IColors; } export interface IColors { [colorId: string]: string; } export interface ITokenThemeRule { token: string; foreground?: string; background?: string; fontStyle?: string; } export interface ITokenColorCustomizations { [groupIdOrThemeSettingsId: string]: | string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITokenColorizationRule[]; comments?: string | ITokenColorizationSetting; strings?: string | ITokenColorizationSetting; numbers?: string | ITokenColorizationSetting; keywords?: string | ITokenColorizationSetting; types?: string | ITokenColorizationSetting; functions?: string | ITokenColorizationSetting; variables?: string | ITokenColorizationSetting; textMateRules?: ITokenColorizationRule[]; } export const VS_LIGHT_THEME_NAME = 'vs'; export const VS_DARK_THEME_NAME = 'vs-dark'; export const HC_BLACK_THEME_NAME = 'hc-black'; export const HC_LIGHT_THEME_NAME = 'hc-light'; export interface IThemeContribution { id?: string; label: string; description?: string; // default to be vs uiTheme?: BuiltinTheme; path: string; extensionId: string; } // base themes export const DARK: ThemeType = 'dark'; export const LIGHT: ThemeType = 'light'; export const HIGH_CONTRAST_DARK: ThemeType = 'hcDark'; export const HIGH_CONTRAST_LIGHT: ThemeType = 'hcLight'; export type ThemeType = 'light' | 'dark' | 'hcDark' | 'hcLight'; export type IconThemeType = 'light' | 'dark'; export function getBuiltinRules(builtinTheme: BuiltinTheme): IStandaloneThemeData { switch (builtinTheme) { case VS_LIGHT_THEME_NAME: return vs; case VS_DARK_THEME_NAME: return vs_dark; case HC_BLACK_THEME_NAME: return hc_black; case HC_LIGHT_THEME_NAME: return hc_light; } } export function getThemeTypeSelector(type: ThemeType): string { switch (type) { case DARK: return 'vs-dark'; case HIGH_CONTRAST_DARK: return 'hc-black'; case HIGH_CONTRAST_LIGHT: return 'hc-light'; default: return 'vs'; } } export function getThemeType(base: BuiltinTheme): ThemeType { switch (base) { case VS_LIGHT_THEME_NAME: return LIGHT; case VS_DARK_THEME_NAME: return DARK; case HC_BLACK_THEME_NAME: return HIGH_CONTRAST_DARK; case HC_LIGHT_THEME_NAME: return HIGH_CONTRAST_LIGHT; } } export type ColorIdentifier = string; export interface ITheme { readonly type: ThemeType; readonly themeData: IThemeData; /** * Resolves the color of the given color identifier. If the theme does not * specify the color, the default color is returned unless useDefault is set to false. * @param color the id of the color * @param useDefault specifies if the default color should be used. If not set, the default is used. */ getColor(color: ColorIdentifier, useDefault?: boolean): Color | undefined; /** * Returns whether the theme defines a value for the color. If not, that means the * default color will be used. */ defines(color: ColorIdentifier): boolean; } export interface IColorCustomizations { [colorIdOrThemeSettingsId: string]: string | IColorCustomizations; } export interface ColorContribution { readonly id: ColorIdentifier; readonly description: string; readonly defaults: ColorDefaults | null; readonly needsTransparency: boolean; readonly deprecationMessage: string | undefined; } export interface ExtColorContribution { id: string; description: string; defaults: { light: string; dark: string; highContrast: string; highContrastLight?: string }; } export type ColorFunction = (theme: ITheme) => Color | undefined; export interface ColorDefaults { light: ColorValue | null; dark: ColorValue | null; hcDark: ColorValue | null; hcLight: ColorValue | null; } /** * A Color Value is either a color literal, a reference to other color or a derived color */ export type ColorValue = Color | string | ColorIdentifier | ColorFunction; export interface ThemeInfo { name: string; base: BuiltinTheme; themeId: string; extensionId: string; inherit?: boolean; } export interface IconThemeInfo { name: string; themeId: string; extensionId: string; } export function themeColorFromId(id: ColorIdentifier) { return { id }; } export function getThemeId(contribution: IThemeContribution) { if (contribution.id) { return contribution.id; } return `${contribution.uiTheme || 'vs-dark'} ${toCSSSelector('vscode-theme', contribution.path)}`; } function toCSSSelector(extensionId: string, path: string) { if (path.indexOf('./') === 0) { path = path.substring(2); } let str = `${extensionId}-${path}`; // remove all characters that are not allowed in css str = str.replace(/[^_\-a-zA-Z0-9]/g, '-'); if (str.charAt(0).match(/[0-9\-]/)) { str = '_' + str; } return str; }