Compare commits

..

1386 commits

Author SHA1 Message Date
coderaiser
7f28803a82 chore: cloudcmd: actions: lint ☘️ 2026-07-16 15:31:22 +00:00
coderaiser
943bdecd3e test: coverage 2026-07-16 15:29:08 +00:00
coderaiser
e4ff4a78cc chore: cloudcmd: actions: lint ☘️ 2026-07-16 12:27:35 +00:00
coderaiser
fec5538f01 test: common: util: coverage 2026-07-16 12:26:33 +00:00
coderaiser
b7bb363aa8 test: cloudfunc: coverage 2026-07-16 12:23:46 +00:00
coderaiser
cd4a2cec76 chore: cloudcmd: actions: lint ☘️ 2026-07-16 09:28:42 +00:00
coderaiser
0638ca32cb feature: migrate from webpack to rspack 2026-07-16 12:27:20 +03:00
coderaiser
fe185cc93e chore: cloudcmd: actions: lint ☘️ 2026-07-15 20:27:25 +00:00
coderiaser
7edea50296 chore: lint 2026-07-15 23:26:28 +03:00
coderaiser
8258cdb651
chore: rm rulesdir 2026-07-15 21:18:06 +02:00
coderaiser
a9e29eee85 test: cloudcmd: coverage 2026-07-15 10:31:07 +00:00
coderaiser
a52a6aae62 test: coverage 2026-07-15 09:36:03 +00:00
coderaiser
92c79830f5 chore: madrun: test: rm test-e2e 2026-07-15 08:21:03 +00:00
coderiaser
8629f67972 feature: cloudcmd: docker: io: arm 2026-06-29 18:19:08 +03:00
coderiaser
c04d363c46 test: e2e: spawn 2026-06-29 18:16:34 +03:00
coderiaser
046a4ef22e chore: lint 2026-06-29 17:54:20 +03:00
coderiaser
579c9e41d7 feature: cloudcmd: serve-once v4.0.1 2026-06-29 17:48:06 +03:00
coderiaser
6014898610 feature: cloudcmd: @babel/preset-env v8.0.2 2026-06-29 17:48:06 +03:00
coderiaser
ad3bf95e5f feature: cloudcmd: @babel/plugin-transform-optional-chaining v8.0.1 2026-06-29 17:48:05 +03:00
coderiaser
4e22a4edde feature: cloudcmd: @babel/core v8.0.1 2026-06-29 17:48:04 +03:00
coderiaser
9c50fe53e7 feature: cloudcmd: @babel/code-frame v8.0.0 2026-06-29 17:48:04 +03:00
coderiaser
a2a2d51ec1 chore: cloudcmd: v19.19.1 2026-06-15 14:50:37 +03:00
coderiaser
c36c32cb08 chore: lint 2026-06-15 14:49:57 +03:00
coderiaser
78b206e06b feature: client: key: Ctrl + L -> Ctrl + Shift + L: Log out (#466) 2026-06-15 14:49:47 +03:00
coderiaser
ef83a61a0c chore: actions: typos 2026-06-04 17:55:58 +03:00
coderaiser
f5c0aa5c4c Update nodejs.yml 2026-06-04 17:55:44 +03:00
coderaiser
e59167b1fa chore: actions: permissions 2026-06-04 16:05:15 +03:00
coderiaser
ea584aceea chore: actions: typos.ai 2026-06-04 15:42:02 +03:00
coderiaser
4d7116b467 chore: docker: io: git 2026-06-03 23:47:50 +03:00
coderiaser
b149288545 feature: docker: io: NPM_CONFIG_PACKAGE_LOCK=false 2026-06-03 23:40:16 +03:00
coderiaser
9d06279ba8 featur: docker: io: add gh 2026-06-03 23:39:27 +03:00
coderiaser
21e1dcf0e5 chore: actions: typos 2026-06-03 18:46:19 +03:00
coderaiser
b3b0c1d159 chore: package: rm unused optimize-css-assets-webpack-plugin 2026-06-03 13:02:04 +00:00
coderaiser
76d7bf4d73
feature: docker: typos, shellcheck: add 2026-06-02 10:03:23 +02:00
coderaiser
f1d80282eb chore: cloudcmd: actions: lint ☘️ 2026-06-01 20:39:12 +00:00
coderiaser
220cc5557b chore: actions: API_KEY 2026-06-01 23:32:27 +03:00
coderiaser
d2176c93fb chore: actions: typo 2026-06-01 22:56:08 +03:00
coderaiser
c558e44a1c
docs: readme: InstaPods 2026-06-01 13:29:58 +03:00
coderaiser
45ec062bce
docs: readme: deploy on InstaPods 2026-06-01 13:24:07 +03:00
coderaiser
f8edddd442
chore: .nvmrc 2026-05-30 13:12:09 +02:00
coderiaser
fc3d5ac700 chore: cloudcmd: v19.19.0 2026-05-26 23:17:05 +03:00
coderaiser
4ee38db68c feature: client: view: flac: add support 2026-05-26 23:16:40 +03:00
coderiaser
1dfa7ca355 chore: cloudcmd: v19.18.1 2026-05-26 22:09:29 +03:00
coderiaser
40ecef5ec4 feature: cloudcmd: ratelimit: X-Forwarded-For (#437) 2026-05-26 22:08:16 +03:00
coderiaser
cad5e0f44e chore: lint 2026-05-26 21:45:25 +03:00
coderiaser
d139011cc3 chore: cloudcmd: v19.18.0 2026-05-26 00:21:45 +03:00
coderiaser
5d9628ce70 feature: cloudcmd: rate limit: add support (#437) 2026-05-26 00:21:24 +03:00
coderiaser
161bede8db fix: client: menu: @putout/bundle@5.5. 2026-05-24 22:24:54 +03:00
coderiaser
2be395e640 feature: cloudcmd: get rid of manifest: 401 basic auth 2026-05-24 21:40:26 +03:00
coderiaser
447d990f43 fix: cloudcmd: server: rest: sendError 2026-05-21 00:05:43 +03:00
coderaiser
4f4725ac3c chore: cloudcmd: actions: lint ☘️ 2026-05-17 11:25:50 +00:00
coderiaser
eff03299b6 chore: cloudcmd: v19.17.0 2026-05-17 14:24:10 +03:00
coderiaser
a6adf39db2 chore: lint 2026-05-17 14:24:10 +03:00
coderiaser
70ff6fb29f feature: cloudcmd: cssnano-preset-default v8.0.1 2026-05-17 14:24:10 +03:00
coderiaser
eb32fff688 feature: style: owner, mode: improve 2026-05-17 14:24:10 +03:00
coderiaser
6071b66118 fix: css: query: hide time on mobile 2026-05-17 14:24:10 +03:00
coderiaser
83c0b8d3f1 feature: cloudcmd: superc8 v12.6.0 2026-05-17 14:24:10 +03:00
coderiaser
79da2319f8 feature: docker: io: cline: add 2026-05-17 14:24:10 +03:00
coderiaser
68eacb916a feature: qword: add 2026-05-13 00:22:57 +03:00
coderiaser
ccf608a52e chore: lint 2026-05-10 22:51:18 +03:00
coderiaser
4acd294bf6 feature: docker: io: tmux 2026-05-10 22:44:33 +03:00
coderiaser
05c800431d feature: cloudcmd: @supertape/loader-css v1.0.0 2026-05-06 22:21:16 +03:00
coderiaser
6ff1db2710 chore: lint 2026-05-05 23:51:29 +03:00
coderiaser
800a6545a1 feature: cloudcmd: eslint-plugin-n v18.0.1 2026-05-05 23:51:23 +03:00
coderiaser
a985bb36ca feature: cloudcmd: supertape v13.0.0 2026-05-05 23:50:02 +03:00
coderiaser
e7c55e05b8 fix: docker: io: XDG_CONFIG_HOME 2026-05-03 23:57:16 +03:00
coderiaser
4315ec6179 feature: docker: io XDG_CONFIG_HOME: /etc -> /usr/local/etc 2026-05-03 16:08:15 +03:00
coderiaser
628d12fa2f chore: cloudcmd: v19.16.0 2026-05-03 16:02:16 +03:00
coderiaser
d82d0335b2 feature: client: vim: rr for rename file 2026-05-03 16:01:46 +03:00
coderiaser
acfa27cf74 feature: docker: io: nvchad: add 2026-05-03 00:42:54 +03:00
coderiaser
14f009f832 feature: docker: io: bash-completion: add 2026-05-03 00:16:16 +03:00
coderiaser
d1f6476604 chore: docker: io: no git 2026-05-01 21:01:24 +03:00
coderiaser
c6c6014691 feature: docker: io: f4 2026-05-01 20:21:49 +03:00
coderiaser
9a6b89356e feature: docker: io: GOPATH 2026-05-01 19:05:41 +03:00
coderiaser
882fe83623 chore: cloudcmd: v19.15.0 2026-04-29 12:36:10 +03:00
coderiaser
a9bc22ce29 feature: cloudcmd: operation: rm useless checks 2026-04-29 12:36:10 +03:00
coderiaser
36bacfe915 feature: cloudcmd: client: key: vim: cc, mm 2026-04-29 12:36:10 +03:00
coderiaser
bcbef1a3b0 chore: cloudcmd: v19.14.0 2026-04-29 12:36:10 +03:00
coderiaser
591da25cfe feature: cloudcmd: ponse v8.0.0 2026-04-29 12:36:10 +03:00
coderiaser
1e1b073c8c feature: docker: io: /usr/src/cloudcmd -> /usr/local/share/cloudcmd 2026-04-29 12:35:51 +03:00
coderiaser
ea7b828cbb feature: docker: io: ubuntu: resolute 2026-04-26 18:38:39 +03:00
coderiaser
cd6c11ace2 feature: docker: io: far2l 2026-04-26 18:29:45 +03:00
coderiaser
c4beeec63b feature: docker: io: gdu: add 2026-04-26 12:06:57 +03:00
coderiaser
87c13e7cce chore: cloudcmd: v19.13.1 2026-04-21 23:31:22 +03:00
coderiaser
d5cd11e812 feature: cloudcmd: montag v2.0.1 2026-04-21 23:30:41 +03:00
coderiaser
dc94d2db7a feature: docker: io: add latest git version 2026-04-21 22:47:17 +03:00
coderiaser
1637beee78 feature: docker: io: /usr/local/src -> /usr/local/share 2026-04-20 23:46:29 +03:00
coderaiser
0541b7e351 chore: cloudcmd: actions: lint ☘️ 2026-04-19 16:35:25 +00:00
coderiaser
b1bc4e731f feature: Docker: io: pv 2026-04-19 19:34:22 +03:00
coderiaser
7c0dca6038 feature: docker: io: git: master 2026-04-15 16:22:31 +03:00
coderiaser
24fd0a8aa7 chore: cloudcmd: v19.13.0 2026-04-15 02:39:12 +03:00
coderiaser
2b3dc1573d feature: cloudcmd: Ctrl + L: logout 2026-04-15 02:39:12 +03:00
coderiaser
48693d9e24 fix: docker: io: XDG_CACHE_HOME 2026-04-14 00:09:31 +03:00
coderiaser
977a8aaaf4 feature: docker: io: strace: add 2026-04-13 22:40:14 +03:00
coderiaser
15537ec518 chore: docker: io: remove haskell
This reverts commit b0687b54c7.
2026-04-13 22:36:54 +03:00
coderiaser
7d0098fd5c feature: docker: io: XDG_CACHE_HOME=/tmp/cache 2026-04-13 22:36:43 +03:00
coderiaser
da0c4c4704 chore: actions: docker: io: rm cache 2026-04-13 22:36:43 +03:00
coderiaser
b0687b54c7 chore: docker: io: get back haskell 2026-04-13 22:36:43 +03:00
coderiaser
a89e901b2b feature: actions: docker: io: add 2026-04-12 19:00:58 +03:00
coderiaser
ef06a0c310 chore: cloudcmd: v19.12.5 2026-04-12 18:37:50 +03:00
coderiaser
e7242900a4 chore: docker: io: get rid of haskell 2026-04-12 18:37:50 +03:00
coderiaser
a6b01dce49 chore: actions: cache 2026-04-12 18:31:55 +03:00
coderiaser
1cfc1a6f5d feature: docker: io: only amd64 2026-04-12 18:03:46 +03:00
coderiaser
9915757dce chore: cloudcmd: v19.12.4 2026-04-12 14:13:49 +03:00
coderiaser
673fba2e21 feature: docker: io: haskell 2026-04-12 14:13:49 +03:00
coderiaser
d37f8cd36e feature: docker: io: palabra: node 2026-04-12 13:56:16 +03:00
coderiaser
5e509a4d7b chore: cloudcmd: v19.12.3 2026-04-12 13:04:53 +03:00
coderiaser
cb6cabd4c9 feature: docker: io: PALABRA_DIR 2026-04-12 12:54:58 +03:00
coderiaser
f61cc46f41 chore: cloudcmd: v19.12.2 2026-04-11 22:29:12 +03:00
coderiaser
32a8f169d4 feature: docker: io: get rid of haskell: to slow install 2026-04-11 22:29:12 +03:00
coderiaser
aa74123228 feature: docker: io: rizin, yara 2026-04-11 16:47:53 +03:00
coderiaser
27f20d6af8 chore: cloudcmd: v19.12.1 2026-04-11 15:54:19 +03:00
coderiaser
4695480ead feature: docker: io: add PREFIX 2026-04-11 15:54:19 +03:00
coderiaser
bec3f5a799 chore: cloudcmd: v19.12.0 2026-04-09 19:14:43 +03:00
coderiaser
13b15b7bea feature: docker: io: palabra 2026-04-09 19:13:55 +03:00
coderiaser
969bc770e3 chore: cloudcmd: v19.11.14 2026-04-07 20:55:31 +03:00
coderiaser
5ecc48e348 feature: docker: io: rustup env 2026-04-07 20:55:31 +03:00
coderiaser
9efefc23e0 chore: cloudcmd: v19.11.13 2026-04-07 12:34:02 +03:00
coderiaser
6a08479a1b feature: docker: io: BUN_INSTALL 2026-04-07 12:33:26 +03:00
coderiaser
c2563ed6b3 chore: cloudcmd: v19.11.12 2026-04-06 21:22:52 +03:00
coderiaser
46c655546e feature: docker: io: npm_config_cache 2026-04-06 21:22:23 +03:00
coderiaser
4fa0786a39 chore: cloudcmd: v19.11.11 2026-04-06 20:59:36 +03:00
coderiaser
ae2ce3882c feature: docker: io: $DENO_DIR 2026-04-06 20:58:51 +03:00
coderiaser
3c9389c4f0 chore: cloudcmd: v19.11.10 2026-04-06 19:56:41 +03:00
coderiaser
bb6be7abbb feature: docker: io: nvm: node 2026-04-06 19:56:41 +03:00
coderiaser
3aac4e68b4 chore: cloudcmd: v19.11.9 2026-04-06 13:24:03 +03:00
coderiaser
ea96d13fab feature: docker: io: net-tools 2026-04-06 13:23:34 +03:00
coderiaser
f9c5804fb8 chore: cloudcmd: v19.11.8 2026-04-06 12:56:31 +03:00
coderiaser
84f96fa185 feature: docker: io: debian -> ubuntu 2026-04-06 12:56:31 +03:00
coderiaser
8238bd4011 chore: cloudcmd: v19.11.7 2026-04-05 21:38:55 +03:00
coderiaser
36cdef3717 feature: docker: io: bookworm 2026-04-05 21:38:26 +03:00
coderiaser
f8035d6c49 chore: cloudcmd: v19.11.6 2026-04-05 20:53:01 +03:00
coderiaser
845f9bd127 feature: cloudcmd: gritty v10.2.0 2026-04-05 20:52:26 +03:00
coderiaser
b5ec9ce210 chore: cloudcmd: v19.11.5 2026-04-05 00:33:37 +03:00
coderiaser
71a1d78c39 feature: docker: io: hexyl: add 2026-04-05 00:33:37 +03:00
coderiaser
6bbfeb9024 chore: cloudcmd: v19.11.4 2026-04-05 00:33:37 +03:00
coderiaser
a038470816 feature: docker: io: neovim: apt -> github 2026-04-05 00:33:37 +03:00
coderiaser
45e0ff41f4 chore: cloudcmd: v19.11.3 2026-04-04 18:39:59 +03:00
coderiaser
9970ff7629 feature: docker: io: btop 2026-04-04 18:34:13 +03:00
coderiaser
4395a47178 feature: docker: io: $PATH: add $HOME/.local/bin 2026-04-04 18:30:01 +03:00
coderiaser
77daf5028e chore: cloudcmd: v19.11.2 2026-04-04 18:23:34 +03:00
coderiaser
c40ae8e405 feature: docker: io: htop 2026-04-04 18:23:04 +03:00
coderiaser
f463c5c5a7 feature: docker: io: aptitude: add 2026-04-04 17:20:05 +03:00
coderiaser
d1032f09ab feature: docker: io: remove unused 2026-04-04 17:19:09 +03:00
coderiaser
b3e682c0a5 chore: cloudcmd: v19.11.1 2026-04-04 17:11:39 +03:00
coderiaser
ffabdc5276 feature: docker: io: ncdu 2026-04-04 17:11:39 +03:00
coderiaser
7787bfc22e feature: cloudcmd: user-menu: runFromCDN 2026-04-04 17:04:55 +03:00
coderiaser
406429aec8 chore: cloudcmd: v19.11.0 2026-04-04 02:05:28 +03:00
coderiaser
32f89d3874 feature: cloudcmd: user-menu: root 2026-04-04 02:04:40 +03:00
coderiaser
1c0021dbb6 chore: cloudcmd: v19.10.2 2026-04-04 00:06:30 +03:00
coderiaser
df4fb51716 feature: cloudcmd: aleman v2.0.1 2026-04-04 00:05:55 +03:00
coderiaser
314cceb462 chore: cloudcmd: v19.10.1 2026-04-03 23:12:27 +03:00
coderiaser
ceb7ef4ff7 feature: docker: io: keep /var/lib/apt/lists 2026-04-03 23:12:00 +03:00
coderiaser
34fad6ea73 chore: cloudcmd: v19.10.0 2026-04-02 18:19:15 +03:00
chaoliang yan
d12e7bd0c5 fix: distribute: fix event listener leak on socket disconnect (#462) 2026-04-02 18:18:47 +03:00
coderiaser
b8a621ca1b chore: cloudcmd: v19.9.24 2026-04-02 18:13:16 +03:00
coderiaser
1fc1e3734c feature: docker: io: apt-get upgrade 2026-04-02 18:13:16 +03:00
coderiaser
582b7423dc chore: cloudcmd: v19.9.23 2026-04-01 23:25:43 +03:00
coderiaser
e4d7d441fc feature: docker: io: nix 2026-04-01 23:25:43 +03:00
coderiaser
e53aa06cdb chore: cloudcmd: v19.9.22 2026-03-31 00:59:44 +03:00
coderiaser
b4345ed40d feature: docker: io: DENO_DIR 2026-03-31 00:59:02 +03:00
coderiaser
ddbf4d69a4 chore: cloudcmd: v19.9.21 2026-03-31 00:55:49 +03:00
coderiaser
e2855641ca feature: docker: io: rustup 2026-03-31 00:55:49 +03:00
coderiaser
5c6a9a958e fix: css: columns: name: 40% -> 35% 2026-03-31 00:50:35 +03:00
coderiaser
d2a7f6343a chore: cloudcmd: v19.9.20 2026-03-31 00:28:13 +03:00
coderiaser
2b29448062 feature: iocmd: io: nvm 2026-03-31 00:28:13 +03:00
coderiaser
09a8b2bd1c chore: cloudcmd: v19.9.19 2026-03-30 20:54:27 +03:00
coderiaser
6a52b11e46 feature: docker: io: go, rust 2026-03-30 20:53:59 +03:00
coderiaser
9a053ec374 chore: cloudcmd: v19.9.18 2026-03-30 20:36:44 +03:00
coderaiser
6aa496c995 chore: cloudcmd: actions: lint ☘️ 2026-03-30 20:36:43 +03:00
coderiaser
738059f27f feature: docker: io: fzf 2026-03-30 19:19:04 +03:00
coderiaser
3fc8932f4c feature: docker: io: less, el_GR 2026-03-30 19:10:51 +03:00
coderiaser
2db458bc7b chore: cloudcmd: v19.9.17 2026-03-30 00:11:13 +03:00
coderiaser
cf424d6c05 feature: docker: io: command-not-found update 2026-03-30 00:10:48 +03:00
coderiaser
8b9004b4dd chore: cloudcmd: v19.9.16 2026-03-29 23:22:14 +03:00
coderiaser
efb24e229f feature: docker: io: get back debian 2026-03-29 23:22:14 +03:00
coderiaser
19347a2b4c feature: docker: io: add command-not-found 2026-03-29 23:14:51 +03:00
coderiaser
a80f109d19 chore: cloudcmd: v19.9.15 2026-03-29 22:42:08 +03:00
coderiaser
74e008198f feature: docker: io: BUN_INSTALL_CACHE_DIR 2026-03-29 22:42:08 +03:00
coderiaser
d25a08d2bd feature: docker: io: ubuntu 2026-03-29 22:42:08 +03:00
coderiaser
e04c459489 feature: docker: io: net-tools: add 2026-03-29 20:12:12 +03:00
coderiaser
3d2944bd13 chore: cloudcmd: v19.9.14 2026-03-29 19:58:06 +03:00
coderaiser
52a9076f14 chore: cloudcmd: actions: lint ☘️ 2026-03-29 14:01:56 +00:00
coderiaser
7e2d2ede98 test: bin: coverage 2026-03-29 17:00:48 +03:00
coderiaser
6c7709be9c fix: docker: io: PS1 2026-03-29 17:00:48 +03:00
coderiaser
340a639f1b test: bin: cloudcmd: -h 2026-03-29 13:44:18 +03:00
coderiaser
aa5a772f00 chore: cloudcmd: v19.9.13 2026-03-29 13:41:12 +03:00
coderiaser
439b3710cc fix: bin: currify 2026-03-29 13:40:39 +03:00
coderiaser
e41828410c fix: docker: io: xterm-256color 2026-03-29 13:39:54 +03:00
coderiaser
799d86bda4 chore: cloudcmd: v19.9.12 2026-03-29 12:58:45 +03:00
coderiaser
a423739cb8 feature: cloudcmd: bin: get rid of require 2026-03-29 12:58:45 +03:00
coderiaser
47acb8fdf7 chore: cloudcmd: v19.9.11 2026-03-29 12:30:45 +03:00
coderiaser
1f95f18898 feature: cloudcmd: get rid of simport 2026-03-29 12:29:39 +03:00
coderiaser
2aaf0a5385 chore: cloudcmd: v19.9.10 2026-03-29 12:21:37 +03:00
coderiaser
1a44ab0eac fix: docker: io: use node
This reverts commit cc7f9dc75b.
2026-03-29 12:21:37 +03:00
coderiaser
fe3e201143 docs: help: readme from cloudcmd.io 2026-03-29 12:21:37 +03:00
coderiaser
f0deb32362 feature: docker: io: add ja_JP.UTF-8 2026-03-29 00:59:53 +02:00
coderiaser
ba6f66e73e chore: cloudcmd: v19.9.9 2026-03-29 00:56:35 +02:00
coderiaser
cc7f9dc75b feature: docker: io: use ubuntu 2026-03-29 00:55:53 +02:00
coderaiser
f671f79814
fix: docker: io: PS1 environment variable in Dockerfile 2026-03-28 23:42:07 +01:00
coderaiser
fa8875d53c chore: cloudcmd: actions: lint ☘️ 2026-03-28 20:44:34 +00:00
coderiaser
2dd62241f7 chore: cloudcmd: v19.9.8 2026-03-28 22:43:32 +02:00
coderiaser
4d1cd8cdc6 feature: docker: io: ffmpeg 2026-03-28 22:42:38 +02:00
coderiaser
54b56fdc38 feature: cloudcmd: vim: ESC: use only to enable, do not use to disable 2026-03-28 22:40:46 +02:00
coderiaser
785c32e2e3 chore: docker: io: echo 2026-03-27 19:50:01 +02:00
coderiaser
c3f2b7c32f chore: cloudcmd: v19.9.7 2026-03-27 19:46:11 +02:00
coderiaser
110908e221 feature: docker: io: apt-get 2026-03-27 19:45:32 +02:00
coderiaser
8be917da4a chore: cloudcmd: v19.9.6 2026-03-27 18:31:34 +02:00
coderiaser
8db81f30dc feature: docker: io: add UTF-8 2026-03-27 18:31:34 +02:00
coderiaser
56439d5ec2 chore: cloudcmd: v19.9.5 2026-03-26 13:17:11 +02:00
coderiaser
e761cacbd0 fix: columns: name-size-date-time: 20% -> 19% 2026-03-26 13:16:40 +02:00
coderiaser
0bb706995a chore: cloudcmd: v19.9.4 2026-03-26 13:15:02 +02:00
coderiaser
69498ed6ab feature: docker: io: pull.rebase by default 2026-03-26 13:13:44 +02:00
coderiaser
82f8d846a9 chore: docker: get rid of healcheck 2026-03-24 16:41:10 +02:00
coderiaser
1ea4aa67ca chore: cloudcmd: v19.9.3 2026-03-24 16:33:55 +02:00
coderiaser
8763788bad feature: docker: io: healthcheck 2026-03-24 16:33:31 +02:00
coderiaser
c1c67c18af chore: cloudcmd: v19.9.2 2026-03-23 23:55:21 +02:00
coderiaser
09a02074e1 feature: docker: io: git config: add 2026-03-23 23:54:48 +02:00
coderiaser
c448eaa48b feature: docker: io: buni 2026-03-23 23:54:48 +02:00
coderaiser
05ad2fd635 chore: cloudcmd: actions: lint ☘️ 2026-03-23 21:50:02 +00:00
coderiaser
b949a149ec chore: cloudcmd: v19.9.1 2026-03-23 23:49:01 +02:00
coderiaser
6e5318faa7 fix: client: modules: config: input: quote 2026-03-23 23:48:34 +02:00
coderiaser
ec66ba78a7 chore: cloudcmd: v19.9.0 2026-03-23 23:04:38 +02:00
coderiaser
a1216cddeb feature: cloudcmd: add ability to hide port configuration 2026-03-23 23:03:47 +02:00
coderiaser
2f0affd3a3 chore: cloudcmd: v19.8.15 2026-03-23 22:51:10 +02:00
coderiaser
68b2aa780b feature: docker: io: cloudcmd_vim 2026-03-23 22:50:34 +02:00
coderiaser
56e21b4011 chore: cloudcmd: v19.8.14 2026-03-23 22:12:37 +02:00
coderiaser
665ed9c2e9 fix: docker: io: get back port 2026-03-23 22:12:03 +02:00
coderiaser
a44a28f496 chore: cloudcmd: v19.8.13 2026-03-23 19:42:52 +02:00
coderaiser
f51229fc6f feature: docker: io: port 2026-03-23 17:42:02 +00:00
coderiaser
0c9f3922f8 chore: actions: lint 2026-03-23 19:19:04 +02:00
coderiaser
4410cd2665 chore: cloudcmd: v19.8.12 2026-03-23 17:07:19 +00:00
coderiaser
618a561514 feature: docker: io: PS1 2026-03-23 17:07:19 +00:00
coderiaser
682729ac3e chore: cloudcmd: v19.8.11 2026-03-23 18:53:19 +02:00
coderiaser
67f27d8425 feature: docker: io: bun, deno 2026-03-23 18:52:54 +02:00
coderiaser
97672ef5d5 fix: docker: io: apt-get install -> apt-get 2026-03-23 18:41:51 +02:00
coderiaser
e6684df59e chore: cloudcmd: v19.8.10 2026-03-23 18:32:52 +02:00
coderiaser
9493e4de7e featur: docker: io: ls --color 2026-03-23 18:18:10 +02:00
coderiaser
025b005e12 feature: docker: io: add PS1 2026-03-23 18:16:38 +02:00
coderiaser
2d524a4974 chore: cloudcmd: v19.8.9 2026-03-23 15:46:53 +02:00
coderiaser
5e0b681d8b chore: docker: lint 2026-03-23 15:46:11 +02:00
coderiaser
b052cf220d fix: cloudcmd: no time available: --.--.---- -> --:--:-- (#461) 2026-03-23 15:46:00 +02:00
coderiaser
4fa92f9394 chore: cloudcmd: v19.8.8 2026-03-23 12:23:24 +02:00
coderiaser
02dbe56d79 feature: server: user-menu: when error send it 2026-03-23 12:20:31 +02:00
coderiaser
e616cc0bc9 chore: cloudcmd: v19.8.7 2026-03-23 12:13:18 +02:00
coderiaser
ecc76e8b1e feature: docker: io: renamify-cli, runny, redfork 2026-03-23 12:12:28 +02:00
coderiaser
0fbfa0f0cf chore: cloudcmd: v19.8.6 2026-03-23 01:49:08 +02:00
coderiaser
53c072abe4 fix: @putout/plugin-cloudcmd: devDependencies -> dependnecies 2026-03-23 01:48:46 +02:00
coderiaser
4b9922bf92 feature: docker: /usr/src: app -> cloudcmd 2026-03-23 01:43:05 +02:00
coderiaser
1bb77992b0 chore: cloudcmd: v19.8.5 2026-03-23 01:39:37 +02:00
coderiaser
bec991d941 chore: lint 2026-03-23 01:39:15 +02:00
coderiaser
56fc8b838a fix: docker: gritty 2026-03-23 01:31:58 +02:00
coderiaser
df21e81d5a chore: docker: lint 2026-03-23 00:52:54 +02:00
coderiaser
8b83277ec0 chore: cloudcmd: v19.8.4 2026-03-23 00:30:04 +02:00
coderiaser
660f57e914 chore: actions: docker: set-output -> GITHUB_OUTPUT (https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/) 2026-03-23 00:30:02 +02:00
coderiaser
151c56f9a3 feature: Dockerfile: simplify 2026-03-23 00:19:55 +02:00
coderiaser
9e529605e1 feature: docker: vim nvim 2026-03-22 23:22:01 +02:00
coderiaser
ca97c21e9c chore: cloudcmd: v19.8.3 2026-03-22 22:25:32 +02:00
coderiaser
01677e6a09 fix: docker: io: slim -> io 2026-03-22 22:24:44 +02:00
coderiaser
7e35c60677 feature: docker: io: curl wget 2026-03-22 22:19:21 +02:00
coderiaser
a20640ed75 chore: cloudcmd: v19.8.2 2026-03-22 16:02:19 +02:00
coderaiser
e5b221f7ac feature: docker: io: add 2026-03-22 13:59:47 +00:00
coderiaser
2396000a6a chore: cloudcmd: v19.8.1 2026-03-22 02:04:27 +02:00
coderiaser
7661f69f1a test: cloudcmd: coverage 2026-03-22 02:04:27 +02:00
coderaiser
708a4c6b99 feature: docker: slim: add 2026-03-21 23:38:56 +00:00
coderaiser
84f3a3dc79 chore: cloudcmd: actions: lint ☘️ 2026-03-21 22:16:01 +00:00
coderaiser
80613f46fa feature: docker: slim: add 2026-03-21 22:14:53 +00:00
coderaiser
e9a8910c9c chore: cloudcmd: actions: lint ☘️ 2026-03-20 15:56:30 +00:00
coderiaser
e78f5d943f chore: cloudcmd: v19.8.0 2026-03-20 17:55:27 +02:00
coderiaser
10934b3ad9 feature: cloudcmd: add ability to show modification time (#230) 2026-03-20 17:54:58 +02:00
coderaiser
ddf5a59f70 chore: cloudcmd: actions: lint ☘️ 2026-03-20 14:55:06 +00:00
coderiaser
59037f2c18 fix: cloudcmd: bin: --show-config 2026-03-20 16:53:57 +02:00
coderiaser
1444550865 chore: cloudcmd: v19.7.1 2026-03-18 12:46:21 +02:00
coderiaser
b0c1d36c81 feature: cloudcmd: @cloudcmd/fileop v9.0.7 (#460) 2026-03-18 12:45:30 +02:00
coderaiser
10b225a337 chore: cloudcmd: actions: lint ☘️ 2026-03-17 19:12:28 +00:00
coderiaser
289f67e0d3 chore: cloudcmd: v19.7.0 2026-03-17 21:11:22 +02:00
coderiaser
daf8387516 feature: cloudfunc: override date format (#459) 2026-03-17 21:10:56 +02:00
coderaiser
170b4da211 chore: cloudcmd: actions: lint ☘️ 2026-03-17 18:45:13 +00:00
coderiaser
c9b27bb514 chore: cloudcmd: v19.6.9 2026-03-17 20:44:05 +02:00
coderiaser
5c19ddba61 chore: lint 2026-03-17 20:43:43 +02:00
coderiaser
b28a070a28 feature: cloudcmd: redzip v4.6.1 2026-03-17 20:41:28 +02:00
coderiaser
43c5a01102 feature: cloudcmd: css-minimizer-webpack-plugin v8.0.0 2026-03-17 20:41:21 +02:00
coderiaser
15dcae5c5e feature: cloudcmd: webpack-cli v7.0.2 2026-03-17 20:41:15 +02:00
coderiaser
5976da81df feature: cloudcmd: @cloudcmd/fileop v9.0.5 2026-02-28 15:50:32 +02:00
coderiaser
37cb83f2cf feature: cloudcmd: redzip v4.6.0 2026-02-28 15:50:27 +02:00
coderiaser
dd5f392293 chore: cloudcmd: v19.6.8 2026-02-27 01:14:17 +02:00
coderiaser
15fab5142f feature: cloudcmd: copymitter v10.3.0 (#458) 2026-02-27 01:13:19 +02:00
coderiaser
d2801ca896 chore: cloudcmd: v19.6.7 2026-02-26 20:18:27 +02:00
coderiaser
68c7d0be3b feature: cloudcmd: onezip v7.0.0 2026-02-26 20:17:28 +02:00
coderiaser
bfbaae9ec0 chore: cloudcmd: v19.6.6 2026-02-26 19:24:05 +02:00
coderiaser
3987cc82c1 feature: cloudcmd: redzip v4.5.1 (#457) 2026-02-26 19:23:14 +02:00
coderiaser
559b7424b6 chore: cloudcmd: v19.6.5 2026-02-26 16:06:55 +02:00
coderiaser
964ae989f2 feature: cloudcmd: redzip v4.5.0 (#457) 2026-02-26 16:06:21 +02:00
coderaiser
ff4e02c210 chore: cloudcmd: actions: lint ☘️ 2026-02-25 15:04:30 +00:00
coderiaser
9ead35242a chore: lint: scan -> fix 2026-02-25 17:03:22 +02:00
coderiaser
061ab38b24 chore: madrun: simplify 2026-02-25 17:02:22 +02:00
coderiaser
07bc9e2782 chore: cloudcmd: v19.6.4 2026-02-25 17:00:54 +02:00
coderiaser
a66eeda37e feature: cloudcmd: copymitter v10.2.0 (coderaiser/cloudcmd#457) 2026-02-25 16:59:19 +02:00
coderiaser
4340533acf feature: cloudcmd: c8 v11.0.0 2026-02-25 16:59:12 +02:00
coderiaser
394939e0d5 chore: cloudcmd: v19.6.3 2026-02-24 23:21:34 +02:00
coderiaser
0857711f61 feature: cloudcmd: redzip v4.2.0 (#457) 2026-02-24 23:21:27 +02:00
coderiaser
5aba36cca0 chore: cloudcmd: v19.6.2 2026-02-24 21:26:52 +02:00
coderiaser
3d2e5feda2 chore: lint 2026-02-24 21:24:15 +02:00
coderiaser
321a54dd59 feature: cloudcmd: @cloudcmd/fileop v9.0.2 (#457) 2026-02-24 21:23:33 +02:00
coderiaser
feb6eb2468 chore: cloudcmd: v19.6.1 2026-02-24 15:12:26 +02:00
coderiaser
7c5ac408e5 feature: cloudcmd: @cloudcmd/fileop v9.0.1 (#457) 2026-02-24 15:11:44 +02:00
coderiaser
37ecb3d7ac docs: readme: import: default -> named 2026-02-21 17:36:59 +02:00
coderiaser
52023c82cb chore: cloudcmd: v19.6.0 2026-02-21 17:34:43 +02:00
coderiaser
6d19bf2e14 feature: common: object.omit -> omit 2026-02-21 17:33:20 +02:00
coderiaser
d7214aba70 chore: cloudcmd: v19.5.1 2026-02-18 19:57:27 +02:00
coderiaser
6e1cf4ef52 feature: cloudcmd: supermenu v5.0.0 2026-02-18 19:56:56 +02:00
coderiaser
3aed798ff1 chore: cloudcmd: v19.5.0 2026-02-18 19:46:35 +02:00
coderaiser
957fbc6533 chore: cloudcmd: actions: lint ☘️ 2026-02-18 17:41:57 +00:00
coderiaser
b20539ef2b feature: common: entity: encode {,} 2026-02-18 19:40:55 +02:00
coderiaser
7ef134f46f feature: cloudcmd: rendy v5.0.0 2026-02-18 19:21:47 +02:00
coderiaser
cdeb40d191 chore: cloudcmd: v19.4.1 2026-02-18 14:55:46 +02:00
coderiaser
1e18d51342 feature: cloudcmd: @cloudcmd/fileop v9.0.0 2026-02-18 14:54:54 +02:00
coderiaser
8836d816d5 chore: cloudcmd: v19.4.0 2026-02-18 14:16:01 +02:00
coderiaser
ff9fcb1189 chore: lint 2026-02-18 14:15:22 +02:00
coderiaser
45cf9bafcd fix: menu: prefix (#456) 2026-02-18 14:09:30 +02:00
coderiaser
3e647290fd feature: cloudcmd: redlint v6.0.0 2026-02-18 14:04:19 +02:00
coderiaser
800ed012ba feature: cloudcmd: putout v42.0.5 2026-02-18 14:04:18 +02:00
coderiaser
525c17d4bd feature: cloudcmd: madrun v13.0.0 2026-02-18 14:04:17 +02:00
coderiaser
4424749978 feature: cloudcmd: eslint-plugin-putout v31.0.0 2026-02-18 14:04:17 +02:00
coderiaser
9fc3c389bf chore: cloudcmd: v19.3.9 2026-02-15 23:46:59 +02:00
coderiaser
9ffe3ef137 feature: cloudcmd: copymitter v10.0.0 (#457) 2026-02-15 23:44:18 +02:00
coderiaser
a9105e6797 chore: cloudcmd: v19.3.8 2026-02-15 22:35:16 +02:00
coderiaser
3438a0c6a1 chore: webpack: add css minification 2026-02-15 22:34:39 +02:00
coderaiser
e969b3c2b3 chore: cloudcmd: actions: lint ☘️ 2026-02-15 20:25:32 +00:00
coderiaser
d274a2b3db fix: spinner (#456) 2026-02-15 22:24:34 +02:00
coderiaser
5c9ae0a0de chore: cloudcmd: v19.3.7 2026-02-13 16:25:48 +02:00
coderiaser
8fd79a2792 feature: cloudcmd: win32 v8.0.0 2026-02-13 16:24:43 +02:00
coderiaser
9267f7a28d chore: cloudcmd: v19.3.6 2026-02-12 14:07:31 +02:00
coderiaser
3479c3b3f2 test: server: route: console-io -> gritty 2026-02-12 14:06:46 +02:00
coderiaser
ac94eccd90 feature: cloudcmd: konsole: named 2026-02-12 14:00:34 +02:00
coderiaser
144e4a3468 feature: cloudcmd: gritty v10.0.0 2026-02-12 14:00:34 +02:00
coderiaser
938f9e7674 feature: cloudcmd: console-io v15.0.1 2026-02-10 20:01:56 +02:00
coderiaser
c229160605 chore: cloudcmd: v19.3.5 2026-02-08 20:41:09 +02:00
coderiaser
fb40bd9cec feature: rm cssnano: has no sense for spinner, option disabled by default (https://svgo.dev/docs/plugins/convertPathData/) 2026-02-08 20:39:03 +02:00
coderiaser
76125be9f5 feature: cloudcmd: eslint v10.0.0 2026-02-07 16:51:36 +02:00
coderiaser
d0730e12d9 chore: cloudcmd: v19.3.4 2026-02-06 22:00:21 +02:00
coderiaser
66a08c7f71 feature: cloudcmd: deepword v11.0.0 2026-02-06 21:59:42 +02:00
coderiaser
f3a0d3a3fe chore: cloudcmd: v19.3.3 2026-02-05 14:22:52 +02:00
coderiaser
4a5a56f403 feature: cloudcmd: dword v16.0.0 2026-02-05 14:22:09 +02:00
coderiaser
846dd1bf22 chore: cloudcmd: v19.3.2 2026-02-04 20:52:09 +02:00
coderiaser
99d8435e9d fix: cloudcmd: exports 2026-02-04 20:51:08 +02:00
coderiaser
30f42e9495 feature: cloudcmd: restafary v13.0.1 2026-02-04 20:50:00 +02:00
coderiaser
f84ce8534e feature: cloudcmd: edward v16.0.0 2026-02-03 21:31:42 +02:00
coderiaser
a266c1452c fix: cloudcmd: default -> named 2026-02-03 20:08:58 +02:00
coderaiser
b61818fe62 chore: cloudcmd: actions: lint ☘️ 2026-02-03 14:27:37 +00:00
coderiaser
dad4cd1f57 chore: cloudcmd: v19.3.1 2026-02-03 16:23:35 +02:00
coderiaser
f5cabc7696 test: static: user-menu: compare directories 2026-02-03 16:23:21 +02:00
coderiaser
6e3ba27131 fix: Closing X in editor disappeared (#455) 2026-02-03 16:23:13 +02:00
coderaiser
e68338a17b chore: cloudcmd: actions: lint ☘️ 2026-02-03 14:16:16 +00:00
coderiaser
c7095e2570 chore: cloudcmd: v19.3.0 2026-02-03 16:11:23 +02:00
coderiaser
31570e4fae chore: current-file: typo 2026-02-03 16:11:23 +02:00
coderiaser
307583e374 chore: lint 2026-02-03 15:53:21 +02:00
coderiaser
09601047e5 chore: cssnano: back to cjs 2026-02-03 15:50:38 +02:00
coderiaser
3d19f4727a test: user-menu: coverage 2026-02-03 15:45:45 +02:00
coderiaser
4533a25c6f feature: cloudcmd: migrate to ESM 2026-02-03 15:03:49 +02:00
coderiaser
e8a81c49ea feature: client: dom: events: migrate to ESM 2026-02-03 14:19:16 +02:00
coderiaser
071141bc5a feature: client: terminal-run: migrate to ESM 2026-02-03 14:11:52 +02:00
coderiaser
1db64d7324 chore: cloudcmd: v19.2.0 2026-02-03 14:06:43 +02:00
coderiaser
bb32f7c448 feature: client: dom: migrate to ESM 2026-02-03 14:06:12 +02:00
coderiaser
457c83dba9 feature: client: migrate to ESM 2026-02-03 13:35:24 +02:00
coderiaser
f8a941bdd9 feature: client: modules: markdown: migrate to ESM 2026-02-03 13:29:12 +02:00
coderiaser
9d6cffafdc feature: client: polifyll: migrate to ESM 2026-02-03 13:27:16 +02:00
coderiaser
5b704d0684 feature: client: edit-names: migrate to ESM 2026-02-03 12:23:26 +02:00
coderiaser
2c2ca8eb39 feature: client: edit-file: migrate to ESM 2026-02-03 12:20:47 +02:00
coderiaser
c9f57c5f20 feature: client: modules: edit-file-vim: migrate to ESM 2026-02-03 01:07:24 +02:00
coderiaser
327ac9dee6 feature: client: modules: help: migrate to ESM 2026-02-03 00:58:16 +02:00
coderiaser
dc34ee8714 feature: cloudcmd: @putout/plugin-cloudcmd v5.0.0 2026-02-03 00:56:39 +02:00
coderiaser
3cd3695be6 feature: client: modules: edit-names-vim: migrate to ESM 2026-02-03 00:56:05 +02:00
coderiaser
dfcbfd632e feature: client: modules: terminal: migrate to ESM 2026-02-03 00:36:40 +02:00
coderiaser
bf50550911 chore: cloudcmd: v19.1.21 2026-02-02 22:39:21 +02:00
coderiaser
e3ad330ebc feature: client: konsole: migrate to ESM 2026-02-02 22:38:53 +02:00
coderiaser
983fd9af3d feature: client: edit: migrate to ESM 2026-02-02 22:37:02 +02:00
coderiaser
ebabad948f feature: common: entity: migrate to ESM 2026-02-02 22:23:33 +02:00
coderiaser
c3b71653bb feature: client: dom: images -> #images 2026-02-02 19:18:08 +02:00
coderiaser
8cad7514e7 feature: common: cloudfunc -> #common/cloudfunc 2026-02-02 19:16:21 +02:00
coderiaser
1f174870ab feature: client: view: migrate to ESM 2026-02-02 19:10:56 +02:00
coderiaser
7173f6cb18 feature: cloudcmd: smalltalk v5.0.0 2026-02-02 19:05:52 +02:00
coderiaser
0d7b037ac2 client: modules: command-line: migrate to ESM 2026-02-01 23:31:20 +02:00
coderiaser
dae57c3d20 chore: cloudcmd: v19.1.20 2026-01-31 22:48:38 +02:00
coderiaser
c1014c9cc0 feature: client: dom: operations: rename-current: migrate to ESM 2026-01-31 22:47:52 +02:00
coderiaser
2e486f8bed feature: cloudcmd: restafary v13.0.0 2026-01-31 22:45:48 +02:00
coderiaser
6addb29f7f feature: cloudcmd: redzip v4.0.0 2026-01-31 22:28:28 +02:00
coderiaser
c0988590a7 chore: lint 2026-01-30 21:43:39 +02:00
coderiaser
5e742c5b82 chore: cloudcmd: v19.1.19 2026-01-30 21:04:48 +02:00
coderiaser
2a0feac7e0 feature: cloudcmd: @cloudcmd/modal v4.0.0 2026-01-30 21:03:43 +02:00
coderiaser
603e232ca2 chore: cloudcmd: v19.1.18 2026-01-30 00:56:11 +02:00
coderiaser
73fa4961c0 feature: client: modules: user-menu: migrate to ESM 2026-01-30 00:55:32 +02:00
coderiaser
ebfdf8c087 feature: client: modules: operation: migrate to ESM 2026-01-29 18:52:38 +02:00
coderiaser
ad6831712b feature: client: modules: menu: migrate to ESM 2026-01-29 18:49:38 +02:00
coderiaser
9e65c52ff1 test: client: dom: index: migrate to ESM 2026-01-29 18:47:30 +02:00
coderiaser
0054cfa363 feature: client: dom: load-remote: migrate to ESM 2026-01-29 18:46:43 +02:00
coderiaser
145bc48722 chore: cloudcmd: types 2026-01-29 18:33:05 +02:00
coderiaser
e4d0ece093 feature: client: dom: rest: migrate to ESM 2026-01-29 18:31:10 +02:00
coderiaser
c704ffe47a feature: client: dom: storage: migrate to ESM 2026-01-29 18:22:12 +02:00
coderiaser
5182cb8107 feature: client: modules: upload: migrate to ESM 2026-01-29 18:18:39 +02:00
coderiaser
9543f1adb0 feature: client: dom: upload-files: migrate to ESM 2026-01-29 18:17:24 +02:00
coderiaser
21d765fb9f chore: cloudcmd: v19.1.17 2026-01-29 16:14:04 +02:00
coderiaser
f9c2831939 feature: client: dom: migrate to ESM 2026-01-29 16:13:41 +02:00
coderiaser
9d2c4e4a0d feature: client: dom: cmd: move out 2026-01-29 14:51:15 +02:00
coderiaser
5a2aa70f90 feature: client: dom: simplify require 2026-01-29 14:45:02 +02:00
coderiaser
dee50a8548 feature: client: dom: files: migrate to ESM 2026-01-29 14:27:19 +02:00
coderiaser
23c0d77042 feature: client: dom: dom-tree: migrate to ESM 2026-01-29 14:25:56 +02:00
coderiaser
cf2c641564 feature: client: directory: migrate to ESM 2026-01-29 14:25:56 +02:00
coderiaser
0eb802e7bd feature: client: dom: dialog: migrate to ESM 2026-01-29 14:18:34 +02:00
coderiaser
8223800ac6 chore: madrun: wisdom: CI 2026-01-28 23:07:39 +02:00
coderiaser
041d889970 chore: cloudcmd: v19.1.16 2026-01-28 23:05:36 +02:00
coderiaser
4c242631fa fix: css: spinner: do not minify svg 2026-01-28 23:05:08 +02:00
coderiaser
75938809ea chore: cloudcmd: v19.1.15 2026-01-28 23:01:32 +02:00
coderiaser
265c0b491a feature: client: key: vim: migrate to ESM 2026-01-28 23:00:50 +02:00
coderiaser
9ab2cec877 chore: lint 2026-01-28 22:54:29 +02:00
coderiaser
82bb338c1f test: client: key: migrate to ESM 2026-01-28 22:53:36 +02:00
coderiaser
3bc49f02f8 feature: client: set-current-by-char: migrate to ESM 2026-01-28 22:52:32 +02:00
coderiaser
4f1a797cd7 chore: cloudcmd: v19.1.14 2026-01-28 21:04:24 +02:00
coderiaser
f8a63b5a13 feature: client: key: binder: migrate to ESM 2026-01-28 21:03:53 +02:00
coderiaser
d250bf0223 test: client: get rid of mock-require 2026-01-28 20:59:38 +02:00
coderiaser
e1d2f01f57 test: client: vim: get rid of mock-require 2026-01-28 20:58:46 +02:00
coderiaser
2cc97f33f1 feature: client: cloud: migrate to ESM 2026-01-28 20:57:26 +02:00
coderiaser
b5acbafab9 chore: lint 2026-01-28 16:17:52 +02:00
coderiaser
bf1ffd0751 chore: cloudcmd: v19.1.13 2026-01-27 21:18:53 +02:00
coderiaser
41b5a96fa6 feature: client: load: migrate to ESM 2026-01-27 21:18:16 +02:00
coderiaser
116430e542 chore: cloudcmd: v19.1.12 2026-01-27 20:37:28 +02:00
coderiaser
27a52d33d0 feature: client: sw: migrate to ESM 2026-01-27 20:35:38 +02:00
coderiaser
54c89e52f8 client: config: migrate to ESM 2026-01-27 20:20:38 +02:00
coderiaser
6b049d954b feature: client: sw: register: migrate to ESM 2026-01-27 13:41:21 +02:00
coderiaser
5a16b018c5 chore: cloudcmd: v19.1.11 2026-01-25 22:54:09 +02:00
coderiaser
f849b8423d feature: client: listeners: migrate to ESM 2026-01-25 22:53:32 +02:00
coderiaser
091f901789 feature: client: listeners: simplify 2026-01-25 22:44:42 +02:00
coderiaser
3c0c7c3044 client: key: migrate to ESM 2026-01-25 22:42:57 +02:00
coderiaser
3c1a82e702 feature: client: key: view: get rid of mock-require 2026-01-25 22:41:02 +02:00
coderiaser
6fe61b775b chore: cloudcmd: v19.1.10 2026-01-25 21:43:26 +02:00
coderiaser
dc5867b006 feature: client: key: vim: get rid of mock-require 2026-01-25 21:42:57 +02:00
coderiaser
77b70b2142 feature: cloudcmd: aleman v2.0.0 2026-01-25 21:36:47 +02:00
coderiaser
78e87796df chore: cloudcmd: v19.1.9 2026-01-21 20:08:24 +02:00
coderiaser
75ad4415c4 feature: cloudcmd: @putout/eslint-flat v4.0.0 2026-01-21 20:06:53 +02:00
coderiaser
c5d9bd7c1f feature: client: key: vim: get rid of mock-require 2026-01-21 20:06:35 +02:00
coderiaser
f437a52ff0 feature: client: images: migrate to EMS 2026-01-21 19:59:27 +02:00
coderiaser
7192a56e94 feature: client: dom: current-file: migrate to ESM 2026-01-21 19:50:30 +02:00
coderiaser
b9dd4f2676 chore: cloudcmd: v19.1.8 2026-01-20 23:43:51 +02:00
coderiaser
e8cf3c92f9 chore: lint 2026-01-20 23:43:06 +02:00
coderiaser
d574a93d6d feature: client: key: migrate to ESM 2026-01-20 23:41:26 +02:00
coderiaser
8a769fd512 fix: client: modules: operation: no update after copy 2026-01-20 23:41:26 +02:00
coderiaser
3b409074c1 feature: client: modules: operation: migrate to ESM 2026-01-20 23:41:26 +02:00
coderiaser
3b6b0b5a5b feature: client: buffer: migrate to ESM 2026-01-20 23:41:26 +02:00
coderiaser
8876f050e0 feature: cloudcmd: eslint-plugin-putout v30.0.0 2026-01-20 23:41:26 +02:00
coderiaser
8507282d55 test: cloudcmd: client: key: rm skip 2026-01-20 23:41:26 +02:00
coderaiser
f61b21eecc chore: cloudcmd: actions: lint ☘️ 2026-01-17 12:36:49 +00:00
coderiaser
242820b7cf chore: cloudcmd: v19.1.7 2026-01-17 14:36:00 +02:00
coderiaser
dd240ba9b2 test: client: key: vim: rm skip 2026-01-17 14:36:00 +02:00
coderaiser
4b945c0047 chore: cloudcmd: actions: lint ☘️ 2026-01-17 12:23:41 +00:00
coderiaser
23a6a6981a feature: client: dom/events -> #dom/events 2026-01-17 14:22:49 +02:00
coderiaser
9cebb2416f feature: client: dom: events: migrate to ESM 2026-01-17 14:22:49 +02:00
coderiaser
a94fa0d465 feature: client: cloudcmd: migrate to ESM 2026-01-17 14:22:49 +02:00
coderiaser
3bdf47a5bb feature: client: migrate to ESM 2026-01-17 14:22:49 +02:00
coderaiser
0ccd109a50 chore: cloudcmd: actions: lint ☘️ 2026-01-16 22:25:24 +00:00
coderaiser
6b0bd2e1de
refactor: client: move out inner functions 2026-01-16 23:24:31 +01:00
coderiaser
c23a6a12c8 chore: cloudcmd: v19.1.6 2026-01-16 23:56:12 +02:00
coderiaser
a523ef65f5 fix: tests 2026-01-16 23:55:32 +02:00
coderaiser
0971ac4e94 chore: cloudcmd: actions: lint ☘️ 2026-01-16 21:49:12 +00:00
coderiaser
64654e8d5c feature: common: cloudfunc: migrate to ESM 2026-01-16 23:48:19 +02:00
coderaiser
a063353a27 chore: cloudcmd: actions: lint ☘️ 2026-01-16 21:21:49 +00:00
coderiaser
add31607f9 feature: common: cloudfunc: get rid of bas64 2026-01-16 23:20:54 +02:00
coderiaser
e36de00ce8 feature: modulas: migrate to ESM 2026-01-16 23:20:54 +02:00
coderaiser
2c1210bbb1 chore: cloudcmd: actions: lint ☘️ 2026-01-16 17:14:25 +00:00
coderiaser
85ebf21122 chore: cloudcmd: v19.1.5 2026-01-16 19:13:27 +02:00
coderiaser
450f14614d feature: client: improve testability 2026-01-16 19:12:44 +02:00
coderaiser
f75bf4a401 chore: cloudcmd: actions: lint ☘️ 2026-01-16 16:28:05 +00:00
coderiaser
d979e94927 feature: server: env: migrate to ESM 2026-01-16 18:27:01 +02:00
coderiaser
8de9bd0847 chore: webpack: migrate to ESM 2026-01-16 14:19:35 +02:00
coderiaser
e178321be9 chore: cloudcmd: v19.1.4 2026-01-15 22:14:35 +02:00
coderiaser
4b84d20bb0 chore: cloudcmd 2026-01-15 22:14:01 +02:00
coderiaser
6e778a35ba feature: client: sort: migrate to ESM 2026-01-15 18:03:09 +02:00
coderiaser
e27ef51d43 feature: client: sort: migrate to ESM 2026-01-15 18:02:54 +02:00
coderiaser
917f585137 feature: client: load-module: migrate to ESM 2026-01-15 17:55:17 +02:00
coderiaser
9950cacad9 feature: client: get-json-from-file-table: migrate to ESM 2026-01-15 17:53:45 +02:00
coderiaser
457e23f31d chore: cloudcmd: v19.1.3 2026-01-15 16:33:27 +02:00
coderiaser
cfa0b5e382 test: cloudcmd: client: key 2026-01-15 16:32:28 +02:00
coderiaser
f903c5c9c5 feature: cloudcmd: multi-rename v3.0.0 2026-01-15 16:22:47 +02:00
coderiaser
08dd5ac183 chore: cloudcmd: v19.1.2 2026-01-14 14:38:46 +02:00
coderiaser
9e2c5ac635 fix: client: edit-names: group rename not renaming (#453) 2026-01-14 14:37:29 +02:00
coderiaser
6856207d0d feature: server: env -> env.parse 2026-01-14 13:20:37 +02:00
coderiaser
f0dcbe946f fix: client: key: config 2026-01-14 13:18:53 +02:00
coderiaser
008279df57 chore: rm eslintrc 2026-01-13 16:03:49 +02:00
coderiaser
dc99417c27 feature: client: key: get rid of mock-require 2026-01-13 15:19:32 +02:00
coderiaser
12751646fe test: ployfill: get rid of mock-require 2026-01-13 13:47:14 +02:00
coderaiser
8234201a39 chore: cloudcmd: actions: lint ☘️ 2026-01-13 11:37:25 +00:00
coderiaser
4bb7d704b4 feature: client: modules: view: get rid of mock-require 2026-01-13 13:36:35 +02:00
coderiaser
feb5aad36b test: client: sw: register: get rid of mock-require 2026-01-13 13:28:31 +02:00
coderiaser
c231fca334 test: client: dom: operations: rename-current: get rid of mock-require 2026-01-12 21:48:18 +02:00
coderiaser
14452d05b6 test: client: dom: io: get rid of mock-require 2026-01-12 21:40:54 +02:00
coderiaser
5c1ad5f8ff chore: cloudcmd: v19.1.1 2026-01-12 13:33:21 +02:00
coderiaser
5b4bb90d61 test: client: dom: goTiDirectory: get rid of mock-require 2026-01-12 13:32:48 +02:00
coderiaser
5cc6f79d6a feature: cloudcmd: @cloudcmd/stub v5.0.0 2026-01-12 13:15:36 +02:00
coderiaser
024bc41345 feature: cloudcmd: fullstore v4.0.0 2026-01-12 13:15:36 +02:00
coderiaser
fb115c675e test: common: util: get rid of mock-require 2026-01-12 13:10:05 +02:00
coderiaser
53f6f9e76f feature: cloudcmd: globals v17.0.0 2026-01-04 22:43:24 +02:00
coderiaser
6d21c539d4 feature: cloudcmd: madrun v12.1.0 2026-01-04 22:43:21 +02:00
coderiaser
253389ea7b feature: cloudcmd: supertape v12.0.0 2026-01-04 22:43:17 +02:00
coderiaser
cdf11f7483 chore: cloudcmd: v19.1.0 2025-12-31 14:14:29 +02:00
coderiaser
0ff16314d7 feature: cloudcmd: redlint v5.0.0 2025-12-31 14:13:33 +02:00
coderiaser
cc889bda4f chore: lint 2025-12-31 14:13:10 +02:00
coderiaser
43edba8cb8 feature: cloudcmd: try-to-catch v4.0.0 2025-12-31 13:44:04 +02:00
coderiaser
06f3b78256 feature: cloudcmd: try-catch v4.0.4 2025-12-31 13:44:00 +02:00
coderiaser
dfcd655780 feature: deno config: add 2025-12-24 16:47:48 +02:00
coderiaser
ab20a462db feature: server: bun support (oven-sh/bun#25674) 2025-12-24 15:53:38 +02:00
coderiaser
2ec57132ed chore: cloudcmd: v19.0.17 2025-12-24 14:42:43 +02:00
coderiaser
0222d177fc feature: cloudcmd: gritty v9.0.0 2025-12-24 14:25:32 +02:00
coderiaser
db0e0aef73 chore: cloudcmd: v19.0.16 2025-12-05 19:32:16 +02:00
coderiaser
14ec19e80a feature: cloudcmd: find-up v8.0.0 2025-12-05 19:30:34 +02:00
coderiaser
e6a00979ff feature: cloudcmd: eslint-plugin-putout v29.0.2 2025-12-05 19:29:52 +02:00
coderiaser
5b5352c7d9 feature: cloudcmd: putout v41.0.0 2025-12-05 19:29:47 +02:00
coderiaser
78ddbd8a39 chore: cloudcmd: v19.0.15 2025-11-28 13:07:28 +02:00
coderiaser
0067653109 feature: cloudcmd: aleman v1.16.5 2025-11-28 13:06:31 +02:00
coderiaser
5a2ac765d4 chore: cloudcmd: v19.0.14 2025-11-27 20:17:40 +02:00
coderiaser
3ceb9a8c59 feature: cloudcmd: open v11.0.0 2025-11-27 20:16:41 +02:00
coderiaser
2a525e9bbb fix: aleman: copy paste in text editor (#449) 2025-11-27 20:16:05 +02:00
coderaiser
a49e963dc9 chore: lint 2025-10-26 15:17:50 +02:00
coderiaser
1037a1a264 chore: cloudcmd: v19.0.13 2025-09-26 13:21:16 +03:00
coderiaser
8477f3e466 feature: cloudcmd: aleman v1.16.3 (#446) 2025-09-26 13:20:41 +03:00
coderiaser
e5d004c0af chore: cloudcmd: v19.0.12 2025-09-25 20:26:41 +03:00
coderiaser
836e908e11 feature: cloudcmd: aleman v1.16.2 2025-09-25 20:25:54 +03:00
coderiaser
9fd4a451ab chore: cloudcmd: v19.0.11 2025-09-24 18:08:25 +03:00
coderiaser
f4386a6f0f feature: cloudcmd: aleman v1.16.1 2025-09-24 18:07:18 +03:00
coderaiser
91f9c0a1aa
chore: putout: client: disable nodejs 2025-09-24 08:13:59 +02:00
coderiaser
babeb9fb85 chore: cloudcmd: v19.0.10 2025-09-23 21:16:46 +03:00
coderiaser
2e667ba693 feature: cloudcmd: aleman v1.15.0 2025-09-23 21:16:21 +03:00
coderiaser
07d18cfe96 chore: cloudcmd: v19.0.9 2025-09-22 17:58:22 +03:00
coderiaser
60c56164b0 feature: cloudcmd: aleman v1.14.4 2025-09-22 17:57:42 +03:00
coderiaser
d8453236c2 chore: cloudcmd: v19.0.8 2025-09-20 13:14:00 +03:00
coderiaser
efe81320e1 feature: cloudcmd: aleman v1.14.3 2025-09-20 13:13:42 +03:00
coderaiser
14e6754be3 chore: cloudcmd: actions: lint ☘️ 2025-09-18 16:56:23 +00:00
coderiaser
3033aed2e3 chore: cloudcmd: v19.0.7 2025-09-18 19:55:21 +03:00
coderiaser
5b972e2e4a feature: cloudcmd: aleman v1.14.0 2025-09-18 19:54:51 +03:00
coderaiser
543e08a837 chore: cloudcmd: actions: lint ☘️ 2025-09-17 20:17:02 +00:00
coderiaser
2a8109f090 chore: cloudcmd: v19.0.6 2025-09-17 23:16:00 +03:00
coderiaser
39a24028ac feature: cloudcmd: aleman v1.13.0 2025-09-17 23:15:17 +03:00
coderiaser
3f3c644500 chore: cloudcmd: v19.0.5 2025-09-16 17:13:04 +03:00
coderiaser
38dd510158 feature: cloudcmd: aleman v1.12.4 2025-09-16 17:12:17 +03:00
coderiaser
64df81bc45 fix: cloudcmd: client: listeners: f9: stopPropagation 2025-09-16 17:11:32 +03:00
coderiaser
8366b3bbe2 chore: cloudcmd: v19.0.4 2025-09-15 22:11:21 +03:00
coderiaser
66db798c35 feature: cloudcmd: aleman v1.12.3 2025-09-15 22:10:56 +03:00
coderiaser
56ed1cd535 chore: cloudcmd: v19.0.3 2025-09-15 17:49:10 +03:00
coderiaser
c5aed16f63 feature: cloudcmd: aleman v1.12.2 2025-09-15 17:48:40 +03:00
coderiaser
1537fa73fc chore: cloudcmd: v19.0.2 2025-09-14 22:22:41 +03:00
coderiaser
511347d3ef feature: cloudcmd: aleman v1.11.0 2025-09-14 22:22:04 +03:00
coderiaser
c963ffefee chore: cloudcmd: v19.0.1 2025-09-14 22:10:54 +03:00
coderiaser
fc6304a1c6 fix: tmpl: config: aleman, supermenu 2025-09-14 22:10:30 +03:00
coderiaser
a05ecdb406 feature: cloudcmd: aleman v1.10.0 2025-09-14 22:07:48 +03:00
coderiaser
9e4d66df4d docs: help: menu: typos 2025-09-14 19:46:31 +03:00
coderiaser
3f7e17721e chore: cloudcmd: v19.0.0 2025-09-14 19:40:40 +03:00
coderiaser
50b19dcc44 feature: cloudcmd: menu: default: supermenu -> aleman 2025-09-14 19:38:46 +03:00
coderiaser
7fb130cdbd chore: actions: v24 2025-09-14 19:38:18 +03:00
coderiaser
5970f10a84 feature: cloudcmd: drop support of node < 22 2025-09-14 19:37:41 +03:00
coderiaser
84a51a932c chore: cloudcmd: v18.8.11 2025-09-14 19:12:26 +03:00
coderiaser
b0360d8ef4 feature: cloudcmd: aleman v1.9.1 2025-09-14 19:11:45 +03:00
coderiaser
00a20129de feature: cloudcmd: html: importsmap: add 2025-09-14 19:11:04 +03:00
coderiaser
5e657e9bd1 chore: cloudcmd: v18.8.10 2025-09-14 18:20:57 +03:00
coderiaser
2559343eef docs: help: menu: hot keys 2025-09-14 18:09:41 +03:00
coderiaser
ddf9e45567 feature: cloudcmd: aleman v1.9.0 2025-09-14 18:09:15 +03:00
coderiaser
84bfef562d chore: cloudcmd: v18.8.9 2025-09-14 12:13:49 +03:00
coderiaser
2e7bdb8aa0 feature: cloudcmd: aleman v1.8.0 2025-09-14 12:13:00 +03:00
coderiaser
a2f66951a8 chore: cloudcmd: v18.8.8 2025-09-13 16:56:52 +03:00
coderiaser
03631d95f7 feature: cloudcmd: aleman v1.7.0 2025-09-13 16:55:59 +03:00
coderiaser
1fc57fdb91 chore: cloudcmd: v18.8.7 2025-09-12 16:43:18 +03:00
coderiaser
09408af5dd feature: cloudcmd: aleman v1.6.1 2025-09-12 16:42:35 +03:00
coderiaser
4eb47f9d42 chore: cloudcmd: v18.8.6 2025-09-12 16:15:36 +03:00
coderiaser
4fcaf2885d feature: cloudcmd: aleman v1.6.0 2025-09-12 16:14:20 +03:00
coderiaser
eaea183536 chore: cloudcmd: v18.8.5 2025-09-10 19:56:40 +03:00
coderiaser
c69ec16ef0 feature: cloudcmd: aleman v1.5.0 2025-09-10 19:56:04 +03:00
coderiaser
3cf13a92c0 chore: cloudcmd: v18.8.4 2025-09-09 18:53:23 +03:00
coderiaser
08d13c6d6b feature: cloudcmd: aleman v1.4.9 2025-09-09 18:52:27 +03:00
coderiaser
df79dda4d0 chore: cloudcmd: v18.8.3 2025-09-04 23:40:33 +03:00
coderiaser
b4792fc3f2 feature: cloudcmd: aleman v1.4.0 2025-09-04 23:40:07 +03:00
coderiaser
c164766895 chore: cloudcmd: v18.8.2 2025-09-04 20:21:08 +03:00
coderiaser
0584f4f431 feature: cloudcmd: aleman v1.3.0 2025-09-04 20:21:08 +03:00
coderaiser
67e4b8f0e4 chore: cloudcmd: actions: lint ☘️ 2025-09-03 22:33:25 +00:00
coderiaser
876bbfded5 chore: cloudcmd: v18.8.1 2025-09-04 01:32:26 +03:00
coderiaser
15b71c1485 feature: cloudcmd: aleman v1.2.5 2025-09-04 01:31:41 +03:00
coderiaser
d252fe5fcb feature: robots.txt: add 2025-09-03 11:55:13 +03:00
coderaiser
25119ac6ea chore: cloudcmd: actions: lint ☘️ 2025-09-01 21:45:36 +00:00
coderiaser
fc20b9a4e1 chore: cloudcmd: v18.8.0 2025-09-02 00:44:38 +03:00
coderaiser
faa2f9c765 chore: cloudcmd: actions: lint ☘️ 2025-09-01 21:44:29 +00:00
coderiaser
08b5c6b2b5 feature: client: menu: aleman: add 2025-09-02 00:33:25 +03:00
coderiaser
83a1e527e5 chore: cloudcmd: v18.7.4 2025-08-30 15:10:44 +03:00
coderiaser
a6d18ddb88 fix: select file: name -> line 2025-08-30 15:09:23 +03:00
coderiaser
7db31363ff refactor: clients: dom: events: simplify 2025-08-17 15:13:51 +03:00
coderiaser
2077468a85 fix: client: listeners: click: avoid select on conext menu 2025-08-17 15:13:24 +03:00
coderaiser
e3fcb7f24e
chore: client: view: rm comment 2025-08-13 21:23:53 +02:00
coderiaser
64e4aba414 fix: client: menu: before show: unsetBind 2025-08-10 23:50:05 +03:00
coderaiser
899266a62a
docs: HELP: demo 2025-08-01 17:45:23 +03:00
coderiaser
d568a4d896 chore: cloudcmd: v18.7.3 2025-07-26 22:53:15 +03:00
coderiaser
884c83eb60 fix: client: polyfill (#442) 2025-07-26 22:52:50 +03:00
coderaiser
b34ee44bb1
docs: bug_report: update 2025-07-26 14:12:44 +03:00
coderaiser
c3251bf5e1
docs: README: demo 2025-07-26 14:03:54 +03:00
coderiaser
54bbd4e34d chore: cloudcmd: v18.7.2 2025-07-24 19:17:31 +03:00
coderiaser
3d13b9c5f8 feature: docker: npm -> bun 2025-07-24 19:17:31 +03:00
coderiaser
61107329fc feature: cloudcmd: html-looks-like: remove 2025-07-24 16:50:42 +03:00
coderiaser
f0bc286aea chore: cloudcmd: v18.7.1 2025-07-06 20:25:56 +03:00
coderiaser
8eb724b58c fix: docker: install: -f 2025-07-06 20:25:56 +03:00
coderiaser
784bb2ebdd fix: build: sw 2025-07-06 20:19:40 +03:00
coderiaser
8f52376d3f feature: cloudcmd: revert: optimize-css-assets-webpack-plugin -> css-minimizer-webpack-plugin: broken spinner
This reverts commit 8200874912.
2025-07-06 20:02:22 +03:00
coderiaser
ed31dd2409 chore: devDependencies: serviceworker-webpack-plugin 2025-07-06 20:02:08 +03:00
coderiaser
8200874912 feature: cloudcmd: optimize-css-assets-webpack-plugin -> css-minimizer-webpack-plugin 2025-07-06 15:11:18 +03:00
coderiaser
4b8568b264 chore: cloudcmd: v18.7.0 2025-07-05 20:43:51 +03:00
coderiaser
de41aec77d test: server 2025-07-05 20:42:36 +03:00
coderaiser
74f5eb0cae chore: cloudcmd: actions: lint ☘️ 2025-07-05 17:40:30 +00:00
coderiaser
1d983e9950 test: cloudcmd: server 2025-07-05 20:39:25 +03:00
coderiaser
f9b3844153 chore: lint 2025-07-05 20:35:16 +03:00
coderiaser
546d061068 feature: cloudcmd: process v0.11.10 2025-07-05 20:34:57 +03:00
coderiaser
b1e231a5bb fix: client: menu: close: ESC 2025-07-05 20:32:21 +03:00
coderiaser
121b114e89 feature: cloudcmd: path-browserify v1.0.1 2025-07-05 20:29:57 +03:00
coderiaser
8592cedce0 feature: cloudcmd: mini-css-extract-plugin v2.9.2 2025-07-05 20:27:55 +03:00
coderiaser
a53ab67b7d feature: cloudcmd: webpack-cli v6.0.1 2025-07-05 20:26:27 +03:00
coderiaser
de2cedd99d feature: cloudcmd: webpack v5.99.9 2025-07-05 20:26:27 +03:00
coderiaser
da545ea418 feature: cloudcmd: style-loader v4.0.0 2025-07-05 20:26:26 +03:00
coderiaser
db6e8334b8 feature: cloudcmd: optimize-css-assets-webpack-plugin v6.0.1 2025-07-05 20:26:26 +03:00
coderiaser
2f0c1a616c feature: cloudcmd: html-webpack-plugin v5.6.3 2025-07-05 20:26:25 +03:00
coderiaser
e100dcf627 feature: cloudcmd: extract-text-webpack-plugin v3.0.2 2025-07-05 20:26:25 +03:00
coderiaser
76c4000890 feature: cloudcmd: css-loader v7.1.2 2025-07-05 20:26:24 +03:00
coderiaser
fb5e5a320f feature: cloudcmd: clean-css-loader v4.2.1 2025-07-05 20:26:24 +03:00
coderiaser
8551cd54a6 feature: cloudcmd: babel-loader v10.0.0 2025-07-05 20:26:23 +03:00
coderiaser
c93803190b feature: webpack 5 2025-07-05 20:25:41 +03:00
coderiaser
ddc94adbf1 feature: cloudcmd: eslint-plugin-putout v28.0.0 2025-07-04 14:23:05 +03:00
coderiaser
ef42330848 chore: cloudcmd: v18.6.1 2025-07-04 13:41:56 +03:00
coderiaser
a54caa1d7c chore: lint 2025-07-04 13:41:22 +03:00
coderiaser
9eafa189a8 feature: cloudcmd: http-auth v4.2.1 2025-07-04 13:41:22 +03:00
coderaiser
baaf47e62f chore: cloudcmd: actions: lint ☘️ 2025-07-04 10:34:29 +00:00
coderiaser
0d61a972fb chore: lint 2025-07-04 13:32:47 +03:00
coderiaser
af77eeed8d chore: lint 2025-07-04 12:54:30 +03:00
coderiaser
e99d084728 feature: cloudcmd: montag v1.2.1 2025-07-03 23:21:44 +03:00
coderiaser
b77e9c91d4 feature: cloudcmd: pipe-io v4.0.1 2025-07-03 23:21:05 +03:00
coderiaser
4b476a6dff feature: cloudcmd: globals v16.3.0 2025-07-03 23:20:57 +03:00
coderiaser
2057065dbf feature: cloudcmd: @putout/eslint-flat v3.0.1 2025-07-03 23:20:53 +03:00
coderiaser
a87e5ceaf6 chore: cloudcmd: v18.6.0 2025-07-02 21:18:44 +03:00
coderiaser
2eb3dc6692 feature: cloudcmd: @iocmd/wait v2.1.0 2025-07-02 21:17:48 +03:00
coderiaser
5f0391fc44 chore: lint 2025-07-02 21:15:33 +03:00
coderiaser
1679b788c2 feature: cloudcmd: webpackbar v7.0.0 2025-07-02 19:57:34 +03:00
coderiaser
9a4cf388d3 feature: cloudcmd: eslint-plugin-putout v27.2.1 2025-07-02 18:42:15 +03:00
coderiaser
f4b0f92f05 feature: cloudcmd: express v5.1.0 2025-07-02 18:40:04 +03:00
coderiaser
4ab4be124b feature: thread-it: get rid (#438) 2025-07-02 18:34:59 +03:00
coderiaser
99ad0c217d feature: cloudcmd: rm @putout/babel 2025-07-02 17:17:12 +03:00
coderiaser
8ccec23d6c feature: cloudcmd: help: require -> import 2025-07-02 17:16:39 +03:00
coderiaser
2a97ac66fb feature: cloudcmd: yargs-parser v22.0.0 2025-07-02 17:14:47 +03:00
coderiaser
b26c8bba37 feature: cloudcmd: thread-it v3.0.0 2025-04-24 17:19:31 +03:00
coderiaser
4b78d70b5c chore: cloudcmd: v18.5.2 2025-04-10 17:24:15 +03:00
coderiaser
8450bfa611 feature: cloudcmd: putout v40.0.3 2025-04-10 17:23:39 +03:00
coderaiser
84c6935ae4 chore: cloudcmd: actions: lint ☘️ 2025-03-29 17:32:46 +00:00
coderiaser
9f52ed795d chore: package: postcss 2025-03-29 19:30:48 +02:00
coderiaser
f7379c0562 chore: lint 2025-03-29 19:30:48 +02:00
coderiaser
51f51b54de feature: cloudcmd: @putout/plugin-cloudcmd v4.0.0 2025-03-29 19:30:48 +02:00
coderiaser
e87418adde chore: lint 2025-03-29 19:30:48 +02:00
coderiaser
08ab63d704 feature: cloudcmd: supertape v11.0.4 2025-03-29 19:30:48 +02:00
coderiaser
e7cc9b92ae feature: cloudcmd: redlint v4.1.1 2025-03-29 19:30:48 +02:00
coderiaser
368c9bb814 feature: cloudcmd: eslint v9.23.0 2025-03-29 19:30:48 +02:00
coderiaser
43fd5ed660 feature: cloudcmd: madrun v11.0.0 2025-03-29 19:30:48 +02:00
coderiaser
f774d5b290 feature: cloudcmd: eslint-plugin-putout v26.1.0 2025-03-29 19:30:48 +02:00
coderiaser
b0a7fc1648 feature: cloudcmd: putout v39.3.0 2025-03-29 19:30:48 +02:00
coderaiser
f1193955fb chore: cloudcmd: actions: lint ☘️ 2025-02-04 17:04:57 +00:00
coderaiser
2d21001e55
Update config.yml 2025-02-04 19:03:58 +02:00
coderiaser
99118e65a6 chore: lint 2025-02-04 19:02:58 +02:00
coderaiser
8ff0ee6a1a
Update issue_template.md 2025-02-04 18:14:03 +02:00
coderaiser
ac9a27b333 chore: cloudcmd: actions: lint ☘️ 2025-02-04 16:13:38 +00:00
coderaiser
84719365b1
Update issue templates 2025-02-04 18:12:28 +02:00
coderaiser
34fb175d63 chore: cloudcmd: actions: lint ☘️ 2025-02-04 15:45:56 +00:00
coderaiser
dbd8c6ab7c
Update issue_template.md 2025-02-04 17:45:01 +02:00
coderaiser
716765104c
Update config.yml 2025-02-04 17:44:25 +02:00
coderaiser
51a4cee688
Update issue_template.md 2025-02-04 17:43:54 +02:00
coderaiser
b2f113bf0a
Update issue_template.md 2025-02-04 17:43:12 +02:00
coderaiser
2e02ba46e8 chore: cloudcmd: actions: lint ☘️ 2025-02-04 15:41:51 +00:00
coderaiser
38666cbb8b
Update issue_template.md 2025-02-04 17:40:55 +02:00
coderaiser
53acae1a63 chore: cloudcmd: actions: lint ☘️ 2025-02-04 15:39:36 +00:00
coderaiser
e2591061af
chore: issue_template: updated 2025-02-04 17:38:33 +02:00
coderaiser
3d52ce80f2
Update FUNDING.yml 2025-02-04 16:06:08 +02:00
coderaiser
a598ef3ed0 chore: cloudcmd: actions: lint ☘️ 2025-02-04 14:05:53 +00:00
coderaiser
e71550d557
chrome: issue_template: improve 2025-02-04 16:04:53 +02:00
coderaiser
56d621b1ff chore: cloudcmd: actions: lint ☘️ 2025-02-04 13:59:35 +00:00
coderaiser
a38b3a79ce
chrome: issue_template.md 2025-02-04 15:58:38 +02:00
coderaiser
7ae60605bb chore: cloudcmd: actions: lint ☘️ 2025-02-04 13:57:41 +00:00
coderaiser
7492b4bfc7
chore: issue_template: configure 2025-02-04 15:56:47 +02:00
coderaiser
3adfec81c2
chore: github: config: forbid blank issues 2025-02-04 15:54:49 +02:00
coderaiser
c68a3a7f07
chore: github: issue_template.md 2025-02-04 15:54:10 +02:00
coderaiser
4eaeccba1c
chore: github: issue template 2025-02-04 15:53:17 +02:00
coderiaser
2dea0a3b2d chore: cloudcmd: v18.5.1 2025-02-03 14:51:27 +02:00
coderiaser
467f0a79c3 feature: cloudcmd: webpack-merge v6.0.1 2025-02-03 14:50:27 +02:00
coderiaser
353a1fb6c1 feature: cloudcmd: putout v38.0.5 2025-02-03 14:49:47 +02:00
coderiaser
8e98b778bd feature: cloudcmd: eslint-plugin-putout v24.0.0 2025-02-03 14:49:43 +02:00
coderiaser
3ad6720f5f chore: lint 2025-01-24 00:48:44 +02:00
coderiaser
b0be119474 chore: actions: yarn -> bun 2025-01-21 00:52:48 +02:00
coderiaser
161d4f952b chore: lint 2025-01-21 00:45:53 +02:00
coderiaser
301252906f chore: cloudcmd: v18.5.0 2025-01-20 18:48:58 +02:00
coderiaser
ddd1722f39 chore: lint 2025-01-20 18:48:26 +02:00
coderiaser
907afc6e4a chore: test: lint 2025-01-20 18:19:13 +02:00
coderiaser
d8da7922d9 chore: client: lint 2025-01-20 18:18:52 +02:00
coderiaser
2fc503f71f feature: cloudcmd: @putout/babel v3.0.0 2025-01-20 18:05:22 +02:00
coderiaser
ad8e55d824 fix: client: themes -> columns (#434) 2025-01-20 18:04:29 +02:00
coderiaser
5d22722010 chore: lint 2025-01-03 13:44:00 +02:00
coderaiser
13cb970a55
docs: license: 2025 2025-01-01 10:03:15 +01:00
coderiaser
94ebbf84cb chore: cloudcmd: v18.4.1 2024-12-13 14:51:41 +02:00
coderiaser
100e940e4b feature: cloudcmd: putout v37.0.1 2024-12-13 14:51:21 +02:00
coderiaser
a6ce6d3953 chore: cloudcmd: v18.4.0 2024-11-22 12:19:36 +02:00
coderiaser
da0e99ad8c refactor: server: auth: destructure 2024-11-22 12:19:14 +02:00
Hagay Goshen
dff0267239
fix: cloudcmd: make manifest.json accessible when authentication is enabled (#428)
* Make manifest.json accessable when authentication is enabled

* add test for manifest.json availability when authentication is enabled
2024-11-22 11:14:34 +01:00
coderiaser
03e3ba6ad8 chore: cloudcmd: v18.3.0 2024-11-14 12:26:52 +02:00
coderaiser
4c73315b5c chore: cloudcmd: actions: lint ☘️ 2024-11-13 21:13:27 +00:00
Hagay Goshen
71dc8dd6be
feature: cloudcmd: Add support for Progressive Web App (#426)
* Add support for Progressive Web App

* Add resized favicon 256x256
2024-11-13 22:11:13 +01:00
coderaiser
f2ef160ca8 chore: cloudcmd: actions: lint ☘️ 2024-11-06 15:01:08 +00:00
coderiaser
be4f6271f4 chore: cloudcmd: v18.2.1 2024-11-06 16:59:23 +02:00
coderiaser
a733d81441 feature: css: --is-mobile: add 2024-11-06 16:59:03 +02:00
coderaiser
06cf0eebce chore: cloudcmd: actions: lint ☘️ 2024-11-01 19:00:41 +00:00
Hagay Goshen
59024eea98
refactor: css: query: move width consts to css variables (#424)
* Move const panel width from code to css variables

* Update css/query.css

* Update css/query.css

---------

Co-authored-by: coderaiser <mnemonic.enemy@gmail.com>
2024-11-01 19:58:39 +01:00
coderaiser
62af673117 chore: cloudcmd: actions: lint ☘️ 2024-10-30 20:36:45 +00:00
Hagay Goshen
f22120dc38
feature: cloudcmd: prevent unselect being fired on panel click when in mobile view (#422)
* Prevent unselect being fired on panel click when in mobile view

* Prevent unselect being fired on panel click when in mobile view after review

---------

Co-authored-by: hagaygo <hagay@WORKROOM>
2024-10-30 21:34:39 +01:00
Marc
1a0af863a2
feature: docker: add image source label to dockerfiles (#421)
see #419
2024-10-28 09:56:38 +01:00
Marc
0446a74d28
feature: docker: add image source label to dockerfiles (#419)
To get changelogs shown with Renovate a docker container has to add the source label described in the OCI Image Format Specification.

For reference: https://github.com/renovatebot/renovate/blob/main/lib/modules/datasource/docker/readme.md
2024-10-27 12:09:02 +02:00
coderiaser
5562b47d84 chore: cloudcmd: v18.2.0 2024-10-27 12:06:21 +02:00
coderiaser
7309ceff37 chore: lint 2024-10-27 12:05:51 +02:00
coderiaser
ac9abbd385 feature: cloudcmd: eslint-plugin-putout v23.1.0 2024-10-27 12:05:51 +02:00
Hagay Goshen
4bc5a7834e feature: cloudcmd: add context menu option to toggle file selection (#420) 2024-10-27 12:05:42 +02:00
coderaiser
0f5fe2d4fe chore: cloudcmd: actions: lint ☘️ 2024-08-17 10:19:32 +00:00
coderiaser
f5015e75e8 chore: cloudcmd: v18.1.0 2024-08-17 13:17:49 +03:00
coderiaser
ddf4542b75 feature: cloudcmd: add ability to hide dot files (#307) 2024-08-17 13:17:01 +03:00
coderiaser
9dbd812b3d chore: cloudcmd: v18.0.2 2024-08-16 17:18:24 +03:00
coderiaser
3d03efbe63 feature: css: show links in one small screens 2024-08-16 17:17:44 +03:00
coderiaser
a522c49c1f chore: cloudcmd: v18.0.1 2024-08-16 14:54:54 +03:00
coderiaser
9ec94dee15 feature: cloudcmd: chalk v5.3.0 2024-08-16 14:54:05 +03:00
coderiaser
62ed8411ae fix: bin: validateArgs is not a function (#147) 2024-08-16 14:53:14 +03:00
coderaiser
75a75365cf chore: cloudcmd: actions: lint ☘️ 2024-08-16 09:17:21 +00:00
coderiaser
ac3f20c5d1 chore: cloudcmd: v18.0.0 2024-08-16 12:15:44 +03:00
coderiaser
9109511e95 chore: lint 2024-08-16 12:15:19 +03:00
coderiaser
b63d6fccde chore: lint 2024-08-16 12:13:29 +03:00
coderiaser
88df2f0518 chore: actions: node v22 2024-08-16 12:08:02 +03:00
coderiaser
5e93bcca1e feature: cloudcmd: rimraf v6.0.1 2024-08-16 12:06:47 +03:00
coderiaser
74d1eb7e28 feature: drop support of node < 20 2024-08-16 12:05:52 +03:00
coderiaser
544e30dbf8 chore: cloudcmd: v17.4.4 2024-08-16 11:48:11 +03:00
coderiaser
282b3d5cca feature: cloudcmd: @putout/cli-validate-args v2.0.0 2024-08-16 11:47:20 +03:00
coderiaser
a6aa9bbc44 fix: revert rimraf v6.0.1
This reverts commit 6e8348b843.
2024-08-16 11:46:51 +03:00
coderiaser
3140b7f998 chore: cloudcmd: v17.4.3 2024-07-27 15:54:10 +03:00
coderiaser
d70362fbf1 chore: npmvrc: add 2024-07-19 18:15:40 +03:00
coderiaser
6e8348b843 feature: cloudcmd: rimraf v6.0.1 2024-07-19 17:34:34 +03:00
coderiaser
61ca7f36a4 feature: cloudcmd: putout v36.0.2 2024-07-19 17:34:06 +03:00
coderiaser
b5e9ae5ad9 chore: cloudcmd: v17.4.2 2024-07-03 16:09:41 +03:00
coderiaser
ba2d0b36af feature: cloudcmd: just-snake-case v3.2.0 2024-07-03 15:36:21 +03:00
coderiaser
4cc47e30de feature: cloudcmd: just-capitalize v3.2.0 2024-07-03 15:36:21 +03:00
coderiaser
d8451e56aa feature: cloudcmd: just-pascal-case v3.2.0 2024-07-03 15:36:21 +03:00
coderiaser
6abf327d0d feature: cloudcmd: package-json v10.0.0 2024-07-03 15:36:21 +03:00
coderaiser
79fb49479e chore: cloudcmd: actions: lint ☘️ 2024-07-03 12:01:49 +00:00
zilion2000
2ae6ad34fc feature: docker: Dockerimage update Debian12 (#414) 2024-07-03 14:58:40 +03:00
coderiaser
14d9618a64 chore: lint 2024-06-14 22:33:14 +03:00
coderiaser
05ef0ae452 feature: cloudcmd: c8 v10.1.2 2024-06-14 22:32:50 +03:00
coderiaser
883eee96a2 chore: lint 2024-06-14 22:26:27 +03:00
coderaiser
9c8c0533a4
Update CONTRIBUTING.md 2024-06-13 09:11:36 +03:00
coderaiser
14d46c007b chore: cloudcmd: actions: lint ☘️ 2024-05-06 11:52:34 +00:00
coderaiser
25872c3b11 chore: cloudcmd: v17.4.1 2024-05-06 14:49:44 +03:00
coderaiser
610ba8827f chore: lint 2024-05-06 14:48:47 +03:00
coderaiser
154b4bd627 feature: cloudcmd: @cloudcmd/move-files v8.0.0 2024-05-06 12:21:01 +03:00
coderaiser
c409a2db82 feature: cloudcmd: copymitter v9.0.0 2024-05-06 12:20:55 +03:00
coderaiser
52df5bfcf4 chore: cloudcmd: v17.4.0 2024-04-17 00:05:05 +03:00
coderaiser
2428853613 chore: lint 2024-04-17 00:04:12 +03:00
coderaiser
6fb2102099 fix: server: route: path traversal 2024-04-17 00:04:12 +03:00
Devin Buhl
37ab7068d9
feature: publish container image to GHCR (#409)
* feat: publish container image to GHCR

Fixes: #408

* add permissions to workflow
2024-04-16 07:47:40 +03:00
coderaiser
d061aa82ea chore: cloudcmd: v17.3.3 2024-04-03 09:37:44 +03:00
coderaiser
b088b84eb1 feature: cloudcmd: deepword v10.0.0 2024-04-03 09:36:32 +03:00
coderaiser
fceddc20f8 chore: cloudcmd: v17.3.2 2024-03-29 16:01:48 +02:00
coderaiser
f7a6a36696 fix: typo: Wraped -> Wrapped 2024-03-29 16:01:13 +02:00
coderaiser
91de9b3d27 chore: cloudcmd: v17.3.1 2024-03-29 15:03:21 +02:00
coderaiser
2d37f6c6e9 docs: env: theme 2024-03-29 15:01:55 +02:00
coderaiser
d75818297b feature: distribute: convert to ESM 2024-03-29 15:01:55 +02:00
coderaiser
faa2cb3f90 docs: help 2024-03-29 14:28:55 +02:00
coderaiser
29914c09f1 docs: Special Thanks: link 2024-03-29 13:30:50 +02:00
coderaiser
7f5e5c6825 chore: cloudcmd: v17.3.0 2024-03-29 13:28:57 +02:00
coderaiser
6bc4f3ec26 feature: dark theme: add (#332) 2024-03-29 13:27:38 +02:00
coderaiser
35622082a9 feature: route: convert to ESM 2024-03-29 11:24:25 +02:00
coderaiser
65cf97a7be chore: cloudcmd: v17.2.1 2024-03-29 10:33:09 +02:00
coderaiser
cf9ed97c3e chore: lint 2024-03-29 10:32:22 +02:00
coderaiser
cc134464a4 fix: client: vim: space 2024-03-29 10:32:22 +02:00
coderaiser
e3f89e8845 feature: dark mode: add 2024-03-29 10:32:22 +02:00
coderaiser
c45b23fe55 feature: css: vars: add 2024-03-29 10:32:22 +02:00
coderaiser
b1f74c0057 feature: css: add vars 2024-03-29 10:32:22 +02:00
coderaiser
bf2e8f9a7d chore: cloudcmd: actions: lint ☘️ 2024-03-22 21:03:18 +00:00
coderaiser
f5f34a85ea chore: server: cloudcmd: sort imports 2024-03-22 23:01:42 +02:00
coderaiser
04e5d5b7e0 chore: cloudcmd: v17.2.0 2024-03-22 23:00:58 +02:00
coderaiser
3e565109b3 feature: convert to ESM 2024-03-22 23:00:24 +02:00
coderaiser
770a0812aa feature: pack: get rid of mock-require 2024-03-22 22:56:05 +02:00
coderaiser
25d8faea63 feature: rest: get rid of mock-require 2024-03-22 22:56:05 +02:00
coderaiser
98e0011e8e chore: cloudcmd: actions: lint ☘️ 2024-03-22 14:59:40 +00:00
coderaiser
401a669aca feature: user-menu: get rid of mock-require 2024-03-22 16:57:38 +02:00
coderaiser
4e32241d83 feature: terminal: get rid of mock-require 2024-03-22 10:52:25 +02:00
coderaiser
cfa3d69538 chore: cloudcmd: v17.1.6 2024-03-21 09:58:31 +02:00
coderaiser
e01ee4575b feature: server: route: get rid of mock-require 2024-03-21 09:57:49 +02:00
coderaiser
a03185e14e chore: cloudcmd: actions: lint ☘️ 2024-03-21 07:02:53 +00:00
coderaiser
564de89333 chore: server: cloudcmd: lint 2024-03-21 09:00:56 +02:00
coderaiser
0655988ed7 chore: lint 2024-03-21 08:50:25 +02:00
coderaiser
c7f9090126 feature: root: get rid of mock-require 2024-03-21 08:49:14 +02:00
coderaiser
1bef0d4381 chore: lint 2024-03-21 08:46:31 +02:00
coderaiser
fcce26d4e1 feature: cloudfunc: get rid of mock-require 2024-03-21 08:46:31 +02:00
coderaiser
6e99c7e3c5 chore: cloudcmd: actions: lint ☘️ 2024-03-20 11:28:14 +00:00
coderaiser
3f83f6f09a chore: cloudcmd: v17.1.5 2024-03-20 13:26:21 +02:00
coderaiser
bf90bf2295 feature: server: validate: get rid of mock-require 2024-03-20 13:25:44 +02:00
coderaiser
043ba29b32 chore: cloudcmd: v17.1.4 2024-03-20 13:13:28 +02:00
coderaiser
95c36b0a37 chore: lint 2024-03-20 13:12:45 +02:00
coderaiser
98d3a4cc56 feature: server: columns: get rid of mock-require 2024-03-20 12:37:27 +02:00
coderaiser
46c8accd84 chore: cloudcmd: actions: lint ☘️ 2024-03-18 16:32:23 +00:00
coderaiser
57d2f8e93d chore: cloudcmd: v17.1.3 2024-03-18 18:30:17 +02:00
coderaiser
e080a54022 feature: server: cloudcmd: get rid of mock-require 2024-03-18 18:29:13 +02:00
coderaiser
eff34215ee chore: lint 2024-03-18 16:46:11 +02:00
coderaiser
4503237ac4 chore: cloudcmd: v17.1.2 2024-03-18 15:04:56 +02:00
coderaiser
bf614e1dc9 feature: cloudcmd: redlint v3.13.1 2024-03-18 15:04:24 +02:00
coderaiser
857c97006d fix: docker: alpine 2024-03-18 15:04:24 +02:00
coderaiser
b2478a8145
docs: README: rm arm v7 2024-03-18 07:51:49 +02:00
coderaiser
3c5ca3dca4 chore: cloudcmd: v17.1.1 2024-03-16 18:45:42 +02:00
coderaiser
a92a5a0d34 feature: cloudcmd: restbox v4.0.0 2024-03-16 17:58:18 +02:00
coderaiser
c51ba1d8f6 feature: docker: drop arm v7 2024-03-16 17:22:05 +02:00
coderaiser
a57833aaf8 chore: cloudcmd: v17.1.0 2024-03-16 15:27:16 +02:00
coderaiser
0bcff30faa chore: lint 2024-03-16 15:26:23 +02:00
coderaiser
10d6d2e247 feature: cloudcmd: @types/node-fetch v2.6.11 2024-03-16 15:26:23 +02:00
coderaiser
2047cb7ad8 feature: cloudcmd: @cloudcmd/dropbox v5.0.1 2024-03-16 15:26:23 +02:00
coderaiser
6b793cca04 feature: cloudcmd: docker: alpine 2024-03-16 15:26:23 +02:00
coderaiser
5fa9fcc57c feature: cloudcmd: pullout v5.0.0 2024-03-16 15:26:23 +02:00
coderaiser
bc617c17ef feature: cloudcmd: serve-once v3.0.1 2024-03-16 15:26:23 +02:00
coderaiser
0ecd1853d0
Update ISSUE_TEMPLATE.md 2024-03-16 11:58:19 +02:00
coderaiser
dc2d3146a1
Update ISSUE_TEMPLATE.md 2024-03-15 12:29:43 +02:00
coderaiser
266aff4ffb chore: cloudcmd: v17.0.7 2024-03-12 17:40:33 +02:00
coderaiser
a407e0c3fa chore: lint 2024-03-12 17:39:58 +02:00
coderaiser
97627dc2ff feature: cloudcmd: auto-globals v4.0.0 2024-03-12 17:24:51 +02:00
coderaiser
683c865eda feature: cloudcmd: gritty v8.0.0 2024-03-12 17:08:35 +02:00
coderaiser
b795a1f5dd chore: cloudcmd: v17.0.6 2024-03-11 22:32:40 +02:00
coderaiser
d928c0b83d fix: cloudcmd: ocker: revert alpine (#406)
This reverts commit 33201dade2.
2024-03-11 22:31:13 +02:00
coderaiser
1d3567f3a3 chore: cloudcmd: v17.0.5 2024-03-11 16:07:05 +02:00
coderaiser
33201dade2 fix: cloudcmd: docker: alpine (#406) 2024-03-11 16:02:35 +02:00
coderaiser
5ced36b0d6 chore: cloudcmd: v17.0.4 2024-02-02 18:22:45 +02:00
coderaiser
7ce954503c feature: cloudcmd: deepword v9.0.0 2024-02-02 18:20:57 +02:00
coderaiser
1c73e525ff feature: cloudcmd: edward v15.0.0 2024-02-02 12:13:32 +02:00
coderaiser
da967f080c feature: cloudcmd: dword v15.0.0 2024-02-02 12:09:28 +02:00
coderaiser
f0a6109a7d feature: cloudcmd: restafary v12.0.0 2024-02-02 11:53:14 +02:00
coderaiser
e84c7dcbab chore: cloudcmd: v17.0.3 2024-02-01 14:43:33 +02:00
coderaiser
796ee627e7 chore: lint 2024-02-01 14:23:33 +02:00
coderaiser
aca4119fb1 feature: cloudcmd: inly v5.0.0 2024-02-01 14:14:37 +02:00
coderaiser
ed95dec964 chore: cloudcmd: v17.0.2 2024-02-01 12:44:28 +02:00
coderaiser
5324a41abd feature: cloudcmd: supertape v10.0.0 2024-02-01 12:43:49 +02:00
coderaiser
d453a1b291 feature: cloudcmd: onezip v6.0.1 2024-02-01 12:43:49 +02:00
coderaiser
71b915bed3 feature: cloudcmd: @cloudcmd/fileop v8.0.0 2024-02-01 12:43:49 +02:00
coderaiser
1ac4191c66
docs: LICENSE: 2024 2024-01-30 09:31:19 +02:00
coderaiser
4f25500351 chore: cloudcmd: actions: lint ☘️ 2024-01-25 10:02:20 +00:00
coderaiser
a0c398264c chore: cloudcmd: v17.0.1 2024-01-25 12:00:28 +02:00
coderaiser
d79a577611 feature: cloudcmd: putout v35.0.0 2024-01-25 11:56:36 +02:00
coderaiser
8d92aa91e9 feature: cloudcmd: package-json v9.0.0 2024-01-25 11:54:47 +02:00
coderaiser
5ab5576e68 feature: cloudcmd: open v10.0.3 2024-01-25 11:54:30 +02:00
coderaiser
c5cfe68c8a feature: cloudcmd: c8 v9.1.0 2024-01-25 11:54:06 +02:00
coderaiser
2fc241a9fa chore: lint 2023-12-12 23:12:50 +02:00
coderaiser
81a6b18165 chore: cloudcmd: actions: lint ☘️ 2023-12-12 18:00:45 +00:00
coderaiser
084c84a277 chore: lint 2023-12-12 19:58:39 +02:00
coderaiser
ba9915a1df chore: lint 2023-12-12 19:57:07 +02:00
coderaiser
60b682b62a
chore: query: lint 2023-12-12 19:09:02 +02:00
coderaiser
a3d310a3e4 chore: cloudcmd: v17.0.0 2023-12-12 16:11:42 +02:00
coderaiser
bbaad041be chore: lint (stylelint/stylelint#7401) 2023-12-12 16:09:30 +02:00
coderaiser
b22ad537e2 chore: proposal -> transform 2023-12-12 16:09:30 +02:00
coderaiser
154e3b078e feature: cloudcmd: find-up v7.0.0 2023-12-12 16:09:30 +02:00
coderaiser
a02d288df9 feature: cloudcmd: @putout/babel v2.0.0 2023-12-12 16:09:30 +02:00
coderaiser
3314f9b94f feature: drop support of node < 18 2023-12-12 16:09:30 +02:00
coderaiser
a6afa205a9 feature: cloudcmd: supertape v9.0.0 2023-12-12 16:05:18 +02:00
coderaiser
e600c05d18 feature: cloudcmd: eslint-plugin-putout v22.0.0 2023-12-12 16:05:18 +02:00
coderaiser
99c009542a feature: cloudcmd: madrun v10.0.0 2023-12-12 16:05:18 +02:00
coderaiser
afdf74342a feature: cloudcmd: putout v34.0.7 2023-12-12 16:05:18 +02:00
coderaiser
552d95013b
chore: server: route: newlines 2023-12-08 15:43:04 +02:00
coderaiser
eb606bb608 chore: cloudcmd: v16.18.0 2023-12-08 13:20:42 +02:00
coderaiser
f4a3add818 test: coverage 2023-12-08 13:19:24 +02:00
coderaiser
4462f26925 feature: cloudcmd: markdown-it v14.0.0 2023-12-08 13:19:24 +02:00
coderaiser
0cc76bd4f7 feature: cloudcmd: philip v3.0.0: decrease bundle size 2023-12-08 13:19:24 +02:00
coderaiser
949a09340f chore: cloudcmd: actions: lint ☘️ 2023-12-06 19:36:45 +00:00
coderaiser
5d6bed3d66 chore: lint 2023-12-06 21:33:38 +02:00
coderaiser
656ebd87e0 feature: client: move out set-current 2023-12-04 23:08:33 +02:00
coderaiser
72df4288ef chore: webpack: apply webpack5 externals 2023-12-04 20:46:37 +02:00
coderaiser
48dba5fa7e chore: cloudcmd: actions: lint ☘️ 2023-12-04 18:43:50 +00:00
coderaiser
8a85f99eb2 chore: cloudcmd: v16.17.9 2023-12-04 20:40:53 +02:00
coderaiser
2c7782c06a chore: putout: use npm instead of bun 2023-12-04 20:40:04 +02:00
coderaiser
fb2d081402 fix: github actions 2023-12-04 20:35:49 +02:00
coderaiser
f8f6377551 chore: cloudcmd: v16.17.8 2023-12-04 20:18:39 +02:00
coderaiser
b04cbcd8c5 chore: lint 2023-12-04 20:13:53 +02:00
coderaiser
094df666f4 test: cloudcmd: update 2023-12-04 20:04:39 +02:00
coderaiser
75bf8a1275 chore: lint 2023-12-03 23:07:50 +02:00
coderaiser
93aa7278b7 feature: yaspeller: rm 2023-12-03 20:23:20 +02:00
coderaiser
af9c916e08 feature: cloudcmd: eslint-plugin-putout v21.0.2 2023-12-03 20:21:52 +02:00
coderaiser
01dccbfd07 feature: cloudcmd: putout v33.13.3 2023-12-03 20:21:49 +02:00
coderaiser
ea86bd6a2f chore: cloudcmd: v16.17.7 2023-10-12 11:00:00 +03:00
coderaiser
7857fb720d feature: package: rendy v4.1.3 2023-10-12 10:37:10 +03:00
coderaiser
900d4307bb chore: cloudcmd: v16.17.6 2023-09-22 11:12:29 +03:00
coderaiser
570cb8c094 feature: package: nodemon v3.0.1 2023-09-22 11:10:11 +03:00
coderaiser
828d10a8ee feature: package: rimraf v5.0.1 2023-09-22 11:02:16 +03:00
coderaiser
88908b3528 feature: package: eslint-plugin-putout v20.0.0 2023-09-22 11:02:01 +03:00
coderaiser
93f4a07ec6 feature: package: putout v32.0.6 2023-09-22 11:01:50 +03:00
coderaiser
a5f93523c1 feature: github actions: use bun 2023-09-12 11:30:09 +03:00
coderaiser
99980c7f1d chore: cloudcmd: v16.17.5 2023-09-06 14:17:51 +03:00
coderaiser
3b0941bc87 feature: actions: docker/login-action@v2 (#396) 2023-09-06 14:17:00 +03:00
coderaiser
0dca7b6419 chore: cloudcmd: v16.17.4 2023-09-05 23:38:51 +03:00
coderaiser
35dedfdf95 feature: github: update docker actions (#396) 2023-09-05 23:37:40 +03:00
coderaiser
c37979801a chore: cloudcmd: v16.17.3 2023-09-05 12:42:26 +03:00
coderaiser
3c93b29b86 feature: github: docker/build-push-action v4 (#396) 2023-09-05 12:41:33 +03:00
coderaiser
7e2587459f chore: cloudcmd: v16.17.2 2023-09-04 17:19:08 +03:00
coderaiser
621f52da2f fix: docker 2023-09-04 17:18:19 +03:00
coderaiser
8ab7e468a8 chore: cloudcmd: v16.17.1 2023-09-04 16:12:18 +03:00
coderaiser
eb3f037a02 fix: docker: bun -> node 2023-09-04 16:11:00 +03:00
coderaiser
55032ebd7d chore: cloudcmd: actions: lint ☘️ 2023-09-04 13:04:01 +00:00
coderaiser
ee5a0fb4b2 fix: disable bun for CI 2023-09-04 16:01:26 +03:00
coderaiser
34258c6f27 chore: cloudcmd: actions: lint ☘️ 2023-09-04 12:59:45 +00:00
coderaiser
47893e3b83 chore: cloudcmd: v16.17.0 2023-09-04 15:56:39 +03:00
coderaiser
4c7dd74abd chore: test: add timeout 2023-09-04 15:55:41 +03:00
coderaiser
224e539708 feature: docker: use bun instead of node 2023-09-04 15:52:23 +03:00
coderaiser
ea929465a8 chore: lint 2023-09-04 15:43:38 +03:00
coderaiser
4b9267f375 feature: package: edward v14.2.0 2023-09-04 15:38:32 +03:00
coderaiser
809ef1cf60 chore: package: v16.16.3 2023-08-10 13:32:55 +03:00
coderaiser
354c137de6 feature: package: console-io v14.0.0 2023-08-10 13:31:41 +03:00
coderaiser
574b425c02 chore: package: v16.16.2 2023-08-09 19:04:52 +03:00
coderaiser
e353fff711 feature: package: redzip v3.0.0 2023-08-09 17:50:41 +03:00
coderaiser
4b74b8f286
Update README.md 2023-08-09 10:04:09 +03:00
coderaiser
339560fab2 chore: cloudcmd: actions: lint ☘️ 2023-08-07 15:09:19 +00:00
coderaiser
ba7133f2e6
Update README.md 2023-08-07 18:06:56 +03:00
coderaiser
e8ca8cedc5
Update README.md 2023-08-07 18:04:42 +03:00
coderaiser
f9aa9911d5 chore: package: v16.16.1 2023-08-07 17:56:00 +03:00
coderaiser
fcd06769b2 chore: lint 2023-08-07 17:54:48 +03:00
coderaiser
24dcf78be8 chore: lint 2023-08-07 17:40:25 +03:00
coderaiser
13279299c4 chore: lint 2023-08-07 17:38:39 +03:00
coderaiser
20a00481d6 chore: server: lint 2023-08-07 17:36:06 +03:00
coderaiser
d55b6fb393 chore: lint 2023-08-07 17:35:20 +03:00
coderaiser
d63169ccc0 feature: package: @putout/babel v1.0.5 2023-08-07 15:10:16 +03:00
coderaiser
bb9276bec5 feature: package: eslint-plugin-putout v19.0.3 2023-08-07 15:08:07 +03:00
coderaiser
9aed5f751e feature: package: putout v31.0.3 2023-08-07 15:08:00 +03:00
coderaiser
10f564a315 chore: package: v16.16.0 2023-07-09 12:53:33 +03:00
coderaiser
c4f56c59d0 feature: package: memfs v4.2.0 2023-07-09 12:51:58 +03:00
coderaiser
6b9d43e9ab chore: cloudcmd: actions: lint ☘️ 2023-07-09 09:51:29 +00:00
coderaiser
7aacf203a0 chore: madrun: node v20 2023-07-09 12:46:56 +03:00
coderaiser
4717e035ee chore: lint 2023-07-09 12:43:24 +03:00
coderaiser
f1279666b5 test: update fixtures 2023-07-08 20:40:12 +03:00
coderaiser
0e663e1dc5 feature: package: @putout/plugin-cloudcmd v3.1.1 2023-07-08 13:56:07 +03:00
coderaiser
ff9265b78b feature: package: c8 v8.0.0 2023-07-08 13:56:00 +03:00
coderaiser
22aa337ac0 feature: package: eslint-plugin-n v16.0.1 2023-07-08 13:55:56 +03:00
coderaiser
13350b5517 feature: package: eslint-plugin-putout v18.0.0 2023-07-08 13:55:33 +03:00
coderaiser
ce196abf1e feature: package: putout v30.1.1 2023-07-08 13:55:28 +03:00
coderaiser
3e18f6be28
Update CONTRIBUTING.md 2023-05-17 12:00:04 +03:00
coderaiser
deedca28a9 chore: package: v16.15.0 2023-05-17 11:52:51 +03:00
coderaiser
a1bf40bd83 feature: package: open v9.1.0 2023-05-17 11:50:19 +03:00
coderaiser
d011b13d30 chore: cloudcmd: actions: lint ☘️ 2023-05-17 11:50:19 +03:00
cintolas
ef6088531a feature: client: self signed certs on Chrome (#393) 2023-05-17 11:50:11 +03:00
coderaiser
895d19a153
Update PULL_REQUEST_TEMPLATE.md 2023-05-15 11:47:03 +03:00
coderaiser
67c0b81067
Update PULL_REQUEST_TEMPLATE.md 2023-05-15 11:46:49 +03:00
coderaiser
235dc1b8db test: server: coverage 2023-05-12 17:34:22 +03:00
coderaiser
d0e5fc0cbd chore: lint 2023-05-12 17:32:57 +03:00
coderaiser
bd7dfd0c28 chore: package: v16.14.1 2023-03-21 21:11:27 +02:00
coderaiser
a6c400ae1c chore: lint 2023-03-21 21:10:17 +02:00
coderaiser
b336a4726a feature: package: eslint-plugin-putout v17.1.0 2023-03-21 21:04:20 +02:00
coderaiser
019e15b8bd feature: package: @cloudcmd/fileop v7.0.0 2023-03-21 21:00:23 +02:00
coderaiser
643543002e feature: package: copymitter v8.0.1 2023-03-21 20:48:15 +02:00
coderaiser
2743788002 feature: package: @cloudcmd/move-files v7.0.0 2023-03-21 20:48:07 +02:00
coderaiser
49280722d1 chore: package: v16.14.0 2023-03-08 12:43:34 +02:00
coderaiser
6b22b2412b feature: package: putout v29.0.3 2023-03-08 12:39:50 +02:00
coderaiser
14b05d1b14 chore: package: v16.13.1 2023-01-30 20:34:27 +02:00
coderaiser
88da092fdb chore: lint 2023-01-30 20:33:36 +02:00
coderaiser
8e25a02568 chore: cloudcmd: lint 2023-01-30 16:06:32 +02:00
coderaiser
bdddc5752d feature: client: add DIR_DIST 2023-01-30 13:20:05 +02:00
coderaiser
d7e43d8a18 refactor: client: DIR_CLIENT_MODULES -> DIR_MODULES 2023-01-30 13:20:05 +02:00
coderaiser
656000e7e0 feature: client: DIRCLIENT -> DIR_CLIENT 2023-01-30 13:20:05 +02:00
coderaiser
ce49d9d6ad chore: cloudcmd: actions: lint ☘️ 2023-01-29 19:54:38 +00:00
coderaiser
ed9af6d7a6 chore: package: v16.13.0 2023-01-29 18:05:00 +02:00
coderaiser
fd1eb1dc27 feature: client: key: vim: open editor with 'e' 2023-01-29 18:03:58 +02:00
coderaiser
a27e4ecf61 feature: client: vim: add ability to show terminal with 'tt' 2023-01-29 17:57:15 +02:00
coderaiser
203117d2b9 chore: package: v16.12.0 2023-01-29 13:49:43 +02:00
coderaiser
4a38c64cd6 feature: client: vim: add ability to create directory with 'md', and create file with 'mf' 2023-01-29 13:48:15 +02:00
coderaiser
31e1eeea50 chore: package: v16.11.0 2023-01-22 20:29:52 +02:00
coderaiser
cf41d7d1dd feature: user-menu: add support of mov 2023-01-22 20:29:11 +02:00
coderaiser
2bfd32a1bd chore: package: v16.10.0 2023-01-19 22:50:12 +02:00
coderaiser
5b5889dcf7 feature: client: user-menu: navigate: add support of \D + [JK] to speed up vim navigation 2023-01-19 22:50:04 +02:00
coderaiser
40f17bb670 chore: madrun: envelop 2023-01-19 22:50:04 +02:00
coderaiser
871b05d4d5 fix: lint: eslint-plugin-node -> eslint-plugin-n 2023-01-19 22:50:04 +02:00
coderaiser
2f6b07c469 chore: package: v16.9.1 2023-01-18 11:11:59 +02:00
coderaiser
fecfa9fa58 fix: static: user-menu: convert flac to mp3 2023-01-18 11:11:28 +02:00
coderaiser
09de19b46e test: static: user-menu: coverage 2023-01-17 21:53:47 +02:00
coderaiser
924f8696d0 chore: css: rm quotes 2023-01-17 21:53:13 +02:00
coderaiser
3135314686 chore: package: v16.9.0 2023-01-17 18:19:52 +02:00
coderaiser
c83a5222ad chore: rm unused eslint-plugin-node 2023-01-17 18:16:20 +02:00
coderaiser
7fe5a998c4 feature: package: scroll-into-view-if-needed v3.0.4 2023-01-17 18:12:39 +02:00
coderaiser
519aec8afe feature: package: tar-stream v3.0.0 2023-01-17 18:11:26 +02:00
coderaiser
8126b51831 feature: static: user-menu: add recipes from Cookbook 2023-01-17 18:10:09 +02:00
coderaiser
1309d825e4 chore: package: v16.8.0 2023-01-16 16:55:05 +02:00
coderaiser
e28215ddf3 feature: package: @putout/plugin-cloudcmd v2.0.0: CloudCmd.loadDir() -> CloudCmd.changeDir()' 2023-01-16 16:52:37 +02:00
coderaiser
026c292bae chore: package: v16.7.0 2023-01-15 19:21:59 +02:00
coderaiser
c2c7c74ae8 chore: lint 2023-01-15 19:17:41 +02:00
coderaiser
52f47a2434 chore: madrun: test: rm --check-duplicates 2023-01-15 19:06:44 +02:00
coderaiser
f5df731491 feature: package: @cloudcmd/modal v3.0.0: add ability to not set cursor when close the modal 2023-01-15 19:06:15 +02:00
coderaiser
d7ab5b912d feature: package: auto-globals v3.0.0 2023-01-15 18:37:05 +02:00
coderaiser
6f737e83da feature: package: rimraf v4.0.5 2023-01-15 18:36:52 +02:00
coderaiser
18f0f55a64 feature: user-menu: rm border 2023-01-15 13:53:39 +02:00
coderaiser
4e52bb1371 chore: package: v16.6.1 2022-10-20 19:53:32 +03:00
coderaiser
e9ab141e30 chore: rm useless coveralls 2022-10-20 19:51:01 +03:00
coderaiser
ebadb8deeb feature: package: package-json v8.1.0 2022-10-20 19:51:01 +03:00
coderaiser
63ecdcbd86 feature: package: supertape v8.1.0 2022-10-20 19:51:01 +03:00
coderaiser
10045f6ffb feature: package: putout v28.0.0 2022-10-20 19:51:01 +03:00
coderaiser
bec0d44429
Update nodejs.yml 2022-10-10 12:45:51 +03:00
coderaiser
0a345a34a1 chore: package: v16.6.0 2022-10-09 20:31:20 +03:00
coderaiser
42ddee49a7 chore: lint 2022-10-09 20:29:11 +03:00
coderaiser
0c8bf9f3d0 feature: package: @cloudcmd/fileop v6.0.0 2022-10-09 20:26:15 +03:00
coderaiser
b70aa0cb9c feature: package: @cloudcmd/move-files v6.0.0 2022-10-09 20:24:07 +03:00
coderaiser
3aa52812ab feature: package: copymitter v7.0.0 2022-10-09 20:09:53 +03:00
coderaiser
35a46d0d07 chore: package: v16.5.0 2022-08-06 18:16:15 +03:00
coderaiser
5c84d1616c chore(cloudcmd) actions: lint ☘️ 2022-08-06 15:07:59 +00:00
coderaiser
24fbeec62d feature: client: add Command Line 2022-08-06 18:05:14 +03:00
coderaiser
a5229fae06 feature: package: add funding 2022-08-01 15:09:54 +03:00
coderaiser
1195e570e4 feature(package) gritty v7.0.0 2022-07-23 10:40:22 +03:00
coderaiser
c101b039ea chore(package) v16.4.1 2022-07-20 20:17:53 +03:00
coderaiser
444f659076 chore(cloudcmd) lint 2022-07-20 20:16:35 +03:00
coderaiser
de15ed6766 feature(package) eslint-plugin-n v15.2.4 2022-07-20 20:16:35 +03:00
coderaiser
d0a8e2daac feature(package) putout v27.0.1 2022-07-20 20:16:34 +03:00
coderaiser
f085c0eb06 feature(package) eslint-plugin-putout v16.0.0 2022-07-20 20:16:34 +03:00
coderaiser
cb86e78f8b
docs(help) Environment Variables: add " " 2022-07-12 14:07:46 +03:00
coderaiser
10bd261a3f chore(cloudcmd) actions: lint ☘️ 2022-07-11 17:48:36 +00:00
coderaiser
c337e4507d
Update HELP.md 2022-07-11 20:21:30 +03:00
coderaiser
ba2d02bd3d
Update README.md 2022-07-11 20:21:12 +03:00
coderaiser
9f644f82b1
Update README.md 2022-07-11 20:20:52 +03:00
coderaiser
b6797907a8 chore(package) v16.4.0 2022-07-11 20:17:06 +03:00
coderaiser
33827fea5e feature(cloudcmd) env: add ability to pass 0 and 1 2022-07-11 13:05:57 +03:00
coderaiser
45d44dc74d
docs(help) add demo 2022-07-10 23:16:11 +03:00
coderaiser
50c3f04dcd docs(help) add Support 2022-07-02 13:53:15 +03:00
coderaiser
404142cc40 chore(package) v16.3.1 2022-07-02 13:50:21 +03:00
coderaiser
ab6b93ddca fix(client) cloudcmd: rm window.Emitify 2022-07-02 13:50:21 +03:00
coderaiser
2c9b23db4e
Update README.md 2022-07-01 15:33:46 +03:00
coderaiser
8cc9b0ec66
docs(readme) add Support 2022-07-01 15:31:12 +03:00
coderaiser
ddee470eba chore(package) v16.3.0 2022-07-01 14:29:23 +03:00
coderaiser
bf3a9acc86 chore(cloudcmd) actions: lint ☘️ 2022-07-01 10:56:25 +00:00
coderaiser
391269b1ed feature(cloudcmd) terminal-run: return -1, when not load 2022-07-01 13:49:47 +03:00
coderaiser
e44e4fa47d chore(package) v16.2.0 2022-06-17 16:49:56 +03:00
coderaiser
7fdac390de feature(package) markdown-it v13.0.1 2022-06-17 16:45:46 +03:00
coderaiser
9f4a23e0f1 feature(cloudcmd) server: convert to ESM 2022-06-17 15:34:35 +03:00
coderaiser
42e8e255c0 chore(cloudcmd) actions: lint ☘️ 2022-06-17 15:34:35 +03:00
coderaiser
c345d8b7ef chore(actions) add ☘️ 2022-06-17 15:34:35 +03:00
coderaiser
975d4eb8a0 chore(actions) add typos 2022-06-14 14:39:06 +03:00
coderaiser
ef10ebe97c chore(cloudcmd) lint 2022-06-14 14:38:11 +03:00
coderaiser
81e76857da feature(package) thread-it v2.0.0 2022-06-12 00:02:00 +03:00
coderaiser
b12e0690cd
docs(license) 2022 2022-06-03 19:07:50 +03:00
coderaiser
3d4ba373a6 chore(cloudcmd) lint using actions 2022-06-02 09:55:39 +00:00
coderaiser
ed0c2ad45a
Update FUNDING.yml 2022-06-02 12:55:16 +03:00
coderaiser
4e406d758f
Update FUNDING.yml 2022-06-02 12:54:34 +03:00
coderaiser
18adf0852d
Create FUNDING.yml 2022-06-02 12:52:55 +03:00
coderaiser
42248bc0f4 chore(cloudcmd) lint 2022-05-12 22:29:01 +03:00
coderaiser
b3f3510b2d chore(package) v16.1.1 2022-05-12 14:45:22 +03:00
coderaiser
bbf0c04f55 feature(package) edward v14.0.0 2022-05-12 14:44:11 +03:00
coderaiser
6af8f19bb6 chore(cloudcmd) lint 2022-05-11 14:21:25 +03:00
coderaiser
95ede62e81 feature(package) putout v26.0.1 2022-05-11 11:44:58 +03:00
coderaiser
0c28633bbd chore(package) v16.1.0 2022-04-23 01:35:30 +03:00
coderaiser
42c8fd7bfe feature(cloudcmd) improve support of NBSP 2022-04-23 01:34:07 +03:00
coderaiser
e129ab6a79 chore(package) v16.0.1 2022-04-22 14:49:17 +03:00
coderaiser
6e4667aa85 chore(cloudcmd) lint 2022-04-22 14:26:04 +03:00
coderaiser
46a84c54ea feature(package) dword v14.0.0 2022-04-22 14:26:04 +03:00
coderaiser
c5e3134f37 feature(package) restafary v11.0.0 2022-04-22 14:26:04 +03:00
coderaiser
aa0c15f6b6 feature(package) @cloudcmd/stub v4.0.1 2022-04-22 14:26:04 +03:00
coderaiser
23fcc2b8f7 feature(package) win32 v7.0.0 2022-04-22 14:26:04 +03:00
coderaiser
f5f3048908 chore(cloudcmd) lint using actions 2022-03-06 17:48:21 +00:00
coderaiser
d8d9025286 chore(cloudcmd) lint 2022-03-06 19:45:19 +02:00
coderaiser
66fdb550a6 feature(package) eslint-plugin-putout v14.4.0 2022-03-06 19:45:19 +02:00
coderaiser
e59f3cfa71 chore(package) v16.0.0 2022-02-19 16:29:53 +02:00
coderaiser
44422a5206 feature(cloudcmd) drop support of node < 16 2022-02-19 15:38:47 +02:00
coderaiser
b67b7cf73d feature(package) supertape v7.1.0 2022-02-19 15:38:47 +02:00
coderaiser
3ba6d4ae53 chore(cloudcmd) lint 2022-02-19 15:38:37 +02:00
coderaiser
69c8eecaf1 feature(package) eslint-plugin-putout v13.11.0 2022-02-19 15:30:39 +02:00
coderaiser
b65b3ff940 feature(package) madrun v9.0.0 2022-02-19 15:30:00 +02:00
coderaiser
4f06e6398f feature(package) putout v25.0.1 2022-02-19 15:28:21 +02:00
coderaiser
62d39841b1 chore(package) v15.9.15 2022-01-20 20:54:29 +02:00
coderaiser
2cfc4908a2 chore(cloudcmd) lint using actions 2022-01-20 18:44:25 +00:00
coderaiser
177771df6a chore(cloudcmd) lint 2022-01-20 19:50:10 +02:00
coderaiser
23171f967e fix(css) icons (#368) 2022-01-20 19:49:20 +02:00
coderaiser
09f4090a4b chore(package) v15.9.14 2022-01-13 15:51:15 +02:00
coderaiser
f2db9bf2d5 feature(package) eslint-plugin-putout v13.0.1 2022-01-13 15:50:04 +02:00
coderaiser
0ff8470278 feature(package) putout v24.0.2 2022-01-13 15:49:57 +02:00
coderaiser
9b0ce7c7b7 fix(cloudcmd) client: edit-names in vim mode 2022-01-13 12:45:10 +02:00
coderaiser
e68b3232d9 chore(cloudcmd) lint using actions 2022-01-08 18:47:37 +00:00
Timo Knapp
c32ba39f43
chore(docker) ci: update GitHub action to use redrun (#366) 2022-01-08 20:44:51 +02:00
Timo Knapp
c7371d9c3a
fix(docker) images: make dockerfiles use node:lts-buster and node:lts-buster-slim for alpine (#363) 2022-01-06 15:23:34 +02:00
coderaiser
3269cff066 chore(cloudcmd) lint using actions 2022-01-05 14:08:35 +00:00
Timo Knapp
75b35139e4
fix(docker) images: make dockerfiles use node:lts-buster-slim as base image (#357) 2022-01-05 16:06:00 +02:00
coderaiser
838b2e29cf chore(package) v15.9.13 2021-12-23 12:11:01 +02:00
coderaiser
9adc0778a9 chore(cloudcmd) lint 2021-12-23 11:57:08 +02:00
coderaiser
130ce433f4 feature(package) putout v23.0.0 2021-12-23 11:53:54 +02:00
coderaiser
cac774d7ce chore(package) v15.9.12 2021-12-16 20:07:56 +02:00
coderaiser
7053e6be0b fix(client) loadDir: history enabled by default 2021-12-16 20:06:44 +02:00
coderaiser
2743f78cd3 chore(css) lint 2021-12-15 18:05:19 +02:00
coderaiser
49f4e5bec7 chore(docker) alpine: python -> python3 2021-12-15 17:54:49 +02:00
coderaiser
228744bec8 chore(common) lint 2021-12-15 17:18:23 +02:00
coderaiser
2654197681 chore(cloudcmd) base64: lint 2021-12-15 12:21:28 +02:00
coderaiser
205d641e75 chore(common) base64: is not defined 2021-12-14 16:05:40 +02:00
coderaiser
5accfeeaa7 chore(cloudcmd) lint 2021-12-14 14:57:40 +02:00
coderaiser
a024dcdb61 chore(cloudcmd) lint using actions 2021-12-09 12:21:33 +00:00
coderaiser
5b661140fc chore(package) v15.9.11 2021-12-09 14:19:04 +02:00
coderaiser
ed14250fe2 fix(client) load: Upload stuck for zero byte files (#359) 2021-12-09 14:10:24 +02:00
coderaiser
21e9736c81 chore(putout) declare-undefined-variables: add custom declaration of stopAll 2021-12-04 21:11:52 +02:00
coderaiser
8097d96c72 chore(package) v15.9.10 2021-12-04 01:35:55 +02:00
coderaiser
efb686c332 chore(cloudcmd) lint 2021-12-04 01:35:55 +02:00
coderaiser
bd5a8afdd5 fix(client) operation: move and copy not working (#358) 2021-12-04 01:03:46 +02:00
coderaiser
d15134b272 feature(package) eslint-plugin-putout v12.2.0 2021-12-04 00:38:50 +02:00
coderaiser
b9b4af0454 refactor(client) listeners: rm initF2 2021-11-22 20:16:03 +02:00
coderaiser
18eeec0165 chore(cloudcmd) lint using actions 2021-11-22 12:24:07 +00:00
coderaiser
b2a7a6770c
docs(help) rm heroku 2021-11-22 14:21:40 +02:00
coderaiser
c40d2bb096 chore(package) v15.9.9 2021-11-22 00:53:20 +02:00
coderaiser
c7242199e7 chore(cloudcmd) lint using actions 2021-11-21 22:49:42 +00:00
coderaiser
f1fcde75c3 chore(madrun) ERR_OSSL_EVP_UNSUPPORTED error on node v17 (https://stackoverflow.com/a/69394785) 2021-11-22 00:47:31 +02:00
coderaiser
0f17c11c04 chore(cloudcmd) lint 2021-11-20 22:20:10 +02:00
coderaiser
57390d541f feature(package) putout v22.0.2 2021-11-20 21:54:16 +02:00
coderaiser
e4cf91ac1d feature(package) yargs-parser v21.0.0 2021-11-20 21:54:06 +02:00
coderaiser
8fc0731900 chore(cloudcmd) lint 2021-11-04 14:45:15 +02:00
coderaiser
ae552ede60 feature(package) yaspeller v8.0.0 2021-11-04 14:41:43 +02:00
coderaiser
3dd9d6e454 feature(package) eslint-plugin-putout v11.1.0 2021-11-04 14:41:13 +02:00
coderaiser
7258013b88 feature(package) putout v21.1.2 2021-11-04 14:41:05 +02:00
coderaiser
3172759ca0 chore(cloudcmd) lint 2021-10-19 15:59:57 +03:00
coderaiser
85015881d0 feature(package) eslint v8.0.1 2021-10-19 15:56:50 +03:00
coderaiser
977d9d057f docs(readme) rm heroku 2021-10-06 15:20:39 +03:00
coderaiser
111eba8c31 chore(cloudcmd) lint 2021-09-30 17:22:59 +03:00
coderaiser
8f466b4c9a feature(package) eslint-plugin-putout v10.3.0 2021-09-30 17:07:20 +03:00
coderaiser
057ec42b25 feature(package) find-up v6.1.0 2021-09-30 17:07:10 +03:00
coderaiser
eab9df4490 feature(package) putout v20.5.1 2021-09-30 17:06:59 +03:00
coderaiser
b6549a6484 feature(package) supertape v6.9.1 2021-09-30 16:59:53 +03:00
coderaiser
883b4487e0 chore(cloudcmd) lint 2021-08-15 14:57:22 +03:00
coderaiser
699deb86c6 feature(package) eslint-plugin-putout v9.0.1 2021-08-15 14:57:04 +03:00
coderaiser
8fcfaec8a0 chore(package) v15.9.8 2021-08-09 17:36:16 +03:00
coderaiser
a5ea620ca8 chore(user-menu) lint 2021-08-09 17:35:27 +03:00
coderaiser
c798b42a64 fix(cloudcmd) args: snake case -> camelCase: yargs strip dashed 2021-08-09 17:31:44 +03:00
coderaiser
13f7fb2200 chore(package) v15.9.7 2021-06-27 16:38:03 +03:00
coderaiser
7ca9b8cece feature(package) package-json v7.0.0 2021-06-27 16:36:53 +03:00
coderaiser
c2e51f75a1 fix(config) rm broadcast: cannot change config 2021-06-27 16:36:37 +03:00
coderaiser
4d9878b574 fix(view) close modals using ESC
show: bindKeys: truthy-> not equal false (abc727b846)
2021-06-27 15:58:38 +03:00
coderaiser
abe512c655 feature(config) add more descriptive titles 2021-06-27 15:38:35 +03:00
coderaiser
b67526f09b
chore(actions) node: add versions matrix 2021-06-13 22:57:19 +03:00
coderaiser
e236e87171 chore(package) v15.9.6 2021-06-13 15:45:24 +03:00
coderaiser
26fad02507 feature(package) @cloudcmd/move-files v5.0.0 2021-06-13 15:27:00 +03:00
coderaiser
e73c327687
docs(help) add missing "boot" 2021-06-10 22:41:01 +03:00
coderaiser
f3e0337584 chore(package) v15.9.5 2021-06-08 14:15:52 +03:00
coderaiser
1736ec7be2 chore(cloudcmd) spell 2021-06-08 14:14:55 +03:00
coderaiser
fbc8ae25ad chore(cloudcmd) lint 2021-06-08 14:13:46 +03:00
coderaiser
f92152195c feature(package) eslint-plugin-putout v8.0.1 2021-06-08 12:08:09 +03:00
coderaiser
bbe790e551 feature(package) es6-promisify v7.0.0 2021-06-08 12:08:04 +03:00
coderaiser
954f2c047c feature(package) putout v18.0.1 2021-06-08 12:07:02 +03:00
IhatemyISP
baac531655
docs(help) added instructions to autostart cloudcmd on boot for FreeBSD systems (#348) 2021-06-04 11:45:08 +03:00
khevin014
262c72d44a docs(help) added instructions to autostart cloudcmd on boot for systems using systemd (#347) 2021-05-28 16:26:00 +03:00
coderaiser
5e63a524c1 docs(readme) example: io.listen -> new Server (#341) 2021-05-04 22:23:46 +03:00
coderaiser
5d93d5752c chore(cloudcmd) lint 2021-05-04 21:02:06 +03:00
coderaiser
241efe971b feature(package) putout v17.0.0 2021-05-04 20:50:48 +03:00
coderaiser
8c59333a02 fix(load) put: replace #: single -> multiple 2021-04-12 19:17:20 +03:00
coderaiser
9a724a4bba fix(server) user-menu: e.message -> errorFind.message 2021-04-12 18:46:58 +03:00
coderaiser
8c8ac3bab4 chore(package) v15.9.4 2021-04-05 19:12:18 +03:00
coderaiser
843c319b1e feature(package) socket.io-client v4.0.1 2021-04-05 19:07:47 +03:00
coderaiser
879bc2ff8c feature(package) open v8.0.5 2021-04-05 19:07:17 +03:00
coderaiser
3e303253d2 feature(package) putout v16.4.0 2021-04-05 19:07:07 +03:00
coderaiser
ad689ece31 chore(package) v15.9.3 2021-03-17 16:08:43 +02:00
coderaiser
1801689ef7 fix(cloudcmd) Docker latest-alpine (15.9.2) stopped with errors (#337) 2021-03-17 16:01:02 +02:00
coderaiser
e77d8a709a chore(package) v15.9.2 2021-03-16 18:02:52 +02:00
coderaiser
b090b498a8 test(route) coverage 2021-03-16 17:54:44 +02:00
coderaiser
b5618412c3 feature(package) open v8.0.2 2021-03-16 17:48:27 +02:00
coderaiser
bb3e07def3 feature(package) socket.io-client v4.0.0 2021-03-16 17:48:27 +02:00
coderaiser
a3f0f4ecae feature(package) socket.io v4.0.0 2021-03-16 17:45:59 +02:00
coderaiser
df60dd7ba0 fix(route) unable to navigate between folders (#333) 2021-03-16 17:44:54 +02:00
coderaiser
21e7ea7d4c test(cloudcmd) ok -> match 2021-03-09 18:41:52 +02:00
coderaiser
a091a382ca chore(cloudcmd) lint using actions 2021-03-09 15:52:07 +00:00
coderaiser
0edbffa093 chore(user-menu) lint 2021-03-09 17:42:01 +02:00
coderaiser
08cc5dbba4 chore(archive-link) update 2021-03-05 21:01:10 +02:00
coderaiser
edbebd983e chore(cloudcmd) lint using actions 2021-03-02 12:42:54 +00:00
coderaiser
90eae0e944 test(route) flop -> win32 2021-03-02 14:40:56 +02:00
coderaiser
3976056ee8 chore(package) v15.9.1 2021-03-01 23:17:42 +02:00
coderaiser
8f12c6c25f feature(cloudcmd) icons: add archive icon for small screens 2021-03-01 22:58:33 +02:00
coderaiser
c92815d78d feature(cloudcmd) add archive-link image 2021-03-01 22:44:37 +02:00
coderaiser
2fe6dcf166 chore(package) v15.9.0 2021-03-01 18:13:17 +02:00
coderaiser
33f2572979 test(route) ci 2021-03-01 18:13:17 +02:00
coderaiser
a71dca0e70 chore(route) set content length 2021-03-01 18:13:17 +02:00
coderaiser
cb37ab871b chore(madrun) spell 2021-03-01 17:42:21 +02:00
coderaiser
f391a3391f feature(cloudcmd) add archive icon 2021-03-01 17:38:37 +02:00
coderaiser
ce511812d5 feature(client) rm unused loadCurrentTime 2021-02-28 11:12:18 +02:00
coderaiser
89af33fb9d feature(package) win32 v6.0.0 2021-02-27 20:58:06 +02:00
coderaiser
fec91e7093 feature(package) redzip v2.0.0 2021-02-27 20:52:22 +02:00
coderaiser
a092a2fc9d chore(travis) rm 2021-02-27 16:11:58 +02:00
coderaiser
775850c004 chore(package) v15.8.1 2021-02-27 16:10:47 +02:00
coderaiser
1a5dc6228c chore(cloudcmd) lint using actions 2021-02-27 14:10:23 +00:00
coderaiser
6622d508af feature(package) restafary v9.7.0 (#330) 2021-02-27 16:06:56 +02:00
coderaiser
a6a1e80afb chore(package) v15.8.0 2021-02-23 14:31:19 +02:00
coderaiser
40f885e7c4 feature(cloudcmd) add ability to suggest option, when wrong provided 2021-02-23 14:29:42 +02:00
coderaiser
f0fd4031bb feature(package) supertape v5.0.0 2021-02-23 12:45:30 +02:00
coderaiser
0831723565 chore(cloudcmd) rm dead code 2021-02-22 00:18:07 +02:00
coderaiser
71a7e9dd93 chore(package) v15.7.1 2021-02-21 11:35:51 +02:00
coderaiser
07fe299ad6 fix(cloudcmd) prefix env has no effect (#328) 2021-02-21 11:25:42 +02:00
coderaiser
b18c738f5b chore(package) v15.7.0 2021-02-19 22:27:41 +02:00
coderaiser
bcc11bda74 chore(lint) add progress 2021-02-19 22:26:13 +02:00
coderaiser
a108bd82b5 feature(package) @cloudcmd/move-files v4.0.0 2021-02-19 22:12:30 +02:00
coderaiser
e3d373acf9 chore(madrun) nyc -> c8 2021-02-17 14:09:08 +02:00
coderaiser
2a5ebe300d chore(putout) ignore: html 2021-02-13 15:04:09 +02:00
coderaiser
04e54f2794 chore(cloudcmd) lint 2021-02-13 12:33:00 +02:00
coderaiser
2e75ca18f4 feature(package) putout v15.0.0 2021-02-11 14:00:08 +02:00
coderaiser
d8f0e92727 feature(package) copymitter v6.0.0 2021-02-10 12:22:35 +02:00
coderaiser
70c8ab537c chore(actions) nodejs: putout -> cloudcmd 2021-02-09 14:46:53 +02:00
coderaiser
f97677f384 chore(putout) lint using actions 2021-02-09 11:54:09 +00:00
coderaiser
bbb6d46c09 chore(package) coverage:report 2021-02-09 13:53:09 +02:00
coderaiser
0c7cc3c0a2 docs(help) add versions history 2021-02-09 13:50:20 +02:00
coderaiser
0128014f51 chore(gitignore) add .DS_Store 2021-02-09 13:39:54 +02:00
coderaiser
75361de125 refactor(release) promisify 2021-02-09 13:39:54 +02:00
coderaiser
98be1b01f1 docs(readme) add link to patreon 2021-02-09 13:32:42 +02:00
coderaiser
3ac0bf23e4 chore(cloudcmd-hq) add 2021-02-07 15:12:23 +02:00
coderaiser
983ccac4d1
docs(readme) Docker: add "\n" 2021-02-05 11:11:38 +02:00
coderaiser
47883b8751
docs(readme) Documentation 2021-02-04 21:55:46 +02:00
Frak
955b7f0c4c
docs(README) add documentation link 2021-02-04 21:55:01 +02:00
coderaiser
77bd89f8f7 chore(putout) lint using actions 2021-02-03 19:07:12 +00:00
coderaiser
135edafaee feature(package) @cloudcmd/olark v3.0.2 2021-02-03 21:04:30 +02:00
coderaiser
fce254f724 chore(github-actions) add ability to autofix lint 2021-02-03 14:07:32 +02:00
coderaiser
60607184d4 feature(client) view: unknown file type: method: GET -> HEAD: speed up 2021-02-03 13:53:41 +02:00
coderaiser
5e545c87b3 chore(package) v15.6.0 2021-02-03 13:22:38 +02:00
coderaiser
2b9705d9fc feature(client) view: add ability to detect file type when extension is missing (coderaiser/cloudcmd#287) 2021-02-03 13:20:33 +02:00
coderaiser
0d79531b69 chore(package) v15.5.2 2021-02-02 14:03:24 +02:00
coderaiser
b4f279d07c chore(cloudcmd) lint 2021-02-02 14:03:24 +02:00
coderaiser
3cd1d67e58 fix(client) view: isAudio (#322) 2021-02-02 13:43:12 +02:00
coderaiser
2fc6c6e1a1 docs(changelog) 314 -> 319 2021-01-31 12:48:35 +02:00
coderaiser
a63ce70d49 chore(package) v15.5.1 2021-01-31 12:46:47 +02:00
coderaiser
bc80069f18 fix(cloudcmd) cannot create a directory (#314) 2021-01-31 12:45:06 +02:00
coderaiser
996d99cbac docs(readme) require -> import 2021-01-30 11:41:50 +02:00
coderaiser
a39ea0d61c chore(package) v15.5.0 2021-01-30 11:26:12 +02:00
coderaiser
3b81abed36 feature(package) style-loader v2.0.0 2021-01-30 11:20:11 +02:00
coderaiser
1b5a6a3721 feature(cloudcmd) improve error handling when viewing or editing a file 2021-01-30 11:19:10 +02:00
coderaiser
b3b618b2ca feature(package) putout v14.0.0 2021-01-26 19:28:03 +02:00
coderaiser
7d1a35d8b2 feature(package) eslint-plugin-putout v7.0.0 2021-01-26 19:27:56 +02:00
coderaiser
dae05277d6 chore(package) v15.4.4 2021-01-26 17:36:56 +02:00
coderaiser
ac85786b62 feature(package) win32 v5.1.11 2021-01-26 17:34:37 +02:00
coderaiser
325706756c chore(putout) rm apply-optional-chaining: enabled by default 2021-01-26 17:34:37 +02:00
coderaiser
ee5cbb13b0 chore(package) v15.4.3 2021-01-25 20:56:57 +02:00
coderaiser
b0e63fd3fd feature(package) restbox v3.0.0 2021-01-25 20:45:48 +02:00
coderaiser
fd32f76bf0 feature(package) ponse v6.0.0 2021-01-25 19:55:29 +02:00
coderaiser
49f298bc46 feature(package) files-io v4.0.0 2021-01-25 19:47:53 +02:00
coderaiser
a3c18dcdb2 chore(package) v15.4.2 2021-01-21 20:22:10 +02:00
coderaiser
38d29a6b27 chore(cssnano) lint 2021-01-21 20:17:31 +02:00
coderaiser
e3cac1a892 feature(package) mellow v3.0.0 2021-01-21 16:48:08 +02:00
coderaiser
9b9070b69b feature(route) tokenize -> wraptile 2021-01-21 16:30:21 +02:00
coderaiser
84b5019dd0 chore(package) v15.4.1 2021-01-20 20:29:16 +02:00
coderaiser
ee81cd1533 feature(route) redzip -> win32 (#317) 2021-01-20 20:22:21 +02:00
coderaiser
5edd57a390 chore(package) v15.4.0 2021-01-19 21:09:58 +02:00
coderaiser
f261a0ea08 feature(cloudcmd) add abilit to parse markdown inside zip archives 2021-01-19 21:08:17 +02:00
coderaiser
6182f621d6 chore(package) v15.3.4 2021-01-19 18:10:09 +02:00
coderaiser
72ed5bbe4e docs(readme) spell 2021-01-19 18:08:38 +02:00
coderaiser
fac9b347c2 feature(package) deepword v8.0.0 2021-01-19 18:06:25 +02:00
Timo Knapp
48ae1c2e63
fix(docker) images: change alpine images to be part of master image (#316) 2021-01-19 17:36:27 +02:00
coderaiser
d785952b7e feature(package) console-io v13.0.0 2021-01-19 14:36:40 +02:00
coderaiser
5f13f380e9 feature(package) edward v13.0.0 2021-01-19 14:26:04 +02:00
Timo Knapp
99b93e760a feature(docker) add support of multi-arch builds (#291) 2021-01-19 13:56:00 +02:00
coderaiser
11cc63adcd chore(package) v15.3.3 2021-01-18 21:59:37 +02:00
Jacob Henry
c057b950c8 fix(dom) bug in Firefox with Imagus extension enabled: change getCurrentName to read from data-name instead of title (#313)
Separated out name generation/parsing logic.
Added test case.
2021-01-18 21:59:37 +02:00
coderaiser
b6f30d1ccb chore(package) v15.3.2 2021-01-18 14:43:15 +02:00
coderaiser
546b511a9a fix(client) dom: goToDirectory 2021-01-18 14:43:15 +02:00
coderaiser
ddef087328 chore(package) v15.3.1 2021-01-17 17:19:27 +02:00
coderaiser
2355618409 feature(package) dword v13.0.0 2021-01-17 17:18:18 +02:00
coderaiser
28461fbe19 feature(modules) socket.io v3.1.0 2021-01-17 17:10:21 +02:00
coderaiser
cdb719eb2a fix(view) view html, pdf 2021-01-17 17:10:12 +02:00
coderaiser
da692a4798 chore(package) v15.3.0 2021-01-17 16:43:18 +02:00
coderaiser
c002fc37c5 feature(client) improve vim support: Esc - toggle vim support in editors 2021-01-17 16:39:00 +02:00
coderaiser
4e86f7f308 refactor(dom) loadCurrentSize: callback -> promise 2021-01-17 16:02:19 +02:00
coderaiser
462d7bf3b5 feature(client) view: add ability to view raw files using Shift + F3 2021-01-17 15:55:24 +02:00
coderaiser
eb2c15ba17 chore(madrun) watch:lint: staged files 2021-01-17 15:49:00 +02:00
coderaiser
635b195d48 feature(client) view: show markdown without shift 2021-01-17 15:48:34 +02:00
coderaiser
301bfa3234 feature(key) add ability to toggle global vim config on the time of session 2021-01-17 15:07:09 +02:00
coderaiser
4b51f363ae chore(package) v15.2.0 2021-01-16 00:57:51 +02:00
coderaiser
57485b5b4b feature(cloudcmd) add ability to open html files using F3 2021-01-16 00:53:37 +02:00
coderaiser
cd7bf0fe01 feature(cloudcmd) add ability to toggle vim hotkes using Esc 2021-01-16 00:53:28 +02:00
coderaiser
595805eeaf feature(package) @cloudcmd/fileop v5.0.0 2021-01-15 21:00:50 +02:00
coderaiser
a2c17e5e0e chore(client) rm console.log 2021-01-14 13:00:37 +02:00
coderaiser
af2548acd1 chore(madrun) simplify with cutEnv 2021-01-12 00:59:29 +02:00
coderaiser
82c576c850 fix(server) distribute: simplify colors 2021-01-11 13:44:31 +02:00
coderaiser
0ec17645b6 chore(package) v15.1.0 2021-01-07 00:35:33 +02:00
coderaiser
b35a3e029f feature(cloudcmd) add ability to read zip files like directories (#309) 2021-01-07 00:08:58 +02:00
coderaiser
0037ec9606 chore(package) v15.0.4 2021-01-05 19:07:42 +02:00
coderaiser
cbd5a2786c feature(package) flop v9.0.0 2021-01-05 19:02:57 +02:00
coderaiser
226d7c93bd feature(package) restafary v9.0.1 2021-01-05 19:02:53 +02:00
coderaiser
a2c82c56fb chore(package) webpack: add progress bar 2021-01-05 18:14:11 +02:00
coderaiser
dd91c810ad chore(package) v15.0.3 2021-01-05 18:05:35 +02:00
coderaiser
fcdb2e3a28 chore(cloudcmd) simportConfig -> importConfig 2021-01-05 18:04:42 +02:00
coderaiser
b06c9499a3 docs(readme) rm fossa badge 2021-01-05 17:53:37 +02:00
coderaiser
394bf46eca fix(cloudcmd) validateRoot: import -> simport: forEachKey is not a function (#311) 2021-01-05 17:51:26 +02:00
coderaiser
0967ad12e8 feature(package) onezip v5.0.0 2021-01-05 14:23:18 +02:00
coderaiser
fdcc060861 feature(package) socket.io v3.0.5 (socketio/socket.io#3720) 2021-01-05 13:41:01 +02:00
coderaiser
77384f35c4 chore(madrun) lint 2021-01-05 13:41:01 +02:00
coderaiser
59338a16ac chore(cloudcmd) lint: key-spacing 2021-01-05 13:41:01 +02:00
coderaiser
a91344595c
chore(madrun) start: js -> mjs 2021-01-04 20:45:49 +02:00
coderaiser
a2586424f9 refactor(cloudfunc) getPathLink: map -> for 2021-01-03 14:51:19 +02:00
coderaiser
d08921bde3 test(cloudfunc) lint 2021-01-03 14:51:03 +02:00
coderaiser
9214bb4f31 test(cloudfunc) getPathLink 2021-01-03 14:43:20 +02:00
coderaiser
77aff31bcc chore(package) v15.0.2 2020-12-31 18:52:13 +02:00
coderaiser
89570857bb fix(cloudcmd) criton: crash when password set (#310) 2020-12-31 18:44:14 +02:00
coderaiser
84d1db5f1e docs(readme) docker: -t -> -it 2020-12-30 16:15:38 +02:00
coderaiser
a87fb57188 chore(package) v15.0.1 2020-12-30 15:41:50 +02:00
coderaiser
95db6de63c fix(docker) cloudcmd.js -> cloudcmd.mjs 2020-12-30 15:41:50 +02:00
coderaiser
bbffe1a9bd chore(travis) node v14 2020-12-30 15:09:55 +02:00
coderaiser
9d6b6b7c93 chore(cloudcmd) lint 2020-12-30 14:41:27 +02:00
coderaiser
76e0eab94c fix(cloudcmd) css: media query for screen size with width more then 1600 (cloudcmd/console-io#6) 2020-12-30 14:38:33 +02:00
coderaiser
9a13502597 fix(cloudcmd) client: build: optional chaining 2020-12-30 14:02:36 +02:00
coderaiser
77d02c7844 feature(package) putout v13.0.0 2020-12-28 23:25:37 +02:00
coderaiser
c3339d8ea2 fix(cloudcmd) drop support of node < 14 2020-12-28 23:00:04 +02:00
coderaiser
7371871f15 chore(package) v15.0.0 2020-12-28 20:18:38 +02:00
coderaiser
8f0f6db9d6 chore(madrun) wisdom:type 2020-12-28 20:15:40 +02:00
coderaiser
27ddaf726d chore(cloudcmd) lint 2020-12-28 20:14:10 +02:00
coderaiser
e6c1d928ec feature(package) auto-globals v2.0.0 2020-12-28 19:47:46 +02:00
coderaiser
ae360b1db3 docs(readme) convert commonjs to esm 2020-12-28 17:39:08 +02:00
coderaiser
b5be1f3933 feature(cloudcmd) convert to EcmaScriptModules 2020-12-28 17:37:29 +02:00
coderaiser
006344b58a feature(package) supertape v4.1.0 2020-12-25 18:16:53 +02:00
coderaiser
b18b6af4dc feature(package) supertape v3.10.0 2020-12-21 15:19:18 +02:00
coderaiser
4d8f489b18 chore(madrun) lint: stylelint now bundled with putout 2020-12-15 16:25:06 +02:00
coderaiser
76380a1f04 chore(cloudcmd) lint 2020-12-15 15:30:32 +02:00
coderaiser
332593c23e feature(package) babel-plugin-macros v3.0.0 2020-12-15 15:26:50 +02:00
coderaiser
fd818c5f31 chore(madrun) commonjs -> esm 2020-12-14 15:19:23 +02:00
coderaiser
1b7b163a66 feature(package) putout v12.0.0 2020-12-14 15:19:23 +02:00
coderaiser
1c9ca22b93
chore(travis) rm windows 2020-12-14 00:06:20 +02:00
coderaiser
5a1a39478f test(console) hang 2020-12-04 22:54:28 +02:00
coderaiser
983609f19a chore(datetime) rm unused 2020-12-04 18:49:37 +02:00
coderaiser
c719e37cc1 chore(cloudcmd) update dependencies 2020-12-04 18:47:24 +02:00
coderaiser
4c7f8cc6b6 chore(cloudcmd) lint 2020-12-03 18:27:59 +02:00
coderaiser
1e853b040d feature(package) supertape v3.1.3 2020-12-03 18:21:01 +02:00
coderaiser
fcc59c0674 feature(package) madrun v8.0.0 2020-12-03 18:20:56 +02:00
coderaiser
ee091a33c9 chore(putout) ignore: add dist 2020-12-03 18:20:21 +02:00
coderaiser
13e7af779b docs(help) change link 2020-12-03 18:20:11 +02:00
coderaiser
a9e9dd7270 fix(cloudcmd) app, help: remove duplicate keys 2020-11-25 20:54:47 +02:00
coderaiser
75c6abb2b1 test(cloudcmd) skip: connect_error tests 2020-11-16 17:40:32 +02:00
coderaiser
ca7acdd812 feature(package) gritty v6.0.0 2020-11-16 16:43:28 +02:00
coderaiser
57750689ee feature(package) console-io v12.0.0 2020-11-16 16:17:54 +02:00
coderaiser
ecd7423ac0 docs(help) lint 2020-11-16 16:10:04 +02:00
coderaiser
f74d3e71a2 feature(package) socket.io-client v3.0.1 2020-11-16 15:44:18 +02:00
coderaiser
eba20025fe feature(package) socket.io v3.0.1 2020-11-16 15:44:09 +02:00
coderaiser
30da4bcd85 docs(readme) add to lint 2020-11-03 10:27:36 +02:00
coderaiser
ab7f266e2b feature(package) putout v11.0.2 2020-11-03 10:23:22 +02:00
coderaiser
32f97392bb feature(package) eslint-plugin-putout v6.0.0 2020-11-02 14:57:39 +02:00
coderaiser
382ac94a1f chore(travis) node v15 2020-10-25 10:12:47 +02:00
coderaiser
42046eff3a chore(putout) true -> on, false -> off 2020-10-17 18:05:31 +03:00
coderaiser
154030c5fa chore(putout) prepare for webpack v5 2020-10-17 18:04:35 +03:00
coderaiser
38fa908872 feature(package) markdown-it v12.0.0 2020-10-17 18:04:35 +03:00
j35tor
fd5b2cc6c6
docs(readme) typos in examples (#300)
Co-authored-by: j35tor <j35tor@disroot.org>
2020-10-07 12:59:32 +03:00
coderaiser
514644269c chore(cloudcmd) menu: add lint, fix:lint, test, coverage 2020-09-21 16:10:35 +03:00
coderaiser
6d1b18da07 chore(menu) button -> key 2020-09-21 15:46:35 +03:00
coderaiser
d918ed1f52 chore(github) workflows: add coverage 2020-09-17 16:59:57 +03:00
coderaiser
175d01f3d1 chore(github) workflows: split into 4 steps 2020-09-17 16:58:14 +03:00
coderaiser
1dbc978916 chore(github) workflows: node: v12 -> v14 2020-09-17 16:56:36 +03:00
coderaiser
14dc7ac3a3 feature(package) putout v10.0.0 2020-09-16 19:18:31 +03:00
coderaiser
f434bd1133 test(distribute) import: env: reassign -> delete 2020-09-11 22:06:55 +03:00
coderaiser
d8c2ac4708 feature(package) gritty v5.0.0 2020-09-11 21:52:09 +03:00
coderaiser
79f68d01c8 feature(distribute) import: do not import if env variable set 2020-09-11 21:41:16 +03:00
coderaiser
b8a83868da chore(cloudcmd) save, then import 2020-09-11 19:12:43 +03:00
coderaiser
0b74250248 fix(cloudcmd) importListen prevent server from start (#295) 2020-09-11 18:42:04 +03:00
coderaiser
8932378e07 chore(app) add stack (https://devcenter.heroku.com/articles/upgrading-to-the-latest-stack) 2020-09-11 18:16:08 +03:00
coderaiser
1e4761a10a feature(package) drop support of node < 12 2020-09-06 21:26:51 +03:00
coderaiser
0a7c52c75d chore(cloudcmd) lint: numeric separators 2020-09-06 21:25:40 +03:00
coderaiser
c61681c331 chore(madrun) rm lint:progress, -f progress enabled by default 2020-09-02 16:02:47 +03:00
coderaiser
19888b3ee5 feature(package) table v6.0.1 2020-09-02 15:53:24 +03:00
coderaiser
6d7f274bde chore(package) v14.9.3 2020-08-21 15:51:28 +03:00
coderaiser
706b823c4a fix(client) dom: buffer: get, set -> getJson, setJson (#295) 2020-08-21 15:50:17 +03:00
coderaiser
aeb5906de3 feature(storage) rm unused removeMatch 2020-08-19 18:14:30 +03:00
coderaiser
c8d2098e5b test(storage) coverage 2020-08-19 18:12:30 +03:00
coderaiser
6cb0da0ce8 chore(package) v14.9.2 2020-08-19 17:45:59 +03:00
coderaiser
d181b84c3b fix(cloudcmd) client: edit: json files (#294) 2020-08-19 17:44:26 +03:00
coderaiser
5732752350 chore(package) v14.9.1 2020-08-16 16:33:24 +03:00
coderaiser
0f93c4fa1b feature(cloudcmd) add IO.createDirectory (b54b5486f4) 2020-08-16 16:32:22 +03:00
coderaiser
92e0e4bb91 chore(package) v14.9.0 2020-08-14 19:08:45 +03:00
coderaiser
291dc39c25 feature(key) vim: add ability to navigate to next and previous using w and b 2020-08-14 19:07:28 +03:00
coderaiser
750b7571f5 feature(key) vim: add ability to navigate using to first and last file using ^ and $ 2020-08-14 19:07:28 +03:00
coderaiser
87f4022759 docs(help) UserMenu: add move, copy 2020-08-14 19:07:28 +03:00
coderaiser
d053e60ed2 chore(package) v14.8.0 2020-08-14 17:16:35 +03:00
coderaiser
5041930685 feature(cloudcmd) IO.cp -> IO.copy 2020-08-14 17:14:45 +03:00
coderaiser
13307f3861 feature(cloudcmd) IO.mv -> IO.move 2020-08-14 17:14:45 +03:00
coderaiser
b081dc8b18 feature(package) find-up v5.0.0 2020-08-13 15:52:44 +03:00
coderaiser
7fd5e2bc90 chore(package) v14.7.2 2020-08-11 13:34:49 +03:00
coderaiser
e973f5fd4e fix(client) key: F3: search appears 2020-08-11 13:31:50 +03:00
coderaiser
b2fa10f2de test(view) initConfig: add 2020-08-11 13:31:50 +03:00
coderaiser
222ce237c5 chore(package) v14.7.1 2020-08-11 01:13:50 +03:00
coderaiser
27df34f000 fix(view) init config: copy 2020-08-11 01:11:08 +03:00
coderaiser
50e0152ced chore(package) v14.7.0 2020-08-10 14:43:05 +03:00
coderaiser
c49d25229c fix(client) user menu: click on user menu title 2020-08-10 14:42:11 +03:00
coderaiser
28ceb7f7ff feature(cloudcmd) rest: add rename 2020-08-10 14:42:11 +03:00
coderaiser
d8dd9f5738 feature(rest) mv: improved user errors 2020-08-09 16:18:22 +03:00
coderaiser
16a1eade2a feature(client) io: promisify -> async 2020-08-06 11:21:57 +03:00
coderaiser
807a1bc26d chore(export) lint 2020-07-27 12:29:05 +03:00
coderaiser
e7feb83f76 chore(edit-names) rm unused arguments 2020-07-08 20:04:13 +03:00
coderaiser
a117867307 feature(package) webpack-merge v5.0.8 2020-07-08 20:04:13 +03:00
coderaiser
e08a6bead8 feature(package) eslint-plugin-putout v5.0.0 2020-06-24 16:13:38 +03:00
coderaiser
2ed1c3a9dc feature(package) putout v9.0.0 2020-06-24 16:13:32 +03:00
coderaiser
378d694871 feature(package) madrun v7.0.0 2020-06-24 16:13:30 +03:00
coderaiser
8bb746aa0e chore(cloudcmd) lint: indexOf -> includes 2020-06-07 11:29:06 +03:00
coderaiser
c30bc69f5d chore(cloudcmd) lint: convert indexOf to includes 2020-06-01 19:44:41 +03:00
coderaiser
73dc17318e test(cloudcmd) test/server/prefixer -> server/prefixer 2020-06-01 19:42:19 +03:00
coderaiser
d23118f692 chore(cloudcmd) lint 2020-05-31 03:12:07 +03:00
coderaiser
b48507d92a chore(cloudcmd) lint 2020-05-28 23:52:46 +03:00
281 changed files with 13269 additions and 7597 deletions

View file

@ -1,8 +0,0 @@
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"module:babel-plugin-macros",
]
}

9
.babelrc.json Normal file
View file

@ -0,0 +1,9 @@
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"module:babel-plugin-macros",
"@babel/plugin-transform-optional-chaining"
]
}

View file

@ -1,5 +1,4 @@
last 2 Chrome versions last 2 Chrome versions
last 2 Safari versions last 2 Safari versions
Firefox ESR Firefox ESR
maintained node versions
not dead not dead

View file

@ -1,25 +1,31 @@
'use strict'; export default {
'F2 - Rename File': renameCurrent,
module.exports = { 'L - Lint': run('npm run lint'),
'F2 - Rename file': async ({DOM}) => { 'F - Fix Lint': run('npm run fix:lint'),
await DOM.renameCurrent(); 'T - Test': run('npm run test'),
}, 'C - Coverage': run('npm run coverage'),
'D - Build Dev': async ({CloudCmd}) => { 'D - Build Dev': run('npm run build:client:dev'),
await CloudCmd.TerminalRun.show({ 'P - Build Prod': run('npm run build:client'),
command: 'npm run build:client:dev',
autoClose: false,
closeMessage: 'Press any button to close Terminal',
});
CloudCmd.refresh();
},
'P - Build Prod': async ({CloudCmd}) => {
await CloudCmd.TerminalRun.show({
command: 'npm run build:client',
autoClose: true,
});
CloudCmd.refresh();
},
}; };
async function renameCurrent(DOM) {
await DOM.renameCurrent();
}
function run(command) {
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

@ -19,6 +19,5 @@ cssnano.config.js
bin/release.js bin/release.js
client client
legacy
server_ server_

View file

@ -9,7 +9,7 @@ root = true
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = false
indent_style = space indent_style = space
indent_size = 4 indent_size = 4

View file

@ -1,34 +0,0 @@
'use strict';
module.exports = {
extends: [
'plugin:putout/recommended',
],
plugins: [
'putout',
'node',
],
overrides: [{
files: ['bin/release.js'],
rules: {
'no-console': 'off',
'node/shebang': 'off',
},
extends: [
'plugin:node/recommended',
],
}, {
files: ['bin/cloudcmd.js'],
rules: {
'no-console': 'off',
},
extends: [
'plugin:node/recommended',
],
}, {
files: ['{client,common}/**/*.js'],
env: {
browser: true,
},
}],
};

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
github: coderaiser
open_collective: cloudcmd
ko_fi: coderaiser

View file

@ -1,12 +0,0 @@
<!--
Thank you for reporting an issue. Please fill in the template below. If unsure
about something, just do as best as you're able.
-->
* **Version** (`cloudcmd -v`):
* **Node Version** `node -v`:
* **OS** (`uname -a` on Linux):
* **Browser name/version**:
* **Used Command Line Parameters**:
* **Changed Config**:

45
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,45 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: needs clarification
assignees: coderaiser
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- **Version** (`cloudcmd -v`):
- **Node Version** `node -v`:
- **OS** (`uname -a` on Linux):
- **Browser name/version**:
- **Used Command Line Parameters**:
- **Changed Config**:
```json
{}
```
- [ ] 🎁 **I'm ready to donate on https://opencollective.com/cloudcmd**
- [ ] 🎁 **I'm ready to donate on https://ko-fi.com/coderaiser**
- [ ] 💪 **I'm willing to work on this issue**
**Additional context**
Add any other context about the problem here.

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Stack Overflow
url: https://stackoverflow.com/search?q=cloudcmd
about: Please ask and answer questions here.

View file

@ -0,0 +1,21 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -0,0 +1,24 @@
***
name: Tracking issue
about: Create an issue with bug report or feature request.
title: ""
labels: needs triage
assignees: coderaiser
***
- **Version** (`cloudcmd -v`):
- **Node Version** `node -v`:
- **OS** (`uname -a` on Linux):
- **Browser name/version**:
- **Used Command Line Parameters**:
- **Changed Config**:
```json
{}
```
- [ ] 🎁 **I'm ready to donate on https://opencollective.com/cloudcmd**
- [ ] 🎁 **I'm ready to donate on https://ko-fi.com/coderaiser**
- [ ] 💪 **I'm willing to work on this issue**

View file

@ -4,6 +4,5 @@ about something, just do as best as you're able.
--> -->
- [ ] commit message named according to [Contributing Guide](https://github.com/coderaiser/cloudcmd/blob/master/CONTRIBUTING.md "Contributting Guide") - [ ] commit message named according to [Contributing Guide](https://github.com/coderaiser/cloudcmd/blob/master/CONTRIBUTING.md "Contributting Guide")
- [ ] `npm run codestyle` is OK - [ ] `npm run fix:lint` is OK
- [ ] `npm test` is OK - [ ] `npm test` is OK

60
.github/workflows/docker-io.yml vendored Normal file
View file

@ -0,0 +1,60 @@
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

88
.github/workflows/docker.yml vendored Normal file
View file

@ -0,0 +1,88 @@
name: Docker CI
permissions:
contents: write
on:
push:
tags:
- "*"
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 22.x
uses: actions/setup-node@v6
with:
node-version: 22.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 base-image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
coderaiser/cloudcmd:latest
coderaiser/cloudcmd:${{ steps.build.outputs.version }}
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}
- name: Build and push alpine-image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile.alpine
platforms: linux/amd64,linux/arm64
push: true
tags: |
coderaiser/cloudcmd:alpine
coderaiser/cloudcmd:latest-alpine
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-alpine
ghcr.io/${{ github.repository }}:latest-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,20 +1,53 @@
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
env:
NAME: cloudcmd
strategy:
matrix:
node-version:
- 22.x
- 24.x
- 26.x
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@v5
- name: Use Node.js 12.x - uses: oven-sh/setup-bun@v2
uses: actions/setup-node@v1 with:
with: bun-version: latest
version: 12.x - name: Use Node.js ${{ matrix.node-version }}
- name: npm install, build, and test uses: actions/setup-node@v6
run: | with:
npm install node-version: ${{ matrix.node-version }}
npm run build - name: Install Redrun
npm test run: bun i redrun madrun -g --no-save
- name: Install
run: bun i --no-save
- name: Init Madrun
run: madrun --init
- name: Lint
run: redrun fix:lint
- name: Typos
uses: coderaiser/typos.ai@v1.1.8
with:
key: ${{ secrets.TYPOS_AI_KEY }}
- name: Commit fixes
uses: EndBug/add-and-commit@v10
continue-on-error: true
with:
message: "chore: ${{ env.NAME }}: actions: lint ☘️"
- name: Build
run: redrun build
- name: Test
run: redrun test
- name: Coverage
run: redrun coverage coverage:report
- name: Coveralls
uses: coverallsapp/github-action@v2
continue-on-error: true
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

23
.gitignore vendored
View file

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

View file

@ -1,178 +1,62 @@
'use strict'; import {run, cutEnv} from 'madrun';
import {defineEnv} from 'supertape/env';
const { const testEnv = defineEnv({
run, timeout: 7000,
parallel, css: true,
predefined, });
} = require('madrun');
const {version} = require('./package'); const buildEnv = {
NODE_ENV: 'production',
};
const names = [ export default {
'bin/cloudcmd.js',
'client',
'common',
'server',
'test',
'bin/release.js',
'webpack.config.js',
'cssnano.config.js',
'.webpack',
'.eslintrc.js',
'.madrun.js',
'{client,server,common}/**/*.spec.js',
];
const {putout} = predefined;
const env = 'THREAD_IT_COUNT=0';
const dockerName = 'coderaiser/cloudcmd';
module.exports = {
'start': () => 'node bin/cloudcmd.js', 'start': () => 'node bin/cloudcmd.js',
'start:dev': () => `NODE_ENV=development ${run('start')}`, 'start:dev': async () => await run('start', null, {
'build:start': () => run(['build:client', 'start']), NODE_ENV: 'development',
'build:start:dev': () => run(['build:client:dev', 'start:dev']),
'lint:all': () => run(['lint', 'lint:css', 'spell']),
'lint:base': () => putout({
names,
}), }),
'lint:css': () => 'stylelint css/*.css', 'build:start': () => run(['build:client', 'start']),
'spell': () => 'yaspeller .', 'build:start:dev': () => run([
'fix:lint': () => run(['lint', 'lint:css'], '--fix'), 'build:client:dev',
'lint': () => run('lint:progress', '--cache'), 'start:dev',
'lint:progress': () => run('lint:base', '-f progress'), ]),
'lint:stream': () => run('lint:base', '-f stream'), 'lint:all': () => run('lint:progress'),
'lint': () => 'redlint fix; putout .',
'test:base': () => { 'lint:progress': () => run('lint', '-f progress'),
const cmd = 'tape'; 'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"',
const names = `'test/**/*.js' '{client,static,common,server}/**/*.spec.js'`; 'fresh:lint': () => run('lint', '--fresh'),
'lint:fresh': () => run('lint', '--fresh'),
return `${cmd} ${names}`; 'fix:lint': async () => `putout --fix . && redlint fix`,
}, 'lint:stream': () => run('lint', '-f stream'),
'test': () => [testEnv, `tape '{test}/**/*.js' '{bin,client,static,common,server}/**/*.spec.js' -f fail`],
'test': () => `${env} ${run('test:base')}`, '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': () => run(['lint:all', 'build', 'test']), 'wisdom': async () => await run(['lint:all', 'build', 'test'], null, {
CI: 1,
}),
'wisdom:type': () => 'bin/release.js', 'wisdom:type': () => 'bin/release.js',
'docker:pull': () => 'docker pull node', 'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`],
'docker:pull:alpine': () => 'docker pull mhart/alpine-node', 'coverage:report': () => 'c8 report --reporter=lcov',
'docker:pull:arm32': () => 'docker pull arm32v7/node:slim', 'report': () => 'c8 report --reporter=lcov',
'docker:pull:arm64': () => 'docker pull arm64v8/node:slim', '6to5': () => [buildEnv, 'rspack build --config rspack.config.js'],
'docker:push': () => dockerPush('x64', version),
'docker:push:latest': () => dockerPush('x64'),
'docker:push:alpine': () => dockerPush('alpine', version),
'docker:push:alpine:latest': () => dockerPush('alpine'),
'docker:push:arm32': () => dockerPush('arm32', version),
'docker:push:arm32:latest': () => dockerPush('arm32'),
'docker:push:arm64': () => dockerPush('arm64', version),
'docker:push:arm64:latest': () => dockerPush('arm64'),
'docker:build': () => dockerBuild('docker/Dockerfile', 'x64', version),
'docker:build:alpine': () => dockerBuild('docker/Dockerfile.alpine', 'alpine', version),
'docker:build:arm32': () => dockerBuild('docker/arm/Dockerfile.arm32v7', 'arm32', version),
'docker:build:arm64': () => dockerBuild('docker/arm/Dockerfile.arm64v8', 'arm64', version),
'docker:manifest:create': () => {
const images = [
`${dockerName}:latest`,
`${dockerName}:latest-x64`,
// `${dockerName}:latest-arm32`,
// `${dockerName}:latest-arm64`,
].join(' ');
return `docker manifest create ${images}`;
},
'docker:manifest:push': () => `docker manifest push ${dockerName}:latest`,
'docker': () => run(['docker:x64', 'docker:alpine', 'docker:manifest:*']),
'docker-ci': () => run(['build', 'docker-login', 'docker']),
'docker-login': () => 'docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD',
'docker:x64': () => run([
'docker:pull',
'docker:build',
'docker:tag',
'docker:push',
'docker:push:latest',
]),
'docker:alpine': () => run([
'docker:pull:alpine',
'docker:build:alpine',
'docker:tag:alpine',
'docker:push:alpine',
'docker:push:alpine:latest',
]),
/*
'docker:arm32': () => run([
'docker:pull:arm32',
'docker:build:arm32',
'docker:tag:arm32',
'docker:push:arm32',
'docker:push:arm32:latest',
]),
'docker:arm64': () => run([
'docker:pull:arm64',
'docker:build:arm64',
'docker:tag:arm64',
'docker:push:arm64',
'docker:push:arm64:latest',
]),
*/
'docker:manifest': () => run([
'docker:manifest:create',
'docker:manifest:push',
]),
'docker:tag': () => dockerTag('x64', version),
'docker:tag:alpine': () => dockerTag('alpine', version),
'docker:tag:arm32': () => dockerTag('arm32', version),
'docker:tag:arm64': () => dockerTag('arm64', version),
'docker:rm:version': () => dockerRmi('x64', version),
'docker:rm:latest': () => dockerRmi('x64'),
'docker:rm:alpine': () => dockerRmi('alpine', version),
'docker:rm:latest-alpine': () => dockerRmi('alpine'),
'docker:rm:arm32': () => dockerRmi('arm32', version),
'docker:rm:latest-arm32': () => dockerRmi('arm32'),
'docker:rm:arm64': () => dockerRmi('arm64', version),
'docker:rm:latest-arm64': () => dockerRmi('arm64'),
'docker:rm-old': () => `${parallel('docker:rm:*')} || true`,
'coverage': () => `${env} nyc ${run('test:base')}`,
'report': () => 'nyc report --reporter=text-lcov | coveralls',
'6to5': () => 'webpack --progress',
'6to5:client': () => run('6to5', '--mode production'), '6to5:client': () => run('6to5', '--mode production'),
'6to5:client:dev': () => `NODE_ENV=development ${run('6to5', '--mode development')}`, '6to5:client:dev': async () => await run('6to5', '--mode development', {
NODE_ENV: 'development',
}),
'pre6to5:client': () => 'rimraf dist', 'pre6to5:client': () => 'rimraf dist',
'pre6to5:client:dev': () => 'rimraf dist-dev', 'pre6to5:client:dev': () => 'rimraf dist-dev',
'watch:client': () => run('6to5:client', '--watch'), 'watch:client': () => run('6to5:client', '--watch'),
'watch:client:dev': () => run('6to5:client:dev', '--watch'), 'watch:client:dev': () => run('6to5:client:dev', '--watch'),
'watch:server': () => 'nodemon bin/cloudcmd.js', 'watch:server': () => 'nodemon bin/cloudcmd.js',
'watch:lint': () => `nodemon -w client -w server -w webpack.config.js -x ${run('lint')}`, 'watch:test': async () => [testEnv, `nodemon -w client -w server -w test -w common -x ${await cutEnv('test')}`],
'watch:test': () => `nodemon -w client -w server -w test -w common -x ${run('test')}`, 'watch:test:client': async () => `nodemon -w client -w test/client -x ${await run('test:client')}`,
'watch:test:client': () => `nodemon -w client -w test/client -x ${run('test:client')}`, 'watch:test:server': async () => `nodemon -w client -w test/client -x ${await run('test:server')}`,
'watch:test:server': () => `nodemon -w client -w test/client -x ${run('test:server')}`, 'watch:coverage': async () => [testEnv, `nodemon -w server -w test -w common -x ${await cutEnv('coverage')}`],
'watch:coverage': () => `nodemon -w server -w test -w common -x ${run('coverage')}`, 'watch:fix:lint': async () => `nodemon -w client -w server -w test -w common -x '${await run('fix:lint')}'`,
'build': () => run('6to5:*'), 'build': async () => run('6to5:*'),
'build:dev': async () => run('build:client:dev'),
'build:client': () => run('6to5:client'), 'build:client': () => run('6to5:client'),
'build:client:dev': () => run('6to5:client:dev'), 'build:client:dev': () => run('6to5:client:dev'),
'heroku-postbuild': () => run('6to5:client'), 'heroku-postbuild': () => run('6to5:client'),
}; };
function dockerPush(type, version = 'latest') {
return `docker push coderaiser/cloudcmd:${version}-${type}`;
}
function dockerBuild(file, type, version) {
return `docker build -f ${file} -t coderaiser/cloudcmd:${version}-${type} .`;
}
function dockerTag(type, version) {
return `docker tag coderaiser/cloudcmd:${version}-${type} coderaiser/cloudcmd:latest-${type}`;
}
function dockerRmi(type, version = 'latest') {
return `docker rmi -f coderaiser/cloudcmd:${version}-${type}`;
}

View file

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

15
.nycrc.json Normal file
View file

@ -0,0 +1,15 @@
{
"checkCoverage": false,
"all": false,
"exclude": [
"**/*.spec.js",
"**/*.*.js",
"**/*.config.*",
"**/fixture",
"**/test/**"
],
"branches": 100,
"lines": 100,
"functions": 100,
"statements": 100
}

View file

@ -1,27 +1,60 @@
{ {
"plugins": ["cloudcmd"],
"ignore": [
"*.md",
"app.json",
"fontello.json",
"html",
"fixture*"
],
"rules": {
"package-json/add-type": "off"
},
"match": { "match": {
".filesystem.json": {
"nodejs/rename-file-cjs-to-js": "off"
},
"base64": {
"types/convert-typeof-to-is-type": "off"
},
"*.md": {
"nodejs/convert-commonjs-to-esm": "on"
},
".webpack": {
"webpack": "on"
},
"server": { "server": {
"remove-process-exit": true "nodejs/remove-process-exit": "on"
}, },
"server/(server|exit).js": { "server/{server,exit}.js": {
"remove-process-exit": false, "nodejs/remove-process-exit": "off"
"remove-console": false
}, },
"server/(terminal|distribute/log).js": { "server/{server,exit,terminal,distribute/log}.{js,mjs}": {
"remove-console": false "remove-console": "off"
}, },
"client/(client|cloudcmd|load-module).js": { "client/{client,cloudcmd,load-module}.{js,mjs}": {
"remove-console": false "remove-console": "off"
}, },
"client/modules/config/index.js": { "client": {
"apply-shorthand-properties": [{ "nodejs": "off"
"ignore": [ },
"ONE_MINUTE" "client/sw": {
] "remove-console": "off"
}]
}, },
"test/common/cloudfunc.js": { "test/common/cloudfunc.js": {
"remove-console": false "remove-console": "off"
},
"storage.js": {
"promises/remove-useless-async": "off"
},
"docker.yml": {
"github/set-node-versions": "off"
},
"vim.js": {
"merge-duplicate-functions": "off"
},
"common": {
"nodejs/declare": "off"
} }
} }
} }

38
.rspack/css.js Normal file
View file

@ -0,0 +1,38 @@
import {env} from 'node:process';
import {rspack} from '@rspack/core';
const {CssExtractRspackPlugin} = rspack;
const isDev = env.NODE_ENV === 'development';
const plugins = [
new CssExtractRspackPlugin({
filename: '[name].css',
}),
];
const rules = [{
test: /\.css$/i,
use: [CssExtractRspackPlugin.loader, {
loader: 'css-loader',
options: {
url: true,
},
}],
}, {
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
type: 'asset/inline',
}];
export default {
mode: isDev ? 'development' : 'production',
plugins,
module: {
rules,
},
optimization: {
minimize: !isDev,
minimizer: [
new rspack.LightningCssMinimizerRspackPlugin(),
],
},
};

36
.rspack/html.js Normal file
View file

@ -0,0 +1,36 @@
import {env} from 'node:process';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const isDev = env.NODE_ENV === 'development';
export const plugins = [
new HtmlWebpackPlugin({
inject: false,
template: 'html/index.html',
minify: !isDev && getMinifyHtmlOptions(),
}),
];
function getMinifyHtmlOptions() {
return {
removeComments: true,
removeCommentsFromCDATA: true,
removeCDATASectionsFromCDATA: true,
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
/* оставляем, поскольку у нас
* в элемент fm генерируеться
* таблица файлов
*/
removeEmptyElements: false,
removeOptionalTags: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
};
}

173
.rspack/js.js Normal file
View file

@ -0,0 +1,173 @@
import {resolve, sep} from 'node:path';
import {fileURLToPath} from 'node:url';
import {env} from 'node:process';
import {rspack} from '@rspack/core';
const resolveModule = (a) => fileURLToPath(import.meta.resolve(a));
const {
EnvironmentPlugin,
NormalModuleReplacementPlugin,
ProvidePlugin,
} = rspack;
const modules = './modules';
const dirModules = './client/modules';
const dirCss = './css';
const dirThemes = `${dirCss}/themes`;
const dirColumns = `${dirCss}/columns`;
const dir = './client';
const {NODE_ENV} = env;
const isDev = NODE_ENV === 'development';
const rootDir = new URL('..', import.meta.url).pathname;
const dist = resolve(rootDir, 'dist');
const distDev = resolve(rootDir, 'dist-dev');
const devtool = isDev ? 'eval' : 'source-map';
const noParse = (a) => a.endsWith('.spec.js');
// codegen.macro is a babel-macro (build-time codegen), not supported by
// Rspack's native SWC transform, so client/sw/sw.js (the only file that
// uses it) keeps going through babel-loader. Everything else uses
// Rspack's built-in SWC loader, which is the main source of the speedup.
const rules = [{
test: /sw\/sw\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
}, {
test: /\.[mc]?js$/,
exclude: [/node_modules/, /sw\/sw\.js$/],
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'ecmascript',
},
},
env: {
targets: 'defaults',
},
},
}];
const plugins = [
new NormalModuleReplacementPlugin(/^node:/, (resource) => {
resource.request = resource.request.replace(/^node:/, '');
}),
new NormalModuleReplacementPlugin(/^putout$/, '@putout/bundle'),
new EnvironmentPlugin({
NODE_ENV,
}),
new ProvidePlugin({
process: 'process/browser',
}),
];
const splitChunks = {
chunks: 'all',
cacheGroups: {
abcCommon: {
name: 'cloudcmd.common',
chunks: (chunk) => {
const lazyChunks = [
'sw',
'nojs',
'view',
'edit',
'terminal',
'config',
'user-menu',
'help',
'themes/dark',
'themes/light',
'columns/name-size',
'columns/name-size-date',
'columns/name-size-time',
'columns/name-size-date-time',
];
return !lazyChunks.includes(chunk.name);
},
minChunks: 1,
enforce: true,
priority: -1,
reuseExistingChunk: true,
},
},
};
export default {
resolve: {
symlinks: false,
alias: {
'node:process': 'process',
'node:path': 'path',
},
fallback: {
path: resolveModule('path-browserify'),
process: resolveModule('process/browser'),
util: resolveModule('util'),
},
},
devtool,
optimization: {
splitChunks,
},
entry: {
'themes/dark': `${dirThemes}/dark.css`,
'themes/light': `${dirThemes}/light.css`,
'columns/name-size': `${dirColumns}/name-size.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`,
'help': `${dirCss}/help.css`,
'view': `${dirCss}/view.css`,
'config': `${dirCss}/config.css`,
'terminal': `${dirCss}/terminal.css`,
'user-menu': `${dirCss}/user-menu.css`,
'sw': `${dir}/sw/sw.js`,
'cloudcmd': `${dir}/cloudcmd.js`,
[`${modules}/edit`]: `${dirModules}/edit.js`,
[`${modules}/edit-file`]: `${dirModules}/edit-file.js`,
[`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`,
[`${modules}/edit-names`]: `${dirModules}/edit-names.js`,
[`${modules}/edit-names-vim`]: `${dirModules}/edit-names-vim.js`,
[`${modules}/menu`]: `${dirModules}/menu/index.js`,
[`${modules}/view`]: `${dirModules}/view/index.js`,
[`${modules}/help`]: `${dirModules}/help.js`,
[`${modules}/markdown`]: `${dirModules}/markdown.js`,
[`${modules}/config`]: `${dirModules}/config/index.js`,
[`${modules}/contact`]: `${dirModules}/contact.js`,
[`${modules}/upload`]: `${dirModules}/upload.js`,
[`${modules}/operation`]: `${dirModules}/operation/index.js`,
[`${modules}/konsole`]: `${dirModules}/konsole.js`,
[`${modules}/terminal`]: `${dirModules}/terminal.js`,
[`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`,
[`${modules}/cloud`]: `${dirModules}/cloud.js`,
[`${modules}/user-menu`]: `${dirModules}/user-menu/index.js`,
[`${modules}/polyfill`]: `${dirModules}/polyfill.js`,
[`${modules}/command-line`]: `${dirModules}/command-line.js`,
},
output: {
filename: '[name].js',
path: isDev ? distDev : dist,
pathinfo: isDev,
devtoolModuleFilenameTemplate,
publicPath: '/dist/',
},
module: {
rules,
noParse,
},
plugins,
performance: {
maxEntrypointSize: 800_000,
maxAssetSize: 600_000,
},
};
function devtoolModuleFilenameTemplate(info) {
const resource = info.absoluteResourcePath.replace(rootDir + sep, '');
return `file://cloudcmd/${resource}`;
}

View file

@ -1,17 +0,0 @@
extends: stylelint-config-standard
rules:
indentation: 4
declaration-block-trailing-semicolon: always
declaration-colon-space-before: null
selector-list-comma-newline-after: null
comment-empty-line-before: null
number-leading-zero: null
number-no-trailing-zeros: null
string-quotes: single
function-url-quotes: never
no-eol-whitespace: null
font-family-name-quotes: always-unless-keyword
font-family-no-missing-generic-family-keyword: null
rule-empty-line-before: null
max-empty-lines: 2

View file

@ -1,52 +0,0 @@
language: node_js
node_js:
- 14
- 12
- 10
cache:
npm: false
matrix:
allow_failures:
- os: windows
os:
- linux
- osx
- windows
# https://docs.travis-ci.com/user/customizing-the-build/#git-end-of-line-conversion-control
# need for windows
git:
autocrlf: input
script:
- npm run lint && npm run build && npm run coverage && npm run report
notifications:
email:
on_success: never
on_failure: change
sudo: required
services:
- docker
before_deploy:
- echo '{"experimental":"enabled"}' | sudo tee /etc/docker/daemon.json
- mkdir -p $HOME/.docker
- echo '{"experimental":"enabled"}' | tee $HOME/.docker/config.json
- sudo service docker start
deploy:
provider: script
script: npm run docker-ci
skip_cleanup: true
on:
node: 12
condition: $TRAVIS_OS_NAME = linux
tags: true
all_branches: false

2
.typos.toml Normal file
View file

@ -0,0 +1,2 @@
[files]
extend-exclude = ["ChangeLog"]

View file

@ -1,81 +0,0 @@
'use strict';
const fs = require('fs');
const {
basename,
extname,
join,
} = require('path');
const {env} = process;
const isDev = env.NODE_ENV === 'development';
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const extractCSS = (a) => new ExtractTextPlugin(`${a}.css`);
const extractMain = extractCSS('[name]');
const cssNames = [
'nojs',
'view',
'config',
'terminal',
'user-menu',
...getCSSList('columns'),
];
const cssPlugins = cssNames.map(extractCSS);
const clean = (a) => a.filter(Boolean);
const plugins = clean([
...cssPlugins,
extractMain,
!isDev && new OptimizeCssAssetsPlugin(),
]);
const rules = [{
test: /\.css$/,
exclude: /css\/(nojs|view|config|terminal|user-menu|columns.*)\.css/,
use: extractMain.extract([
'css-loader',
]),
},
...cssPlugins.map(extract), {
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
},
},
}];
module.exports = {
plugins,
module: {
rules,
},
};
function getCSSList(dir) {
const base = (a) => basename(a, extname(a));
const addDir = (name) => `${dir}/${name}`;
const rootDir = join(__dirname, '..');
return fs.readdirSync(`${rootDir}/css/${dir}`)
.map(base)
.map(addDir);
}
function extract(extractPlugin) {
const {filename} = extractPlugin;
return {
test: RegExp(`css/${filename}`),
use: extractPlugin.extract([
'css-loader',
]),
};
}

View file

@ -1,43 +0,0 @@
'use strict';
const {env} = process;
const isDev = env.NODE_ENV === 'development';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const plugins = [
new HtmlWebpackPlugin({
inject: false,
template: 'html/index.html',
minify: !isDev && getMinifyHtmlOptions(),
}),
];
module.exports = {
plugins,
};
function getMinifyHtmlOptions() {
return {
removeComments: true,
removeCommentsFromCDATA: true,
removeCDATASectionsFromCDATA: true,
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
/* оставляем, поскольку у нас
* в элемент fm генерируеться
* таблица файлов
*/
removeEmptyElements: false,
removeOptionalTags: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
};
}

View file

@ -1,134 +0,0 @@
'use strict';
const {
resolve,
sep,
join,
} = require('path');
const {EnvironmentPlugin} = require('webpack');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
const dir = './client';
const dirModules = './client/modules';
const modules = './modules';
const {env} = process;
const {NODE_ENV} = env;
const isDev = NODE_ENV === 'development';
const rootDir = join(__dirname, '..');
const dist = resolve(rootDir, 'dist');
const distDev = resolve(rootDir, 'dist-dev');
const devtool = isDev ? 'eval' : 'source-map';
const notEmpty = (a) => a;
const clean = (array) => array.filter(notEmpty);
const noParse = (a) => /\.spec\.js$/.test(a);
const options = {
babelrc: false,
plugins: [
'module:babel-plugin-macros',
],
};
const rules = clean([
!isDev && {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
isDev && {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options,
}]);
const plugins = [
new EnvironmentPlugin({
NODE_ENV,
}),
new ServiceWorkerWebpackPlugin({
entry: join(__dirname, '..', 'client', 'sw', 'sw.js'),
excludes: ['*'],
}),
];
const splitChunks = {
name: 'cloudcmd.common',
chunks: 'all',
};
module.exports = {
resolve: {
symlinks: false,
},
devtool,
optimization: {
splitChunks,
},
entry: {
cloudcmd: `${dir}/cloudcmd.js`,
[modules + '/edit']: `${dirModules}/edit.js`,
[modules + '/edit-file']: `${dirModules}/edit-file.js`,
[modules + '/edit-file-vim']: `${dirModules}/edit-file-vim.js`,
[modules + '/edit-names']: `${dirModules}/edit-names.js`,
[modules + '/edit-names-vim']: `${dirModules}/edit-names-vim.js`,
[modules + '/menu']: `${dirModules}/menu.js`,
[modules + '/view']: `${dirModules}/view.js`,
[modules + '/help']: `${dirModules}/help.js`,
[modules + '/markdown']: `${dirModules}/markdown.js`,
[modules + '/config']: `${dirModules}/config/index.js`,
[modules + '/contact']: `${dirModules}/contact.js`,
[modules + '/upload']: `${dirModules}/upload.js`,
[modules + '/operation']: `${dirModules}/operation/index.js`,
[modules + '/konsole']: `${dirModules}/konsole.js`,
[modules + '/terminal']: `${dirModules}/terminal.js`,
[modules + '/terminal-run']: `${dirModules}/terminal-run.js`,
[modules + '/cloud']: `${dirModules}/cloud.js`,
[modules + '/user-menu']: `${dirModules}/user-menu/index.js`,
[modules + '/polyfill']: `${dirModules}/polyfill.js`,
},
output: {
filename: '[name].js',
path: isDev ? distDev : dist,
pathinfo: isDev,
devtoolModuleFilenameTemplate,
publicPath: '/dist/',
},
externals: [
externals,
],
module: {
rules,
noParse,
},
plugins,
performance: {
maxEntrypointSize: 600000,
maxAssetSize: 600000,
},
};
function externals(context, request, fn) {
if (!isDev)
return fn();
const list = [];
if (list.includes(request))
return fn(null, request);
fn();
}
function devtoolModuleFilenameTemplate(info) {
const resource = info.absoluteResourcePath.replace(rootDir + sep, '');
return `file://cloudcmd/${resource}`;
}

View file

@ -22,12 +22,14 @@
"Iptables", "Iptables",
"JitSu", "JitSu",
"Node", "Node",
"IO",
"Olena", "Olena",
"TarZak", "TarZak",
"Termux", "Termux",
"Zalitok", "Zalitok",
"WebSocket", "WebSocket",
"auth", "auth",
"autostart",
"binded", "binded",
"cd", "cd",
"cloudcmd", "cloudcmd",
@ -44,6 +46,7 @@
"gz", "gz",
"io", "io",
"js", "js",
"linux",
"maintainers", "maintainers",
"markdown", "markdown",
"microservice", "microservice",
@ -53,6 +56,8 @@
"nginx", "nginx",
"npm", "npm",
"or io", "or io",
"patreon",
"rc",
"refactor", "refactor",
"sexualized", "sexualized",
"sslPort", "sslPort",
@ -61,6 +66,7 @@
"v0", "v0",
"v1", "v1",
"v2", "v2",
"yml" "yml",
"systemd"
] ]
} }

View file

@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
Examples of behavior that contributes to creating a positive environment include: Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language - Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences - Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism - Gracefully accepting constructive criticism
* Focusing on what is best for the community - Focusing on what is best for the community
* Showing empathy towards other community members - Showing empathy towards other community members
Examples of unacceptable behavior by participants include: Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances - The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks - Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment - Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission - Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting - Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities ## Our Responsibilities

View file

@ -1,27 +1,30 @@
Commit ## Commit
---------------
Format of the commit message: **type(scope) subject** Format of the commit message: **type(scope) subject**
**Type**: **Type**:
- feature(scope) subject
- fix(scope) subject - feature: scope: subject
- docs(scope) subject - fix: scope: subject
- refactor(scope) subject - docs: scope: subject
- test(scope) subject - refactor: scope: subject
- chore(scope) subject - test: scope: subject
- chore: scope: subject
**Scope**: **Scope**:
Scope could be anything specifying place of the commit change. Scope could be anything specifying place of the commit change.
For example util, console, view, edit, style etc... For example util, console, view, edit, style etc...
**Subject text**: **Subject text**:
- use imperative, present tense: “change” not “changed” nor “changes” - use imperative, present tense: “change” not “changed” nor “changes”
- don't capitalize first letter - don't capitalize first letter
- no dot (.) at the end - no dot (.) at the end
**Message body**: **Message body**:
- just as in <subject> use imperative, present tense: “change” not “changed” nor “changes” - just as in <subject> use imperative, present tense: “change” not “changed” nor “changes”
- includes motivation for the change and contrasts with previous behavior - includes motivation for the change and contrasts with previous behavior
**Examples**: **Examples**:
- [fix(style) .name{width}: 37% -> 35%](https://github.com/coderaiser/cloudcmd/commit/94b0642e3990c17b3a0ee3efeb75f343e1e7c050)
- [fix(console) dispatch: focus -> mouseup](https://github.com/coderaiser/cloudcmd/commit/f41ec5058d1411e86a881f8e8077e0572e0409ec) - [fix: style: .name{width}: 37% -> 35%](https://github.com/coderaiser/cloudcmd/commit/94b0642e3990c17b3a0ee3efeb75f343e1e7c050)
- [fix: console: dispatch: focus -> mouseup](https://github.com/coderaiser/cloudcmd/commit/f41ec5058d1411e86a881f8e8077e0572e0409ec)

1953
ChangeLog

File diff suppressed because it is too large Load diff

1010
HELP.md

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
(The MIT License) (The MIT License)
Copyright (c) 2012-2019 Coderaiser <mnemonic.enemy@gmail.com> Copyright (c) 2012-2025 Coderaiser <mnemonic.enemy@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View file

@ -1,25 +1,22 @@
# Cloud Commander v14.6.0 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fcoderaiser%2Fcloudcmd.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fcoderaiser%2Fcloudcmd?ref=badge_shield) # Cloud Commander v19.19.1 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [![Deploy on InstaPods][DeployInstaPodsIMG]][DeployInstaPodsURL]
### [Main][MainURL] [Blog][BlogURL] Live([Heroku][HerokuURL]) ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png [MainURL]: https://cloudcmd.io "Main"
[MainURL]: http://cloudcmd.io "Main" [BlogURL]: https://blog.cloudcmd.io "Blog"
[BlogURL]: http://blog.cloudcmd.io "Blog" [SupportURL]: https://patreon.com/coderaiser "Patreon"
[HerokuURL]: https://cloudcmd.herokuapp.com/ "Heroku" [DemoURL]: https://cloudcmd-zdp6.onrender.com
[BuildStatusURL]: https://travis-ci.org/coderaiser/cloudcmd "Build Status" [NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
[BuildStatusIMGURL]: https://img.shields.io/travis/coderaiser/cloudcmd.svg?style=flat-squere&longCache=true [BuildStatusURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml "Build Status"
[BuildStatusIMGURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml/badge.svg
[BuildAppveyorURL]: https://ci.appveyor.com/project/coderaiser/cloudcmd [CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd
[BuildAppveyorIMGURL]: https://ci.appveyor.com/api/projects/status/tse6sc8dxrqxqehi?svg=true [CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36
[GitterURL]: https://gitter.im/cloudcmd/hello
[CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd [GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
[CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36 [DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
[DeployIMG]: https://www.herokucdn.com/deploy/button.png
[GitterURL]: https://gitter.im/cloudcmd/hello [DeployInstaPodsIMG]: https://img.shields.io/badge/deploy%20on-InstaPods-blue
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg [DeployInstaPodsURL]: https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
[DeployIMG]: https://www.herokucdn.com/deploy/button.png
**Cloud Commander** a file manager for the web with console and editor. **Cloud Commander** a file manager for the web with console and editor.
@ -30,6 +27,7 @@
``` ```
npm i cloudcmd -g npm i cloudcmd -g
``` ```
## Start ## Start
For starting just type in console: For starting just type in console:
@ -47,8 +45,8 @@ Open url `http://localhost:8000` in browser.
You will see something similar to this. You will see something similar to this.
![View](https://cloudcmd.io/img/screen/view.png "View") ![View](https://cloudcmd.io/img/screen/view.png "View")
## Deploy ## Deploy
`Cloud Commander` could be easily deployed to [Heroku][DeployURL]. `Cloud Commander` could be easily deployed to [Heroku][DeployURL].
[![Deploy][DeployIMG]][DeployURL] [![Deploy][DeployIMG]][DeployURL]
@ -72,17 +70,19 @@ npm i cloudcmd express socket.io -S
And create `index.js`: And create `index.js`:
```js ```js
const http = require('http'); import http from 'node:http';
const cloudcmd = require('cloudcmd'); import {cloudcmd} from 'cloudcmd';
const io = require('socket.io'); import {Server} from 'socket.io';
const app = require('express')(); import express from 'express';
const app = express();
const port = 1337; const port = 1337;
const prefix = '/'; const prefix = '/';
const server = http.createServer(app); const server = http.createServer(app);
const socket = io.listen(server, { const socket = new Server(server, {
path: `${prefix}/socket.io` path: `${prefix}socket.io`,
}); });
const config = { const config = {
@ -93,8 +93,8 @@ const filePicker = {
data: { data: {
FilePicker: { FilePicker: {
key: 'key', key: 'key',
} },
} },
}; };
// override option from json/modules.json // override option from json/modules.json
@ -109,11 +109,11 @@ const {
const configManager = createConfigManager({ const configManager = createConfigManager({
configPath, configPath,
}), });
app.use(prefix, cloudcmd({ app.use(prefix, cloudcmd({
socket, // used by Config, Edit (optional) and Console (required) socket, // used by Config, Edit (optional) and Console (required)
config, // config data (optional) config, // config data (optional)
modules, // optional modules, // optional
configManager, // optional configManager, // optional
})); }));
@ -121,12 +121,21 @@ app.use(prefix, cloudcmd({
server.listen(port); server.listen(port);
``` ```
Docker ## Docker
---------------
The docker images are provided for multiple architectures and types. The following list shows all existing images:
| Architecture | Type |
|----------------|--------------|
| amd64 | linux |
| arm64 (arm/v8) | linux |
| amd64 | linux-alpine |
| arm64 (arm/v8) | linux-alpine |
`Cloud Commander` could be used as a [docker container](https://hub.docker.com/r/coderaiser/cloudcmd/ "Docker container") this way: `Cloud Commander` could be used as a [docker container](https://hub.docker.com/r/coderaiser/cloudcmd/ "Docker container") this way:
```sh ```sh
docker run -t --rm -v ~:/root -v /:/mnt/fs -w=/root -p 8000:8000 coderaiser/cloudcmd docker run -it --rm -v ~:/root -v /:/mnt/fs -w=/root -p 8000:8000 coderaiser/cloudcmd
``` ```
Config would be read from home directory, hosts root file system would be mount to `/mnt/fs`, Config would be read from home directory, hosts root file system would be mount to `/mnt/fs`,
@ -152,11 +161,15 @@ When you create this file run:
docker-compose up docker-compose up
``` ```
Get involved ## Documentation
---------------
More documentation you can find on https://cloudcmd.io/.
## Get involved
There is a lot ways to be involved in `Cloud Commander` development: There is a lot ways to be involved in `Cloud Commander` development:
- support project on patreon: https://patreon.com/coderaiser;
- if you find a bug or got idea to share [create an issue](https://github.com/coderaiser/cloudcmd/issues/new "Create issue"); - if you find a bug or got idea to share [create an issue](https://github.com/coderaiser/cloudcmd/issues/new "Create issue");
- if you fixed a bug, typo or implemented new feature [create pull request](https://github.com/coderaiser/cloudcmd/compare "Create pull request"); - if you fixed a bug, typo or implemented new feature [create pull request](https://github.com/coderaiser/cloudcmd/compare "Create pull request");
- if you know languages you can help with [site translations](https://github.com/coderaiser/cloudcmd/wiki "Cloud Commander community wiki"); - if you know languages you can help with [site translations](https://github.com/coderaiser/cloudcmd/wiki "Cloud Commander community wiki");

View file

@ -11,6 +11,7 @@
"folder", "folder",
"orthodox" "orthodox"
], ],
"stack": "heroku-18",
"env": { "env": {
"NPM_CONFIG_PRODUCTION": { "NPM_CONFIG_PRODUCTION": {
"description": "Keep false to install devDependencies and build frontend", "description": "Keep false to install devDependencies and build frontend",
@ -62,11 +63,6 @@
"value": "", "value": "",
"required": false "required": false
}, },
"CLOUDCMD_CONFIG_DIALOG": {
"description": "show config dialog",
"value": "false",
"required": false
},
"CLOUDCMD_ONE_FILE_PANEL": { "CLOUDCMD_ONE_FILE_PANEL": {
"description": "show one file panel", "description": "show one file panel",
"value": "false", "value": "false",

View file

@ -1,37 +1,48 @@
#!/usr/bin/env node #!/usr/bin/env node
'use strict'; import process from 'node:process';
import {promisify} from 'node:util';
import {tryToCatch} from 'try-to-catch';
import parse from 'yargs-parser';
import exit from '../server/exit.js';
import {createConfig, configPath} from '../server/config.js';
import * as env from '../server/env.js';
import prefixer from '../server/prefixer.js';
import * as validate from '../server/validate.js';
import Info from '../package.json' with {
type: 'json',
};
const Info = require('../package'); process.on('unhandledRejection', exit);
const DIR_SERVER = '../server/';
const {promisify} = require('util'); const isUndefined = (a) => typeof a === 'undefined';
const tryToCatch = require('try-to-catch');
const exit = require(DIR_SERVER + 'exit');
const {
createConfig,
configPath,
} = require(DIR_SERVER + 'config');
const config = createConfig({
configPath,
});
const env = require(DIR_SERVER + 'env');
const prefixer = require(DIR_SERVER + '/prefixer');
const choose = (a, b) => { const choose = (a, b) => {
if (a === undefined) if (isUndefined(a))
return b; return b;
return a; return a;
}; };
process.on('unhandledRejection', exit); const config = createConfig({
configPath,
});
const {argv} = process; const maybeRoot = (a) => {
const args = require('minimist')(argv.slice(2), { if (a === '.')
return process.cwd();
return a;
};
const yargsOptions = {
configuration: {
'strip-aliased': true,
'strip-dashed': true,
},
coerce: {
root: maybeRoot,
},
string: [ string: [
'name', 'name',
'port', 'port',
@ -46,6 +57,8 @@ const args = require('minimist')(argv.slice(2), {
'terminal-path', 'terminal-path',
'terminal-command', 'terminal-command',
'columns', 'columns',
'menu',
'theme',
'import-url', 'import-url',
'import-token', 'import-token',
'export-token', 'export-token',
@ -60,6 +73,7 @@ const args = require('minimist')(argv.slice(2), {
'open', 'open',
'config-dialog', 'config-dialog',
'config-auth', 'config-auth',
'config-port',
'console', 'console',
'sync-console-path', 'sync-console-path',
'contact', 'contact',
@ -69,6 +83,7 @@ const args = require('minimist')(argv.slice(2), {
'confirm-copy', 'confirm-copy',
'confirm-move', 'confirm-move',
'show-config', 'show-config',
'show-dot-files',
'show-file-name', 'show-file-name',
'vim', 'vim',
'keys-panel', 'keys-panel',
@ -77,65 +92,70 @@ const args = require('minimist')(argv.slice(2), {
'import', 'import',
'import-listen', 'import-listen',
'log', 'log',
'zip',
'dropbox', 'dropbox',
], ],
default: { default: {
'server' : true, 'server': true,
'name' : choose(env('name'), config('name')), 'name': choose(env.parse('name'), config('name')),
'auth' : choose(env.bool('auth'), config('auth')), 'auth': choose(env.bool('auth'), config('auth')),
'port' : config('port'), 'port': config('port'),
'online' : config('online'), 'online': config('online'),
'open' : choose(env.bool('open'), config('open')), 'open': choose(env.bool('open'), config('open')),
'editor' : env('editor') || config('editor'), 'editor': env.parse('editor') || config('editor'),
'packer' : config('packer') || 'tar', 'menu': env.parse('menu') || config('menu'),
'zip' : config('zip'), 'packer': config('packer') || 'tar',
'username' : env('username') || config('username'), 'zip': config('zip'),
'root' : choose(env('root'), config('root')), 'username': env.parse('username') || config('username'),
'prefix' : choose(env('cloudcmd_prefix'), config('prefix')), 'root': choose(env.parse('root'), config('root')),
'console' : choose(env.bool('console'), config('console')), 'prefix': choose(env.parse('prefix'), config('prefix')),
'contact' : choose(env.bool('contact'), config('contact')), 'console': choose(env.bool('console'), config('console')),
'terminal' : choose(env.bool('terminal'), config('terminal')), 'contact': choose(env.bool('contact'), config('contact')),
'columns' : env('columns') || config('columns') || '', 'terminal': choose(env.bool('terminal'), config('terminal')),
'vim' : choose(env.bool('vim'), config('vim')), 'columns': env.parse('columns') || config('columns') || '',
'log' : config('log'), 'theme': env.parse('theme') || config('theme') || '',
'vim': choose(env.bool('vim'), config('vim')),
'log': choose(env.bool('log'), config('log')),
'import-url': env('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')),
'import' : choose(env.bool('import'), config('import')), 'import': choose(env.bool('import'), config('import')),
'export' : choose(env.bool('export'), config('export')), 'export': choose(env.bool('export'), config('export')),
'prefix-socket': config('prefixSocket'), 'prefix-socket': config('prefixSocket'),
'show-dot-files': choose(env.bool('show_dot_files'), config('showDotFiles')),
'show-file-name': choose(env.bool('show_file_name'), config('showFileName')), 'show-file-name': choose(env.bool('show_file_name'), config('showFileName')),
'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')),
'terminal-path': env('terminal_path') || config('terminalPath'), 'config-port': choose(env.bool('config_port'), config('configPort')),
'terminal-command': env('terminal_command') || config('terminalCommand'), 'terminal-path': env.parse('terminal_path') || config('terminalPath'),
'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')),
'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')), 'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')),
'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')), 'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')),
'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')), 'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')),
'keys-panel': env.bool('keys_panel') || config('keysPanel'), 'keys-panel': env.bool('keys_panel') || config('keysPanel'),
'import-token': env('import_token') || config('importToken'), 'import-token': env.parse('import_token') || config('importToken'),
'export-token': env('export_token') || config('exportToken'), 'export-token': env.parse('export_token') || config('exportToken'),
'dropbox': config('dropbox'), 'dropbox': config('dropbox'),
'dropbox-token': config('dropboxToken'), 'dropbox-token': config('dropboxToken') || '',
}, },
alias: { alias: {
v: 'version', version: 'v',
h: 'help', help: 'h',
p: 'password', password: 'p',
o: 'online', online: 'o',
u: 'username', username: 'u',
s: 'save', save: 's',
a: 'auth', auth: 'a',
c: 'config', config: 'c',
}, },
unknown: (cmd) => { };
exit('\'%s\' is not a cloudcmd option. See \'cloudcmd --help\'.', cmd);
}, const {argv} = process;
}); const args = parse(argv.slice(2), yargsOptions);
if (args.version) if (args.version)
version(); version();
@ -145,8 +165,21 @@ else
main(); main();
async function main() { async function main() {
const {validateArgs} = await import('@putout/cli-validate-args');
const error = await validateArgs(args, [
...yargsOptions.boolean,
...yargsOptions.string,
]);
if (error)
return exit(error);
if (args.repl) if (args.repl)
repl(); await repl();
validate.columns(args.columns);
validate.theme(args.theme);
port(args.port); port(args.port);
@ -156,37 +189,41 @@ async function main() {
config('open', args.open); config('open', args.open);
config('username', args.username); config('username', args.username);
config('console', args.console); config('console', args.console);
config('syncConsolePath', args['sync-console-path']); config('syncConsolePath', args.syncConsolePath);
config('showFileName', args['show-file-name']); config('showDotFiles', args.showDotFiles);
config('showFileName', args.showFileName);
config('contact', args.contact); config('contact', args.contact);
config('terminal', args.terminal); config('terminal', args.terminal);
config('terminalPath', args['terminal-path']); config('terminalPath', args.terminalPath);
config('terminalCommand', args['terminal-command']); config('terminalCommand', args.terminalCommand);
config('terminalAutoRestart', args['terminal-auto-restart']); config('terminalAutoRestart', args.terminalAutoRestart);
config('editor', args.editor); config('editor', args.editor);
config('menu', args.menu);
config('prefix', prefixer(args.prefix)); config('prefix', prefixer(args.prefix));
config('prefixSocket', prefixer(args['prefix-socket'])); config('prefixSocket', prefixer(args.prefixSocket));
config('root', args.root || '/'); config('root', args.root || '/');
config('vim', args.vim); config('vim', args.vim);
config('theme', args.theme);
config('columns', args.columns); config('columns', args.columns);
config('log', args.log); config('log', args.log);
config('confirmCopy', args['confirm-copy']); config('confirmCopy', args.confirmCopy);
config('confirmMove', args['confirm-move']); config('confirmMove', args.confirmMove);
config('oneFilePanel', args['one-file-panel']); config('oneFilePanel', args.oneFilePanel);
config('configDialog', args['config-dialog']); config('configDialog', args.configDialog);
config('configAuth', args['config-auth']); config('configAuth', args.configAuth);
config('keysPanel', args['keys-panel']); config('configPort', args.configPort);
config('keysPanel', args.keysPanel);
config('export', args.export); config('export', args.export);
config('exportToken', args['export-token']); config('exportToken', args.exportToken);
config('import', args.import); config('import', args.import);
config('importToken', args['import-token']); config('importToken', args.importToken);
config('importListen', args['import-listen']); config('importListen', args.importListen);
config('importUrl', args['import-url']); config('importUrl', args.importUrl);
config('dropbox', args['dropbox']); config('dropbox', args.dropbox);
config('dropboxToken', args['dropbox-token'] || ''); config('dropboxToken', args.dropboxToken);
readConfig(args.config); await readConfig(args.config);
const options = { const options = {
root: config('root'), root: config('root'),
@ -195,42 +232,44 @@ async function main() {
prefix: config('prefix'), prefix: config('prefix'),
prefixSocket: config('prefixSocket'), prefixSocket: config('prefixSocket'),
columns: config('columns'), columns: config('columns'),
theme: config('theme'),
menu: config('menu'),
}; };
const password = env('password') || args.password; const password = env.parse('password') || args.password;
if (password) if (password)
config('password', getPassword(password)); config('password', await getPassword(password));
validateRoot(options.root, config); validateRoot(options.root, config);
if (args['show-config']) if (args.showConfig)
showConfig(); await showConfig();
const distribute = require('../server/distribute'); const {distributeImport} = await import('../server/distribute/import.js');
const importConfig = promisify(distribute.import); const importConfig = promisify(distributeImport);
await importConfig(config); await start(options, config);
if (args.save) if (args.save)
config.write(); config.write();
start(options, config);
await tryToCatch(checkUpdate); await tryToCatch(checkUpdate);
await importConfig(config);
} }
function validateRoot(root, config) { function validateRoot(root, config) {
const validate = require(DIR_SERVER + 'validate');
validate.root(root, config); validate.root(root, config);
if (root === '/') if (root === '/')
return; return;
console.log(`root: ${root}`); if (config('log'))
console.log(`root: ${root}`);
} }
function getPassword(password) { async function getPassword(password) {
const criton = require('criton'); const {default: criton} = await import('criton');
return criton(password, config('algo')); return criton(password, config('algo'));
} }
@ -238,13 +277,11 @@ function version() {
console.log('v' + Info.version); console.log('v' + Info.version);
} }
function start(options, config) { async function start(options, config) {
const SERVER = DIR_SERVER + 'server';
if (!args.server) if (!args.server)
return; return;
const server = require(SERVER); const {default: server} = await import('../server/server.js');
server(options, config); server(options, config);
} }
@ -257,38 +294,38 @@ function port(arg) {
exit('cloudcmd --port: should be a number'); exit('cloudcmd --port: should be a number');
} }
function showConfig() { async function showConfig() {
const show = require('../server/show-config'); const {showConfig} = await import('../server/show-config.js');
const data = show(config('*')); const data = showConfig(config('*'));
console.log(data); console.log(data);
} }
function readConfig(name) { async function readConfig(name) {
if (!name) if (!name)
return; return;
const fs = require('fs'); const {default: forEachKey} = await import('for-each-key');
const tryCatch = require('try-catch');
const jju = require('jju');
const forEachKey = require('for-each-key');
const readjsonSync = (name) => jju.parse(fs.readFileSync(name, 'utf8'), { const data = await import(name, {
mode: 'json', with: {
type: 'json',
},
}); });
const [error, data] = tryCatch(readjsonSync, name);
if (error)
return exit(error.message);
forEachKey(config, data); forEachKey(config, data);
} }
function help() { async function help() {
const bin = require('../json/help'); const {default: bin} = await import('../json/help.json', {
const forEachKey = require('for-each-key'); with: {
const currify = require('currify'); type: 'json',
},
});
const {default: forEachKey} = await import('for-each-key');
const {default: currify} = await import('currify');
const usage = 'Usage: cloudcmd [options]'; const usage = 'Usage: cloudcmd [options]';
const url = Info.homepage; const url = Info.homepage;
const log = currify((a, b, c) => console.log(a, b, c)); const log = currify((a, b, c) => console.log(a, b, c));
@ -299,28 +336,27 @@ function help() {
console.log('\nGeneral help using Cloud Commander: <%s>', url); console.log('\nGeneral help using Cloud Commander: <%s>', url);
} }
function repl() { async function repl() {
console.log('REPL mode enabled (telnet localhost 1337)'); console.log('REPL mode enabled (telnet localhost 1337)');
require(DIR_SERVER + 'repl'); await import('../server/repl.js');
} }
async function checkUpdate() { async function checkUpdate() {
const load = require('package-json'); const {default: load} = await import('package-json');
const {version} = await load(Info.name, 'latest'); const {version} = await load(Info.name, 'latest');
showUpdateInfo(version);
await showUpdateInfo(version);
} }
function showUpdateInfo(version) { async function showUpdateInfo(version) {
if (version === Info.version) if (version === Info.version)
return; return;
const chalk = require('chalk'); const {default: chalk} = await import('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}`;
const current = chalk.dim(`(current: v${Info.version})`); const current = chalk.dim(`(current: v${Info.version})`);
console.log('%s %s', latest, current); console.log('%s %s', latest, current);
} }

26
bin/cloudcmd.spec.js Normal file
View file

@ -0,0 +1,26 @@
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

@ -1,67 +1,72 @@
#!/usr/bin/env node #!/usr/bin/env node
'use strict'; import {promisify} from 'node:util';
import process from 'node:process';
import {tryToCatch} from 'try-to-catch';
import minor from 'minor';
import _place from 'place';
import {rendy} from 'rendy';
import shortdate from 'shortdate';
import Info from '../package.json' with {
type: 'json',
};
const DIR = '../'; const place = promisify(_place);
const Info = require(DIR + 'package');
const minor = require('minor'); await main();
const place = require('place');
const rendy = require('rendy');
const shortdate = require('shortdate');
const ERROR = Error('ERROR: version is missing. release --patch|--minor|--major'); async function main() {
const history = '## Version history\n\n';
main((error) => {
if (error)
console.error(error.message);
});
function main(callback) {
const history = 'Version history\n---------------\n';
const link = '//github.com/coderaiser/cloudcmd/releases/tag/'; const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
const template = '- *{{ date }}*, ' +
'**[v{{ version }}]' + const template = '- ' +
'(' + link + 'v{{ version }})**\n'; '*{{ date }}*, ' +
'**[v{{ version }}]' +
'(' +
link +
'v{{ version }})**\n';
const {version} = Info; const {version} = Info;
cl((error, versionNew) => { const [error, versionNew] = await tryToCatch(cl);
if (error)
return callback(error); if (error)
return console.error(error);
replaceVersion('README.md', version, versionNew, callback);
replaceVersion('HELP.md', version, versionNew, () => { await replaceVersion('README.md', version, versionNew);
const historyNew = history + rendy(template, { await replaceVersion('HELP.md', version, versionNew);
date : shortdate(),
version : versionNew, const historyNew = history +
}); rendy(template, {
date: shortdate(),
replaceVersion('HELP.md', history, historyNew, callback); version: versionNew,
}); });
});
await replaceVersion('HELP.md', history, historyNew);
} }
function replaceVersion(name, version, versionNew, callback) { async function replaceVersion(name, version, versionNew) {
place(name, version, versionNew, (error) => { const [error] = await tryToCatch(place, name, version, versionNew);
if (error)
return callback(error); if (error)
return console.error(error);
callback(null, 'done: ' + name);
}); console.log(`done: ${name}`);
} }
function cl(callback) { async function cl() {
const {argv} = process; const {argv} = process;
const length = argv.length - 1; const length = argv.length - 1;
const last = process.argv[length]; const last = process.argv[length];
const regExp = /^--(major|minor|patch)$/; const regExp = /^--(major|minor|patch)$/;
const [, match] = last.match(regExp) || []; const [, match] = last.match(regExp) || [];
if (!regExp.test(last)) console.log(last);
return callback(ERROR);
callback(null, getVersionNew(last, match)); if (!regExp.test(last))
throw Error('ERROR: version is missing. release --patch|--minor|--major');
return getVersionNew(last, match);
} }
function getVersionNew(last, match) { function getVersionNew(last, match) {
@ -70,4 +75,3 @@ function getVersionNew(last, match) {
return last.substr(3); return last.substr(3);
} }

View file

@ -1,74 +1,59 @@
'use strict'; import process from 'node:process';
import Emitify from 'emitify';
/* global DOM */ import inherits from 'inherits';
import {rendy} from 'rendy';
const Emitify = require('emitify'); import load from 'load.js';
const inherits = require('inherits'); import {tryToCatch} from 'try-to-catch';
const rendy = require('rendy'); import {addSlashToEnd} from 'format-io';
const load = require('load.js'); import pascalCase from 'just-pascal-case';
const tryToCatch = require('try-to-catch'); import currify from 'currify';
import {
const pascalCase = require('just-pascal-case');
const isDev = process.env.NODE_ENV === 'development';
const Images = require('./dom/images');
const {unregisterSW} = require('./sw/register');
const getJsonFromFileTable = require('./get-json-from-file-table');
const currify = require('currify');
const noJS = (a) => a.replace(/.js$/, '');
const {
apiURL, apiURL,
formatMsg, formatMsg,
buildFromJSON, buildFromJSON,
} = require('../common/cloudfunc'); } from '#common/cloudfunc';
import * as Images from '#dom/images';
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 loadModule = require('./load-module'); const noJS = (a) => a.replace(/.js$/, '');
const isDev = process.env.NODE_ENV === 'development';
inherits(CloudCmdProto, Emitify); inherits(CloudCmdProto, Emitify);
module.exports = new CloudCmdProto(DOM); export const createCloudCmd = ({DOM, Listeners}) => {
return new CloudCmdProto({
DOM,
Listeners,
});
};
load.addErrorListener((e, src) => { load.addErrorListener((e, src) => {
const msg = `file ${src} could not be loaded`; const msg = `file ${src} could not be loaded`;
Images.show.error(msg); Images.show.error(msg);
}); });
function CloudCmdProto(DOM) { function CloudCmdProto({DOM, Listeners}) {
let Listeners;
const log = (...a) => {
if (!isDev )
return;
console.log(...a);
};
Emitify.call(this); Emitify.call(this);
const CloudCmd = this; const CloudCmd = this;
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
const { const {Storage, Files} = DOM;
Storage,
Files,
} = DOM;
this.log = log; this.log = () => {
if (!isDev)
return;
};
this.prefix = ''; this.prefix = '';
this.prefixSocket = ''; this.prefixSocket = '';
this.prefixURL = ''; this.prefixURL = '';
this.DIRCLIENT = '/dist/';
this.DIRCLIENT_MODULES = this.DIRCLIENT + 'modules/';
this.MIN_ONE_PANEL_WIDTH = 1155; this.MIN_ONE_PANEL_WIDTH = DOM.getCSSVar('min-one-panel-width');
this.HOST = location.origin || location.protocol + '//' + location.host; this.HOST = location.origin || location.protocol + '//' + location.host;
const TITLE = 'Cloud Commander';
this.TITLE = TITLE;
this.sort = { this.sort = {
left: 'name', left: 'name',
right: 'name', right: 'name',
@ -79,28 +64,16 @@ function CloudCmdProto(DOM) {
right: 'asc', right: 'asc',
}; };
/** this.changeDir = async (path, overrides = {}) => {
* Функция привязываеться ко всем ссылкам и
* загружает содержимое каталогов
*
* @param params - {
* paramLink - ссылка
* needRefresh - необходимость обязательной загрузки данных с сервера
* panel
* }
* @param callback
*/
this.loadDir = async (params) => {
const p = params;
const refresh = p.isRefresh;
const { const {
isRefresh,
panel, panel,
history, history = true,
noCurrent, noCurrent,
currentName, currentName,
} = p; } = overrides;
const refresh = isRefresh;
let panelChanged; let panelChanged;
if (!noCurrent && panel && panel !== Info.panel) { if (!noCurrent && panel && panel !== Info.panel) {
@ -116,11 +89,12 @@ function CloudCmdProto(DOM) {
Images.show.load(imgPosition, panel); Images.show.load(imgPosition, panel);
/* загружаем содержимое каталога */ /* загружаем содержимое каталога */
await ajaxLoad(p.path, { await ajaxLoad(addSlashToEnd(path), {
refresh, refresh,
history, history,
noCurrent, noCurrent,
currentName, currentName,
showDotFiles: CloudCmd.config('showDotFiles'),
}, panel); }, panel);
}; };
@ -133,6 +107,8 @@ function CloudCmdProto(DOM) {
CloudCmd.prefix = prefix; CloudCmd.prefix = prefix;
CloudCmd.prefixURL = `${prefix}${apiURL}`; CloudCmd.prefixURL = `${prefix}${apiURL}`;
CloudCmd.prefixSocket = config.prefixSocket; CloudCmd.prefixSocket = config.prefixSocket;
CloudCmd.DIR_DIST = `${prefix}/dist`;
CloudCmd.DIR_MODULES = `${this.DIR_DIST}/modules`;
CloudCmd.config = (key) => config[key]; CloudCmd.config = (key) => config[key];
CloudCmd.config.if = currify((key, fn, a) => config[key] && fn(a)); CloudCmd.config.if = currify((key, fn, a) => config[key] && fn(a));
@ -141,7 +117,6 @@ function CloudCmdProto(DOM) {
* should be called from config.js only * should be called from config.js only
* after successful update on server * after successful update on server
*/ */
if (key === 'password') if (key === 'password')
return; return;
@ -152,22 +127,14 @@ function CloudCmdProto(DOM) {
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity; CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
if (!document.body.scrollIntoViewIfNeeded) if (!document.body.scrollIntoViewIfNeeded)
await load.js(prefix + CloudCmd.DIRCLIENT_MODULES + 'polyfill.js'); await load.js(`${CloudCmd.DIR_MODULES}/polyfill.js`);
await initModules(); await initModules();
await baseInit(); await baseInit();
await loadStyle();
CloudCmd.route(location.hash); CloudCmd.route(location.hash);
}; };
async function loadStyle() {
const {prefix} = CloudCmd;
const name = prefix + '/dist/cloudcmd.common.css';
await load.css(name);
}
this.route = (path) => { this.route = (path) => {
const query = path.split('/'); const query = path.split('/');
@ -177,12 +144,13 @@ function CloudCmdProto(DOM) {
const [kebabModule] = query; const [kebabModule] = query;
const module = noJS(pascalCase(kebabModule.slice(1))); const module = noJS(pascalCase(kebabModule.slice(1)));
const file = query[1]; const [, file] = query;
const current = DOM.getCurrentByName(file); const current = DOM.getCurrentByName(file);
if (file && !current) { if (file && !current) {
const msg = formatMsg('set current file', file, 'error'); const msg = formatMsg('set current file', file, 'error');
CloudCmd.log(msg); CloudCmd.log(msg);
return; return;
} }
@ -204,7 +172,7 @@ function CloudCmdProto(DOM) {
}; };
const initModules = async () => { const initModules = async () => {
CloudCmd.Key = new CloudCmd.Key(); CloudCmd.Key = Key;
CloudCmd.Key.bind(); CloudCmd.Key.bind();
const [, modules] = await tryToCatch(Files.get, 'modules'); const [, modules] = await tryToCatch(Files.get, 'modules');
@ -254,7 +222,7 @@ function CloudCmdProto(DOM) {
}); });
const dirPath = DOM.getCurrentDirPath(); const dirPath = DOM.getCurrentDirPath();
Listeners = CloudCmd.Listeners;
Listeners.init(); Listeners.init();
const panels = getPanels(); const panels = getPanels();
@ -268,7 +236,7 @@ function CloudCmdProto(DOM) {
const data = await Storage.get(dirPath); const data = await Storage.get(dirPath);
if (!data) if (!data)
await Storage.set(dirPath, getJsonFromFileTable()); await Storage.setJson(dirPath, getJsonFromFileTable());
} }
function getPanels() { function getPanels() {
@ -291,19 +259,15 @@ function CloudCmdProto(DOM) {
}; };
this.refresh = async (options = {}) => { this.refresh = async (options = {}) => {
const { const {panel = Info.panel, currentName} = options;
panel = Info.panel,
currentName,
} = options;
const path = DOM.getCurrentDirPath(panel); const path = DOM.getCurrentDirPath(panel);
const isRefresh = true; const isRefresh = true;
const history = false; const history = false;
const noCurrent = options ? options.noCurrent : false; const noCurrent = options?.noCurrent;
await CloudCmd.loadDir({ await CloudCmd.changeDir(path, {
path,
isRefresh, isRefresh,
history, history,
panel, panel,
@ -319,22 +283,18 @@ function CloudCmdProto(DOM) {
* @param options * @param options
* { refresh, history } - необходимость обновить данные о каталоге * { refresh, history } - необходимость обновить данные о каталоге
* @param panel * @param panel
* @param callback
* *
*/ */
async function ajaxLoad(path, options = {}, panel) { async function ajaxLoad(path, options = {}, panel) {
const {RESTful} = DOM; const {RESTful} = DOM;
CloudCmd.log('reading dir: "' + path + '";'); CloudCmd.log(`reading dir: "${path}";`);
const dirStorage = CloudCmd.config('dirStorage'); const dirStorage = CloudCmd.config('dirStorage');
const json = dirStorage && await Storage.get(path); const json = dirStorage && await Storage.getJson(path);
const name = options.currentName || Info.name; const name = options.currentName || Info.name;
const { const {noCurrent, refresh} = options;
noCurrent,
refresh,
} = options;
if (!refresh && json) if (!refresh && json)
return await createFileTable(json, panel, options); return await createFileTable(json, panel, options);
@ -351,7 +311,8 @@ function CloudCmdProto(DOM) {
const [, newObj] = await RESTful.read(path + query, 'json'); const [, newObj] = await RESTful.read(path + query, 'json');
if (!newObj) if (!newObj)
return; // that's OK, error handled by RESTful // that's OK, error handled by RESTful
return;
options.sort = sort; options.sort = sort;
options.order = order; options.order = order;
@ -364,28 +325,35 @@ function CloudCmdProto(DOM) {
if (!CloudCmd.config('dirStorage')) if (!CloudCmd.config('dirStorage'))
return; return;
Storage.set(path, newObj); Storage.setJson(path, newObj);
} }
/** /**
* Функция строит файловую таблицу * Функция строит файловую таблицу
* @param json - данные о файлах * @param data - данные о файлах
* @param panelParam * @param panelParam
* @param history * @param options - history, noCurrent, showDotFiles
* @param callback
*/ */
async function createFileTable(data, panelParam, options) { async function createFileTable(data, panelParam, options) {
const { const {
history, history,
noCurrent, noCurrent,
showDotFiles,
} = options; } = options;
const names = ['file', 'path', 'link', 'pathLink']; const names = [
'file',
'path',
'link',
'pathLink',
];
const [ const [error, [
error, file,
[file, path, link, pathLink], path,
] = await tryToCatch(Files.get, names); link,
pathLink,
]] = await tryToCatch(Files.get, names);
if (error) if (error)
return DOM.Dialog.alert(error.responseText); return DOM.Dialog.alert(error.responseText);
@ -393,10 +361,7 @@ function CloudCmdProto(DOM) {
const panel = panelParam || DOM.getPanel(); const panel = panelParam || DOM.getPanel();
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
const { const {dir, name} = Info;
dir,
name,
} = Info;
const {childNodes} = panel; const {childNodes} = panel;
let i = childNodes.length; let i = childNodes.length;
@ -405,12 +370,13 @@ function CloudCmdProto(DOM) {
panel.removeChild(panel.lastChild); panel.removeChild(panel.lastChild);
panel.innerHTML = buildFromJSON({ panel.innerHTML = buildFromJSON({
sort : options.sort, sort: options.sort,
order : options.order, order: options.order,
data, data,
id : panel.id, id: panel.id,
prefix, prefix,
template : { showDotFiles,
template: {
file, file,
path, path,
pathLink, pathLink,
@ -450,9 +416,7 @@ function CloudCmdProto(DOM) {
const path = parentDirPath; const path = parentDirPath;
await CloudCmd.loadDir({ await CloudCmd.changeDir(path);
path,
});
const current = DOM.getCurrentByName(dir); const current = DOM.getCurrentByName(dir);
const [first] = DOM.getFiles(panel); const [first] = DOM.getFiles(panel);
@ -462,4 +426,3 @@ function CloudCmdProto(DOM) {
}); });
}; };
} }

View file

@ -1,42 +1,44 @@
'use strict'; import '../css/main.css';
import process from 'node:process';
require('../css/main.css'); import wraptile from 'wraptile';
require('../css/nojs.css'); import load from 'load.js';
require('../css/columns/name-size-date.css'); import * as Util from '#common/util';
require('../css/columns/name-size.css'); import * as CloudFunc from '#common/cloudfunc';
import DOM from '#dom';
const wraptile = require('wraptile'); import {registerSW, listenSW} from './sw/register.js';
const load = require('load.js'); import {initSortPanel, sortPanel} from './sort.js';
import {createCloudCmd} from './client.js';
import * as Listeners from './listeners/index.js';
const isDev = process.env.NODE_ENV === 'development'; const isDev = process.env.NODE_ENV === 'development';
const { export default init;
registerSW,
listenSW,
} = require('./sw/register');
// prevent additional loading of emitify globalThis.CloudCmd = init;
window.Emitify = require('emitify');
module.exports = window.CloudCmd = (config) => { async function init(config) {
window.Util = require('../common/util'); globalThis.CloudCmd = createCloudCmd({
window.CloudFunc = require('../common/cloudfunc'); DOM,
Listeners,
});
globalThis.DOM = DOM;
globalThis.Util = Util;
globalThis.CloudFunc = CloudFunc;
const DOM = require('./dom'); await register(config);
window.DOM = DOM;
window.CloudCmd = require('./client');
register(config);
require('./listeners');
require('./key');
require('./sort');
initSortPanel();
globalThis.CloudCmd.sortPanel = sortPanel;
const prefix = getPrefix(config.prefix); const prefix = getPrefix(config.prefix);
window.CloudCmd.init(prefix, config); globalThis.CloudCmd.init(prefix, config);
};
if (globalThis.CloudCmd.config('menu') === 'aleman')
setTimeout(() => {
import('https://esm.sh/@putout/processor-html');
import('https://esm.sh/@putout/bundle@5.5');
}, 100);
}
function getPrefix(prefix) { function getPrefix(prefix) {
if (!prefix) if (!prefix)
@ -52,7 +54,7 @@ const onUpdateFound = wraptile(async (config) => {
if (isDev) if (isDev)
return; return;
const {DOM} = window; const {DOM} = globalThis;
const prefix = getPrefix(config.prefix); const prefix = getPrefix(config.prefix);
await load.js(`${prefix}/dist/cloudcmd.common.js`); await load.js(`${prefix}/dist/cloudcmd.common.js`);
@ -61,7 +63,7 @@ const onUpdateFound = wraptile(async (config) => {
console.log('cloudcmd: sw: updated'); console.log('cloudcmd: sw: updated');
DOM.Events.removeAll(); DOM.Events.removeAll();
window.CloudCmd(config); globalThis.CloudCmd(config);
}); });
async function register(config) { async function register(config) {
@ -70,4 +72,3 @@ async function register(config) {
listenSW(sw, 'updatefound', onUpdateFound(config)); listenSW(sw, 'updatefound', onUpdateFound(config));
} }

View file

@ -1,137 +1,124 @@
'use strict'; /* global CloudCmd*/
import * as Storage from '#dom/storage';
import tryToPromiseAll from '../../common/try-to-promise-all.js';
/* global CloudCmd */ const CLASS = 'cut-file';
const COPY = 'copy';
const CUT = 'cut';
const tryToPromiseAll = require('../../common/try-to-promise-all'); function showMessage(msg) {
const Storage = require('./storage'); globalThis.DOM.Dialog.alert(msg);
const DOM = require('./');
module.exports = new BufferProto();
function BufferProto() {
const Info = DOM.CurrentInfo;
const CLASS = 'cut-file';
const COPY = 'copy';
const CUT = 'cut';
const Buffer = {
cut : callIfEnabled.bind(null, cut),
copy : callIfEnabled.bind(null, copy),
clear : callIfEnabled.bind(null, clear),
paste : callIfEnabled.bind(null, paste),
};
function showMessage(msg) {
DOM.Dialog.alert(msg);
}
function getNames() {
const files = DOM.getActiveFiles();
const names = DOM.getFilenames(files);
return names;
}
function addCutClass() {
const files = DOM.getActiveFiles();
for (const element of files) {
element.classList.add(CLASS);
}
}
function rmCutClass() {
const files = DOM.getByClassAll(CLASS);
for (const element of files) {
element.classList.remove(CLASS);
}
}
function callIfEnabled(callback) {
const is = CloudCmd.config('buffer');
if (is)
return callback();
showMessage('Buffer disabled in config!');
}
async function readBuffer() {
const [e, cp, ct] = await tryToPromiseAll([
Storage.get(COPY),
Storage.get(CUT),
]);
return [
e,
cp,
ct,
];
}
async function copy() {
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
await Storage.remove(CUT);
await Storage.set(COPY, {
from,
names,
});
}
async function cut() {
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
addCutClass();
await Storage.set(CUT, {
from,
names,
});
}
async function clear() {
await Storage.remove(COPY);
await Storage.remove(CUT);
rmCutClass();
}
async function paste() {
const [error, cp, ct] = await readBuffer();
if (error || !cp && !ct)
return showMessage(error || 'Buffer is empty!');
const opStr = cp ? 'copy' : 'move';
const data = cp || ct;
const {Operation} = CloudCmd;
const msg = 'Path is same!';
const to = Info.dirPath;
if (data.from === to)
return showMessage(msg);
Operation.show(opStr, {
...data,
to,
});
await clear();
}
return Buffer;
} }
function getNames() {
const {DOM} = globalThis;
const files = DOM.getActiveFiles();
return DOM.getFilenames(files);
}
function addCutClass() {
const {DOM} = globalThis;
const files = DOM.getActiveFiles();
for (const element of files) {
element.classList.add(CLASS);
}
}
function rmCutClass() {
const {DOM} = globalThis;
const files = DOM.getByClassAll(CLASS);
for (const element of files) {
element.classList.remove(CLASS);
}
}
const checkEnabled = (fn) => () => {
const is = CloudCmd.config('buffer');
if (is)
return fn();
showMessage('Buffer disabled in config!');
};
async function readBuffer() {
const [e, cp, ct] = await tryToPromiseAll([
Storage.getJson(COPY),
Storage.getJson(CUT),
]);
return [
e,
cp,
ct,
];
}
export const copy = checkEnabled(async () => {
const Info = globalThis.DOM.CurrentInfo;
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
await Storage.remove(CUT);
await Storage.setJson(COPY, {
from,
names,
});
});
export const cut = checkEnabled(async () => {
const Info = globalThis.DOM.CurrentInfo;
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
addCutClass();
await Storage.setJson(CUT, {
from,
names,
});
});
export const clear = checkEnabled(async () => {
await Storage.remove(COPY);
await Storage.remove(CUT);
rmCutClass();
});
export const paste = checkEnabled(async () => {
const Info = globalThis.DOM.CurrentInfo;
const [error, cp, ct] = await readBuffer();
if (error || !cp && !ct)
return showMessage(error || 'Buffer is empty!');
const opStr = cp ? 'copy' : 'move';
const data = cp || ct;
const {Operation} = CloudCmd;
const msg = 'Path is same!';
const to = Info.dirPath;
if (data.from === to)
return showMessage(msg);
Operation.show(opStr, {
...data,
to,
});
await clear();
});

83
client/dom/cmd.js Normal file
View file

@ -0,0 +1,83 @@
/* 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,28 +1,16 @@
'use strict';
/* global DOM */ /* global DOM */
/* global CloudCmd */ /* global CloudCmd */
import createElement from '@cloudcmd/create-element';
const btoa = require('../../common/btoa'); import {getTitle, FS} from '#common/cloudfunc';
const createElement = require('@cloudcmd/create-element'); import {encode, decode} from '#common/entity';
const {
encode,
decode,
} = require('../../common/entity');
const {
getTitle,
FS,
} = require('../../common/cloudfunc');
let Title; let Title;
const CURRENT_FILE = 'current-file'; const CURRENT_FILE = 'current-file';
const NBSP_REG = RegExp(String.fromCharCode(160), 'g'); const encodeNBSP = (a) => a?.replace('\xa0', '&nbsp;');
const SPACE = ' '; const decodeNBSP = (a) => a?.replace('&nbsp;', '\xa0');
module.exports._CURRENT_FILE = CURRENT_FILE; export const _CURRENT_FILE = CURRENT_FILE;
/** /**
* set name from current (or param) file * set name from current (or param) file
@ -30,7 +18,7 @@ module.exports._CURRENT_FILE = CURRENT_FILE;
* @param name * @param name
* @param current * @param current
*/ */
module.exports.setCurrentName = (name, current) => { export const setCurrentName = (name, current) => {
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
const {link} = Info; const {link} = Info;
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
@ -41,7 +29,7 @@ module.exports.setCurrentName = (name, current) => {
link.href = dir + encoded; link.href = dir + encoded;
link.innerHTML = encoded; link.innerHTML = encoded;
current.setAttribute('data-name', 'js-file-' + btoa(encodeURI(name))); current.setAttribute('data-name', createNameAttribute(name));
CloudCmd.emit('current-file', current); CloudCmd.emit('current-file', current);
return link; return link;
@ -52,28 +40,48 @@ module.exports.setCurrentName = (name, current) => {
* *
* @param currentFile * @param currentFile
*/ */
module.exports.getCurrentName = (currentFile) => { export const getCurrentName = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
if (!current) if (!current)
return ''; return '';
const link = DOM.getCurrentLink(current); return parseNameAttribute(current.getAttribute('data-name'));
if (!link)
return '';
return decode(link.title)
.replace(NBSP_REG, SPACE);
}; };
/** /**
* get current direcotory path * Generate a `data-name` attribute for the given filename
* @param name The string name to encode
*/ */
module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => { const createNameAttribute = (name) => {
const encoded = btoa(encodeURI(name));
return `js-file-${encoded}`;
};
/**
* Parse a `data-name` attribute string back into the original filename
* @param attribute The string we wish to decode
*/
const parseNameAttribute = (attribute) => {
attribute = attribute.replace('js-file-', '');
return decodeNBSP(decodeURI(atob(attribute)));
};
export const _parseNameAttribute = parseNameAttribute;
const parseHrefAttribute = (prefix, attribute) => {
attribute = attribute.replace(RegExp('^' + prefix + FS), '');
return decode(decodeNBSP(attribute));
};
export const _parseHrefAttribute = parseHrefAttribute;
/**
* get current directory path
*/
export const getCurrentDirPath = (panel = DOM.getPanel()) => {
const path = DOM.getByDataName('js-path', panel); const path = DOM.getByDataName('js-path', panel);
return path.textContent return path.textContent;
.replace(NBSP_REG, SPACE);
}; };
/** /**
@ -81,36 +89,31 @@ module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => {
* *
* @param currentFile - current file by default * @param currentFile - current file by default
*/ */
module.exports.getCurrentPath = (currentFile) => { export const getCurrentPath = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const [element] = DOM.getByTag('a', current); const [element] = DOM.getByTag('a', current);
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
const path = element return parseHrefAttribute(prefix, element.getAttribute('href'));
.getAttribute('href')
.replace(RegExp('^' + prefix + FS), '')
.replace(NBSP_REG, SPACE);
return decode(path);
}; };
/** /**
* get current direcotory name * get current directory name
*/ */
module.exports.getCurrentDirName = () => { export const getCurrentDirName = () => {
const href = DOM.getCurrentDirPath() const href = DOM
.getCurrentDirPath()
.replace(/\/$/, ''); .replace(/\/$/, '');
const substr = href.substr(href, href.lastIndexOf('/')); const substr = href.substr(href, href.lastIndexOf('/'));
const ret = href.replace(substr + '/', '') || '/';
return ret; return href.replace(`${substr}/`, '') || '/';
}; };
/** /**
* get current direcotory path * get current directory path
*/ */
module.exports.getParentDirPath = (panel) => { export const 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);
@ -122,9 +125,9 @@ module.exports.getParentDirPath = (panel) => {
}; };
/** /**
* get not current direcotory path * get not current directory path
*/ */
module.exports.getNotCurrentDirPath = () => { export const getNotCurrentDirPath = () => {
const panel = DOM.getPanel({ const panel = DOM.getPanel({
active: false, active: false,
}); });
@ -137,20 +140,20 @@ module.exports.getNotCurrentDirPath = () => {
* *
* @currentFile * @currentFile
*/ */
module.exports.getCurrentFile = () => { export const getCurrentFile = () => {
return DOM.getByClass(CURRENT_FILE); return DOM.getByClass(CURRENT_FILE);
}; };
/** /**
* get current file by name * get current file by name
*/ */
module.exports.getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => { export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => {
const dataName = 'js-file-' + btoa(encodeURI(name)); const dataName = 'js-file-' + btoa(encodeURI(encodeNBSP(name)));
return DOM.getByDataName(dataName, panel); return DOM.getByDataName(dataName, panel);
}; };
/** /**
* private function thet unset currentfile * private function that unset currentFile
* *
* @currentFile * @currentFile
*/ */
@ -166,7 +169,7 @@ function unsetCurrentFile(currentFile) {
/** /**
* unified way to set current file * unified way to set current file
*/ */
module.exports.setCurrentFile = (currentFile, options) => { export const setCurrentFile = (currentFile, options) => {
const o = options; const o = options;
const currentFileWas = DOM.getCurrentFile(); const currentFileWas = DOM.getCurrentFile();
@ -195,7 +198,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
* but it should be false * but it should be false
* to prevent default behavior * to prevent default behavior
*/ */
if (!o || o.history !== false) { if (!o || o.history) {
const historyPath = path === '/' ? path : FS + path; const historyPath = path === '/' ? path : FS + path;
DOM.setHistory(historyPath, null, historyPath); DOM.setHistory(historyPath, null, historyPath);
} }
@ -203,6 +206,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
/* scrolling to current file */ /* scrolling to current file */
const CENTER = true; const CENTER = true;
DOM.scrollIntoViewIfNeeded(currentFile, CENTER); DOM.scrollIntoViewIfNeeded(currentFile, CENTER);
CloudCmd.emit('current-file', currentFile); CloudCmd.emit('current-file', currentFile);
@ -212,7 +216,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
return DOM; return DOM;
}; };
this.setCurrentByName = (name) => { export const setCurrentByName = (name) => {
const current = DOM.getCurrentByName(name); const current = DOM.getCurrentByName(name);
return DOM.setCurrentFile(current); return DOM.setCurrentFile(current);
}; };
@ -223,7 +227,7 @@ this.setCurrentByName = (name) => {
* @param layer - element * @param layer - element
* @param - position {x, y} * @param - position {x, y}
*/ */
module.exports.getCurrentByPosition = ({x, y}) => { export const getCurrentByPosition = ({x, y}) => {
const element = document.elementFromPoint(x, y); const element = document.elementFromPoint(x, y);
const getEl = (el) => { const getEl = (el) => {
@ -255,7 +259,7 @@ module.exports.getCurrentByPosition = ({x, y}) => {
* *
* @param currentFile * @param currentFile
*/ */
module.exports.isCurrentFile = (currentFile) => { export const isCurrentFile = (currentFile) => {
if (!currentFile) if (!currentFile)
return false; return false;
@ -267,8 +271,7 @@ module.exports.isCurrentFile = (currentFile) => {
* *
* @param name * @param name
*/ */
export const setTitle = (name) => {
module.exports.setTitle = (name) => {
if (!Title) if (!Title)
Title = DOM.getByTag('title')[0] || createElement('title', { Title = DOM.getByTag('title')[0] || createElement('title', {
innerHTML: name, innerHTML: name,
@ -285,20 +288,24 @@ module.exports.setTitle = (name) => {
* *
* @param currentFile * @param currentFile
*/ */
module.exports.isCurrentIsDir = (currentFile) => { export const isCurrentIsDir = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const path = DOM.getCurrentPath(current);
const fileType = DOM.getCurrentType(current); const fileType = DOM.getCurrentType(current);
return /^directory(-link)?/.test(fileType); const isZip = path.endsWith('.zip');
const isDir = /^directory(-link)?/.test(fileType);
return isDir || isZip;
}; };
module.exports.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();
return type; return type;
}; };

View file

@ -1,69 +1,72 @@
'use strict'; import {test, stub} from 'supertape';
import {create} from 'auto-globals';
import wraptile from 'wraptile';
import * as currentFile from './current-file.js';
const test = require('supertape');
const {create} = require('auto-globals');
const stub = require('@cloudcmd/stub');
const id = (a) => a; const id = (a) => a;
const wraptile = require('wraptile');
const returns = wraptile(id);
const currentFile = require('./current-file'); const returns = wraptile(id);
const {_CURRENT_FILE} = currentFile; const {_CURRENT_FILE} = currentFile;
test('current-file: setCurrentName: setAttribute', (t) => { test('current-file: setCurrentName: setAttribute', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const {setAttribute} = current; const {setAttribute} = current;
currentFile.setCurrentName('hello', current); currentFile.setCurrentName('hello', current);
t.ok(setAttribute.calledWith('data-name', 'js-file-aGVsbG8='), 'should call setAttribute'); globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
global.DOM = DOM;
global.CloudCmd = CloudCmd;
t.calledWith(setAttribute, [
'data-name',
'js-file-aGVsbG8=',
], 'should call setAttribute');
t.end(); t.end();
}); });
test('current-file: setCurrentName: setAttribute: cyrillic', (t) => { test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const {setAttribute} = current; const {setAttribute} = current;
currentFile.setCurrentName('ай', current); currentFile.setCurrentName('ай', current);
t.ok(setAttribute.calledWith('data-name', 'js-file-JUQwJUIwJUQwJUI5'), 'should call setAttribute'); globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
global.DOM = DOM; t.calledWith(setAttribute, [
global.CloudCmd = CloudCmd; 'data-name',
'js-file-JUQwJUIwJUQwJUI5',
], 'should call setAttribute');
t.end();
});
test('current-file: getCurrentName', (t) => {
const current = create();
current.getAttribute.returns('js-file-Ymlu');
const result = currentFile.getCurrentName(current);
t.equal(result, 'bin');
t.end(); t.end();
}); });
test('current-file: emit', (t) => { test('current-file: emit', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
const emit = stub(); const emit = stub();
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd({ globalThis.CloudCmd = getCloudCmd({
emit, emit,
}); });
@ -71,47 +74,41 @@ test('current-file: emit', (t) => {
currentFile.setCurrentName('hello', current); currentFile.setCurrentName('hello', current);
t.ok(emit.calledWith('current-file', current), 'should call emit'); globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
global.DOM = DOM;
global.CloudCmd = CloudCmd;
t.calledWith(emit, ['current-file', current], 'should call emit');
t.end(); t.end();
}); });
test('current-file: setCurrentName: return', (t) => { test('current-file: setCurrentName: return', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
const link = {}; const link = {};
global.DOM = getDOM({ globalThis.DOM = getDOM({
link, link,
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.setCurrentName('hello', current); const result = currentFile.setCurrentName('hello', current);
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.equal(result, link, 'should return link'); t.equal(result, link, 'should return link');
global.DOM = DOM;
global.CloudCmd = CloudCmd;
t.end(); t.end();
}); });
test('current-file: getParentDirPath: result', (t) => { test('current-file: getParentDirPath: result', (t) => {
const {DOM} = global; const {DOM} = globalThis;
const getCurrentDirPath = returns('/D/Films/+++favorite films/'); const getCurrentDirPath = returns('/D/Films/+++favorite films/');
const getCurrentDirName = returns('+++favorite films'); const getCurrentDirName = returns('+++favorite films');
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentDirPath, getCurrentDirPath,
getCurrentDirName, getCurrentDirName,
}); });
@ -119,65 +116,55 @@ test('current-file: getParentDirPath: result', (t) => {
const result = currentFile.getParentDirPath(); const result = currentFile.getParentDirPath();
const expected = '/D/Films/'; const expected = '/D/Films/';
global.DOM = DOM; globalThis.DOM = DOM;
t.equal(result, expected, 'should return parent dir path'); t.equal(result, expected, 'should return parent dir path');
t.end(); t.end();
}); });
test('current-file: isCurrentFile: no', (t) => { test('current-file: isCurrentFile: no', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const result = currentFile.isCurrentFile(); const result = currentFile.isCurrentFile();
const expect = false;
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.equal(result, expect, 'should equal'); t.notOk(result);
t.end(); t.end();
}); });
test('current-file: isCurrentFile', (t) => { test('current-file: isCurrentFile', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
const isContainClass = stub(); const isContainClass = stub();
global.DOM = getDOM({ globalThis.DOM = getDOM({
isContainClass, isContainClass,
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = {}; const current = {};
currentFile.isCurrentFile(current); currentFile.isCurrentFile(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.ok(isContainClass.calledWith(current, _CURRENT_FILE), 'should call isContainClass'); t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass');
t.end(); t.end();
}); });
test('current-file: getCurrentType', (t) => { test('current-file: getCurrentType', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const {getByDataName} = global.DOM; const {getByDataName} = globalThis.DOM;
getByDataName.returns({ getByDataName.returns({
className: 'mini-icon directory', className: 'mini-icon directory',
@ -187,122 +174,129 @@ test('current-file: getCurrentType', (t) => {
currentFile.getCurrentType(current); currentFile.getCurrentType(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.ok(getByDataName.calledWith('js-type', current)); t.calledWith(getByDataName, ['js-type', current]);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: getCurrentType', (t) => { test('current-file: isCurrentIsDir: getCurrentType', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const {getCurrentType} = global.DOM; const {getCurrentType} = globalThis.DOM;
const current = create(); const current = create();
currentFile.isCurrentIsDir(current); currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.ok(getCurrentType.calledWith(current)); t.calledWith(getCurrentType, [current]);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: directory', (t) => { test('current-file: isCurrentIsDir: directory', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentType: stub().returns('directory'), getCurrentType: stub().returns('directory'),
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.isCurrentIsDir(current); const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.ok(result); t.ok(result);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: directory-link', (t) => { test('current-file: isCurrentIsDir: directory-link', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentType: stub().returns('directory-link'), getCurrentType: stub().returns('directory-link'),
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.isCurrentIsDir(current); const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.ok(result); t.ok(result);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: file', (t) => { test('current-file: isCurrentIsDir: file', (t) => {
const { const {DOM, CloudCmd} = globalThis;
DOM,
CloudCmd,
} = global;
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentType: stub().returns('file'), getCurrentType: stub().returns('file'),
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.isCurrentIsDir(current); const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.notOk(result); t.notOk(result);
t.end(); t.end();
}); });
function getCloudCmd({emit} = {}) { const getCloudCmd = ({emit} = {}) => ({
return { prefix: '',
prefix: '', emit: emit || stub(),
emit: emit || stub(), });
};
}
function getDOM({ test('current-file: parseNameAttribute', (t) => {
link = {}, const result = currentFile._parseNameAttribute('js-file-aGVsbG8mbmJzcDt3b3JsZA==');
getCurrentDirPath = stub(), const expected = 'hello\xa0world';
getCurrentDirName = stub(),
getByDataName = stub(), t.equal(result, expected);
isContainClass = stub(), t.end();
getCurrentType = stub(), });
} = {}) {
test('current-file: parseHrefAttribute', (t) => {
const prefix = '/api/v1';
const result = currentFile._parseHrefAttribute(prefix, '/api/v1/fs/hello&nbsp;world');
const expected = '/hello\xa0world';
t.equal(result, expected);
t.end();
});
function getDOM(overrides = {}) {
const {
link = {},
getCurrentDirPath = stub(),
getCurrentDirName = stub(),
getByDataName = stub(),
isContainClass = stub(),
getCurrentType = stub(),
getCurrentPath = stub().returns(''),
} = overrides;
return { return {
getCurrentDirPath, getCurrentDirPath,
getCurrentDirName, getCurrentDirName,
getCurrentPath,
getByDataName, getByDataName,
isContainClass, isContainClass,
getCurrentType, getCurrentType,
@ -312,4 +306,3 @@ function getDOM({
}, },
}; };
} }

View file

@ -1,27 +1,18 @@
'use strict'; import {tryToCatch} from 'try-to-catch';
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';
module.exports.alert = (...a) => alert(title, ...a, { export const alert = (...a) => smalltalk.alert(title, ...a, {
cancel: false, cancel: false,
}); });
module.exports.prompt = (...a) => tryToCatch(prompt, title, ...a); export const prompt = (...a) => tryToCatch(smalltalk.prompt, title, ...a);
module.exports.confirm = (...a) => tryToCatch(confirm, title, ...a); export const confirm = (...a) => tryToCatch(smalltalk.confirm, title, ...a);
module.exports.progress = (...a) => progress(title, ...a); export const progress = (...a) => smalltalk.progress(title, ...a);
module.exports.alert.noFiles = () => { alert.noFiles = () => {
return alert(title, 'No files selected!', { return smalltalk.alert(title, 'No files selected!', {
cancel: false, cancel: false,
}); });
}; };

View file

@ -1,27 +1,19 @@
'use strict'; /* global DOM, CloudCmd */
import philip from 'philip';
import * as Dialog from '#dom/dialog';
import {FS} from '#common/cloudfunc';
import * as Images from '#dom/images';
/* global CloudCmd */ export const uploadDirectory = (items) => {
const philip = require('philip');
const Images = require('./images');
const {FS} = require('../../common/cloudfunc');
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');
const entries = Array.from(items).map((item) => { const entries = Array
return item.webkitGetAsEntry(); .from(items)
}); .map((item) => item.webkitGetAsEntry());
const dirPath = getPathWhenRootEmpty(); const dirPath = DOM.getCurrentDirPath();
const path = dirPath const path = dirPath.replace(/\/$/, '');
.replace(/\/$/, '');
const progress = Dialog.progress('Uploading...'); const progress = Dialog.progress('Uploading...');
@ -65,15 +57,8 @@ module.exports = (items) => {
uploader.on('end', CloudCmd.refresh); uploader.on('end', CloudCmd.refresh);
}; };
function percent(i, n, per = 100) { const percent = (i, n, per = 100) => Math.round(i * per / n);
return Math.round(i * per / n);
}
function uploadFile(url, data) { const uploadFile = (url, data) => DOM.load.put(url, data);
return DOM.load.put(url, data);
}
function uploadDir(url) {
return DOM.load.put(url + '?dir');
}
const uploadDir = (url) => DOM.load.put(`${url}?dir`);

View file

@ -1,8 +1,15 @@
'use strict'; import currify from 'currify';
const currify = require('currify'); const DOM = {
show,
const DOM = module.exports; hide,
getByClass,
getByClassAll,
getByDataName,
getById,
getByTag,
isContainClass,
};
/** /**
* check class of element * check class of element
@ -10,7 +17,7 @@ const DOM = module.exports;
* @param element * @param element
* @param className * @param className
*/ */
const isContainClass = (element, className) => { export function isContainClass(element, className) {
if (!element) if (!element)
throw Error('element could not be empty!'); throw Error('element could not be empty!');
@ -18,67 +25,68 @@ const isContainClass = (element, className) => {
throw Error('className could not be empty!'); throw Error('className could not be empty!');
if (Array.isArray(className)) if (Array.isArray(className))
return className.some(currify(isContainClass, element)); return className.some(currify(
isContainClass,
element,
));
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
*/ */
module.exports.getByTag = (tag, element = document) => { export function getByTag(tag, element = document) {
return element.getElementsByTagName(tag); return element.getElementsByTagName(tag);
}; }
/** /**
* Function search element by id * Function search element by id
* @param Id - id * @param id
* @param element
*/ */
module.exports.getById = (id, element = document) => { export function 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
*/ */
module.exports.getByClass = (className, element = document) => { export function getByClass(className, element = document) {
return DOM.getByClassAll(className, element)[0]; return DOM.getByClassAll(className, element)[0];
}; }
module.exports.getByDataName = (attribute, element = document) => { export function 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 pClass - className * @param className
* @param element - element * @param element
*/ */
module.exports.getByClassAll = (className, element) => { export function 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
*/ */
module.exports.hide = (element) => { export function hide(element) {
element.classList.add('hidden'); element.classList.add('hidden');
return DOM; return DOM;
}; }
module.exports.show = (element) => { export function show(element) {
element.classList.remove('hidden'); element.classList.remove('hidden');
return DOM; return DOM;
}; }

View file

@ -1,19 +1,27 @@
'use strict'; import {test, stub} from 'supertape';
import {create} from 'auto-globals';
const test = require('supertape'); import {tryCatch} from 'try-catch';
const {create} = require('auto-globals'); import {
const tryCatch = require('try-catch'); isContainClass,
getByTag,
const {isContainClass} = require('./dom-tree'); getById,
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);
t.equal(e.message, 'element could not be empty!', 'should throw when no element'); t.equal(e.message, 'element could not be empty!', 'should throw when no element');
t.end(); t.end();
}); });
test('dom: isContainClass: no className', (t) => { test('dom: isContainClass: no className', (t) => {
const [e] = tryCatch(isContainClass, {}); const [e] = tryCatch(isContainClass, {});
t.equal(e.message, 'className could not be empty!', 'should throw when no element'); t.equal(e.message, 'className could not be empty!', 'should throw when no element');
t.end(); t.end();
}); });
@ -25,7 +33,7 @@ test('dom: isContainClass: contains', (t) => {
const className = 'hello'; const className = 'hello';
isContainClass(el, className); isContainClass(el, className);
t.ok(contains.calledWith(className), 'should call contains'); t.calledWith(contains, [className], 'should call contains');
t.end(); t.end();
}); });
@ -34,13 +42,111 @@ test('dom: isContainClass: contains: array', (t) => {
const {contains} = el.classList; const {contains} = el.classList;
const className = 'hello'; const className = 'hello';
isContainClass(el, [ isContainClass(el, ['world', className, 'hello']);
'world',
className,
'hello',
]);
t.ok(contains.calledWith(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,8 +1,6 @@
'use strict';
let list = []; let list = [];
module.exports.add = (el, name, fn) => { export const add = (el, name, fn) => {
list.push([ list.push([
el, el,
name, name,
@ -10,11 +8,8 @@ module.exports.add = (el, name, fn) => {
]); ]);
}; };
module.exports.clear = () => { export const clear = () => {
list = []; list = [];
}; };
module.exports.get = () => { export const get = () => list;
return list;
};

View file

@ -1,7 +1,5 @@
'use strict'; import {test} from 'supertape';
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 = {};
@ -10,11 +8,16 @@ test('event-store: get', (t) => {
eventStore.add(el, name, fn); eventStore.add(el, name, fn);
const result = eventStore.get(); const result = eventStore.get();
const expected = [ const expected = [
[el, name, fn], [
el,
name,
fn,
],
]; ];
t.deepEqual(expected, result, 'should equal'); t.deepEqual(result, expected);
t.end(); t.end();
}); });
@ -29,6 +32,6 @@ test('event-store: clear', (t) => {
const result = eventStore.get(); const result = eventStore.get();
const expected = []; const expected = [];
t.deepEqual(expected, result, 'should equal'); t.deepEqual(result, expected);
t.end(); t.end();
}); });

View file

@ -1,250 +1,203 @@
'use strict'; import itype from 'itype';
import * as EventStore from './event-store.js';
const itype = require('itype'); /**
const EventStore = require('./event-store'); * safe add event listener
*
module.exports = new EventsProto(); * @param type
* @param element - document by default
function EventsProto() { * @param listener
const Events = this; */
export const add = (type, element, listener) => {
checkType(type);
const getEventOptions = (eventName) => { parseArgs(type, element, listener, (element, args) => {
if (eventName !== 'touchstart') const [name, fn, options] = args;
return false;
return { element.addEventListener(name, fn, options);
passive: true, EventStore.add(element, name, fn);
}; });
return Events;
};
/**
* safe add event listener
*
* @param type
* @param listener
* @param element - document by default
*/
export const addOnce = (type, element, listener) => {
const once = (event) => {
Events.remove(type, element, once);
listener(event);
}; };
function parseArgs(eventName, element, listener, callback) { if (!listener) {
let isFunc; listener = element;
const args = [ element = null;
eventName,
element,
listener,
callback,
];
const EVENT_NAME = 1;
const ELEMENT = 0;
const type = itype(eventName);
switch(type) {
default:
if (!/element$/.test(type))
throw Error('unknown eventName: ' + type);
parseArgs(
args[EVENT_NAME],
args[ELEMENT],
listener,
callback,
);
break;
case 'string':
isFunc = itype.function(element);
if (isFunc) {
listener = element;
element = null;
}
if (!element)
element = window;
callback(element, [
eventName,
listener,
getEventOptions(eventName),
]);
break;
case 'array':
for (const name of eventName) {
parseArgs(
name,
element,
listener,
callback,
);
}
break;
case 'object':
for (const name of Object.keys(eventName)) {
const eventListener = eventName[name];
parseArgs(
name,
element,
eventListener,
callback,
);
}
break;
}
} }
/** add(type, element, once);
* safe add event listener
*
* @param type
* @param element {document by default}
* @param listener
*/
this.add = (type, element, listener) => {
checkType(type);
parseArgs(type, element, listener, (element, args) => {
const [name, fn, options] = args;
element.addEventListener(name, fn, options);
EventStore.add(element, name, fn);
});
return Events;
};
/** return Events;
* safe add event listener };
*
* @param type /**
* @param listener * safe remove event listener
* @param element {document by default} *
*/ * @param type
this.addOnce = (type, element, listener) => { * @param listener
const once = (event) => { * @param element - document by default
Events.remove(type, element, once); */
listener(event); export const remove = (type, element, listener) => {
}; checkType(type);
parseArgs(type, element, listener, (element, args) => {
element.removeEventListener(...args);
});
return Events;
};
/**
* remove all added event listeners
*/
export const removeAll = () => {
const events = EventStore.get();
for (const [el, name, fn] of events)
el.removeEventListener(name, fn);
EventStore.clear();
};
/**
* safe add event keydown listener
*
* @param args
*/
export const addKey = function(...args) {
return add('keydown', ...args);
};
/**
* safe remove event click listener
*
* @param args
*/
export const rmKey = function(...args) {
return Events.remove('keydown', ...args);
};
/**
* safe add event click listener
*/
export const addClick = function(...args) {
return Events.add('click', ...args);
};
/**
* safe remove event click listener
*/
export const rmClick = function(...args) {
return remove('click', ...args);
};
export const addContextMenu = function(...args) {
return add('contextmenu', ...args);
};
/**
* safe add load listener
*/
export const addLoad = function(...args) {
return add('load', ...args);
};
function checkType(type) {
if (!type)
throw Error('type could not be empty!');
}
const getEventOptions = (eventName) => {
if (eventName !== 'touchstart')
return false;
return {
passive: true,
};
};
function parseArgs(eventName, element, listener, callback) {
let isFunc;
const args = [
eventName,
element,
listener,
callback,
];
const EVENT_NAME = 1;
const ELEMENT = 0;
const type = itype(eventName);
switch(type) {
default:
if (!type.endsWith('element'))
throw Error(`unknown eventName: ${type}`);
if (!listener) { parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback);
break;
case 'string':
isFunc = itype.function(element);
if (isFunc) {
listener = element; listener = element;
element = null; element = null;
} }
this.add(type, element, once); if (!element)
element = window;
return Events; callback(element, [
}; eventName,
listener,
getEventOptions(eventName),
]);
break;
/** case 'array':
* safe remove event listener
*
* @param type
* @param listener
* @param element {document by default}
*/
this.remove = (type, element, listener) => {
checkType(type);
parseArgs(type, element, listener, (element, args) => { for (const name of eventName) {
element.removeEventListener(...args); parseArgs(name, element, listener, callback);
}); }
return Events; break;
};
/** case 'object':
* remove all added event listeners
*
* @param listener
*/
this.removeAll = () => {
const events = EventStore.get();
for (const [el, name, fn] of events) for (const name of Object.keys(eventName)) {
el.removeEventListener(name, fn); const eventListener = eventName[name];
parseArgs(name, element, eventListener, callback);
}
EventStore.clear(); break;
};
/**
* safe add event keydown listener
*
* @param listener
*/
this.addKey = function(...argsArr) {
const name = 'keydown';
const args = [name, ...argsArr];
return Events.add(...args);
};
/**
* safe remove event click listener
*
* @param listener
*/
this.rmKey = function(...argsArr) {
const name = 'keydown';
const args = [name, ...argsArr];
return Events.remove(...args);
};
/**
* safe add event click listener
*
* @param listener
*/
this.addClick = function(...argsArr) {
const name = 'click';
const args = [name, ...argsArr];
return Events.add(...args);
};
/**
* safe remove event click listener
*
* @param listener
*/
this.rmClick = function(...argsArr) {
const name = 'click';
const args = [name, ...argsArr];
return Events.remove(...args);
};
this.addContextMenu = function(...argsArr) {
const name = 'contextmenu';
const args = [name, ...argsArr];
return Events.add(...args);
};
/**
* safe add event click listener
*
* @param listener
*/
this.addError = function(...argsArr) {
const name = 'error';
const args = [name, ...argsArr];
return Events.add(...args);
};
/**
* safe add load click listener
*
* @param listener
*/
this.addLoad = function(...argsArr) {
const name = 'load';
const args = [name, ...argsArr];
return Events.add(...args);
};
function checkType(type) {
if (!type)
throw Error('type could not be empty!');
} }
} }
const Events = {
add,
addClick,
addContextMenu,
addKey,
addLoad,
addOnce,
remove,
removeAll,
rmClick,
rmKey,
};

View file

@ -1,37 +1,29 @@
/* global CloudCmd */ /* global CloudCmd */
import itype from 'itype';
'use strict'; import {promisify} from 'es6-promisify';
import * as load from '#dom/load';
const itype = require('itype'); import * as RESTful from '#dom/rest';
const currify = require('currify');
const {promisify} = require('es6-promisify');
const load = require('./load');
const RESTful = require('./rest');
const Promises = {}; const Promises = {};
const FILES_JSON = 'config|modules'; const FILES_JSON = 'config|modules';
const FILES_HTML = 'file|path|link|pathLink|media'; const FILES_HTML = 'file|path|link|pathLink|media';
const FILES_HTML_ROOT = 'view/media-tmpl|config-tmpl|upload'; const FILES_HTML_ROOT = 'view/media-tmpl|config-tmpl|upload';
const DIR_HTML = '/tmpl/'; const DIR_HTML = '/tmpl/';
const DIR_HTML_FS = DIR_HTML + 'fs/'; const DIR_HTML_FS = `${DIR_HTML}fs/`;
const DIR_JSON = '/json/'; const DIR_JSON = '/json/';
const timeout = getTimeoutOnce(2000); const timeout = getTimeoutOnce(2000);
const get = currify(getFile); export const get = getFile;
const unaryMap = (array, fn) => array.map((a) => fn(a));
module.exports.get = get; function getFile(name) {
async function getFile(name) {
const type = itype(name); const type = itype(name);
check(name); check(name);
if (type === 'string') if (type === 'string')
return await getModule(name); return getModule(name);
if (type === 'array') if (type === 'array')
return Promise.all(unaryMap(name, get)); return Promise.all(name.map(getFile));
} }
function check(name) { function check(name) {
@ -39,9 +31,9 @@ function check(name) {
throw Error('name could not be empty!'); throw Error('name could not be empty!');
} }
async function getModule(name) { function getModule(name) {
const regExpHTML = new RegExp(FILES_HTML + '|' + FILES_HTML_ROOT); const regExpHTML = RegExp(FILES_HTML + '|' + FILES_HTML_ROOT);
const regExpJSON = new RegExp(FILES_JSON); const regExpJSON = RegExp(FILES_JSON);
const isHTML = regExpHTML.test(name); const isHTML = regExpHTML.test(name);
const isJSON = regExpJSON.test(name); const isJSON = regExpJSON.test(name);
@ -53,12 +45,13 @@ async function getModule(name) {
return getConfig(); return getConfig();
const path = getPath(name, isHTML, isJSON); const path = getPath(name, isHTML, isJSON);
return getSystemFile(path); return getSystemFile(path);
} }
function getPath(name, isHTML, isJSON) { function getPath(name, isHTML, isJSON) {
let path; let path;
const regExp = new RegExp(FILES_HTML_ROOT); const regExp = RegExp(FILES_HTML_ROOT);
const isRoot = regExp.test(name); const isRoot = regExp.test(name);
if (isHTML) { if (isHTML) {
@ -76,8 +69,8 @@ function getPath(name, isHTML, isJSON) {
} }
function showError(name) { function showError(name) {
const str = 'Wrong file name: ' + name; const str = `Wrong file name: ${name}`;
const error = new Error(str); const error = Error(str);
throw error; throw error;
} }
@ -141,4 +134,3 @@ function getTimeoutOnce(time) {
}, time); }, time);
}; };
} }

View file

@ -1,20 +1,13 @@
/* global DOM */ /* global DOM */
import createElement from '@cloudcmd/create-element';
'use strict';
const createElement = require('@cloudcmd/create-element');
const Images = module.exports;
const LOADING = 'loading'; const LOADING = 'loading';
const HIDDEN = 'hidden'; const HIDDEN = 'hidden';
const ERROR = 'error'; const ERROR = 'error';
function getLoadingType() { const getLoadingType = () => isSVG() ? '-svg' : '-gif';
return isSVG() ? '-svg' : '-gif';
}
module.exports.get = getElement; export const get = getElement;
/** /**
* check SVG SMIL animation support * check SVG SMIL animation support
@ -43,7 +36,7 @@ function getElement() {
} }
/* Функция создаёт картинку загрузки */ /* Функция создаёт картинку загрузки */
module.exports.loading = () => { export const loading = () => {
const element = getElement(); const element = getElement();
const {classList} = element; const {classList} = element;
const loadingImage = LOADING + getLoadingType(); const loadingImage = LOADING + getLoadingType();
@ -55,7 +48,7 @@ module.exports.loading = () => {
}; };
/* Функция создаёт картинку ошибки загрузки */ /* Функция создаёт картинку ошибки загрузки */
module.exports.error = () => { export const error = () => {
const element = getElement(); const element = getElement();
const {classList} = element; const {classList} = element;
const loadingImage = LOADING + getLoadingType(); const loadingImage = LOADING + getLoadingType();
@ -66,15 +59,21 @@ module.exports.error = () => {
return element; return element;
}; };
module.exports.show = show; show.load = show;
module.exports.show.load = show; show.error = (text) => {
module.exports.show.error = error; const image = Images.error();
DOM.show(image);
image.title = text;
return image;
};
/** /**
* Function shows loading spinner * Function shows loading spinner
* position = {top: true}; * position = {top: true};
*/ */
function show(position, panel) { export function show(position, panel) {
const image = Images.loading(); const image = Images.loading();
const parent = image.parentElement; const parent = image.parentElement;
const refreshButton = DOM.getRefreshButton(panel); const refreshButton = DOM.getRefreshButton(panel);
@ -100,34 +99,25 @@ function show(position, panel) {
return image; return image;
} }
function error(text) {
const image = Images.error();
DOM.show(image);
image.title = text;
return image;
}
/** /**
* hide load image * hide load image
*/ */
module.exports.hide = () => { export function hide() {
const element = Images.get(); const element = Images.get();
DOM.hide(element); DOM.hide(element);
return Images; return Images;
}; }
module.exports.setProgress = (value, title) => { export const setProgress = (value, title) => {
const DATA = 'data-progress'; const DATA = 'data-progress';
const element = Images.get(); const element = Images.get();
if (!element) if (!element)
return Images; return Images;
element.setAttribute(DATA, value + '%'); element.setAttribute(DATA, `${value}%`);
if (title) if (title)
element.title = title; element.title = title;
@ -135,7 +125,7 @@ module.exports.setProgress = (value, title) => {
return Images; return Images;
}; };
module.exports.clearProgress = () => { export const clearProgress = () => {
const DATA = 'data-progress'; const DATA = 'data-progress';
const element = Images.get(); const element = Images.get();
@ -148,3 +138,12 @@ module.exports.clearProgress = () => {
return Images; return Images;
}; };
const Images = {
clearProgress,
setProgress,
show,
hide,
get,
error,
loading,
};

File diff suppressed because it is too large Load diff

52
client/dom/index.spec.js Normal file
View file

@ -0,0 +1,52 @@
import {test, stub} from 'supertape';
import {getCSSVar, goToDirectory} from '#dom';
globalThis.CloudCmd = {};
test('cloudcmd: client: dom: goToDirectory', async (t) => {
const path = '';
const changeDir = stub();
const prompt = stub().returns([null, path]);
await goToDirectory({
prompt,
changeDir,
});
t.calledWith(changeDir, [path]);
t.end();
});
test('cloudcmd: client: dom: getCSSVar', (t) => {
const body = {};
const getPropertyValue = stub().returns(0);
globalThis.getComputedStyle = stub().returns({
getPropertyValue,
});
const result = getCSSVar('hello', {
body,
});
delete globalThis.getComputedStyle;
t.notOk(result);
t.end();
});
test('cloudcmd: client: dom: getCSSVar: 1', (t) => {
const body = {};
const getPropertyValue = stub().returns(1);
globalThis.getComputedStyle = stub().returns({
getPropertyValue,
});
const result = getCSSVar('hello', {
body,
});
delete globalThis.getComputedStyle;
t.ok(result);
t.end();
});

View file

@ -1,216 +0,0 @@
'use strict';
/* global CloudCmd*/
const itype = require('itype');
const {promisify} = require('es6-promisify');
const {FS} = require('../../common/cloudfunc');
const Images = require('./images');
const load = require('./load');
const imgPosition = {
top: true,
};
module.exports._replaceHash = replaceHash;
function replaceHash(url) {
/*
* if we send ajax request -
* no need in hash so we escape #
*/
return url.replace(/#/g, '%23');
}
module.exports.delete = promisify((url, data, callback) => {
const isFunc = itype.function(data);
if (!callback && isFunc) {
callback = data;
data = null;
}
sendRequest({
method : 'DELETE',
url : FS + url,
data,
callback,
imgPosition : {
top: !!data,
},
});
});
module.exports.patch = promisify((url, data, callback) => {
const isFunc = itype.function(data);
if (!callback && isFunc) {
callback = data;
data = null;
}
sendRequest({
method: 'PATCH',
url: FS + url,
data,
callback,
imgPosition,
});
});
module.exports.write = promisify((url, data, callback) => {
const isFunc = itype.function(data);
if (!callback && isFunc) {
callback = data;
data = null;
}
sendRequest({
method: 'PUT',
url: FS + url,
data,
callback,
imgPosition,
});
});
module.exports.read = promisify((url, dataType, callback) => {
const notLog = !url.includes('?');
const isFunc = itype.function(dataType);
if (!callback && isFunc) {
callback = dataType;
dataType = 'text';
}
sendRequest({
method: 'GET',
url: FS + url,
callback,
notLog,
dataType,
});
});
module.exports.cp = promisify((data, callback) => {
sendRequest({
method: 'PUT',
url: '/cp',
data,
callback,
imgPosition,
});
});
module.exports.pack = promisify((data, callback) => {
sendRequest({
method: 'PUT',
url: '/pack',
data,
callback,
});
});
module.exports.extract = promisify((data, callback) => {
sendRequest({
method : 'PUT',
url : '/extract',
data,
callback,
});
});
module.exports.mv = promisify((data, callback) => {
sendRequest({
method : 'PUT',
url : '/mv',
data,
callback,
imgPosition,
});
});
module.exports.Config = {
read: promisify((callback) => {
sendRequest({
method: 'GET',
url: '/config',
callback,
imgPosition,
notLog: true,
});
}),
write: promisify((data, callback) => {
sendRequest({
method: 'PATCH',
url: '/config',
data,
callback,
imgPosition,
});
}),
};
module.exports.Markdown = {
read: promisify((url, callback) => {
sendRequest({
method: 'GET',
url: '/markdown' + url,
callback,
imgPosition,
notLog: true,
});
}),
render: promisify((data, callback) => {
sendRequest({
method: 'PUT',
url: '/markdown',
data,
callback,
imgPosition,
notLog: true,
});
}),
};
function sendRequest(params) {
const p = params;
const {prefixURL} = CloudCmd;
p.url = prefixURL + p.url;
p.url = encodeURI(p.url);
p.url = replaceHash(p.url);
load.ajax({
method : p.method,
url : p.url,
data : p.data,
dataType : p.dataType,
error : (jqXHR) => {
const response = jqXHR.responseText;
const {
statusText,
status,
} = jqXHR;
const text = status === 404 ? response : statusText;
p.callback(Error(text));
},
success: (data) => {
Images.hide();
if (!p.notLog)
CloudCmd.log(data);
p.callback(null, data);
},
});
}

166
client/dom/io/index.js Normal file
View file

@ -0,0 +1,166 @@
import {FS} from '#common/cloudfunc';
import {sendRequest as _sendRequest} from './send-request.js';
const {assign} = Object;
const imgPosition = {
top: true,
};
export const remove = async (url, data, overrides = {}) => {
const {
sendRequest = _sendRequest,
} = overrides;
const request = {
method: 'DELETE',
url: FS + url,
imgPosition: {
top: Boolean(data),
},
};
if (data)
assign(request, {
data,
url: `${request.url}?files`,
});
return await sendRequest(request);
};
export const patch = async (url, data) => {
return await _sendRequest({
method: 'PATCH',
url: FS + url,
data,
imgPosition,
});
};
export const write = async (url, data) => {
return await _sendRequest({
method: 'PUT',
url: FS + url,
data,
imgPosition,
});
};
export const createDirectory = async (url, overrides = {}) => {
const {
sendRequest = _sendRequest,
} = overrides;
return await sendRequest({
method: 'PUT',
url: `${FS}${url}?dir`,
imgPosition,
});
};
export const read = async (url, dataType = 'text') => {
const notLog = !url.includes('?');
return await _sendRequest({
method: 'GET',
url: FS + url,
notLog,
dataType,
});
};
export const copy = async (from, to, names) => {
return await _sendRequest({
method: 'PUT',
url: '/copy',
data: {
from,
to,
names,
},
imgPosition,
});
};
export const pack = async (data) => {
return await _sendRequest({
method: 'PUT',
url: '/pack',
data,
});
};
export const extract = async (data) => {
return await _sendRequest({
method: 'PUT',
url: '/extract',
data,
});
};
export const move = async (from, to, names) => {
return await _sendRequest({
method: 'PUT',
url: '/move',
data: {
from,
to,
names,
},
imgPosition,
});
};
export const rename = async (from, to) => {
return await _sendRequest({
method: 'PUT',
url: '/rename',
data: {
from,
to,
},
imgPosition,
});
};
export const Config = {
read: async () => {
return await _sendRequest({
method: 'GET',
url: '/config',
imgPosition,
notLog: true,
});
},
write: async (data) => {
return await _sendRequest({
method: 'PATCH',
url: '/config',
data,
imgPosition,
});
},
};
export const Markdown = {
read: async (url) => {
return await _sendRequest({
method: 'GET',
url: `/markdown${url}`,
imgPosition,
notLog: true,
});
},
render: async (data) => {
return await _sendRequest({
method: 'PUT',
url: '/markdown',
data,
imgPosition,
notLog: true,
});
},
};

View file

@ -0,0 +1,61 @@
import {test, stub} from 'supertape';
import * as io from './index.js';
test('client: dom: io', (t) => {
const sendRequest = stub();
io.createDirectory('/hello', {
sendRequest,
});
const expected = {
imgPosition: {
top: true,
},
method: 'PUT',
url: '/fs/hello?dir',
};
t.calledWith(sendRequest, [expected]);
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

@ -0,0 +1,48 @@
/* global CloudCmd */
import {promisify} from 'es6-promisify';
import * as Images from '#dom/images';
import * as load from '#dom/load';
export const sendRequest = promisify((params, callback) => {
const p = params;
const {prefixURL} = CloudCmd;
p.url = prefixURL + p.url;
p.url = encodeURI(p.url);
p.url = replaceHash(p.url);
load.ajax({
method: p.method,
url: p.url,
data: p.data,
dataType: p.dataType,
error: (jqXHR) => {
const response = jqXHR.responseText;
const {statusText, status} = jqXHR;
const text = status === 404 ? response : statusText;
callback(Error(text));
},
success: (data) => {
Images.hide();
if (!p.notLog)
CloudCmd.log(data);
callback(null, data);
},
});
});
export const _replaceHash = replaceHash;
function replaceHash(url) {
/*
* if we send ajax request -
* no need in hash so we escape #
*/
return url.replace(/#/g, '%23');
}

View file

@ -1,15 +1,11 @@
'use strict'; import {test} from 'supertape';
import {_replaceHash} from './send-request.js';
const test = require('supertape');
const io = require('./io');
test('cloudcmd: client: io: replaceHash', (t) => { test('cloudcmd: client: io: replaceHash', (t) => {
const {_replaceHash} = io;
const url = '/hello/####world'; const url = '/hello/####world';
const result = _replaceHash(url); const result = _replaceHash(url);
const expected = '/hello/%23%23%23%23world'; const expected = '/hello/%23%23%23%23world';
t.equal(result, expected, 'should equal'); t.equal(result, expected);
t.end(); t.end();
}); });

View file

@ -1,60 +1,58 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
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';
const rendy = require('rendy'); export const loadRemote = callbackify(async (name, options) => {
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 callback(); return;
Files.get('modules').then(async (modules) => { const modules = await Files.get('modules');
const online = config('online') && navigator.onLine;
const module = findObjByNameInArr(modules.remote, name); const online = config('online') && navigator.onLine;
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);
const isArray = itype.array(module.local); if (!e)
const {version} = module; return;
}
let remoteTmpls;
let local; await load.parallel(localURL);
});
if (isArray) {
remoteTmpls = module.remote;
local = module.local;
} else {
remoteTmpls = [module.remote];
local = [module.local];
}
const localURL = local.map((url) => {
return 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,12 +1,8 @@
'use strict'; import itype from 'itype';
import jonny from 'jonny';
const itype = require('itype'); import Emitify from 'emitify';
const jonny = require('jonny'); import exec from 'execon';
const Emitify = require('emitify'); import * as Images from '#dom/images';
const exec = require('execon');
const Images = require('./images');
module.exports.getIdBySrc = getIdBySrc;
/** /**
* Function gets id by src * Function gets id by src
@ -14,17 +10,18 @@ module.exports.getIdBySrc = getIdBySrc;
* *
* Example: http://domain.com/1.js -> 1_js * Example: http://domain.com/1.js -> 1_js
*/ */
function getIdBySrc(src) { export function getIdBySrc(src) {
const isStr = itype.string(src); const isStr = itype.string(src);
if (!isStr) if (!isStr)
return; return;
if (~src.indexOf(':')) if (src.includes(':'))
src += '-join'; src += '-join';
const num = src.lastIndexOf('/') + 1; const num = src.lastIndexOf('/') + 1;
const sub = src.substr(src, num); const sub = src.substr(src, num);
const id = src const id = src
.replace(sub, '') .replace(sub, '')
.replace(/\./g, '-'); .replace(/\./g, '-');
@ -37,15 +34,15 @@ function getIdBySrc(src) {
* *
* @param params * @param params
*/ */
module.exports.ajax = (params) => { export const 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);
const isArrayBuf = itype(p.data) === 'arraybuffer'; const isArrayBuf = itype(p.data) === 'arraybuffer';
const type = p.type || p.method || 'GET'; const type = p.type || p.method || 'GET';
const {
headers = {}, const {headers = {}} = p;
} = p;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open(type, p.url, true); xhr.open(type, p.url, true);
@ -63,7 +60,7 @@ module.exports.ajax = (params) => {
if (!isArrayBuf && isObject || isArray) if (!isArrayBuf && isObject || isArray)
data = jonny.stringify(p.data); data = jonny.stringify(p.data);
else else
data = p.data; ({data} = p);
xhr.onreadystatechange = (event) => { xhr.onreadystatechange = (event) => {
const xhr = event.target; const xhr = event.target;
@ -81,7 +78,7 @@ module.exports.ajax = (params) => {
return exec(p.error, xhr); return exec(p.error, xhr);
const notText = p.dataType !== 'text'; const notText = p.dataType !== 'text';
const isContain = ~type.indexOf(TYPE_JSON); const isContain = type.includes(TYPE_JSON);
let data = xhr.response; let data = xhr.response;
@ -94,12 +91,11 @@ module.exports.ajax = (params) => {
xhr.send(data); xhr.send(data);
}; };
module.exports.put = (url, body) => { export const put = (url, body) => {
const emitter = Emitify(); const emitter = Emitify();
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
url = encodeURI(url) url = encodeURI(url).replace(/#/g, '#');
.replace('#', '%23');
xhr.open('put', url, true); xhr.open('put', url, true);
@ -120,8 +116,10 @@ module.exports.put = (url, body) => {
if (!over) if (!over)
return; return;
if (xhr.status === OK) if (xhr.status === OK) {
emitter.emit('progress', 100);
return emitter.emit('end'); return emitter.emit('end');
}
const error = Error(xhr.responseText); const error = Error(xhr.responseText);
emitter.emit('error', error); emitter.emit('error', error);
@ -131,4 +129,3 @@ module.exports.put = (url, body) => {
return emitter; return emitter;
}; };

View file

@ -1,23 +1,27 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
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';
const capitalize = require('just-capitalize'); export default async (current, overrides = {}) => {
const {
const Dialog = require('../dialog'); refresh = CloudCmd.refresh,
const Storage = require('../storage'); Dialog = _Dialog,
const RESTful = require('../rest'); currentFile = _currentFile,
const { } = overrides;
isCurrentFile,
getCurrentName, const {
getCurrentFile, isCurrentFile,
getCurrentByName, getCurrentName,
getCurrentType, getCurrentFile,
getCurrentDirPath, getCurrentByName,
setCurrentName, getCurrentType,
} = require('../current-file'); getCurrentDirPath,
setCurrentName,
module.exports = async (current) => { } = currentFile;
if (!isCurrentFile(current)) if (!isCurrentFile(current))
current = getCurrentFile(); current = getCurrentFile();
@ -46,12 +50,11 @@ module.exports = async (current) => {
return; return;
const dirPath = getCurrentDirPath(); const dirPath = getCurrentDirPath();
const files = {
from : dirPath + from,
to : dirPath + to,
};
const [e] = await RESTful.mv(files); const fromFull = `${dirPath}${from}`;
const toFull = `${dirPath}${to}`;
const [e] = await RESTful.rename(fromFull, toFull);
if (e) if (e)
return; return;
@ -59,5 +62,5 @@ module.exports = async (current) => {
setCurrentName(to, current); setCurrentName(to, current);
Storage.remove(dirPath); Storage.remove(dirPath);
CloudCmd.refresh(); refresh();
}; };

View file

@ -1,37 +1,26 @@
'use strict'; import {test, stub} from 'supertape';
import renameCurrent from './rename-current.js';
const test = require('supertape');
const stub = require('@cloudcmd/stub');
const mockRequire = require('mock-require');
const {reRequire} = mockRequire;
test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => { test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
const current = {}; const current = {};
const isCurrentFile = stub(); const isCurrentFile = stub();
mockRequire('../dialog', stubDialog()); const currentFile = stubCurrentFile({
mockRequire('../current-file', stubCurrentFile({
isCurrentFile, isCurrentFile,
})); });
const renameCurrent = reRequire('./rename-current'); await renameCurrent(current, {
await renameCurrent(current); Dialog: stubDialog(),
currentFile,
});
t.ok(isCurrentFile.calledWith(current), 'should call isCurrentFile'); t.calledWith(isCurrentFile, [current], 'should call isCurrentFile');
t.end(); t.end();
}); });
test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => { test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
const current = {}; const current = {};
const name = 'hello'; const name = 'hello';
const {CloudCmd} = global;
const CloudCmdStub = {
refresh: stub(),
};
global.CloudCmd = CloudCmdStub;
const prompt = stub().returns([null, name]); const prompt = stub().returns([null, name]);
const confirm = stub().returns([true]); const confirm = stub().returns([true]);
@ -39,23 +28,24 @@ test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
const getCurrentByName = stub().returns(current); const getCurrentByName = stub().returns(current);
const getCurrentType = stub().returns('directory'); const getCurrentType = stub().returns('directory');
mockRequire('../dialog', stubDialog({ const Dialog = stubDialog({
confirm, confirm,
prompt, prompt,
})); });
mockRequire('../current-file', stubCurrentFile({ const currentFile = stubCurrentFile({
getCurrentByName, getCurrentByName,
getCurrentType, getCurrentType,
})); });
const renameCurrent = reRequire('./rename-current'); await renameCurrent(null, {
await renameCurrent(); Dialog,
currentFile,
});
const expected = 'Directory "hello" already exists. Proceed?'; const expected = 'Directory "hello" already exists. Proceed?';
global.CloudCmd = CloudCmd;
t.ok(confirm.calledWith(expected), 'should call confirm'); t.calledWith(confirm, [expected], 'should call confirm');
t.end(); t.end();
}); });
@ -94,4 +84,3 @@ const stubCurrentFile = (fns = {}) => {
setCurrentName, setCurrentName,
}; };
}; };

View file

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

View file

@ -1,22 +1,18 @@
'use strict'; import {alert, prompt} from '#dom/dialog';
import {getRegExp} from '#common/util';
import {getCurrentName} from './current-file.js';
import {
isSelected,
toggleSelectedFile,
} from './cmd.js';
let SelectType = '*.*'; let SelectType = '*.*';
const {getRegExp} = require('../../common/util'); export const selectByPattern = async (msg, files) => {
const {
alert,
prompt,
} = require('./dialog');
const DOM = require('.');
module.exports = async (msg, files) => {
if (!files) if (!files)
return; return;
const allMsg = `Specify file type for ${msg} selection`; const allMsg = `Specify file type for ${msg} selection`;
/* eslint require-atomic-updates: 0 */
const [cancel, type] = await prompt(allMsg, SelectType); const [cancel, type] = await prompt(allMsg, SelectType);
if (cancel) if (cancel)
@ -28,24 +24,23 @@ module.exports = async (msg, files) => {
let matches = 0; let matches = 0;
for (const current of files) { for (const current of files) {
const name = DOM.getCurrentName(current); const name = getCurrentName(current);
if (name === '..' || !regExp.test(name)) if (name === '..' || !regExp.test(name))
continue; continue;
++matches; ++matches;
let isSelected = DOM.isSelected(current); let selected = isSelected(current);
const shouldSel = msg === 'expand'; const shouldSel = msg === 'expand';
if (shouldSel) if (shouldSel)
isSelected = !isSelected; selected = !selected;
if (isSelected) if (selected)
DOM.toggleSelectedFile(current); toggleSelectedFile(current);
} }
if (!matches) if (!matches)
alert('No matches found!'); alert('No matches found!');
}; };

View file

@ -1,38 +1,26 @@
'use strict';
const tryCatch = require('try-catch');
const {parse, stringify} = JSON; const {parse, stringify} = JSON;
const isObj = (a) => typeof a === 'object';
module.exports.set = (name, data) => { export const set = (name, data) => {
const primitive = !isObj(data) ? data : stringify(data); localStorage.setItem(name, data);
localStorage.setItem(name, primitive);
}; };
module.exports.get = async (name) => { export const setJson = (name, data) => {
localStorage.setItem(name, stringify(data));
};
export const get = (name) => {
return localStorage.getItem(name);
};
export const getJson = (name) => {
const data = localStorage.getItem(name); const data = localStorage.getItem(name);
const [, result = data] = tryCatch(parse, data); return parse(data);
return result;
}; };
module.exports.clear = () => { export const clear = () => {
localStorage.clear(); localStorage.clear();
}; };
module.exports.remove = (item) => { export const remove = (item) => {
localStorage.removeItem(item); localStorage.removeItem(item);
}; };
module.exports.removeMatch = (string) => {
const reg = RegExp('^' + string + '.*$');
const test = (a) => reg.test(a);
const remove = (a) => localStorage.removeItem(a);
Object.keys(localStorage)
.filter(test)
.forEach(remove);
};

105
client/dom/storage.spec.js Normal file
View file

@ -0,0 +1,105 @@
import {test, stub} from 'supertape';
import * as storage from '#dom/storage';
const {stringify} = JSON;
test('cloudcmd: client: storage: set', async (t) => {
const {localStorage} = globalThis;
const setItem = stub();
globalThis.localStorage = {
setItem,
};
await storage.set('hello', 'world');
globalThis.localStorage = localStorage;
t.calledWith(setItem, ['hello', 'world'], 'should call setItem');
t.end();
});
test('cloudcmd: client: storage: get', async (t) => {
const {localStorage} = globalThis;
const getItem = stub().returns('world');
globalThis.localStorage = {
getItem,
};
const result = await storage.get('hello');
globalThis.localStorage = localStorage;
t.equal(result, 'world');
t.end();
});
test('cloudcmd: client: storage: getJson', async (t) => {
const {localStorage} = globalThis;
const expected = {
hello: 'world',
};
const getItem = stub().returns(stringify(expected));
globalThis.localStorage = {
getItem,
};
const result = await storage.getJson('hello');
globalThis.localStorage = localStorage;
t.deepEqual(result, expected);
t.end();
});
test('cloudcmd: client: storage: setJson', async (t) => {
const {localStorage} = globalThis;
const data = {
hello: 'world',
};
const expected = stringify(data);
const setItem = stub();
globalThis.localStorage = {
setItem,
};
await storage.setJson('hello', data);
globalThis.localStorage = localStorage;
t.calledWith(setItem, ['hello', expected]);
t.end();
});
test('cloudcmd: client: storage: remove', async (t) => {
const {localStorage} = globalThis;
const removeItem = stub();
globalThis.localStorage = {
removeItem,
};
await storage.remove('hello');
globalThis.localStorage = localStorage;
t.calledWith(removeItem, ['hello'], 'should call removeItem');
t.end();
});
test('cloudcmd: client: storage: clear', async (t) => {
const {localStorage} = globalThis;
const clear = stub();
globalThis.localStorage = {
clear,
};
await storage.clear();
globalThis.localStorage = localStorage;
t.calledWithNoArgs(clear, 'should call clear');
t.end();
});

View file

@ -1,25 +1,19 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
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';
const {eachSeries} = require('execon');
const wraptile = require('wraptile');
const load = require('./load');
const Images = require('./images');
const {alert} = require('./dialog');
const {FS} = require('../../common/cloudfunc');
const onEnd = wraptile(_onEnd);
const loadFile = wraptile(_loadFile); const loadFile = wraptile(_loadFile);
const onEnd = wraptile(_onEnd);
const {getCurrentDirPath: getPathWhenRootEmpty} = require('.'); export const uploadFiles = (dir, files) => {
module.exports = (dir, files) => {
if (!files) { if (!files) {
files = dir; files = dir;
dir = getPathWhenRootEmpty(); dir = getCurrentDirPath();
} }
const n = files.length; const n = files.length;
@ -55,7 +49,8 @@ function _loadFile(dir, n, file, callback) {
++i; ++i;
load.put(api + path, file) load
.put(api + path, file)
.on('error', showError) .on('error', showError)
.on('end', callback) .on('end', callback)
.on('progress', (count) => { .on('progress', (count) => {
@ -70,4 +65,3 @@ function _loadFile(dir, n, file, callback) {
function showError({message}) { function showError({message}) {
alert(message); alert(message);
} }

View file

@ -1,40 +1,13 @@
'use strict';
/* global DOM */ /* global DOM */
const Info = DOM.CurrentInfo;
/** /**
* Функция генерирует JSON из html-таблицы файлов и * Функция генерирует JSON из html-таблицы файлов и
* используеться при первом заходе в корень * используеться при первом заходе в корень
*/ */
module.exports = () => { export const getJsonFromFileTable = () => {
const Info = DOM.CurrentInfo;
const path = DOM.getCurrentDirPath(); const path = DOM.getCurrentDirPath();
const infoFiles = Info.files || []; const infoFiles = Info.files || [];
const notParent = (current) => {
const name = DOM.getCurrentName(current);
return name !== '..';
};
const parse = (current) => {
const name = DOM.getCurrentName(current);
const size = DOM.getCurrentSize(current);
const owner = DOM.getCurrentOwner(current);
const mode = DOM.getCurrentMode(current);
const date = DOM.getCurrentDate(current);
const type = DOM.getCurrentType(current);
return {
name,
size,
mode,
owner,
date,
type,
};
};
const files = infoFiles const files = infoFiles
.filter(notParent) .filter(notParent)
.map(parse); .map(parse);
@ -47,3 +20,25 @@ module.exports = () => {
return fileTable; return fileTable;
}; };
const notParent = (current) => {
const name = DOM.getCurrentName(current);
return name !== '..';
};
const parse = (current) => {
const name = DOM.getCurrentName(current);
const size = DOM.getCurrentSize(current);
const owner = DOM.getCurrentOwner(current);
const mode = DOM.getCurrentMode(current);
const date = DOM.getCurrentDate(current);
const type = DOM.getCurrentType(current);
return {
name,
size,
mode,
owner,
date,
type,
};
};

15
client/key/binder.js Normal file
View file

@ -0,0 +1,15 @@
export const createBinder = () => {
let binded = false;
return {
isBind() {
return binded;
},
setBind() {
binded = true;
},
unsetBind() {
binded = false;
},
};
};

28
client/key/binder.spec.js Normal file
View file

@ -0,0 +1,28 @@
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();
});

File diff suppressed because it is too large Load diff

56
client/key/index.spec.js Normal file
View file

@ -0,0 +1,56 @@
import autoGlobals from 'auto-globals';
import supertape from 'supertape';
import {ESC} from './key.js';
import {Key, _listener} from './index.js';
import {getDOM, getCloudCmd} from './vim/globals.fixture.js';
const test = autoGlobals(supertape);
const {stub} = supertape;
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
test('cloudcmd: client: key: enable vim', async (t) => {
const vim = stub();
const config = stub().returns(true);
const _config = stub();
const event = {
keyCode: ESC,
key: 'Escape',
altKey: false,
};
Key.setBind();
await _listener(event, {
vim,
config,
_config,
switchKey: stub(),
});
t.calledWith(vim, ['Escape', event]);
t.end();
});
test('cloudcmd: client: key: disable vim', async (t) => {
const _config = stub();
const config = stub();
const event = {
keyCode: ESC,
key: 'Escape',
altKey: false,
};
Key.setBind();
await _listener(event, {
config,
_config,
switchKey: stub(),
});
t.calledWith(_config, ['vim', true]);
t.end();
});

View file

@ -1,81 +1,58 @@
'use strict'; export const BACKSPACE = 8;
export const TAB = 9;
module.exports = { export const ENTER = 13;
BACKSPACE : 8, export const CAPSLOCK = 20;
TAB : 9, export const ESC = 27;
ENTER : 13, export const SPACE = 32;
ESC : 27, export const PAGE_UP = 33;
export const PAGE_DOWN = 34;
SPACE : 32, export const END = 35;
PAGE_UP : 33, export const HOME = 36;
PAGE_DOWN : 34, export const LEFT = 37;
END : 35, export const UP = 38;
HOME : 36, export const RIGHT = 39;
export const DOWN = 40;
LEFT : 37, export const INSERT = 45;
UP : 38, export const DELETE = 46;
RIGHT : 39, export const ZERO = 48;
DOWN : 40, export const SEMICOLON = 52;
export const A = 65;
INSERT : 45, export const C = 67;
DELETE : 46, export const D = 68;
export const G = 71;
ZERO : 48, export const J = 74;
export const K = 75;
SEMICOLON : 52, export const L = 76;
export const M = 77;
COLON : 54, export const O = 79;
export const P = 80;
A : 65, export const Q = 81;
export const R = 82;
C : 67, export const S = 83;
D : 68, export const T = 84;
export const U = 85;
G : 71, export const V = 86;
export const X = 88;
J : 74, export const Z = 90;
K : 75, export const INSERT_MAC = 96;
export const ASTERISK = 106;
M : 77, export const PLUS = 107;
export const MINUS = 109;
O : 79, export const F1 = 112;
P : 80, export const F2 = 113;
Q : 81, export const F3 = 114;
R : 82, export const F4 = 115;
S : 83, export const F5 = 116;
T : 84, export const F6 = 117;
U : 85, export const F7 = 118;
export const F8 = 119;
V : 86, export const F9 = 120;
export const F10 = 121;
X : 88, export const COLON = 186;
export const EQUAL = 187;
Z : 90, export const HYPHEN = 189;
export const DOT = 190;
INSERT_MAC : 96, export const SLASH = 191;
export const TRA = 192;
ASTERISK : 106, export const BACKSLASH = 220;
PLUS : 107, export const BRACKET_CLOSE = 221;
MINUS : 109,
F1 : 112,
F2 : 113,
F3 : 114,
F4 : 115,
F5 : 116,
F6 : 117,
F7 : 118,
F8 : 119,
F9 : 120,
F10 : 121,
EQUAL : 187,
HYPHEN : 189,
DOT : 190,
SLASH : 191,
TRA : 192, /* Typewritten Reverse Apostrophe (`) */
BACKSLASH : 220,
BRACKET_CLOSE: 221,
};

View file

@ -1,18 +1,15 @@
/* global DOM */ /* global DOM */
import {escapeRegExp} from '#common/util';
'use strict'; export default function setCurrentByChar(char, charStore) {
const Info = DOM.CurrentInfo;
const Info = DOM.CurrentInfo;
const {escapeRegExp} = require('../../common/util');
module.exports = function setCurrentByChar(char, charStore) {
let firstByName; let firstByName;
let skipCount = 0; let skipCount = 0;
let setted = false; let set = false;
let i = 0; let i = 0;
const escapeChar = escapeRegExp(char); const escapeChar = escapeRegExp(char);
const regExp = new RegExp('^' + escapeChar + '.*$', 'i'); const regExp = new RegExp(`^${escapeChar}.*$`, 'i');
const {files} = Info; const {files} = Info;
const chars = charStore(); const chars = charStore();
const n = chars.length; const n = chars.length;
@ -31,12 +28,14 @@ module.exports = function setCurrentByChar(char, charStore) {
const isTest = (a) => regExp.test(a); const isTest = (a) => regExp.test(a);
const isRoot = (a) => a === '..'; const isRoot = (a) => a === '..';
const not = (f) => (a) => !f(a); const not = (f) => (a) => !f(a);
const setCurrent = (name) => { const setCurrent = (name) => {
const byName = DOM.getCurrentByName(name); const byName = DOM.getCurrentByName(name);
if (!skipCount) { if (!skipCount) {
setted = true; set = true;
DOM.setCurrentFile(byName); DOM.setCurrentFile(byName);
return true; return true;
} }
@ -51,9 +50,8 @@ module.exports = function setCurrentByChar(char, charStore) {
.filter(not(isRoot)) .filter(not(isRoot))
.some(setCurrent); .some(setCurrent);
if (!setted) { if (!set) {
DOM.setCurrentFile(firstByName); DOM.setCurrentFile(firstByName);
charStore([char]); charStore([char]);
} }
}; }

View file

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

View file

@ -1,16 +1,14 @@
'use strict'; import test from 'supertape';
import {getDOM} from './globals.fixture.js';
const test = require('supertape'); import {
const dir = './';
const {getDOM} = require('./globals.fixture');
global.DOM = getDOM();
const {
_next, _next,
_previous, _previous,
} = require(dir + 'find'); find,
findNext,
findPrevious,
} from './find.js';
globalThis.DOM = getDOM();
test('cloudcmd: client: vim: _next', (t) => { test('cloudcmd: client: vim: _next', (t) => {
const result = _next(1, 2); const result = _next(1, 2);
@ -19,6 +17,13 @@ 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);
@ -26,3 +31,25 @@ test('cloudcmd: client: vim: _previous', (t) => {
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,13 +1,12 @@
'use strict'; const noop = () => {};
module.exports.getDOM = () => { export const getDOM = () => {
const prompt = Promise.resolve.bind(Promise); const prompt = Promise.resolve.bind(Promise);
const CurrentInfo = { const CurrentInfo = {
element: {}, element: {},
files: [], files: [],
}; };
const noop = () => {};
const Buffer = { const Buffer = {
copy: noop, copy: noop,
paste: noop, paste: noop,
@ -28,16 +27,20 @@ module.exports.getDOM = () => {
getCurrentName: noop, getCurrentName: noop,
setCurrentByName: noop, setCurrentByName: noop,
toggleSelectedFile: noop, toggleSelectedFile: noop,
prompNewDirectory: noop,
promptNewFile: noop,
}; };
}; };
module.exports.getCloudCmd = () => { export const getCloudCmd = () => {
const show = () => {}; const show = () => {};
return { return {
Operation: { Operation: {
show, show,
}, },
config: noop,
_config: noop,
}; };
}; };

View file

@ -1,102 +1,167 @@
'use strict'; import vim from './vim.js';
import * as finder from './find.js';
import {
setCurrent,
selectFileNotParent,
} from './set-current.js';
/* global CloudCmd */ export default (key, event, overrides = {}) => {
/* global DOM */ const defaults = {
...globalThis.DOM,
const vim = require('./vim'); ...globalThis.CloudCmd,
const finder = require('./find'); };
const Info = DOM.CurrentInfo; const deps = {
const {Dialog} = DOM; ...defaults,
...overrides,
module.exports = async (key, event) => { };
const operations = getOperations(event);
await vim(key, operations); const operations = getOperations(event, deps);
vim(key, operations, deps);
}; };
const getOperations = (event) => { const getOperations = (event, deps) => {
const {
Info = globalThis.DOM.CurrentInfo,
CloudCmd = globalThis.CloudCmd,
Operation,
unselectFiles,
setCurrentFile,
setCurrentByName,
getCurrentName,
prompt = globalThis.DOM.Dialog.prompt,
preventDefault = event?.preventDefault?.bind(event),
stopImmediatePropagation = event?.preventDefault?.bind(event),
promptNewFile = globalThis.DOM.promptNewFile,
toggleSelectedFile,
Buffer = {},
createFindNext = _createFindNext,
createFindPrevious = _createFindPrevious,
createMakeFile = _createMakeFile,
renameCurrent,
} = deps;
return { return {
escape: DOM.unselectFiles, makeFile: createMakeFile({
promptNewFile,
preventDefault,
stopImmediatePropagation,
}),
findNext: createFindNext({
setCurrentByName,
}),
findPrevious: createFindPrevious({
setCurrentByName,
}),
escape: unselectFiles,
rename: () => {
event.preventDefault();
renameCurrent();
},
remove: () => { remove: () => {
CloudCmd.Operation.show('delete'); Operation.show('delete');
}, },
operationCopy: () => {
event.preventDefault();
Operation.show('copy');
},
operationMove: () => {
event.preventDefault();
Operation.show('move');
},
makeDirectory: () => {
event.stopImmediatePropagation();
event.preventDefault();
globalThis.DOM.promptNewDir();
},
terminal: () => {
CloudCmd.Terminal.show();
},
edit: () => {
CloudCmd.EditFileVim.show();
},
copy: () => { copy: () => {
DOM.Buffer.copy(); Buffer.copy();
DOM.unselectFiles(); unselectFiles();
}, },
select: () => { select: () => {
const current = Info.element; const current = Info.element;
DOM.toggleSelectedFile(current); toggleSelectedFile(current);
}, },
paste: DOM.Buffer.paste,
paste: Buffer.paste,
moveNext: ({count, isVisual, isDelete}) => { moveNext: ({count, isVisual, isDelete}) => {
setCurrent('next', { setCurrent('next', {
count, count,
isVisual, isVisual,
isDelete, isDelete,
}, {
Info,
setCurrentFile,
unselectFiles,
Operation,
}); });
}, },
movePrevious: ({count, isVisual, isDelete}) => { movePrevious: ({count, isVisual, isDelete}) => {
setCurrent('previous', { setCurrent('previous', {
count, count,
isVisual, isVisual,
isDelete, isDelete,
}, {
Info,
setCurrentFile,
unselectFiles,
Operation,
}); });
}, },
find: async () => { find: async () => {
event.preventDefault(); preventDefault();
const [, value] = await Dialog.prompt('Find', ''); const [, value] = await prompt('Find', '');
if (!value) if (!value)
return; return;
const names = Info.files.map(DOM.getCurrentName); const names = Info.files.map(getCurrentName);
const [result] = finder.find(value, names); const [result] = finder.find(value, names);
DOM.setCurrentByName(result); setCurrentByName(result);
},
findNext: () => {
const name = finder.findNext();
DOM.setCurrentByName(name);
},
findPrevious: () => {
const name = finder.findPrevious();
DOM.setCurrentByName(name);
}, },
}; };
}; };
module.exports.selectFile = selectFile; export const selectFile = selectFileNotParent;
function selectFile(current) { const _createFindPrevious = (overrides = {}) => () => {
const name = DOM.getCurrentName(current); const {setCurrentByName} = overrides;
const name = finder.findPrevious();
if (name === '..') setCurrentByName(name);
return; };
DOM.selectFile(current);
}
function setCurrent(sibling, {count, isVisual, isDelete}) { const _createFindNext = (overrides = {}) => () => {
let current = Info.element; const {setCurrentByName} = overrides;
const select = isVisual ? selectFile : DOM.unselectFile; const name = finder.findNext();
select(current); setCurrentByName(name);
};
const position = `${sibling}Sibling`;
for (let i = 0; i < count; i++) {
const next = current[position];
if (!next)
break;
current = next;
select(current);
}
DOM.setCurrentFile(current);
if (isDelete)
CloudCmd.Operation.show('delete');
}
const _createMakeFile = (overrides = {}) => () => {
const {
promptNewFile,
stopImmediatePropagation,
preventDefault,
} = overrides;
stopImmediatePropagation();
preventDefault();
promptNewFile();
};

View file

@ -1,46 +1,35 @@
'use strict'; import {test, stub} from 'supertape';
import {getDOM, getCloudCmd} from './globals.fixture.js';
import vim, {selectFile as vimSelectFile} from './index.js';
import * as finder from './find.js';
const {join} = require('path'); globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const test = require('supertape'); const {assign} = Object;
const stub = require('@cloudcmd/stub'); const {DOM} = globalThis;
const mockRequire = require('mock-require');
const {reRequire, stopAll} = mockRequire;
const dir = '../';
const pathVim = join(dir, 'vim');
const pathFind = join(dir, 'vim', 'find');
const {
getDOM,
getCloudCmd,
} = require('./globals.fixture');
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
const {DOM} = global;
const {Buffer} = DOM; const {Buffer} = DOM;
const vim = require(pathVim);
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 unselectFiles = stub();
const Info = {
element,
}; };
const setCurrentFile = stub(); vim('j', {}, {
Info,
setCurrentFile,
unselectFiles,
});
global.DOM.CurrentInfo.element = element; t.calledWith(setCurrentFile, [element], 'should set next file');
global.DOM.setCurrentFile = setCurrentFile;
vim('j', {});
t.ok(setCurrentFile.calledWith(element), 'should set next file');
t.end(); t.end();
}); });
test('cloudcmd: client: key: set next file current', (t) => { test('cloudcmd: client: key: set next file current: j', async (t) => {
const nextSibling = 'hello'; const nextSibling = 'hello';
const element = { const element = {
nextSibling, nextSibling,
@ -48,17 +37,21 @@ test('cloudcmd: client: key: set next file current', (t) => {
const setCurrentFile = stub(); const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.setCurrentFile = setCurrentFile; element,
};
vim('j', {}); await vim('j', {}, {
Info,
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file'); setCurrentFile,
unselectFiles: stub(),
});
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
t.end(); t.end();
}); });
test('cloudcmd: client: key: set next file current', (t) => { test('cloudcmd: client: key: set next file current: mjj', (t) => {
const nextSibling = 'hello'; const nextSibling = 'hello';
const element = { const element = {
nextSibling, nextSibling,
@ -66,15 +59,21 @@ test('cloudcmd: client: key: set next file current', (t) => {
const setCurrentFile = stub(); const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.setCurrentFile = setCurrentFile; element,
};
vim('m', {}); const deps = {
vim('j', {}); Info,
vim('j', {}); setCurrentFile,
unselectFiles: stub(),
};
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file'); vim('m', {}, deps);
vim('j', {}, deps);
vim('j', {}, deps);
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
t.end(); t.end();
}); });
@ -86,38 +85,44 @@ test('cloudcmd: client: key: set next file current: g', (t) => {
const setCurrentFile = stub(); const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.setCurrentFile = setCurrentFile; element,
};
vim('g', {}); const deps = {
vim('j', {}); Info,
setCurrentFile,
unselectFiles: stub(),
};
t.ok(setCurrentFile.calledWith(nextSibling), 'should ignore g'); vim('g', {}, deps);
vim('j', {}, deps);
t.calledWith(setCurrentFile, [nextSibling], 'should ignore g');
t.end(); t.end();
}); });
test('cloudcmd: client: key: set +2 file current', (t) => { test('cloudcmd: client: key: set +2 file current', (t) => {
const last = {}; const last = {};
const nextSibling = {
nextSibling: last,
};
const element = {
nextSibling,
};
const setCurrentFile = stub(); const setCurrentFile = stub();
const element = {};
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.setCurrentFile = setCurrentFile; element,
};
const deps = {
setCurrentFile,
Info,
unselectFiles: stub(),
};
const event = {}; const event = {};
vim('2', event); vim('2', event, deps);
vim('j', event); vim('j', event, deps);
t.ok(setCurrentFile.calledWith(last), 'should set next file');
t.calledWith(setCurrentFile, [last], 'should set next file');
t.end(); t.end();
}); });
@ -126,25 +131,39 @@ test('cloudcmd: client: key: select +2 files from current before delete', (t) =>
const nextSibling = { const nextSibling = {
nextSibling: last, nextSibling: last,
}; };
const element = { const element = {
nextSibling, nextSibling,
}; };
const setCurrentFile = stub(); const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.setCurrentFile = setCurrentFile; element,
global.DOM.selectFile = stub(); };
global.DOM.getCurrentName = () => false;
global.CloudCmd.Operation.show = stub(); const Operation = {
show: stub(),
};
const selectFile = stub();
const getCurrentName = stub().returns('x');
const event = {}; const event = {};
vim('d', event); const deps = {
vim('2', event); Info,
vim('j', event); setCurrentFile,
selectFile,
getCurrentName,
Operation,
};
t.ok(setCurrentFile.calledWith(last), 'should set next file'); vim('d', event, deps);
vim('2', event, deps);
vim('j', event, deps);
t.calledWith(setCurrentFile, [last], 'should set next file');
t.end(); t.end();
}); });
@ -153,6 +172,7 @@ test('cloudcmd: client: key: delete +2 files from current', (t) => {
const nextSibling = { const nextSibling = {
nextSibling: last, nextSibling: last,
}; };
const element = { const element = {
nextSibling, nextSibling,
}; };
@ -160,20 +180,26 @@ test('cloudcmd: client: key: delete +2 files from current', (t) => {
const setCurrentFile = stub(); const setCurrentFile = stub();
const show = stub(); const show = stub();
global.DOM.CurrentInfo.element = element; const deps = {
global.DOM.setCurrentFile = setCurrentFile; Info: {
global.DOM.selectFile = stub(); element,
global.DOM.getCurrentName = () => false; },
global.CloudCmd.Operation.show = show; Operation: {
show,
},
setCurrentFile,
selectFile: stub(),
getCurrentName: stub().returns('x'),
unselectFiles: stub(),
};
const event = {}; const event = {};
vim('d', event); vim('d', event, deps);
vim('2', event); vim('2', event, deps);
vim('j', event); vim('j', event, deps);
t.ok(show.calledWith('delete'), 'should call delete');
t.calledWith(show, ['delete'], 'should call delete');
t.end(); t.end();
}); });
@ -184,49 +210,84 @@ test('cloudcmd: client: key: set previous file current', (t) => {
}; };
const setCurrentFile = stub(); const setCurrentFile = stub();
const unselectFiles = stub();
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.setCurrentFile = setCurrentFile; element,
};
vim('k', {}); const deps = {
Info,
setCurrentFile,
unselectFiles,
};
t.ok(setCurrentFile.calledWith(previousSibling), 'should set previous file'); vim('k', {}, deps);
t.calledWith(setCurrentFile, [previousSibling], 'should set previous file');
t.end(); t.end();
}); });
test('cloudcmd: client: key: copy: no', (t) => { test('cloudcmd: client: key: copy: no', (t) => {
const copy = stub(); const copy = stub();
Buffer.copy = copy; vim('y', {}, {
unselectFiles: stub(),
vim('y', {}); Buffer: {
copy,
t.notOk(copy.called, 'should not copy files'); },
});
t.notCalled(copy, 'should not copy files');
t.end(); t.end();
}); });
test('cloudcmd: client: key: copy', (t) => { test('cloudcmd: client: key: copy', (t) => {
const copy = stub(); const copy = stub();
const Info = {
element: {},
};
Buffer.copy = copy; const toggleSelectedFile = stub();
const unselectFiles = stub();
vim('v', {}); const deps = {
vim('y', {}); Info,
unselectFiles,
toggleSelectedFile,
Buffer: {
copy,
},
};
t.ok(copy.calledWith(), 'should copy files'); vim('v', {}, deps);
vim('y', {}, deps);
t.calledWithNoArgs(copy, 'should copy files');
t.end(); t.end();
}); });
test('cloudcmd: client: key: copy: unselectFiles', (t) => { test('cloudcmd: client: key: copy: unselectFiles', (t) => {
const unselectFiles = stub(); const unselectFiles = stub();
const Info = {
element: {},
};
DOM.unselectFiles = unselectFiles; const toggleSelectedFile = stub();
vim('v', {}); const deps = {
vim('y', {}); Info,
unselectFiles,
toggleSelectedFile,
Buffer: {
copy: stub(),
},
};
t.ok(unselectFiles.calledWith(), 'should unselect files'); vim('v', {}, deps);
vim('y', {}, deps);
t.calledWithNoArgs(unselectFiles, 'should unselect files');
t.end(); t.end();
}); });
@ -235,63 +296,91 @@ test('cloudcmd: client: key: paste', (t) => {
Buffer.paste = paste; Buffer.paste = paste;
vim('p', {}); vim('p', {}, {
Buffer,
t.ok(paste.calledWith(), 'should paste files'); });
t.calledWithNoArgs(paste, 'should paste files');
t.end(); t.end();
}); });
test('cloudcmd: client: key: selectFile: ..', (t) => { test('cloudcmd: client: key: selectFile: ..', (t) => {
const getCurrentName = stub().returns('..');
const selectFile = stub(); const selectFile = stub();
const getCurrentName = stub();
DOM.selectFile = selectFile;
DOM.getCurrentName = () => '..';
const current = {}; const current = {};
vim.selectFile(current);
t.notOk(getCurrentName.called, 'should not call selectFile'); vimSelectFile(current, {
selectFile,
getCurrentName,
});
t.notCalled(selectFile, 'should not call selectFile');
t.end(); t.end();
}); });
test('cloudcmd: client: key: selectFile', (t) => { test('cloudcmd: client: key: selectFile', (t) => {
const selectFile = stub(); const selectFile = stub();
const getCurrentName = stub().returns('x');
DOM.selectFile = selectFile;
DOM.getCurrentName = (a) => a.name;
const current = {}; const current = {};
vim.selectFile(current); vimSelectFile(current, {
selectFile,
getCurrentName,
});
t.ok(selectFile.calledWith(current), 'should call selectFile'); t.calledWith(selectFile, [current], 'should call selectFile');
t.end(); t.end();
}); });
test('cloudcmd: client: key: set last file current', (t) => { test('cloudcmd: client: key: set last file current: shift + g', async (t) => {
const last = 'last'; const last = 'last';
const nextSibling = { const nextSibling = {
nextSibling: last, nextSibling: last,
}; };
const element = { const element = {
nextSibling, nextSibling,
}; };
const setCurrentFile = stub(); const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element; await vim('G', {}, {
global.DOM.setCurrentFile = setCurrentFile; Info: {
element,
vim('G', {}); },
setCurrentFile,
t.ok(setCurrentFile.calledWith(last), 'should set last file'); unselectFiles: stub(),
});
t.calledWith(setCurrentFile, [last], 'should set last file');
t.end(); t.end();
}); });
test('cloudcmd: client: key: set first file current', (t) => { test('cloudcmd: client: key: set last file current: $', (t) => {
const last = 'last';
const nextSibling = {
nextSibling: last,
};
const element = {
nextSibling,
};
const setCurrentFile = stub();
vim('$', {}, {
Info: {
element,
},
setCurrentFile,
unselectFiles: stub(),
});
t.calledWith(setCurrentFile, [last], 'should set last file');
t.end();
});
test('cloudcmd: client: key: set first file current: gg', (t) => {
const first = 'first'; const first = 'first';
const previousSibling = { const previousSibling = {
previousSibling: first, previousSibling: first,
@ -301,66 +390,121 @@ test('cloudcmd: client: key: set first file current', (t) => {
previousSibling, previousSibling,
}; };
const Operation = {
show: stub(),
};
const unselectFiles = stub();
const setCurrentFile = stub(); const setCurrentFile = stub();
global.DOM.CurrentInfo.element = element; const deps = {
global.DOM.setCurrentFile = setCurrentFile; Operation,
unselectFiles,
setCurrentFile,
Info: {
element,
},
};
vim('g', {}); vim('g', {}, deps);
vim('g', {}); vim('g', {}, deps);
t.ok(setCurrentFile.calledWith(first), 'should set first file'); t.calledWith(setCurrentFile, [first], 'should set first file');
t.end();
});
test('cloudcmd: client: key: set first file current: ^', async (t) => {
const first = 'first';
const previousSibling = {
previousSibling: first,
};
const element = {
previousSibling,
};
const Operation = {
show: stub(),
};
const unselectFiles = stub();
const setCurrentFile = stub();
const deps = {
setCurrentFile,
Info: {
element,
},
unselectFiles,
Operation,
};
await vim('^', {}, deps);
t.calledWith(setCurrentFile, [first], 'should set first file');
t.end(); t.end();
}); });
test('cloudcmd: client: key: visual', (t) => { test('cloudcmd: client: key: visual', (t) => {
const element = { const element = {};
};
const toggleSelectedFile = stub(); const toggleSelectedFile = stub();
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.toggleSelectedFile = toggleSelectedFile; element,
};
vim('v', {}); vim('v', {}, {
Info,
t.ok(toggleSelectedFile.calledWith(element), 'should toggle selection'); toggleSelectedFile,
});
t.calledWith(toggleSelectedFile, [element], 'should toggle selection');
t.end(); t.end();
}); });
test('cloudcmd: client: key: ESC', (t) => { test('cloudcmd: client: key: ESC', (t) => {
const element = { const element = {};
};
const unselectFiles = stub(); const unselectFiles = stub();
global.DOM.CurrentInfo.element = element; const Info = {
global.DOM.unselectFiles = unselectFiles ; element,
};
vim('Escape'); vim('Escape', null, {
Info,
unselectFiles,
});
t.ok(unselectFiles.calledWith(), 'should toggle selection'); t.calledWithNoArgs(unselectFiles, 'should toggle selection');
t.end(); t.end();
}); });
test('cloudcmd: client: key: Enter', (t) => { test('cloudcmd: client: key: Enter', async (t) => {
const nextSibling = 'hello'; const nextSibling = 'hello';
const element = { const element = {
nextSibling, nextSibling,
}; };
const unselectFiles = stub();
const setCurrentFile = stub(); const setCurrentFile = stub();
DOM.CurrentInfo.element = element; const Info = {
DOM.setCurrentFile = setCurrentFile; element,
};
vim('Enter'); await vim('Enter', null, {
Info,
setCurrentFile,
unselectFiles,
});
vim('j'); await vim('j', null, {
Info,
setCurrentFile,
unselectFiles,
});
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file'); t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
t.end(); t.end();
}); });
@ -368,50 +512,245 @@ test('cloudcmd: client: key: /', (t) => {
const preventDefault = stub(); const preventDefault = stub();
const element = {}; const element = {};
DOM.CurrentInfo.element = element; const Info = {
DOM.getCurrentName = () => ''; element,
files: [],
};
vim('/', { const getCurrentName = stub().returns('');
const event = {
preventDefault, preventDefault,
};
const prompt = stub().returns([]);
vim('/', event, {
getCurrentName,
Info,
prompt,
}); });
t.ok(preventDefault.calledWith(), 'should call preventDefault'); t.calledWithNoArgs(preventDefault);
t.end();
});
test('cloudcmd: client: find', (t) => {
assign(DOM.Dialog, {
prompt: stub().returns([]),
});
const setCurrentByName = stub();
assign(DOM, {
setCurrentByName,
});
const event = {
preventDefault: stub(),
};
vim('/', event);
t.notCalled(setCurrentByName);
t.end(); t.end();
}); });
test('cloudcmd: client: key: n', (t) => { test('cloudcmd: client: key: n', (t) => {
const findNext = stub(); const findNext = stub();
const createFindNext = stub().returns(findNext);
mockRequire(pathFind, {
findNext,
});
const vim = reRequire(pathVim);
const event = {}; const event = {};
vim('n', event); vim('n', event, {
createFindNext,
});
stopAll(pathFind); t.calledWithNoArgs(findNext, 'should call findNext');
t.ok(findNext.calledWith(), 'should call findNext');
t.end(); t.end();
}); });
test('cloudcmd: client: key: N', (t) => { test('cloudcmd: client: key: N', (t) => {
const findPrevious = stub(); const findPrevious = stub();
const createFindPrevious = stub().returns(findPrevious);
mockRequire(pathFind, {
findPrevious,
});
const vim = reRequire(dir + 'vim');
const event = {}; const event = {};
vim('N', event); vim('N', event, {
createFindPrevious,
});
stopAll(pathFind); t.calledWithNoArgs(findPrevious);
t.ok(findPrevious.calledWith(), 'should call findPrevious');
t.end(); t.end();
}); });
test('cloudcmd: client: key: make directory', async (t) => {
const {DOM} = globalThis;
assign(DOM, {
promptNewDir: stub(),
});
const event = {
stopImmediatePropagation: stub(),
preventDefault: stub(),
};
await vim('m', event);
await vim('d', event);
t.calledWithNoArgs(DOM.promptNewDir);
t.end();
});
test('cloudcmd: client: key: make file', (t) => {
const promptNewFile = stub();
const event = {
stopImmediatePropagation: stub(),
preventDefault: stub(),
};
vim('m', event);
vim('f', event, {
promptNewFile,
});
t.calledWithNoArgs(promptNewFile);
t.end();
});
test('cloudcmd: client: vim: terminal', (t) => {
const CloudCmd = {
Terminal: {
show: stub(),
},
};
const event = {};
vim('t', event, {
CloudCmd,
});
vim('t', event, {
CloudCmd,
});
t.calledWithNoArgs(CloudCmd.Terminal.show);
t.end();
});
test('cloudcmd: client: vim: edit', async (t) => {
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const {CloudCmd} = globalThis;
assign(CloudCmd, {
EditFileVim: {
show: stub(),
},
});
const event = {};
await vim('e', event);
t.calledWithNoArgs(CloudCmd.EditFileVim.show);
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

@ -0,0 +1,33 @@
/* global DOM */
export function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) {
const name = getCurrentName(current);
if (name === '..')
return;
selectFile(current);
}
export const setCurrent = (sibling, {count, isVisual, isDelete}, {Info, setCurrentFile, unselectFiles, Operation}) => {
let current = Info.element;
const select = isVisual ? selectFileNotParent : unselectFiles;
select(current);
const position = `${sibling}Sibling`;
for (let i = 0; i < count; i++) {
const next = current[position];
if (!next)
break;
current = next;
select(current);
}
setCurrentFile(current);
if (isDelete)
Operation.show('delete');
};

View file

@ -1,6 +1,5 @@
'use strict'; import {fullstore} from 'fullstore';
const fullstore = require('fullstore');
const store = fullstore(''); const store = fullstore('');
const visual = fullstore(false); const visual = fullstore(false);
@ -21,10 +20,11 @@ const rmFirst = (a) => {
const noop = () => {}; const noop = () => {};
module.exports = (key, operations) => { export default (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));
const { const {
escape = noop, escape = noop,
moveNext = noop, moveNext = noop,
@ -36,6 +36,13 @@ module.exports = (key, operations) => {
find = noop, find = noop,
findNext = noop, findNext = noop,
findPrevious = noop, findPrevious = noop,
makeFile = noop,
makeDirectory = noop,
terminal = noop,
edit = noop,
operationCopy = noop,
operationMove = noop,
rename = noop,
} = operations; } = operations;
if (key === 'Enter') if (key === 'Enter')
@ -44,10 +51,11 @@ module.exports = (key, operations) => {
if (key === 'Escape') { if (key === 'Escape') {
visual(false); visual(false);
escape(); escape();
return end(); return end();
} }
if (key === 'j') { if (key === 'j' || key === 'w') {
const { const {
count, count,
isDelete, isDelete,
@ -63,7 +71,7 @@ module.exports = (key, operations) => {
return end(); return end();
} }
if (key === 'k') { if (key === 'k' || key === 'b') {
const { const {
count, count,
isDelete, isDelete,
@ -79,11 +87,8 @@ module.exports = (key, operations) => {
return end(); return end();
} }
if (/^gg$/.test(value)) { if (value === 'gg' || key === '^') {
const { const {isDelete, isVisual} = handleDelete(prevStore);
isDelete,
isVisual,
} = handleDelete(prevStore);
movePrevious({ movePrevious({
count: Infinity, count: Infinity,
@ -94,13 +99,49 @@ module.exports = (key, operations) => {
return end(); return end();
} }
if (key === 'd' && (visual() || prevStore === 'd')) { if (value === 'md') {
stopVisual(); makeDirectory();
remove();
return end(); return end();
} }
if (key === 'G') { if (value === 'tt') {
terminal();
return end();
}
if (value === 'e') {
edit();
return end();
}
if (value === 'cc') {
operationCopy();
return end();
}
if (value === 'mm') {
operationMove();
return end();
}
if (value === 'mf') {
makeFile();
return end();
}
if (value === 'rr') {
rename();
return end();
}
if (key === 'd' && (visual() || prevStore === 'd')) {
stopVisual();
remove();
return end();
}
if (key === 'G' || key === '$') {
moveNext({ moveNext({
count: Infinity, count: Infinity,
isVisual, isVisual,
@ -115,6 +156,7 @@ module.exports = (key, operations) => {
stopVisual(); stopVisual();
copy(); copy();
return end(); return end();
} }
@ -126,6 +168,7 @@ module.exports = (key, operations) => {
if (/^v$/i.test(key)) { if (/^v$/i.test(key)) {
visual(!visual()); visual(!visual());
select(); select();
return end(); return end();
} }
@ -143,6 +186,9 @@ module.exports = (key, operations) => {
findPrevious(); findPrevious();
return end(); return end();
} }
if (key === ' ')
return end();
}; };
function handleDelete(prevStore) { function handleDelete(prevStore) {
@ -172,4 +218,3 @@ function getNumber(value) {
return parseInt(value); return parseInt(value);
} }

View file

@ -1,7 +1,5 @@
'use strict'; import {test, stub} from 'supertape';
import vim from './vim.js';
const test = require('supertape');
const vim = require('./vim');
test('vim: no operations', (t) => { test('vim: no operations', (t) => {
const result = vim('hello', {}); const result = vim('hello', {});
@ -10,3 +8,93 @@ test('vim: no operations', (t) => {
t.end(); t.end();
}); });
test('vim: space', (t) => {
const moveNext = stub();
vim(' ');
vim('j', {
moveNext,
});
const args = [{
count: 1,
isDelete: false,
isVisual: false,
}];
t.calledWith(moveNext, args);
t.end();
});
test('vim: ^', (t) => {
const movePrevious = stub();
vim('^', {
movePrevious,
});
const expected = {
count: Infinity,
isVisual: false,
isDelete: false,
};
t.calledWith(movePrevious, [expected], 'should call movePrevious');
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) => {
const moveNext = stub();
vim('w', {
moveNext,
});
const expected = {
count: 1,
isVisual: false,
isDelete: false,
};
t.calledWith(moveNext, [expected], 'should call moveNext');
t.end();
});
test('vim: b', (t) => {
const movePrevious = stub();
vim('b', {
movePrevious,
});
const expected = {
count: 1,
isVisual: false,
isDelete: false,
};
t.calledWith(movePrevious, [expected], 'should call movePrevious');
t.end();
});

View file

@ -1,11 +1,10 @@
'use strict'; import currify from 'currify';
module.exports = (array, item) => { export const getIndex = currify((array, item) => {
const index = array.indexOf(item); const index = array.indexOf(item);
if (!~index) if (!~index)
return 0; return 0;
return index; return index;
}; });

View file

@ -1,9 +1,5 @@
'use strict'; import test from 'supertape';
import {getIndex} from './get-index.js';
const test = require('supertape');
const dir = '../../../client/listeners';
const getIndex = require(`${dir}/get-index`);
test('cloudcmd: client: listeners: getIndex: not found', (t) => { test('cloudcmd: client: listeners: getIndex: not found', (t) => {
const array = ['hello']; const array = ['hello'];
@ -13,9 +9,11 @@ test('cloudcmd: client: listeners: getIndex: not found', (t) => {
}); });
test('cloudcmd: client: listeners: getIndex: found', (t) => { test('cloudcmd: client: listeners: getIndex: found', (t) => {
const array = ['hello', 'world']; const array = [
'hello',
'world',
];
t.equal(getIndex(array, 'world'), 1, 'should return index'); t.equal(getIndex(array, 'world'), 1, 'should return index');
t.end(); t.end();
}); });

View file

@ -1,6 +1,4 @@
'use strict'; export const getRange = (indexFrom, indexTo, files) => {
module.exports = (indexFrom, indexTo, files) => {
if (indexFrom < indexTo) if (indexFrom < indexTo)
return files.slice(indexFrom, indexTo + 1); return files.slice(indexFrom, indexTo + 1);
@ -9,4 +7,3 @@ module.exports = (indexFrom, indexTo, files) => {
return [files[indexFrom]]; return [files[indexFrom]];
}; };

View file

@ -0,0 +1,52 @@
import test from 'supertape';
import {getRange} from './get-range.js';
test('cloudcmd: client: listeners: getRange: direct', (t) => {
const expected = [
'hello',
'world',
];
const files = [
...expected,
'how',
'come',
];
const result = getRange(0, 1, files);
t.deepEqual(result, expected, 'should return range');
t.end();
});
test('cloudcmd: client: listeners: getRange: reverse', (t) => {
const expected = [
'hello',
'world',
];
const files = [
...expected,
'how',
'come',
];
const result = getRange(1, 0, files);
t.deepEqual(result, expected, 'should return range');
t.end();
});
test('cloudcmd: client: listeners: getRange: one', (t) => {
const expected = ['hello'];
const files = [
...expected,
'how',
'come',
];
const result = getRange(0, 0, files);
t.deepEqual(result, expected, 'should return range');
t.end();
});

View file

@ -1,36 +1,30 @@
/* global DOM, CloudCmd */ /* global DOM, CloudCmd */
import exec from 'execon';
'use strict'; import itype from 'itype';
import currify from 'currify';
const exec = require('execon'); import {tryToCatch} from 'try-to-catch';
const itype = require('itype'); import clipboard from '@cloudcmd/clipboard';
const currify = require('currify'); import * as Events from '#dom/events';
const tryToCatch = require('try-to-catch'); import {uploadFiles} from '#dom/upload-files';
const clipboard = require('@cloudcmd/clipboard'); import {FS} from '#common/cloudfunc';
import {getRange} from './get-range.js';
const getRange = require('./get-range'); import {getIndex} from './get-index.js';
const getIndex = currify(require('./get-index'));
const uploadFiles = require('../dom/upload-files');
const {FS} = require('../../common/cloudfunc');
const NBSP_REG = RegExp(String.fromCharCode(160), 'g'); const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
const SPACE = ' '; const SPACE = ' ';
module.exports.init = () => { export async function init() {
contextMenu(); contextMenu();
dragndrop(); dragndrop();
unload(); unload();
pop(); pop();
resize(); resize();
config();
header(); header();
}; await config();
}
CloudCmd.Listeners = module.exports;
const unselect = (event) => { const unselect = (event) => {
const isMac = /Mac/.test(window.navigator.platform); const isMac = /Mac/.test(globalThis.navigator.platform);
const { const {
shiftKey, shiftKey,
metaKey, metaKey,
@ -48,14 +42,9 @@ const execAll = currify((funcs, event) => {
fn(event); fn(event);
}); });
const Info = DOM.CurrentInfo;
const {Events} = DOM;
const EventsFiles = { const EventsFiles = {
mousedown: exec.with(execIfNotUL, setCurrentFileByEvent), mousedown: exec.with(execIfNotUL, setCurrentFileByEvent),
click: execAll([ click: execAll([onClick, exec.with(execIfNotMobile, unselect)]),
onClick,
unselect,
]),
dragstart: exec.with(execIfNotUL, onDragStart), dragstart: exec.with(execIfNotUL, onDragStart),
dblclick: exec.with(execIfNotUL, onDblClick), dblclick: exec.with(execIfNotUL, onDblClick),
touchstart: exec.with(execIfNotUL, onTouch), touchstart: exec.with(execIfNotUL, onTouch),
@ -66,6 +55,7 @@ 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);
}; };
@ -77,8 +67,7 @@ function header() {
if (parent.dataset.name !== 'js-fm-header') if (parent.dataset.name !== 'js-fm-header')
return; return;
const name = (el.dataset.name || '') const name = (el.dataset.name || '').replace('js-', '');
.replace('js-', '');
if (!/^(name|size|date)$/.test(name)) if (!/^(name|size|date)$/.test(name))
return; return;
@ -101,58 +90,58 @@ function getPath(el, path = []) {
async function config() { async function config() {
const [, config] = await tryToCatch(DOM.Files.get, 'config'); const [, config] = await tryToCatch(DOM.Files.get, 'config');
const type = config && config.packer; const type = config?.packer;
EXT = DOM.getPackerExt(type); EXT = DOM.getPackerExt(type);
} }
module.exports.initKeysPanel = () => { export const initKeysPanel = () => {
const keysElement = DOM.getById('js-keyspanel'); const keysElement = DOM.getById('js-keyspanel');
if (!keysElement) if (!keysElement)
return; return;
Events.addClick(keysElement, ({target}) => { Events.addClick(keysElement, (event) => {
const {target} = event;
const {id} = target; const {id} = target;
const operation = (name) => { const operation = (name) => {
const {Operation} = CloudCmd; const {Operation} = CloudCmd;
const fn = Operation.show.bind(null, name);
return fn; return Operation.show.bind(null, name);
}; };
const clickFuncs = { const clickFuncs = {
'f1' : CloudCmd.Help.show, 'f1': CloudCmd.Help.show,
'f2' : initF2, 'f2': CloudCmd.UserMenu.show,
'f3' : CloudCmd.View.show, 'f3': CloudCmd.View.show,
'f4' : CloudCmd.EditFile.show, 'f4': CloudCmd.EditFile.show,
'f5' : operation('copy'), 'f5': operation('copy'),
'f6' : operation('move'), 'f6': operation('move'),
'f7' : DOM.promptNewDir, 'f7': DOM.promptNewDir,
'f8' : operation('delete'), 'f8': operation('delete'),
'f9' : CloudCmd.Menu.show, 'f9': () => {
'f10' : CloudCmd.Config.show, event.stopPropagation();
'~' : CloudCmd.Konsole.show, CloudCmd.Menu.show();
'shift~' : CloudCmd.Terminal.show, },
'contact' : CloudCmd.Contact.show, 'f10': CloudCmd.Config.show,
'~': CloudCmd.Konsole.show,
'shift~': CloudCmd.Terminal.show,
'contact': CloudCmd.Contact.show,
}; };
exec(clickFuncs[id]); exec(clickFuncs[id]);
}); });
}; };
function initF2() {
CloudCmd.UserMenu.show();
}
const getPanel = (side) => { const getPanel = (side) => {
if (!itype.string(side)) if (!itype.string(side))
return side; return side;
return DOM.getByDataName('js-' + side); return DOM.getByDataName(`js-${side}`);
}; };
module.exports.setOnPanel = (side) => { export const setOnPanel = (side) => {
const panel = getPanel(side); const panel = getPanel(side);
const filesElement = DOM.getByDataName('js-files', panel); const filesElement = DOM.getByDataName('js-files', panel);
@ -168,6 +157,7 @@ function getPathListener(panel) {
} }
function isNoCurrent(panel) { function isNoCurrent(panel) {
const Info = DOM.CurrentInfo;
const infoPanel = Info.panel; const infoPanel = Info.panel;
if (!infoPanel) if (!infoPanel)
@ -186,13 +176,13 @@ function decodePath(path) {
return decodeURI(path) return decodeURI(path)
.replace(url, '') .replace(url, '')
.replace(prefixReg, '') .replace(prefixReg, '') // browser doesn't replace % -> %25% do it for him
// browser doesn't replace % -> %25% do it for him
.replace('%%', '%25%') .replace('%%', '%25%')
.replace(NBSP_REG, SPACE) || '/'; .replace(NBSP_REG, SPACE) || '/';
} }
async function onPathElementClick(panel, event) { async function onPathElementClick(panel, event) {
const Info = DOM.CurrentInfo;
event.preventDefault(); event.preventDefault();
const element = event.target; const element = event.target;
@ -214,19 +204,26 @@ async function onPathElementClick(panel, event) {
const {href} = element; const {href} = element;
const path = decodePath(href); const path = decodePath(href);
await CloudCmd.loadDir({ await CloudCmd.changeDir(path, {
path,
isRefresh: false, isRefresh: false,
panel: noCurrent ? panel : Info.panel, panel: noCurrent ? panel : Info.panel,
}); });
} }
function copyPath(el) { function copyPath(el) {
clipboard.writeText(el.parentElement.title) clipboard
.writeText(el.parentElement.title)
.then(CloudCmd.log) .then(CloudCmd.log)
.catch(CloudCmd.log); .catch(CloudCmd.log);
} }
function execIfNotMobile(callback, event) {
const isMobile = DOM.getCSSVar('is-mobile');
if (!isMobile)
callback(event);
}
function execIfNotUL(callback, event) { function execIfNotUL(callback, event) {
const {target} = event; const {target} = event;
const {tagName} = target; const {tagName} = target;
@ -241,14 +238,14 @@ function onClick(event) {
} }
function toggleSelect(key, files) { function toggleSelect(key, files) {
const isMac = /Mac/.test(window.navigator.platform); const isMac = /Mac/.test(globalThis.navigator.platform);
if (!key) if (!key)
throw Error('key should not be undefined!'); throw Error('key should not be undefined!');
const [file] = files; const [file] = files;
if (isMac && key.meta || key.ctrl) if (isMac && key.meta)
return DOM.toggleSelectedFile(file); return DOM.toggleSelectedFile(file);
if (key.shift) if (key.shift)
@ -256,6 +253,7 @@ function toggleSelect(key, files) {
} }
function changePanel(element) { function changePanel(element) {
const Info = DOM.CurrentInfo;
const {panel} = Info; const {panel} = Info;
const files = DOM.getByDataName('js-files', panel); const files = DOM.getByDataName('js-files', panel);
const ul = getULElement(element); const ul = getULElement(element);
@ -265,21 +263,16 @@ function changePanel(element) {
} }
async function onDblClick(event) { async function onDblClick(event) {
event.preventDefault();
const current = getLIElement(event.target); const current = getLIElement(event.target);
const isDir = DOM.isCurrentIsDir(current); const isDir = DOM.isCurrentIsDir(current);
const path = DOM.getCurrentPath(current); const path = DOM.getCurrentPath(current);
if (isDir) { if (!isDir)
await CloudCmd.loadDir({ return CloudCmd.View.show();
path: path === '/' ? '/' : path + '/',
}); await CloudCmd.changeDir(path);
event.preventDefault();
} else {
CloudCmd.View.show();
event.preventDefault();
}
} }
async function onTouch(event) { async function onTouch(event) {
@ -294,9 +287,7 @@ async function onTouch(event) {
if (!isCurrent) if (!isCurrent)
return; return;
await CloudCmd.loadDir({ await CloudCmd.changeDir(DOM.getCurrentPath(current));
path: DOM.getCurrentPath(current),
});
} }
/* /*
@ -304,6 +295,7 @@ async function onTouch(event) {
* in Chrome (HTML5) * in Chrome (HTML5)
*/ */
function onDragStart(event) { function onDragStart(event) {
const Info = DOM.CurrentInfo;
const {prefixURL} = CloudCmd; const {prefixURL} = CloudCmd;
const element = getLIElement(event.target); const element = getLIElement(event.target);
const {isDir} = Info; const {isDir} = Info;
@ -320,8 +312,8 @@ function onDragStart(event) {
event.dataTransfer.setData( event.dataTransfer.setData(
'DownloadURL', 'DownloadURL',
'application/octet-stream' + ':' + 'application/octet-stream' + ':' + name +
name + ':' + ':' +
link, link,
); );
} }
@ -344,6 +336,7 @@ function getULElement(element) {
} }
function setCurrentFileByEvent(event) { function setCurrentFileByEvent(event) {
const Info = DOM.CurrentInfo;
const BUTTON_LEFT = 0; const BUTTON_LEFT = 0;
const key = { const key = {
@ -407,10 +400,7 @@ function dragndrop() {
}; };
const onDrop = (event) => { const onDrop = (event) => {
const { const {files, items} = event.dataTransfer;
files,
items,
} = event.dataTransfer;
const {length: filesCount} = files; const {length: filesCount} = files;
@ -420,7 +410,10 @@ function dragndrop() {
return uploadFiles(files); return uploadFiles(files);
const isFile = (item) => item.kind === 'file'; const isFile = (item) => item.kind === 'file';
const dirFiles = Array.from(items).filter(isFile);
const dirFiles = Array
.from(items)
.filter(isFile);
if (dirFiles.length) if (dirFiles.length)
return DOM.uploadDirectory(dirFiles); return DOM.uploadDirectory(dirFiles);
@ -432,7 +425,7 @@ function dragndrop() {
}; };
/** /**
* In Mac OS Chrome dropEffect = 'none' * In macOS Chrome dropEffect = 'none'
* so drop do not firing up when try * so drop do not firing up when try
* to upload file from download bar * to upload file from download bar
*/ */
@ -457,9 +450,9 @@ function dragndrop() {
} }
function unload() { function unload() {
DOM.Events.add(['unload', 'beforeunload'], (event) => { Events.add(['unload', 'beforeunload'], (event) => {
const {Key} = CloudCmd; const {Key} = CloudCmd;
const isBind = Key && Key.isBind(); const isBind = Key?.isBind();
if (isBind) if (isBind)
return; return;
@ -477,8 +470,8 @@ function pop() {
return CloudCmd.route(location.hash); return CloudCmd.route(location.hash);
const history = false; const history = false;
await CloudCmd.loadDir({
path, await CloudCmd.changeDir(path, {
history, history,
}); });
}); });
@ -486,7 +479,8 @@ function pop() {
function resize() { function resize() {
Events.add('resize', () => { Events.add('resize', () => {
const is = window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH; const Info = DOM.CurrentInfo;
const is = globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH;
if (!is) if (!is)
return; return;
@ -506,4 +500,3 @@ function resize() {
DOM.changePanel(); DOM.changePanel();
}); });
} }

View file

@ -1,19 +1,16 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
import exec from 'execon';
import {tryToCatch} from 'try-to-catch';
import {js as loadJS} from 'load.js';
import pascalCase from 'just-pascal-case';
const exec = require('execon');
const tryToCatch = require('try-to-catch');
const loadJS = require('load.js').js;
const pascalCase = require('just-pascal-case');
const noJS = (a) => a.replace(/.js$/, ''); const noJS = (a) => a.replace(/.js$/, '');
/** /**
* function load modules * function load modules
* @params = {name, path, func, dobefore, arg} * @params = {name, path, func, dobefore, arg}
*/ */
module.exports = function loadModule(params) { export const loadModule = (params) => {
if (!params) if (!params)
return; return;
@ -25,24 +22,24 @@ module.exports = function loadModule(params) {
if (CloudCmd[name]) if (CloudCmd[name])
return; return;
CloudCmd[name] = () => { CloudCmd[name] = async () => {
exec(doBefore); exec(doBefore);
const {prefix} = CloudCmd;
const pathFull = prefix + CloudCmd.DIRCLIENT_MODULES + path + '.js';
return loadJS(pathFull).then(async () => { const {DIR_MODULES} = CloudCmd;
const newModule = async (f) => f && f(); const pathFull = `${DIR_MODULES}/${path}.js`;
const module = CloudCmd[name];
await loadJS(pathFull);
Object.assign(newModule, module); const newModule = async (f) => f && f();
const module = CloudCmd[name];
CloudCmd[name] = newModule;
Object.assign(newModule, module);
CloudCmd.log('init', name);
await module.init(); CloudCmd[name] = newModule;
CloudCmd.log('init', name);
return newModule;
}); await module.init();
return newModule;
}; };
CloudCmd[name].show = async (...args) => { CloudCmd[name].show = async (...args) => {
@ -52,9 +49,8 @@ module.exports = function loadModule(params) {
const [e, a] = await tryToCatch(m); const [e, a] = await tryToCatch(m);
if (e) if (e)
return console.error(e); return;
await a.show(...args); return await a.show(...args);
}; };
}; };

View file

@ -1,31 +1,32 @@
/* global CloudCmd, filepicker */ /* global CloudCmd, filepicker */
import exec from 'execon';
'use strict'; import currify from 'currify';
import load from 'load.js';
const exec = require('execon'); import {ajax} from '#dom/load';
const currify = require('currify'); import * as Files from '#dom/files';
const load = require('load.js'); import * as Images from '#dom/images';
const {log} = CloudCmd; const {log} = CloudCmd;
const {ajax} = require('../dom/load');
const Files = require('../dom/files');
const Images = require('../dom/images');
const upload = currify(_upload); const upload = currify(_upload);
const Name = 'Cloud'; const Name = 'Cloud';
CloudCmd[Name] = module.exports;
module.exports.init = async () => { CloudCmd[Name] = {
init,
uploadFile,
saveFile,
};
export async function init() {
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();
}; }
module.exports.uploadFile = (filename, data) => { export function uploadFile(filename, data) {
const mimetype = ''; const mimetype = '';
filepicker.store(data, { filepicker.store(data, {
@ -34,17 +35,14 @@ module.exports.uploadFile = (filename, data) => {
}, (fpFile) => { }, (fpFile) => {
filepicker.exportFile(fpFile, log, log); filepicker.exportFile(fpFile, log, log);
}); });
}; }
module.exports.saveFile = (callback) => { export function saveFile(callback) {
filepicker.pick(upload(callback)); filepicker.pick(upload(callback));
}; }
function _upload(callback, file) { function _upload(callback, file) {
const { const {url, filename} = file;
url,
filename,
} = file;
const responseType = 'arraybuffer'; const responseType = 'arraybuffer';
const success = exec.with(callback, filename); const success = exec.with(callback, filename);
@ -56,7 +54,7 @@ function _upload(callback, file) {
}); });
} }
async function loadFiles() { function loadFiles() {
const js = '//api.filepicker.io/v2/filepicker.js'; const js = '//api.filepicker.io/v2/filepicker.js';
return Promise.all([ return Promise.all([
@ -64,4 +62,3 @@ async function loadFiles() {
load.js(js), load.js(js),
]); ]);
} }

View file

@ -0,0 +1,31 @@
/* global CloudCmd */
import * as Dialog from '#dom/dialog';
export function init() {}
CloudCmd.CommandLine = {
init,
show,
hide,
};
export async function show() {
const [, cmd] = await Dialog.prompt('Command Line', '');
const TERMINAL = '^(t|terminal)';
if (RegExp(`${TERMINAL}$`).test(cmd))
return await CloudCmd.Terminal.show();
if (RegExp(TERMINAL).test(cmd)) {
const command = cmd.replace(RegExp(`${TERMINAL} `), '');
const exitCode = await CloudCmd.TerminalRun.show({
command: `bash -c '${command}'`,
});
if (exitCode === -1)
await Dialog.alert(`☝️ Looks like Terminal is disabled, start Cloud Coammnder with '--terminal' flag.`);
return;
}
}
export function hide() {}

View file

@ -1,28 +1,23 @@
'use strict'; import '../../../css/config.css';
import {rendy} from 'rendy';
import currify from 'currify';
import wraptile from 'wraptile';
import squad from 'squad';
import {promisify} from 'es6-promisify';
import {tryToCatch} from 'try-to-catch';
import load from 'load.js';
import createElement from '@cloudcmd/create-element';
import * as Events from '#dom/events';
import * as Files from '#dom/files';
import {getTitle} from '#common/cloudfunc';
import * as Images from '#dom/images';
import * as input from './input.js';
/* global CloudCmd, DOM, io */ const {CloudCmd, DOM} = globalThis;
require('../../../css/config.css');
const rendy = require('rendy');
const currify = require('currify');
const wraptile = require('wraptile');
const squad = require('squad');
const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch');
const load = require('load.js');
const createElement = require('@cloudcmd/create-element');
const input = require('./input');
const Images = require('../../dom/images');
const Events = require('../../dom/events');
const Files = require('../../dom/files');
const {getTitle} = require('../../../common/cloudfunc');
const {Dialog, setTitle} = DOM; const {Dialog, setTitle} = DOM;
const Name = 'Config'; const Name = 'Config';
CloudCmd[Name] = module.exports;
const loadSocket = promisify(DOM.loadSocket); const loadSocket = promisify(DOM.loadSocket);
@ -46,28 +41,25 @@ let Template;
const loadCSS = load.css; const loadCSS = load.css;
module.exports.init = async () => { export async function init() {
if (!CloudCmd.config('configDialog')) if (!CloudCmd.config('configDialog'))
return; return;
showLoad(); showLoad();
const {prefix} = CloudCmd; const {DIR_DIST} = CloudCmd;
[Template] = await Promise.all([ [Template] = await Promise.all([
Files.get('config-tmpl'), Files.get('config-tmpl'),
loadSocket(), loadSocket(),
loadCSS(prefix + '/dist/config.css'), loadCSS(`${DIR_DIST}/config.css`),
CloudCmd.View(), CloudCmd.View(),
]); ]);
initSocket(); initSocket();
}; }
const { const {config, Key} = CloudCmd;
config,
Key,
} = CloudCmd;
let Element; let Element;
@ -77,24 +69,20 @@ function getHost() {
origin, origin,
protocol, protocol,
} = location; } = location;
const href = origin || `${protocol}//${host}`;
return href; return origin || `${protocol}//${host}`;
} }
function initSocket() { function initSocket() {
const href = getHost(); const href = getHost();
const { const {prefixSocket, prefix} = CloudCmd;
prefixSocket,
prefix,
} = CloudCmd;
const ONE_MINUTE = 60 * 1000; const ONE_MINUTE = 60 * 1000;
const socket = io.connect(href + prefixSocket + '/config', { const socket = globalThis.io.connect(href + prefixSocket + '/config', {
reconnectionAttempts: Infinity, reconnectionAttempts: Infinity,
reconnectionDelay: ONE_MINUTE, reconnectionDelay: ONE_MINUTE,
path: prefix + '/socket.io', path: `${prefix}/socket.io`,
}); });
const save = (data) => { const save = (data) => {
@ -125,9 +113,7 @@ function authCheck(socket) {
Config.save = saveHttp; Config.save = saveHttp;
module.exports.show = show; export async function show() {
async function show() {
if (!CloudCmd.config('configDialog')) if (!CloudCmd.config('configDialog'))
return; return;
@ -142,21 +128,27 @@ async function fillTemplate() {
const { const {
editor, editor,
menu,
packer, packer,
columns, columns,
theme,
configAuth, configAuth,
configPort,
...obj ...obj
} = input.convert(config); } = input.convert(config);
obj[editor + '-selected'] = 'selected'; obj[`${menu}-selected`] = 'selected';
obj[packer + '-selected'] = 'selected'; obj[`${editor}-selected`] = 'selected';
obj[columns + '-selected'] = 'selected'; obj[`${packer}-selected`] = 'selected';
obj[`${columns}-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);
Element = createElement('form', { Element = createElement('form', {
className : 'config', className: 'config',
innerHTML, innerHTML,
}); });
@ -173,20 +165,20 @@ async function fillTemplate() {
const getTarget = ({target}) => target; const getTarget = ({target}) => target;
const handleChange = squad(onChange, getTarget); const handleChange = squad(onChange, getTarget);
Array.from(inputs) Array
.from(inputs)
.map(addKey(onKey)) .map(addKey(onKey))
.map(addChange(handleChange)); .map(addChange(handleChange));
const autoSize = true; const autoSize = true;
CloudCmd.View.show(Element, { CloudCmd.View.show(Element, {
autoSize, autoSize,
afterShow, afterShow,
}); });
} }
module.exports.hide = hide; export function hide() {
function hide() {
CloudCmd.View.hide(); CloudCmd.View.hide();
} }
@ -228,7 +220,7 @@ function onAuthChange(checked) {
const elUsername = input.getElementByName('username', Element); const elUsername = input.getElementByName('username', Element);
const elPassword = input.getElementByName('password', Element); const elPassword = input.getElementByName('password', Element);
elUsername.disabled = elUsername.disabled = !checked;
elPassword.disabled = !checked; elPassword.disabled = !checked;
} }
@ -238,13 +230,18 @@ function onNameChange(name) {
})); }));
} }
function onKey({keyCode, target}) { async function onKey({keyCode, target}) {
switch(keyCode) { switch(keyCode) {
case Key.ESC: case Key.ESC:
return hide(); return hide();
case Key.ENTER: case Key.ENTER:
return onChange(target); return await onChange(target);
} }
} }
CloudCmd[Name] = {
init,
show,
hide,
};

View file

@ -1,23 +1,17 @@
'use strict'; import {encode} from '#common/entity';
const currify = require('currify'); const isBool = (a) => typeof a === 'boolean';
const isString = (a) => typeof a === 'string';
const isType = currify((type, object, name) => { const {keys} = Object;
return typeof object[name] === type;
});
const isBool = isType('boolean'); export function getElementByName(selector, element) {
module.exports.getElementByName = getElementByName;
function getElementByName(selector, element) {
const str = `[data-name="js-${selector}"]`; const str = `[data-name="js-${selector}"]`;
return element return element.querySelector(str);
.querySelector(str);
} }
module.exports.getName = (element) => { export const getName = (element) => {
const name = element const name = element
.getAttribute('data-name') .getAttribute('data-name')
.replace(/^js-/, ''); .replace(/^js-/, '');
@ -25,17 +19,21 @@ module.exports.getName = (element) => {
return name; return name;
}; };
module.exports.convert = (config) => { export const convert = (config) => {
const result = { const result = config;
...config,
};
const array = Object.keys(config);
const filtered = array.filter(isBool(config)); for (const name of keys(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;
@ -48,7 +46,7 @@ function setState(state) {
return ''; return '';
} }
module.exports.getValue = (name, element) => { export const getValue = (name, element) => {
const el = getElementByName(name, element); const el = getElementByName(name, element);
const {type} = el; const {type} = el;
@ -64,7 +62,7 @@ module.exports.getValue = (name, element) => {
} }
}; };
module.exports.setValue = (name, value, element) => { export const setValue = (name, value, element) => {
const el = getElementByName(name, element); const el = getElementByName(name, element);
const {type} = el; const {type} = el;
@ -78,4 +76,3 @@ module.exports.setValue = (name, value, element) => {
break; break;
} }
}; };

View file

@ -0,0 +1,140 @@
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,36 +1,34 @@
/* global CloudCmd */ /* global CloudCmd */
/* global DOM */ /* global DOM */
import olark from '@cloudcmd/olark';
import * as Images from '#dom/images';
'use strict'; CloudCmd.Contact = {
init,
CloudCmd.Contact = exports; show,
hide,
const olark = require('@cloudcmd/olark'); };
const Images = require('../dom/images');
const {Events} = DOM; const {Events} = DOM;
const {Key} = CloudCmd; const {Key} = CloudCmd;
module.exports.show = show; export function init() {
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);
}; }
function show() { export function show() {
Key.unsetBind(); Key.unsetBind();
Images.hide(); Images.hide();
olark('api.box.expand'); olark('api.box.expand');
} }
function hide() { export function hide() {
Key.setBind(); Key.setBind();
olark('api.box.hide'); olark('api.box.hide');
} }
@ -39,4 +37,3 @@ function onKey({keyCode}) {
if (keyCode === Key.ESC) if (keyCode === Key.ESC)
hide(); hide();
} }

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