mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-01-23 02:35:49 +00:00
feature: dark theme: add (#332)
This commit is contained in:
parent
35622082a9
commit
6bc4f3ec26
23 changed files with 186 additions and 51 deletions
|
|
@ -1,5 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const {env} = require('node:process');
|
||||
const fs = require('node:fs');
|
||||
const {
|
||||
basename,
|
||||
|
|
@ -10,7 +11,6 @@ const {
|
|||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
|
||||
const {env} = require('node:process');
|
||||
const isDev = env.NODE_ENV === 'development';
|
||||
|
||||
const extractCSS = (a) => new ExtractTextPlugin(`${a}.css`);
|
||||
|
|
@ -23,6 +23,7 @@ const cssNames = [
|
|||
'terminal',
|
||||
'user-menu',
|
||||
...getCSSList('columns'),
|
||||
...getCSSList('themes'),
|
||||
];
|
||||
|
||||
const cssPlugins = cssNames.map(extractCSS);
|
||||
|
|
@ -36,7 +37,7 @@ const plugins = clean([
|
|||
|
||||
const rules = [{
|
||||
test: /\.css$/,
|
||||
exclude: /css\/(nojs|view|config|terminal|user-menu|columns.*)\.css/,
|
||||
exclude: /css\/(nojs|view|config|terminal|user-menu|columns.*|themes.*)\.css/,
|
||||
use: extractMain.extract(['css-loader']),
|
||||
}, ...cssPlugins.map(extract), {
|
||||
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const {env} = require('node:process');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const isDev = env.NODE_ENV === 'development';
|
||||
|
||||
|
|
|
|||
8
HELP.md
8
HELP.md
|
|
@ -93,7 +93,7 @@ Cloud Commander supports the following command-line parameters:
|
|||
| `--terminal-command` | set command to run in terminal (shell by default)
|
||||
| `--terminal-auto-restart` | restart command on exit
|
||||
| `--vim` | enable vim hot keys
|
||||
| `--columns` | set visible columns
|
||||
| `--themes` | set visible themes
|
||||
| `--export` | enable export of config through a server
|
||||
| `--export-token` | authorization token used by export server
|
||||
| `--import` | enable import of config
|
||||
|
|
@ -122,7 +122,7 @@ Cloud Commander supports the following command-line parameters:
|
|||
| `--no-terminal-command` | set default shell to run in terminal
|
||||
| `--no-terminal-auto-restart` | do not restart command on exit
|
||||
| `--no-vim` | disable vim hot keys
|
||||
| `--no-columns` | set default visible columns
|
||||
| `--no-themes` | set default visible themes
|
||||
| `--no-export` | disable export config through a server
|
||||
| `--no-import` | disable import of config
|
||||
| `--no-import-listen` | disable listen on config updates from import server
|
||||
|
|
@ -408,7 +408,7 @@ Here's a description of all options:
|
|||
"terminalCommand": "", // set command to run in terminal
|
||||
"terminalAutoRestart": true, // restart command on exit
|
||||
"vim": false, // disable vim hot keys
|
||||
"columns": "name-size-date-owner-mode", // set visible columns
|
||||
"themes": "name-size-date-owner-mode", // set visible themes
|
||||
"export": false, // enable export of config through a server
|
||||
"exportToken": "root", // token used by export server
|
||||
"import": false, // enable import of config
|
||||
|
|
@ -428,7 +428,7 @@ Some config options can be overridden with environment variables, such as:
|
|||
- `CLOUDCMD_NAME` - set tab name in web browser
|
||||
- `CLOUDCMD_OPEN` - open web browser when server started
|
||||
- `CLOUDCMD_EDITOR` - set editor
|
||||
- `CLOUDCMD_COLUMNS` - set visible columns
|
||||
- `CLOUDCMD_COLUMNS` - set visible themes
|
||||
- `CLOUDCMD_CONTACT` - enable contact
|
||||
- `CLOUDCMD_CONFIG_DIALOG` - enable config dialog
|
||||
- `CLOUDCMD_CONFIG_AUTH` - enable auth change in config dialog
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import process from 'node:process';
|
||||
import {createRequire} from 'node:module';
|
||||
import {promisify} from 'node:util';
|
||||
import tryToCatch from 'try-to-catch';
|
||||
import {createSimport} from 'simport';
|
||||
import parse from 'yargs-parser';
|
||||
import process from 'node:process';
|
||||
import exit from '../server/exit.js';
|
||||
import {createConfig, configPath} from '../server/config.js';
|
||||
import env from '../server/env.js';
|
||||
import prefixer from '../server/prefixer.js';
|
||||
import * as validate from '../server/validate.mjs';
|
||||
|
||||
process.on('unhandledRejection', exit);
|
||||
|
||||
|
|
@ -61,6 +62,7 @@ const yargsOptions = {
|
|||
'terminal-path',
|
||||
'terminal-command',
|
||||
'columns',
|
||||
'theme',
|
||||
'import-url',
|
||||
'import-token',
|
||||
'export-token',
|
||||
|
|
@ -112,6 +114,7 @@ const yargsOptions = {
|
|||
'contact': choose(env.bool('contact'), config('contact')),
|
||||
'terminal': choose(env.bool('terminal'), config('terminal')),
|
||||
'columns': env('columns') || config('columns') || '',
|
||||
'theme': env('theme') || config('theme') || '',
|
||||
'vim': choose(env.bool('vim'), config('vim')),
|
||||
'log': config('log'),
|
||||
|
||||
|
|
@ -174,6 +177,9 @@ async function main() {
|
|||
if (args.repl)
|
||||
repl();
|
||||
|
||||
validate.columns(args.columns);
|
||||
validate.theme(args.theme);
|
||||
|
||||
port(args.port);
|
||||
|
||||
config('name', args.name);
|
||||
|
|
@ -194,6 +200,7 @@ async function main() {
|
|||
config('prefixSocket', prefixer(args.prefixSocket));
|
||||
config('root', args.root || '/');
|
||||
config('vim', args.vim);
|
||||
config('theme', args.theme);
|
||||
config('columns', args.columns);
|
||||
config('log', args.log);
|
||||
config('confirmCopy', args.confirmCopy);
|
||||
|
|
@ -221,6 +228,7 @@ async function main() {
|
|||
prefix: config('prefix'),
|
||||
prefixSocket: config('prefixSocket'),
|
||||
columns: config('columns'),
|
||||
theme: config('theme'),
|
||||
};
|
||||
|
||||
const password = env('password') || args.password;
|
||||
|
|
@ -228,7 +236,7 @@ async function main() {
|
|||
if (password)
|
||||
config('password', await getPassword(password));
|
||||
|
||||
await validateRoot(options.root, config);
|
||||
validateRoot(options.root, config);
|
||||
|
||||
if (args.showConfig)
|
||||
await showConfig();
|
||||
|
|
@ -245,8 +253,7 @@ async function main() {
|
|||
await importConfig(config);
|
||||
}
|
||||
|
||||
async function validateRoot(root, config) {
|
||||
const validate = await simport(`${DIR_SERVER}validate.js`);
|
||||
function validateRoot(root, config) {
|
||||
validate.root(root, config);
|
||||
|
||||
if (root === '/')
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const process = require('node:process');
|
||||
require('../css/main.css');
|
||||
require('../css/nojs.css');
|
||||
require('../css/columns/name-size-date.css');
|
||||
require('../css/columns/name-size.css');
|
||||
require('./css');
|
||||
|
||||
const wraptile = require('wraptile');
|
||||
const load = require('load.js');
|
||||
|
|
|
|||
8
client/css.js
Normal file
8
client/css.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
require('../css/main.css');
|
||||
require('../css/nojs.css');
|
||||
require('../css/columns/name-size-date.css');
|
||||
require('../css/columns/name-size.css');
|
||||
require('../css/themes/light.css');
|
||||
require('../css/themes/dark.css');
|
||||
|
|
@ -137,6 +137,7 @@ async function fillTemplate() {
|
|||
editor,
|
||||
packer,
|
||||
columns,
|
||||
theme,
|
||||
configAuth,
|
||||
...obj
|
||||
} = input.convert(config);
|
||||
|
|
@ -144,6 +145,7 @@ async function fillTemplate() {
|
|||
obj[`${editor}-selected`] = 'selected';
|
||||
obj[`${packer}-selected`] = 'selected';
|
||||
obj[`${columns}-selected`] = 'selected';
|
||||
obj[`${theme}-selected`] = 'selected';
|
||||
obj.configAuth = configAuth ? '' : 'hidden';
|
||||
|
||||
const innerHTML = rendy(Template, obj);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
/* @import url(./themes/dark.css); */
|
||||
@import url(./themes/light.css);
|
||||
@import url(./urls.css);
|
||||
@import url(./reset.css);
|
||||
@import url(./style.css);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ code {
|
|||
-ms-user-select: initial;
|
||||
-o-user-select: initial;
|
||||
user-select: text;
|
||||
color: var(--column-color);
|
||||
}
|
||||
|
||||
.panel,
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
:root {
|
||||
--text-color: rgb(49 123 249);
|
||||
--border-color: rgb(49 123 249 / 40%);
|
||||
}
|
||||
|
|
@ -15,6 +15,9 @@
|
|||
<style data-name="columns">
|
||||
{{ columns }}
|
||||
</style>
|
||||
<style data-name="themes">
|
||||
{{ themes }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="fm">{{ fm }}</div>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
"showFileName": false,
|
||||
"vim": false,
|
||||
"columns": "name-size-date-owner-mode",
|
||||
"theme": "light",
|
||||
"export": false,
|
||||
"exportToken": "root",
|
||||
"import": false,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
"--terminal-auto-restart ": "restart command on exit",
|
||||
"--vim ": "enable vim hot keys",
|
||||
"--columns ": "set visible columns",
|
||||
"--theme ": "set theme 'light' or 'dark'",
|
||||
"--export ": "enable export of config through a server",
|
||||
"--export-token ": "authorization token used by export server",
|
||||
"--import ": "enable import of config",
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ programs in browser from any computer, mobile or tablet device.
|
|||
--terminal-auto-restart restart command on exit
|
||||
--vim enable vim hot keys
|
||||
--columns set visible columns
|
||||
--theme set theme 'light' or 'dark'
|
||||
--export enable export of config through a server
|
||||
--export-token authorization token used by export server
|
||||
--import enable import of config
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import modulas from './modulas.js';
|
|||
import userMenu from './user-menu.mjs';
|
||||
import rest from './rest/index.js';
|
||||
import route from './route.mjs';
|
||||
import validate from './validate.js';
|
||||
import * as validate from './validate.mjs';
|
||||
import prefixer from './prefixer.js';
|
||||
import terminal from './terminal.js';
|
||||
import distribute from './distribute/index.js';
|
||||
|
|
@ -64,7 +64,7 @@ function cloudcmd(params) {
|
|||
if (/root/.test(name))
|
||||
validate.root(value, config);
|
||||
|
||||
if (/editor|packer|columns/.test(name))
|
||||
if (/editor|packer|themes/.test(name))
|
||||
validate[name](value);
|
||||
|
||||
if (/prefix/.test(name))
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
const fullstore = require('fullstore');
|
||||
const process = require('node:process');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const {nanomemoize} = require('nano-memoize');
|
||||
const readFilesSync = require('@cloudcmd/read-files-sync');
|
||||
import path, {dirname} from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import process from 'node:process';
|
||||
import fs from 'node:fs';
|
||||
import fullstore from 'fullstore';
|
||||
import nanomemoizeDefault from 'nano-memoize';
|
||||
import readFilesSync from '@cloudcmd/read-files-sync';
|
||||
|
||||
const {nanomemoize} = nanomemoizeDefault;
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const isMap = (a) => /\.map$/.test(a);
|
||||
const not = (fn) => (a) => !fn(a);
|
||||
|
||||
|
|
@ -19,9 +20,9 @@ const defaultColumns = {
|
|||
const _isDev = fullstore(process.env.NODE_ENV === 'development');
|
||||
const getDist = (isDev) => isDev ? 'dist-dev' : 'dist';
|
||||
|
||||
module.exports.isDev = _isDev;
|
||||
export const isDev = _isDev;
|
||||
|
||||
module.exports.getColumns = ({isDev = _isDev()} = {}) => {
|
||||
export const getColumns = ({isDev = _isDev()} = {}) => {
|
||||
const columns = readFilesSyncMemo(isDev);
|
||||
|
||||
return {
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
'use strict';
|
||||
import {dirname} from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import test from 'supertape';
|
||||
import fs from 'node:fs';
|
||||
import {getColumns, isDev} from './columns.mjs';
|
||||
|
||||
const test = require('supertape');
|
||||
const fs = require('node:fs');
|
||||
const {getColumns, isDev} = require('./columns');
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
test('columns: prod', (t) => {
|
||||
const columns = getColumns({
|
||||
|
|
@ -13,8 +13,9 @@ import {contentType} from 'mime-types';
|
|||
import root from './root.js';
|
||||
import prefixer from './prefixer.js';
|
||||
import CloudFunc from '../common/cloudfunc.js';
|
||||
import {getColumns} from './columns.js';
|
||||
import Template from './template.js';
|
||||
import {getColumns} from './columns.mjs';
|
||||
import {getThemes} from './theme.mjs';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const {stringify} = JSON;
|
||||
|
|
@ -164,6 +165,7 @@ function indexProcessing(config, options) {
|
|||
prefix: getPrefix(config),
|
||||
config: stringify(config('*')),
|
||||
columns: getColumns()[config('columns')],
|
||||
themes: getThemes()[config('theme')],
|
||||
});
|
||||
|
||||
return data;
|
||||
|
|
|
|||
33
server/theme.mjs
Normal file
33
server/theme.mjs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import path, {dirname} from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import process from 'node:process';
|
||||
import fs from 'node:fs';
|
||||
import fullstore from 'fullstore';
|
||||
import nanomemoizeDefault from 'nano-memoize';
|
||||
import readFilesSync from '@cloudcmd/read-files-sync';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const isMap = (a) => /\.map$/.test(a);
|
||||
const not = (fn) => (a) => !fn(a);
|
||||
|
||||
const _isDev = fullstore(process.env.NODE_ENV === 'development');
|
||||
const getDist = (isDev) => isDev ? 'dist-dev' : 'dist';
|
||||
|
||||
export const isDev = _isDev;
|
||||
|
||||
export const getThemes = ({isDev = _isDev()} = {}) => {
|
||||
return readFilesSyncMemo(isDev);
|
||||
};
|
||||
|
||||
const {nanomemoize} = nanomemoizeDefault;
|
||||
|
||||
const readFilesSyncMemo = nanomemoize((isDev) => {
|
||||
const dist = getDist(isDev);
|
||||
const themesDir = path.join(__dirname, '..', dist, 'themes');
|
||||
const names = fs
|
||||
.readdirSync(themesDir)
|
||||
.filter(not(isMap));
|
||||
|
||||
return readFilesSync(themesDir, names, 'utf8');
|
||||
});
|
||||
31
server/themes.spec.mjs
Normal file
31
server/themes.spec.mjs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {dirname} from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import test from 'supertape';
|
||||
import fs from 'node:fs';
|
||||
import {getThemes, isDev} from './theme.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
test('themes: dev', (t) => {
|
||||
const themes = getThemes({
|
||||
isDev: true,
|
||||
});
|
||||
|
||||
const css = fs.readFileSync(`${__dirname}/../css/themes/dark.css`, 'utf8');
|
||||
|
||||
t.equal(themes.dark, css);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('themes: no args', (t) => {
|
||||
const currentIsDev = isDev();
|
||||
isDev(true);
|
||||
const themes = getThemes();
|
||||
|
||||
const css = fs.readFileSync(`${__dirname}/../css/themes/light.css`, 'utf8');
|
||||
isDev(currentIsDev);
|
||||
|
||||
t.equal(themes.light, css);
|
||||
t.end();
|
||||
});
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
'use strict';
|
||||
import {statSync as _statSync} from 'node:fs';
|
||||
import tryCatch from 'try-catch';
|
||||
import _exit from './exit.js';
|
||||
import {getColumns as _getColumns} from './columns.mjs';
|
||||
import {getThemes as _getThemes} from './theme.mjs';
|
||||
|
||||
const {statSync: _statSync} = require('node:fs');
|
||||
const tryCatch = require('try-catch');
|
||||
|
||||
const _exit = require('./exit');
|
||||
const {getColumns: _getColumns} = require('./columns');
|
||||
const isString = (a) => typeof a === 'string';
|
||||
|
||||
module.exports.root = (dir, config, {exit = _exit, statSync = _statSync} = {}) => {
|
||||
export const root = (dir, config, {exit = _exit, statSync = _statSync} = {}) => {
|
||||
if (!isString(dir))
|
||||
throw Error('dir should be a string');
|
||||
|
||||
|
|
@ -23,21 +22,21 @@ module.exports.root = (dir, config, {exit = _exit, statSync = _statSync} = {}) =
|
|||
return exit('cloudcmd --root: %s', error.message);
|
||||
};
|
||||
|
||||
module.exports.editor = (name, {exit = _exit} = {}) => {
|
||||
export const editor = (name, {exit = _exit} = {}) => {
|
||||
const reg = /^(dword|edward|deepword)$/;
|
||||
|
||||
if (!reg.test(name))
|
||||
exit('cloudcmd --editor: could be "dword", "edward" or "deepword" only');
|
||||
};
|
||||
|
||||
module.exports.packer = (name, {exit = _exit} = {}) => {
|
||||
export const packer = (name, {exit = _exit} = {}) => {
|
||||
const reg = /^(tar|zip)$/;
|
||||
|
||||
if (!reg.test(name))
|
||||
exit('cloudcmd --packer: could be "tar" or "zip" only');
|
||||
};
|
||||
|
||||
module.exports.columns = (type, {exit = _exit, getColumns = _getColumns} = {}) => {
|
||||
export const columns = (type, {exit = _exit, getColumns = _getColumns} = {}) => {
|
||||
const addQuotes = (a) => `"${a}"`;
|
||||
const all = Object
|
||||
.keys(getColumns())
|
||||
|
|
@ -51,3 +50,18 @@ module.exports.columns = (type, {exit = _exit, getColumns = _getColumns} = {}) =
|
|||
if (!all.includes(type))
|
||||
exit(`cloudcmd --columns: can be only one of: ${names}`);
|
||||
};
|
||||
|
||||
export const theme = (type, {exit = _exit, getThemes = _getThemes} = {}) => {
|
||||
const addQuotes = (a) => `"${a}"`;
|
||||
const all = Object
|
||||
.keys(getThemes())
|
||||
.concat('');
|
||||
|
||||
const names = all
|
||||
.filter(Boolean)
|
||||
.map(addQuotes)
|
||||
.join(', ');
|
||||
|
||||
if (!all.includes(type))
|
||||
exit(`cloudcmd --theme: can be only one of: ${names}`);
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import {test, stub} from 'supertape';
|
||||
import tryCatch from 'try-catch';
|
||||
import validate from './validate.js';
|
||||
import * as validate from './validate.mjs';
|
||||
import cloudcmd from './cloudcmd.mjs';
|
||||
|
||||
test('validate: root: bad', (t) => {
|
||||
|
|
@ -102,3 +102,32 @@ test('validate: columns: wrong', (t) => {
|
|||
t.calledWith(exit, [msg], 'should call exit');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('validate: theme', (t) => {
|
||||
const exit = stub();
|
||||
|
||||
validate.theme('dark', {
|
||||
exit,
|
||||
});
|
||||
|
||||
t.notCalled(exit, 'should not call exit');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('validate: theme: wrong', (t) => {
|
||||
const getThemes = stub().returns({
|
||||
light: '',
|
||||
dark: '',
|
||||
});
|
||||
|
||||
const exit = stub();
|
||||
const msg = 'cloudcmd --theme: can be only one of: "light", "dark"';
|
||||
|
||||
validate.theme('hello', {
|
||||
exit,
|
||||
getThemes,
|
||||
});
|
||||
|
||||
t.calledWith(exit, [msg], 'should call exit');
|
||||
t.end();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,8 +43,14 @@
|
|||
Zip
|
||||
</label>
|
||||
</li>
|
||||
<li title="Theme">
|
||||
<select data-name="js-theme" class="form-control full-width" title="Theme">
|
||||
<option {{ light-selected }}>light</option>
|
||||
<option {{ dark-selected }}>dark</option>
|
||||
</select>
|
||||
</li>
|
||||
<li title="Visible Columns">
|
||||
<select data-name="js-columns" class="form-control full-width" title="Visible Columns">
|
||||
<select data-name="js-themes" class="form-control full-width" title="Visible Columns">
|
||||
<option {{ name-size-date-owner-mode-selected }}>name-size-date-owner-mode</option>
|
||||
<option {{ name-size-date-selected }}>name-size-date</option>
|
||||
<option {{ name-size-selected }}>name-size</option>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue