Compare commits

..

No commits in common. "master" and "v19.1.12" have entirely different histories.

219 changed files with 2047 additions and 4292 deletions

View file

@ -1,31 +1,45 @@
export default { 'use strict';
'F2 - Rename File': renameCurrent,
'L - Lint': run('npm run lint'), module.exports = {
'F - Fix Lint': run('npm run fix:lint'), 'F2 - Rename file': async ({DOM}) => {
'T - Test': run('npm run test'), await DOM.renameCurrent();
'C - Coverage': run('npm run coverage'), },
'D - Build Dev': run('npm run build:client:dev'), 'L - Lint': async ({CloudCmd}) => {
'P - Build Prod': run('npm run build:client'), const {TerminalRun} = CloudCmd;
await run(TerminalRun, 'npm run lint');
},
'F - Fix Lint': async ({CloudCmd}) => {
const {TerminalRun} = CloudCmd;
await run(TerminalRun, 'npm run fix:lint');
},
'T - Test': async ({CloudCmd}) => {
const {TerminalRun} = CloudCmd;
await run(TerminalRun, 'npm run test');
},
'C - Coverage': async ({CloudCmd}) => {
const {TerminalRun} = CloudCmd;
await run(TerminalRun, 'npm run coverage');
},
'D - Build Dev': async ({CloudCmd}) => {
const {TerminalRun} = CloudCmd;
await run(TerminalRun, 'npm run build:client:dev');
CloudCmd.refresh();
},
'P - Build Prod': async ({CloudCmd}) => {
const {TerminalRun} = CloudCmd;
await run(TerminalRun, 'npm run build:client');
CloudCmd.refresh();
},
}; };
async function renameCurrent(DOM) { async function run(TerminalRun, command) {
await DOM.renameCurrent(); await TerminalRun.show({
} command,
closeMessage: 'Press any key to close Terminal',
function run(command) { autoClose: false,
return async ({CloudCmd, DOM}) => { });
const {TerminalRun, config} = CloudCmd;
const {CurrentInfo} = DOM;
const {dirPath} = CurrentInfo;
const cwd = config('root') + dirPath;
return await TerminalRun.show({
cwd,
command,
closeMessage: 'Press any key to close Terminal',
autoClose: false,
});
};
} }

View file

@ -1,60 +0,0 @@
name: Docker IO
permissions:
contents: write
on:
- push
jobs:
buildx:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Use Node.js 24.x
uses: actions/setup-node@v6
with:
node-version: 24.x
- name: Install Redrun
run: bun i redrun -g --no-save
- name: NPM Install
run: bun i --no-save
- name: Lint
run: redrun lint
- name: Build
id: build
run: |
redrun build
VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4)
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push io-image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile.io
platforms: linux/amd64,linux/arm64
push: true
tags: |
coderaiser/cloudcmd:io
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-io
ghcr.io/${{ github.repository }}-io
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-io

View file

@ -1,6 +1,4 @@
name: Docker CI name: Docker CI
permissions:
contents: write
on: on:
push: push:
tags: tags:
@ -31,25 +29,24 @@ jobs:
id: build id: build
run: | run: |
redrun build redrun build
VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4) echo "::set-output name=version::$(grep '"version":' package.json -m1 | cut -d\" -f4)"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v4 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@v3
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v4 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }} password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v4 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push base-image - name: Build and push base-image
uses: docker/build-push-action@v7 uses: docker/build-push-action@v5
with: with:
context: . context: .
file: docker/Dockerfile file: docker/Dockerfile
@ -61,28 +58,14 @@ jobs:
ghcr.io/${{ github.repository }}:latest ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }} ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}
- name: Build and push alpine-image - name: Build and push alpine-image
uses: docker/build-push-action@v7 uses: docker/build-push-action@v5
with: with:
context: . context: .
file: docker/Dockerfile.alpine file: docker/Dockerfile.alpine
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: | tags: |
coderaiser/cloudcmd:alpine
coderaiser/cloudcmd:latest-alpine coderaiser/cloudcmd:latest-alpine
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-alpine coderaiser/cloudcmd:${{ steps.build.outputs.version }}-alpine
ghcr.io/${{ github.repository }}:latest-alpine ghcr.io/${{ github.repository }}:latest-alpine
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-alpine ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-alpine
- name: Build and push slim-image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile.slim
platforms: linux/amd64,linux/arm64
push: true
tags: |
coderaiser/cloudcmd:slim
coderaiser/cloudcmd:latest-slim
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-slim
ghcr.io/${{ github.repository }}-slim
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-slim

View file

@ -1,8 +1,6 @@
name: Node CI name: Node CI
on: on:
- push - push
permissions:
contents: write
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -13,7 +11,7 @@ jobs:
node-version: node-version:
- 22.x - 22.x
- 24.x - 24.x
- 26.x - 25.x
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2 - uses: oven-sh/setup-bun@v2
@ -24,19 +22,26 @@ jobs:
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install Redrun - name: Install Redrun
run: bun i redrun madrun -g --no-save run: bun i redrun -g --no-save
- name: Install - name: Install
run: bun i --no-save run: bun i --no-save
- name: Init Madrun
run: madrun --init
- name: Lint - name: Lint
run: redrun fix:lint run: redrun fix:lint
- name: Typos - uses: actions/cache@v4
uses: coderaiser/typos.ai@v1.1.8
with: with:
key: ${{ secrets.TYPOS_AI_KEY }} path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Typos Install
run: cargo install typos-cli || echo 'already installed'
- name: Typos
run: typos --write-changes
- name: Commit fixes - name: Commit fixes
uses: EndBug/add-and-commit@v10 uses: EndBug/add-and-commit@v9
continue-on-error: true continue-on-error: true
with: with:
message: "chore: ${{ env.NAME }}: actions: lint ☘️" message: "chore: ${{ env.NAME }}: actions: lint ☘️"

22
.gitignore vendored
View file

@ -1,17 +1,19 @@
*.swp
*.log
*.lock
.nyc_output
.DS_Store
.idea
package-lock.json package-lock.json
npm-debug.log* yarn.lock
yarn-error.log
node_modules node_modules
npm-debug.log*
coverage coverage
modules/execon modules/execon
modules/emitify modules/emitify
.nyc_output
*.swp
.DS_Store
dist dist
dist-dev dist-dev
.idea

View file

@ -1,45 +1,47 @@
import process from 'node:process';
import {run, cutEnv} from 'madrun'; import {run, cutEnv} from 'madrun';
import {defineEnv} from 'supertape/env';
const testEnv = defineEnv({ const testEnv = {
timeout: 7000, SUPERTAPE_TIMEOUT: 7000,
css: true, };
});
const buildEnv = { const is17 = /^v1[789]/.test(process.version);
const is20 = process.version.startsWith('v2');
// fix for ERR_OSSL_EVP_UNSUPPORTED on node v17
// flag '--openssl-legacy-provider' not supported
// on earlier version of node.js
//
// https://stackoverflow.com/a/69746937/4536327
const buildEnv = (is17 || is20) && {
NODE_OPTIONS: '--openssl-legacy-provider',
NODE_ENV: 'production', NODE_ENV: 'production',
}; };
export default { export default {
'start': () => 'node bin/cloudcmd.js', 'start': () => 'node bin/cloudcmd.mjs',
'start:dev': async () => await run('start', null, { 'start:dev': async () => await run('start', null, {
NODE_ENV: 'development', NODE_ENV: 'development',
}), }),
'build:start': () => run(['build:client', 'start']), 'build:start': () => run(['build:client', 'start']),
'build:start:dev': () => run([ 'build:start:dev': () => run(['build:client:dev', 'start:dev']),
'build:client:dev',
'start:dev',
]),
'lint:all': () => run('lint:progress'), 'lint:all': () => run('lint:progress'),
'lint': () => 'redlint fix; putout .', 'lint': () => 'putout .',
'lint:progress': () => run('lint', '-f progress'), 'lint:progress': () => run('lint', '-f progress'),
'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"', 'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"',
'fresh:lint': () => run('lint', '--fresh'), 'fresh:lint': () => run('lint', '--fresh'),
'lint:fresh': () => run('lint', '--fresh'), 'lint:fresh': () => run('lint', '--fresh'),
'fix:lint': async () => `putout --fix . && redlint fix`, 'fix:lint': () => run('lint', '--fix'),
'lint:stream': () => run('lint', '-f stream'), 'lint:stream': () => run('lint', '-f stream'),
'test': () => [testEnv, `tape '{test}/**/*.js' '{bin,client,static,common,server}/**/*.spec.js' -f fail`], 'test': () => [testEnv, `tape 'test/**/*.{js,mjs}' '{client,static,common,server}/**/*.spec.{js,mjs}' -f fail`],
'test:e2e': () => `tape 'test-e2e/**/*.js'`,
'test:client': () => `tape 'test/client/**/*.js'`, 'test:client': () => `tape 'test/client/**/*.js'`,
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`, 'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
'wisdom': async () => await run(['lint:all', 'build', 'test'], null, { 'wisdom': () => run(['lint:all', 'build', 'test']),
CI: 1, 'wisdom:type': () => 'bin/release.mjs',
}),
'wisdom:type': () => 'bin/release.js',
'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`], 'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`],
'coverage:report': () => 'c8 report --reporter=lcov', 'coverage:report': () => 'c8 report --reporter=lcov',
'report': () => 'c8 report --reporter=lcov', 'report': () => 'c8 report --reporter=lcov',
'6to5': () => [buildEnv, 'rspack build --config rspack.config.js'], '6to5': () => [buildEnv, 'webpack --progress'],
'6to5:client': () => run('6to5', '--mode production'), '6to5:client': () => run('6to5', '--mode production'),
'6to5:client:dev': async () => await run('6to5', '--mode development', { '6to5:client:dev': async () => await run('6to5', '--mode development', {
NODE_ENV: 'development', NODE_ENV: 'development',
@ -53,7 +55,6 @@ export default {
'watch:test:client': async () => `nodemon -w client -w test/client -x ${await run('test:client')}`, 'watch:test:client': async () => `nodemon -w client -w test/client -x ${await run('test:client')}`,
'watch:test:server': async () => `nodemon -w client -w test/client -x ${await run('test:server')}`, 'watch:test:server': async () => `nodemon -w client -w test/client -x ${await run('test:server')}`,
'watch:coverage': async () => [testEnv, `nodemon -w server -w test -w common -x ${await cutEnv('coverage')}`], 'watch:coverage': async () => [testEnv, `nodemon -w server -w test -w common -x ${await cutEnv('coverage')}`],
'watch:fix:lint': async () => `nodemon -w client -w server -w test -w common -x '${await run('fix:lint')}'`,
'build': async () => run('6to5:*'), 'build': async () => run('6to5:*'),
'build:dev': async () => run('build:client:dev'), 'build:dev': async () => run('build:client:dev'),
'build:client': () => run('6to5:client'), 'build:client': () => run('6to5:client'),

View file

@ -1,29 +1,34 @@
.*
*.spec.* *.spec.*
*.config.* *.config.*
*.fixture.js* *.fixture.js*
*.ai
*.cdr
*.eps
*.log
*.lock
.*
manifest.yml manifest.yml
docker-compose.yml
now.json
app.json
bower.json
deno.json
bin/release.*
img/logo/cloudcmd-hq.png
webpack.config.js
docker docker
test* docker-compose.yml
test
fixture fixture
fixture-* fixture-*
coverage coverage
css css
html html
yarn-error.log
yarn.lock
now.json
app.json
bower.json
manifest.yml
deno.json
bin/release.mjs
client client
img/logo/cloudcmd-hq.png
webpack.config.js
*.ai
*.cdr
*.eps
*.config.*

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
v20.15.1

View file

@ -1,11 +1,11 @@
{ {
"checkCoverage": false, "check-coverage": false,
"all": false, "all": false,
"exclude": [ "exclude": [
"**/*.spec.js", "**/*.spec.js",
"**/fixture",
"**/*.*.js", "**/*.*.js",
"**/*.config.*", "**/*.config.*",
"**/fixture",
"**/test/**" "**/test/**"
], ],
"branches": 100, "branches": 100,

View file

@ -1,11 +1,11 @@
{ {
"plugins": ["cloudcmd"], "plugins": ["cloudcmd"],
"ignore": [ "ignore": [
"*.md", "html",
"fixture*",
"app.json", "app.json",
"fontello.json", "fontello.json",
"html", "*.md"
"fixture*"
], ],
"rules": { "rules": {
"package-json/add-type": "off" "package-json/add-type": "off"

View file

@ -1,2 +1,2 @@
[files] [files]
extend-exclude = ["ChangeLog"] extend-exclude= ["ChangeLog", "*.js"]

View file

@ -1,18 +1,21 @@
import {env} from 'node:process'; import {env} from 'node:process';
import {rspack} from '@rspack/core'; import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
const {CssExtractRspackPlugin} = rspack;
const isDev = env.NODE_ENV === 'development'; const isDev = env.NODE_ENV === 'development';
const plugins = [ const clean = (a) => a.filter(Boolean);
new CssExtractRspackPlugin({
const plugins = clean([
new MiniCssExtractPlugin({
filename: '[name].css', filename: '[name].css',
}), }),
]; !isDev && new OptimizeCssAssetsPlugin(),
]);
const rules = [{ const rules = [{
test: /\.css$/i, test: /\.css$/i,
use: [CssExtractRspackPlugin.loader, { use: [MiniCssExtractPlugin.loader, {
loader: 'css-loader', loader: 'css-loader',
options: { options: {
url: true, url: true,
@ -24,15 +27,8 @@ const rules = [{
}]; }];
export default { export default {
mode: isDev ? 'development' : 'production',
plugins, plugins,
module: { module: {
rules, rules,
}, },
optimization: {
minimize: !isDev,
minimizer: [
new rspack.LightningCssMinimizerRspackPlugin(),
],
},
}; };

View file

@ -1,15 +1,12 @@
import {resolve, sep} from 'node:path'; import {resolve, sep} from 'node:path';
import {fileURLToPath} from 'node:url';
import {env} from 'node:process'; import {env} from 'node:process';
import {rspack} from '@rspack/core'; import webpack from 'webpack';
import WebpackBar from 'webpackbar';
const resolveModule = (a) => fileURLToPath(import.meta.resolve(a));
const { const {
EnvironmentPlugin, EnvironmentPlugin,
NormalModuleReplacementPlugin, NormalModuleReplacementPlugin,
ProvidePlugin, } = webpack;
} = rspack;
const modules = './modules'; const modules = './modules';
const dirModules = './client/modules'; const dirModules = './client/modules';
@ -25,31 +22,27 @@ const dist = resolve(rootDir, 'dist');
const distDev = resolve(rootDir, 'dist-dev'); const distDev = resolve(rootDir, 'dist-dev');
const devtool = isDev ? 'eval' : 'source-map'; const devtool = isDev ? 'eval' : 'source-map';
const noParse = (a) => a.endsWith('.spec.js'); const notEmpty = (a) => a;
const clean = (array) => array.filter(notEmpty);
// codegen.macro is a babel-macro (build-time codegen), not supported by const noParse = (a) => a.endsWith('.spec.js');
// Rspack's native SWC transform, so client/sw/sw.js (the only file that const options = {
// uses it) keeps going through babel-loader. Everything else uses babelrc: true,
// Rspack's built-in SWC loader, which is the main source of the speedup. };
const rules = [{
test: /sw\/sw\.js$/, const rules = clean([
exclude: /node_modules/, !isDev && {
loader: 'babel-loader', test: /\.[mc]?js$/,
}, { exclude: /node_modules/,
test: /\.[mc]?js$/, loader: 'babel-loader',
exclude: [/node_modules/, /sw\/sw\.js$/],
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'ecmascript',
},
},
env: {
targets: 'defaults',
},
}, },
}]; isDev && {
test: /\.[mc]?js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options,
},
]);
const plugins = [ const plugins = [
new NormalModuleReplacementPlugin(/^node:/, (resource) => { new NormalModuleReplacementPlugin(/^node:/, (resource) => {
@ -59,9 +52,7 @@ const plugins = [
new EnvironmentPlugin({ new EnvironmentPlugin({
NODE_ENV, NODE_ENV,
}), }),
new ProvidePlugin({ new WebpackBar(),
process: 'process/browser',
}),
]; ];
const splitChunks = { const splitChunks = {
@ -83,8 +74,6 @@ const splitChunks = {
'themes/light', 'themes/light',
'columns/name-size', 'columns/name-size',
'columns/name-size-date', 'columns/name-size-date',
'columns/name-size-time',
'columns/name-size-date-time',
]; ];
return !lazyChunks.includes(chunk.name); return !lazyChunks.includes(chunk.name);
@ -105,9 +94,8 @@ export default {
'node:path': 'path', 'node:path': 'path',
}, },
fallback: { fallback: {
path: resolveModule('path-browserify'), path: import.meta.resolve('path-browserify'),
process: resolveModule('process/browser'), process: import.meta.resolve('process/browser'),
util: resolveModule('util'),
}, },
}, },
devtool, devtool,
@ -119,15 +107,14 @@ export default {
'themes/light': `${dirThemes}/light.css`, 'themes/light': `${dirThemes}/light.css`,
'columns/name-size': `${dirColumns}/name-size.css`, 'columns/name-size': `${dirColumns}/name-size.css`,
'columns/name-size-date': `${dirColumns}/name-size-date.css`, 'columns/name-size-date': `${dirColumns}/name-size-date.css`,
'columns/name-size-date-time': `${dirColumns}/name-size-date-time.css`,
'nojs': `${dirCss}/nojs.css`, 'nojs': `${dirCss}/nojs.css`,
'help': `${dirCss}/help.css`, 'help': `${dirCss}/help.css`,
'view': `${dirCss}/view.css`, 'view': `${dirCss}/view.css`,
'config': `${dirCss}/config.css`, 'config': `${dirCss}/config.css`,
'terminal': `${dirCss}/terminal.css`, 'terminal': `${dirCss}/terminal.css`,
'user-menu': `${dirCss}/user-menu.css`, 'user-menu': `${dirCss}/user-menu.css`,
'sw': `${dir}/sw/sw.js`, 'sw': `${dir}/sw/sw.mjs`,
'cloudcmd': `${dir}/cloudcmd.js`, 'cloudcmd': `${dir}/cloudcmd.mjs`,
[`${modules}/edit`]: `${dirModules}/edit.js`, [`${modules}/edit`]: `${dirModules}/edit.js`,
[`${modules}/edit-file`]: `${dirModules}/edit-file.js`, [`${modules}/edit-file`]: `${dirModules}/edit-file.js`,
[`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`, [`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`,
@ -137,10 +124,10 @@ export default {
[`${modules}/view`]: `${dirModules}/view/index.js`, [`${modules}/view`]: `${dirModules}/view/index.js`,
[`${modules}/help`]: `${dirModules}/help.js`, [`${modules}/help`]: `${dirModules}/help.js`,
[`${modules}/markdown`]: `${dirModules}/markdown.js`, [`${modules}/markdown`]: `${dirModules}/markdown.js`,
[`${modules}/config`]: `${dirModules}/config/index.js`, [`${modules}/config`]: `${dirModules}/config/index.mjs`,
[`${modules}/contact`]: `${dirModules}/contact.js`, [`${modules}/contact`]: `${dirModules}/contact.js`,
[`${modules}/upload`]: `${dirModules}/upload.js`, [`${modules}/upload`]: `${dirModules}/upload.js`,
[`${modules}/operation`]: `${dirModules}/operation/index.js`, [`${modules}/operation`]: `${dirModules}/operation/index.mjs`,
[`${modules}/konsole`]: `${dirModules}/konsole.js`, [`${modules}/konsole`]: `${dirModules}/konsole.js`,
[`${modules}/terminal`]: `${dirModules}/terminal.js`, [`${modules}/terminal`]: `${dirModules}/terminal.js`,
[`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`, [`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`,
@ -162,7 +149,7 @@ export default {
}, },
plugins, plugins,
performance: { performance: {
maxEntrypointSize: 800_000, maxEntrypointSize: 600_000,
maxAssetSize: 600_000, maxAssetSize: 600_000,
}, },
}; };

689
ChangeLog
View file

@ -1,686 +1,3 @@
2026.06.15, v19.19.1
feature:
- 78b206e0 client: key: Ctrl + L -> Ctrl + Shift + L: Log out (#466)
- b1492885 docker: io: NPM_CONFIG_PACKAGE_LOCK=false
- 76d7bf4d docker: typos, shellcheck: add
2026.05.26, v19.19.0
feature:
- 4ee38db6 client: view: flac: add support
2026.05.26, v19.18.1
feature:
- 40ecef5e cloudcmd: ratelimit: X-Forwarded-For (#437)
2026.05.26, v19.18.0
fix:
- 161bede8 client: menu: @putout/bundle@5.5.
- 447d990f cloudcmd: server: rest: sendError
feature:
- 5d9628ce cloudcmd: rate limit: add support (#437)
- 2be395e6 cloudcmd: get rid of manifest: 401 basic auth
2026.05.17, v19.17.0
fix:
- d96f6c46 css: query: hide time on mobile
- e7c55e05 docker: io: XDG_CONFIG_HOME
feature:
- ae1ca0f8 cloudcmd: cssnano-preset-default v8.0.1
- 94d5096a style: owner, mode: improve
- 807f8346 cloudcmd: superc8 v12.6.0
- 67a95722 docker: io: cline: add
- 68eacb91 qword: add
- 4acd294b docker: io: tmux
- 05c80043 cloudcmd: @supertape/loader-css v1.0.0
- 800a6545 cloudcmd: eslint-plugin-n v18.0.1
- a985bb36 cloudcmd: supertape v13.0.0
- 4315ec61 docker: io XDG_CONFIG_HOME: /etc -> /usr/local/etc
2026.05.03, v19.16.0
feature:
- d82d0335 client: vim: rr for rename file
- acfa27cf docker: io: nvchad: add
- 14f009f8 docker: io: bash-completion: add
- c6c60146 docker: io: f4
- 9a6b8935 docker: io: GOPATH
- a9bc22ce cloudcmd: operation: rm useless checks
- 36bacfe9 cloudcmd: client: key: vim: cc, mm
- 591da25c cloudcmd: ponse v8.0.0
- 1e1b073c docker: io: /usr/src/cloudcmd -> /usr/local/share/cloudcmd
2026.04.28, v19.15.0
feature:
- 9d97343b cloudcmd: operation: rm useless checks
- 46a88cfa cloudcmd: client: key: vim: cc, mm
2026.04.28, v19.14.0
feature:
- 36a8b641 cloudcmd: ponse v8.0.0
- 1c263c18 docker: io: /usr/src/cloudcmd -> /usr/local/share/cloudcmd
- ea7b828c docker: io: ubuntu: resolute
- cd6c11ac docker: io: far2l
- c4beeec6 docker: io: gdu: add
2026.04.21, v19.13.1
feature:
- d5cd11e8 cloudcmd: montag v2.0.1
- dc94d2db docker: io: add latest git version
- 1637beee docker: io: /usr/local/src -> /usr/local/share
- b1bc4e73 Docker: io: pv
- 7c0dca60 docker: io: git: master
2026.04.15, v19.13.0
fix:
- 48693d9e docker: io: XDG_CACHE_HOME
feature:
- 4b2395c5 cloudcmd: Ctrl + L: logout
- 977a8aaa docker: io: strace: add
- 7d0098fd docker: io: XDG_CACHE_HOME=/tmp/cache
- a89e901b actions: docker: io: add
2026.04.12, v19.12.5
feature:
- 1cfc1a6f docker: io: only amd64
2026.04.12, v19.12.4
feature:
- 3c2b5658 docker: io: haskell
- d37f8cd3 docker: io: palabra: node
2026.04.12, v19.12.3
feature:
- cb6cabd4 docker: io: PALABRA_DIR
2026.04.11, v19.12.2
feature:
- 2ce11fa2 docker: io: get rid of haskell: to slow install
- aa741232 docker: io: rizin, yara
2026.04.09, v19.12.1
feature:
- 5bd03215 docker: io: add PREFIX
2026.04.09, v19.12.0
feature:
- 13b15b7b docker: io: palabra
2026.04.07, v19.11.14
feature:
- a31beab0 docker: io: rustup env
2026.04.07, v19.11.13
feature:
- 6a08479a docker: io: BUN_INSTALL
2026.04.06, v19.11.12
feature:
- 46c65554 docker: io: npm_config_cache
2026.04.06, v19.11.11
feature:
- ae2ce388 docker: io: $DENO_DIR
2026.04.06, v19.11.10
feature:
- 12ea14ac docker: io: nvm: node
2026.04.06, v19.11.9
feature:
- ea96d13f docker: io: net-tools
2026.04.06, v19.11.8
feature:
- 5c08565f docker: io: debian -> ubuntu
2026.04.05, v19.11.7
feature:
- 36cdef37 docker: io: bookworm
2026.04.05, v19.11.6
feature:
- 845f9bd1 cloudcmd: gritty v10.2.0
2026.04.05, v19.11.5
feature:
- f9c513cc docker: io: hexyl: add
2026.04.04, v19.11.4
feature:
- e7347d25 docker: io: neovim: apt -> github
2026.04.04, v19.11.3
feature:
- 9970ff76 docker: io: btop
- 4395a471 docker: io: $PATH: add $HOME/.local/bin
2026.04.04, v19.11.2
feature:
- c40ae8e4 docker: io: htop
- f463c5c5 docker: io: aptitude: add
- d1032f09 docker: io: remove unused
2026.04.04, v19.11.1
feature:
- 7787bfc2 cloudcmd: user-menu: runFromCDN
2026.04.04, v19.11.0
feature:
- 32f89d38 cloudcmd: user-menu: root
2026.04.04, v19.10.2
feature:
- df4fb517 cloudcmd: aleman v2.0.1
2026.04.03, v19.10.1
feature:
- ceb7ef4f docker: io: keep /var/lib/apt/lists
2026.04.02, v19.10.0
fix:
- d12e7bd0 distribute: fix event listener leak on socket disconnect (#462)
2026.04.02, v19.9.24
feature:
- c4d26c6a docker: io: apt-get upgrade
- 9ddb8c29 docker: io: get rid of nix
- e4d7d441 docker: io: nix
2026.04.01, v19.9.23
feature:
- 5a3413ce docker: io: nix
2026.03.31, v19.9.22
feature:
- b4345ed4 docker: io: DENO_DIR
2026.03.31, v19.9.21
fix:
- 5c6a9a95 css: columns: name: 40% -> 35%
2026.03.30, v19.9.20
feature:
- cd0b5554 iocmd: io: nvm
2026.03.30, v19.9.19
feature:
- 6a52b11e docker: io: go, rust
2026.03.30, v19.9.18
feature:
- 738059f2 docker: io: fzf
- 3fc8932f docker: io: less, el_GR
2026.03.30, v19.9.17
feature:
- cf424d6c docker: io: command-not-found update
2026.03.29, v19.9.16
feature:
- 19347a2b docker: io: add command-not-found
2026.03.29, v19.9.15
feature:
- ee170552 docker: io: ubuntu
- e04c4594 docker: io: net-tools: add
2026.03.29, v19.9.14
fix:
- 6c7709be docker: io: PS1
2026.03.29, v19.9.13
fix:
- 439b3710 bin: currify
- e4182841 docker: io: xterm-256color
2026.03.29, v19.9.12
feature:
- 811a47fd cloudcmd: bin: get rid of require
2026.03.29, v19.9.11
feature:
- 1f95f188 cloudcmd: get rid of simport
2026.03.29, v19.9.10
feature:
- f0deb323 docker: io: add ja_JP.UTF-8
2026.03.29, v19.9.9
fix:
- f671f798 docker: io: PS1 environment variable in Dockerfile
feature:
- cc7f9dc7 docker: io: use ubuntu
2026.03.28, v19.9.8
feature:
- 4d1cd8cd docker: io: ffmpeg
- 54b56fdc cloudcmd: vim: ESC: use only to enable, do not use to disable
2026.03.27, v19.9.7
feature:
- 110908e2 docker: io: apt-get
2026.03.26, v19.9.6
feature:
- 6450a2f8 docker: io: add UTF-8
2026.03.26, v19.9.5
fix:
- e761cacb columns: name-size-date-time: 20% -> 19%
2026.03.26, v19.9.4
feature:
- 69498ed6 docker: io: pull.rebase by default
2026.03.24, v19.9.3
feature:
- 8763788b docker: io: healthcheck
2026.03.23, v19.9.2
feature:
- 09a02074 docker: io: git config: add
- c448eaa4 docker: io: buni
2026.03.23, v19.9.1
fix:
- 6e5318fa client: modules: config: input: quote
2026.03.23, v19.9.0
feature:
- a1216cdd cloudcmd: add ability to hide port configuration
2026.03.23, v19.8.15
feature:
- 68b2aa78 docker: io: cloudcmd_vim
2026.03.23, v19.8.14
fix:
- 665ed9c2 docker: io: get back port
2026.03.23, v19.8.13
feature:
- 618a5615 docker: io: PS1
2026.03.23, v19.8.12
feature:
- 556b0150 docker: io: PS1
2026.03.23, v19.8.11
fix:
- 97672ef5 docker: io: apt-get install -> apt-get
feature:
- 67f27d84 docker: io: bun, deno
2026.03.23, v19.8.10
feature:
- 025b005e docker: io: add PS1
2026.03.23, v19.8.9
fix:
- b052cf22 cloudcmd: no time available: --.--.---- -> --:--:-- (#461)
2026.03.23, v19.8.8
feature:
- 02dbe56d server: user-menu: when error send it
2026.03.23, v19.8.7
feature:
- ecc76e8b docker: io: renamify-cli, runny, redfork
2026.03.23, v19.8.6
fix:
- 53c072ab @putout/plugin-cloudcmd: devDependencies -> dependnecies
feature:
- 4b9922bf docker: /usr/src: app -> cloudcmd
2026.03.23, v19.8.5
fix:
- 56fc8b83 docker: gritty
2026.03.22, v19.8.4
feature:
- 5000227e docker: vim nvim
2026.03.22, v19.8.3
fix:
- 01677e6a docker: io: slim -> io
feature:
- 7e35c606 docker: io: curl wget
2026.03.22, v19.8.2
feature:
- e5b221f7 docker: io: add
2026.03.22, v19.8.1
feature:
- 708a4c6b docker: slim: add
- 80613f46 docker: slim: add
2026.03.20, v19.8.0
fix:
- 59037f2c cloudcmd: bin: --show-config
feature:
- 10934b3a cloudcmd: add ability to show modification time (#230)
2026.03.18, v19.7.1
feature:
- b0c1d36c cloudcmd: @cloudcmd/fileop v9.0.7 (#460)
2026.03.17, v19.7.0
feature:
- daf83875 cloudfunc: override date format (#459)
2026.03.17, v19.6.9
feature:
- b28a070a cloudcmd: redzip v4.6.1
- 43c5a011 cloudcmd: css-minimizer-webpack-plugin v8.0.0
- 15dcae5c cloudcmd: webpack-cli v7.0.2
- 5976da81 cloudcmd: @cloudcmd/fileop v9.0.5
- 37cb83f2 cloudcmd: redzip v4.6.0
2026.02.27, v19.6.8
feature:
- 15fab514 cloudcmd: copymitter v10.3.0 (#458)
2026.02.26, v19.6.7
feature:
- 68c7d0be cloudcmd: onezip v7.0.0
2026.02.26, v19.6.6
feature:
- 3987cc82 cloudcmd: redzip v4.5.1 (#457)
2026.02.26, v19.6.5
feature:
- 964ae989 cloudcmd: redzip v4.5.0 (#457)
2026.02.25, v19.6.4
feature:
- a66eeda3 cloudcmd: copymitter v10.2.0 (coderaiser/cloudcmd#457)
- 4340533a cloudcmd: c8 v11.0.0
- 0857711f cloudcmd: redzip v4.2.0 (#457)
2026.02.24, v19.6.3
feature:
- 2234f1b4 cloudcmd: redzip v4.2.0 (#475)
2026.02.24, v19.6.2
feature:
- 321a54dd cloudcmd: @cloudcmd/fileop v9.0.2 (#457)
2026.02.24, v19.6.1
feature:
- 7c5ac408 cloudcmd: @cloudcmd/fileop v9.0.1 (#457)
2026.02.21, v19.6.0
feature:
- 6d19bf2e common: object.omit -> omit
2026.02.18, v19.5.1
feature:
- 6e1cf4ef cloudcmd: supermenu v5.0.0
2026.02.18, v19.5.0
feature:
- b20539ef common: entity: encode {,}
- 7ef134f4 cloudcmd: rendy v5.0.0
2026.02.18, v19.4.1
feature:
- 1e18d513 cloudcmd: @cloudcmd/fileop v9.0.0
2026.02.18, v19.4.0
fix:
- 45cf9baf menu: prefix (#456)
feature:
- 3e647290 cloudcmd: redlint v6.0.0
- 800ed012 cloudcmd: putout v42.0.5
- 525c17d4 cloudcmd: madrun v13.0.0
- 44247499 cloudcmd: eslint-plugin-putout v31.0.0
2026.02.15, v19.3.9
feature:
- 9ffe3ef1 cloudcmd: copymitter v10.0.0 (#457)
2026.02.15, v19.3.8
fix:
- d274a2b3 spinner (#456)
2026.02.13, v19.3.7
feature:
- 8fd79a27 cloudcmd: win32 v8.0.0
2026.02.12, v19.3.6
feature:
- ac94eccd cloudcmd: konsole: named
- 144e4a34 cloudcmd: gritty v10.0.0
- 938f9e76 cloudcmd: console-io v15.0.1
2026.02.08, v19.3.5
feature:
- fb40bd9c rm cssnano: has no sense for spinner, option disabled by default (https://svgo.dev/docs/plugins/convertPathData/)
- 76125be9 cloudcmd: eslint v10.0.0
2026.02.06, v19.3.4
feature:
- 66a08c7f cloudcmd: deepword v11.0.0
2026.02.05, v19.3.3
feature:
- 4a5a56f4 cloudcmd: dword v16.0.0
2026.02.04, v19.3.2
fix:
- 99d8435e cloudcmd: exports
- a266c145 cloudcmd: default -> named
- 6e3ba271 Closing X in editor disappeared (#455)
feature:
- 30f42e94 cloudcmd: restafary v13.0.1
- f84ce853 cloudcmd: edward v16.0.0
2026.02.03, v19.3.1
fix:
- 5661bc4f Closing X in editor disappeared (#455)
2026.02.03, v19.3.0
feature:
- 4533a25c cloudcmd: migrate to ESM
- e8a81c49 client: dom: events: migrate to ESM
- 071141bc client: terminal-run: migrate to ESM
2026.02.03, v19.2.0
feature:
- bb32f7c4 client: dom: migrate to ESM
- 457c83db client: migrate to ESM
- f8a941bd client: modules: markdown: migrate to ESM
- 9d6cffaf client: polifyll: migrate to ESM
- 5b704d06 client: edit-names: migrate to ESM
- 2c2ca8eb client: edit-file: migrate to ESM
- c9f57c5f client: modules: edit-file-vim: migrate to ESM
- 327ac9de client: modules: help: migrate to ESM
- dc34ee87 cloudcmd: @putout/plugin-cloudcmd v5.0.0
- 3cd3695b client: modules: edit-names-vim: migrate to ESM
- dfcbfd63 client: modules: terminal: migrate to ESM
2026.02.02, v19.1.21
feature:
- e3ad330e client: konsole: migrate to ESM
- 983fd9af client: edit: migrate to ESM
- ebabad94 common: entity: migrate to ESM
- c3b71653 client: dom: images -> #images
- 8cad7514 common: cloudfunc -> #common/cloudfunc
- 1f174870 client: view: migrate to ESM
- 7173f6cb cloudcmd: smalltalk v5.0.0
2026.01.31, v19.1.20
feature:
- c1014c9c client: dom: operations: rename-current: migrate to ESM
- 2e486f8b cloudcmd: restafary v13.0.0
- 6addb29f cloudcmd: redzip v4.0.0
2026.01.30, v19.1.19
feature:
- 2a0feac7 cloudcmd: @cloudcmd/modal v4.0.0
2026.01.30, v19.1.18
feature:
- 73fa4961 client: modules: user-menu: migrate to ESM
- ebfdf8c0 client: modules: operation: migrate to ESM
- ad683171 client: modules: menu: migrate to ESM
- 0054cfa3 client: dom: load-remote: migrate to ESM
- e4d0ece0 client: dom: rest: migrate to ESM
- c704ffe4 client: dom: storage: migrate to ESM
- 5182cb81 client: modules: upload: migrate to ESM
- 9543f1ad client: dom: upload-files: migrate to ESM
2026.01.29, v19.1.17
feature:
- f9c28319 client: dom: migrate to ESM
- 9d2c4e4a client: dom: cmd: move out
- 5a2aa70f client: dom: simplify require
- dee50a85 client: dom: files: migrate to ESM
- 23c0d770 client: dom: dom-tree: migrate to ESM
- cf2c6415 client: directory: migrate to ESM
- 0eb802e7 client: dom: dialog: migrate to ESM
2026.01.28, v19.1.16
fix:
- 4c242631 css: spinner: do not minify svg
2026.01.28, v19.1.15
feature:
- 265c0b49 client: key: vim: migrate to ESM
- 3bc49f02 client: set-current-by-char: migrate to ESM
2026.01.28, v19.1.14
feature:
- f8a63b5a client: key: binder: migrate to ESM
- 2cc97f33 client: cloud: migrate to ESM
2026.01.27, v19.1.13
feature:
- 41b5a96f client: load: migrate to ESM
2026.01.27, v19.1.12 2026.01.27, v19.1.12
feature: feature:
@ -7778,7 +7095,7 @@ with clicks on files.
- Removed allowed from cache property in config. - Removed allowed from cache property in config.
- Added ability to hide "Upload To" menu item if - Added ability to hide "Upload To" menu item if
no storage modules set up in modules.json. no storage modules setted up in modules.json.
- From now any file minifying only if last modified - From now any file minifying only if last modified
time was changed. time was changed.
@ -7984,7 +7301,7 @@ in any position in anyLoadOnLoad function.
- Added chainable to Cache object in DOM. - Added chainable to Cache object in DOM.
- Changed default ip to null so IP would be geted from - Changed default ip to null so IP would be geted from
config.json only if it set up. config.json only if it setted up.
- Fixed bug with starting node from other then projects dir. - Fixed bug with starting node from other then projects dir.
@ -8293,7 +7610,7 @@ time was possible.
- Fixed bug with undefined size on root directory of Cloud Commander. - Fixed bug with undefined size on root directory of Cloud Commander.
Now Cloud Commander writes size 0, if can't get size, and besides will Now Cloud Commander writes size 0, if can't get size, and besides will
set b char: "0b". setted b char: "0b".
- Added supporting of Russian language in directory names. - Added supporting of Russian language in directory names.

172
HELP.md
View file

@ -1,6 +1,6 @@
# Cloud Commander v19.19.1 # Cloud Commander v19.1.12
### [Main][MainURL] [Blog][BlogURL] [Demo][DemoURL] [Deploy](#deploy) ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
[MainURL]: https://cloudcmd.io "Main" [MainURL]: https://cloudcmd.io "Main"
[BlogURL]: https://blog.cloudcmd.io "Blog" [BlogURL]: https://blog.cloudcmd.io "Blog"
@ -9,17 +9,13 @@
[DWORD]: https://github.com/cloudcmd/dword "Editor based on CodeMirror" [DWORD]: https://github.com/cloudcmd/dword "Editor based on CodeMirror"
[EDWARD]: https://github.com/cloudcmd/edward "Editor based on Ace" [EDWARD]: https://github.com/cloudcmd/edward "Editor based on Ace"
[DEEPWORD]: https://github.com/cloudcmd/deepword "Editor based on Monaco" [DEEPWORD]: https://github.com/cloudcmd/deepword "Editor based on Monaco"
[QWORD]: https://github.com/cloudcmd/qword "Editor based on CodeMirror 6"
[EDWARD_KEYS]: https://github.com/cloudcmd/edward/#hot-keys "Edward Hot keys" [EDWARD_KEYS]: https://github.com/cloudcmd/edward/#hot-keys "Edward Hot keys"
[TERMUX]: https://termux.com "Termux" [TERMUX]: https://termux.com "Termux"
[INLY]: https://github.com/coderaiser/node-inly "Extract archive" [INLY]: https://github.com/coderaiser/node-inly "Extract archive"
[DeployInstaPodsIMG]: https://img.shields.io/badge/deploy%20on-InstaPods-blue
[DeployInstaPodsURL]: https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd
**Cloud Commander** is a file manager for the web. It includes a command-line console and a text editor. Cloud Commander helps you manage your server and work with files, directories and programs in a web browser from any computer, mobile or tablet. **Cloud Commander** is a file manager for the web. It includes a command-line console and a text editor. Cloud Commander helps you manage your server and work with files, directories and programs in a web browser from any computer, mobile or tablet.
![Cloud Commander](https://cloudcmd.io/img/logo/cloudcmd.png "Cloud Commander") ![Cloud Commander](/img/logo/cloudcmd.png "Cloud Commander")
## Benefits ## Benefits
@ -30,7 +26,7 @@
- Server works on **Windows**, **Linux**, **Mac OS** and **Android** (with help of [Termux][TERMUX]). - Server works on **Windows**, **Linux**, **Mac OS** and **Android** (with help of [Termux][TERMUX]).
- Can be used local or remotely. - Can be used local or remotely.
- Adapts to screen size. - Adapts to screen size.
- **3 built-in editors** with support of **syntax highlighting**: [Dword][DWORD], [Edward][EDWARD] [Deepword][DEEPWORD] and [Qword][QWORD]. - **3 built-in editors** with support of **syntax highlighting**: [Dword][DWORD], [Edward][EDWARD] and [Deepword][DEEPWORD].
- [Console](https://github.com/cloudcmd/console "Console") with support of the default OS command line. - [Console](https://github.com/cloudcmd/console "Console") with support of the default OS command line.
- Written in **JavaScript/Node.js**. - Written in **JavaScript/Node.js**.
- Built-in archives pack: **zip** and **tar.gz**. - Built-in archives pack: **zip** and **tar.gz**.
@ -92,7 +88,6 @@ Cloud Commander supports the following command-line parameters:
| `--contact` | enable contact | `--contact` | enable contact
| `--config-dialog` | enable config dialog | `--config-dialog` | enable config dialog
| `--config-auth` | enable auth change in config dialog | `--config-auth` | enable auth change in config dialog
| `--config-port` | enable port change in config dialog
| `--console` | enable console | `--console` | enable console
| `--sync-console-path` | sync console path | `--sync-console-path` | sync console path
| `--terminal` | enable terminal | `--terminal` | enable terminal
@ -123,7 +118,6 @@ Cloud Commander supports the following command-line parameters:
| `--no-confirm-move` | do not confirm move | `--no-confirm-move` | do not confirm move
| `--no-config-dialog` | disable config dialog | `--no-config-dialog` | disable config dialog
| `--no-config-auth` | disable auth change in config dialog | `--no-config-auth` | disable auth change in config dialog
| `--no-config-port` | disable port change in config dialog
| `--no-console` | disable console | `--no-console` | disable console
| `--no-sync-console-path` | do not sync console path | `--no-sync-console-path` | do not sync console path
| `--no-contact` | disable contact | `--no-contact` | disable contact
@ -195,7 +189,6 @@ Then, start the server again with `cloudcmd` and reload the page.
| `Ctrl + F3` | sort by name | `Ctrl + F3` | sort by name
| `Ctrl + F5` | sort by date | `Ctrl + F5` | sort by date
| `Ctrl + F6` | sort by size | `Ctrl + F6` | sort by size
| `Ctrl + Shift + L` | logout
| `Ctrl + Command + .` | show/hide dot files | `Ctrl + Command + .` | show/hide dot files
| `Up` | move cursor up | `Up` | move cursor up
| `Down` | move cursor down | `Down` | move cursor down
@ -218,27 +211,24 @@ Then, start the server again with `cloudcmd` and reload the page.
When the `--vim` option is provided, or the configuration parameter `vim` is set, the following hotkeys become available: When the `--vim` option is provided, or the configuration parameter `vim` is set, the following hotkeys become available:
| Key |Operation |Key |Operation
|:------------|:-------------------------------------------- |:----------------------|:--------------------------------------------
| `j` | navigate to next file | `j` | navigate to next file
| `k` | navigate to previous file | `k` | navigate to previous file
| `dd` | remove current file | `dd` | remove current file
| `G` or `$` | navigate to bottom file | `G` or `$` | navigate to bottom file
| `gg` or `^` | navigate to top file | `gg` or `^` | navigate to top file
| `v` | visual mode | `v` | visual mode
| `y` | copy (selected in visual mode files) | `y` | copy (selected in visual mode files)
| `p` | paste files | `p` | paste files
| `Esc` | unselect all | `Esc` | unselect all
| `/` | find file in current directory | `/` | find file in current directory
| `n` | navigate to next found file | `n` | navigate to next found file
| `N` | navigate to previous found file | `N` | navigate to previous found file
| `md` | make directory | `md` | make directory
| `mf` | make file | `mf` | make file
| `tt` | show terminal | `tt` | show terminal
| `e` | edit file | `e` | edit file
| `cc` | copy
| `mm` | move
| `rr` | rename file
Commands can be joined, for example: Commands can be joined, for example:
@ -450,7 +440,6 @@ Some config options can be overridden with environment variables, such as:
- `CLOUDCMD_CONTACT` - enable contact - `CLOUDCMD_CONTACT` - enable contact
- `CLOUDCMD_CONFIG_DIALOG` - enable config dialog - `CLOUDCMD_CONFIG_DIALOG` - enable config dialog
- `CLOUDCMD_CONFIG_AUTH` - enable auth change in config dialog - `CLOUDCMD_CONFIG_AUTH` - enable auth change in config dialog
- `CLOUDCMD_CONFIG_PORT` - enable port change in config dialog
- `CLOUDCMD_CONSOLE` - enable console - `CLOUDCMD_CONSOLE` - enable console
- `CLOUDCMD_SYNC_CONSOLE_PATH` - sync console path - `CLOUDCMD_SYNC_CONSOLE_PATH` - sync console path
- `CLOUDCMD_TERMINAL` - enable terminal - `CLOUDCMD_TERMINAL` - enable terminal
@ -699,7 +688,7 @@ And create `index.js`:
```js ```js
import http from 'node:http'; import http from 'node:http';
import {cloudcmd} from 'cloudcmd'; import cloudcmd from 'cloudcmd';
import {Server} from 'socket.io'; import {Server} from 'socket.io';
import express from 'express'; import express from 'express';
@ -933,9 +922,9 @@ ln -s ./sites-available/io.cloudcmd.io ./sites-enabled
## Deploy ## Deploy
`Cloud Commander` can be easily deployed to [InstaPods][DeployInstaPodsURL]. After deploy you receive email with credentials. `Cloud Commander` can be easily deployed to [Heroku](https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy to Heroku").
[![Deploy on InstaPods](https://instapods.com/deploy-button.svg)](https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd) [![Deploy to Heroku](https://www.herokucdn.com/deploy/button.png "Deploy to Heroku")](https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd)
## Docker ## Docker
@ -1122,117 +1111,6 @@ There are a lot of ways to be involved in `Cloud Commander` development:
## Version history ## Version history
- *2026.06.15*, **[v19.19.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.19.1)**
- *2026.05.26*, **[v19.19.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.19.0)**
- *2026.05.26*, **[v19.18.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.18.1)**
- *2026.05.26*, **[v19.18.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.18.0)**
- *2026.05.17*, **[v19.17.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.17.0)**
- *2026.05.03*, **[v19.16.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.16.0)**
- *2026.04.28*, **[v19.15.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.15.0)**
- *2026.04.28*, **[v19.14.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.14.0)**
- *2026.04.21*, **[v19.13.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.13.1)**
- *2026.04.15*, **[v19.13.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.13.0)**
- *2026.04.12*, **[v19.12.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.5)**
- *2026.04.12*, **[v19.12.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.4)**
- *2026.04.12*, **[v19.12.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.3)**
- *2026.04.11*, **[v19.12.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.2)**
- *2026.04.09*, **[v19.12.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.1)**
- *2026.04.09*, **[v19.12.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.0)**
- *2026.04.07*, **[v19.11.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.14)**
- *2026.04.07*, **[v19.11.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.13)**
- *2026.04.06*, **[v19.11.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.12)**
- *2026.04.06*, **[v19.11.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.11)**
- *2026.04.06*, **[v19.11.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.10)**
- *2026.04.06*, **[v19.11.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.9)**
- *2026.04.06*, **[v19.11.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.8)**
- *2026.04.05*, **[v19.11.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.7)**
- *2026.04.05*, **[v19.11.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.6)**
- *2026.04.05*, **[v19.11.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.5)**
- *2026.04.04*, **[v19.11.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.4)**
- *2026.04.04*, **[v19.11.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.3)**
- *2026.04.04*, **[v19.11.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.2)**
- *2026.04.04*, **[v19.11.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.1)**
- *2026.04.04*, **[v19.11.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.0)**
- *2026.04.04*, **[v19.10.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.10.2)**
- *2026.04.03*, **[v19.10.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.10.1)**
- *2026.04.02*, **[v19.10.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.10.0)**
- *2026.04.02*, **[v19.9.24](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.24)**
- *2026.04.01*, **[v19.9.23](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.23)**
- *2026.03.31*, **[v19.9.22](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.22)**
- *2026.03.31*, **[v19.9.21](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.21)**
- *2026.03.30*, **[v19.9.20](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.20)**
- *2026.03.30*, **[v19.9.19](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.19)**
- *2026.03.30*, **[v19.9.18](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.18)**
- *2026.03.30*, **[v19.9.17](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.17)**
- *2026.03.29*, **[v19.9.16](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.16)**
- *2026.03.29*, **[v19.9.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.15)**
- *2026.03.29*, **[v19.9.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.14)**
- *2026.03.29*, **[v19.9.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.13)**
- *2026.03.29*, **[v19.9.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.12)**
- *2026.03.29*, **[v19.9.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.11)**
- *2026.03.29*, **[v19.9.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.10)**
- *2026.03.29*, **[v19.9.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.9)**
- *2026.03.28*, **[v19.9.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.8)**
- *2026.03.27*, **[v19.9.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.7)**
- *2026.03.26*, **[v19.9.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.6)**
- *2026.03.26*, **[v19.9.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.5)**
- *2026.03.26*, **[v19.9.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.4)**
- *2026.03.24*, **[v19.9.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.3)**
- *2026.03.23*, **[v19.9.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.2)**
- *2026.03.23*, **[v19.9.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.1)**
- *2026.03.23*, **[v19.9.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.0)**
- *2026.03.23*, **[v19.8.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.15)**
- *2026.03.23*, **[v19.8.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.14)**
- *2026.03.23*, **[v19.8.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.13)**
- *2026.03.23*, **[v19.8.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.12)**
- *2026.03.23*, **[v19.8.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.11)**
- *2026.03.23*, **[v19.8.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.10)**
- *2026.03.23*, **[v19.8.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.9)**
- *2026.03.23*, **[v19.8.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.8)**
- *2026.03.23*, **[v19.8.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.7)**
- *2026.03.23*, **[v19.8.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.6)**
- *2026.03.23*, **[v19.8.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.5)**
- *2026.03.22*, **[v19.8.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.4)**
- *2026.03.22*, **[v19.8.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.3)**
- *2026.03.22*, **[v19.8.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.2)**
- *2026.03.22*, **[v19.8.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.1)**
- *2026.03.20*, **[v19.8.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.0)**
- *2026.03.18*, **[v19.7.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.7.1)**
- *2026.03.17*, **[v19.7.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.7.0)**
- *2026.03.17*, **[v19.6.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.9)**
- *2026.02.27*, **[v19.6.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.8)**
- *2026.02.26*, **[v19.6.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.7)**
- *2026.02.26*, **[v19.6.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.6)**
- *2026.02.26*, **[v19.6.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.5)**
- *2026.02.25*, **[v19.6.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.4)**
- *2026.02.24*, **[v19.6.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.3)**
- *2026.02.24*, **[v19.6.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.2)**
- *2026.02.24*, **[v19.6.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.1)**
- *2026.02.21*, **[v19.6.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.0)**
- *2026.02.18*, **[v19.5.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.5.1)**
- *2026.02.18*, **[v19.5.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.5.0)**
- *2026.02.18*, **[v19.4.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.4.1)**
- *2026.02.18*, **[v19.4.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.4.0)**
- *2026.02.15*, **[v19.3.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.9)**
- *2026.02.15*, **[v19.3.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.8)**
- *2026.02.13*, **[v19.3.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.7)**
- *2026.02.12*, **[v19.3.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.6)**
- *2026.02.08*, **[v19.3.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.5)**
- *2026.02.06*, **[v19.3.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.4)**
- *2026.02.05*, **[v19.3.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.3)**
- *2026.02.04*, **[v19.3.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.2)**
- *2026.02.03*, **[v19.3.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.1)**
- *2026.02.03*, **[v19.3.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.0)**
- *2026.02.03*, **[v19.2.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.2.0)**
- *2026.02.02*, **[v19.1.21](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.21)**
- *2026.01.31*, **[v19.1.20](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.20)**
- *2026.01.30*, **[v19.1.19](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.19)**
- *2026.01.30*, **[v19.1.18](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.18)**
- *2026.01.29*, **[v19.1.17](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.17)**
- *2026.01.28*, **[v19.1.16](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.16)**
- *2026.01.28*, **[v19.1.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.15)**
- *2026.01.28*, **[v19.1.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.14)**
- *2026.01.27*, **[v19.1.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.13)**
- *2026.01.27*, **[v19.1.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.12)** - *2026.01.27*, **[v19.1.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.12)**
- *2026.01.25*, **[v19.1.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.11)** - *2026.01.25*, **[v19.1.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.11)**
- *2026.01.25*, **[v19.1.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.10)** - *2026.01.25*, **[v19.1.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.10)**

View file

@ -1,4 +1,4 @@
# Cloud Commander v19.19.1 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [![Deploy on InstaPods][DeployInstaPodsIMG]][DeployInstaPodsURL] # Cloud Commander v19.1.12 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL]
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
@ -15,8 +15,6 @@
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg [GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy" [DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
[DeployIMG]: https://www.herokucdn.com/deploy/button.png [DeployIMG]: https://www.herokucdn.com/deploy/button.png
[DeployInstaPodsIMG]: https://img.shields.io/badge/deploy%20on-InstaPods-blue
[DeployInstaPodsURL]: https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd
**Cloud Commander** a file manager for the web with console and editor. **Cloud Commander** a file manager for the web with console and editor.
@ -71,7 +69,7 @@ And create `index.js`:
```js ```js
import http from 'node:http'; import http from 'node:http';
import {cloudcmd} from 'cloudcmd'; import cloudcmd from 'cloudcmd';
import {Server} from 'socket.io'; import {Server} from 'socket.io';
import express from 'express'; import express from 'express';

View file

@ -1,21 +1,24 @@
#!/usr/bin/env node #!/usr/bin/env node
import process from 'node:process'; import process from 'node:process';
import {createRequire} from 'node:module';
import {promisify} from 'node:util'; import {promisify} from 'node:util';
import {tryToCatch} from 'try-to-catch'; import {tryToCatch} from 'try-to-catch';
import {createSimport} from 'simport';
import parse from 'yargs-parser'; import parse from 'yargs-parser';
import exit from '../server/exit.js'; import exit from '../server/exit.js';
import {createConfig, configPath} from '../server/config.js'; import {createConfig, configPath} from '../server/config.mjs';
import * as env from '../server/env.js'; import * as env from '../server/env.mjs';
import prefixer from '../server/prefixer.js'; import prefixer from '../server/prefixer.js';
import * as validate from '../server/validate.js'; import * as validate from '../server/validate.mjs';
import Info from '../package.json' with {
type: 'json',
};
process.on('unhandledRejection', exit); process.on('unhandledRejection', exit);
const require = createRequire(import.meta.url);
const Info = require('../package.json');
const isUndefined = (a) => typeof a === 'undefined'; const isUndefined = (a) => typeof a === 'undefined';
const simport = createSimport(import.meta.url);
const choose = (a, b) => { const choose = (a, b) => {
if (isUndefined(a)) if (isUndefined(a))
@ -28,6 +31,8 @@ const config = createConfig({
configPath, configPath,
}); });
const DIR_SERVER = '../server/';
const maybeRoot = (a) => { const maybeRoot = (a) => {
if (a === '.') if (a === '.')
return process.cwd(); return process.cwd();
@ -73,7 +78,6 @@ const yargsOptions = {
'open', 'open',
'config-dialog', 'config-dialog',
'config-auth', 'config-auth',
'config-port',
'console', 'console',
'sync-console-path', 'sync-console-path',
'contact', 'contact',
@ -115,7 +119,7 @@ const yargsOptions = {
'columns': env.parse('columns') || config('columns') || '', 'columns': env.parse('columns') || config('columns') || '',
'theme': env.parse('theme') || config('theme') || '', 'theme': env.parse('theme') || config('theme') || '',
'vim': choose(env.bool('vim'), config('vim')), 'vim': choose(env.bool('vim'), config('vim')),
'log': choose(env.bool('log'), config('log')), 'log': config('log'),
'import-url': env.parse('import_url') || config('importUrl'), 'import-url': env.parse('import_url') || config('importUrl'),
'import-listen': choose(env.bool('import_listen'), config('importListen')), 'import-listen': choose(env.bool('import_listen'), config('importListen')),
@ -128,7 +132,6 @@ const yargsOptions = {
'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')), 'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')),
'config-dialog': choose(env.bool('config_dialog'), config('configDialog')), 'config-dialog': choose(env.bool('config_dialog'), config('configDialog')),
'config-auth': choose(env.bool('config_auth'), config('configAuth')), 'config-auth': choose(env.bool('config_auth'), config('configAuth')),
'config-port': choose(env.bool('config_port'), config('configPort')),
'terminal-path': env.parse('terminal_path') || config('terminalPath'), 'terminal-path': env.parse('terminal_path') || config('terminalPath'),
'terminal-command': env.parse('terminal_command') || config('terminalCommand'), 'terminal-command': env.parse('terminal_command') || config('terminalCommand'),
'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')), 'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')),
@ -165,7 +168,7 @@ else
main(); main();
async function main() { async function main() {
const {validateArgs} = await import('@putout/cli-validate-args'); const {validateArgs} = await simport('@putout/cli-validate-args');
const error = await validateArgs(args, [ const error = await validateArgs(args, [
...yargsOptions.boolean, ...yargsOptions.boolean,
@ -176,7 +179,7 @@ async function main() {
return exit(error); return exit(error);
if (args.repl) if (args.repl)
await repl(); repl();
validate.columns(args.columns); validate.columns(args.columns);
validate.theme(args.theme); validate.theme(args.theme);
@ -211,7 +214,6 @@ async function main() {
config('oneFilePanel', args.oneFilePanel); config('oneFilePanel', args.oneFilePanel);
config('configDialog', args.configDialog); config('configDialog', args.configDialog);
config('configAuth', args.configAuth); config('configAuth', args.configAuth);
config('configPort', args.configPort);
config('keysPanel', args.keysPanel); config('keysPanel', args.keysPanel);
config('export', args.export); config('export', args.export);
config('exportToken', args.exportToken); config('exportToken', args.exportToken);
@ -246,7 +248,7 @@ async function main() {
if (args.showConfig) if (args.showConfig)
await showConfig(); await showConfig();
const {distributeImport} = await import('../server/distribute/import.js'); const {distributeImport} = await simport('../server/distribute/import.mjs');
const importConfig = promisify(distributeImport); const importConfig = promisify(distributeImport);
await start(options, config); await start(options, config);
@ -264,12 +266,11 @@ function validateRoot(root, config) {
if (root === '/') if (root === '/')
return; return;
if (config('log')) console.log(`root: ${root}`);
console.log(`root: ${root}`);
} }
async function getPassword(password) { async function getPassword(password) {
const {default: criton} = await import('criton'); const criton = await simport('criton');
return criton(password, config('algo')); return criton(password, config('algo'));
} }
@ -278,10 +279,12 @@ function version() {
} }
async function start(options, config) { async function start(options, config) {
const SERVER = `${DIR_SERVER}server.mjs`;
if (!args.server) if (!args.server)
return; return;
const {default: server} = await import('../server/server.js'); const server = await simport(SERVER);
server(options, config); server(options, config);
} }
@ -295,8 +298,8 @@ function port(arg) {
} }
async function showConfig() { async function showConfig() {
const {showConfig} = await import('../server/show-config.js'); const show = await simport('../server/show-config');
const data = showConfig(config('*')); const data = show(config('*'));
console.log(data); console.log(data);
} }
@ -305,13 +308,13 @@ async function readConfig(name) {
if (!name) if (!name)
return; return;
const {default: forEachKey} = await import('for-each-key'); const tryToCatch = await simport('try-to-catch');
const forEachKey = await simport('for-each-key');
const data = await import(name, { const [error, data] = await tryToCatch(simport, name);
with: {
type: 'json', if (error)
}, return exit(error.message);
});
forEachKey(config, data); forEachKey(config, data);
} }
@ -323,8 +326,8 @@ async function help() {
}, },
}); });
const {default: forEachKey} = await import('for-each-key'); const forEachKey = await simport('for-each-key');
const {default: currify} = await import('currify'); const currify = await simport('currify');
const usage = 'Usage: cloudcmd [options]'; const usage = 'Usage: cloudcmd [options]';
const url = Info.homepage; const url = Info.homepage;
@ -336,15 +339,15 @@ async function help() {
console.log('\nGeneral help using Cloud Commander: <%s>', url); console.log('\nGeneral help using Cloud Commander: <%s>', url);
} }
async function repl() { function repl() {
console.log('REPL mode enabled (telnet localhost 1337)'); console.log('REPL mode enabled (telnet localhost 1337)');
await import('../server/repl.js'); require(`${DIR_SERVER}repl`);
} }
async function checkUpdate() { async function checkUpdate() {
const {default: load} = await import('package-json'); const load = await simport('package-json');
const {version} = await load(Info.name, 'latest');
const {version} = await load(Info.name, 'latest');
await showUpdateInfo(version); await showUpdateInfo(version);
} }
@ -352,7 +355,7 @@ async function showUpdateInfo(version) {
if (version === Info.version) if (version === Info.version)
return; return;
const {default: chalk} = await import('chalk'); const chalk = await simport('chalk');
const latestVersion = chalk.green.bold(`v${version}`); const latestVersion = chalk.green.bold(`v${version}`);
const latest = `update available: ${latestVersion}`; const latest = `update available: ${latestVersion}`;

View file

@ -1,26 +0,0 @@
import {spawnSync} from 'node:child_process';
import {test} from 'supertape';
import info from '../package.json' with {
type: 'json',
};
const cliPath = new URL('cloudcmd.js', import.meta.url).pathname;
test('cloudcmd: bin: cli: -h', (t) => {
const {stdout} = spawnSync(cliPath, ['-h'], {
encoding: 'utf8',
});
t.match(stdout, `Options`);
t.end();
});
test('cloudcmd: bin: cli: -v', (t) => {
const {version} = info;
const {stdout} = spawnSync(cliPath, ['-v'], {
encoding: 'utf8',
});
t.match(stdout, `v${version}`);
t.end();
});

View file

@ -3,27 +3,26 @@
import {promisify} from 'node:util'; import {promisify} from 'node:util';
import process from 'node:process'; import process from 'node:process';
import {tryToCatch} from 'try-to-catch'; import {tryToCatch} from 'try-to-catch';
import {createSimport} from 'simport';
import minor from 'minor'; import minor from 'minor';
import _place from 'place'; import _place from 'place';
import {rendy} from 'rendy'; import rendy from 'rendy';
import shortdate from 'shortdate'; import shortdate from 'shortdate';
import Info from '../package.json' with {
type: 'json',
};
const simport = createSimport(import.meta.url);
const place = promisify(_place); const place = promisify(_place);
const Info = await simport('../package.json');
await main(); await main();
async function main() { async function main() {
const history = '## Version history\n\n'; const history = '## Version history\n\n';
const link = '//github.com/coderaiser/cloudcmd/releases/tag/'; const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
const template = '- ' + const template = '- ' +
'*{{ date }}*, ' + '*{{ date }}*, ' +
'**[v{{ version }}]' + '**[v{{ version }}]' +
'(' + '(' + link +
link +
'v{{ version }})**\n'; 'v{{ version }})**\n';
const {version} = Info; const {version} = Info;
@ -36,11 +35,10 @@ async function main() {
await replaceVersion('README.md', version, versionNew); await replaceVersion('README.md', version, versionNew);
await replaceVersion('HELP.md', version, versionNew); await replaceVersion('HELP.md', version, versionNew);
const historyNew = history + const historyNew = history + rendy(template, {
rendy(template, { date: shortdate(),
date: shortdate(), version: versionNew,
version: versionNew, });
});
await replaceVersion('HELP.md', history, historyNew); await replaceVersion('HELP.md', history, historyNew);
} }

View file

@ -1,22 +1,24 @@
import process from 'node:process'; import process from 'node:process';
/* global DOM */
import Emitify from 'emitify'; import Emitify from 'emitify';
import inherits from 'inherits'; import inherits from 'inherits';
import {rendy} from 'rendy'; import rendy from 'rendy';
import load from 'load.js'; import load from 'load.js';
import {tryToCatch} from 'try-to-catch'; import {tryToCatch} from 'try-to-catch';
import {addSlashToEnd} from 'format-io'; import {addSlashToEnd} from 'format-io';
import pascalCase from 'just-pascal-case'; import pascalCase from 'just-pascal-case';
import currify from 'currify'; import currify from 'currify';
import * as Images from './dom/images.mjs';
import {unregisterSW} from './sw/register.mjs';
import {getJsonFromFileTable} from './get-json-from-file-table.mjs';
import {Key} from './key/index.mjs';
import { import {
apiURL, apiURL,
formatMsg, formatMsg,
buildFromJSON, buildFromJSON,
} from '#common/cloudfunc'; } from '../common/cloudfunc.mjs';
import * as Images from '#dom/images'; import {loadModule} from './load-module.mjs';
import {unregisterSW} from './sw/register.js';
import {getJsonFromFileTable} from './get-json-from-file-table.js';
import {Key} from './key/index.js';
import {loadModule} from './load-module.js';
const noJS = (a) => a.replace(/.js$/, ''); const noJS = (a) => a.replace(/.js$/, '');
@ -348,12 +350,7 @@ function CloudCmdProto({DOM, Listeners}) {
'pathLink', 'pathLink',
]; ];
const [error, [ const [error, [file, path, link, pathLink]] = await tryToCatch(Files.get, names);
file,
path,
link,
pathLink,
]] = await tryToCatch(Files.get, names);
if (error) if (error)
return DOM.Dialog.alert(error.responseText); return DOM.Dialog.alert(error.responseText);

View file

@ -1,14 +1,14 @@
import '../css/main.css';
import process from 'node:process'; import process from 'node:process';
import wraptile from 'wraptile'; import wraptile from 'wraptile';
import load from 'load.js'; import load from 'load.js';
import * as Util from '#common/util'; import '../css/main.css';
import * as CloudFunc from '#common/cloudfunc'; import {registerSW, listenSW} from './sw/register.mjs';
import DOM from '#dom'; import {initSortPanel, sortPanel} from './sort.mjs';
import {registerSW, listenSW} from './sw/register.js'; import Util from '../common/util.js';
import {initSortPanel, sortPanel} from './sort.js'; import * as CloudFunc from '../common/cloudfunc.mjs';
import {createCloudCmd} from './client.js'; import DOM from './dom/index.js';
import * as Listeners from './listeners/index.js'; import {createCloudCmd} from './client.mjs';
import * as Listeners from './listeners/index.mjs';
const isDev = process.env.NODE_ENV === 'development'; const isDev = process.env.NODE_ENV === 'development';
@ -36,7 +36,7 @@ async function init(config) {
if (globalThis.CloudCmd.config('menu') === 'aleman') if (globalThis.CloudCmd.config('menu') === 'aleman')
setTimeout(() => { setTimeout(() => {
import('https://esm.sh/@putout/processor-html'); import('https://esm.sh/@putout/processor-html');
import('https://esm.sh/@putout/bundle@5.5'); import('https://esm.sh/@putout/bundle');
}, 100); }, 100);
} }

View file

@ -1,6 +1,6 @@
/* global CloudCmd*/ /* global CloudCmd*/
import * as Storage from '#dom/storage';
import tryToPromiseAll from '../../common/try-to-promise-all.js'; import tryToPromiseAll from '../../common/try-to-promise-all.js';
import Storage from './storage.js';
const CLASS = 'cut-file'; const CLASS = 'cut-file';
const COPY = 'copy'; const COPY = 'copy';

View file

@ -1,83 +0,0 @@
/* global DOM */
const SELECTED_FILE = 'selected-file';
const Cmd = {
getSelectedFiles,
isSelected,
unselectFile,
selectFile,
selectAllFiles,
toggleSelectedFile,
toggleAllSelectedFiles,
};
/**
* selected file check
*
* @param currentFile
*/
export function isSelected(currentFile) {
if (!currentFile)
return false;
return DOM.isContainClass(currentFile, SELECTED_FILE);
}
/**
* select current file
* @param currentFile
*/
export function selectFile(currentFile) {
const current = currentFile || DOM.getCurrentFile();
current.classList.add(SELECTED_FILE);
return Cmd;
}
export function unselectFile(currentFile) {
const current = currentFile || DOM.getCurrentFile();
current.classList.remove(SELECTED_FILE);
return Cmd;
}
export function toggleSelectedFile(currentFile) {
const current = currentFile || DOM.getCurrentFile();
const name = DOM.getCurrentName(current);
if (name === '..')
return Cmd;
current.classList.toggle(SELECTED_FILE);
return Cmd;
}
export function toggleAllSelectedFiles() {
DOM
.getAllFiles()
.map(DOM.toggleSelectedFile);
return Cmd;
}
export function selectAllFiles() {
DOM
.getAllFiles()
.map(DOM.selectFile);
return Cmd;
}
/**
* unified way to get selected files
*
* @currentFile
*/
export function getSelectedFiles() {
const panel = DOM.getPanel();
const selected = DOM.getByClassAll(SELECTED_FILE, panel);
return Array.from(selected);
}

View file

@ -1,8 +1,8 @@
/* global DOM */ /* global DOM */
/* global CloudCmd */ /* global CloudCmd */
import createElement from '@cloudcmd/create-element'; import createElement from '@cloudcmd/create-element';
import {getTitle, FS} from '#common/cloudfunc'; import {encode, decode} from '../../common/entity.js';
import {encode, decode} from '#common/entity'; import {getTitle, FS} from '../../common/cloudfunc.mjs';
let Title; let Title;
@ -77,7 +77,7 @@ const parseHrefAttribute = (prefix, attribute) => {
export const _parseHrefAttribute = parseHrefAttribute; export const _parseHrefAttribute = parseHrefAttribute;
/** /**
* get current directory path * get current direcotory path
*/ */
export const getCurrentDirPath = (panel = DOM.getPanel()) => { export const getCurrentDirPath = (panel = DOM.getPanel()) => {
const path = DOM.getByDataName('js-path', panel); const path = DOM.getByDataName('js-path', panel);
@ -98,7 +98,7 @@ export const getCurrentPath = (currentFile) => {
}; };
/** /**
* get current directory name * get current direcotory name
*/ */
export const getCurrentDirName = () => { export const getCurrentDirName = () => {
const href = DOM const href = DOM
@ -111,7 +111,7 @@ export const getCurrentDirName = () => {
}; };
/** /**
* get current directory path * get current direcotory path
*/ */
export const getParentDirPath = (panel) => { export const getParentDirPath = (panel) => {
const path = DOM.getCurrentDirPath(panel); const path = DOM.getCurrentDirPath(panel);
@ -125,7 +125,7 @@ export const getParentDirPath = (panel) => {
}; };
/** /**
* get not current directory path * get not current direcotory path
*/ */
export const getNotCurrentDirPath = () => { export const getNotCurrentDirPath = () => {
const panel = DOM.getPanel({ const panel = DOM.getPanel({
@ -153,7 +153,7 @@ export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => {
}; };
/** /**
* private function that unset currentFile * private function thet unset currentfile
* *
* @currentFile * @currentFile
*/ */
@ -302,7 +302,6 @@ export const isCurrentIsDir = (currentFile) => {
export const getCurrentType = (currentFile) => { export const getCurrentType = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const el = DOM.getByDataName('js-type', current); const el = DOM.getByDataName('js-type', current);
const type = el.className const type = el.className
.split(' ') .split(' ')
.pop(); .pop();

View file

@ -1,7 +1,7 @@
import {test, stub} from 'supertape'; import {test, stub} from 'supertape';
import {create} from 'auto-globals'; import {create} from 'auto-globals';
import wraptile from 'wraptile'; import wraptile from 'wraptile';
import * as currentFile from './current-file.js'; import * as currentFile from './current-file.mjs';
const id = (a) => a; const id = (a) => a;
@ -19,13 +19,11 @@ test('current-file: setCurrentName: setAttribute', (t) => {
currentFile.setCurrentName('hello', current); currentFile.setCurrentName('hello', current);
t.calledWith(setAttribute, ['data-name', 'js-file-aGVsbG8='], 'should call setAttribute');
globalThis.DOM = DOM; globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.calledWith(setAttribute, [
'data-name',
'js-file-aGVsbG8=',
], 'should call setAttribute');
t.end(); t.end();
}); });
@ -40,13 +38,11 @@ test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
currentFile.setCurrentName('ай', current); currentFile.setCurrentName('ай', current);
t.calledWith(setAttribute, ['data-name', 'js-file-JUQwJUIwJUQwJUI5'], 'should call setAttribute');
globalThis.DOM = DOM; globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.calledWith(setAttribute, [
'data-name',
'js-file-JUQwJUIwJUQwJUI5',
], 'should call setAttribute');
t.end(); t.end();
}); });
@ -74,10 +70,11 @@ test('current-file: emit', (t) => {
currentFile.setCurrentName('hello', current); currentFile.setCurrentName('hello', current);
t.calledWith(emit, ['current-file', current], 'should call emit');
globalThis.DOM = DOM; globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.calledWith(emit, ['current-file', current], 'should call emit');
t.end(); t.end();
}); });
@ -93,12 +90,14 @@ test('current-file: setCurrentName: return', (t) => {
globalThis.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.setCurrentName('hello', current); const result = currentFile.setCurrentName('hello', current);
t.equal(result, link, 'should return link');
globalThis.DOM = DOM; globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.equal(result, link, 'should return link');
t.end(); t.end();
}); });

View file

@ -1,18 +1,26 @@
import {tryToCatch} from 'try-to-catch'; 'use strict';
import * as smalltalk from 'smalltalk';
const {tryToCatch} = require('try-to-catch');
const {
alert,
prompt,
confirm,
progress,
} = require('smalltalk');
const title = 'Cloud Commander'; const title = 'Cloud Commander';
export const alert = (...a) => smalltalk.alert(title, ...a, { module.exports.alert = (...a) => alert(title, ...a, {
cancel: false, cancel: false,
}); });
export const prompt = (...a) => tryToCatch(smalltalk.prompt, title, ...a); module.exports.prompt = (...a) => tryToCatch(prompt, title, ...a);
export const confirm = (...a) => tryToCatch(smalltalk.confirm, title, ...a); module.exports.confirm = (...a) => tryToCatch(confirm, title, ...a);
export const progress = (...a) => smalltalk.progress(title, ...a); module.exports.progress = (...a) => progress(title, ...a);
alert.noFiles = () => { module.exports.alert.noFiles = () => {
return smalltalk.alert(title, 'No files selected!', { return alert(title, 'No files selected!', {
cancel: false, cancel: false,
}); });
}; };

View file

@ -1,10 +1,16 @@
/* global DOM, CloudCmd */ 'use strict';
import philip from 'philip';
import * as Dialog from '#dom/dialog';
import {FS} from '#common/cloudfunc';
import * as Images from '#dom/images';
export const uploadDirectory = (items) => { /* global CloudCmd */
const philip = require('philip');
const Images = require('./images.mjs');
const {FS} = require('../../common/cloudfunc.mjs');
const DOM = require('.');
const Dialog = require('./dialog');
const {getCurrentDirPath: getPathWhenRootEmpty} = DOM;
module.exports = (items) => {
if (items.length) if (items.length)
Images.show('top'); Images.show('top');
@ -12,7 +18,7 @@ export const uploadDirectory = (items) => {
.from(items) .from(items)
.map((item) => item.webkitGetAsEntry()); .map((item) => item.webkitGetAsEntry());
const dirPath = DOM.getCurrentDirPath(); const dirPath = getPathWhenRootEmpty();
const path = dirPath.replace(/\/$/, ''); const path = dirPath.replace(/\/$/, '');
const progress = Dialog.progress('Uploading...'); const progress = Dialog.progress('Uploading...');

View file

@ -1,15 +1,8 @@
import currify from 'currify'; 'use strict';
const DOM = { const currify = require('currify');
show,
hide, const DOM = module.exports;
getByClass,
getByClassAll,
getByDataName,
getById,
getByTag,
isContainClass,
};
/** /**
* check class of element * check class of element
@ -17,7 +10,7 @@ const DOM = {
* @param element * @param element
* @param className * @param className
*/ */
export function isContainClass(element, className) { const isContainClass = (element, className) => {
if (!element) if (!element)
throw Error('element could not be empty!'); throw Error('element could not be empty!');
@ -33,60 +26,58 @@ export function isContainClass(element, className) {
const {classList} = element; const {classList} = element;
return classList.contains(className); return classList.contains(className);
} };
module.exports.isContainClass = isContainClass;
/** /**
* Function search element by tag * Function search element by tag
* @param tag - className * @param tag - className
* @param element - element * @param element - element
*/ */
export function getByTag(tag, element = document) { module.exports.getByTag = (tag, element = document) => {
return element.getElementsByTagName(tag); return element.getElementsByTagName(tag);
} };
/** /**
* Function search element by id * Function search element by id
* @param id * @param Id - id
* @param element
*/ */
export function getById(id, element = document) { module.exports.getById = (id, element = document) => {
return element.querySelector(`#${id}`); return element.querySelector(`#${id}`);
} };
/** /**
* Function search first element by class name * Function search first element by class name
* @param className - className * @param className - className
* @param element - element * @param element - element
*/ */
export function getByClass(className, element = document) { module.exports.getByClass = (className, element = document) => DOM.getByClassAll(className, element)[0];
return DOM.getByClassAll(className, element)[0];
}
export function getByDataName(attribute, element = document) { module.exports.getByDataName = (attribute, element = document) => {
const selector = '[' + 'data-name="' + attribute + '"]'; const selector = '[' + 'data-name="' + attribute + '"]';
return element.querySelector(selector); return element.querySelector(selector);
} };
/** /**
* Function search element by class name * Function search element by class name
* @param className * @param pClass - className
* @param element * @param element - element
*/ */
export function getByClassAll(className, element) { module.exports.getByClassAll = (className, element) => {
return (element || document).getElementsByClassName(className); return (element || document).getElementsByClassName(className);
} };
/** /**
* add class=hidden to element * add class=hidden to element
* *
* @param element * @param element
*/ */
export function hide(element) { module.exports.hide = (element) => {
element.classList.add('hidden'); element.classList.add('hidden');
return DOM; return DOM;
} };
export function show(element) { module.exports.show = (element) => {
element.classList.remove('hidden'); element.classList.remove('hidden');
return DOM; return DOM;
} };

View file

@ -1,16 +1,10 @@
import {test, stub} from 'supertape'; 'use strict';
import {create} from 'auto-globals';
import {tryCatch} from 'try-catch'; const test = require('supertape');
import { const {create} = require('auto-globals');
isContainClass, const {tryCatch} = require('try-catch');
getByTag,
getById, const {isContainClass} = require('./dom-tree');
getByClass,
getByDataName,
getByClassAll,
hide,
show,
} from './dom-tree.js';
test('dom: isContainClass: no element', (t) => { test('dom: isContainClass: no element', (t) => {
const [e] = tryCatch(isContainClass); const [e] = tryCatch(isContainClass);
@ -47,106 +41,3 @@ test('dom: isContainClass: contains: array', (t) => {
t.calledWith(contains, [className], 'should call contains'); t.calledWith(contains, [className], 'should call contains');
t.end(); t.end();
}); });
test('dom: getByTag', (t) => {
const getElementsByTagName = stub();
const element = {
getElementsByTagName,
};
getByTag('div', element);
t.calledWith(getElementsByTagName, ['div'], 'should call getElementsByTagName');
t.end();
});
test('dom: getById', (t) => {
const querySelector = stub();
const element = {
querySelector,
};
getById('my-id', element);
t.calledWith(querySelector, ['#my-id'], 'should call querySelector with id selector');
t.end();
});
test('dom: getByClassAll', (t) => {
const getElementsByClassName = stub();
const element = {
getElementsByClassName,
};
getByClassAll('my-class', element);
t.calledWith(getElementsByClassName, ['my-class'], 'should call getElementsByClassName');
t.end();
});
test('dom: getByClass: calls getByClassAll', (t) => {
const element = {
getElementsByClassName: stub().returns(['first']),
};
const result = getByClass('my-class', element);
t.equal(result, 'first', 'should return first element from class list');
t.end();
});
test('dom: getByDataName', (t) => {
const querySelector = stub();
const element = {
querySelector,
};
getByDataName('hello', element);
t.calledWith(querySelector, ['[data-name="hello"]'], 'should call querySelector with data-name selector');
t.end();
});
test('dom: hide', (t) => {
const add = stub();
const element = {
classList: {
add,
},
};
hide(element);
t.calledWith(add, ['hidden'], 'should add hidden class');
t.end();
});
test('dom: show', (t) => {
const remove = stub();
const element = {
classList: {
remove,
},
};
show(element);
t.calledWith(remove, ['hidden'], 'should remove hidden class');
t.end();
});
test('dom: getByClassAll: without element uses document', (t) => {
const getElementsByClassName = stub();
const prevDocument = globalThis.document;
globalThis.document = {
getElementsByClassName,
};
getByClassAll('my-class');
globalThis.document = prevDocument;
t.calledWith(getElementsByClassName, ['my-class'], 'should fallback to document when no element');
t.end();
});

View file

@ -1,6 +1,8 @@
'use strict';
let list = []; let list = [];
export const add = (el, name, fn) => { module.exports.add = (el, name, fn) => {
list.push([ list.push([
el, el,
name, name,
@ -8,8 +10,8 @@ export const add = (el, name, fn) => {
]); ]);
}; };
export const clear = () => { module.exports.clear = () => {
list = []; list = [];
}; };
export const get = () => list; module.exports.get = () => list;

View file

@ -1,5 +1,7 @@
import {test} from 'supertape'; 'use strict';
import * as eventStore from './event-store.js';
const test = require('supertape');
const eventStore = require('./event-store');
test('event-store: get', (t) => { test('event-store: get', (t) => {
const el = {}; const el = {};

View file

@ -1,5 +1,5 @@
import itype from 'itype'; import itype from 'itype';
import * as EventStore from './event-store.js'; import EventStore from './event-store.js';
/** /**
* safe add event listener * safe add event listener

View file

@ -1,8 +1,11 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
import itype from 'itype'; const itype = require('itype');
import {promisify} from 'es6-promisify'; const {promisify} = require('es6-promisify');
import * as load from '#dom/load';
import * as RESTful from '#dom/rest'; const load = require('./load');
const RESTful = require('./rest');
const Promises = {}; const Promises = {};
const FILES_JSON = 'config|modules'; const FILES_JSON = 'config|modules';
@ -13,7 +16,7 @@ const DIR_HTML_FS = `${DIR_HTML}fs/`;
const DIR_JSON = '/json/'; const DIR_JSON = '/json/';
const timeout = getTimeoutOnce(2000); const timeout = getTimeoutOnce(2000);
export const get = getFile; module.exports.get = getFile;
function getFile(name) { function getFile(name) {
const type = itype(name); const type = itype(name);

View file

@ -102,13 +102,13 @@ export function show(position, panel) {
/** /**
* hide load image * hide load image
*/ */
export function hide() { export const hide = () => {
const element = Images.get(); const element = Images.get();
DOM.hide(element); DOM.hide(element);
return Images; return Images;
} };
export const setProgress = (value, title) => { export const setProgress = (value, title) => {
const DATA = 'data-progress'; const DATA = 'data-progress';

View file

@ -1,121 +1,70 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
import * as load from '#dom/load'; const Util = require('../../common/util');
import * as Files from '#dom/files';
import * as Dialog from '#dom/dialog';
import * as Events from '#dom/events';
import {getExt} from '#common/util';
import * as Storage from '#dom/storage';
import * as RESTful from '#dom/rest';
import * as Images from '#dom/images';
import renameCurrent from './operations/rename-current.js';
import * as CurrentFile from './current-file.js';
import * as DOMTree from './dom-tree.js';
import * as Cmd from './cmd.js';
import * as IO from './io/index.js';
import {uploadDirectory} from './directory.js';
import * as Buffer from './buffer.js';
import {loadRemote as _loadRemote} from './load-remote.js';
import {selectByPattern} from './select-by-pattern.js';
const {assign} = Object; const Images = require('./images.mjs');
const RESTful = require('./rest');
const Storage = require('./storage');
const renameCurrent = require('./operations/rename-current');
const CurrentFile = require('./current-file.mjs');
const DOMTree = require('./dom-tree');
const Cmd = module.exports;
const DOM = { const DOM = {
getCurrentDirName,
getNotCurrentDirPath,
getParentDirPath,
loadRemote,
loadSocket,
promptNewDir,
promptNewFile,
unselectFiles,
getActiveFiles,
getCurrentDate,
getCurrentSize,
loadCurrentSize,
loadCurrentHash,
setCurrentSize,
getCurrentMode,
getCurrentOwner,
getCurrentData,
getRefreshButton,
getAllFiles,
expandSelection,
shrinkSelection,
setHistory,
getCurrentLink,
getFilenames,
checkStorageHash,
saveDataToStorage,
getFM,
getPanelPosition,
getCSSVar,
getPanel,
getFiles,
showPanel,
hidePanel,
remove,
deleteCurrent,
deleteSelected,
renameCurrent,
scrollIntoViewIfNeeded,
scrollByPages,
changePanel,
getPackerExt,
goToDirectory,
duplicatePanel,
swapPanels,
updateCurrentInfo,
};
assign(DOM, {
...DOMTree, ...DOMTree,
...CurrentFile, ...CurrentFile,
...Cmd, ...Cmd,
}); };
export const CurrentInfo = {}; const CurrentInfo = {};
DOM.Images = Images; DOM.Images = Images;
DOM.load = load; DOM.load = require('./load');
DOM.Files = Files; DOM.Files = require('./files');
DOM.RESTful = RESTful; DOM.RESTful = RESTful;
DOM.IO = IO; DOM.IO = require('./io');
DOM.Storage = Storage; DOM.Storage = Storage;
DOM.Dialog = Dialog; DOM.Dialog = require('./dialog');
DOM.CurrentInfo = CurrentInfo; DOM.CurrentInfo = CurrentInfo;
export default DOM; module.exports = DOM;
DOM.uploadDirectory = uploadDirectory; DOM.uploadDirectory = require('./directory');
DOM.Buffer = Buffer; DOM.Buffer = require('./buffer.mjs');
DOM.Events = Events; DOM.Events = require('#dom/events');
const loadRemote = require('./load-remote');
const selectByPattern = require('./select-by-pattern');
const isString = (a) => typeof a === 'string'; const isString = (a) => typeof a === 'string';
const SELECTED_FILE = 'selected-file';
const TabPanel = { const TabPanel = {
'js-left': null, 'js-left': null,
'js-right': null, 'js-right': null,
}; };
export function loadRemote(name, options, callback) { module.exports.loadRemote = (name, options, callback) => {
_loadRemote(name, options, callback); loadRemote(name, options, callback);
return DOM; return DOM;
} };
export function loadSocket(callback) { module.exports.loadSocket = (callback) => {
DOM.loadRemote('socket', { DOM.loadRemote('socket', {
name: 'io', name: 'io',
}, callback); }, callback);
return DOM; return DOM;
} };
/** /**
* create new folder * create new folder
* *
*/ */
export async function promptNewDir() { module.exports.promptNewDir = async function() {
await promptNew('directory'); await promptNew('directory');
} };
/** /**
* create new file * create new file
@ -123,9 +72,9 @@ export async function promptNewDir() {
* @typeName * @typeName
* @type * @type
*/ */
export async function promptNewFile() { module.exports.promptNewFile = async () => {
await promptNew('file'); await promptNew('file');
} };
async function promptNew(typeName) { async function promptNew(typeName) {
const {Dialog} = DOM; const {Dialog} = DOM;
@ -160,9 +109,9 @@ async function promptNew(typeName) {
} }
/** /**
* get current directory name * get current direcotory name
*/ */
export function getCurrentDirName() { module.exports.getCurrentDirName = () => {
const href = DOM const href = DOM
.getCurrentDirPath() .getCurrentDirPath()
.replace(/\/$/, ''); .replace(/\/$/, '');
@ -170,12 +119,12 @@ export function getCurrentDirName() {
const substr = href.substr(href, href.lastIndexOf('/')); const substr = href.substr(href, href.lastIndexOf('/'));
return href.replace(`${substr}/`, '') || '/'; return href.replace(`${substr}/`, '') || '/';
} };
/** /**
* get current directory path * get current direcotory path
*/ */
export function getParentDirPath(panel) { module.exports.getParentDirPath = (panel) => {
const path = DOM.getCurrentDirPath(panel); const path = DOM.getCurrentDirPath(panel);
const dirName = DOM.getCurrentDirName() + '/'; const dirName = DOM.getCurrentDirName() + '/';
const index = path.lastIndexOf(dirName); const index = path.lastIndexOf(dirName);
@ -184,36 +133,48 @@ export function getParentDirPath(panel) {
return path.slice(0, index); return path.slice(0, index);
return path; return path;
} };
/** /**
* get not current directory path * get not current direcotory path
*/ */
export function getNotCurrentDirPath() { module.exports.getNotCurrentDirPath = () => {
const panel = DOM.getPanel({ const panel = DOM.getPanel({
active: false, active: false,
}); });
return DOM.getCurrentDirPath(panel); return DOM.getCurrentDirPath(panel);
} };
/**
* unified way to get selected files
*
* @currentFile
*/
module.exports.getSelectedFiles = () => {
const panel = DOM.getPanel();
const selected = DOM.getByClassAll(SELECTED_FILE, panel);
return Array.from(selected);
};
/* /*
* unselect all files * unselect all files
*/ */
export function unselectFiles(files) { module.exports.unselectFiles = (files) => {
files = files || DOM.getSelectedFiles(); files = files || DOM.getSelectedFiles();
Array Array
.from(files) .from(files)
.forEach(DOM.toggleSelectedFile); .forEach(DOM.toggleSelectedFile);
} };
/** /**
* get all selected files or current when none selected * get all selected files or current when none selected
* *
* @currentFile * @currentFile
*/ */
export function getActiveFiles() { module.exports.getActiveFiles = () => {
const current = DOM.getCurrentFile(); const current = DOM.getCurrentFile();
const files = DOM.getSelectedFiles(); const files = DOM.getSelectedFiles();
const name = DOM.getCurrentName(current); const name = DOM.getCurrentName(current);
@ -222,19 +183,19 @@ export function getActiveFiles() {
return [current]; return [current];
return files; return files;
} };
export function getCurrentDate(currentFile) { module.exports.getCurrentDate = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
return DOM.getByDataName('js-date', current).textContent; return DOM.getByDataName('js-date', current).textContent;
} };
/** /**
* get size * get size
* @currentFile * @currentFile
*/ */
export function getCurrentSize(currentFile) { module.exports.getCurrentSize = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
/* если это папка - возвращаем слово dir вместо размера*/ /* если это папка - возвращаем слово dir вместо размера*/
@ -244,13 +205,13 @@ export function getCurrentSize(currentFile) {
.replace(/^<|>$/g, ''); .replace(/^<|>$/g, '');
return size; return size;
} };
/** /**
* get size * get size
* @currentFile * @currentFile
*/ */
export async function loadCurrentSize(currentFile) { module.exports.loadCurrentSize = async (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const query = '?size'; const query = '?size';
const link = DOM.getCurrentPath(current); const link = DOM.getCurrentPath(current);
@ -266,14 +227,14 @@ export async function loadCurrentSize(currentFile) {
Images.hide(); Images.hide();
return current; return current;
} };
/** /**
* load hash * load hash
* @callback * @callback
* @currentFile * @currentFile
*/ */
export async function loadCurrentHash(currentFile) { module.exports.loadCurrentHash = async (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const query = '?hash'; const query = '?hash';
const link = DOM.getCurrentPath(current); const link = DOM.getCurrentPath(current);
@ -281,45 +242,45 @@ export async function loadCurrentHash(currentFile) {
const [, data] = await RESTful.read(link + query); const [, data] = await RESTful.read(link + query);
return data; return data;
} };
/** /**
* set size * set size
* @currentFile * @currentFile
*/ */
export function setCurrentSize(size, currentFile) { module.exports.setCurrentSize = (size, currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const sizeElement = DOM.getByDataName('js-size', current); const sizeElement = DOM.getByDataName('js-size', current);
sizeElement.textContent = size; sizeElement.textContent = size;
} };
/** /**
* @currentFile * @currentFile
*/ */
export function getCurrentMode(currentFile) { module.exports.getCurrentMode = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const mode = DOM.getByDataName('js-mode', current); const mode = DOM.getByDataName('js-mode', current);
return mode.textContent; return mode.textContent;
} };
/** /**
* @currentFile * @currentFile
*/ */
export function getCurrentOwner(currentFile) { module.exports.getCurrentOwner = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const owner = DOM.getByDataName('js-owner', current); const owner = DOM.getByDataName('js-owner', current);
return owner.textContent; return owner.textContent;
} };
/** /**
* unified way to get current file content * unified way to get current file content
* *
* @param currentFile * @param currentFile
*/ */
export async function getCurrentData(currentFile) { module.exports.getCurrentData = async (currentFile) => {
const {Dialog} = DOM; const {Dialog} = DOM;
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
@ -361,16 +322,64 @@ export async function getCurrentData(currentFile) {
await DOM.saveDataToStorage(path, data, hashNew); await DOM.saveDataToStorage(path, data, hashNew);
return [null, data]; return [null, data];
} };
/** /**
* unified way to get RefreshButton * unified way to get RefreshButton
*/ */
export function getRefreshButton(panel = DOM.getPanel()) { module.exports.getRefreshButton = (panel = DOM.getPanel()) => {
return DOM.getByDataName('js-refresh', panel); return DOM.getByDataName('js-refresh', panel);
} };
export function getAllFiles() { /**
* select current file
* @param currentFile
*/
module.exports.selectFile = (currentFile) => {
const current = currentFile || DOM.getCurrentFile();
current.classList.add(SELECTED_FILE);
return Cmd;
};
module.exports.unselectFile = (currentFile) => {
const current = currentFile || DOM.getCurrentFile();
current.classList.remove(SELECTED_FILE);
return Cmd;
};
module.exports.toggleSelectedFile = (currentFile) => {
const current = currentFile || DOM.getCurrentFile();
const name = DOM.getCurrentName(current);
if (name === '..')
return Cmd;
current.classList.toggle(SELECTED_FILE);
return Cmd;
};
module.exports.toggleAllSelectedFiles = () => {
DOM
.getAllFiles()
.map(DOM.toggleSelectedFile);
return Cmd;
};
module.exports.selectAllFiles = () => {
DOM
.getAllFiles()
.map(DOM.selectFile);
return Cmd;
};
module.exports.getAllFiles = () => {
const panel = DOM.getPanel(); const panel = DOM.getPanel();
const files = DOM.getFiles(panel); const files = DOM.getFiles(panel);
const name = DOM.getCurrentName(files[0]); const name = DOM.getCurrentName(files[0]);
@ -381,32 +390,32 @@ export function getAllFiles() {
return Array return Array
.from(files) .from(files)
.slice(i); .slice(i);
} };
/** /**
* open dialog with expand selection * open dialog with expand selection
*/ */
export async function expandSelection() { module.exports.expandSelection = () => {
const msg = 'expand'; const msg = 'expand';
const {files} = CurrentInfo; const {files} = CurrentInfo;
await selectByPattern(msg, files); selectByPattern(msg, files);
} };
/** /**
* open dialog with shrink selection * open dialog with shrink selection
*/ */
export async function shrinkSelection() { module.exports.shrinkSelection = () => {
const msg = 'shrink'; const msg = 'shrink';
const {files} = CurrentInfo; const {files} = CurrentInfo;
await selectByPattern(msg, files); selectByPattern(msg, files);
} };
/** /**
* setting history wrapper * setting history wrapper
*/ */
export function setHistory(data, title, url) { module.exports.setHistory = (data, title, url) => {
const ret = globalThis.history; const ret = globalThis.history;
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
@ -416,21 +425,33 @@ export function setHistory(data, title, url) {
history.pushState(data, title, url); history.pushState(data, title, url);
return ret; return ret;
} };
/**
* selected file check
*
* @param currentFile
*/
module.exports.isSelected = (selected) => {
if (!selected)
return false;
return DOM.isContainClass(selected, SELECTED_FILE);
};
/** /**
* get link from current (or param) file * get link from current (or param) file
* *
* @param currentFile - current file by default * @param currentFile - current file by default
*/ */
export function getCurrentLink(currentFile) { module.exports.getCurrentLink = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const link = DOM.getByTag('a', current); const link = DOM.getByTag('a', current);
return link[0]; return link[0];
} };
export function getFilenames(files) { module.exports.getFilenames = (files) => {
if (!files) if (!files)
throw Error('AllFiles could not be empty'); throw Error('AllFiles could not be empty');
@ -442,13 +463,17 @@ export function getFilenames(files) {
if (name === '..') if (name === '..')
allFiles.shift(); allFiles.shift();
return allFiles.map(DOM.getCurrentName); const names = allFiles.map((current) => {
} return DOM.getCurrentName(current);
});
return names;
};
/** /**
* check storage hash * check storage hash
*/ */
export async function checkStorageHash(name) { module.exports.checkStorageHash = async (name) => {
const nameHash = `${name}-hash`; const nameHash = `${name}-hash`;
if (!isString(name)) if (!isString(name))
@ -460,7 +485,7 @@ export async function checkStorageHash(name) {
]); ]);
return [loadHash, storeHash]; return [loadHash, storeHash];
} };
/** /**
* save data to storage * save data to storage
@ -468,8 +493,9 @@ export async function checkStorageHash(name) {
* @param name * @param name
* @param data * @param data
* @param hash * @param hash
* @param callback
*/ */
export async function saveDataToStorage(name, data, hash) { module.exports.saveDataToStorage = async (name, data, hash) => {
const isDir = DOM.isCurrentIsDir(); const isDir = DOM.isCurrentIsDir();
if (isDir) if (isDir)
@ -484,28 +510,25 @@ export async function saveDataToStorage(name, data, hash) {
await Storage.set(nameData, data); await Storage.set(nameData, data);
return hash; return hash;
} };
export function getFM() { module.exports.getFM = () => DOM.getPanel().parentElement;
const {parentElement} = DOM.getPanel();
return parentElement;
}
export function getPanelPosition(panel) { module.exports.getPanelPosition = (panel) => {
panel = panel || DOM.getPanel(); panel = panel || DOM.getPanel();
return panel.dataset.name.replace('js-', ''); return panel.dataset.name.replace('js-', '');
} };
export function getCSSVar(name, {body = document.body} = {}) { module.exports.getCSSVar = (name, {body = document.body} = {}) => {
const bodyStyle = getComputedStyle(body); const bodyStyle = getComputedStyle(body);
return bodyStyle.getPropertyValue(`--${name}`); return bodyStyle.getPropertyValue(`--${name}`);
} };
/** function getting panel active, or passive /** function getting panel active, or passive
* @param options = {active: true} * @param options = {active: true}
*/ */
export function getPanel(options) { module.exports.getPanel = (options) => {
let files; let files;
let panel; let panel;
let isLeft; let isLeft;
@ -538,17 +561,17 @@ export function getPanel(options) {
throw Error('can not find Active Panel!'); throw Error('can not find Active Panel!');
return panel; return panel;
} };
export function getFiles(element) { module.exports.getFiles = (element) => {
const files = DOM.getByDataName('js-files', element); const files = DOM.getByDataName('js-files', element);
return files.children || []; return files.children || [];
} };
/** /**
* shows panel right or left (or active) * shows panel right or left (or active)
*/ */
export function showPanel(active) { module.exports.showPanel = (active) => {
const panel = DOM.getPanel({ const panel = DOM.getPanel({
active, active,
}); });
@ -559,12 +582,12 @@ export function showPanel(active) {
DOM.show(panel); DOM.show(panel);
return true; return true;
} };
/** /**
* hides panel right or left (or active) * hides panel right or left (or active)
*/ */
export function hidePanel(active) { module.exports.hidePanel = (active) => {
const panel = DOM.getPanel({ const panel = DOM.getPanel({
active, active,
}); });
@ -573,27 +596,27 @@ export function hidePanel(active) {
return false; return false;
return DOM.hide(panel); return DOM.hide(panel);
} };
/** /**
* remove child of element * remove child of element
* @param child * @param pChild
* @param element * @param element
*/ */
export function remove(child, element) { module.exports.remove = (child, element) => {
const parent = element || document.body; const parent = element || document.body;
parent.removeChild(child); parent.removeChild(child);
return DOM; return DOM;
} };
/** /**
* remove current file from file table * remove current file from file table
* @param current * @param current
* *
*/ */
export function deleteCurrent(current) { module.exports.deleteCurrent = (current) => {
if (!current) if (!current)
DOM.getCurrentFile(); DOM.getCurrentFile();
@ -607,44 +630,51 @@ export function deleteCurrent(current) {
DOM.setCurrentFile(next || prev); DOM.setCurrentFile(next || prev);
parent.removeChild(current); parent.removeChild(current);
} }
} };
/** /**
* remove selected files from file table * remove selected files from file table
* @Selected * @Selected
*/ */
export function deleteSelected(selected) { module.exports.deleteSelected = (selected) => {
selected = selected || DOM.getSelectedFiles(); selected = selected || DOM.getSelectedFiles();
if (!selected) if (!selected)
return; return;
selected.map(DOM.deleteCurrent); selected.map(DOM.deleteCurrent);
} };
/** /**
* rename current file * rename current file
* *
* @currentFile * @currentFile
*/ */
export function scrollIntoViewIfNeeded(element, center = false) { module.exports.renameCurrent = renameCurrent;
/**
* unified way to scrollIntoViewIfNeeded
* (native suporte by webkit only)
* @param element
* @param center - to scroll as small as possible param should be false
*/
module.exports.scrollIntoViewIfNeeded = (element, center = false) => {
if (!element || !element.scrollIntoViewIfNeeded) if (!element || !element.scrollIntoViewIfNeeded)
return; return;
element.scrollIntoViewIfNeeded(center); element.scrollIntoViewIfNeeded(center);
} };
/* scroll on one page */ /* scroll on one page */
export function scrollByPages(element, pPages) { module.exports.scrollByPages = (element, pPages) => {
const ret = element?.scrollByPages && pPages; const ret = element?.scrollByPages && pPages;
if (ret) if (ret)
element.scrollByPages(pPages); element.scrollByPages(pPages);
return ret; return ret;
} };
export function changePanel() { module.exports.changePanel = () => {
const Info = CurrentInfo; const Info = CurrentInfo;
let panel = DOM.getPanel(); let panel = DOM.getPanel();
@ -690,21 +720,18 @@ export function changePanel() {
CloudCmd.emit('active-dir', Info.dirPath); CloudCmd.emit('active-dir', Info.dirPath);
return DOM; return DOM;
} };
export function getPackerExt(type) { module.exports.getPackerExt = (type) => {
if (type === 'zip') if (type === 'zip')
return '.zip'; return '.zip';
return '.tar.gz'; return '.tar.gz';
} };
export async function goToDirectory(overrides = {}) { module.exports.goToDirectory = async (overrides = {}) => {
const {Dialog} = DOM; const {Dialog} = DOM;
const { const {prompt = Dialog.prompt, changeDir = CloudCmd.changeDir} = overrides;
prompt = Dialog.prompt,
changeDir = CloudCmd.changeDir,
} = overrides;
const msg = 'Go to directory:'; const msg = 'Go to directory:';
const {dirPath} = CurrentInfo; const {dirPath} = CurrentInfo;
@ -715,9 +742,9 @@ export async function goToDirectory(overrides = {}) {
return; return;
await changeDir(path); await changeDir(path);
} };
export async function duplicatePanel() { module.exports.duplicatePanel = async () => {
const Info = CurrentInfo; const Info = CurrentInfo;
const {isDir} = Info; const {isDir} = Info;
const panel = Info.panelPassive; const panel = Info.panelPassive;
@ -736,9 +763,9 @@ export async function duplicatePanel() {
panel, panel,
noCurrent, noCurrent,
}); });
} };
export async function swapPanels() { module.exports.swapPanels = async () => {
const Info = CurrentInfo; const Info = CurrentInfo;
const { const {
panel, panel,
@ -769,9 +796,11 @@ export async function swapPanels() {
const el = Info.files[currentIndex]; const el = Info.files[currentIndex];
DOM.setCurrentFile(el); DOM.setCurrentFile(el);
} };
export function updateCurrentInfo(currentFile) { module.exports.CurrentInfo = CurrentInfo;
module.exports.updateCurrentInfo = (currentFile) => {
const info = DOM.CurrentInfo; const info = DOM.CurrentInfo;
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const files = current.parentElement; const files = current.parentElement;
@ -787,7 +816,7 @@ export function updateCurrentInfo(currentFile) {
info.dirPath = DOM.getCurrentDirPath(); info.dirPath = DOM.getCurrentDirPath();
info.parentDirPath = DOM.getParentDirPath(); info.parentDirPath = DOM.getParentDirPath();
info.element = current; info.element = current;
info.ext = getExt(name); info.ext = Util.getExt(name);
info.files = Array.from(files.children); info.files = Array.from(files.children);
info.filesPassive = Array.from(filesPassive); info.filesPassive = Array.from(filesPassive);
info.first = files.firstChild; info.first = files.firstChild;
@ -808,4 +837,4 @@ export function updateCurrentInfo(currentFile) {
.name .name
.replace('js-', ''); .replace('js-', '');
info.isOnePanel = info.panel.getAttribute('data-name') === info.panelPassive.getAttribute('data-name'); info.isOnePanel = info.panel.getAttribute('data-name') === info.panelPassive.getAttribute('data-name');
} };

View file

@ -1,5 +1,9 @@
import {test, stub} from 'supertape'; 'use strict';
import {getCSSVar, goToDirectory} from '#dom';
require('css-modules-require-hook/preset');
const {test, stub} = require('supertape');
const {getCSSVar, goToDirectory} = require('./index');
globalThis.CloudCmd = {}; globalThis.CloudCmd = {};

View file

@ -1,35 +1,24 @@
import {FS} from '#common/cloudfunc'; 'use strict';
import {sendRequest as _sendRequest} from './send-request.js';
const {assign} = Object; const {FS} = require('../../../common/cloudfunc.mjs');
const _sendRequest = require('./send-request');
const imgPosition = { const imgPosition = {
top: true, top: true,
}; };
export const remove = async (url, data, overrides = {}) => { module.exports.delete = async (url, data) => {
const { return await _sendRequest({
sendRequest = _sendRequest,
} = overrides;
const request = {
method: 'DELETE', method: 'DELETE',
url: FS + url, url: FS + url,
data,
imgPosition: { imgPosition: {
top: Boolean(data), top: Boolean(data),
}, },
}; });
if (data)
assign(request, {
data,
url: `${request.url}?files`,
});
return await sendRequest(request);
}; };
export const patch = async (url, data) => { module.exports.patch = async (url, data) => {
return await _sendRequest({ return await _sendRequest({
method: 'PATCH', method: 'PATCH',
url: FS + url, url: FS + url,
@ -38,7 +27,7 @@ export const patch = async (url, data) => {
}); });
}; };
export const write = async (url, data) => { module.exports.write = async (url, data) => {
return await _sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: FS + url, url: FS + url,
@ -47,7 +36,7 @@ export const write = async (url, data) => {
}); });
}; };
export const createDirectory = async (url, overrides = {}) => { module.exports.createDirectory = async (url, overrides = {}) => {
const { const {
sendRequest = _sendRequest, sendRequest = _sendRequest,
} = overrides; } = overrides;
@ -59,7 +48,7 @@ export const createDirectory = async (url, overrides = {}) => {
}); });
}; };
export const read = async (url, dataType = 'text') => { module.exports.read = async (url, dataType = 'text') => {
const notLog = !url.includes('?'); const notLog = !url.includes('?');
return await _sendRequest({ return await _sendRequest({
@ -70,7 +59,7 @@ export const read = async (url, dataType = 'text') => {
}); });
}; };
export const copy = async (from, to, names) => { module.exports.copy = async (from, to, names) => {
return await _sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/copy', url: '/copy',
@ -83,7 +72,7 @@ export const copy = async (from, to, names) => {
}); });
}; };
export const pack = async (data) => { module.exports.pack = async (data) => {
return await _sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/pack', url: '/pack',
@ -91,7 +80,7 @@ export const pack = async (data) => {
}); });
}; };
export const extract = async (data) => { module.exports.extract = async (data) => {
return await _sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/extract', url: '/extract',
@ -99,7 +88,7 @@ export const extract = async (data) => {
}); });
}; };
export const move = async (from, to, names) => { module.exports.move = async (from, to, names) => {
return await _sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/move', url: '/move',
@ -112,7 +101,7 @@ export const move = async (from, to, names) => {
}); });
}; };
export const rename = async (from, to) => { module.exports.rename = async (from, to) => {
return await _sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/rename', url: '/rename',
@ -124,7 +113,7 @@ export const rename = async (from, to) => {
}); });
}; };
export const Config = { module.exports.Config = {
read: async () => { read: async () => {
return await _sendRequest({ return await _sendRequest({
method: 'GET', method: 'GET',
@ -144,7 +133,7 @@ export const Config = {
}, },
}; };
export const Markdown = { module.exports.Markdown = {
read: async (url) => { read: async (url) => {
return await _sendRequest({ return await _sendRequest({
method: 'GET', method: 'GET',

View file

@ -1,5 +1,7 @@
import {test, stub} from 'supertape'; 'use strict';
import * as io from './index.js';
const {test, stub} = require('supertape');
const io = require('.');
test('client: dom: io', (t) => { test('client: dom: io', (t) => {
const sendRequest = stub(); const sendRequest = stub();
@ -19,43 +21,3 @@ test('client: dom: io', (t) => {
t.calledWith(sendRequest, [expected]); t.calledWith(sendRequest, [expected]);
t.end(); t.end();
}); });
test('client: dom: io: remove: no files', async (t) => {
const sendRequest = stub();
await io.remove('/hello', null, {
sendRequest,
});
const expected = {
imgPosition: {
top: false,
},
method: 'DELETE',
url: '/fs/hello',
};
t.calledWith(sendRequest, [expected]);
t.end();
});
test('client: dom: io: remove: files', async (t) => {
const sendRequest = stub();
const files = ['world'];
await io.remove('/hello', files, {
sendRequest,
});
const expected = {
imgPosition: {
top: true,
},
data: ['world'],
method: 'DELETE',
url: '/fs/hello?files',
};
t.calledWith(sendRequest, [expected]);
t.end();
});

View file

@ -1,9 +1,12 @@
/* global CloudCmd */ 'use strict';
import {promisify} from 'es6-promisify';
import * as Images from '#dom/images';
import * as load from '#dom/load';
export const sendRequest = promisify((params, callback) => { /* global CloudCmd */
const {promisify} = require('es6-promisify');
const Images = require('../images.mjs');
const load = require('../load');
module.exports = promisify((params, callback) => {
const p = params; const p = params;
const {prefixURL} = CloudCmd; const {prefixURL} = CloudCmd;
@ -37,8 +40,7 @@ export const sendRequest = promisify((params, callback) => {
}); });
}); });
export const _replaceHash = replaceHash; module.exports._replaceHash = replaceHash;
function replaceHash(url) { function replaceHash(url) {
/* /*
* if we send ajax request - * if we send ajax request -

View file

@ -1,5 +1,7 @@
import {test} from 'supertape'; 'use strict';
import {_replaceHash} from './send-request.js';
const test = require('supertape');
const {_replaceHash} = require('./send-request');
test('cloudcmd: client: io: replaceHash', (t) => { test('cloudcmd: client: io: replaceHash', (t) => {
const url = '/hello/####world'; const url = '/hello/####world';

View file

@ -1,58 +1,56 @@
/* global CloudCmd */ 'use strict';
import {callbackify} from 'node:util';
import {rendy} from 'rendy';
import itype from 'itype';
import * as load from 'load.js';
import {tryToCatch} from 'try-to-catch';
import {findObjByNameInArr} from '#common/util';
import * as Files from '#dom/files';
export const loadRemote = callbackify(async (name, options) => { /* global CloudCmd */
const rendy = require('rendy');
const itype = require('itype');
const load = require('load.js');
const {tryToCatch} = require('try-to-catch');
const {findObjByNameInArr} = require('../../common/util');
const Files = require('./files');
module.exports = (name, options, callback = options) => {
const {prefix, config} = CloudCmd; const {prefix, config} = CloudCmd;
const o = options; const o = options;
if (o.name && window[o.name]) if (o.name && window[o.name])
return; return callback();
const modules = await Files.get('modules'); Files.get('modules').then(async (modules) => {
const online = config('online') && navigator.onLine;
const online = config('online') && navigator.onLine; const module = findObjByNameInArr(modules.remote, name);
const module = findObjByNameInArr(modules.remote, name);
const isArray = itype.array(module.local);
const {version} = module;
let remoteTmpls;
let local;
if (isArray) {
remoteTmpls = module.remote;
({local} = module);
} else {
remoteTmpls = [module.remote];
local = [module.local];
}
const localURL = [];
for (const url of local) {
localURL.push(prefix + url);
}
const remoteURL = [];
for (const tmpl of remoteTmpls) {
remoteURL.push(rendy(tmpl, {
version,
}));
}
if (online) {
const [e] = await tryToCatch(load.parallel, remoteURL);
if (!e) const isArray = itype.array(module.local);
return; const {version} = module;
}
let remoteTmpls;
await load.parallel(localURL); let local;
});
if (isArray) {
remoteTmpls = module.remote;
({local} = module);
} else {
remoteTmpls = [module.remote];
local = [module.local];
}
const localURL = local.map((url) => prefix + url);
const remoteURL = remoteTmpls.map((tmpl) => {
return rendy(tmpl, {
version,
});
});
if (online) {
const [e] = await tryToCatch(load.parallel, remoteURL);
if (!e)
return callback();
}
const [e] = await tryToCatch(load.parallel, localURL);
callback(e);
});
};

View file

@ -1,16 +1,19 @@
import itype from 'itype'; 'use strict';
import jonny from 'jonny';
import Emitify from 'emitify';
import exec from 'execon';
import * as Images from '#dom/images';
const itype = require('itype');
const jonny = require('jonny');
const Emitify = require('emitify');
const exec = require('execon');
const Images = require('./images.mjs');
module.exports.getIdBySrc = getIdBySrc;
/** /**
* Function gets id by src * Function gets id by src
* @param src * @param src
* *
* Example: http://domain.com/1.js -> 1_js * Example: http://domain.com/1.js -> 1_js
*/ */
export function getIdBySrc(src) { function getIdBySrc(src) {
const isStr = itype.string(src); const isStr = itype.string(src);
if (!isStr) if (!isStr)
@ -34,7 +37,7 @@ export function getIdBySrc(src) {
* *
* @param params * @param params
*/ */
export const ajax = (params) => { module.exports.ajax = (params) => {
const p = params; const p = params;
const isObject = itype.object(p.data); const isObject = itype.object(p.data);
const isArray = itype.array(p.data); const isArray = itype.array(p.data);
@ -91,11 +94,11 @@ export const ajax = (params) => {
xhr.send(data); xhr.send(data);
}; };
export const put = (url, body) => { module.exports.put = (url, body) => {
const emitter = Emitify(); const emitter = Emitify();
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
url = encodeURI(url).replace(/#/g, '#'); url = encodeURI(url).replace(/#/g, '%23');
xhr.open('put', url, true); xhr.open('put', url, true);

View file

@ -1,11 +1,15 @@
/* global CloudCmd */ 'use strict';
import capitalize from 'just-capitalize';
import * as _Dialog from '#dom/dialog';
import * as Storage from '#dom/storage';
import * as RESTful from '#dom/rest';
import * as _currentFile from '../current-file.js';
export default async (current, overrides = {}) => { /* global CloudCmd */
const capitalize = require('just-capitalize');
const _Dialog = require('../dialog');
const Storage = require('../storage');
const RESTful = require('../rest');
const _currentFile = require('../current-file.mjs');
module.exports = async (current, overrides = {}) => {
const { const {
refresh = CloudCmd.refresh, refresh = CloudCmd.refresh,
Dialog = _Dialog, Dialog = _Dialog,

View file

@ -1,5 +1,8 @@
import {test, stub} from 'supertape'; 'use strict';
import renameCurrent from './rename-current.js';
const {test, stub} = require('supertape');
const renameCurrent = require('./rename-current');
test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => { test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
const current = {}; const current = {};

View file

@ -1,8 +1,12 @@
import {tryToCatch} from 'try-to-catch'; 'use strict';
import * as Dialog from '#dom/dialog';
import * as Images from '#dom/images'; const {tryToCatch} = require('try-to-catch');
import {encode} from '#common/entity';
import * as IO from './io/index.js'; const {encode} = require('../../common/entity');
const Images = require('./images.mjs');
const IO = require('./io');
const Dialog = require('./dialog');
const handleError = (promise) => async (...args) => { const handleError = (promise) => async (...args) => {
const [e, data] = await tryToCatch(promise, ...args); const [e, data] = await tryToCatch(promise, ...args);
@ -18,23 +22,23 @@ const handleError = (promise) => async (...args) => {
return [e, data]; return [e, data];
}; };
export const remove = handleError(IO.remove); module.exports.delete = handleError(IO.delete);
export const patch = handleError(IO.patch); module.exports.patch = handleError(IO.patch);
export const write = handleError(IO.write); module.exports.write = handleError(IO.write);
export const createDirectory = handleError(IO.createDirectory); module.exports.createDirectory = handleError(IO.createDirectory);
export const read = handleError(IO.read); module.exports.read = handleError(IO.read);
export const copy = handleError(IO.copy); module.exports.copy = handleError(IO.copy);
export const pack = handleError(IO.pack); module.exports.pack = handleError(IO.pack);
export const extract = handleError(IO.extract); module.exports.extract = handleError(IO.extract);
export const move = handleError(IO.move); module.exports.move = handleError(IO.move);
export const rename = handleError(IO.rename); module.exports.rename = handleError(IO.rename);
export const Config = { module.exports.Config = {
read: handleError(IO.Config.read), read: handleError(IO.Config.read),
write: handleError(IO.Config.write), write: handleError(IO.Config.write),
}; };
export const Markdown = { module.exports.Markdown = {
read: handleError(IO.Markdown.read), read: handleError(IO.Markdown.read),
render: handleError(IO.Markdown.render), render: handleError(IO.Markdown.render),
}; };

View file

@ -1,14 +1,13 @@
import {alert, prompt} from '#dom/dialog'; 'use strict';
import {getRegExp} from '#common/util';
import {getCurrentName} from './current-file.js';
import {
isSelected,
toggleSelectedFile,
} from './cmd.js';
let SelectType = '*.*'; let SelectType = '*.*';
export const selectByPattern = async (msg, files) => { const {getRegExp} = require('../../common/util');
const {alert, prompt} = require('./dialog');
const DOM = require('.');
module.exports = async (msg, files) => {
if (!files) if (!files)
return; return;
@ -24,21 +23,21 @@ export const selectByPattern = async (msg, files) => {
let matches = 0; let matches = 0;
for (const current of files) { for (const current of files) {
const name = getCurrentName(current); const name = DOM.getCurrentName(current);
if (name === '..' || !regExp.test(name)) if (name === '..' || !regExp.test(name))
continue; continue;
++matches; ++matches;
let selected = isSelected(current); let isSelected = DOM.isSelected(current);
const shouldSel = msg === 'expand'; const shouldSel = msg === 'expand';
if (shouldSel) if (shouldSel)
selected = !selected; isSelected = !isSelected;
if (selected) if (isSelected)
toggleSelectedFile(current); DOM.toggleSelectedFile(current);
} }
if (!matches) if (!matches)

View file

@ -1,26 +1,28 @@
'use strict';
const {parse, stringify} = JSON; const {parse, stringify} = JSON;
export const set = (name, data) => { module.exports.set = async (name, data) => {
localStorage.setItem(name, data); localStorage.setItem(name, data);
}; };
export const setJson = (name, data) => { module.exports.setJson = async (name, data) => {
localStorage.setItem(name, stringify(data)); localStorage.setItem(name, stringify(data));
}; };
export const get = (name) => { module.exports.get = async (name) => {
return localStorage.getItem(name); return localStorage.getItem(name);
}; };
export const getJson = (name) => { module.exports.getJson = async (name) => {
const data = localStorage.getItem(name); const data = localStorage.getItem(name);
return parse(data); return parse(data);
}; };
export const clear = () => { module.exports.clear = () => {
localStorage.clear(); localStorage.clear();
}; };
export const remove = (item) => { module.exports.remove = (item) => {
localStorage.removeItem(item); localStorage.removeItem(item);
}; };

View file

@ -1,5 +1,8 @@
import {test, stub} from 'supertape'; 'use strict';
import * as storage from '#dom/storage';
const {test, stub} = require('supertape');
const storage = require('./storage');
const {stringify} = JSON; const {stringify} = JSON;

View file

@ -1,19 +1,24 @@
/* global CloudCmd */ 'use strict';
import {eachSeries} from 'execon';
import wraptile from 'wraptile';
import * as load from '#dom/load';
import {alert} from '#dom/dialog';
import {FS} from '#common/cloudfunc';
import * as Images from '#dom/images';
import {getCurrentDirPath} from './current-file.js';
/* global CloudCmd */
const {eachSeries} = require('execon');
const wraptile = require('wraptile');
const load = require('./load');
const Images = require('./images.mjs');
const {alert} = require('./dialog');
const {FS} = require('../../common/cloudfunc.mjs');
const {getCurrentDirPath: getPathWhenRootEmpty} = require('.');
const loadFile = wraptile(_loadFile); const loadFile = wraptile(_loadFile);
const onEnd = wraptile(_onEnd); const onEnd = wraptile(_onEnd);
export const uploadFiles = (dir, files) => { module.exports = (dir, files) => {
if (!files) { if (!files) {
files = dir; files = dir;
dir = getCurrentDirPath(); dir = getPathWhenRootEmpty();
} }
const n = files.length; const n = files.length;

View file

@ -1,4 +1,6 @@
export const createBinder = () => { 'use strict';
module.exports.createBinder = () => {
let binded = false; let binded = false;
return { return {

View file

@ -1,28 +0,0 @@
import {test} from 'supertape';
import {createBinder} from './binder.js';
test('client: key: binder: isBind: default', (t) => {
const binder = createBinder();
t.notOk(binder.isBind(), 'should not be bind by default');
t.end();
});
test('client: key: binder: setBind', (t) => {
const binder = createBinder();
binder.setBind();
t.ok(binder.isBind(), 'should be bind');
t.end();
});
test('client: key: binder: unsetBind', (t) => {
const binder = createBinder();
binder.setBind();
binder.unsetBind();
t.notOk(binder.isBind(), 'should not be bind');
t.end();
});

View file

@ -2,8 +2,8 @@
import clipboard from '@cloudcmd/clipboard'; import clipboard from '@cloudcmd/clipboard';
import {fullstore} from 'fullstore'; import {fullstore} from 'fullstore';
import * as Events from '#dom/events'; import * as Events from '#dom/events';
import * as Buffer from '../dom/buffer.js'; import * as Buffer from '../dom/buffer.mjs';
import * as KEY from './key.js'; import * as KEY from './key.mjs';
import _vim from './vim/index.js'; import _vim from './vim/index.js';
import setCurrentByChar from './set-current-by-char.js'; import setCurrentByChar from './set-current-by-char.js';
import {createBinder} from './binder.js'; import {createBinder} from './binder.js';
@ -13,8 +13,8 @@ const Chars = fullstore();
const toggleVim = (keyCode, overrides = {}) => { const toggleVim = (keyCode, overrides = {}) => {
const {_config, config} = overrides; const {_config, config} = overrides;
if (!config('vim') && keyCode === KEY.ESC) if (keyCode === KEY.ESC)
_config('vim', true); _config('vim', !config('vim'));
}; };
const isUndefined = (a) => typeof a === 'undefined'; const isUndefined = (a) => typeof a === 'undefined';
@ -63,7 +63,7 @@ async function listener(event, overrides = {}) {
const {keyCode} = event; const {keyCode} = event;
// strange chrome bug calls listener twice // strange chrome bug calles listener twice
// in second time event misses a lot fields // in second time event misses a lot fields
if (isUndefined(event.altKey)) if (isUndefined(event.altKey))
return; return;
@ -442,14 +442,6 @@ async function _switchKey(event) {
break; break;
case KEY.L:
if (ctrlMeta && shift) {
CloudCmd.logOut();
event.preventDefault();
}
break;
case KEY.M: case KEY.M:
if (ctrlMeta) { if (ctrlMeta) {
if (config('vim')) if (config('vim'))

View file

@ -1,9 +1,15 @@
import autoGlobals from 'auto-globals'; 'use strict';
import supertape from 'supertape';
import {ESC} from './key.js';
import {Key, _listener} from './index.js';
import {getDOM, getCloudCmd} from './vim/globals.fixture.js';
require('css-modules-require-hook/preset');
const autoGlobals = require('auto-globals');
const supertape = require('supertape');
const {ESC} = require('./key.mjs');
const {Key, _listener} = require('./index.mjs');
const {getDOM, getCloudCmd} = require('./vim/globals.fixture');
const test = autoGlobals(supertape); const test = autoGlobals(supertape);
const {stub} = supertape; const {stub} = supertape;
@ -37,7 +43,6 @@ test('cloudcmd: client: key: enable vim', async (t) => {
test('cloudcmd: client: key: disable vim', async (t) => { test('cloudcmd: client: key: disable vim', async (t) => {
const _config = stub(); const _config = stub();
const config = stub(); const config = stub();
const event = { const event = {
keyCode: ESC, keyCode: ESC,
key: 'Escape', key: 'Escape',

View file

@ -22,7 +22,6 @@ export const D = 68;
export const G = 71; export const G = 71;
export const J = 74; export const J = 74;
export const K = 75; export const K = 75;
export const L = 76;
export const M = 77; export const M = 77;
export const O = 79; export const O = 79;
export const P = 80; export const P = 80;

View file

@ -1,11 +1,14 @@
/* global DOM */ /* global DOM */
import {escapeRegExp} from '#common/util';
export default function setCurrentByChar(char, charStore) { 'use strict';
const {escapeRegExp} = require('../../common/util');
module.exports = function setCurrentByChar(char, charStore) {
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
let firstByName; let firstByName;
let skipCount = 0; let skipCount = 0;
let set = false; let setted = false;
let i = 0; let i = 0;
const escapeChar = escapeRegExp(char); const escapeChar = escapeRegExp(char);
@ -33,7 +36,7 @@ export default function setCurrentByChar(char, charStore) {
const byName = DOM.getCurrentByName(name); const byName = DOM.getCurrentByName(name);
if (!skipCount) { if (!skipCount) {
set = true; setted = true;
DOM.setCurrentFile(byName); DOM.setCurrentFile(byName);
return true; return true;
@ -50,8 +53,8 @@ export default function setCurrentByChar(char, charStore) {
.filter(not(isRoot)) .filter(not(isRoot))
.some(setCurrent); .some(setCurrent);
if (!set) { if (!setted) {
DOM.setCurrentFile(firstByName); DOM.setCurrentFile(firstByName);
charStore([char]); charStore([char]);
} }
} };

View file

@ -1,10 +1,12 @@
import {fullstore} from 'fullstore'; 'use strict';
import limier from 'limier';
const {fullstore} = require('fullstore');
const limier = require('limier');
const searchStore = fullstore([]); const searchStore = fullstore([]);
const searchIndex = fullstore(0); const searchIndex = fullstore(0);
export const find = (value, names) => { module.exports.find = (value, names) => {
const result = limier(value, names); const result = limier(value, names);
searchStore(result); searchStore(result);
@ -13,7 +15,7 @@ export const find = (value, names) => {
return result; return result;
}; };
export const findNext = () => { module.exports.findNext = () => {
const names = searchStore(); const names = searchStore();
const index = next(searchIndex(), names.length); const index = next(searchIndex(), names.length);
@ -21,7 +23,7 @@ export const findNext = () => {
return names[searchIndex()]; return names[searchIndex()];
}; };
export const findPrevious = () => { module.exports.findPrevious = () => {
const names = searchStore(); const names = searchStore();
const index = previous(searchIndex(), names.length); const index = previous(searchIndex(), names.length);
@ -29,8 +31,8 @@ export const findPrevious = () => {
return names[index]; return names[index];
}; };
export const _next = next; module.exports._next = next;
export const _previous = previous; module.exports._previous = previous;
function next(index, length) { function next(index, length) {
if (index === length - 1) if (index === length - 1)

View file

@ -1,15 +1,14 @@
import test from 'supertape'; 'use strict';
import {getDOM} from './globals.fixture.js';
import { const test = require('supertape');
_next, const dir = './';
_previous,
find, const {getDOM} = require('./globals.fixture');
findNext,
findPrevious,
} from './find.js';
globalThis.DOM = getDOM(); globalThis.DOM = getDOM();
const {_next, _previous} = require(`${dir}find`);
test('cloudcmd: client: vim: _next', (t) => { test('cloudcmd: client: vim: _next', (t) => {
const result = _next(1, 2); const result = _next(1, 2);
@ -17,39 +16,9 @@ test('cloudcmd: client: vim: _next', (t) => {
t.end(); t.end();
}); });
test('cloudcmd: client: vim: _next: increment', (t) => {
const result = _next(0, 2);
t.equal(result, 1, 'should return 1');
t.end();
});
test('cloudcmd: client: vim: _previous', (t) => { test('cloudcmd: client: vim: _previous', (t) => {
const result = _previous(0, 2); const result = _previous(0, 2);
t.equal(result, 1, 'should return 1'); t.equal(result, 1, 'should return 1');
t.end(); t.end();
}); });
test('cloudcmd: client: vim: _previous: decrement', (t) => {
const result = _previous(1, 2);
t.equal(result, 0, 'should return 0');
t.end();
});
test('cloudcmd: client: vim: findNext: after find', (t) => {
find('a', ['alpha', 'beta', 'apple']);
const result = findNext();
t.equal(result, 'beta', 'should return next found name');
t.end();
});
test('cloudcmd: client: vim: findPrevious: after find', (t) => {
find('a', ['alpha', 'beta', 'apple']);
const result = findPrevious();
t.equal(result, 'apple', 'should return previous found name');
t.end();
});

View file

@ -1,6 +1,8 @@
'use strict';
const noop = () => {}; const noop = () => {};
export const getDOM = () => { module.exports.getDOM = () => {
const prompt = Promise.resolve.bind(Promise); const prompt = Promise.resolve.bind(Promise);
const CurrentInfo = { const CurrentInfo = {
element: {}, element: {},
@ -32,7 +34,7 @@ export const getDOM = () => {
}; };
}; };
export const getCloudCmd = () => { module.exports.getCloudCmd = () => {
const show = () => {}; const show = () => {};
return { return {

View file

@ -1,11 +1,15 @@
import vim from './vim.js'; 'use strict';
import * as finder from './find.js';
import { /* global CloudCmd */
/* global DOM */
const vim = require('./vim');
const finder = require('./find');
const {
setCurrent, setCurrent,
selectFileNotParent, selectFileNotParent,
} from './set-current.js'; } = require('./set-current');
export default (key, event, overrides = {}) => { module.exports = (key, event, overrides = {}) => {
const defaults = { const defaults = {
...globalThis.DOM, ...globalThis.DOM,
...globalThis.CloudCmd, ...globalThis.CloudCmd,
@ -33,13 +37,13 @@ const getOperations = (event, deps) => {
prompt = globalThis.DOM.Dialog.prompt, prompt = globalThis.DOM.Dialog.prompt,
preventDefault = event?.preventDefault?.bind(event), preventDefault = event?.preventDefault?.bind(event),
stopImmediatePropagation = event?.preventDefault?.bind(event), stopImmediatePropagation = event?.preventDefault?.bind(event),
promptNewFile = globalThis.DOM.promptNewFile, promptNewFile = DOM.promptNewFile,
toggleSelectedFile, toggleSelectedFile,
Buffer = {}, Buffer = {},
createFindNext = _createFindNext, createFindNext = _createFindNext,
createFindPrevious = _createFindPrevious, createFindPrevious = _createFindPrevious,
createMakeFile = _createMakeFile, createMakeFile = _createMakeFile,
renameCurrent,
} = deps; } = deps;
return { return {
@ -55,26 +59,15 @@ const getOperations = (event, deps) => {
setCurrentByName, setCurrentByName,
}), }),
escape: unselectFiles, escape: unselectFiles,
rename: () => {
event.preventDefault();
renameCurrent();
},
remove: () => { remove: () => {
Operation.show('delete'); Operation.show('delete');
}, },
operationCopy: () => {
event.preventDefault();
Operation.show('copy');
},
operationMove: () => {
event.preventDefault();
Operation.show('move');
},
makeDirectory: () => { makeDirectory: () => {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
event.preventDefault(); event.preventDefault();
globalThis.DOM.promptNewDir(); DOM.promptNewDir();
}, },
terminal: () => { terminal: () => {
@ -138,7 +131,7 @@ const getOperations = (event, deps) => {
}; };
}; };
export const selectFile = selectFileNotParent; module.exports.selectFile = selectFileNotParent;
const _createFindPrevious = (overrides = {}) => () => { const _createFindPrevious = (overrides = {}) => () => {
const {setCurrentByName} = overrides; const {setCurrentByName} = overrides;

View file

@ -1,15 +1,25 @@
import {test, stub} from 'supertape'; 'use strict';
import {getDOM, getCloudCmd} from './globals.fixture.js';
import vim, {selectFile as vimSelectFile} from './index.js'; const {join} = require('node:path');
import * as finder from './find.js'; const {test, stub} = require('supertape');
const dir = '../';
const pathVim = join(dir, 'vim');
const {getDOM, getCloudCmd} = require('./globals.fixture');
globalThis.DOM = getDOM(); globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const vim = require('./index.js');
const {assign} = Object; const {assign} = Object;
const {DOM} = globalThis; const {DOM} = globalThis;
const {Buffer} = DOM; const {Buffer} = DOM;
const {reRequire, stopAll} = require('mock-require');
test('cloudcmd: client: key: set next file: no', (t) => { test('cloudcmd: client: key: set next file: no', (t) => {
const element = {}; const element = {};
const setCurrentFile = stub(); const setCurrentFile = stub();
@ -309,7 +319,7 @@ test('cloudcmd: client: key: selectFile: ..', (t) => {
const selectFile = stub(); const selectFile = stub();
const current = {}; const current = {};
vimSelectFile(current, { vim.selectFile(current, {
selectFile, selectFile,
getCurrentName, getCurrentName,
}); });
@ -323,7 +333,7 @@ test('cloudcmd: client: key: selectFile', (t) => {
const getCurrentName = stub().returns('x'); const getCurrentName = stub().returns('x');
const current = {}; const current = {};
vimSelectFile(current, { vim.selectFile(current, {
selectFile, selectFile,
getCurrentName, getCurrentName,
}); });
@ -448,7 +458,6 @@ test('cloudcmd: client: key: set first file current: ^', async (t) => {
test('cloudcmd: client: key: visual', (t) => { test('cloudcmd: client: key: visual', (t) => {
const element = {}; const element = {};
const toggleSelectedFile = stub(); const toggleSelectedFile = stub();
const Info = { const Info = {
element, element,
}; };
@ -465,7 +474,6 @@ test('cloudcmd: client: key: visual', (t) => {
test('cloudcmd: client: key: ESC', (t) => { test('cloudcmd: client: key: ESC', (t) => {
const element = {}; const element = {};
const unselectFiles = stub(); const unselectFiles = stub();
const Info = { const Info = {
element, element,
}; };
@ -511,7 +519,6 @@ test('cloudcmd: client: key: Enter', async (t) => {
test('cloudcmd: client: key: /', (t) => { test('cloudcmd: client: key: /', (t) => {
const preventDefault = stub(); const preventDefault = stub();
const element = {}; const element = {};
const Info = { const Info = {
element, element,
files: [], files: [],
@ -546,12 +553,16 @@ test('cloudcmd: client: find', (t) => {
setCurrentByName, setCurrentByName,
}); });
const vim = reRequire(pathVim);
const event = { const event = {
preventDefault: stub(), preventDefault: stub(),
}; };
vim('/', event); vim('/', event);
stopAll();
t.notCalled(setCurrentByName); t.notCalled(setCurrentByName);
t.end(); t.end();
}); });
@ -584,6 +595,7 @@ test('cloudcmd: client: key: N', (t) => {
}); });
test('cloudcmd: client: key: make directory', async (t) => { test('cloudcmd: client: key: make directory', async (t) => {
const vim = reRequire(pathVim);
const {DOM} = globalThis; const {DOM} = globalThis;
assign(DOM, { assign(DOM, {
@ -658,99 +670,3 @@ test('cloudcmd: client: vim: edit', async (t) => {
t.calledWithNoArgs(CloudCmd.EditFileVim.show); t.calledWithNoArgs(CloudCmd.EditFileVim.show);
t.end(); t.end();
}); });
test('cloudcmd: client: vim: rename', async (t) => {
const DOM = getDOM();
const renameCurrent = stub();
assign(DOM, {
renameCurrent,
});
const event = {
preventDefault: stub(),
};
await vim('rr', event, DOM);
t.calledWithNoArgs(renameCurrent);
t.end();
});
test('cloudcmd: client: key: cc: operationCopy', (t) => {
const show = stub();
const preventDefault = stub();
const Operation = {
show,
};
const event = {
preventDefault,
};
vim('c', event, {
Operation,
});
vim('c', event, {
Operation,
});
t.calledWith(show, ['copy'], 'should show copy operation');
t.end();
});
test('cloudcmd: client: key: mm: operationMove', (t) => {
const show = stub();
const preventDefault = stub();
const Operation = {
show,
};
const event = {
preventDefault,
};
vim('m', event, {
Operation,
});
vim('m', event, {
Operation,
});
t.calledWith(show, ['move'], 'should show move operation');
t.end();
});
test('cloudcmd: client: key: n: findNext: real', (t) => {
const setCurrentByName = stub();
finder.find('a', ['alpha', 'beta', 'apple']);
const event = {};
vim('n', event, {
setCurrentByName,
});
t.calledWith(setCurrentByName, ['beta'], 'should set current by next found name');
t.end();
});
test('cloudcmd: client: key: N: findPrevious: real', (t) => {
const setCurrentByName = stub();
finder.find('a', ['alpha', 'beta', 'apple']);
const event = {};
vim('N', event, {
setCurrentByName,
});
t.calledWith(setCurrentByName, ['apple'], 'should set current by previous found name');
t.end();
});

View file

@ -1,5 +1,8 @@
'use strict';
/* global DOM */ /* global DOM */
export function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) { module.exports.selectFileNotParent = selectFileNotParent;
function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) {
const name = getCurrentName(current); const name = getCurrentName(current);
if (name === '..') if (name === '..')
@ -8,7 +11,7 @@ export function selectFileNotParent(current, {getCurrentName, selectFile} = DOM)
selectFile(current); selectFile(current);
} }
export const setCurrent = (sibling, {count, isVisual, isDelete}, {Info, setCurrentFile, unselectFiles, Operation}) => { module.exports.setCurrent = (sibling, {count, isVisual, isDelete}, {Info, setCurrentFile, unselectFiles, Operation}) => {
let current = Info.element; let current = Info.element;
const select = isVisual ? selectFileNotParent : unselectFiles; const select = isVisual ? selectFileNotParent : unselectFiles;

View file

@ -1,5 +1,6 @@
import {fullstore} from 'fullstore'; 'use strict';
const {fullstore} = require('fullstore');
const store = fullstore(''); const store = fullstore('');
const visual = fullstore(false); const visual = fullstore(false);
@ -20,7 +21,7 @@ const rmFirst = (a) => {
const noop = () => {}; const noop = () => {};
export default (key, operations = {}) => { module.exports = (key, operations = {}) => {
const prevStore = store(); const prevStore = store();
const isVisual = visual(); const isVisual = visual();
const value = store(prevStore.concat(key)); const value = store(prevStore.concat(key));
@ -40,9 +41,6 @@ export default (key, operations = {}) => {
makeDirectory = noop, makeDirectory = noop,
terminal = noop, terminal = noop,
edit = noop, edit = noop,
operationCopy = noop,
operationMove = noop,
rename = noop,
} = operations; } = operations;
if (key === 'Enter') if (key === 'Enter')
@ -114,26 +112,11 @@ export default (key, operations = {}) => {
return end(); return end();
} }
if (value === 'cc') {
operationCopy();
return end();
}
if (value === 'mm') {
operationMove();
return end();
}
if (value === 'mf') { if (value === 'mf') {
makeFile(); makeFile();
return end(); return end();
} }
if (value === 'rr') {
rename();
return end();
}
if (key === 'd' && (visual() || prevStore === 'd')) { if (key === 'd' && (visual() || prevStore === 'd')) {
stopVisual(); stopVisual();
remove(); remove();

View file

@ -1,5 +1,8 @@
import {test, stub} from 'supertape'; 'use strict';
import vim from './vim.js';
const {test, stub} = require('supertape');
const vim = require('./vim');
test('vim: no operations', (t) => { test('vim: no operations', (t) => {
const result = vim('hello', {}); const result = vim('hello', {});
@ -43,28 +46,6 @@ test('vim: ^', (t) => {
t.end(); t.end();
}); });
test('vim: cc', (t) => {
const operationCopy = stub();
vim('cc', {
operationCopy,
});
t.calledWithNoArgs(operationCopy);
t.end();
});
test('vim: mm', (t) => {
const operationMove = stub();
vim('mm', {
operationMove,
});
t.calledWithNoArgs(operationMove);
t.end();
});
test('vim: w', (t) => { test('vim: w', (t) => {
const moveNext = stub(); const moveNext = stub();

View file

@ -1,5 +1,5 @@
import test from 'supertape'; import test from 'supertape';
import {getIndex} from './get-index.js'; import {getIndex} from './get-index.mjs';
test('cloudcmd: client: listeners: getIndex: not found', (t) => { test('cloudcmd: client: listeners: getIndex: not found', (t) => {
const array = ['hello']; const array = ['hello'];

View file

@ -1,5 +1,5 @@
import test from 'supertape'; import test from 'supertape';
import {getRange} from './get-range.js'; import {getRange} from './get-range.mjs';
test('cloudcmd: client: listeners: getRange: direct', (t) => { test('cloudcmd: client: listeners: getRange: direct', (t) => {
const expected = [ const expected = [

View file

@ -5,15 +5,15 @@ import currify from 'currify';
import {tryToCatch} from 'try-to-catch'; import {tryToCatch} from 'try-to-catch';
import clipboard from '@cloudcmd/clipboard'; import clipboard from '@cloudcmd/clipboard';
import * as Events from '#dom/events'; import * as Events from '#dom/events';
import {uploadFiles} from '#dom/upload-files'; import {getRange} from './get-range.mjs';
import {FS} from '#common/cloudfunc'; import uploadFiles from '../dom/upload-files.js';
import {getRange} from './get-range.js'; import {FS} from '../../common/cloudfunc.mjs';
import {getIndex} from './get-index.js'; import {getIndex} from './get-index.mjs';
const NBSP_REG = RegExp(String.fromCharCode(160), 'g'); const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
const SPACE = ' '; const SPACE = ' ';
export async function init() { export const init = async () => {
contextMenu(); contextMenu();
dragndrop(); dragndrop();
unload(); unload();
@ -21,7 +21,7 @@ export async function init() {
resize(); resize();
header(); header();
await config(); await config();
} };
const unselect = (event) => { const unselect = (event) => {
const isMac = /Mac/.test(globalThis.navigator.platform); const isMac = /Mac/.test(globalThis.navigator.platform);
@ -55,7 +55,6 @@ let EXT;
function header() { function header() {
const fm = DOM.getFM(); const fm = DOM.getFM();
const isDataset = (el) => el.dataset; const isDataset = (el) => el.dataset;
const isPanel = (el) => { const isPanel = (el) => {
return /^js-(left|right)$/.test(el.dataset.name); return /^js-(left|right)$/.test(el.dataset.name);
}; };
@ -310,12 +309,8 @@ function onDragStart(event) {
link.href = prefixURL + '/pack' + Info.path + EXT; link.href = prefixURL + '/pack' + Info.path + EXT;
} }
event.dataTransfer.setData( event.dataTransfer.setData('DownloadURL', 'application/octet-stream' + ':' + name +
'DownloadURL', ':' + link);
'application/octet-stream' + ':' + name +
':' +
link,
);
} }
function getLIElement(element) { function getLIElement(element) {

View file

@ -1,32 +1,32 @@
/* global CloudCmd, filepicker */ /* global CloudCmd, filepicker */
import exec from 'execon';
import currify from 'currify';
import load from 'load.js';
import {ajax} from '#dom/load';
import * as Files from '#dom/files';
import * as Images from '#dom/images';
'use strict';
const exec = require('execon');
const currify = require('currify');
const load = require('load.js');
const {ajax} = require('../dom/load');
const Files = require('../dom/files');
const Images = require('../dom/images.mjs');
const {log} = CloudCmd; const {log} = CloudCmd;
const upload = currify(_upload); const upload = currify(_upload);
const Name = 'Cloud'; const Name = 'Cloud';
CloudCmd[Name] = { CloudCmd[Name] = module.exports;
init,
uploadFile,
saveFile,
};
export async function init() { module.exports.init = async () => {
const [modules] = await loadFiles(); const [modules] = await loadFiles();
const {key} = modules.data.FilePicker; const {key} = modules.data.FilePicker;
filepicker.setKey(key); filepicker.setKey(key);
Images.hide(); Images.hide();
} };
export function uploadFile(filename, data) { module.exports.uploadFile = (filename, data) => {
const mimetype = ''; const mimetype = '';
filepicker.store(data, { filepicker.store(data, {
@ -35,11 +35,11 @@ export function uploadFile(filename, data) {
}, (fpFile) => { }, (fpFile) => {
filepicker.exportFile(fpFile, log, log); filepicker.exportFile(fpFile, log, log);
}); });
} };
export function saveFile(callback) { module.exports.saveFile = (callback) => {
filepicker.pick(upload(callback)); filepicker.pick(upload(callback));
} };
function _upload(callback, file) { function _upload(callback, file) {
const {url, filename} = file; const {url, filename} = file;

View file

@ -1,14 +1,18 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
import * as Dialog from '#dom/dialog'; CloudCmd.CommandLine = exports;
export function init() {} const Dialog = require('../dom/dialog');
CloudCmd.CommandLine = {
init,
show,
hide,
};
export async function show() { const noop = () => {};
module.exports.init = noop;
module.exports.show = show;
module.exports.hide = hide;
async function show() {
const [, cmd] = await Dialog.prompt('Command Line', ''); const [, cmd] = await Dialog.prompt('Command Line', '');
const TERMINAL = '^(t|terminal)'; const TERMINAL = '^(t|terminal)';
@ -28,4 +32,4 @@ export async function show() {
} }
} }
export function hide() {} function hide() {}

View file

@ -1,5 +1,5 @@
import '../../../css/config.css'; /* global CloudCmd, DOM, io */
import {rendy} from 'rendy'; import rendy from 'rendy';
import currify from 'currify'; import currify from 'currify';
import wraptile from 'wraptile'; import wraptile from 'wraptile';
import squad from 'squad'; import squad from 'squad';
@ -8,12 +8,11 @@ import {tryToCatch} from 'try-to-catch';
import load from 'load.js'; import load from 'load.js';
import createElement from '@cloudcmd/create-element'; import createElement from '@cloudcmd/create-element';
import * as Events from '#dom/events'; import * as Events from '#dom/events';
import * as Files from '#dom/files'; import '../../../css/config.css';
import {getTitle} from '#common/cloudfunc'; import * as input from './input.mjs';
import * as Images from '#dom/images'; import * as Images from '../../dom/images.mjs';
import * as input from './input.js'; import Files from '../../dom/files.js';
import {getTitle} from '../../../common/cloudfunc.mjs';
const {CloudCmd, DOM} = globalThis;
const {Dialog, setTitle} = DOM; const {Dialog, setTitle} = DOM;
@ -41,7 +40,7 @@ let Template;
const loadCSS = load.css; const loadCSS = load.css;
export async function init() { export const init = async () => {
if (!CloudCmd.config('configDialog')) if (!CloudCmd.config('configDialog'))
return; return;
@ -57,7 +56,7 @@ export async function init() {
]); ]);
initSocket(); initSocket();
} };
const {config, Key} = CloudCmd; const {config, Key} = CloudCmd;
@ -79,7 +78,7 @@ function initSocket() {
const ONE_MINUTE = 60 * 1000; const ONE_MINUTE = 60 * 1000;
const socket = globalThis.io.connect(href + prefixSocket + '/config', { const socket = io.connect(href + prefixSocket + '/config', {
reconnectionAttempts: Infinity, reconnectionAttempts: Infinity,
reconnectionDelay: ONE_MINUTE, reconnectionDelay: ONE_MINUTE,
path: `${prefix}/socket.io`, path: `${prefix}/socket.io`,
@ -133,7 +132,6 @@ async function fillTemplate() {
columns, columns,
theme, theme,
configAuth, configAuth,
configPort,
...obj ...obj
} = input.convert(config); } = input.convert(config);
@ -143,7 +141,6 @@ async function fillTemplate() {
obj[`${columns}-selected`] = 'selected'; obj[`${columns}-selected`] = 'selected';
obj[`${theme}-selected`] = 'selected'; obj[`${theme}-selected`] = 'selected';
obj.configAuth = configAuth ? '' : 'hidden'; obj.configAuth = configAuth ? '' : 'hidden';
obj.configPort = configPort ? '' : 'hidden';
const innerHTML = rendy(Template, obj); const innerHTML = rendy(Template, obj);

View file

@ -1,9 +1,7 @@
import {encode} from '#common/entity'; import currify from 'currify';
const isBool = (a) => typeof a === 'boolean'; const isType = currify((type, object, name) => type === typeof object[name]);
const isString = (a) => typeof a === 'string'; const isBool = isType('boolean');
const {keys} = Object;
export function getElementByName(selector, element) { export function getElementByName(selector, element) {
const str = `[data-name="js-${selector}"]`; const str = `[data-name="js-${selector}"]`;
@ -21,19 +19,13 @@ export const getName = (element) => {
export const convert = (config) => { export const convert = (config) => {
const result = config; const result = config;
const array = Object.keys(config);
for (const name of keys(config)) { const filtered = array.filter(isBool(config));
for (const name of filtered) {
const item = config[name]; const item = config[name];
result[name] = setState(item);
if (isBool(item)) {
result[name] = setState(item);
continue;
}
if (isString(item)) {
result[name] = encode(item);
continue;
}
} }
return result; return result;

View file

@ -1,140 +0,0 @@
import {test, stub} from 'supertape';
import {
convert,
getName,
getValue,
setValue,
} from './input.js';
test('cloudcmd: client: config: input: convert', (t) => {
const result = convert({
name: 'hello <world>',
});
const expected = {
name: 'hello &lt;world&gt;',
};
t.deepEqual(result, expected);
t.end();
});
test('cloudcmd: client: config: input: convert: bool', (t) => {
const result = convert({
auth: true,
});
const expected = {
auth: ' checked',
};
t.deepEqual(result, expected);
t.end();
});
test('cloudcmd: client: config: input: convert: bool false', (t) => {
const result = convert({
auth: false,
});
const expected = {
auth: '',
};
t.deepEqual(result, expected);
t.end();
});
test('cloudcmd: client: config: input: getName', (t) => {
const getAttribute = stub().returns('js-hello');
const element = {
getAttribute,
};
const result = getName(element);
t.equal(result, 'hello', 'should strip js- prefix');
t.end();
});
test('cloudcmd: client: config: input: getValue: checkbox', (t) => {
const querySelector = stub().returns({
type: 'checkbox',
checked: true,
});
const element = {
querySelector,
};
const result = getValue('auth', element);
t.ok(result, 'should return checked value');
t.end();
});
test('cloudcmd: client: config: input: getValue: number', (t) => {
const querySelector = stub().returns({
type: 'number',
value: '42',
});
const element = {
querySelector,
};
const result = getValue('port', element);
t.equal(result, 42, 'should return number');
t.end();
});
test('cloudcmd: client: config: input: getValue: default', (t) => {
const querySelector = stub().returns({
type: 'text',
value: 'hello',
});
const element = {
querySelector,
};
const result = getValue('name', element);
t.equal(result, 'hello', 'should return value as is');
t.end();
});
test('cloudcmd: client: config: input: setValue: checkbox', (t) => {
const el = {
type: 'checkbox',
checked: false,
};
const querySelector = stub().returns(el);
const element = {
querySelector,
};
setValue('auth', true, element);
t.ok(el.checked, 'should set checked');
t.end();
});
test('cloudcmd: client: config: input: setValue: default', (t) => {
const el = {
type: 'text',
value: 'old',
};
const querySelector = stub().returns(el);
const element = {
querySelector,
};
setValue('name', 'new', element);
t.equal(el.value, 'new', 'should set value');
t.end();
});

View file

@ -1,34 +1,36 @@
/* global CloudCmd */ /* global CloudCmd */
/* global DOM */ /* global DOM */
import olark from '@cloudcmd/olark';
import * as Images from '#dom/images';
CloudCmd.Contact = { 'use strict';
init,
show, CloudCmd.Contact = exports;
hide,
}; const olark = require('@cloudcmd/olark');
const Images = require('../dom/images.mjs');
const {Events} = DOM; const {Events} = DOM;
const {Key} = CloudCmd; const {Key} = CloudCmd;
export function init() { module.exports.show = show;
module.exports.hide = hide;
module.exports.init = () => {
Events.addKey(onKey); Events.addKey(onKey);
olark.identify('6216-545-10-4223'); olark.identify('6216-545-10-4223');
olark('api.box.onExpand', show); olark('api.box.onExpand', show);
olark('api.box.onShow', show); olark('api.box.onShow', show);
olark('api.box.onShrink', hide); olark('api.box.onShrink', hide);
} };
export function show() { function show() {
Key.unsetBind(); Key.unsetBind();
Images.hide(); Images.hide();
olark('api.box.expand'); olark('api.box.expand');
} }
export function hide() { function hide() {
Key.setBind(); Key.setBind();
olark('api.box.hide'); olark('api.box.hide');
} }

View file

@ -1,12 +1,9 @@
import * as Events from '#dom/events'; 'use strict';
const {CloudCmd} = globalThis; /* global CloudCmd */
CloudCmd.EditFileVim = exports;
CloudCmd.EditFileVim = { const Events = require('#dom/events');
init,
show,
hide,
};
const {Key} = CloudCmd; const {Key} = CloudCmd;
@ -18,11 +15,11 @@ const ConfigView = {
}, },
}; };
export async function init() { module.exports.init = async () => {
await CloudCmd.EditFile(); await CloudCmd.EditFile();
} };
export async function show() { module.exports.show = async () => {
Events.addKey(listener); Events.addKey(listener);
const editFile = await CloudCmd.EditFile.show(ConfigView); const editFile = await CloudCmd.EditFile.show(ConfigView);
@ -30,9 +27,11 @@ export async function show() {
editFile editFile
.getEditor() .getEditor()
.setKeyMap('vim'); .setKeyMap('vim');
} };
export function hide() { module.exports.hide = hide;
function hide() {
CloudCmd.Edit.hide(); CloudCmd.Edit.hide();
} }

View file

@ -1,15 +1,12 @@
/* global CloudCmd, DOM*/ 'use strict';
import Format from 'format-io';
import {fullstore} from 'fullstore';
import exec from 'execon';
import {supermenu} from 'supermenu';
CloudCmd.EditFile = { /* global CloudCmd, DOM*/
init, CloudCmd.EditFile = exports;
show,
hide, const Format = require('format-io');
isChanged, const {fullstore} = require('fullstore');
}; const exec = require('execon');
const supermenu = require('supermenu');
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
@ -29,7 +26,7 @@ const ConfigView = {
}, },
}; };
export async function init() { module.exports.init = async () => {
isLoading(true); isLoading(true);
await CloudCmd.Edit(); await CloudCmd.Edit();
@ -39,7 +36,7 @@ export async function init() {
setListeners(editor); setListeners(editor);
isLoading(false); isLoading(false);
} };
function getName() { function getName() {
const {name, isDir} = Info; const {name, isDir} = Info;
@ -50,7 +47,7 @@ function getName() {
return name; return name;
} }
export async function show(options) { module.exports.show = async (options) => {
if (isLoading()) if (isLoading())
return; return;
@ -89,9 +86,11 @@ export async function show(options) {
CloudCmd.Edit.show(optionsEdit); CloudCmd.Edit.show(optionsEdit);
return CloudCmd.Edit; return CloudCmd.Edit;
} };
export function hide() { module.exports.hide = hide;
function hide() {
CloudCmd.Edit.hide(); CloudCmd.Edit.hide();
} }
@ -177,7 +176,9 @@ function setMsgChanged(name) {
MSG_CHANGED = `Do you want to save changes to ${name}?`; MSG_CHANGED = `Do you want to save changes to ${name}?`;
} }
export async function isChanged() { module.exports.isChanged = isChanged;
async function isChanged() {
const editor = CloudCmd.Edit.getEditor(); const editor = CloudCmd.Edit.getEditor();
const is = editor.isChanged(); const is = editor.isChanged();

View file

@ -1,12 +1,9 @@
import * as Events from '#dom/events'; 'use strict';
const {CloudCmd} = globalThis; /* global CloudCmd */
CloudCmd.EditNamesVim = exports;
CloudCmd.EditNamesVim = { const Events = require('#dom/events');
init,
show,
hide,
};
const {Key} = CloudCmd; const {Key} = CloudCmd;
const ConfigView = { const ConfigView = {
@ -17,20 +14,22 @@ const ConfigView = {
}, },
}; };
export async function init() { module.exports.init = async () => {
await CloudCmd.EditNames(); await CloudCmd.EditNames();
} };
export function show() { module.exports.show = () => {
Events.addKey(listener); Events.addKey(listener);
CloudCmd.EditNames CloudCmd.EditNames
.show(ConfigView) .show(ConfigView)
.getEditor() .getEditor()
.setKeyMap('vim'); .setKeyMap('vim');
} };
export function hide() { module.exports.hide = hide;
function hide() {
CloudCmd.Edit.hide(); CloudCmd.Edit.hide();
} }

View file

@ -1,16 +1,13 @@
import {tryToCatch} from 'try-to-catch'; 'use strict';
import exec from 'execon';
import {supermenu} from 'supermenu';
import {multiRename} from 'multi-rename';
const {CloudCmd, DOM} = globalThis; const {tryToCatch} = require('try-to-catch');
CloudCmd.EditNames = { /* global CloudCmd, DOM */
init, CloudCmd.EditNames = exports;
show,
hide, const exec = require('execon');
isChanged, const supermenu = require('supermenu');
}; const {multiRename} = require('multi-rename');
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
const {Dialog} = DOM; const {Dialog} = DOM;
@ -25,13 +22,13 @@ const ConfigView = {
}, },
}; };
export async function init() { module.exports.init = async () => {
await CloudCmd.Edit(); await CloudCmd.Edit();
setListeners(); setListeners();
} };
export function show(options) { module.exports.show = (options) => {
const names = getActiveNames().join('\n'); const names = getActiveNames().join('\n');
const config = { const config = {
...ConfigView, ...ConfigView,
@ -53,7 +50,7 @@ export function show(options) {
CloudCmd.Edit.show(config); CloudCmd.Edit.show(config);
return CloudCmd.Edit; return CloudCmd.Edit;
} };
async function keyListener(event) { async function keyListener(event) {
const ctrl = event.ctrlKey; const ctrl = event.ctrlKey;
@ -63,10 +60,7 @@ async function keyListener(event) {
if (ctrlMeta && event.keyCode === Key.S) { if (ctrlMeta && event.keyCode === Key.S) {
hide(); hide();
return; } else if (ctrlMeta && event.keyCode === Key.P) {
}
if (ctrlMeta && event.keyCode === Key.P) {
const [, pattern] = await Dialog.prompt('Apply pattern:', '[n][e]'); const [, pattern] = await Dialog.prompt('Apply pattern:', '[n][e]');
pattern && applyPattern(pattern); pattern && applyPattern(pattern);
} }
@ -83,7 +77,9 @@ function getActiveNames() {
return DOM.getFilenames(DOM.getActiveFiles()); return DOM.getFilenames(DOM.getActiveFiles());
} }
export function hide() { module.exports.hide = hide;
function hide() {
CloudCmd.Edit.hide(); CloudCmd.Edit.hide();
} }
@ -205,7 +201,9 @@ function setMenu(event) {
Menu.show(position.x, position.y); Menu.show(position.x, position.y);
} }
export async function isChanged() { module.exports.isChanged = isChanged;
async function isChanged() {
const editor = CloudCmd.Edit.getEditor(); const editor = CloudCmd.Edit.getEditor();
const msg = 'Apply new names?'; const msg = 'Apply new names?';

View file

@ -1,28 +1,23 @@
/* global CloudCmd */ /* global CloudCmd */
import {montag} from 'montag';
import {promisify} from 'es6-promisify';
import {tryToCatch} from 'try-to-catch';
import createElement from '@cloudcmd/create-element';
import load from 'load.js';
import {MAX_FILE_SIZE as maxSize} from '#common/cloudfunc';
import {time, timeEnd} from '#common/util';
export function getEditor() { 'use strict';
return editor;
}
const montag = require('montag');
const {promisify} = require('es6-promisify');
const {tryToCatch} = require('try-to-catch');
const createElement = require('@cloudcmd/create-element');
const load = require('load.js');
const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc.mjs');
const {time, timeEnd} = require('../../common/util');
const getEditor = () => editor;
const isFn = (a) => typeof a === 'function'; const isFn = (a) => typeof a === 'function';
const loadJS = load.js; const loadJS = load.js;
const Name = 'Edit'; const Name = 'Edit';
CloudCmd[Name] = { CloudCmd[Name] = exports;
init,
show,
hide,
getEditor,
getElement,
};
const EditorName = CloudCmd.config('editor'); const EditorName = CloudCmd.config('editor');
@ -38,12 +33,12 @@ const ConfigView = {
}, },
}; };
export async function init() { module.exports.init = async () => {
const element = create(); const element = create();
await CloudCmd.View(); await CloudCmd.View();
await loadFiles(element); await loadFiles(element);
} };
function create() { function create() {
const element = createElement('div', { const element = createElement('div', {
@ -84,7 +79,7 @@ function initConfig(options = {}) {
return config; return config;
} }
export function show(options) { module.exports.show = (options) => {
if (Loading) if (Loading)
return; return;
@ -93,15 +88,15 @@ export function show(options) {
getEditor().setOptions({ getEditor().setOptions({
fontSize: 16, fontSize: 16,
}); });
} };
export function getElement() { module.exports.getEditor = getEditor;
return Element;
}
export function hide() { module.exports.getElement = () => Element;
module.exports.hide = () => {
CloudCmd.View.hide(); CloudCmd.View.hide();
} };
const loadFiles = async (element) => { const loadFiles = async (element) => {
const prefix = `${CloudCmd.prefix}/${EditorName}`; const prefix = `${CloudCmd.prefix}/${EditorName}`;

View file

@ -1,18 +1,18 @@
import * as Images from '#dom/images'; 'use strict';
const {CloudCmd} = globalThis; /* global CloudCmd */
CloudCmd.Help = exports;
CloudCmd.Help = { const Images = require('../dom/images.mjs');
init,
show, module.exports.init = () => {
hide, Images.show.load('top');
}; };
export function init() { module.exports.show = show;
Images.show.load('top'); module.exports.hide = hide;
}
export function show() { function show() {
const positionLoad = 'top'; const positionLoad = 'top';
const relative = true; const relative = true;
@ -22,6 +22,6 @@ export function show() {
}); });
} }
export function hide() { function hide() {
CloudCmd.View.hide(); CloudCmd.View.hide();
} }

View file

@ -1,20 +1,18 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
/* global Util */ /* global Util */
/* global DOM */ /* global DOM */
/* global Console */ /* global Console */
import exec from 'execon'; CloudCmd.Konsole = exports;
import currify from 'currify';
import {tryToCatch} from 'try-to-catch';
import {js as loadJS} from 'load.js';
import createElement from '@cloudcmd/create-element';
import * as Images from '#dom/images';
CloudCmd.Konsole = { const exec = require('execon');
init, const currify = require('currify');
show, const {tryToCatch} = require('try-to-catch');
hide, const loadJS = require('load.js').js;
}; const createElement = require('@cloudcmd/create-element');
const Images = require('../dom/images.mjs');
const {Dialog, CurrentInfo: Info} = DOM; const {Dialog, CurrentInfo: Info} = DOM;
const rmLastSlash = (a) => a.replace(/\/$/, '') || '/'; const rmLastSlash = (a) => a.replace(/\/$/, '') || '/';
@ -29,7 +27,7 @@ const Name = 'Konsole';
let Element; let Element;
let Loaded; let Loaded;
export async function init() { module.exports.init = async () => {
if (!config('console')) if (!config('console'))
return; return;
@ -38,13 +36,13 @@ export async function init() {
await CloudCmd.View(); await CloudCmd.View();
await load(); await load();
await create(); await create();
} };
export function hide() { module.exports.hide = () => {
CloudCmd.View.hide(); CloudCmd.View.hide();
} };
export const clear = () => { module.exports.clear = () => {
konsole.clear(); konsole.clear();
}; };
@ -111,7 +109,7 @@ function authCheck(konsole) {
}); });
} }
export function show(callback) { module.exports.show = (callback) => {
if (!Loaded) if (!Loaded)
return; return;
@ -124,7 +122,7 @@ export function show(callback) {
exec(callback); exec(callback);
}, },
}); });
} };
const load = async () => { const load = async () => {
Util.time(`${Name} load`); Util.time(`${Name} load`);

View file

@ -1,26 +1,26 @@
import createElement from '@cloudcmd/create-element'; 'use strict';
import * as Images from '#dom/images';
import {Markdown} from '#dom/rest';
import {alert} from '#dom/dialog';
const {CloudCmd} = globalThis; /* global CloudCmd */
CloudCmd.Markdown = exports;
CloudCmd.Markdown = { const createElement = require('@cloudcmd/create-element');
init,
show,
hide,
};
export async function init() { const Images = require('../dom/images.mjs');
const {Markdown} = require('../dom/rest');
const {alert} = require('../dom/dialog');
module.exports.init = async () => {
Images.show.load('top'); Images.show.load('top');
await CloudCmd.View(); await CloudCmd.View();
} };
export function hide() { module.exports.show = show;
module.exports.hide = () => {
CloudCmd.View.hide(); CloudCmd.View.hide();
} };
export async function show(name, options = {}) { async function show(name, options = {}) {
const {positionLoad, relative} = options; const {positionLoad, relative} = options;
Images.show.load(positionLoad); Images.show.load(positionLoad);

View file

@ -1,4 +1,4 @@
import {supermenu} from 'supermenu'; import supermenu from 'supermenu';
const noop = () => {}; const noop = () => {};
const {CloudCmd} = globalThis; const {CloudCmd} = globalThis;
@ -14,9 +14,8 @@ export const createCloudMenu = async (fm, options, menuData) => {
async function loadMenu() { async function loadMenu() {
if (CloudCmd.config('menu') === 'aleman') { if (CloudCmd.config('menu') === 'aleman') {
const {prefix} = CloudCmd;
const {host, protocol} = globalThis.location; const {host, protocol} = globalThis.location;
const url = `${protocol}//${host}${prefix}/node_modules/aleman/menu/menu.js`; const url = `${protocol}//${host}/node_modules/aleman/menu/menu.js`;
const {createMenu} = await import(/* webpackIgnore: true */url); const {createMenu} = await import(/* webpackIgnore: true */url);
return createMenu; return createMenu;

View file

@ -1,11 +1,15 @@
import exec from 'execon'; /* global CloudCmd, DOM */
import wrap from 'wraptile';
import createElement from '@cloudcmd/create-element'; 'use strict';
import {getIdBySrc} from '#dom/load';
import * as RESTful from '#dom/rest'; const exec = require('execon');
import {FS} from '#common/cloudfunc'; const wrap = require('wraptile');
const createElement = require('@cloudcmd/create-element');
const {FS} = require('../../../common/cloudfunc.mjs');
const {getIdBySrc} = require('../../dom/load');
const RESTful = require('../../dom/rest');
const {CloudCmd, DOM} = globalThis;
const {config, Key} = CloudCmd; const {config, Key} = CloudCmd;
const { const {
@ -23,15 +27,11 @@ let MenuShowedName;
let MenuContext; let MenuContext;
let MenuContextFile; let MenuContextFile;
export const ENABLED = false; module.exports.ENABLED = false;
CloudCmd.Menu = { CloudCmd.Menu = exports;
init,
show,
hide,
};
export async function init() { module.exports.init = async () => {
const {isAuth, menuDataFile} = getFileMenuData(); const {isAuth, menuDataFile} = getFileMenuData();
const fm = DOM.getFM(); const fm = DOM.getFM();
@ -45,7 +45,7 @@ export async function init() {
type: 'file', type: 'file',
}); });
const {createCloudMenu} = await import('./cloudmenu.js'); const {createCloudMenu} = await import('./cloudmenu.mjs');
const {name} = fm.dataset; const {name} = fm.dataset;
@ -56,21 +56,23 @@ export async function init() {
MenuContextFile.addContextMenuListener(); MenuContextFile.addContextMenuListener();
Events.addKey(listener); Events.addKey(listener);
} };
export function hide() { module.exports.hide = hide;
function hide() {
MenuContext.hide(); MenuContext.hide();
MenuContextFile.hide(); MenuContextFile.hide();
} }
export function show(position) { module.exports.show = (position) => {
const {x, y} = getPosition(position); const {x, y} = getPosition(position);
MenuContext.show(x, y); MenuContext.show(x, y);
MenuContextFile.show(x, y); MenuContextFile.show(x, y);
Images.hide(); Images.hide();
} };
function getPosition(position) { function getPosition(position) {
if (position) if (position)
@ -296,7 +298,7 @@ function download(type) {
* no need in hash so we escape # * no need in hash so we escape #
* and all other characters, like "%" * and all other characters, like "%"
*/ */
const encodedPath = encodeURI(path).replace(/#/g, '#'); const encodedPath = encodeURI(path).replace(/#/g, '%23');
const id = getIdBySrc(path); const id = getIdBySrc(path);
let src; let src;

View file

@ -1,4 +1,6 @@
export const format = (operation, from, to) => { 'use strict';
module.exports = (operation, from, to) => {
if (!to) if (!to)
return `${operation} ${from}`; return `${operation} ${from}`;

View file

@ -1,9 +1,11 @@
import currify from 'currify'; 'use strict';
const currify = require('currify');
const not = currify((array, value) => !array.includes(value)); const not = currify((array, value) => !array.includes(value));
const notOneOf = currify((a, b) => a.filter(not(b))); const notOneOf = currify((a, b) => a.filter(not(b)));
export const getNextCurrentName = (currentName, names, removedNames) => { module.exports = (currentName, names, removedNames) => {
const i = names.indexOf(currentName); const i = names.indexOf(currentName);
const nextNames = notOneOf(names, removedNames); const nextNames = notOneOf(names, removedNames);

View file

@ -4,10 +4,10 @@ import {promisify} from 'es6-promisify';
import exec from 'execon'; import exec from 'execon';
import load from 'load.js'; import load from 'load.js';
import {tryToCatch} from 'try-to-catch'; import {tryToCatch} from 'try-to-catch';
import {encode} from '#common/entity'; import {encode} from '../../../common/entity.js';
import {removeExtension} from './remove-extension.js'; import removeExtension from './remove-extension.js';
import {setListeners} from './set-listeners.js'; import {setListeners} from './set-listeners.mjs';
import {getNextCurrentName} from './get-next-current-name.js'; import getNextCurrentName from './get-next-current-name.js';
const {DOM, CloudCmd} = globalThis; const {DOM, CloudCmd} = globalThis;
@ -190,11 +190,11 @@ function getPacker(type) {
return packTarFn; return packTarFn;
} }
export function hide() { export const hide = () => {
CloudCmd.View.hide(); CloudCmd.View.hide();
} };
export function show(operation, data) { export const show = (operation, data) => {
if (!Loaded) if (!Loaded)
return; return;
@ -215,7 +215,7 @@ export function show(operation, data) {
if (operation === 'extract') if (operation === 'extract')
return Operation.extract(); return Operation.extract();
} };
Operation.copy = processFiles({ Operation.copy = processFiles({
type: 'copy', type: 'copy',

View file

@ -1,6 +1,8 @@
import {getExt} from '#common/util'; 'use strict';
export const removeExtension = (name) => { const {getExt} = require('../../../common/util');
module.exports = (name) => {
const ext = getExtension(name); const ext = getExtension(name);
return name.replace(ext, ''); return name.replace(ext, '');

View file

@ -1,5 +1,7 @@
import test from 'supertape'; 'use strict';
import {removeExtension} from './remove-extension.js';
const test = require('supertape');
const removeExtension = require(`./remove-extension`);
test('cloudcmd: client: modules: operation: removeExtension: .tar.gz', (t) => { test('cloudcmd: client: modules: operation: removeExtension: .tar.gz', (t) => {
const name = 'hello'; const name = 'hello';

View file

@ -1,7 +1,7 @@
/* global DOM */ /* global DOM */
import forEachKey from 'for-each-key'; import forEachKey from 'for-each-key';
import wraptile from 'wraptile'; import wraptile from 'wraptile';
import {format} from './format.js'; import format from './format.js';
const {Dialog, Images} = DOM; const {Dialog, Images} = DOM;
@ -15,12 +15,15 @@ export const setListeners = (options) => (emitter) => {
} = options; } = options;
let done; let done;
let lastError;
const onAbort = wraptile(({emitter, operation}) => { const onAbort = wraptile(({emitter, operation}) => {
emitter.abort(); emitter.abort();
const msg = `${operation} aborted`; const msg = `${operation} aborted`;
lastError = true;
Dialog.alert(msg, { Dialog.alert(msg, {
cancel: false, cancel: false,
}); });
@ -37,10 +40,13 @@ export const setListeners = (options) => (emitter) => {
operation, operation,
})); }));
let noProgress = true;
const listeners = { const listeners = {
progress: (value) => { progress: (value) => {
done = value === 100; done = value === 100;
progress.setProgress(value); progress.setProgress(value);
noProgress = false;
}, },
end: () => { end: () => {
@ -48,10 +54,13 @@ export const setListeners = (options) => (emitter) => {
forEachKey(removeListener, listeners); forEachKey(removeListener, listeners);
progress.remove(); progress.remove();
callback(); if (lastError || done || noProgress)
callback();
}, },
error: async (error) => { error: async (error) => {
lastError = error;
if (noContinue) { if (noContinue) {
listeners.end(error); listeners.end(error);
Dialog.alert(error); Dialog.alert(error);

View file

@ -1,8 +1,12 @@
import _scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'; 'use strict';
require('domtokenlist-shim');
const _scrollIntoViewIfNeeded = require('scroll-into-view-if-needed');
globalThis.DOM = globalThis.DOM || {}; globalThis.DOM = globalThis.DOM || {};
export const scrollIntoViewIfNeeded = (el, overrides = {}) => { const scrollIntoViewIfNeeded = (el, overrides = {}) => {
const { const {
scroll = _scrollIntoViewIfNeeded, scroll = _scrollIntoViewIfNeeded,
} = overrides; } = overrides;
@ -13,3 +17,4 @@ export const scrollIntoViewIfNeeded = (el, overrides = {}) => {
}; };
globalThis.DOM.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded; globalThis.DOM.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;

View file

@ -1,7 +1,9 @@
import {test, stub} from 'supertape'; 'use strict';
import {scrollIntoViewIfNeeded} from './polyfill.js';
test('cloudcmd: client: polyfill: scrollIntoViewIfNeeded', (t) => { const {test, stub} = require('supertape');
const {scrollIntoViewIfNeeded} = require('./polyfill');
test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => {
const scroll = stub(); const scroll = stub();
const el = {}; const el = {};
@ -15,6 +17,6 @@ test('cloudcmd: client: polyfill: scrollIntoViewIfNeeded', (t) => {
}, },
]; ];
t.calledWith(scroll, args, 'should call scrollIntoViewIfNeeded'); t.calledWith(scroll, args, 'should call scrollIntoViewIfNeaded');
t.end(); t.end();
}); });

View file

@ -1,21 +1,21 @@
import '../../css/terminal.css'; 'use strict';
import {promisify} from 'es6-promisify';
import {tryToCatch} from 'try-to-catch'; /* global CloudCmd, gritty */
import {fullstore} from 'fullstore'; const {promisify} = require('es6-promisify');
import exec from 'execon'; const {tryToCatch} = require('try-to-catch');
import load from 'load.js'; const {fullstore} = require('fullstore');
import DOM from '#dom';
import * as Images from '#dom/images'; require('../../css/terminal.css');
const exec = require('execon');
const load = require('load.js');
const DOM = require('../dom');
const Images = require('../dom/images.mjs');
const {Dialog} = DOM; const {Dialog} = DOM;
const {CloudCmd} = globalThis;
const {Key, config} = CloudCmd; const {Key, config} = CloudCmd;
CloudCmd.TerminalRun = { CloudCmd.TerminalRun = exports;
init,
show,
hide,
};
let Loaded; let Loaded;
let Terminal; let Terminal;
@ -40,7 +40,7 @@ const loadAll = async () => {
Loaded = true; Loaded = true;
}; };
export async function init() { module.exports.init = async () => {
if (!config('terminal')) if (!config('terminal'))
return; return;
@ -48,13 +48,9 @@ export async function init() {
await CloudCmd.View(); await CloudCmd.View();
await loadAll(); await loadAll();
} };
export async function show(options = {}) { module.exports.show = promisify((options = {}, fn) => {
return await runTerminal(options);
}
const runTerminal = promisify((options, fn) => {
if (!Loaded) if (!Loaded)
return fn(null, -1); return fn(null, -1);
@ -73,7 +69,9 @@ const runTerminal = promisify((options, fn) => {
}); });
}); });
export function hide() { module.exports.hide = hide;
function hide() {
CloudCmd.View.hide(); CloudCmd.View.hide();
} }
@ -110,7 +108,7 @@ function create(createOptions) {
let commandExit = false; let commandExit = false;
const {socket, terminal} = globalThis.gritty(document.body, options); const {socket, terminal} = gritty(document.body, options);
Socket = socket; Socket = socket;
Terminal = terminal; Terminal = terminal;

View file

@ -1,22 +1,22 @@
import '#css/terminal.css'; 'use strict';
import {tryToCatch} from 'try-to-catch';
import exec from 'execon'; /* global CloudCmd */
import load from 'load.js'; /* global gritty */
import * as Images from '#dom/images'; const {tryToCatch} = require('try-to-catch');
import DOM from '#dom';
require('../../css/terminal.css');
const exec = require('execon');
const load = require('load.js');
const DOM = require('../dom');
const Images = require('../dom/images.mjs');
const loadParallel = load.parallel; const loadParallel = load.parallel;
const {CloudCmd} = globalThis;
const {Dialog} = DOM; const {Dialog} = DOM;
const {Key, config} = globalThis.CloudCmd; const {Key, config} = CloudCmd;
CloudCmd.Terminal = { CloudCmd.Terminal = exports;
init,
show,
hide,
};
let Loaded; let Loaded;
let Terminal; let Terminal;
@ -39,7 +39,7 @@ const loadAll = async () => {
Loaded = true; Loaded = true;
}; };
export async function init() { module.exports.init = async () => {
if (!config('terminal')) if (!config('terminal'))
return; return;
@ -48,9 +48,12 @@ export async function init() {
await CloudCmd.View(); await CloudCmd.View();
await loadAll(); await loadAll();
create(); create();
} };
export function hide() { module.exports.show = show;
module.exports.hide = hide;
function hide() {
CloudCmd.View.hide(); CloudCmd.View.hide();
} }
@ -75,7 +78,7 @@ function create() {
fontFamily: 'Droid Sans Mono', fontFamily: 'Droid Sans Mono',
}; };
const {socket, terminal} = globalThis.gritty(document.body, options); const {socket, terminal} = gritty(document.body, options);
Socket = socket; Socket = socket;
Terminal = terminal; Terminal = terminal;
@ -98,7 +101,7 @@ function authCheck(spawn) {
}); });
} }
export function show() { function show() {
if (!Loaded) if (!Loaded)
return; return;

View file

@ -1,21 +1,23 @@
/* global CloudCmd, DOM */ /* global CloudCmd, DOM */
import createElement from '@cloudcmd/create-element';
import * as Files from '#dom/files';
import {uploadFiles} from '#dom/upload-files';
import * as Images from '#dom/images';
CloudCmd.Upload = { 'use strict';
init,
show,
hide,
};
export async function init() { CloudCmd.Upload = exports;
const createElement = require('@cloudcmd/create-element');
const Files = require('../dom/files');
const Images = require('../dom/images.mjs');
const uploadFiles = require('../dom/upload-files');
module.exports.init = async () => {
Images.show.load('top'); Images.show.load('top');
await CloudCmd.View(); await CloudCmd.View();
} };
export async function show() { module.exports.show = show;
module.exports.hide = hide;
async function show() {
Images.show.load('top'); Images.show.load('top');
const innerHTML = await Files.get('upload'); const innerHTML = await Files.get('upload');
@ -47,7 +49,7 @@ export async function show() {
}); });
} }
export function hide() { function hide() {
CloudCmd.View.hide(); CloudCmd.View.hide();
} }

View file

@ -1,4 +1,6 @@
export const getUserMenu = (menuFn) => { 'use strict';
module.exports = (menuFn) => {
const module = {}; const module = {};
const fn = Function('module', menuFn); const fn = Function('module', menuFn);

View file

@ -1,5 +1,7 @@
import test from 'supertape'; 'use strict';
import {getUserMenu} from './get-user-menu.js';
const test = require('supertape');
const getUserMenu = require('./get-user-menu');
test('user-menu: getUserMenu', (t) => { test('user-menu: getUserMenu', (t) => {
const menu = `module.exports = { const menu = `module.exports = {

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