mirror of https://github.com/openspug/spug
A 新增web终端自定义主题
parent
dfbe1bf426
commit
02261a7a6f
|
@ -2,6 +2,7 @@
|
||||||
# Copyright: (c) <spug.dev@gmail.com>
|
# Copyright: (c) <spug.dev@gmail.com>
|
||||||
# Released under the AGPL-3.0 License.
|
# Released under the AGPL-3.0 License.
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from apps.account.models import User
|
||||||
from libs import ModelMixin
|
from libs import ModelMixin
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
@ -40,3 +41,13 @@ class Setting(models.Model, ModelMixin):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = 'settings'
|
db_table = 'settings'
|
||||||
|
|
||||||
|
|
||||||
|
class UserSetting(models.Model, ModelMixin):
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
key = models.CharField(max_length=32)
|
||||||
|
value = models.TextField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'user_settings'
|
||||||
|
unique_together = ('user', 'key')
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
# Released under the AGPL-3.0 License.
|
# Released under the AGPL-3.0 License.
|
||||||
# from django.urls import path
|
# from django.urls import path
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from .views import *
|
from apps.setting.views import *
|
||||||
|
from apps.setting.user import UserSettingView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', SettingView.as_view()),
|
url(r'^$', SettingView.as_view()),
|
||||||
|
url(r'^user/$', UserSettingView.as_view()),
|
||||||
url(r'^ldap_test/$', ldap_test),
|
url(r'^ldap_test/$', ldap_test),
|
||||||
url(r'^email_test/$', email_test),
|
url(r'^email_test/$', email_test),
|
||||||
url(r'^mfa/$', MFAView.as_view()),
|
url(r'^mfa/$', MFAView.as_view()),
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||||
|
# Copyright: (c) <spug.dev@gmail.com>
|
||||||
|
# Released under the AGPL-3.0 License.
|
||||||
|
from django.views.generic import View
|
||||||
|
from libs import JsonParser, Argument, json_response
|
||||||
|
from apps.setting.models import UserSetting
|
||||||
|
|
||||||
|
|
||||||
|
class UserSettingView(View):
|
||||||
|
def get(self, request):
|
||||||
|
response = {}
|
||||||
|
for item in UserSetting.objects.filter(user=request.user):
|
||||||
|
response[item.key] = item.value
|
||||||
|
return json_response(response)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('key', help='参数错误'),
|
||||||
|
Argument('value', help='参数错误'),
|
||||||
|
).parse(request.body)
|
||||||
|
if error is None:
|
||||||
|
UserSetting.objects.update_or_create(
|
||||||
|
user=request.user,
|
||||||
|
key=form.key,
|
||||||
|
defaults={'value': form.value}
|
||||||
|
)
|
||||||
|
return self.get(request)
|
||||||
|
return json_response(error=error)
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||||
|
* Copyright (c) <spug.dev@gmail.com>
|
||||||
|
* Released under the AGPL-3.0 License.
|
||||||
|
*/
|
||||||
|
import { observable } from 'mobx';
|
||||||
|
import http from 'libs/http';
|
||||||
|
import themes from 'pages/ssh/themes';
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
isReady = false;
|
||||||
|
@observable terminal = {
|
||||||
|
fontSize: 16,
|
||||||
|
fontFamily: 'Courier',
|
||||||
|
theme: 'dark',
|
||||||
|
styles: themes['dark']
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleSettings = (res) => {
|
||||||
|
if (res.terminal) {
|
||||||
|
const terminal = JSON.parse(res.terminal)
|
||||||
|
terminal.styles = themes[terminal.theme]
|
||||||
|
this.terminal = terminal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchUserSettings = () => {
|
||||||
|
if (this.isReady) return
|
||||||
|
http.get('/api/setting/user/')
|
||||||
|
.then(res => {
|
||||||
|
this.isReady = true
|
||||||
|
this._handleSettings(res)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
updateUserSettings = (key, value) => {
|
||||||
|
return http.post('/api/setting/user/', {key, value})
|
||||||
|
.then(res => {
|
||||||
|
this.isReady = true
|
||||||
|
this._handleSettings(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Store()
|
|
@ -229,6 +229,7 @@ class FileManager extends React.Component {
|
||||||
<Input ref={ref => this.input2 = ref} size="small" className={styles.input}
|
<Input ref={ref => this.input2 = ref} size="small" className={styles.input}
|
||||||
suffix={<div style={{color: '#999', fontSize: 12}}>回车确认</div>}
|
suffix={<div style={{color: '#999', fontSize: 12}}>回车确认</div>}
|
||||||
value={this.state.inputPath} onChange={e => this.setState({inputPath: e.target.value})}
|
value={this.state.inputPath} onChange={e => this.setState({inputPath: e.target.value})}
|
||||||
|
onBlur={this.handleInputEnter}
|
||||||
onPressEnter={this.handleInputEnter}/>
|
onPressEnter={this.handleInputEnter}/>
|
||||||
) : (
|
) : (
|
||||||
<Breadcrumb className={styles.bread}>
|
<Breadcrumb className={styles.bread}>
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||||
|
* Copyright (c) <spug.dev@gmail.com>
|
||||||
|
* Released under the AGPL-3.0 License.
|
||||||
|
*/
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Drawer, Form, Button, Select, Space, message } from 'antd';
|
||||||
|
import themes from './themes';
|
||||||
|
import gStore from 'gStore';
|
||||||
|
import css from './setting.module.less'
|
||||||
|
|
||||||
|
function Setting(props) {
|
||||||
|
const [theme, setTheme] = useState('dark')
|
||||||
|
const [styles, setStyles] = useState(themes['dark'])
|
||||||
|
const [fontSize, setFontSize] = useState(14)
|
||||||
|
const [fontFamily, setFontFamily] = useState('Courier')
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const {theme, styles, fontSize, fontFamily} = gStore.terminal
|
||||||
|
setTheme(theme)
|
||||||
|
setStyles(styles)
|
||||||
|
setFontSize(fontSize)
|
||||||
|
setFontFamily(fontFamily)
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [gStore.terminal])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStyles(themes[theme])
|
||||||
|
}, [theme])
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
setLoading(true)
|
||||||
|
const data = {fontSize, fontFamily, theme}
|
||||||
|
gStore.updateUserSettings('terminal', JSON.stringify(data))
|
||||||
|
.then(() => {
|
||||||
|
message.success('已保存')
|
||||||
|
props.onClose()
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<Drawer
|
||||||
|
title="终端设置"
|
||||||
|
placement="right"
|
||||||
|
width={300}
|
||||||
|
visible={props.visible}
|
||||||
|
onClose={props.onClose}>
|
||||||
|
<Form layout="vertical">
|
||||||
|
<Form.Item label="字体大小">
|
||||||
|
<Select value={fontSize} placeholder="请选择字体大小" onChange={v => setFontSize(v)}>
|
||||||
|
<Select.Option value={12}>12</Select.Option>
|
||||||
|
<Select.Option value={14}>14</Select.Option>
|
||||||
|
<Select.Option value={16}>16</Select.Option>
|
||||||
|
<Select.Option value={18}>18</Select.Option>
|
||||||
|
<Select.Option value={20}>20</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="字体名称">
|
||||||
|
<Select value={fontFamily} placeholder="请选择字体" onChange={v => setFontFamily(v)}>
|
||||||
|
<Select.Option value="Courier">Courier</Select.Option>
|
||||||
|
<Select.Option value="Consolas">Consolas</Select.Option>
|
||||||
|
<Select.Option value="DejaVu Sans Mono">DejaVu Sans Mono</Select.Option>
|
||||||
|
<Select.Option value="Droid Sans Mono">Droid Sans Mono</Select.Option>
|
||||||
|
<Select.Option value="Monaco">Monaco</Select.Option>
|
||||||
|
<Select.Option value="Menlo">Menlo</Select.Option>
|
||||||
|
<Select.Option value="monospace">monospace</Select.Option>
|
||||||
|
<Select.Option value="Source Code Pro">Source Code Pro</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="主题配色">
|
||||||
|
<Space wrap className={css.theme} size={12}>
|
||||||
|
{Object.entries(themes).map(([key, item]) => (
|
||||||
|
<pre key={key} style={{background: item.background, color: item.foreground}}
|
||||||
|
onClick={() => setTheme(key)}>spug</pre>))}
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="预览">
|
||||||
|
<div className={css.preview}
|
||||||
|
style={{fontSize, fontFamily, background: styles.background, color: styles.foreground}}>
|
||||||
|
<div>Welcome to Spug !</div>
|
||||||
|
<div>* Website: https://spug.cc</div>
|
||||||
|
<div>[root@iZ8vb48roZ ~]# ls</div>
|
||||||
|
<div>
|
||||||
|
<span style={{color: styles.brightBlue}}>apps </span>
|
||||||
|
<span style={{color: styles.brightRed}}>bak.tar.gz </span>
|
||||||
|
<span style={{color: styles.brightGreen}}>manage.py </span>
|
||||||
|
<span>README.md</span>
|
||||||
|
</div>
|
||||||
|
<div>[root@iZ8vb48roZ ~]# pwd</div>
|
||||||
|
<div>/data/api</div>
|
||||||
|
<div>[root@iZ8vb48roZ ~]#</div>
|
||||||
|
</div>
|
||||||
|
</Form.Item>
|
||||||
|
<Button block type="primary" className={css.btn} loading={loading} onClick={handleSubmit}>保存</Button>
|
||||||
|
</Form>
|
||||||
|
</Drawer>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Setting
|
|
@ -9,7 +9,7 @@ import { FitAddon } from 'xterm-addon-fit';
|
||||||
import { X_TOKEN } from 'libs';
|
import { X_TOKEN } from 'libs';
|
||||||
import 'xterm/css/xterm.css';
|
import 'xterm/css/xterm.css';
|
||||||
import styles from './index.module.less';
|
import styles from './index.module.less';
|
||||||
|
import gStore from 'gStore';
|
||||||
|
|
||||||
function WebSSH(props) {
|
function WebSSH(props) {
|
||||||
const container = useRef();
|
const container = useRef();
|
||||||
|
@ -18,8 +18,9 @@ function WebSSH(props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
term.loadAddon(fitPlugin);
|
term.loadAddon(fitPlugin);
|
||||||
term.setOption('fontFamily', 'Source Code Pro, Courier New, Courier, Monaco, monospace, PingFang SC, Microsoft YaHei')
|
term.setOption('fontSize', gStore.terminal.fontSize)
|
||||||
term.setOption('theme', {background: '#2b2b2b', foreground: '#A9B7C6'})
|
term.setOption('fontFamily', gStore.terminal.fontFamily)
|
||||||
|
term.setOption('theme', gStore.terminal.styles)
|
||||||
term.attachCustomKeyEventHandler((arg) => {
|
term.attachCustomKeyEventHandler((arg) => {
|
||||||
if (arg.code === 'PageUp' && arg.type === 'keydown') {
|
if (arg.code === 'PageUp' && arg.type === 'keydown') {
|
||||||
term.scrollPages(-1)
|
term.scrollPages(-1)
|
||||||
|
@ -58,6 +59,14 @@ function WebSSH(props) {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
term.setOption('fontSize', gStore.terminal.fontSize)
|
||||||
|
term.setOption('fontFamily', gStore.terminal.fontFamily)
|
||||||
|
term.setOption('theme', gStore.terminal.styles)
|
||||||
|
fitTerminal()
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [gStore.terminal])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.vId === props.activeId) {
|
if (props.vId === props.activeId) {
|
||||||
setTimeout(() => term.focus())
|
setTimeout(() => term.focus())
|
||||||
|
@ -79,9 +88,7 @@ function WebSSH(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.termContainer}>
|
|
||||||
<div className={styles.terminal} ref={container}/>
|
<div className={styles.terminal} ref={container}/>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,14 @@ import {
|
||||||
VerticalAlignMiddleOutlined,
|
VerticalAlignMiddleOutlined,
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
LeftOutlined,
|
LeftOutlined,
|
||||||
|
SkinFilled,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { NotFound, AuthButton } from 'components';
|
import { NotFound, AuthButton } from 'components';
|
||||||
import Terminal from './Terminal';
|
import Terminal from './Terminal';
|
||||||
import FileManager from './FileManager';
|
import FileManager from './FileManager';
|
||||||
|
import Setting from './Setting';
|
||||||
import { http, hasPermission, includes } from 'libs';
|
import { http, hasPermission, includes } from 'libs';
|
||||||
|
import gStore from 'gStore';
|
||||||
import styles from './index.module.less';
|
import styles from './index.module.less';
|
||||||
import LogoSpugText from 'layout/logo-spug-white.png';
|
import LogoSpugText from 'layout/logo-spug-white.png';
|
||||||
import lds from 'lodash';
|
import lds from 'lodash';
|
||||||
|
@ -31,6 +34,7 @@ let posX = 0
|
||||||
|
|
||||||
function WebSSH(props) {
|
function WebSSH(props) {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [visible2, setVisible2] = useState(false);
|
||||||
const [fetching, setFetching] = useState(true);
|
const [fetching, setFetching] = useState(true);
|
||||||
const [rawTreeData, setRawTreeData] = useState([]);
|
const [rawTreeData, setRawTreeData] = useState([]);
|
||||||
const [rawHostList, setRawHostList] = useState([]);
|
const [rawHostList, setRawHostList] = useState([]);
|
||||||
|
@ -46,6 +50,7 @@ function WebSSH(props) {
|
||||||
window.document.title = 'Spug web terminal'
|
window.document.title = 'Spug web terminal'
|
||||||
window.addEventListener('beforeunload', leaveTips)
|
window.addEventListener('beforeunload', leaveTips)
|
||||||
fetchNodes()
|
fetchNodes()
|
||||||
|
gStore.fetchUserSettings()
|
||||||
return () => window.removeEventListener('beforeunload', leaveTips)
|
return () => window.removeEventListener('beforeunload', leaveTips)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -222,11 +227,11 @@ function WebSSH(props) {
|
||||||
<div className={styles.hosts}>
|
<div className={styles.hosts}>
|
||||||
<Spin spinning={fetching}>
|
<Spin spinning={fetching}>
|
||||||
<Input allowClear className={styles.search} prefix={<SearchOutlined style={{color: '#999'}}/>}
|
<Input allowClear className={styles.search} prefix={<SearchOutlined style={{color: '#999'}}/>}
|
||||||
placeholder="输入检索" onChange={e => setSearchValue(e.target.value)}/>
|
placeholder="输入主机名/IP检索" onChange={e => setSearchValue(e.target.value)}/>
|
||||||
<Button icon={<SyncOutlined/>} type="link" loading={fetching} onClick={fetchNodes}/>
|
<Button icon={<SyncOutlined/>} type="link" loading={fetching} onClick={fetchNodes}/>
|
||||||
{treeData.length > 0 ? (
|
{treeData.length > 0 ? (
|
||||||
<Tree.DirectoryTree
|
<Tree.DirectoryTree
|
||||||
defaultExpandAll={treeData.length > 0 && treeData < 5}
|
defaultExpandAll={treeData.length > 0 && treeData.length < 5}
|
||||||
expandAction="doubleClick"
|
expandAction="doubleClick"
|
||||||
treeData={treeData}
|
treeData={treeData}
|
||||||
icon={renderIcon}
|
icon={renderIcon}
|
||||||
|
@ -247,13 +252,15 @@ function WebSSH(props) {
|
||||||
tabBarExtraContent={hosts.length === 0 ? (
|
tabBarExtraContent={hosts.length === 0 ? (
|
||||||
<div className={styles.tips}>小提示:双击标签快速复制窗口,右击标签展开更多操作。</div>
|
<div className={styles.tips}>小提示:双击标签快速复制窗口,右击标签展开更多操作。</div>
|
||||||
) : sshMode ? (
|
) : sshMode ? (
|
||||||
|
<React.Fragment>
|
||||||
<AuthButton
|
<AuthButton
|
||||||
auth="host.console.list"
|
auth="host.console.list"
|
||||||
type="link"
|
type="link"
|
||||||
disabled={!activeId}
|
disabled={!activeId}
|
||||||
style={{marginRight: 5}}
|
|
||||||
onClick={handleOpenFileManager}
|
onClick={handleOpenFileManager}
|
||||||
icon={<LeftOutlined/>}>文件管理器</AuthButton>
|
icon={<LeftOutlined/>}>文件管理器</AuthButton>
|
||||||
|
<SkinFilled className={styles.setting} onClick={() => setVisible2(true)}/>
|
||||||
|
</React.Fragment>
|
||||||
) : null}>
|
) : null}>
|
||||||
{hosts.map(item => (
|
{hosts.map(item => (
|
||||||
<Tabs.TabPane key={item.vId} tab={<TabRender host={item}/>}>
|
<Tabs.TabPane key={item.vId} tab={<TabRender host={item}/>}>
|
||||||
|
@ -280,6 +287,7 @@ function WebSSH(props) {
|
||||||
onClose={() => setVisible(false)}>
|
onClose={() => setVisible(false)}>
|
||||||
<FileManager id={hostId}/>
|
<FileManager id={hostId}/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
<Setting visible={visible2} onClose={() => setVisible2(false)}/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{height: '100vh'}}>
|
<div style={{height: '100vh'}}>
|
||||||
|
|
|
@ -100,6 +100,13 @@
|
||||||
height: calc(100vh - 66px);
|
height: calc(100vh - 66px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.setting {
|
||||||
|
cursor: pointer;
|
||||||
|
padding-right: 6px;
|
||||||
|
margin-right: 6px;
|
||||||
|
color: #fa8c16;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.ant-tabs-nav) {
|
:global(.ant-tabs-nav) {
|
||||||
height: 42px;
|
height: 42px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -126,14 +133,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.termContainer {
|
.terminal {
|
||||||
margin: 12px;
|
margin: 12px;
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #2b2b2b;
|
|
||||||
padding: 10px 0 10px 10px;
|
|
||||||
|
|
||||||
.terminal {
|
:global(.xterm) {
|
||||||
height: calc(100vh - 84px);
|
padding: 10px 0 6px 10px;
|
||||||
|
height: calc(100vh - 66px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.xterm-viewport) {
|
||||||
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
.theme {
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #333333;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: Source Code Pro, Courier New, Courier, Monaco, monospace, PingFang SC, Microsoft YaHei;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
export default {
|
||||||
|
solarized_dark: {
|
||||||
|
foreground: '#839496', background: '#2b2b2b', cursor: '#839496',
|
||||||
|
|
||||||
|
black: '#1b1b1b', brightBlack: '#626262',
|
||||||
|
|
||||||
|
red: '#bb5653', brightRed: '#bb5653',
|
||||||
|
|
||||||
|
green: '#909d62', brightGreen: '#909d62',
|
||||||
|
|
||||||
|
yellow: '#eac179', brightYellow: '#eac179',
|
||||||
|
|
||||||
|
blue: '#7da9c7', brightBlue: '#7da9c7',
|
||||||
|
|
||||||
|
magenta: '#b06597', brightMagenta: '#b06597',
|
||||||
|
|
||||||
|
cyan: '#8cdcd8', brightCyan: '#8cdcd8',
|
||||||
|
|
||||||
|
white: '#d8d8d8', brightWhite: '#f7f7f7'
|
||||||
|
}, dark: {
|
||||||
|
foreground: '#c7c7c7', background: '#000000', cursor: '#c7c7c7',
|
||||||
|
|
||||||
|
black: '#000000', brightBlack: '#676767',
|
||||||
|
|
||||||
|
red: '#c91b00', brightRed: '#ff6d67',
|
||||||
|
|
||||||
|
green: '#00c200', brightGreen: '#5ff967',
|
||||||
|
|
||||||
|
yellow: '#c7c400', brightYellow: '#fefb67',
|
||||||
|
|
||||||
|
blue: '#0225c7', brightBlue: '#6871ff',
|
||||||
|
|
||||||
|
magenta: '#c930c7', brightMagenta: '#ff76ff',
|
||||||
|
|
||||||
|
cyan: '#00c5c7', brightCyan: '#5ffdff',
|
||||||
|
|
||||||
|
white: '#c7c7c7', brightWhite: '#fffefe'
|
||||||
|
}, ubuntu: {
|
||||||
|
foreground: '#f1f1ef', background: '#3f0e2f', cursor: '#c7c7c7',
|
||||||
|
|
||||||
|
black: '#3c4345', brightBlack: '#676965',
|
||||||
|
|
||||||
|
red: '#d71e00', brightRed: '#f44135',
|
||||||
|
|
||||||
|
green: '#5da602', brightGreen: '#98e342',
|
||||||
|
|
||||||
|
yellow: '#cfad00', brightYellow: '#fcea60',
|
||||||
|
|
||||||
|
blue: '#417ab3', brightBlue: '#83afd8',
|
||||||
|
|
||||||
|
magenta: '#88658d', brightMagenta: '#bc93b6',
|
||||||
|
|
||||||
|
cyan: '#00a7aa', brightCyan: '#37e5e7',
|
||||||
|
|
||||||
|
white: '#dbded8', brightWhite: '#f1f1ef'
|
||||||
|
}, light: {
|
||||||
|
foreground: '#000000', background: '#fffefe', cursor: '#000000',
|
||||||
|
|
||||||
|
black: '#000000', brightBlack: '#676767',
|
||||||
|
|
||||||
|
red: '#c91b00', brightRed: '#ff6d67',
|
||||||
|
|
||||||
|
green: '#00c200', brightGreen: '#5ff967',
|
||||||
|
|
||||||
|
yellow: '#c7c400', brightYellow: '#fefb67',
|
||||||
|
|
||||||
|
blue: '#0225c7', brightBlue: '#6871ff',
|
||||||
|
|
||||||
|
magenta: '#c930c7', brightMagenta: '#ff76ff',
|
||||||
|
|
||||||
|
cyan: '#00c5c7', brightCyan: '#5ffdff',
|
||||||
|
|
||||||
|
white: '#c7c7c7', brightWhite: '#fffefe'
|
||||||
|
}, solarized_light: {
|
||||||
|
foreground: '#657b83', background: '#fdf6e3', cursor: '#657b83',
|
||||||
|
|
||||||
|
black: '#073642', brightBlack: '#002b36',
|
||||||
|
|
||||||
|
red: '#dc322f', brightRed: '#cb4b16',
|
||||||
|
|
||||||
|
green: '#859900', brightGreen: '#586e75',
|
||||||
|
|
||||||
|
yellow: '#b58900', brightYellow: '#657b83',
|
||||||
|
|
||||||
|
blue: '#268bd2', brightBlue: '#839496',
|
||||||
|
|
||||||
|
magenta: '#d33682', brightMagenta: '#6c71c4',
|
||||||
|
|
||||||
|
cyan: '#2aa198', brightCyan: '#93a1a1',
|
||||||
|
|
||||||
|
white: '#eee8d5', brightWhite: '#fdf6e3'
|
||||||
|
}, material: {
|
||||||
|
foreground: '#2e2d2c', background: '#eeeeee', cursor: '#2e2d2c',
|
||||||
|
|
||||||
|
black: '#2c2c2c', brightBlack: '#535353',
|
||||||
|
|
||||||
|
red: '#c52728', brightRed: '#ee524f',
|
||||||
|
|
||||||
|
green: '#558a2f', brightGreen: '#8bc24a',
|
||||||
|
|
||||||
|
yellow: '#f8a725', brightYellow: '#ffea3b',
|
||||||
|
|
||||||
|
blue: '#1564bf', brightBlue: '#64b4f5',
|
||||||
|
|
||||||
|
magenta: '#691e99', brightMagenta: '#b967c7',
|
||||||
|
|
||||||
|
cyan: '#00828e', brightCyan: '#26c5d9',
|
||||||
|
|
||||||
|
white: '#f2f1f1', brightWhite: '#e0dfdf'
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in New Issue