Compare commits

..

415 commits

Author SHA1 Message Date
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
198 changed files with 3930 additions and 3036 deletions

View file

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

View file

@ -1,42 +0,0 @@
'use strict';
module.exports = {
extends: [
'plugin:putout/safe',
],
plugins: [
'putout',
'n',
],
rules: {
'key-spacing': 'off',
},
overrides: [{
files: ['bin/release.js'],
rules: {
'no-console': 'off',
'n/shebang': 'off',
},
extends: [
'plugin:n/recommended',
],
}, {
files: ['client/dom/index.js'],
rules: {
'no-multi-spaces': 'off',
},
}, {
files: ['bin/cloudcmd.js'],
rules: {
'no-console': 'off',
},
extends: [
'plugin:n/recommended',
],
}, {
files: ['{client,common,static}/**/*.js'],
env: {
browser: true,
},
}],
};

1
.github/FUNDING.yml vendored
View file

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

View file

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

@ -6,39 +6,47 @@ on:
jobs:
buildx:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Use Node.js 18.x
uses: actions/setup-node@v4
- name: Use Node.js 22.x
uses: actions/setup-node@v6
with:
node-version: 18.x
node-version: 22.x
- name: Install Redrun
run: bun i yarn redrun -g --no-save
run: bun i redrun -g --no-save
- name: NPM Install
run: yarn --no-lockfile
run: bun i --no-save
- name: Lint
run: redrun lint
- name: Build
id: build
run: >
run: |
redrun build
echo "::set-output name=version::$(grep '"version":' package.json -m1 | cut -d\" -f4)"
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push base-image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
@ -47,13 +55,17 @@ jobs:
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@v4
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile.alpine
platforms: linux/amd64,linux/arm/v7,linux/arm64
platforms: linux/amd64,linux/arm64
push: true
tags: |
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

View file

@ -9,24 +9,25 @@ jobs:
strategy:
matrix:
node-version:
- 18.x
- 20.x
- 22.x
- 24.x
- 25.x
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Install Redrun
run: bun i yarn redrun -g --no-save
run: bun i redrun -g --no-save
- name: Install
run: yarn --no-lockfile
run: bun i --no-save
- name: Lint
run: redrun fix:lint
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/

View file

@ -1,11 +1,7 @@
import {
run,
cutEnv,
} from 'madrun';
import process from 'node:process';
import {run, cutEnv} from 'madrun';
const testEnv = {
THREAD_IT_COUNT: 0,
SUPERTAPE_TIMEOUT: 7000,
};
@ -19,6 +15,7 @@ const is20 = process.version.startsWith('v2');
// https://stackoverflow.com/a/69746937/4536327
const buildEnv = (is17 || is20) && {
NODE_OPTIONS: '--openssl-legacy-provider',
NODE_ENV: 'production',
};
export default {
@ -36,7 +33,7 @@ export default {
'lint:fresh': () => run('lint', '--fresh'),
'fix:lint': () => run('lint', '--fix'),
'lint:stream': () => run('lint', '-f stream'),
'test': () => [testEnv, `tape 'test/**/*.js' '{client,static,common,server}/**/*.spec.js' -f fail`],
'test': () => [testEnv, `tape 'test/**/*.{js,mjs}' '{client,static,common,server}/**/*.spec.{js,mjs}' -f fail`],
'test:client': () => `tape 'test/client/**/*.js'`,
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
'wisdom': () => run(['lint:all', 'build', 'test']),

View file

@ -1,5 +1,6 @@
.*
*.spec.js
*.spec.*
*.config.*
*.fixture.js*
manifest.yml
docker
@ -13,13 +14,13 @@ html
yarn-error.log
yarn.lock
now.json
cssnano.config.js
app.json
bower.json
manifest.yml
deno.json
bin/release.js
bin/release.mjs
client
img/logo/cloudcmd-hq.png
@ -30,3 +31,4 @@ webpack.config.js
*.cdr
*.eps
*.config.*

1
.nvmrc Normal file
View file

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

View file

@ -4,7 +4,9 @@
"exclude": [
"**/*.spec.js",
"**/fixture",
"**/*.*.js"
"**/*.*.js",
"**/*.config.*",
"**/test/**"
],
"branches": 100,
"lines": 100,

View file

@ -4,20 +4,15 @@
"html",
"fixture*",
"app.json",
"fontello.json"
"fontello.json",
"*.md"
],
"rules": {
"github/convert-npm-to-bun": "off",
"github/set-node-versions": ["on", {
"versions": [
"18.x",
"20.x"
]
}]
"package-json/add-type": "off"
},
"match": {
"base64": {
"convert-typeof-to-is-type": "off"
"types/convert-typeof-to-is-type": "off"
},
"*.md": {
"nodejs/convert-commonjs-to-esm": "on"
@ -34,9 +29,12 @@
"server/{server,exit,terminal,distribute/log}.{js,mjs}": {
"remove-console": "off"
},
"client/{client,cloudcmd,load-module}.js": {
"client/{client,cloudcmd,load-module}.{js,mjs}": {
"remove-console": "off"
},
"client": {
"nodejs": "off"
},
"client/sw": {
"remove-console": "off"
},
@ -51,6 +49,9 @@
},
"vim.js": {
"merge-duplicate-functions": "off"
},
"common": {
"nodejs/declare": "off"
}
}
}

View file

@ -1,76 +0,0 @@
'use strict';
const fs = require('fs');
const {
basename,
extname,
join,
} = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const {env} = require('process');
const isDev = env.NODE_ENV === 'development';
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: 100_000,
},
},
}];
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']),
};
}

34
.webpack/css.mjs Normal file
View file

@ -0,0 +1,34 @@
import {env} from 'node:process';
import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
const isDev = env.NODE_ENV === 'development';
const clean = (a) => a.filter(Boolean);
const plugins = clean([
new MiniCssExtractPlugin({
filename: '[name].css',
}),
!isDev && new OptimizeCssAssetsPlugin(),
]);
const rules = [{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, {
loader: 'css-loader',
options: {
url: true,
},
}],
}, {
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
type: 'asset/inline',
}];
export default {
plugins,
module: {
rules,
},
};

View file

@ -1,11 +1,9 @@
'use strict';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {env} = require('process');
import {env} from 'node:process';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const isDev = env.NODE_ENV === 'development';
const plugins = [
export const plugins = [
new HtmlWebpackPlugin({
inject: false,
template: 'html/index.html',
@ -13,10 +11,6 @@ const plugins = [
}),
];
module.exports = {
plugins,
};
function getMinifyHtmlOptions() {
return {
removeComments: true,

View file

@ -1,24 +1,23 @@
'use strict';
import {resolve, sep} from 'node:path';
import {env} from 'node:process';
import webpack from 'webpack';
import WebpackBar from 'webpackbar';
const {
resolve,
sep,
join,
} = require('path');
EnvironmentPlugin,
NormalModuleReplacementPlugin,
} = webpack;
const {EnvironmentPlugin} = require('webpack');
const WebpackBar = require('webpackbar');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
const {env} = require('process');
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 = join(__dirname, '..');
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';
@ -26,9 +25,7 @@ const devtool = isDev ? 'eval' : 'source-map';
const notEmpty = (a) => a;
const clean = (array) => array.filter(notEmpty);
const noParse = (a) => /\.spec\.js$/.test(a);
const convertToWebpack5Externals = (fn) => (context, request, cb) => fn({context, request}, cb);
const noParse = (a) => a.endsWith('.spec.js');
const options = {
babelrc: true,
};
@ -48,44 +45,89 @@ const rules = clean([
]);
const plugins = [
new NormalModuleReplacementPlugin(/^node:/, (resource) => {
resource.request = resource.request.replace(/^node:/, '');
}),
new NormalModuleReplacementPlugin(/^putout$/, '@putout/bundle'),
new EnvironmentPlugin({
NODE_ENV,
}),
new ServiceWorkerWebpackPlugin({
entry: join(__dirname, '..', 'client', 'sw', 'sw.js'),
excludes: ['*'],
}),
new WebpackBar(),
];
const splitChunks = {
name: 'cloudcmd.common',
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',
];
return !lazyChunks.includes(chunk.name);
},
minChunks: 1,
enforce: true,
priority: -1,
reuseExistingChunk: true,
},
},
};
module.exports = {
export default {
resolve: {
symlinks: false,
alias: {
'node:process': 'process',
'node:path': 'path',
},
fallback: {
path: import.meta.resolve('path-browserify'),
process: import.meta.resolve('process/browser'),
},
},
devtool,
optimization: {
splitChunks,
},
entry: {
cloudcmd: `${dir}/cloudcmd.js`,
'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`,
'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.mjs`,
[`${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}/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}/operation`]: `${dirModules}/operation/index.mjs`,
[`${modules}/konsole`]: `${dirModules}/konsole.js`,
[`${modules}/terminal`]: `${dirModules}/terminal.js`,
[`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`,
@ -101,7 +143,6 @@ module.exports = {
devtoolModuleFilenameTemplate,
publicPath: '/dist/',
},
externals: [convertToWebpack5Externals(externals)],
module: {
rules,
noParse,
@ -113,18 +154,6 @@ module.exports = {
},
};
function externals({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

@ -26,5 +26,5 @@ For example util, console, view, edit, style etc...
**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)

568
ChangeLog
View file

@ -1,3 +1,569 @@
2026.01.21, v19.1.9
feature:
- 75ad4415 cloudcmd: @putout/eslint-flat v4.0.0
- c5d9bd7c client: key: vim: get rid of mock-require
- f437a52f client: images: migrate to EMS
- 7192a56e client: dom: current-file: migrate to ESM
2026.01.20, v19.1.8
fix:
- 8a769fd5 client: modules: operation: no update after copy
feature:
- d574a93d client: key: migrate to ESM
- 3b409074 client: modules: operation: migrate to ESM
- 3b6b0b5a client: buffer: migrate to ESM
- 8876f050 cloudcmd: eslint-plugin-putout v30.0.0
2026.01.17, v19.1.7
feature:
- 23a6a698 client: dom/events -> #dom/events
- 9cebb241 client: dom: events: migrate to ESM
- a94fa0d4 client: cloudcmd: migrate to ESM
- 3bdf47a5 client: migrate to ESM
2026.01.16, v19.1.6
fix:
- a523ef65 tests
feature:
- 64654e8d common: cloudfunc: migrate to ESM
- add31607 common: cloudfunc: get rid of bas64
- e36de00c modulas: migrate to ESM
2026.01.16, v19.1.5
feature:
- 450f1461 client: improve testability
- d979e949 server: env: migrate to ESM
2026.01.15, v19.1.4
feature:
- 6e778a35 client: sort: migrate to ESM
- e27ef51d client: sort: migrate to ESM
- 917f5851 client: load-module: migrate to ESM
- 9950caca client: get-json-from-file-table: migrate to ESM
2026.01.15, v19.1.3
feature:
- f903c5c9 cloudcmd: multi-rename v3.0.0
2026.01.14, v19.1.2
fix:
- 9e2c5ac6 client: edit-names: group rename not renaming (#453)
- f0dcbe94 client: key: config
feature:
- 6856207d server: env -> env.parse
- dc99417c client: key: get rid of mock-require
- 4bb7d704 client: modules: view: get rid of mock-require
2026.01.12, v19.1.1
feature:
- 5cc6f79d cloudcmd: @cloudcmd/stub v5.0.0
- 024bc413 cloudcmd: fullstore v4.0.0
- 53f6f9e7 cloudcmd: globals v17.0.0
- 6d21c539 cloudcmd: madrun v12.1.0
- 253389ea cloudcmd: supertape v12.0.0
2025.12.31, v19.1.0
feature:
- 0ff16314 cloudcmd: redlint v5.0.0
- 43edba8c cloudcmd: try-to-catch v4.0.0
- 06f3b782 cloudcmd: try-catch v4.0.4
- dfcd6557 deno config: add
- ab20a462 server: bun support (oven-sh/bun#25674)
2025.12.24, v19.0.17
feature:
- 0222d177 cloudcmd: gritty v9.0.0
2025.12.05, v19.0.16
feature:
- 14ec19e8 cloudcmd: find-up v8.0.0
- e6a00979 cloudcmd: eslint-plugin-putout v29.0.2
- 5b5352c7 cloudcmd: putout v41.0.0
2025.11.28, v19.0.15
feature:
- 00676531 cloudcmd: aleman v1.16.5
2025.11.27, v19.0.14
fix:
- 2a525e9b aleman: copy paste in text editor (#449)
feature:
- 3ceb9a8c cloudcmd: open v11.0.0
2025.09.26, v19.0.13
feature:
- 8477f3e4 cloudcmd: aleman v1.16.3 (#446)
2025.09.25, v19.0.12
feature:
- 836e908e cloudcmd: aleman v1.16.2
2025.09.24, v19.0.11
feature:
- f4386a6f cloudcmd: aleman v1.16.1
2025.09.23, v19.0.10
feature:
- 2e667ba6 cloudcmd: aleman v1.15.0
2025.09.22, v19.0.9
feature:
- 60c56164 cloudcmd: aleman v1.14.4
2025.09.20, v19.0.8
feature:
- efe81320 cloudcmd: aleman v1.14.3
2025.09.18, v19.0.7
feature:
- 5b972e2e cloudcmd: aleman v1.14.0
2025.09.17, v19.0.6
feature:
- 39a24028 cloudcmd: aleman v1.13.0
2025.09.16, v19.0.5
fix:
- 64df81bc cloudcmd: client: listeners: f9: stopPropagation
feature:
- 38dd5101 cloudcmd: aleman v1.12.4
2025.09.15, v19.0.4
feature:
- 66db798c cloudcmd: aleman v1.12.3
2025.09.15, v19.0.3
feature:
- c5aed16f cloudcmd: aleman v1.12.2
2025.09.14, v19.0.2
feature:
- 511347d3 cloudcmd: aleman v1.11.0
2025.09.14, v19.0.1
fix:
- fc6304a1 tmpl: config: aleman, supermenu
feature:
- a05ecdb4 cloudcmd: aleman v1.10.0
2025.09.14, v19.0.0
feature:
- 50b19dcc cloudcmd: menu: default: supermenu -> aleman
- 5970f10a cloudcmd: drop support of node < 22
2025.09.14, v18.8.11
feature:
- b0360d8e cloudcmd: aleman v1.9.1
- 00a20129 cloudcmd: html: importsmap: add
2025.09.14, v18.8.10
feature:
- ddf9e455 cloudcmd: aleman v1.9.0
2025.09.14, v18.8.9
feature:
- 2e7bdb8a cloudcmd: aleman v1.8.0
2025.09.13, v18.8.8
feature:
- 03631d95 cloudcmd: aleman v1.7.0
2025.09.12, v18.8.7
feature:
- 09408af5 cloudcmd: aleman v1.6.1
2025.09.12, v18.8.6
feature:
- 4fcaf288 cloudcmd: aleman v1.6.0
2025.09.10, v18.8.5
feature:
- c69ec16e cloudcmd: aleman v1.5.0
2025.09.09, v18.8.4
feature:
- 08d13c6d cloudcmd: aleman v1.4.9
2025.09.04, v18.8.3
feature:
- b4792fc3 cloudcmd: aleman v1.4.0
2025.09.04, v18.8.2
feature:
- a0b3285b cloudcmd: aleman v1.3.0
2025.09.04, v18.8.1
feature:
- 15b71c14 cloudcmd: aleman v1.2.5
- d252fe5f robots.txt: add
2025.09.02, v18.8.0
feature:
- 08b5c6b2 client: menu: aleman: add
2025.08.30, v18.7.4
fix:
- a6d18ddb select file: name -> line
- 2077468a client: listeners: click: avoid select on conext menu
- 64e4aba4 client: menu: before show: unsetBind
2025.07.26, v18.7.3
fix:
- 884c83eb client: polyfill (#442)
2025.07.24, v18.7.2
feature:
- 2e775908 cloudcmd: html-looks-like: remove
- bb6a7a28 docker: npm -> bun
2025.07.06, v18.7.1
fix:
- 784bb2eb build: sw
feature:
- 8f52376d cloudcmd: revert: optimize-css-assets-webpack-plugin -> css-minimizer-webpack-plugin: broken spinner
- 82008749 cloudcmd: optimize-css-assets-webpack-plugin -> css-minimizer-webpack-plugin
2025.07.05, v18.7.0
fix:
- b1e231a5 client: menu: close: ESC
feature:
- 546d0610 cloudcmd: process v0.11.10
- 121b114e cloudcmd: path-browserify v1.0.1
- 8592cedc cloudcmd: mini-css-extract-plugin v2.9.2
- a53ab67b cloudcmd: webpack-cli v6.0.1
- de2cedd9 cloudcmd: webpack v5.99.9
- da545ea4 cloudcmd: style-loader v4.0.0
- db6e8334 cloudcmd: optimize-css-assets-webpack-plugin v6.0.1
- 2f0c1a61 cloudcmd: html-webpack-plugin v5.6.3
- e100dcf6 cloudcmd: extract-text-webpack-plugin v3.0.2
- 76c40008 cloudcmd: css-loader v7.1.2
- fb5e5a32 cloudcmd: clean-css-loader v4.2.1
- 8551cd54 cloudcmd: babel-loader v10.0.0
- c9380319 webpack 5
- ddc94adb cloudcmd: eslint-plugin-putout v28.0.0
2025.07.04, v18.6.1
feature:
- 9eafa189 cloudcmd: http-auth v4.2.1
- e99d0847 cloudcmd: montag v1.2.1
- b77e9c91 cloudcmd: pipe-io v4.0.1
- 4b476a6d cloudcmd: globals v16.3.0
- 2057065d cloudcmd: @putout/eslint-flat v3.0.1
2025.07.02, v18.6.0
feature:
- 2eb3dc66 cloudcmd: @iocmd/wait v2.1.0
- 1679b788 cloudcmd: webpackbar v7.0.0
- 9a4cf388 cloudcmd: eslint-plugin-putout v27.2.1
- f4b0f92f cloudcmd: express v5.1.0
- 4ab4be12 thread-it: get rid (#438)
- 99ad0c21 cloudcmd: rm @putout/babel
- 8ccec23d cloudcmd: help: require -> import
- 2a97ac66 cloudcmd: yargs-parser v22.0.0
- b26c8bba cloudcmd: thread-it v3.0.0
2025.04.10, v18.5.2
feature:
- 8450bfa6 cloudcmd: putout v40.0.3
- 51f51b54 cloudcmd: @putout/plugin-cloudcmd v4.0.0
- 08ab63d7 cloudcmd: supertape v11.0.4
- e7cc9b92 cloudcmd: redlint v4.1.1
- 368c9bb8 cloudcmd: eslint v9.23.0
- 43fd5ed6 cloudcmd: madrun v11.0.0
- f774d5b2 cloudcmd: eslint-plugin-putout v26.1.0
- b0a7fc16 cloudcmd: putout v39.3.0
2025.02.03, v18.5.1
feature:
- 467f0a79 cloudcmd: webpack-merge v6.0.1
- 353a1fb6 cloudcmd: putout v38.0.5
- 8e98b778 cloudcmd: eslint-plugin-putout v24.0.0
2025.01.20, v18.5.0
fix:
- ad8e55d8 client: themes -> columns (#434)
feature:
- 2fc503f7 cloudcmd: @putout/babel v3.0.0
2024.12.13, v18.4.1
feature:
- 100e940e cloudcmd: putout v37.0.1
2024.11.22, v18.4.0
fix:
- dff02672 cloudcmd: make manifest.json accessible when authentication is enabled (#428)
2024.11.14, v18.3.0
feature:
- 71dc8dd6 cloudcmd: Add support for Progressive Web App (#426)
2024.11.06, v18.2.1
feature:
- a733d814 css: --is-mobile: add
- f22120dc cloudcmd: prevent unselect being fired on panel click when in mobile view (#422)
- 1a0af863 docker: add image source label to dockerfiles (#421)
- 0446a74d docker: add image source label to dockerfiles (#419)
2024.10.27, v18.2.0
feature:
- ac9abbd3 cloudcmd: eslint-plugin-putout v23.1.0
- 4bc5a783 cloudcmd: add context menu option to toggle file selection (#420)
2024.08.17, v18.1.0
feature:
- ddf4542b cloudcmd: add ability to hide dot files (#307)
2024.08.16, v18.0.2
feature:
- 3d03efbe css: show links in one small screens
2024.08.16, v18.0.1
fix:
- 62ed8411 bin: validateArgs is not a function (#147)
feature:
- 9ec94dee cloudcmd: chalk v5.3.0
2024.08.16, v18.0.0
feature:
- 5e93bcca cloudcmd: rimraf v6.0.1
- 74d1eb7e drop support of node < 20
2024.08.16, v17.4.4
fix:
- a6aa9bbc revert rimraf v6.0.1
feature:
- 282b3d5c cloudcmd: @putout/cli-validate-args v2.0.0
2024.07.27, v17.4.3
feature:
- 6e8348b8 cloudcmd: rimraf v6.0.1
- 61ca7f36 cloudcmd: putout v36.0.2
2024.07.03, v17.4.2
feature:
- ba2d0b36 cloudcmd: just-snake-case v3.2.0
- 4cc47e30 cloudcmd: just-capitalize v3.2.0
- d8451e56 cloudcmd: just-pascal-case v3.2.0
- 6abf327d cloudcmd: package-json v10.0.0
- 2ae6ad34 docker: Dockerimage update Debian12 (#414)
- 05ef0ae4 cloudcmd: c8 v10.1.2
2024.05.06, v17.4.1
feature:
- 154b4bd6 cloudcmd: @cloudcmd/move-files v8.0.0
- c409a2db cloudcmd: copymitter v9.0.0
2024.04.17, v17.4.0
fix:
- 6fb21020 server: route: path traversal
feature:
- 37ab7068 publish container image to GHCR (#409)
2024.04.03, v17.3.3
feature:
- b088b84e cloudcmd: deepword v10.0.0
2024.03.29, v17.3.2
fix:
- f7a6a366 typo: Wraped -> Wrapped
2024.03.29, v17.3.1
feature:
- d7581829 distribute: convert to ESM
2024.03.29, v17.3.0
feature:
- 6bc4f3ec dark theme: add (#332)
- 35622082 route: convert to ESM
2024.03.29, v17.2.1
fix:
- cc134464 client: vim: space
feature:
- e3f89e88 dark mode: add
- c45b23fe css: vars: add
- b1f74c00 css: add vars
2024.03.22, v17.2.0
feature:
- 3e565109 convert to ESM
- 770a0812 pack: get rid of mock-require
- 25d8faea rest: get rid of mock-require
- 401a669a user-menu: get rid of mock-require
- 4e32241d terminal: get rid of mock-require
2024.03.21, v17.1.6
feature:
- e01ee457 server: route: get rid of mock-require
- c7f90901 root: get rid of mock-require
- fcce26d4 cloudfunc: get rid of mock-require
2024.03.20, v17.1.5
feature:
- bf90bf22 server: validate: get rid of mock-require
2024.03.20, v17.1.4
feature:
- 98d3a4cc server: columns: get rid of mock-require
2024.03.18, v17.1.3
feature:
- e080a540 server: cloudcmd: get rid of mock-require
2024.03.18, v17.1.2
fix:
- 857c9700 docker: alpine
feature:
- bf614e1d cloudcmd: redlint v3.13.1
2024.03.16, v17.1.1
feature:
- a92a5a0d cloudcmd: restbox v4.0.0
- c51ba1d8 docker: drop arm v7
2024.03.16, v17.1.0
feature:
- 10d6d2e2 cloudcmd: @types/node-fetch v2.6.11
- 2047cb7a cloudcmd: @cloudcmd/dropbox v5.0.1
- 6b793cca cloudcmd: docker: alpine
- 5fa9fcc5 cloudcmd: pullout v5.0.0
- bc617c17 cloudcmd: serve-once v3.0.1
2024.03.12, v17.0.7
feature:
- 97627dc2 cloudcmd: auto-globals v4.0.0
- 683c865e cloudcmd: gritty v8.0.0
2024.03.11, v17.0.6
fix:
- d928c0b8 cloudcmd: ocker: revert alpine (#406)
2024.03.11, v17.0.5
fix:
- 33201dad cloudcmd: docker: alpine (#406)
2024.02.02, v17.0.4
feature:
- 7ce95450 cloudcmd: deepword v9.0.0
- 1c73e525 cloudcmd: edward v15.0.0
- da967f08 cloudcmd: dword v15.0.0
- f0a6109a cloudcmd: restafary v12.0.0
2024.02.01, v17.0.3
feature:
- aca4119f cloudcmd: inly v5.0.0
2024.02.01, v17.0.2
feature:
- 5324a41a cloudcmd: supertape v10.0.0
- d453a1b2 cloudcmd: onezip v6.0.1
- 71b915be cloudcmd: @cloudcmd/fileop v8.0.0
2024.01.25, v17.0.1
feature:
- d79a5776 cloudcmd: putout v35.0.0
- 8d92aa91 cloudcmd: package-json v9.0.0
- 5ab5576e cloudcmd: open v10.0.3
- c5cfe68c cloudcmd: c8 v9.1.0
2023.12.12, v17.0.0
feature:
@ -5498,7 +6064,7 @@ fix:
- (rest) onDelete: func(null, body) -> func
- (rest) onStat: add var
- (cloudcmd) change index path
- (server) start: url -> URL
- (server) start: url -> PREFIX
- (server) start: SSLPort -> sslPort
- (server) start: Port -> port
- (dom) getCurrentDirPath: "," -> ";"

364
HELP.md
View file

@ -1,11 +1,11 @@
# Cloud Commander v17.0.0
# Cloud Commander v19.1.9
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
[MainURL]: https://cloudcmd.io "Main"
[BlogURL]: https://blog.cloudcmd.io "Blog"
[SupportURL]: https://patreon.com/coderaiser "Patreon"
[DemoURL]: https://cloudcmd.onrender.com/
[DemoURL]: https://cloudcmd-zdp6.onrender.com/
[DWORD]: https://github.com/cloudcmd/dword "Editor based on CodeMirror"
[EDWARD]: https://github.com/cloudcmd/edward "Editor based on Ace"
[DEEPWORD]: https://github.com/cloudcmd/deepword "Editor based on Monaco"
@ -59,81 +59,84 @@ cloudcmd
Cloud Commander supports the following command-line parameters:
|Parameter |Operation
|:------------------------------|:------------------------------
| `-h, --help` | display help and exit
| `-v, --version` | display version and exit
| `-s, --save` | save configuration
| `-o, --online` | load scripts from remote servers
| `-a, --auth` | enable authorization
| `-u, --username` | set username
| `-p, --password` | set password
| `-c, --config` | configuration file path
| `--show-config` | show config values
| `--show-file-name` | show file name in view and edit
| `--editor` | set editor: "dword", "edward" or "deepword"
| `--packer` | set packer: "tar" or "zip"
| `--root` | set root directory
| `--prefix` | set url prefix
| `--prefix-socket` | set prefix for url connection
| `--port` | set port number
| `--confirm-copy` | confirm copy
| `--confirm-move` | confirm move
| `--open` | open web browser when server starts
| `--name` | set tab name in web browser
| `--one-file-panel` | show one file panel
| `--keys-panel` | show keys panel
| `--contact` | enable contact
| `--config-dialog` | enable config dialog
| `--config-auth` | enable auth change in config dialog
| `--console` | enable console
| `--sync-console-path` | sync console path
| `--terminal` | enable terminal
| `--terminal-path` | set terminal path
| `--terminal-command` | set command to run in terminal (shell by default)
| `--terminal-auto-restart` | restart command on exit
| `--vim` | enable vim hot keys
| `--columns` | set visible columns
| `--export` | enable export of config through a server
| `--export-token` | authorization token used by export server
| `--import` | enable import of config
| `--import-token` | authorization token used to connect to export server
| `--import-url` | url of an import server
| `--import-listen` | enable listen on config updates from import server
| `--dropbox` | enable dropbox integration
| `--dropbox-token` | set dropbox token
| `--log` | enable logging
| `--no-show-config` | do not show config values
| `--no-server` | do not start server
| `--no-auth` | disable authorization
| `--no-online` | load scripts from local server
| `--no-open` | do not open web browser when server started
| `--no-name` | set default tab name in web browser
| `--no-keys-panel` | hide keys panel
| `--no-one-file-panel` | show two file panels
| `--no-confirm-copy` | do not confirm copy
| `--no-confirm-move` | do not confirm move
| `--no-config-dialog` | disable config dialog
| `--no-config-auth` | disable auth change in config dialog
| `--no-console` | disable console
| `--no-sync-console-path` | do not sync console path
| `--no-contact` | disable contact
| `--no-terminal` | disable terminal
| `--no-terminal-command` | set default shell to run in terminal
| `--no-terminal-auto-restart` | do not restart command on exit
| `--no-vim` | disable vim hot keys
| `--no-columns` | set default visible columns
| `--no-export` | disable export config through a server
| `--no-import` | disable import of config
| `--no-import-listen` | disable listen on config updates from import server
| `--no-show-file-name` | do not show file name in view and edit
| `--no-dropbox` | disable dropbox integration
| `--no-dropbox-token` | unset dropbox token
| `--no-log` | disable logging
| Parameter |Operation
|:-----------------------------|:------------------------------
| `-h, --help` | display help and exit
| `-v, --version` | display version and exit
| `-s, --save` | save configuration
| `-o, --online` | load scripts from remote servers
| `-a, --auth` | enable authorization
| `-u, --username` | set username
| `-p, --password` | set password
| `-c, --config` | configuration file path
| `--show-config` | show config values
| `--show-dot-files` | show dot files
| `--show-file-name` | show file name in view and edit
| `--editor` | set editor: "dword", "edward" or "deepword"
| `--packer` | set packer: "tar" or "zip"
| `--root` | set root directory
| `--prefix` | set url prefix
| `--prefix-socket` | set prefix for url connection
| `--port` | set port number
| `--confirm-copy` | confirm copy
| `--confirm-move` | confirm move
| `--open` | open web browser when server starts
| `--name` | set tab name in web browser
| `--menu` | set menu: "supermenu" or "aleman"
| `--one-file-panel` | show one file panel
| `--keys-panel` | show keys panel
| `--contact` | enable contact
| `--config-dialog` | enable config dialog
| `--config-auth` | enable auth change in config dialog
| `--console` | enable console
| `--sync-console-path` | sync console path
| `--terminal` | enable terminal
| `--terminal-path` | set terminal path
| `--terminal-command` | set command to run in terminal (shell by default)
| `--terminal-auto-restart` | restart command on exit
| `--vim` | enable vim hot keys
| `--columns` | set visible columns
| `--theme` | set theme 'light' or 'dark'"
| `--export` | enable export of config through a server
| `--export-token` | authorization token used by export server
| `--import` | enable import of config
| `--import-token` | authorization token used to connect to export server
| `--import-url` | url of an import server
| `--import-listen` | enable listen on config updates from import server
| `--dropbox` | enable dropbox integration
| `--dropbox-token` | set dropbox token
| `--log` | enable logging
| `--no-show-config` | do not show config values
| `--no-server` | do not start server
| `--no-auth` | disable authorization
| `--no-online` | load scripts from local server
| `--no-open` | do not open web browser when server started
| `--no-name` | set default tab name in web browser
| `--no-keys-panel` | hide keys panel
| `--no-one-file-panel` | show two file panels
| `--no-confirm-copy` | do not confirm copy
| `--no-confirm-move` | do not confirm move
| `--no-config-dialog` | disable config dialog
| `--no-config-auth` | disable auth change in config dialog
| `--no-console` | disable console
| `--no-sync-console-path` | do not sync console path
| `--no-contact` | disable contact
| `--no-terminal` | disable terminal
| `--no-terminal-command` | set default shell to run in terminal
| `--no-terminal-auto-restart` | do not restart command on exit
| `--no-vim` | disable vim hot keys
| `--no-themes` | set default visible themes
| `--no-export` | disable export config through a server
| `--no-import` | disable import of config
| `--no-import-listen` | disable listen on config updates from import server
| `--no-show-file-name` | do not show file name in view and edit
| `--no-dropbox` | disable dropbox integration
| `--no-dropbox-token` | unset dropbox token
| `--no-log` | disable logging
For options not specified by command-line parameters, Cloud Commander then reads configuration data from `~/.cloudcmd.json`. It uses port `8000` by default.
To begin using the web client, go to this URL in your browser:
To begin using the web client, go to this PREFIX in your browser:
```
http://localhost:8000
@ -151,56 +154,58 @@ Then, start the server again with `cloudcmd` and reload the page.
## Hot keys
|Key |Operation
|:----------------------|:--------------------------------------------
| `F1` | help
| `F2` | show `user menu`
| `F3` | view, change directory
| `Shift + F3` | view raw file, change directory
| `F4` | edit
| `F5` | copy
| `Alt` + `F5` | pack
| `F6` | rename/move
| `Shift` + `F6` | rename current file
| `F7` | new directory
| `Shift + F7` | new file
| `F8`, `Delete` | remove
| `Shift + Delete` | remove without prompt
| `F9` | menu
| `Alt` + `F9` | extract
| `F10` | config
| `*` | select/unselect all
| `+` | expand selection
| `-` | shrink selection
| `:` | open Command Line
| `Ctrl + X` | cut to buffer
| `Ctrl + C` | copy to buffer
| `Ctrl + V` | paste from buffer
| `Ctrl + Z` | clear buffer
| `Ctrl + P` | copy path
| `Ctrl + R` | refresh
| `Ctrl + D` | clear local storage
| `Ctrl + A` | select all files in a panel
| `Ctrl + M` | [rename selected files](https://github.com/coderaiser/cloudcmd/releases/tag/v12.1.0) in editor
| `Ctrl + U` | swap panels
| `Ctrl + F3` | sort by name
| `Ctrl + F5` | sort by date
| `Ctrl + F6` | sort by size
| `Up`, `Down` | file system navigation
| `Enter` | change directory/view file
| `Alt + Left/Right` | show content of directory under cursor in target panel
| `Alt + G` | go to directory
| `Ctrl + \` | go to the root directory
| `Tab` | move via panels
| `Page Up` | up on one page
| `Page Down` | down on one page
| `Home` | to begin of list
| `End` | to end of list
| `Space` | select current file (and get size of directory)
| `Insert` | select current file (and move to next)
| `F9` | context menu
| `~` | console
| `Esc` | toggle vim hotkeys (`file manager`, `editor`)
| Key |Operation
|:---------------------|:--------------------------------------------
| `F1` | help
| `F2` | show `user menu`
| `F3` | view, change directory
| `Shift + F3` | view raw file, change directory
| `F4` | edit
| `F5` | copy
| `Alt` + `F5` | pack
| `F6` | rename/move
| `Shift` + `F6` | rename current file
| `F7` | new directory
| `Shift + F7` | new file
| `F8`, `Delete` | remove
| `Shift + Delete` | remove without prompt
| `F9` | menu
| `Alt` + `F9` | extract
| `F10` | config
| `*` | select/unselect all
| `+` | expand selection
| `-` | shrink selection
| `:` | open Command Line
| `Ctrl + X` | cut to buffer
| `Ctrl + C` | copy to buffer
| `Ctrl + V` | paste from buffer
| `Ctrl + Z` | clear buffer
| `Ctrl + P` | copy path
| `Ctrl + R` | refresh
| `Ctrl + D` | clear local storage
| `Ctrl + A` | select all files in a panel
| `Ctrl + M` | [rename selected files](https://github.com/coderaiser/cloudcmd/releases/tag/v12.1.0) in editor
| `Ctrl + U` | swap panels
| `Ctrl + F3` | sort by name
| `Ctrl + F5` | sort by date
| `Ctrl + F6` | sort by size
| `Ctrl + Command + .` | show/hide dot files
| `Up` | move cursor up
| `Down` | move cursor down
| `Enter` | change directory/view file
| `Alt + Left/Right` | show content of directory under cursor in target panel
| `Alt + G` | go to directory
| `Ctrl + \` | go to the root directory
| `Tab` | move via panels
| `Page Up` | up on one page
| `Page Down` | down on one page
| `Home` | to begin of list
| `End` | to end of list
| `Space` | select current file (and get size of directory)
| `Insert` | select current file (and move to next)
| `F9` | context menu
| `~` | console
| `Esc` | toggle vim hotkeys (`file manager`, `editor`)
### Vim
@ -397,6 +402,7 @@ Here's a description of all options:
"confirmCopy": true, // confirm copy
"confirmMove": true, // confirm move
"showConfig": false, // show config at startup
"showDotFiles": true, // show dot files
"showFileName": false, // do not show file name in view and edit
"contact": true, // enable contact
"configDialog": true, // enable config dialog
@ -408,7 +414,7 @@ Here's a description of all options:
"terminalCommand": "", // set command to run in terminal
"terminalAutoRestart": true, // restart command on exit
"vim": false, // disable vim hot keys
"columns": "name-size-date-owner-mode", // set visible columns
"themes": "name-size-date-owner-mode", // set visible themes
"export": false, // enable export of config through a server
"exportToken": "root", // token used by export server
"import": false, // enable import of config
@ -428,7 +434,9 @@ Some config options can be overridden with environment variables, such as:
- `CLOUDCMD_NAME` - set tab name in web browser
- `CLOUDCMD_OPEN` - open web browser when server started
- `CLOUDCMD_EDITOR` - set editor
- `CLOUDCMD_COLUMNS` - set visible columns
- `CLOUDCMD_COLUMNS` - set visible themes
- `CLOUDCMD_THEME` - set themes "light" or "dark"
- `CLOUDCMD_MENU` - set menu "supermenu" or "aleman"
- `CLOUDCMD_CONTACT` - enable contact
- `CLOUDCMD_CONFIG_DIALOG` - enable config dialog
- `CLOUDCMD_CONFIG_AUTH` - enable auth change in config dialog
@ -639,10 +647,20 @@ Right-mouse click to show a context menu with these items:
### Hot keys
|Key |Operation
|:----------------------|:--------------------------------------------
| `F9` | open
| `Esc` | close
| Key | Operation |
|:-------------|:------------------------|
| `F9` | open |
| `Esc` | close |
| `Up`, `j` | move cursor up |
| `Down`, `k` | move cursor down |
| `Left`, `h` | close submenu |
| `Right`, `l` | open submenu |
| `G` or `$` | navigate to bottom |
| `gg` or `^` | navigate to top |
Commands can be joined, for example:
- `5j` will navigate **5** items below current;
## One file panel
@ -1093,6 +1111,90 @@ There are a lot of ways to be involved in `Cloud Commander` development:
## Version history
- *2026.01.21*, **[v19.1.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.9)**
- *2026.01.20*, **[v19.1.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.8)**
- *2026.01.17*, **[v19.1.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.7)**
- *2026.01.16*, **[v19.1.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.6)**
- *2026.01.16*, **[v19.1.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.5)**
- *2026.01.15*, **[v19.1.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.4)**
- *2026.01.15*, **[v19.1.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.3)**
- *2026.01.14*, **[v19.1.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.2)**
- *2026.01.12*, **[v19.1.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.1)**
- *2025.12.31*, **[v19.1.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.0)**
- *2025.12.24*, **[v19.0.17](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.17)**
- *2025.12.05*, **[v19.0.16](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.16)**
- *2025.11.28*, **[v19.0.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.15)**
- *2025.11.27*, **[v19.0.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.14)**
- *2025.09.26*, **[v19.0.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.13)**
- *2025.09.25*, **[v19.0.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.12)**
- *2025.09.24*, **[v19.0.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.11)**
- *2025.09.23*, **[v19.0.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.10)**
- *2025.09.22*, **[v19.0.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.9)**
- *2025.09.20*, **[v19.0.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.8)**
- *2025.09.18*, **[v19.0.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.7)**
- *2025.09.17*, **[v19.0.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.6)**
- *2025.09.16*, **[v19.0.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.5)**
- *2025.09.15*, **[v19.0.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.4)**
- *2025.09.15*, **[v19.0.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.3)**
- *2025.09.14*, **[v19.0.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.2)**
- *2025.09.14*, **[v19.0.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.1)**
- *2025.09.14*, **[v19.0.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.0)**
- *2025.09.14*, **[v18.8.11](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.11)**
- *2025.09.14*, **[v18.8.10](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.10)**
- *2025.09.14*, **[v18.8.9](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.9)**
- *2025.09.13*, **[v18.8.8](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.8)**
- *2025.09.12*, **[v18.8.7](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.7)**
- *2025.09.12*, **[v18.8.6](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.6)**
- *2025.09.10*, **[v18.8.5](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.5)**
- *2025.09.09*, **[v18.8.4](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.4)**
- *2025.09.04*, **[v18.8.3](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.3)**
- *2025.09.04*, **[v18.8.2](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.2)**
- *2025.09.04*, **[v18.8.1](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.1)**
- *2025.09.02*, **[v18.8.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.0)**
- *2025.08.30*, **[v18.7.4](//github.com/coderaiser/cloudcmd/releases/tag/v18.7.4)**
- *2025.07.26*, **[v18.7.3](//github.com/coderaiser/cloudcmd/releases/tag/v18.7.3)**
- *2025.07.24*, **[v18.7.2](//github.com/coderaiser/cloudcmd/releases/tag/v18.7.2)**
- *2025.07.06*, **[v18.7.1](//github.com/coderaiser/cloudcmd/releases/tag/v18.7.1)**
- *2025.07.05*, **[v18.7.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.7.0)**
- *2025.07.04*, **[v18.6.1](//github.com/coderaiser/cloudcmd/releases/tag/v18.6.1)**
- *2025.07.02*, **[v18.6.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.6.0)**
- *2025.04.10*, **[v18.5.2](//github.com/coderaiser/cloudcmd/releases/tag/v18.5.2)**
- *2025.02.03*, **[v18.5.1](//github.com/coderaiser/cloudcmd/releases/tag/v18.5.1)**
- *2025.01.20*, **[v18.5.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.5.0)**
- *2024.12.13*, **[v18.4.1](//github.com/coderaiser/cloudcmd/releases/tag/v18.4.1)**
- *2024.11.22*, **[v18.4.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.4.0)**
- *2024.11.14*, **[v18.3.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.3.0)**
- *2024.11.06*, **[v18.2.1](//github.com/coderaiser/cloudcmd/releases/tag/v18.2.1)**
- *2024.10.27*, **[v18.2.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.2.0)**
- *2024.08.17*, **[v18.1.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.1.0)**
- *2024.08.16*, **[v18.0.2](//github.com/coderaiser/cloudcmd/releases/tag/v18.0.2)**
- *2024.08.16*, **[v18.0.1](//github.com/coderaiser/cloudcmd/releases/tag/v18.0.1)**
- *2024.08.16*, **[v18.0.0](//github.com/coderaiser/cloudcmd/releases/tag/v18.0.0)**
- *2024.08.16*, **[v17.4.4](//github.com/coderaiser/cloudcmd/releases/tag/v17.4.4)**
- *2024.07.27*, **[v17.4.3](//github.com/coderaiser/cloudcmd/releases/tag/v17.4.3)**
- *2024.07.03*, **[v17.4.2](//github.com/coderaiser/cloudcmd/releases/tag/v17.4.2)**
- *2024.05.06*, **[v17.4.1](//github.com/coderaiser/cloudcmd/releases/tag/v17.4.1)**
- *2024.04.17*, **[v17.4.0](//github.com/coderaiser/cloudcmd/releases/tag/v17.4.0)**
- *2024.04.03*, **[v17.3.3](//github.com/coderaiser/cloudcmd/releases/tag/v17.3.3)**
- *2024.03.29*, **[v17.3.2](//github.com/coderaiser/cloudcmd/releases/tag/v17.3.2)**
- *2024.03.29*, **[v17.3.1](//github.com/coderaiser/cloudcmd/releases/tag/v17.3.1)**
- *2024.03.29*, **[v17.3.0](//github.com/coderaiser/cloudcmd/releases/tag/v17.3.0)**
- *2024.03.29*, **[v17.2.1](//github.com/coderaiser/cloudcmd/releases/tag/v17.2.1)**
- *2024.03.22*, **[v17.2.0](//github.com/coderaiser/cloudcmd/releases/tag/v17.2.0)**
- *2024.03.21*, **[v17.1.6](//github.com/coderaiser/cloudcmd/releases/tag/v17.1.6)**
- *2024.03.20*, **[v17.1.5](//github.com/coderaiser/cloudcmd/releases/tag/v17.1.5)**
- *2024.03.20*, **[v17.1.4](//github.com/coderaiser/cloudcmd/releases/tag/v17.1.4)**
- *2024.03.18*, **[v17.1.3](//github.com/coderaiser/cloudcmd/releases/tag/v17.1.3)**
- *2024.03.18*, **[v17.1.2](//github.com/coderaiser/cloudcmd/releases/tag/v17.1.2)**
- *2024.03.16*, **[v17.1.1](//github.com/coderaiser/cloudcmd/releases/tag/v17.1.1)**
- *2024.03.16*, **[v17.1.0](//github.com/coderaiser/cloudcmd/releases/tag/v17.1.0)**
- *2024.03.12*, **[v17.0.7](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.7)**
- *2024.03.11*, **[v17.0.6](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.6)**
- *2024.03.11*, **[v17.0.5](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.5)**
- *2024.02.02*, **[v17.0.4](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.4)**
- *2024.02.01*, **[v17.0.3](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.3)**
- *2024.02.01*, **[v17.0.2](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.2)**
- *2024.01.25*, **[v17.0.1](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.1)**
- *2023.12.12*, **[v17.0.0](//github.com/coderaiser/cloudcmd/releases/tag/v17.0.0)**
- *2023.12.08*, **[v16.18.0](//github.com/coderaiser/cloudcmd/releases/tag/v16.18.0)**
- *2023.12.04*, **[v16.17.9](//github.com/coderaiser/cloudcmd/releases/tag/v16.17.9)**
@ -1529,7 +1631,7 @@ There are a lot of ways to be involved in `Cloud Commander` development:
## Special Thanks
- [Olena Zalitok](http://www.linkedin.com/in/olena-zalitok-ux-designer "Olena Zalitok") for **logo** and **favicon**.
- [Olena Zalitok](https://www.linkedin.com/in/ozalitok-ux-ui/ "Olena Zalitok") for **logo** and **favicon**.
- [TarZak](https://github.com/tarzak "TarZak")
- Russian and Ukrainian translations;
- config template and style;

View file

@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2012-2022 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
a copy of this software and associated documentation files (the

View file

@ -1,11 +1,11 @@
# Cloud Commander v17.0.0 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL]
# Cloud Commander v19.1.9 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL]
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
[MainURL]: https://cloudcmd.io "Main"
[BlogURL]: https://blog.cloudcmd.io "Blog"
[SupportURL]: https://patreon.com/coderaiser "Patreon"
[DemoURL]: https://cloudcmd.onrender.com/
[DemoURL]: https://cloudcmd-zdp6.onrender.com
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
[BuildStatusURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml "Build Status"
[BuildStatusIMGURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml/badge.svg
@ -126,10 +126,8 @@ The docker images are provided for multiple architectures and types. The followi
| Architecture | Type |
|----------------|--------------|
| amd64 | linux |
| arm/v7 | linux |
| arm64 (arm/v8) | linux |
| amd64 | linux-alpine |
| arm/v7 | 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:

View file

@ -1,29 +1,27 @@
#!/usr/bin/env node
import process from 'node:process';
import {createRequire} from 'node:module';
import {promisify} from 'node:util';
import tryToCatch from 'try-to-catch';
import {tryToCatch} from 'try-to-catch';
import {createSimport} from 'simport';
import parse from 'yargs-parser';
import process from 'node:process';
import exit from '../server/exit.js';
import {
createConfig,
configPath,
} from '../server/config.js';
import env from '../server/env.js';
import {createConfig, configPath} from '../server/config.mjs';
import * as env from '../server/env.mjs';
import prefixer from '../server/prefixer.js';
import * as validate from '../server/validate.mjs';
process.on('unhandledRejection', exit);
const require = createRequire(import.meta.url);
const Info = require('../package.json');
const isUndefined = (a) => typeof a === 'undefined';
const simport = createSimport(import.meta.url);
const choose = (a, b) => {
if (a === undefined)
if (isUndefined(a))
return b;
return a;
@ -64,6 +62,8 @@ const yargsOptions = {
'terminal-path',
'terminal-command',
'columns',
'menu',
'theme',
'import-url',
'import-token',
'export-token',
@ -87,6 +87,7 @@ const yargsOptions = {
'confirm-copy',
'confirm-move',
'show-config',
'show-dot-files',
'show-file-name',
'vim',
'keys-panel',
@ -100,43 +101,46 @@ const yargsOptions = {
],
default: {
'server': true,
'name': choose(env('name'), config('name')),
'name': choose(env.parse('name'), config('name')),
'auth': choose(env.bool('auth'), config('auth')),
'port': config('port'),
'online': config('online'),
'open': choose(env.bool('open'), config('open')),
'editor': env('editor') || config('editor'),
'editor': env.parse('editor') || config('editor'),
'menu': env.parse('menu') || config('menu'),
'packer': config('packer') || 'tar',
'zip': config('zip'),
'username': env('username') || config('username'),
'root': choose(env('root'), config('root')),
'prefix': choose(env('prefix'), config('prefix')),
'username': env.parse('username') || config('username'),
'root': choose(env.parse('root'), config('root')),
'prefix': choose(env.parse('prefix'), config('prefix')),
'console': choose(env.bool('console'), config('console')),
'contact': choose(env.bool('contact'), config('contact')),
'terminal': choose(env.bool('terminal'), config('terminal')),
'columns': env('columns') || config('columns') || '',
'columns': env.parse('columns') || config('columns') || '',
'theme': env.parse('theme') || config('theme') || '',
'vim': choose(env.bool('vim'), config('vim')),
'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': choose(env.bool('import'), config('import')),
'export': choose(env.bool('export'), config('export')),
'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')),
'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')),
'config-dialog': choose(env.bool('config_dialog'), config('configDialog')),
'config-auth': choose(env.bool('config_auth'), config('configAuth')),
'terminal-path': env('terminal_path') || config('terminalPath'),
'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')),
'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')),
'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')),
'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')),
'keys-panel': env.bool('keys_panel') || config('keysPanel'),
'import-token': env('import_token') || config('importToken'),
'export-token': env('export_token') || config('exportToken'),
'import-token': env.parse('import_token') || config('importToken'),
'export-token': env.parse('export_token') || config('exportToken'),
'dropbox': config('dropbox'),
'dropbox-token': config('dropboxToken') || '',
@ -164,7 +168,7 @@ else
main();
async function main() {
const validateArgs = await simport('@putout/cli-validate-args');
const {validateArgs} = await simport('@putout/cli-validate-args');
const error = await validateArgs(args, [
...yargsOptions.boolean,
@ -177,6 +181,9 @@ async function main() {
if (args.repl)
repl();
validate.columns(args.columns);
validate.theme(args.theme);
port(args.port);
config('name', args.name);
@ -186,6 +193,7 @@ async function main() {
config('username', args.username);
config('console', args.console);
config('syncConsolePath', args.syncConsolePath);
config('showDotFiles', args.showDotFiles);
config('showFileName', args.showFileName);
config('contact', args.contact);
config('terminal', args.terminal);
@ -193,10 +201,12 @@ async function main() {
config('terminalCommand', args.terminalCommand);
config('terminalAutoRestart', args.terminalAutoRestart);
config('editor', args.editor);
config('menu', args.menu);
config('prefix', prefixer(args.prefix));
config('prefixSocket', prefixer(args.prefixSocket));
config('root', args.root || '/');
config('vim', args.vim);
config('theme', args.theme);
config('columns', args.columns);
config('log', args.log);
config('confirmCopy', args.confirmCopy);
@ -224,20 +234,22 @@ async function main() {
prefix: config('prefix'),
prefixSocket: config('prefixSocket'),
columns: config('columns'),
theme: config('theme'),
menu: config('menu'),
};
const password = env('password') || args.password;
const password = env.parse('password') || args.password;
if (password)
config('password', await getPassword(password));
await validateRoot(options.root, config);
validateRoot(options.root, config);
if (args.showConfig)
await showConfig();
const distribute = await simport('../server/distribute/index.js');
const importConfig = promisify(distribute.import);
const {distributeImport} = await simport('../server/distribute/import.mjs');
const importConfig = promisify(distributeImport);
await start(options, config);
@ -248,8 +260,7 @@ async function main() {
await importConfig(config);
}
async function validateRoot(root, config) {
const validate = await simport(`${DIR_SERVER}validate.js`);
function validateRoot(root, config) {
validate.root(root, config);
if (root === '/')
@ -309,7 +320,12 @@ async function readConfig(name) {
}
async function help() {
const bin = require('../json/help.json');
const {default: bin} = await import('../json/help.json', {
with: {
type: 'json',
},
});
const forEachKey = await simport('for-each-key');
const currify = await simport('currify');

View file

@ -1,13 +1,13 @@
#!/usr/bin/env node
import {promisify} from 'node:util';
import tryToCatch from 'try-to-catch';
import process from 'node:process';
import {tryToCatch} from 'try-to-catch';
import {createSimport} from 'simport';
import minor from 'minor';
import _place from 'place';
import rendy from 'rendy';
import shortdate from 'shortdate';
import process from 'node:process';
const simport = createSimport(import.meta.url);
const place = promisify(_place);
@ -19,7 +19,8 @@ await main();
async function main() {
const history = '## Version history\n\n';
const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
const template = '- *{{ date }}*, ' +
const template = '- ' +
'*{{ date }}*, ' +
'**[v{{ version }}]' +
'(' + link +
'v{{ version }})**\n';

View file

@ -1,30 +1,24 @@
'use strict';
const process = require('process');
import process from 'node:process';
/* global DOM */
const Emitify = require('emitify');
const inherits = require('inherits');
const rendy = require('rendy');
const load = require('load.js');
const tryToCatch = require('try-to-catch');
const {addSlashToEnd} = require('format-io');
const pascalCase = require('just-pascal-case');
const currify = require('currify');
const Images = require('./dom/images');
const {unregisterSW} = require('./sw/register');
const getJsonFromFileTable = require('./get-json-from-file-table');
const Key = require('./key');
const {
import Emitify from 'emitify';
import inherits from 'inherits';
import rendy from 'rendy';
import load from 'load.js';
import {tryToCatch} from 'try-to-catch';
import {addSlashToEnd} from 'format-io';
import pascalCase from 'just-pascal-case';
import currify from 'currify';
import * as Images from './dom/images.mjs';
import {unregisterSW} from './sw/register.js';
import {getJsonFromFileTable} from './get-json-from-file-table.mjs';
import {Key} from './key/index.mjs';
import {
apiURL,
formatMsg,
buildFromJSON,
} = require('../common/cloudfunc');
const loadModule = require('./load-module');
} from '../common/cloudfunc.mjs';
import {loadModule} from './load-module.mjs';
const noJS = (a) => a.replace(/.js$/, '');
@ -32,16 +26,19 @@ const isDev = process.env.NODE_ENV === 'development';
inherits(CloudCmdProto, Emitify);
module.exports = new CloudCmdProto(DOM);
export const createCloudCmd = ({DOM, Listeners}) => {
return new CloudCmdProto({
DOM,
Listeners,
});
};
load.addErrorListener((e, src) => {
const msg = `file ${src} could not be loaded`;
Images.show.error(msg);
});
function CloudCmdProto(DOM) {
let Listeners;
function CloudCmdProto({DOM, Listeners}) {
Emitify.call(this);
const CloudCmd = this;
@ -49,21 +46,16 @@ function CloudCmdProto(DOM) {
const {Storage, Files} = DOM;
this.log = (...a) => {
this.log = () => {
if (!isDev)
return;
console.log(...a);
};
this.prefix = '';
this.prefixSocket = '';
this.prefixURL = '';
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.TITLE = 'Cloud Commander';
this.sort = {
left: 'name',
right: 'name',
@ -74,13 +66,15 @@ function CloudCmdProto(DOM) {
right: 'asc',
};
this.changeDir = async (path, {
isRefresh,
panel,
history = true,
noCurrent,
currentName,
} = {}) => {
this.changeDir = async (path, overrides = {}) => {
const {
isRefresh,
panel,
history = true,
noCurrent,
currentName,
} = overrides;
const refresh = isRefresh;
let panelChanged;
@ -95,15 +89,17 @@ function CloudCmdProto(DOM) {
imgPosition = 'top';
Images.show.load(imgPosition, panel);
/* загружаем содержимое каталога */
await ajaxLoad(addSlashToEnd(path), {
refresh,
history,
noCurrent,
currentName,
showDotFiles: CloudCmd.config('showDotFiles'),
}, panel);
};
/**
* Конструктор CloudClient, который
* выполняет весь функционал по
@ -137,18 +133,10 @@ function CloudCmdProto(DOM) {
await initModules();
await baseInit();
await loadStyle();
CloudCmd.route(location.hash);
};
async function loadStyle() {
const {prefix} = CloudCmd;
const name = `${prefix}/dist/cloudcmd.common.css`;
await load.css(name);
}
this.route = (path) => {
const query = path.split('/');
@ -158,7 +146,7 @@ function CloudCmdProto(DOM) {
const [kebabModule] = query;
const module = noJS(pascalCase(kebabModule.slice(1)));
const file = query[1];
const [, file] = query;
const current = DOM.getCurrentByName(file);
if (file && !current) {
@ -216,7 +204,7 @@ function CloudCmdProto(DOM) {
async function saveCurrentName(currentName) {
await Storage.set('current-name', currentName);
}
async function baseInit() {
const files = DOM.getFiles();
@ -237,7 +225,6 @@ function CloudCmdProto(DOM) {
const dirPath = DOM.getCurrentDirPath();
Listeners = CloudCmd.Listeners;
Listeners.init();
const panels = getPanels();
@ -253,7 +240,7 @@ function CloudCmdProto(DOM) {
if (!data)
await Storage.setJson(dirPath, getJsonFromFileTable());
}
function getPanels() {
const panels = ['left'];
@ -265,7 +252,7 @@ function CloudCmdProto(DOM) {
'right',
];
}
this.execFromModule = async (moduleName, funcName, ...args) => {
await CloudCmd[moduleName]();
@ -290,7 +277,7 @@ function CloudCmdProto(DOM) {
currentName,
});
};
/**
* Функция загружает json-данные о Файловой Системе
* через ajax-запрос.
@ -298,7 +285,6 @@ function CloudCmdProto(DOM) {
* @param options
* { refresh, history } - необходимость обновить данные о каталоге
* @param panel
* @param callback
*
*/
async function ajaxLoad(path, options = {}, panel) {
@ -343,16 +329,19 @@ function CloudCmdProto(DOM) {
Storage.setJson(path, newObj);
}
/**
* Функция строит файловую таблицу
* @param json - данные о файлах
* @param data - данные о файлах
* @param panelParam
* @param history
* @param callback
* @param options - history, noCurrent, showDotFiles
*/
async function createFileTable(data, panelParam, options) {
const {history, noCurrent} = options;
const {
history,
noCurrent,
showDotFiles,
} = options;
const names = [
'file',
@ -383,6 +372,7 @@ function CloudCmdProto(DOM) {
data,
id: panel.id,
prefix,
showDotFiles,
template: {
file,
path,
@ -409,7 +399,7 @@ function CloudCmdProto(DOM) {
CloudCmd.emit('active-dir', Info.dirPath);
}
}
this.goToParentDir = async () => {
const {
dir,

View file

@ -1,65 +0,0 @@
'use strict';
const process = require('process');
require('../css/main.css');
require('../css/nojs.css');
require('../css/columns/name-size-date.css');
require('../css/columns/name-size.css');
const wraptile = require('wraptile');
const load = require('load.js');
const {registerSW, listenSW} = require('./sw/register');
const isDev = process.env.NODE_ENV === 'development';
module.exports = window.CloudCmd = async (config) => {
window.Util = require('../common/util');
window.CloudFunc = require('../common/cloudfunc');
window.DOM = require('./dom');
window.CloudCmd = require('./client');
await register(config);
require('./listeners');
require('./key');
require('./sort');
const prefix = getPrefix(config.prefix);
window.CloudCmd.init(prefix, config);
};
function getPrefix(prefix) {
if (!prefix)
return '';
if (!prefix.indexOf('/'))
return prefix;
return `/${prefix}`;
}
const onUpdateFound = wraptile(async (config) => {
if (isDev)
return;
const {DOM} = window;
const prefix = getPrefix(config.prefix);
await load.js(`${prefix}/dist/cloudcmd.common.js`);
await load.js(`${prefix}/dist/cloudcmd.js`);
console.log('cloudcmd: sw: updated');
DOM.Events.removeAll();
window.CloudCmd(config);
});
async function register(config) {
const {prefix} = config;
const sw = await registerSW(prefix);
listenSW(sw, 'updatefound', onUpdateFound(config));
}

74
client/cloudcmd.mjs Normal file
View file

@ -0,0 +1,74 @@
import process from 'node:process';
import wraptile from 'wraptile';
import load from 'load.js';
import '../css/main.css';
import {registerSW, listenSW} from './sw/register.js';
import {initSortPanel, sortPanel} from './sort.mjs';
import Util from '../common/util.js';
import * as CloudFunc from '../common/cloudfunc.mjs';
import DOM from './dom/index.js';
import {createCloudCmd} from './client.mjs';
import * as Listeners from './listeners/index.js';
const isDev = process.env.NODE_ENV === 'development';
export default init;
globalThis.CloudCmd = init;
async function init(config) {
globalThis.CloudCmd = createCloudCmd({
DOM,
Listeners,
});
globalThis.DOM = DOM;
globalThis.Util = Util;
globalThis.CloudFunc = CloudFunc;
await register(config);
initSortPanel();
globalThis.CloudCmd.sortPanel = sortPanel;
const prefix = getPrefix(config.prefix);
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');
}, 100);
}
function getPrefix(prefix) {
if (!prefix)
return '';
if (!prefix.indexOf('/'))
return prefix;
return `/${prefix}`;
}
const onUpdateFound = wraptile(async (config) => {
if (isDev)
return;
const {DOM} = globalThis;
const prefix = getPrefix(config.prefix);
await load.js(`${prefix}/dist/cloudcmd.common.js`);
await load.js(`${prefix}/dist/cloudcmd.js`);
console.log('cloudcmd: sw: updated');
DOM.Events.removeAll();
globalThis.CloudCmd(config);
});
async function register(config) {
const {prefix} = config;
const sw = await registerSW(prefix);
listenSW(sw, 'updatefound', onUpdateFound(config));
}

View file

@ -1,135 +0,0 @@
'use strict';
/* global CloudCmd */
const tryToPromiseAll = require('../../common/try-to-promise-all');
const Storage = require('./storage');
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();
return DOM.getFilenames(files);
}
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.getJson(COPY),
Storage.getJson(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.setJson(COPY, {
from,
names,
});
}
async function cut() {
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
addCutClass();
await Storage.setJson(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;
}

124
client/dom/buffer.mjs Normal file
View file

@ -0,0 +1,124 @@
/* global CloudCmd*/
import tryToPromiseAll from '../../common/try-to-promise-all.js';
import Storage from './storage.js';
const CLASS = 'cut-file';
const COPY = 'copy';
const CUT = 'cut';
function showMessage(msg) {
globalThis.DOM.Dialog.alert(msg);
}
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();
});

View file

@ -1,13 +1,8 @@
'use strict';
/* global DOM */
/* global CloudCmd */
const {atob, btoa} = require('../../common/base64');
const createElement = require('@cloudcmd/create-element');
const {encode, decode} = require('../../common/entity');
const {getTitle, FS} = require('../../common/cloudfunc');
import createElement from '@cloudcmd/create-element';
import {encode, decode} from '../../common/entity.js';
import {getTitle, FS} from '../../common/cloudfunc.mjs';
let Title;
@ -15,14 +10,15 @@ const CURRENT_FILE = 'current-file';
const encodeNBSP = (a) => a?.replace('\xa0', '&nbsp;');
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
*
* @param name
* @param current
*/
module.exports.setCurrentName = (name, current) => {
export const setCurrentName = (name, current) => {
const Info = DOM.CurrentInfo;
const {link} = Info;
const {prefix} = CloudCmd;
@ -44,7 +40,7 @@ module.exports.setCurrentName = (name, current) => {
*
* @param currentFile
*/
module.exports.getCurrentName = (currentFile) => {
export const getCurrentName = (currentFile) => {
const current = currentFile || DOM.getCurrentFile();
if (!current)
@ -71,18 +67,19 @@ const parseNameAttribute = (attribute) => {
return decodeNBSP(decodeURI(atob(attribute)));
};
module.exports._parseNameAttribute = parseNameAttribute;
export const _parseNameAttribute = parseNameAttribute;
const parseHrefAttribute = (prefix, attribute) => {
attribute = attribute.replace(RegExp('^' + prefix + FS), '');
return decode(decodeNBSP(attribute));
};
module.exports._parseHrefAttribute = parseHrefAttribute;
export const _parseHrefAttribute = parseHrefAttribute;
/**
* get current direcotory path
*/
module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => {
export const getCurrentDirPath = (panel = DOM.getPanel()) => {
const path = DOM.getByDataName('js-path', panel);
return path.textContent;
};
@ -92,7 +89,7 @@ module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => {
*
* @param currentFile - current file by default
*/
module.exports.getCurrentPath = (currentFile) => {
export const getCurrentPath = (currentFile) => {
const current = currentFile || DOM.getCurrentFile();
const [element] = DOM.getByTag('a', current);
const {prefix} = CloudCmd;
@ -103,7 +100,7 @@ module.exports.getCurrentPath = (currentFile) => {
/**
* get current direcotory name
*/
module.exports.getCurrentDirName = () => {
export const getCurrentDirName = () => {
const href = DOM
.getCurrentDirPath()
.replace(/\/$/, '');
@ -116,7 +113,7 @@ module.exports.getCurrentDirName = () => {
/**
* get current direcotory path
*/
module.exports.getParentDirPath = (panel) => {
export const getParentDirPath = (panel) => {
const path = DOM.getCurrentDirPath(panel);
const dirName = DOM.getCurrentDirName() + '/';
const index = path.lastIndexOf(dirName);
@ -130,7 +127,7 @@ module.exports.getParentDirPath = (panel) => {
/**
* get not current direcotory path
*/
module.exports.getNotCurrentDirPath = () => {
export const getNotCurrentDirPath = () => {
const panel = DOM.getPanel({
active: false,
});
@ -143,14 +140,14 @@ module.exports.getNotCurrentDirPath = () => {
*
* @currentFile
*/
module.exports.getCurrentFile = () => {
export const getCurrentFile = () => {
return DOM.getByClass(CURRENT_FILE);
};
/**
* 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(encodeNBSP(name)));
return DOM.getByDataName(dataName, panel);
};
@ -172,7 +169,7 @@ function unsetCurrentFile(currentFile) {
/**
* unified way to set current file
*/
module.exports.setCurrentFile = (currentFile, options) => {
export const setCurrentFile = (currentFile, options) => {
const o = options;
const currentFileWas = DOM.getCurrentFile();
@ -196,7 +193,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
name,
path,
}));
/* history could be present
* but it should be false
* to prevent default behavior
@ -219,7 +216,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
return DOM;
};
this.setCurrentByName = (name) => {
export const setCurrentByName = (name) => {
const current = DOM.getCurrentByName(name);
return DOM.setCurrentFile(current);
};
@ -230,7 +227,7 @@ this.setCurrentByName = (name) => {
* @param layer - element
* @param - position {x, y}
*/
module.exports.getCurrentByPosition = ({x, y}) => {
export const getCurrentByPosition = ({x, y}) => {
const element = document.elementFromPoint(x, y);
const getEl = (el) => {
@ -262,7 +259,7 @@ module.exports.getCurrentByPosition = ({x, y}) => {
*
* @param currentFile
*/
module.exports.isCurrentFile = (currentFile) => {
export const isCurrentFile = (currentFile) => {
if (!currentFile)
return false;
@ -274,7 +271,7 @@ module.exports.isCurrentFile = (currentFile) => {
*
* @param name
*/
module.exports.setTitle = (name) => {
export const setTitle = (name) => {
if (!Title)
Title = DOM.getByTag('title')[0] || createElement('title', {
innerHTML: name,
@ -291,22 +288,21 @@ module.exports.setTitle = (name) => {
*
* @param currentFile
*/
module.exports.isCurrentIsDir = (currentFile) => {
export const isCurrentIsDir = (currentFile) => {
const current = currentFile || DOM.getCurrentFile();
const path = DOM.getCurrentPath(current);
const fileType = DOM.getCurrentType(current);
const isZip = /\.zip$/.test(path);
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 el = DOM.getByDataName('js-type', current);
const type = el
.className
const type = el.className
.split(' ')
.pop();

View file

@ -1,20 +1,18 @@
'use strict';
import {test, stub} from 'supertape';
import {create} from 'auto-globals';
import wraptile from 'wraptile';
import * as currentFile from './current-file.mjs';
const {test, stub} = require('supertape');
const {create} = require('auto-globals');
const wraptile = require('wraptile');
const currentFile = require('./current-file');
const id = (a) => a;
const returns = wraptile(id);
const {_CURRENT_FILE} = currentFile;
test('current-file: setCurrentName: setAttribute', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const current = create();
const {setAttribute} = current;
@ -23,17 +21,17 @@ test('current-file: setCurrentName: setAttribute', (t) => {
t.calledWith(setAttribute, ['data-name', 'js-file-aGVsbG8='], 'should call setAttribute');
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.end();
});
test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const current = create();
const {setAttribute} = current;
@ -42,8 +40,8 @@ test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
t.calledWith(setAttribute, ['data-name', 'js-file-JUQwJUIwJUQwJUI5'], 'should call setAttribute');
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.end();
});
@ -59,12 +57,12 @@ test('current-file: getCurrentName', (t) => {
});
test('current-file: emit', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
const emit = stub();
global.DOM = getDOM();
global.CloudCmd = getCloudCmd({
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd({
emit,
});
@ -74,22 +72,22 @@ test('current-file: emit', (t) => {
t.calledWith(emit, ['current-file', current], 'should call emit');
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.end();
});
test('current-file: setCurrentName: return', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
const link = {};
global.DOM = getDOM({
globalThis.DOM = getDOM({
link,
});
global.CloudCmd = getCloudCmd();
globalThis.CloudCmd = getCloudCmd();
const current = create();
@ -97,19 +95,19 @@ test('current-file: setCurrentName: return', (t) => {
t.equal(result, link, 'should return link');
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.end();
});
test('current-file: getParentDirPath: result', (t) => {
const {DOM} = global;
const {DOM} = globalThis;
const getCurrentDirPath = returns('/D/Films/+++favorite films/');
const getCurrentDirName = returns('+++favorite films');
global.DOM = getDOM({
globalThis.DOM = getDOM({
getCurrentDirPath,
getCurrentDirName,
});
@ -117,55 +115,55 @@ test('current-file: getParentDirPath: result', (t) => {
const result = currentFile.getParentDirPath();
const expected = '/D/Films/';
global.DOM = DOM;
globalThis.DOM = DOM;
t.equal(result, expected, 'should return parent dir path');
t.end();
});
test('current-file: isCurrentFile: no', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const result = currentFile.isCurrentFile();
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.notOk(result);
t.end();
});
test('current-file: isCurrentFile', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
const isContainClass = stub();
global.DOM = getDOM({
globalThis.DOM = getDOM({
isContainClass,
});
global.CloudCmd = getCloudCmd();
globalThis.CloudCmd = getCloudCmd();
const current = {};
currentFile.isCurrentFile(current);
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass');
t.end();
});
test('current-file: getCurrentType', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const {getByDataName} = global.DOM;
const {getByDataName} = globalThis.DOM;
getByDataName.returns({
className: 'mini-icon directory',
@ -175,98 +173,96 @@ test('current-file: getCurrentType', (t) => {
currentFile.getCurrentType(current);
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.calledWith(getByDataName, ['js-type', current]);
t.end();
});
test('current-file: isCurrentIsDir: getCurrentType', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const {getCurrentType} = global.DOM;
const {getCurrentType} = globalThis.DOM;
const current = create();
currentFile.isCurrentIsDir(current);
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.calledWith(getCurrentType, [current]);
t.end();
});
test('current-file: isCurrentIsDir: directory', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM({
globalThis.DOM = getDOM({
getCurrentType: stub().returns('directory'),
});
global.CloudCmd = getCloudCmd();
globalThis.CloudCmd = getCloudCmd();
const current = create();
const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.ok(result);
t.end();
});
test('current-file: isCurrentIsDir: directory-link', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM({
globalThis.DOM = getDOM({
getCurrentType: stub().returns('directory-link'),
});
global.CloudCmd = getCloudCmd();
globalThis.CloudCmd = getCloudCmd();
const current = create();
const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.ok(result);
t.end();
});
test('current-file: isCurrentIsDir: file', (t) => {
const {DOM, CloudCmd} = global;
const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM({
globalThis.DOM = getDOM({
getCurrentType: stub().returns('file'),
});
global.CloudCmd = getCloudCmd();
globalThis.CloudCmd = getCloudCmd();
const current = create();
const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM;
global.CloudCmd = CloudCmd;
globalThis.DOM = DOM;
globalThis.CloudCmd = CloudCmd;
t.notOk(result);
t.end();
});
function getCloudCmd({emit} = {}) {
return {
prefix: '',
emit: emit || stub(),
};
}
const getCloudCmd = ({emit} = {}) => ({
prefix: '',
emit: emit || stub(),
});
test('current-file: parseNameAttribute', (t) => {
const result = currentFile._parseNameAttribute('js-file-aGVsbG8mbmJzcDt3b3JsZA==');
@ -285,15 +281,17 @@ test('current-file: parseHrefAttribute', (t) => {
t.end();
});
function getDOM({
link = {},
getCurrentDirPath = stub(),
getCurrentDirName = stub(),
getByDataName = stub(),
isContainClass = stub(),
getCurrentType = stub(),
getCurrentPath = stub(),
} = {}) {
function getDOM(overrides = {}) {
const {
link = {},
getCurrentDirPath = stub(),
getCurrentDirName = stub(),
getByDataName = stub(),
isContainClass = stub(),
getCurrentType = stub(),
getCurrentPath = stub().returns(''),
} = overrides;
return {
getCurrentDirPath,
getCurrentDirName,

View file

@ -1,6 +1,6 @@
'use strict';
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const {
alert,

View file

@ -3,8 +3,8 @@
/* global CloudCmd */
const philip = require('philip');
const Images = require('./images');
const {FS} = require('../../common/cloudfunc');
const Images = require('./images.mjs');
const {FS} = require('../../common/cloudfunc.mjs');
const DOM = require('.');
const Dialog = require('./dialog');
@ -63,14 +63,8 @@ module.exports = (items) => {
uploader.on('end', CloudCmd.refresh);
};
function percent(i, n, per = 100) {
return Math.round(i * per / n);
}
const percent = (i, n, per = 100) => Math.round(i * per / n);
function uploadFile(url, data) {
return DOM.load.put(url, data);
}
const uploadFile = (url, data) => DOM.load.put(url, data);
function uploadDir(url) {
return DOM.load.put(`${url}?dir`);
}
const uploadDir = (url) => DOM.load.put(`${url}?dir`);

View file

@ -2,7 +2,7 @@
const test = require('supertape');
const {create} = require('auto-globals');
const tryCatch = require('try-catch');
const {tryCatch} = require('try-catch');
const {isContainClass} = require('./dom-tree');

View file

@ -1,257 +0,0 @@
'use strict';
const itype = require('itype');
const EventStore = require('./event-store');
module.exports = new EventsProto();
function EventsProto() {
const Events = this;
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}`);
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;
}
}
/**
* 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;
};
/**
* safe add event listener
*
* @param type
* @param listener
* @param element {document by default}
*/
this.addOnce = (type, element, listener) => {
const once = (event) => {
Events.remove(type, element, once);
listener(event);
};
if (!listener) {
listener = element;
element = null;
}
this.add(type, element, once);
return Events;
};
/**
* 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) => {
element.removeEventListener(...args);
});
return Events;
};
/**
* remove all added event listeners
*
* @param listener
*/
this.removeAll = () => {
const events = EventStore.get();
for (const [el, name, fn] of events)
el.removeEventListener(name, fn);
EventStore.clear();
};
/**
* 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!');
}
}

203
client/dom/events/index.mjs Normal file
View file

@ -0,0 +1,203 @@
import itype from 'itype';
import EventStore from './event-store.js';
/**
* safe add event listener
*
* @param type
* @param element - document by default
* @param listener
*/
export const 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;
};
/**
* 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);
};
if (!listener) {
listener = element;
element = null;
}
add(type, element, once);
return Events;
};
/**
* safe remove event listener
*
* @param type
* @param listener
* @param element - document by default
*/
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}`);
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;
}
}
const Events = {
add,
addClick,
addContextMenu,
addKey,
addLoad,
addOnce,
remove,
removeAll,
rmClick,
rmKey,
};

View file

@ -1,20 +1,14 @@
/* global DOM */
'use strict';
const createElement = require('@cloudcmd/create-element');
const Images = module.exports;
import createElement from '@cloudcmd/create-element';
const LOADING = 'loading';
const HIDDEN = 'hidden';
const ERROR = 'error';
function getLoadingType() {
return isSVG() ? '-svg' : '-gif';
}
const getLoadingType = () => isSVG() ? '-svg' : '-gif';
export const get = getElement;
module.exports.get = getElement;
/**
* check SVG SMIL animation support
*/
@ -42,7 +36,7 @@ function getElement() {
}
/* Функция создаёт картинку загрузки */
module.exports.loading = () => {
export const loading = () => {
const element = getElement();
const {classList} = element;
const loadingImage = LOADING + getLoadingType();
@ -54,7 +48,7 @@ module.exports.loading = () => {
};
/* Функция создаёт картинку ошибки загрузки */
module.exports.error = () => {
export const error = () => {
const element = getElement();
const {classList} = element;
const loadingImage = LOADING + getLoadingType();
@ -65,14 +59,21 @@ module.exports.error = () => {
return element;
};
module.exports.show = show;
module.exports.show.load = show;
module.exports.show.error = error;
show.load = show;
show.error = (text) => {
const image = Images.error();
DOM.show(image);
image.title = text;
return image;
};
/**
* Function shows loading spinner
* position = {top: true};
*/
function show(position, panel) {
export function show(position, panel) {
const image = Images.loading();
const parent = image.parentElement;
const refreshButton = DOM.getRefreshButton(panel);
@ -98,19 +99,10 @@ function show(position, panel) {
return image;
}
function error(text) {
const image = Images.error();
DOM.show(image);
image.title = text;
return image;
}
/**
* hide load image
*/
module.exports.hide = () => {
export const hide = () => {
const element = Images.get();
DOM.hide(element);
@ -118,7 +110,7 @@ module.exports.hide = () => {
return Images;
};
module.exports.setProgress = (value, title) => {
export const setProgress = (value, title) => {
const DATA = 'data-progress';
const element = Images.get();
@ -133,7 +125,7 @@ module.exports.setProgress = (value, title) => {
return Images;
};
module.exports.clearProgress = () => {
export const clearProgress = () => {
const DATA = 'data-progress';
const element = Images.get();
@ -145,3 +137,13 @@ module.exports.clearProgress = () => {
return Images;
};
const Images = {
clearProgress,
setProgress,
show,
hide,
get,
error,
loading,
};

View file

@ -3,12 +3,12 @@
/* global CloudCmd */
const Util = require('../../common/util');
const Images = require('./images');
const Images = require('./images.mjs');
const RESTful = require('./rest');
const Storage = require('./storage');
const renameCurrent = require('./operations/rename-current');
const CurrentFile = require('./current-file');
const CurrentFile = require('./current-file.mjs');
const DOMTree = require('./dom-tree');
const Cmd = module.exports;
@ -32,8 +32,8 @@ DOM.CurrentInfo = CurrentInfo;
module.exports = DOM;
DOM.uploadDirectory = require('./directory');
DOM.Buffer = require('./buffer');
DOM.Events = require('./events');
DOM.Buffer = require('./buffer.mjs');
DOM.Events = require('#dom/events');
const loadRemote = require('./load-remote');
const selectByPattern = require('./select-by-pattern');
@ -416,7 +416,7 @@ module.exports.shrinkSelection = () => {
* setting history wrapper
*/
module.exports.setHistory = (data, title, url) => {
const ret = window.history;
const ret = globalThis.history;
const {prefix} = CloudCmd;
url = prefix + url;
@ -520,6 +520,11 @@ module.exports.getPanelPosition = (panel) => {
return panel.dataset.name.replace('js-', '');
};
module.exports.getCSSVar = (name, {body = document.body} = {}) => {
const bodyStyle = getComputedStyle(body);
return bodyStyle.getPropertyValue(`--${name}`);
};
/** function getting panel active, or passive
* @param options = {active: true}
*/
@ -549,7 +554,7 @@ module.exports.getPanel = (options) => {
* then always work with passive
* panel
*/
if (window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH)
if (globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH)
panel = DOM.getByDataName('js-left');
if (!panel)
@ -724,17 +729,19 @@ module.exports.getPackerExt = (type) => {
return '.tar.gz';
};
module.exports.goToDirectory = async () => {
const msg = 'Go to directory:';
module.exports.goToDirectory = async (overrides = {}) => {
const {Dialog} = DOM;
const {prompt = Dialog.prompt, changeDir = CloudCmd.changeDir} = overrides;
const msg = 'Go to directory:';
const {dirPath} = CurrentInfo;
const [cancel, path = dirPath] = await Dialog.prompt(msg, dirPath);
const [cancel, path = dirPath] = await prompt(msg, dirPath);
if (cancel)
return;
await CloudCmd.changeDir(path);
await changeDir(path);
};
module.exports.duplicatePanel = async () => {

View file

@ -3,29 +3,54 @@
require('css-modules-require-hook/preset');
const {test, stub} = require('supertape');
const mockRequire = require('mock-require');
const {reRequire, stopAll} = mockRequire;
const {getCSSVar, goToDirectory} = require('./index');
global.CloudCmd = {};
globalThis.CloudCmd = {};
test('cloudcmd: client: dom: goToDirectory', async (t) => {
const path = '';
const {CloudCmd} = global;
const changeDir = stub();
const prompt = stub().returns([null, path]);
CloudCmd.changeDir = changeDir;
mockRequire('./dialog', {
await goToDirectory({
prompt,
changeDir,
});
const {goToDirectory} = reRequire('.');
await goToDirectory();
stopAll();
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,14 +1,14 @@
'use strict';
const {FS} = require('../../../common/cloudfunc');
const sendRequest = require('./send-request');
const {FS} = require('../../../common/cloudfunc.mjs');
const _sendRequest = require('./send-request');
const imgPosition = {
top: true,
};
module.exports.delete = async (url, data) => {
return await sendRequest({
return await _sendRequest({
method: 'DELETE',
url: FS + url,
data,
@ -19,7 +19,7 @@ module.exports.delete = async (url, data) => {
};
module.exports.patch = async (url, data) => {
return await sendRequest({
return await _sendRequest({
method: 'PATCH',
url: FS + url,
data,
@ -28,7 +28,7 @@ module.exports.patch = async (url, data) => {
};
module.exports.write = async (url, data) => {
return await sendRequest({
return await _sendRequest({
method: 'PUT',
url: FS + url,
data,
@ -36,7 +36,11 @@ module.exports.write = async (url, data) => {
});
};
module.exports.createDirectory = async (url) => {
module.exports.createDirectory = async (url, overrides = {}) => {
const {
sendRequest = _sendRequest,
} = overrides;
return await sendRequest({
method: 'PUT',
url: `${FS}${url}?dir`,
@ -47,7 +51,7 @@ module.exports.createDirectory = async (url) => {
module.exports.read = async (url, dataType = 'text') => {
const notLog = !url.includes('?');
return await sendRequest({
return await _sendRequest({
method: 'GET',
url: FS + url,
notLog,
@ -56,7 +60,7 @@ module.exports.read = async (url, dataType = 'text') => {
};
module.exports.copy = async (from, to, names) => {
return await sendRequest({
return await _sendRequest({
method: 'PUT',
url: '/copy',
data: {
@ -69,7 +73,7 @@ module.exports.copy = async (from, to, names) => {
};
module.exports.pack = async (data) => {
return await sendRequest({
return await _sendRequest({
method: 'PUT',
url: '/pack',
data,
@ -77,7 +81,7 @@ module.exports.pack = async (data) => {
};
module.exports.extract = async (data) => {
return await sendRequest({
return await _sendRequest({
method: 'PUT',
url: '/extract',
data,
@ -85,7 +89,7 @@ module.exports.extract = async (data) => {
};
module.exports.move = async (from, to, names) => {
return await sendRequest({
return await _sendRequest({
method: 'PUT',
url: '/move',
data: {
@ -98,7 +102,7 @@ module.exports.move = async (from, to, names) => {
};
module.exports.rename = async (from, to) => {
return await sendRequest({
return await _sendRequest({
method: 'PUT',
url: '/rename',
data: {
@ -111,7 +115,7 @@ module.exports.rename = async (from, to) => {
module.exports.Config = {
read: async () => {
return await sendRequest({
return await _sendRequest({
method: 'GET',
url: '/config',
imgPosition,
@ -120,7 +124,7 @@ module.exports.Config = {
},
write: async (data) => {
return await sendRequest({
return await _sendRequest({
method: 'PATCH',
url: '/config',
data,
@ -131,7 +135,7 @@ module.exports.Config = {
module.exports.Markdown = {
read: async (url) => {
return await sendRequest({
return await _sendRequest({
method: 'GET',
url: `/markdown${url}`,
imgPosition,
@ -140,7 +144,7 @@ module.exports.Markdown = {
},
render: async (data) => {
return await sendRequest({
return await _sendRequest({
method: 'PUT',
url: '/markdown',
data,

View file

@ -1,18 +1,14 @@
'use strict';
const {test, stub} = require('supertape');
const mockRequire = require('mock-require');
const {reRequire, stopAll} = mockRequire;
const io = require('.');
test('client: dom: io', (t) => {
const sendRequest = stub();
mockRequire('./send-request', sendRequest);
const io = reRequire('.');
io.createDirectory('/hello');
io.createDirectory('/hello', {
sendRequest,
});
const expected = {
imgPosition: {
@ -22,8 +18,6 @@ test('client: dom: io', (t) => {
url: '/fs/hello?dir',
};
stopAll();
t.calledWith(sendRequest, [expected]);
t.end();
});

View file

@ -3,7 +3,7 @@
/* global CloudCmd */
const {promisify} = require('es6-promisify');
const Images = require('../images');
const Images = require('../images.mjs');
const load = require('../load');
module.exports = promisify((params, callback) => {

View file

@ -4,7 +4,7 @@
const rendy = require('rendy');
const itype = require('itype');
const load = require('load.js');
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const {findObjByNameInArr} = require('../../common/util');
@ -17,42 +17,40 @@ module.exports = (name, options, callback = options) => {
if (o.name && window[o.name])
return callback();
Files
.get('modules')
.then(async (modules) => {
const online = config('online') && navigator.onLine;
const module = findObjByNameInArr(modules.remote, name);
Files.get('modules').then(async (modules) => {
const online = config('online') && navigator.onLine;
const module = findObjByNameInArr(modules.remote, name);
const isArray = itype.array(module.local);
const {version} = module;
const isArray = itype.array(module.local);
const {version} = module;
let remoteTmpls;
let local;
let remoteTmpls;
let local;
if (isArray) {
remoteTmpls = module.remote;
local = module.local;
} else {
remoteTmpls = [module.remote];
local = [module.local];
}
if (isArray) {
remoteTmpls = module.remote;
({local} = module);
} else {
remoteTmpls = [module.remote];
local = [module.local];
}
const localURL = local.map((url) => prefix + url);
const localURL = local.map((url) => prefix + url);
const remoteURL = remoteTmpls.map((tmpl) => {
return rendy(tmpl, {
version,
});
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);
});
if (online) {
const [e] = await tryToCatch(load.parallel, remoteURL);
if (!e)
return callback();
}
const [e] = await tryToCatch(load.parallel, localURL);
callback(e);
});
};

View file

@ -4,7 +4,7 @@ const itype = require('itype');
const jonny = require('jonny');
const Emitify = require('emitify');
const exec = require('execon');
const Images = require('./images');
const Images = require('./images.mjs');
module.exports.getIdBySrc = getIdBySrc;
/**
@ -63,7 +63,7 @@ module.exports.ajax = (params) => {
if (!isArrayBuf && isObject || isArray)
data = jonny.stringify(p.data);
else
data = p.data;
({data} = p);
xhr.onreadystatechange = (event) => {
const xhr = event.target;

View file

@ -3,21 +3,29 @@
/* global CloudCmd */
const capitalize = require('just-capitalize');
const Dialog = require('../dialog');
const _Dialog = require('../dialog');
const Storage = require('../storage');
const RESTful = require('../rest');
const {
isCurrentFile,
getCurrentName,
getCurrentFile,
getCurrentByName,
getCurrentType,
getCurrentDirPath,
setCurrentName,
} = require('../current-file');
const _currentFile = require('../current-file.mjs');
module.exports = async (current) => {
module.exports = async (current, overrides = {}) => {
const {
refresh = CloudCmd.refresh,
Dialog = _Dialog,
currentFile = _currentFile,
} = overrides;
const {
isCurrentFile,
getCurrentName,
getCurrentFile,
getCurrentByName,
getCurrentType,
getCurrentDirPath,
setCurrentName,
} = currentFile;
if (!isCurrentFile(current))
current = getCurrentFile();
@ -58,5 +66,5 @@ module.exports = async (current) => {
setCurrentName(to, current);
Storage.remove(dirPath);
CloudCmd.refresh();
refresh();
};

View file

@ -2,23 +2,20 @@
const {test, stub} = require('supertape');
const mockRequire = require('mock-require');
const {reRequire, stopAll} = mockRequire;
const renameCurrent = require('./rename-current');
test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
const current = {};
const isCurrentFile = stub();
mockRequire('../dialog', stubDialog());
mockRequire('../current-file', stubCurrentFile({
const currentFile = stubCurrentFile({
isCurrentFile,
}));
});
const renameCurrent = reRequire('./rename-current');
await renameCurrent(current);
stopAll();
await renameCurrent(current, {
Dialog: stubDialog(),
currentFile,
});
t.calledWith(isCurrentFile, [current], 'should call isCurrentFile');
t.end();
@ -27,11 +24,6 @@ test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
const current = {};
const name = 'hello';
const {CloudCmd} = global;
global.CloudCmd = {
refresh: stub(),
};
const prompt = stub().returns([null, name]);
const confirm = stub().returns([true]);
@ -39,25 +31,23 @@ test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
const getCurrentByName = stub().returns(current);
const getCurrentType = stub().returns('directory');
mockRequire('../dialog', stubDialog({
const Dialog = stubDialog({
confirm,
prompt,
}));
});
mockRequire('../current-file', stubCurrentFile({
const currentFile = stubCurrentFile({
getCurrentByName,
getCurrentType,
}));
});
const renameCurrent = reRequire('./rename-current');
await renameCurrent();
await renameCurrent(null, {
Dialog,
currentFile,
});
const expected = 'Directory "hello" already exists. Proceed?';
global.CloudCmd = CloudCmd;
stopAll();
t.calledWith(confirm, [expected], 'should call confirm');
t.end();
});

View file

@ -1,10 +1,10 @@
'use strict';
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const {encode} = require('../../common/entity');
const Images = require('./images');
const Images = require('./images.mjs');
const IO = require('./io');
const Dialog = require('./dialog');

View file

@ -7,58 +7,58 @@ const storage = require('./storage');
const {stringify} = JSON;
test('cloudcmd: client: storage: set', async (t) => {
const {localStorage} = global;
const {localStorage} = globalThis;
const setItem = stub();
global.localStorage = {
globalThis.localStorage = {
setItem,
};
await storage.set('hello', 'world');
global.localStorage = localStorage;
globalThis.localStorage = localStorage;
t.calledWith(setItem, ['hello', 'world'], 'should call setItem');
t.end();
});
test('cloudcmd: client: storage: get', async (t) => {
const {localStorage} = global;
const {localStorage} = globalThis;
const getItem = stub().returns('world');
global.localStorage = {
globalThis.localStorage = {
getItem,
};
const result = await storage.get('hello');
global.localStorage = localStorage;
globalThis.localStorage = localStorage;
t.equal(result, 'world');
t.end();
});
test('cloudcmd: client: storage: getJson', async (t) => {
const {localStorage} = global;
const {localStorage} = globalThis;
const expected = {
hello: 'world',
};
const getItem = stub().returns(stringify(expected));
global.localStorage = {
globalThis.localStorage = {
getItem,
};
const result = await storage.getJson('hello');
global.localStorage = localStorage;
globalThis.localStorage = localStorage;
t.deepEqual(result, expected);
t.end();
});
test('cloudcmd: client: storage: setJson', async (t) => {
const {localStorage} = global;
const {localStorage} = globalThis;
const data = {
hello: 'world',
};
@ -66,42 +66,42 @@ test('cloudcmd: client: storage: setJson', async (t) => {
const expected = stringify(data);
const setItem = stub();
global.localStorage = {
globalThis.localStorage = {
setItem,
};
await storage.setJson('hello', data);
global.localStorage = localStorage;
globalThis.localStorage = localStorage;
t.calledWith(setItem, ['hello', expected]);
t.end();
});
test('cloudcmd: client: storage: remove', async (t) => {
const {localStorage} = global;
const {localStorage} = globalThis;
const removeItem = stub();
global.localStorage = {
globalThis.localStorage = {
removeItem,
};
await storage.remove('hello');
global.localStorage = localStorage;
globalThis.localStorage = localStorage;
t.calledWith(removeItem, ['hello'], 'should call removeItem');
t.end();
});
test('cloudcmd: client: storage: clear', async (t) => {
const {localStorage} = global;
const {localStorage} = globalThis;
const clear = stub();
global.localStorage = {
globalThis.localStorage = {
clear,
};
await storage.clear();
global.localStorage = localStorage;
globalThis.localStorage = localStorage;
t.calledWithNoArgs(clear, 'should call clear');
t.end();

View file

@ -5,10 +5,10 @@ const {eachSeries} = require('execon');
const wraptile = require('wraptile');
const load = require('./load');
const Images = require('./images');
const Images = require('./images.mjs');
const {alert} = require('./dialog');
const {FS} = require('../../common/cloudfunc');
const {FS} = require('../../common/cloudfunc.mjs');
const {getCurrentDirPath: getPathWhenRootEmpty} = require('.');
const loadFile = wraptile(_loadFile);
@ -61,7 +61,7 @@ function _loadFile(dir, n, file, callback) {
.on('progress', (count) => {
const max = step(n);
const value = (i - 1) * max + percent(count, 100, max);
Images.show.load('top');
Images.setProgress(Math.round(value));
});

View file

@ -1,47 +0,0 @@
'use strict';
/* global DOM */
const Info = DOM.CurrentInfo;
/**
* Функция генерирует JSON из html-таблицы файлов и
* используеться при первом заходе в корень
*/
module.exports = () => {
const path = DOM.getCurrentDirPath();
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
.filter(notParent)
.map(parse);
const fileTable = {
path,
files,
};
return fileTable;
};

View file

@ -0,0 +1,44 @@
/* global DOM */
/**
* Функция генерирует JSON из html-таблицы файлов и
* используеться при первом заходе в корень
*/
export const getJsonFromFileTable = () => {
const Info = DOM.CurrentInfo;
const path = DOM.getCurrentDirPath();
const infoFiles = Info.files || [];
const files = infoFiles
.filter(notParent)
.map(parse);
const fileTable = {
path,
files,
};
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,
};
};

View file

@ -1,22 +1,17 @@
'use strict';
/* global CloudCmd, DOM */
const clipboard = require('@cloudcmd/clipboard');
const fullstore = require('fullstore');
import clipboard from '@cloudcmd/clipboard';
import {fullstore} from 'fullstore';
import * as Events from '#dom/events';
import * as Buffer from '../dom/buffer.mjs';
import KEY from './key.js';
import _vim from './vim/index.js';
import setCurrentByChar from './set-current-by-char.js';
import {createBinder} from './binder.js';
const Buffer = require('../dom/buffer');
const Events = require('../dom/events');
const KEY = require('./key');
const vim = require('./vim');
const setCurrentByChar = require('./set-current-by-char');
const {createBinder} = require('./binder');
const Info = DOM.CurrentInfo;
const Chars = fullstore();
const toggleVim = (keyCode) => {
const {_config, config} = CloudCmd;
const toggleVim = (keyCode, overrides = {}) => {
const {_config, config} = overrides;
if (keyCode === KEY.ESC)
_config('vim', !config('vim'));
@ -29,13 +24,16 @@ Chars([]);
const {assign} = Object;
const binder = createBinder();
module.exports = assign(binder, KEY);
module.exports.bind = () => {
const bind = () => {
Events.addKey(listener, true);
binder.setBind();
};
module.exports._listener = listener;
export const Key = assign(binder, KEY, {
bind,
});
export const _listener = listener;
function getChar(event) {
/*
@ -55,7 +53,14 @@ function getChar(event) {
return [symbol, char];
}
async function listener(event) {
async function listener(event, overrides = {}) {
const {
config = CloudCmd.config,
_config = CloudCmd._config,
switchKey = _switchKey,
vim = _vim,
} = overrides;
const {keyCode} = event;
// strange chrome bug calles listener twice
@ -74,8 +79,12 @@ async function listener(event) {
if (!binder.isBind())
return;
toggleVim(keyCode);
const isVim = CloudCmd.config('vim');
toggleVim(keyCode, {
config,
_config,
});
const isVim = config('vim');
if (!isVim && !isNumpad && !alt && !ctrl && !meta && (isBetween || symbol))
return setCurrentByChar(char, Chars);
@ -112,7 +121,8 @@ function fromCharCode(keyIdentifier) {
return String.fromCharCode(hex);
}
async function switchKey(event) {
async function _switchKey(event) {
const Info = DOM.CurrentInfo;
let i;
let isSelected;
let prev;
@ -509,6 +519,18 @@ async function switchKey(event) {
event.preventDefault();
}
break;
case KEY.DOT:
if (meta && shift) {
const showDotFiles = !CloudCmd.config('showDotFiles');
CloudCmd._config('showDotFiles', showDotFiles);
CloudCmd.refresh();
await DOM.RESTful.Config.write({
showDotFiles,
});
}
break;
}
}

View file

@ -3,27 +3,23 @@
require('css-modules-require-hook/preset');
const autoGlobals = require('auto-globals');
const mockRequire = require('mock-require');
const supertape = require('supertape');
const {ESC} = require('./key');
const {Key, _listener} = require('./index.mjs');
const {getDOM, getCloudCmd} = require('./vim/globals.fixture');
const test = autoGlobals(supertape);
const {reRequire, stopAll} = mockRequire;
const {stub} = supertape;
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
test('cloudcmd: client: key: enable vim', async (t) => {
const vim = stub();
const {CloudCmd} = global;
const {config} = CloudCmd;
CloudCmd.config = stub().returns(true);
CloudCmd._config = stub();
mockRequire('./vim', vim);
const {_listener, setBind} = reRequire('.');
const config = stub().returns(true);
const _config = stub();
const event = {
keyCode: ESC,
@ -31,11 +27,14 @@ test('cloudcmd: client: key: enable vim', async (t) => {
altKey: false,
};
setBind();
await _listener(event);
Key.setBind();
CloudCmd.config = config;
stopAll();
await _listener(event, {
vim,
config,
_config,
switchKey: stub(),
});
t.calledWith(vim, ['Escape', event]);
t.end();
@ -43,25 +42,20 @@ test('cloudcmd: client: key: enable vim', async (t) => {
test('cloudcmd: client: key: disable vim', async (t) => {
const _config = stub();
const config = stub();
const event = {
keyCode: ESC,
key: 'Escape',
altKey: false,
};
const {CloudCmd} = global;
const {config} = CloudCmd;
Key.setBind();
await _listener(event, {
config,
_config,
switchKey: stub(),
});
global.CloudCmd.config = _config;
global.CloudCmd._config = _config;
const {_listener, setBind} = reRequire('.');
setBind();
await _listener(event);
CloudCmd.config = config;
t.calledWith(_config, ['vim']);
t.calledWith(_config, ['vim', true]);
t.end();
});

View file

@ -3,9 +3,9 @@
'use strict';
const {escapeRegExp} = require('../../common/util');
const Info = DOM.CurrentInfo;
module.exports = function setCurrentByChar(char, charStore) {
const Info = DOM.CurrentInfo;
let firstByName;
let skipCount = 0;
let setted = false;

View file

@ -1,6 +1,6 @@
'use strict';
const fullstore = require('fullstore');
const {fullstore} = require('fullstore');
const limier = require('limier');
const searchStore = fullstore([]);

View file

@ -5,7 +5,7 @@ const dir = './';
const {getDOM} = require('./globals.fixture');
global.DOM = getDOM();
globalThis.DOM = getDOM();
const {_next, _previous} = require(`${dir}find`);

View file

@ -9,31 +9,43 @@ const {
selectFileNotParent,
} = require('./set-current');
const {Dialog} = DOM;
const DEPS = {
...DOM,
...CloudCmd,
};
module.exports = async (key, event, deps = DEPS) => {
module.exports = (key, event, overrides = {}) => {
const defaults = {
...globalThis.DOM,
...globalThis.CloudCmd,
};
const deps = {
...defaults,
...overrides,
};
const operations = getOperations(event, deps);
await vim(key, operations);
vim(key, operations, deps);
};
const getOperations = (event, deps) => {
const {
Info = DOM.CurrentInfo,
Info = globalThis.DOM.CurrentInfo,
CloudCmd = globalThis.CloudCmd,
Operation,
unselectFiles,
setCurrentFile,
setCurrentByName,
getCurrentName,
prompt = globalThis.DOM.Dialog.prompt,
preventDefault = event?.preventDefault?.bind(event),
toggleSelectedFile,
Buffer = {},
createFindNext = _createFindNext,
} = deps;
return {
findNext: createFindNext({
setCurrentByName,
}),
escape: unselectFiles,
remove: () => {
@ -99,8 +111,8 @@ const getOperations = (event, deps) => {
},
find: async () => {
event.preventDefault();
const [, value] = await Dialog.prompt('Find', '');
preventDefault();
const [, value] = await prompt('Find', '');
if (!value)
return;
@ -111,11 +123,6 @@ const getOperations = (event, deps) => {
setCurrentByName(result);
},
findNext: () => {
const name = finder.findNext();
setCurrentByName(name);
},
findPrevious: () => {
const name = finder.findPrevious();
setCurrentByName(name);
@ -124,3 +131,10 @@ const getOperations = (event, deps) => {
};
module.exports.selectFile = selectFileNotParent;
const _createFindNext = (overrides = {}) => () => {
const {setCurrentByName} = overrides;
const name = finder.findNext();
setCurrentByName(name);
};

View file

@ -1,6 +1,6 @@
'use strict';
const {join} = require('path');
const {join} = require('node:path');
const {test, stub} = require('supertape');
const mockRequire = require('mock-require');
@ -10,13 +10,13 @@ const pathVim = join(dir, 'vim');
const {getDOM, getCloudCmd} = require('./globals.fixture');
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const vim = require(pathVim);
const vim = require('./index.js');
const {assign} = Object;
const {DOM} = global;
const {DOM} = globalThis;
const {Buffer} = DOM;
const pathFind = join(dir, 'vim', 'find');
const {reRequire, stopAll} = mockRequire;
@ -520,15 +520,26 @@ test('cloudcmd: client: key: Enter', async (t) => {
test('cloudcmd: client: key: /', (t) => {
const preventDefault = stub();
const element = {};
const Info = {
element,
files: [],
};
DOM.CurrentInfo.element = element;
DOM.getCurrentName = () => '';
const getCurrentName = stub().returns('');
vim('/', {
const event = {
preventDefault,
};
const prompt = stub().returns([]);
vim('/', event, {
getCurrentName,
Info,
prompt,
});
t.calledWithNoArgs(preventDefault, 'should call preventDefault');
t.calledWithNoArgs(preventDefault);
t.end();
});
@ -559,17 +570,13 @@ test('cloudcmd: client: find', (t) => {
test('cloudcmd: client: key: n', (t) => {
const findNext = stub();
const createFindNext = stub().returns(findNext);
mockRequire(pathFind, {
findNext,
});
const vim = reRequire(pathVim);
const event = {};
vim('n', event);
stopAll();
vim('n', event, {
createFindNext,
});
t.calledWithNoArgs(findNext, 'should call findNext');
t.end();
@ -595,7 +602,7 @@ test('cloudcmd: client: key: N', (t) => {
test('cloudcmd: client: key: make directory', async (t) => {
const vim = reRequire(pathVim);
const {DOM} = global;
const {DOM} = globalThis;
assign(DOM, {
promptNewDir: stub(),
@ -615,7 +622,7 @@ test('cloudcmd: client: key: make directory', async (t) => {
test('cloudcmd: client: key: make file', (t) => {
const vim = reRequire(pathVim);
const {DOM} = global;
const {DOM} = globalThis;
assign(DOM, {
promptNewFile: stub(),
@ -634,28 +641,30 @@ test('cloudcmd: client: key: make file', (t) => {
});
test('cloudcmd: client: vim: terminal', (t) => {
const {CloudCmd} = global;
assign(CloudCmd, {
const CloudCmd = {
Terminal: {
show: stub(),
},
});
};
const event = {};
vim('t', event);
vim('t', event);
vim('t', event, {
CloudCmd,
});
vim('t', event, {
CloudCmd,
});
t.calledWithNoArgs(CloudCmd.Terminal.show);
t.end();
});
test('cloudcmd: client: vim: edit', async (t) => {
global.DOM = getDOM();
global.CloudCmd = getCloudCmd();
globalThis.DOM = getDOM();
globalThis.CloudCmd = getCloudCmd();
const {CloudCmd} = global;
const {CloudCmd} = globalThis;
assign(CloudCmd, {
EditFileVim: {

View file

@ -1,6 +1,6 @@
'use strict';
const fullstore = require('fullstore');
const {fullstore} = require('fullstore');
const store = fullstore('');
const visual = fullstore(false);
@ -21,7 +21,7 @@ const rmFirst = (a) => {
const noop = () => {};
module.exports = (key, operations) => {
module.exports = (key, operations = {}) => {
const prevStore = store();
const isVisual = visual();
const value = store(prevStore.concat(key));
@ -169,6 +169,9 @@ module.exports = (key, operations) => {
findPrevious();
return end();
}
if (key === ' ')
return end();
};
function handleDelete(prevStore) {

View file

@ -11,6 +11,24 @@ test('vim: no operations', (t) => {
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();

View file

@ -5,12 +5,13 @@
const exec = require('execon');
const itype = require('itype');
const currify = require('currify');
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const clipboard = require('@cloudcmd/clipboard');
const getRange = require('./get-range');
const uploadFiles = require('../dom/upload-files');
const {FS} = require('../../common/cloudfunc');
const {FS} = require('../../common/cloudfunc.mjs');
const Events = require('#dom/events');
const getIndex = currify(require('./get-index'));
@ -29,10 +30,8 @@ module.exports.init = async () => {
]);
};
CloudCmd.Listeners = module.exports;
const unselect = (event) => {
const isMac = /Mac/.test(window.navigator.platform);
const isMac = /Mac/.test(globalThis.navigator.platform);
const {
shiftKey,
metaKey,
@ -50,12 +49,9 @@ const execAll = currify((funcs, event) => {
fn(event);
});
const Info = DOM.CurrentInfo;
const {Events} = DOM;
const EventsFiles = {
mousedown: exec.with(execIfNotUL, setCurrentFileByEvent),
click: execAll([onClick, unselect]),
click: execAll([onClick, exec.with(execIfNotMobile, unselect)]),
dragstart: exec.with(execIfNotUL, onDragStart),
dblclick: exec.with(execIfNotUL, onDblClick),
touchstart: exec.with(execIfNotUL, onTouch),
@ -111,8 +107,10 @@ module.exports.initKeysPanel = () => {
if (!keysElement)
return;
Events.addClick(keysElement, ({target}) => {
Events.addClick(keysElement, (event) => {
const {target} = event;
const {id} = target;
const operation = (name) => {
const {Operation} = CloudCmd;
@ -128,7 +126,10 @@ module.exports.initKeysPanel = () => {
'f6': operation('move'),
'f7': DOM.promptNewDir,
'f8': operation('delete'),
'f9': CloudCmd.Menu.show,
'f9': () => {
event.stopPropagation();
CloudCmd.Menu.show();
},
'f10': CloudCmd.Config.show,
'~': CloudCmd.Konsole.show,
'shift~': CloudCmd.Terminal.show,
@ -162,6 +163,7 @@ function getPathListener(panel) {
}
function isNoCurrent(panel) {
const Info = DOM.CurrentInfo;
const infoPanel = Info.panel;
if (!infoPanel)
@ -186,6 +188,7 @@ function decodePath(path) {
}
async function onPathElementClick(panel, event) {
const Info = DOM.CurrentInfo;
event.preventDefault();
const element = event.target;
@ -215,12 +218,18 @@ async function onPathElementClick(panel, event) {
function copyPath(el) {
clipboard
.writeText(el
.parentElement.title)
.writeText(el.parentElement.title)
.then(CloudCmd.log)
.catch(CloudCmd.log);
}
function execIfNotMobile(callback, event) {
const isMobile = DOM.getCSSVar('is-mobile');
if (!isMobile)
callback(event);
}
function execIfNotUL(callback, event) {
const {target} = event;
const {tagName} = target;
@ -235,14 +244,14 @@ function onClick(event) {
}
function toggleSelect(key, files) {
const isMac = /Mac/.test(window.navigator.platform);
const isMac = /Mac/.test(globalThis.navigator.platform);
if (!key)
throw Error('key should not be undefined!');
const [file] = files;
if (isMac && key.meta || key.ctrl)
if (isMac && key.meta)
return DOM.toggleSelectedFile(file);
if (key.shift)
@ -250,6 +259,7 @@ function toggleSelect(key, files) {
}
function changePanel(element) {
const Info = DOM.CurrentInfo;
const {panel} = Info;
const files = DOM.getByDataName('js-files', panel);
const ul = getULElement(element);
@ -291,6 +301,7 @@ async function onTouch(event) {
* in Chrome (HTML5)
*/
function onDragStart(event) {
const Info = DOM.CurrentInfo;
const {prefixURL} = CloudCmd;
const element = getLIElement(event.target);
const {isDir} = Info;
@ -327,6 +338,7 @@ function getULElement(element) {
}
function setCurrentFileByEvent(event) {
const Info = DOM.CurrentInfo;
const BUTTON_LEFT = 0;
const key = {
@ -415,7 +427,7 @@ function dragndrop() {
};
/**
* In Mac OS Chrome dropEffect = 'none'
* In macOS Chrome dropEffect = 'none'
* so drop do not firing up when try
* to upload file from download bar
*/
@ -440,7 +452,7 @@ function dragndrop() {
}
function unload() {
DOM.Events.add(['unload', 'beforeunload'], (event) => {
Events.add(['unload', 'beforeunload'], (event) => {
const {Key} = CloudCmd;
const isBind = Key?.isBind();
@ -469,7 +481,8 @@ function pop() {
function 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)
return;

View file

@ -1,18 +1,16 @@
'use strict';
/* global CloudCmd */
const exec = require('execon');
const tryToCatch = require('try-to-catch');
const loadJS = require('load.js').js;
import exec from 'execon';
import {tryToCatch} from 'try-to-catch';
import {js as loadJS} from 'load.js';
import pascalCase from 'just-pascal-case';
const pascalCase = require('just-pascal-case');
const noJS = (a) => a.replace(/.js$/, '');
/**
* function load modules
* @params = {name, path, func, dobefore, arg}
*/
module.exports = function loadModule(params) {
export const loadModule = (params) => {
if (!params)
return;
@ -51,7 +49,7 @@ module.exports = function loadModule(params) {
const [e, a] = await tryToCatch(m);
if (e)
return console.error(e);
return;
return await a.show(...args);
};

View file

@ -9,7 +9,7 @@ const load = require('load.js');
const {ajax} = require('../dom/load');
const Files = require('../dom/files');
const Images = require('../dom/images');
const Images = require('../dom/images.mjs');
const {log} = CloudCmd;
const upload = currify(_upload);

View file

@ -8,16 +8,16 @@ const currify = require('currify');
const wraptile = require('wraptile');
const squad = require('squad');
const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch');
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 Images = require('../../dom/images.mjs');
const Events = require('#dom/events');
const Files = require('../../dom/files');
const {getTitle} = require('../../../common/cloudfunc');
const {getTitle} = require('../../../common/cloudfunc.mjs');
const {Dialog, setTitle} = DOM;
const Name = 'Config';
@ -52,12 +52,12 @@ module.exports.init = async () => {
showLoad();
const {prefix} = CloudCmd;
const {DIR_DIST} = CloudCmd;
[Template] = await Promise.all([
Files.get('config-tmpl'),
loadSocket(),
loadCSS(`${prefix}/dist/config.css`),
loadCSS(`${DIR_DIST}/config.css`),
CloudCmd.View(),
]);
@ -135,15 +135,19 @@ async function fillTemplate() {
const {
editor,
menu,
packer,
columns,
theme,
configAuth,
...obj
} = input.convert(config);
obj[`${menu}-selected`] = 'selected';
obj[`${editor}-selected`] = 'selected';
obj[`${packer}-selected`] = 'selected';
obj[`${columns}-selected`] = 'selected';
obj[`${theme}-selected`] = 'selected';
obj.configAuth = configAuth ? '' : 'hidden';
const innerHTML = rendy(Template, obj);
@ -223,7 +227,8 @@ function onAuthChange(checked) {
const elUsername = input.getElementByName('username', Element);
const elPassword = input.getElementByName('password', Element);
elUsername.disabled = elPassword.disabled = !checked;
elUsername.disabled = !checked;
elPassword.disabled = !checked;
}
function onNameChange(name) {

View file

@ -6,7 +6,7 @@
CloudCmd.Contact = exports;
const olark = require('@cloudcmd/olark');
const Images = require('../dom/images');
const Images = require('../dom/images.mjs');
const {Events} = DOM;
const {Key} = CloudCmd;

View file

@ -3,7 +3,7 @@
/* global CloudCmd */
CloudCmd.EditFileVim = exports;
const Events = require('../dom/events');
const Events = require('#dom/events');
const {Key} = CloudCmd;

View file

@ -4,7 +4,7 @@
CloudCmd.EditFile = exports;
const Format = require('format-io');
const fullstore = require('fullstore');
const {fullstore} = require('fullstore');
const exec = require('execon');
const supermenu = require('supermenu');
@ -61,8 +61,7 @@ module.exports.show = async (options) => {
Images.show.load();
CloudCmd
.Edit
CloudCmd.Edit
.getEditor()
.setOption('keyMap', 'default');
@ -78,8 +77,7 @@ module.exports.show = async (options) => {
setMsgChanged(name);
CloudCmd
.Edit
CloudCmd.Edit
.getEditor()
.setValueFirst(path, data)
.setModeForPath(name)
@ -131,8 +129,7 @@ function setMenu(event) {
},
afterClick: () => {
CloudCmd
.Edit
CloudCmd.Edit
.getEditor()
.focus();
},

View file

@ -3,7 +3,7 @@
/* global CloudCmd */
CloudCmd.EditNamesVim = exports;
const Events = require('../dom/events');
const Events = require('#dom/events');
const {Key} = CloudCmd;
const ConfigView = {
@ -21,8 +21,7 @@ module.exports.init = async () => {
module.exports.show = () => {
Events.addKey(listener);
CloudCmd
.EditNames
CloudCmd.EditNames
.show(ConfigView)
.getEditor()
.setKeyMap('vim');

View file

@ -1,21 +1,17 @@
'use strict';
const {tryToCatch} = require('try-to-catch');
/* global CloudCmd, DOM */
CloudCmd.EditNames = exports;
const currify = require('currify');
const exec = require('execon');
const supermenu = require('supermenu');
const multiRename = require('multi-rename');
const reject = Promise.reject.bind(Promise);
const {multiRename} = require('multi-rename');
const Info = DOM.CurrentInfo;
const {Dialog} = DOM;
const refresh = currify(_refresh);
const rename = currify(_rename);
let Menu;
const ConfigView = {
@ -44,8 +40,7 @@ module.exports.show = (options) => {
DOM.Events.addKey(keyListener);
CloudCmd
.Edit
CloudCmd.Edit
.getEditor()
.setValueFirst('edit-names', names)
.setMode()
@ -63,9 +58,9 @@ async function keyListener(event) {
const ctrlMeta = ctrl || meta;
const {Key} = CloudCmd;
if (ctrlMeta && event.keyCode === Key.S)
if (ctrlMeta && event.keyCode === Key.S) {
hide();
else if (ctrlMeta && event.keyCode === Key.P) {
} else if (ctrlMeta && event.keyCode === Key.P) {
const [, pattern] = await Dialog.prompt('Apply pattern:', '[n][e]');
pattern && applyPattern(pattern);
}
@ -94,7 +89,7 @@ function setListeners() {
DOM.Events.addOnce('contextmenu', element, setMenu);
}
function applyNames() {
async function applyNames() {
const dir = Info.dirPath;
const from = getActiveNames();
const nameIndex = from.indexOf(Info.name);
@ -106,18 +101,18 @@ function applyNames() {
const root = CloudCmd.config('root');
Promise
.resolve(root)
.then(rename(dir, from, to))
.then(refresh(to, nameIndex))
.catch(alert);
const response = rename(dir, from, to, root);
const [error] = await tryToCatch(refresh, to, nameIndex, response);
if (error)
alert(error);
}
function _refresh(to, nameIndex, res) {
if (res.status === 404)
return res
.text()
.then(reject);
function refresh(to, nameIndex, res) {
if (res.status === 404) {
const error = res.text();
throw error;
}
const currentName = to[nameIndex];
@ -133,7 +128,7 @@ function getDir(root, dir) {
return root + dir;
}
function _rename(path, from, to, root) {
function rename(path, from, to, root) {
const dir = getDir(root, path);
const {prefix} = CloudCmd;
@ -173,8 +168,8 @@ function setMenu(event) {
};
const menuData = {
'Save Ctrl+S': () => {
applyNames();
'Save Ctrl+S': async () => {
await applyNames();
hide();
},
'Go To Line Ctrl+G': () => {
@ -215,6 +210,7 @@ async function isChanged() {
if (!editor.isChanged())
return;
const [, names] = await Dialog.confirm(msg);
names && applyNames();
const [cancel] = await Dialog.confirm(msg);
!cancel && await applyNames();
}

View file

@ -5,12 +5,13 @@
const montag = require('montag');
const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const createElement = require('@cloudcmd/create-element');
const load = require('load.js');
const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc');
const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc.mjs');
const {time, timeEnd} = require('../../common/util');
const getEditor = () => editor;
const isFn = (a) => typeof a === 'function';
const loadJS = load.js;
@ -91,10 +92,6 @@ module.exports.show = (options) => {
module.exports.getEditor = getEditor;
function getEditor() {
return editor;
}
module.exports.getElement = () => Element;
module.exports.hide = () => {

View file

@ -3,7 +3,7 @@
/* global CloudCmd */
CloudCmd.Help = exports;
const Images = require('../dom/images');
const Images = require('../dom/images.mjs');
module.exports.init = () => {
Images.show.load('top');

View file

@ -8,11 +8,11 @@ CloudCmd.Konsole = exports;
const exec = require('execon');
const currify = require('currify');
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const loadJS = require('load.js').js;
const createElement = require('@cloudcmd/create-element');
const Images = require('../dom/images');
const Images = require('../dom/images.mjs');
const {Dialog, CurrentInfo: Info} = DOM;
const rmLastSlash = (a) => a.replace(/\/$/, '') || '/';
@ -46,22 +46,18 @@ module.exports.clear = () => {
konsole.clear();
};
function getPrefix() {
return CloudCmd.prefix + '/console';
}
const getPrefix = () => CloudCmd.prefix + '/console';
function getPrefixSocket() {
return CloudCmd.prefixSocket + '/console';
}
function getEnv() {
return {
ACTIVE_DIR: DOM.getCurrentDirPath.bind(DOM),
PASSIVE_DIR: DOM.getNotCurrentDirPath.bind(DOM),
CURRENT_NAME: DOM.getCurrentName.bind(DOM),
CURRENT_PATH: () => Info.path,
};
}
const getEnv = () => ({
ACTIVE_DIR: DOM.getCurrentDirPath.bind(DOM),
PASSIVE_DIR: DOM.getNotCurrentDirPath.bind(DOM),
CURRENT_NAME: DOM.getCurrentName.bind(DOM),
CURRENT_PATH: () => Info.path,
});
async function onPath(path) {
if (Info.dirPath === path)

View file

@ -5,7 +5,7 @@ CloudCmd.Markdown = exports;
const createElement = require('@cloudcmd/create-element');
const Images = require('../dom/images');
const Images = require('../dom/images.mjs');
const {Markdown} = require('../dom/rest');
const {alert} = require('../dom/dialog');

View file

@ -0,0 +1,30 @@
import supermenu from 'supermenu';
const noop = () => {};
const {CloudCmd} = globalThis;
export const createCloudMenu = async (fm, options, menuData) => {
const createMenu = await loadMenu();
const menu = await createMenu(fm, options, menuData);
menu.addContextMenuListener = menu.addContextMenuListener || noop;
return menu;
};
async function loadMenu() {
if (CloudCmd.config('menu') === 'aleman') {
const {host, protocol} = globalThis.location;
const url = `${protocol}//${host}/node_modules/aleman/menu/menu.js`;
const {createMenu} = await import(/* webpackIgnore: true */url);
return createMenu;
}
return createSupermenu;
}
function createSupermenu(name, options, menuData) {
const element = document.querySelector('[data-name="js-fm"]');
return supermenu(element, options, menuData);
}

View file

@ -4,12 +4,11 @@
const exec = require('execon');
const wrap = require('wraptile');
const supermenu = require('supermenu');
const createElement = require('@cloudcmd/create-element');
const {FS} = require('../../common/cloudfunc');
const {getIdBySrc} = require('../dom/load');
const RESTful = require('../dom/rest');
const {FS} = require('../../../common/cloudfunc.mjs');
const {getIdBySrc} = require('../../dom/load');
const RESTful = require('../../dom/rest');
const {config, Key} = CloudCmd;
@ -32,7 +31,7 @@ module.exports.ENABLED = false;
CloudCmd.Menu = exports;
module.exports.init = () => {
module.exports.init = async () => {
const {isAuth, menuDataFile} = getFileMenuData();
const fm = DOM.getFM();
@ -46,8 +45,12 @@ module.exports.init = () => {
type: 'file',
});
MenuContext = supermenu(fm, options, menuData);
MenuContextFile = supermenu(fm, optionsFile, menuDataFile);
const {createCloudMenu} = await import('./cloudmenu.mjs');
const {name} = fm.dataset;
MenuContext = await createCloudMenu(name, options, menuData);
MenuContextFile = await createCloudMenu(name, optionsFile, menuDataFile);
MenuContext.addContextMenuListener();
MenuContextFile.addContextMenuListener();
@ -106,7 +109,9 @@ function getOptions({type}) {
const options = {
icon: true,
infiniteScroll: false,
beforeClose: Key.setBind,
beforeHide: Key.setBind,
beforeShow: exec.with(beforeShow, func),
beforeClick,
name,
@ -126,6 +131,7 @@ function getMenuData(isAuth) {
CloudCmd.Upload.show();
},
'Upload From Cloud': uploadFromCloud,
'Toggle File Selection': DOM.toggleSelectedFile,
'(Un)Select All': DOM.toggleAllSelectedFiles,
};
@ -197,14 +203,25 @@ function isPath(x, y) {
const el = document.elementFromPoint(x, y);
const elements = panel.querySelectorAll('[data-name="js-path"] *');
return ~[].indexOf.call(elements, el);
return !~[].indexOf.call(elements, el);
}
function beforeShow(callback, params) {
const {name} = params;
Key.unsetBind();
const {
name,
position = {
x: params.x,
y: params.y,
},
} = params;
const {x, y} = position;
const el = DOM.getCurrentByPosition({
x: params.x,
y: params.y,
x,
y,
});
const menuName = getMenuNameByEl(el);
@ -220,14 +237,12 @@ function beforeShow(callback, params) {
exec(callback);
if (isShow)
isShow = isPath(params.x, params.y);
isShow = isPath(x, y);
return isShow;
}
function beforeClick(name) {
return MenuShowedName !== name;
}
const beforeClick = (name) => MenuShowedName !== name;
async function _uploadTo(nameModule) {
const [error, data] = await Info.getData();
@ -277,6 +292,7 @@ function download(type) {
const path = DOM.getCurrentPath(file);
CloudCmd.log(`downloading file ${path}...`);
/*
* if we send ajax request -
* no need in hash so we escape #
@ -327,13 +343,12 @@ function listener(event) {
const key = event.keyCode;
const isBind = Key.isBind();
if (!isBind)
return;
if (key === ESC)
if (key === ESC) {
Key.setBind();
return hide();
}
if (key === F9) {
if (isBind && key === F9) {
const position = getCurrentPosition();
MenuContext.show(position.x, position.y);

View file

@ -1,28 +1,20 @@
/* global CloudCmd */
/* global Util */
/* global DOM */
/* global fileop */
import currify from 'currify';
import wraptile from 'wraptile';
import {promisify} from 'es6-promisify';
import exec from 'execon';
import load from 'load.js';
import {tryToCatch} from 'try-to-catch';
import {encode} from '../../../common/entity.js';
import removeExtension from './remove-extension.js';
import {setListeners} from './set-listeners.mjs';
import getNextCurrentName from './get-next-current-name.js';
'use strict';
const currify = require('currify');
const wraptile = require('wraptile');
const {promisify} = require('es6-promisify');
const exec = require('execon');
const load = require('load.js');
const tryToCatch = require('try-to-catch');
const {encode} = require('../../../common/entity');
const removeExtension = require('./remove-extension');
const setListeners = require('./set-listeners');
const getNextCurrentName = require('./get-next-current-name');
const {DOM, CloudCmd} = globalThis;
const removeQuery = (a) => a.replace(/\?.*/, '');
const Name = 'Operation';
CloudCmd[Name] = exports;
const {config} = CloudCmd;
const {Dialog, Images} = DOM;
@ -53,7 +45,7 @@ const noFilesCheck = () => {
return is;
};
module.exports.init = promisify((callback) => {
export const init = promisify((callback) => {
showLoad();
exec.series([
@ -92,7 +84,7 @@ const onConnect = currify((fn, operator) => {
async function initOperations(prefix, socketPrefix, fn) {
socketPrefix = `${socketPrefix}/fileop`;
const operator = await fileop({
const operator = await globalThis.fileop({
prefix,
socketPrefix,
});
@ -198,11 +190,11 @@ function getPacker(type) {
return packTarFn;
}
module.exports.hide = () => {
export const hide = () => {
CloudCmd.View.hide();
};
module.exports.show = (operation, data) => {
export const show = (operation, data) => {
if (!Loaded)
return;
@ -343,10 +335,13 @@ async function _processFiles(options, data) {
let names = [];
if (data) {
from = data.from;
to = data.to;
names = data.names;
panel = Info.panel;
({
from,
to,
names,
} = data);
({panel} = Info);
} else {
from = Info.dirPath;
to = DOM.getNotCurrentDirPath();
@ -502,8 +497,14 @@ async function prompt(msg, to, names) {
return await Dialog.prompt(msg, to);
}
globalThis.CloudCmd[Name] = {
init,
hide,
show,
};
async function loadAll() {
const {prefix} = CloudCmd;
const {prefix} = globalThis.CloudCmd;
const file = `${prefix}/fileop/fileop.js`;
const [error] = await tryToCatch(load.js, file);

View file

@ -9,10 +9,10 @@ module.exports = (name) => {
};
function getExtension(name) {
if (/\.tar\.gz$/.test(name))
if (name.endsWith('.tar.gz'))
return '.tar.gz';
if (/\.tar\.bz2$/.test(name))
if (name.endsWith('.tar.bz2'))
return '.tar.bz2';
return getExt(name);

View file

@ -1,14 +1,11 @@
'use strict';
/* global DOM */
const forEachKey = require('for-each-key');
const wraptile = require('wraptile');
const format = require('./format');
import forEachKey from 'for-each-key';
import wraptile from 'wraptile';
import format from './format.js';
const {Dialog, Images} = DOM;
module.exports = (options) => (emitter) => {
export const setListeners = (options) => (emitter) => {
const {
operation,
callback,
@ -43,10 +40,13 @@ module.exports = (options) => (emitter) => {
operation,
}));
let noProgress = true;
const listeners = {
progress: (value) => {
done = value === 100;
progress.setProgress(value);
noProgress = false;
},
end: () => {
@ -54,7 +54,7 @@ module.exports = (options) => (emitter) => {
forEachKey(removeListener, listeners);
progress.remove();
if (lastError || done)
if (lastError || done || noProgress)
callback();
},

View file

@ -1,10 +1,20 @@
'use strict';
/* global DOM */
require('domtokenlist-shim');
const scrollIntoViewIfNeeded = require('scroll-into-view-if-needed').default;
const _scrollIntoViewIfNeeded = require('scroll-into-view-if-needed');
DOM.scrollIntoViewIfNeeded = (el) => scrollIntoViewIfNeeded(el, {
block: 'nearest',
});
globalThis.DOM = globalThis.DOM || {};
const scrollIntoViewIfNeeded = (el, overrides = {}) => {
const {
scroll = _scrollIntoViewIfNeeded,
} = overrides;
return scroll(el, {
block: 'nearest',
});
};
globalThis.DOM.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;

View file

@ -1,33 +1,21 @@
'use strict';
const {test, stub} = require('supertape');
const mockRequire = require('mock-require');
const {stopAll} = mockRequire;
const {scrollIntoViewIfNeeded} = require('./polyfill');
test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => {
const {DOM} = global;
const scroll = stub();
const el = {};
global.DOM = {};
mockRequire('scroll-into-view-if-needed', {
default: scroll,
scrollIntoViewIfNeeded(el, {
scroll,
});
mockRequire.reRequire('./polyfill');
global.DOM.scrollIntoViewIfNeeded(el);
mockRequire.stop('scroll-into-view-if-neaded');
global.DOM = DOM;
const args = [el, {
block: 'nearest',
}];
stopAll();
const args = [
el, {
block: 'nearest',
},
];
t.calledWith(scroll, args, 'should call scrollIntoViewIfNeaded');
t.end();

View file

@ -2,15 +2,15 @@
/* global CloudCmd, gritty */
const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch');
const fullstore = require('fullstore');
const {tryToCatch} = require('try-to-catch');
const {fullstore} = require('fullstore');
require('../../css/terminal.css');
const exec = require('execon');
const load = require('load.js');
const DOM = require('../dom');
const Images = require('../dom/images');
const Images = require('../dom/images.mjs');
const {Dialog} = DOM;
const {Key, config} = CloudCmd;
@ -33,7 +33,7 @@ const loadAll = async () => {
const [e] = await tryToCatch(load.parallel, [js, css]);
if (e) {
const src = e.target.src.replace(window.location.href, '');
const src = e.target.src.replace(globalThis.location.href, '');
return Dialog.alert(`file ${src} could not be loaded`);
}
@ -75,22 +75,18 @@ function hide() {
CloudCmd.View.hide();
}
function getPrefix() {
return CloudCmd.prefix + '/gritty';
}
const getPrefix = () => CloudCmd.prefix + '/gritty';
function getPrefixSocket() {
return CloudCmd.prefixSocket + '/gritty';
}
function getEnv() {
return {
ACTIVE_DIR: DOM.getCurrentDirPath,
PASSIVE_DIR: DOM.getNotCurrentDirPath,
CURRENT_NAME: DOM.getCurrentName,
CURRENT_PATH: DOM.getCurrentPath,
};
}
const getEnv = () => ({
ACTIVE_DIR: DOM.getCurrentDirPath,
PASSIVE_DIR: DOM.getNotCurrentDirPath,
CURRENT_NAME: DOM.getCurrentName,
CURRENT_PATH: DOM.getCurrentPath,
});
function create(createOptions) {
const {

View file

@ -2,14 +2,14 @@
/* global CloudCmd */
/* global gritty */
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
require('../../css/terminal.css');
const exec = require('execon');
const load = require('load.js');
const DOM = require('../dom');
const Images = require('../dom/images');
const Images = require('../dom/images.mjs');
const loadParallel = load.parallel;
@ -32,7 +32,7 @@ const loadAll = async () => {
const [e] = await tryToCatch(loadParallel, [js, css]);
if (e) {
const src = e.target.src.replace(window.location.href, '');
const src = e.target.src.replace(globalThis.location.href, '');
return Dialog.alert(`file ${src} could not be loaded`);
}
@ -57,22 +57,18 @@ function hide() {
CloudCmd.View.hide();
}
function getPrefix() {
return CloudCmd.prefix + '/gritty';
}
const getPrefix = () => CloudCmd.prefix + '/gritty';
function getPrefixSocket() {
return CloudCmd.prefixSocket + '/gritty';
}
function getEnv() {
return {
ACTIVE_DIR: DOM.getCurrentDirPath,
PASSIVE_DIR: DOM.getNotCurrentDirPath,
CURRENT_NAME: DOM.getCurrentName,
CURRENT_PATH: DOM.getCurrentPath,
};
}
const getEnv = () => ({
ACTIVE_DIR: DOM.getCurrentDirPath,
PASSIVE_DIR: DOM.getNotCurrentDirPath,
CURRENT_NAME: DOM.getCurrentName,
CURRENT_PATH: DOM.getCurrentPath,
});
function create() {
const options = {

View file

@ -4,10 +4,10 @@
CloudCmd.Upload = exports;
const Files = require('../dom/files');
const Images = require('../dom/images');
const uploadFiles = require('../dom/upload-files');
const createElement = require('@cloudcmd/create-element');
const Files = require('../dom/files');
const Images = require('../dom/images.mjs');
const uploadFiles = require('../dom/upload-files');
module.exports.init = async () => {
Images.show.load('top');

View file

@ -5,14 +5,14 @@ require('../../../css/user-menu.css');
const currify = require('currify');
const wraptile = require('wraptile');
const fullstore = require('fullstore');
const {fullstore} = require('fullstore');
const load = require('load.js');
const createElement = require('@cloudcmd/create-element');
const tryCatch = require('try-catch');
const tryToCatch = require('try-to-catch');
const {tryCatch} = require('try-catch');
const {tryToCatch} = require('try-to-catch');
const {codeFrameColumns} = require('@babel/code-frame');
const Images = require('../../dom/images');
const Images = require('../../dom/images.mjs');
const Dialog = require('../../dom/dialog');
const getUserMenu = require('./get-user-menu');
const navigate = require('./navigate');

View file

@ -1,6 +1,6 @@
'use strict';
const fullstore = require('fullstore');
const {fullstore} = require('fullstore');
const {
J,

View file

@ -5,7 +5,7 @@ const testRegExp = currify((name, reg) => reg.test(name));
const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i');
const isPDF = (a) => /\.pdf$/i.test(a);
const isHTML = (a) => /\.html$/.test(a);
const isHTML = (a) => a.endsWith('.html');
const isMarkdown = (a) => /.\.md$/.test(a);
module.exports = (name) => {
@ -45,9 +45,7 @@ function isMedia(name) {
return isAudio(name) || isVideo(name);
}
function isAudio(name) {
return /\.(mp3|ogg|m4a)$/i.test(name);
}
const isAudio = (name) => /\.(mp3|ogg|m4a)$/i.test(name);
function isVideo(name) {
return /\.(mp4|avi|webm)$/i.test(name);

View file

@ -2,19 +2,22 @@
'use strict';
const CloudCmd = globalThis.CloudCmd || {};
const DOM = globalThis.DOM || {};
require('../../../css/view.css');
const rendy = require('rendy');
const currify = require('currify');
const wraptile = require('wraptile');
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const load = require('load.js');
const modal = require('@cloudcmd/modal');
const createElement = require('@cloudcmd/create-element');
const _modal = require('@cloudcmd/modal');
const _createElement = require('@cloudcmd/create-element');
const {time} = require('../../../common/util');
const {FS} = require('../../../common/cloudfunc');
const {FS} = require('../../../common/cloudfunc.mjs');
const {
isImage,
@ -23,8 +26,8 @@ const {
} = require('./types');
const Files = require('../../dom/files');
const Events = require('../../dom/events');
const Images = require('../../dom/images');
const Events = require('#dom/events');
const Images = require('../../dom/images.mjs');
const {encode} = require('../../../common/entity');
const isString = (a) => typeof a === 'string';
@ -55,7 +58,10 @@ CloudCmd[Name] = module.exports;
const Info = DOM.CurrentInfo;
const {Key} = CloudCmd;
const basename = (a) => a.split('/').pop();
const basename = (a) => a
.split('/')
.pop();
let El;
let TemplateAudio;
@ -110,7 +116,7 @@ async function show(data, options = {}) {
if (!options || options.bindKeys !== false)
Events.addKey(listener);
El = createElement('div', {
El = _createElement('div', {
className: 'view',
notAppend: true,
});
@ -123,7 +129,7 @@ async function show(data, options = {}) {
else
El.append(data);
modal.open(El, initConfig(options));
_modal.open(El, initConfig(options));
return;
}
@ -154,7 +160,11 @@ async function show(data, options = {}) {
}
module.exports._createIframe = createIframe;
function createIframe(src) {
function createIframe(src, overrides = {}) {
const {
createElement = _createElement,
} = overrides;
const element = createElement('iframe', {
src,
width: '100%',
@ -169,7 +179,8 @@ function createIframe(src) {
}
module.exports._viewHtml = viewHtml;
function viewHtml(src) {
function viewHtml(src, overrides = {}) {
const {modal = _modal} = overrides;
modal.open(createIframe(src), Config);
}
@ -181,7 +192,7 @@ function viewPDF(src) {
if (CloudCmd.config('showFileName'))
options.title = Info.name;
modal.open(element, options);
_modal.open(element, options);
}
async function viewMedia(path) {
@ -202,7 +213,7 @@ async function viewMedia(path) {
},
};
modal.open(element, allConfig);
_modal.open(element, allConfig);
}
async function viewFile() {
@ -218,7 +229,7 @@ async function viewFile() {
options.title = Info.name;
El.append(element);
modal.open(El, options);
_modal.open(El, options);
}
const copy = (a) => assign({}, a);
@ -250,7 +261,7 @@ function initConfig(options) {
}
function hide() {
modal.close();
_modal.close();
}
function viewImage(path, prefixURL) {
@ -260,8 +271,7 @@ function viewImage(path, prefixURL) {
title: encode(basename(path)),
});
const names = Info
.files
const names = Info.files
.map(DOM.getCurrentPath)
.filter(isSupportedImage);
@ -284,7 +294,7 @@ function viewImage(path, prefixURL) {
...imageConfig,
};
modal.open(titles, config);
_modal.open(titles, config);
}
async function getMediaElement(src) {
@ -309,7 +319,7 @@ async function getMediaElement(src) {
name,
});
const element = createElement('div', {
const element = _createElement('div', {
innerHTML,
});
@ -321,10 +331,6 @@ function check(src) {
throw Error('src should be a string!');
}
/**
* function loads css and js of FancyBox
* @callback - executes, when everything loaded
*/
async function loadAll() {
const {DIR_DIST} = CloudCmd;

View file

@ -3,22 +3,20 @@
require('css-modules-require-hook/preset');
const autoGlobals = require('auto-globals');
const stub = require('@cloudcmd/stub');
const mockRequire = require('mock-require');
const {stub} = require('@cloudcmd/stub');
const test = autoGlobals(require('supertape'));
const {reRequire, stopAll} = mockRequire;
const {
_initConfig,
_viewHtml,
_Config,
_createIframe,
} = require('.');
test('cloudcmd: client: view: initConfig', (t) => {
let config;
let i = 0;
const {CloudCmd, DOM} = global;
global.CloudCmd = {};
global.DOM = {};
const {_initConfig} = reRequire('.');
const afterClose = () => ++i;
const options = {
afterClose,
@ -30,54 +28,32 @@ test('cloudcmd: client: view: initConfig', (t) => {
config = _initConfig(options);
config.afterClose();
global.CloudCmd = CloudCmd;
global.DOM = DOM;
t.equal(i, 2, 'should not change default config');
t.end();
});
test('cloudcmd: client: view: initConfig: no options', (t) => {
const {CloudCmd, DOM} = global;
global.CloudCmd = {};
global.DOM = {};
const {_initConfig} = reRequire('.');
const config = _initConfig();
global.CloudCmd = CloudCmd;
global.DOM = DOM;
t.equal(typeof config, 'object');
t.end();
});
test('cloudcmd: client: view: html', (t) => {
const {CloudCmd, DOM} = global;
global.CloudCmd = {};
global.DOM = {};
const open = stub();
mockRequire('@cloudcmd/modal', {
const modal = {
open,
});
const {_viewHtml, _Config} = reRequire('.');
};
const src = '/hello.html';
_viewHtml(src);
global.CloudCmd = CloudCmd;
global.DOM = DOM;
_viewHtml(src, {
modal,
});
const [first] = open.args;
const [arg] = first;
stopAll();
t.deepEqual(first, [arg, _Config]);
t.end();
});
@ -89,12 +65,11 @@ test('cloudcmd: client: view: createIframe', (t) => {
};
const createElement = stub().returns(el);
mockRequire('@cloudcmd/create-element', createElement);
const {_createIframe} = reRequire('.');
const src = '/hello.html';
_createIframe(src);
_createIframe(src, {
createElement,
});
const expected = {
src,
@ -102,8 +77,6 @@ test('cloudcmd: client: view: createIframe', (t) => {
width: '100%',
};
stopAll();
t.calledWith(createElement, ['iframe', expected]);
t.end();
});
@ -116,13 +89,10 @@ test('cloudcmd: client: view: createIframe: returns', (t) => {
const createElement = stub().returns(el);
mockRequire('@cloudcmd/create-element', createElement);
const {_createIframe} = reRequire('.');
const src = '/hello.html';
const result = _createIframe(src);
stopAll();
const result = _createIframe(src, {
createElement,
});
t.equal(result, el);
t.end();

View file

@ -1,12 +1,13 @@
'use strict';
const {extname} = require('path');
const {extname} = require('node:path');
const currify = require('currify');
const isAudio = (name) => /\.(mp3|ogg|m4a)$/i.test(name);
const testRegExp = currify((name, reg) => reg.test(name));
const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i');
const isPDF = (a) => /\.pdf$/i.test(a);
const isHTML = (a) => /\.html$/.test(a);
const isHTML = (a) => a.endsWith('.html');
const isMarkdown = (a) => /.\.md$/.test(a);
module.exports.getType = async (path) => {
@ -53,9 +54,6 @@ function isMedia(name) {
}
module.exports.isAudio = isAudio;
function isAudio(name) {
return /\.(mp3|ogg|m4a)$/i.test(name);
}
function isVideo(name) {
return /\.(mp4|avi|webm)$/i.test(name);

View file

@ -22,12 +22,9 @@ test('cloudcmd: client: view: types: detectType', async (t) => {
headers: [],
});
const originalFetch = global.fetch;
global.fetch = fetch;
globalThis.fetch = fetch;
await _detectType('/hello');
global.fetch = originalFetch;
const expected = ['/hello', {
method: 'HEAD',
}];
@ -37,17 +34,13 @@ test('cloudcmd: client: view: types: detectType', async (t) => {
});
test('cloudcmd: client: view: types: detectType: found', async (t) => {
const originalFetch = global.fetch;
global.fetch = stub().returns({
globalThis.fetch = stub().returns({
headers: [
['content-type', 'image/png'],
],
});
const result = await _detectType('/hello');
global.fetch = originalFetch;
t.equal(result, '.png');
t.end();
});

View file

@ -1,34 +0,0 @@
'use strict';
/* global CloudCmd */
const DOM = require('./dom');
const Info = DOM.CurrentInfo;
const {sort, order} = CloudCmd;
const position = DOM.getPanelPosition();
let sortPrevious = sort[position];
const {getPanel} = DOM;
CloudCmd.sortPanel = (name, panel = getPanel()) => {
const position = panel.dataset.name.replace('js-', '');
if (name !== sortPrevious)
order[position] = 'asc';
else if (order[position] === 'asc')
order[position] = 'desc';
else
order[position] = 'asc';
sortPrevious = sort[position] = name;
const noCurrent = position !== Info.panelPosition;
CloudCmd.refresh({
panel,
noCurrent,
});
};

36
client/sort.mjs Normal file
View file

@ -0,0 +1,36 @@
/* global CloudCmd */
import {fullstore} from 'fullstore';
import DOM from './dom/index.js';
const sortPrevious = fullstore();
const {getPanel} = DOM;
export const initSortPanel = () => {
const {sort} = CloudCmd;
const position = DOM.getPanelPosition();
sortPrevious(sort[position]);
};
export const sortPanel = (name, panel = getPanel()) => {
const {sort, order} = CloudCmd;
const Info = DOM.CurrentInfo;
const position = panel.dataset.name.replace('js-', '');
if (name !== sortPrevious())
order[position] = 'asc';
else if (order[position] === 'asc')
order[position] = 'desc';
else
order[position] = 'asc';
sortPrevious(name);
sort[position] = name;
const noCurrent = position !== Info.panelPosition;
CloudCmd.refresh({
panel,
noCurrent,
});
};

View file

@ -1,6 +1,6 @@
'use strict';
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
module.exports.registerSW = registerSW;
module.exports.unregisterSW = unregisterSW;

View file

@ -3,14 +3,18 @@
const autoGlobals = require('auto-globals');
const tape = require('supertape');
const stub = require('@cloudcmd/stub');
const {stub} = require('@cloudcmd/stub');
const {tryCatch} = require('try-catch');
const {
listenSW,
registerSW,
unregisterSW,
} = require('./register');
const tryCatch = require('try-catch');
const {reRequire} = require('mock-require');
const test = autoGlobals(tape);
test('sw: listen', (t) => {
const {listenSW} = reRequire('./register');
const addEventListener = stub();
const sw = {
addEventListener,
@ -23,7 +27,6 @@ test('sw: listen', (t) => {
});
test('sw: lesten: no sw', (t) => {
const {listenSW} = reRequire('./register');
const [e] = tryCatch(listenSW, null, 'hello', 'world');
t.notOk(e, 'should not throw');
@ -31,8 +34,6 @@ test('sw: lesten: no sw', (t) => {
});
test('sw: register: registerSW: no serviceWorker', async (t, {navigator}) => {
const {registerSW} = reRequire('./register');
delete navigator.serviceWorker;
await registerSW();
@ -46,8 +47,6 @@ test('sw: register: registerSW: no https', async (t, {location, navigator}) => {
location.protocol = 'http:';
const {registerSW} = reRequire('./register');
await registerSW();
t.notCalled(register, 'should not call register');
@ -62,8 +61,6 @@ test('sw: register: registerSW: http', async (t, {location, navigator}) => {
const {register} = navigator.serviceWorker;
const {registerSW} = reRequire('./register');
await registerSW();
t.notCalled(register, 'should not call register');
@ -79,8 +76,6 @@ test('sw: register: registerSW: https self-signed', async (t, {location, navigat
const {register} = navigator.serviceWorker;
register.throws(Error('Cannot register service worker!'));
const {registerSW} = reRequire('./register');
const result = await registerSW();
t.notOk(result, 'should not throw');
@ -91,8 +86,6 @@ test('sw: register: registerSW', async (t, {location, navigator}) => {
location.hostname = 'localhost';
const {register} = navigator.serviceWorker;
const {registerSW} = reRequire('./register');
await registerSW('/hello');
t.calledWith(register, ['/hello/sw.js'], 'should call register');
@ -107,8 +100,6 @@ test('sw: register: unregisterSW', async (t, {location, navigator}) => {
register.returns(serviceWorker);
const {unregisterSW} = reRequire('./register');
await unregisterSW('/hello');
t.calledWith(register, ['/hello/sw.js'], 'should call register');

View file

@ -1,8 +1,8 @@
'use strict';
const process = require('process');
const process = require('node:process');
const codegen = require('codegen.macro');
const tryToCatch = require('try-to-catch');
const {tryToCatch} = require('try-to-catch');
const currify = require('currify');
const isDev = process.env.NODE_ENV === 'development';
@ -50,14 +50,14 @@ const getRequest = (a, request) => {
return createRequest('/');
};
self.addEventListener('install', wait(onInstall));
self.addEventListener('fetch', respondWith(onFetch));
self.addEventListener('activate', wait(onActivate));
globalThis.addEventListener('install', wait(onInstall));
globalThis.addEventListener('fetch', respondWith(onFetch));
globalThis.addEventListener('activate', wait(onActivate));
async function onActivate() {
console.info(`cloudcmd: sw: activate: ${NAME}`);
await self.clients.claim();
await globalThis.clients.claim();
const keys = await caches.keys();
const deleteCache = caches.delete.bind(caches);
@ -67,7 +67,7 @@ async function onActivate() {
async function onInstall() {
console.info(`cloudcmd: sw: install: ${NAME}`);
await self.skipWaiting();
await globalThis.skipWaiting();
}
async function onFetch(event) {

View file

@ -1,19 +0,0 @@
'use strict';
module.exports.btoa = (str) => {
if (typeof btoa === 'function')
return btoa(str);
return Buffer
.from(str)
.toString('base64');
};
module.exports.atob = (str) => {
if (typeof atob === 'function')
return atob(str);
return Buffer
.from(str, 'base64')
.toString('binary');
};

View file

@ -1,53 +0,0 @@
'use strict';
const {test, stub} = require('supertape');
const {btoa, atob} = require('./base64');
test('btoa: browser', (t) => {
const btoaOriginal = global.btoa;
const str = 'hello';
global.btoa = stub();
btoa(str);
t.calledWith(global.btoa, [str], 'should call global.btoa');
t.end();
global.btoa = btoaOriginal;
});
test('btoa: node', (t) => {
const str = 'hello';
const expected = 'aGVsbG8=';
const result = btoa(str);
t.equal(result, expected, 'should encode base64');
t.end();
});
test('atob: browser', (t) => {
const atobOriginal = global.atob;
const str = 'hello';
global.atob = stub();
atob(str);
t.calledWith(global.atob, [str], 'should call global.btoa');
t.end();
global.atob = atobOriginal;
});
test('atob: node', (t) => {
const str = 'aGVsbG8=';
const expected = 'hello';
const result = atob(str);
t.equal(result, expected, 'should encode base64');
t.end();
});

View file

@ -1,11 +1,11 @@
'use strict';
const tryToCatch = require('try-to-catch');
const {promisify} = require('node:util');
const {tryToCatch} = require('try-to-catch');
const {test, stub} = require('supertape');
const callbackify = require('./callbackify');
const {promisify} = require('util');
test('cloudcmd: common: callbackify: error', async (t) => {
const promise = stub().rejects(Error('hello'));

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