├── typings
├── editorFile.d.ts
├── settings.d.ts
├── index.d.ts
└── acode.d.ts
├── .gitignore
├── icon.png
├── postcss.config.js
├── tsconfig.json
├── dist
└── main.js.LICENSE.txt
├── plugin.json
├── .babelrc
├── src
├── style.css
└── main.js
├── .vscode
├── run-webpack.js
├── server.crt
├── start-dev.js
├── server.key
├── getNet.js
├── pack-zip.js
└── start-server.js
├── package.json
├── webpack.config.js
└── README.md
/typings/editorFile.d.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/typings/settings.d.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | yarn.lock
3 | dist.zip
4 | distnode_modules/
5 |
6 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chaos-19/Acode-Clone-repository/HEAD/icon.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('autoprefixer')({})
4 | ]
5 | };
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2016",
4 | "module": "commonjs",
5 | "allowJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "skipLibCheck": true,
10 | "outDir": "dist"
11 | },
12 | "exclude": ["./dist/**/*", "./postcss.config.js"]
13 | }
14 |
--------------------------------------------------------------------------------
/dist/main.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*!
2 | * The buffer module from node.js, for the browser.
3 | *
4 | * @author Feross Aboukhadijeh
5 | * @license MIT
6 | */
7 |
8 | /*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
9 |
10 | /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */
11 |
--------------------------------------------------------------------------------
/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "aocde.clone.repo",
3 | "name": "Clone Repository",
4 | "main": "dist/main.js",
5 | "version": "2.0.0",
6 | "readme": "readme.md",
7 | "icon": "icon.png",
8 | "files": [],
9 | "minVersionCode": 290,
10 | "price": 0,
11 | "author": {
12 | "name": "Kalkidan Getachew",
13 | "email": "kalgetachew375@gmail.com",
14 | "github": "https://github.com/Chaos-19"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "node": "current"
8 | }
9 | }
10 | ]
11 | ],
12 | "env": {
13 | "test": {
14 | "presets": [
15 | "@babel/env"
16 | ]
17 | }
18 | },
19 | "plugins": [
20 | "html-tag-js/jsx/jsx-to-tag.js",
21 | "html-tag-js/jsx/syntax-parser.js",
22 | [
23 | "@babel/plugin-transform-runtime"
24 | ]
25 | ],
26 | "compact": true
27 | }
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | /*clone dialog*/
2 | .clone-dialog {
3 | padding: 5px 15px;
4 | overflow: hidden;
5 | /* width: 100%; */
6 | }
7 | .clone-dialog p {
8 | margin-bottom: 10px;
9 | }
10 | .clone-dialog p,
11 | .git-cloneBox span {
12 | font-size: 18px;
13 | }
14 |
15 | .clone-dialog p a {
16 | color: #095dff;
17 | }
18 | .clone-cridential #provider {
19 | padding: 1px;
20 | }
21 |
22 | .clone-dialog > input {
23 | width: 100%;
24 | padding: 10px 1px;
25 | border: 1px solid;
26 | margin: auto;
27 | }
28 |
29 | .git-cloneBox {
30 | padding: 10px 15px;
31 | margin: 5px 0;
32 |
33 | display: flex;
34 | justify-content: space-between;
35 | align-items: center;
36 | }
37 |
38 | .depth input {
39 | width: 20%;
40 | }
41 |
--------------------------------------------------------------------------------
/.vscode/run-webpack.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const { spawn } = require('child_process');
3 | const path = require('path');
4 |
5 | const webpack = spawn('npx.cmd', ['webpack', '--mode=development', '--watch'], { cwd: path.resolve(__dirname, '../') });
6 |
7 | webpack.on('error', (webpackError) => {
8 | if (webpackError) {
9 | console.error(webpackError);
10 | process.exit(1);
11 | }
12 | });
13 |
14 | webpack.stdout.on('data', (chunk) => {
15 | const stdout = chunk.toString();
16 | console.log(stdout);
17 | process.send(stdout);
18 | });
19 |
20 | webpack.stdout.on('error', (error) => {
21 | console.log(error);
22 | });
23 |
24 | webpack.stderr.on('data', (chunk) => {
25 | const stderr = chunk.toString();
26 | console.log(stderr);
27 | });
28 |
--------------------------------------------------------------------------------
/.vscode/server.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICfjCCAecCFGoKwe9jqvLXZUsAKK8R9rBoxQBVMA0GCSqGSIb3DQEBCwUAMH4x
3 | CzAJBgNVBAYTAklOMRMwEQYDVQQIDApBaml0IEt1bWFyMQwwCgYDVQQHDANCU1Ax
4 | DjAMBgNVBAoMBUZYREJHMQwwCgYDVQQLDANERVYxDTALBgNVBAMMBEFqaXQxHzAd
5 | BgkqhkiG9w0BCQEWEG1lQGFqaXRrdW1hci5kZXYwHhcNMjIwODIxMDc0NjI1WhcN
6 | MjMwODIxMDc0NjI1WjB+MQswCQYDVQQGEwJJTjETMBEGA1UECAwKQWppdCBLdW1h
7 | cjEMMAoGA1UEBwwDQlNQMQ4wDAYDVQQKDAVGWERCRzEMMAoGA1UECwwDREVWMQ0w
8 | CwYDVQQDDARBaml0MR8wHQYJKoZIhvcNAQkBFhBtZUBhaml0a3VtYXIuZGV2MIGf
9 | MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClD9GyID1GNGCdsq8vshf2h/tMUzxW
10 | mWqOoBi8ZSUrQGoZJ4vxSk5+kPkSvaZzj4zs+LnUMqIXPu1KSIXflVRXzAvh5VIE
11 | 7M0ithYYbGPSyviUWpatdLCvLCOPXZPTXc+66mY7RnCVzlBqGKOR2H4goeGWq0zG
12 | Q+V3pAM7gLUJsQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAHpMjw8nYPtJ20jO2i6M
13 | kJg09Hk91g63HEx4noV8WallM80exNYWJgzNI69UIRdh78QAEknJh43TSF61bVpo
14 | i+mtBHzZLyPv4LHKNN+6eBoMxi58tbWVZXQB4awW6d1AstHY10aSygOmOUKLtGxr
15 | lYt6v4QwWrm3j7oNCDRDwxlj
16 | -----END CERTIFICATE-----
17 |
--------------------------------------------------------------------------------
/.vscode/start-dev.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const { fork, spawn } = require('child_process');
3 | const path = require('path');
4 |
5 | main();
6 |
7 | async function main() {
8 | let serverStarted = false;
9 | console.log('+--------------+');
10 | console.log('| Starting dev |');
11 | console.log('+--------------+');
12 | const webpack = fork(path.resolve(__dirname, './run-webpack.js'));
13 | webpack.on('message', (chunk) => {
14 | if (!serverStarted && chunk.search(/compiled\ssuccessfully/)) {
15 | startServer();
16 | serverStarted = true;
17 | }
18 | });
19 |
20 | webpack.on('error', (err) => {
21 | console.log('WEBPACK ERROR', err);
22 | webpack.kill(1);
23 | process.exit(1);
24 | });
25 | }
26 |
27 | async function startServer() {
28 | const server = fork(path.resolve(__dirname, './start-server.js'));
29 | server.on('error', (err) => {
30 | console.log('SERVER ERROR', err);
31 | server.kill(1);
32 | process.exit(1);
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/.vscode/server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | Proc-Type: 4,ENCRYPTED
3 | DEK-Info: DES-EDE3-CBC,F6E1E7807FC07585
4 |
5 | 1RxeEBdxtQ0+Erd+wmDLuaHy07d81+5uqCBZ1FVkzFOReCwDHFvqT9pyo00soIBJ
6 | ECcUOQyVoV7XyQKZVna+XwQJ8WoiF7R0dVeP7q1E8whFhVD+ybnwvCHSe9Zv1DTo
7 | 8R74rrAqRRKOf0aFEt2DR3sO9vdljOQY0JSTOefFisJs++FSDGSMPzyoUjyGzix+
8 | jOcbA9BjPoossVRNSNta9q7IMZRvYnF+mqbeKrlQ7dDV6BBCICJ15syzp0FFZcry
9 | 7Upmstp+HtFphDr1ABaXlbSzPIzj+lYBro4vV4v/FuyGigwzYhiftTzypz0sVV2u
10 | yOSIGkQkNrg+0iaD35BuLzuZnKvlmjwBeFL0xlN0oh2yUSqveTUwiyGXhJxqjuKe
11 | lK9LEkKFtkj+BB0gwKy0aHNYM7Z3F2FfNGd/FlxxEbZMfORm03W/I3ploJLKk6kO
12 | H69Rkh+5lPsO0q89YBuqptiJH4cgKSup+yWH8LASbz+nuxLEKJTasJZJFEFxO62v
13 | gVHARgwv/V5HYqE4FF860mQs/ZiRVJfTN1HWZ4OpEHjJMuDhWLCyqxHeLMvL8nxd
14 | 5qm9cGoguHWmv7JLe/R238AZhYg6eBybg+WAqOJZ2LdMQjAKFa5+oWezCAk1uLI9
15 | v12C5EBYZFI7znx2C4A+YAN7a3HAf+p6o467c1aL/8CQdb37soSpdnAKApx1uFBp
16 | TBxPrNXBOkND/bdU/w4E1lqMPg5KPFNn3gYe7YTB0fG4YqrBfpA0uswBdNHf4u4E
17 | u2Q99Fw9dIsj/BMkwFxTWM0Mb119VPyZm5nd5L4Y0GZmhND4UyVV0A==
18 | -----END RSA PRIVATE KEY-----
19 |
--------------------------------------------------------------------------------
/.vscode/getNet.js:
--------------------------------------------------------------------------------
1 | const { networkInterfaces } = require('os');
2 |
3 | module.exports = async (mode = 'dev') => {
4 | const { WiFi, Ethernet } = getIp();
5 | const [ip] = WiFi || Ethernet;
6 | const port = '5500';
7 | const src = `https://${ip || '10.0.0'}:${port}`;
8 | console.log('Server starting at: ', src);
9 | return { ip, port };
10 | };
11 |
12 | function getIp() {
13 | const nets = networkInterfaces();
14 | const results = {}; // Or just '{}', an empty object
15 |
16 | Object.keys(nets).forEach((name) => {
17 | nets[name].forEach((net) => {
18 | // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
19 | // 'IPv4' is in Node <= 17, from 18 it's a number 4 or 6
20 | const familyV4Value = typeof net.family === 'string' ? 'IPv4' : 4;
21 | if (net.family === familyV4Value && !net.internal) {
22 | if (!results[name]) {
23 | results[name] = [];
24 | }
25 | results[name].push(net.address);
26 | }
27 | });
28 | });
29 | return results;
30 | }
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "clone-repo",
3 | "version": "1.0.2",
4 | "description": "clone repository to you local machine",
5 | "main": "dist/main.js",
6 | "repository": "https://github.com/deadlyjack/acode-plugin.git",
7 | "author": "Kalkidan Getachew",
8 | "license": "MIT",
9 | "dependencies": {
10 | "@isomorphic-git/cors-proxy": "^2.7.1",
11 | "@isomorphic-git/lightning-fs": "^4.6.0",
12 | "buffer": "^6.0.3",
13 | "css-loader": "^6.8.1",
14 | "html-tag-js": "^1.1.22",
15 | "isomorphic-git": "^1.24.5",
16 | "stream-http": "^3.2.0",
17 | "style-loader": "^3.3.3",
18 | "url": "^0.11.3"
19 | },
20 | "devDependencies": {
21 | "@babel/cli": "^7.18.10",
22 | "@babel/core": "^7.18.13",
23 | "@babel/plugin-transform-runtime": "^7.19.6",
24 | "@babel/preset-env": "^7.18.10",
25 | "babel-loader": "^9.1.0",
26 | "jszip": "^3.10.1",
27 | "live-server": "^1.2.2",
28 | "webpack": "^5.76.0",
29 | "webpack-cli": "^5.0.0"
30 | },
31 | "scripts": {
32 | "build": "webpack",
33 | "build-release": "webpack --mode production",
34 | "start-dev": "node .vscode/start-dev"
35 | },
36 | "browserslist": [
37 | "> 0.25%, not dead"
38 | ],
39 | "resolutions": {
40 | "terser": ">=5.14.2 ",
41 | "glob-parent": ">=5.1.2"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/.vscode/pack-zip.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 | const jszip = require('jszip');
4 |
5 | const iconFile = path.join(__dirname, '../icon.png');
6 | const pluginJSON = path.join(__dirname, '../plugin.json');
7 | const distFolder = path.join(__dirname, '../dist');
8 | let readmeDotMd = path.join(__dirname, '../readme.md');
9 |
10 | if (!fs.existsSync(readmeDotMd)) {
11 | readmeDotMd = path.join(__dirname, '../README.md');
12 | }
13 |
14 | // create zip file of dist folder
15 |
16 | const zip = new jszip();
17 |
18 | zip.file('icon.png', fs.readFileSync(iconFile));
19 | zip.file('plugin.json', fs.readFileSync(pluginJSON));
20 | zip.file('readme.md', fs.readFileSync(readmeDotMd));
21 |
22 | loadFile('', distFolder);
23 |
24 | zip
25 | .generateNodeStream({ type: 'nodebuffer', streamFiles: true })
26 | .pipe(fs.createWriteStream(path.join(__dirname, '../dist.zip')))
27 | .on('finish', () => {
28 | console.log('dist.zip written.');
29 | });
30 |
31 | function loadFile(root, folder) {
32 | const distFiles = fs.readdirSync(folder);
33 | distFiles.forEach((file) => {
34 |
35 | const stat = fs.statSync(path.join(folder, file));
36 |
37 | if (stat.isDirectory()) {
38 | zip.folder(file);
39 | loadFile(path.join(root, file), path.join(folder, file));
40 | return;
41 | }
42 |
43 | if (!/LICENSE.txt/.test(file)) {
44 | zip.file(path.join(root, file), fs.readFileSync(path.join(folder, file)));
45 | }
46 | });
47 | }
--------------------------------------------------------------------------------
/.vscode/start-server.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | const fs = require('fs');
3 | const path = require('path');
4 | const liveServer = require('live-server');
5 | const getNet = require('./getNet');
6 |
7 | const serverCrt = path.resolve(__dirname, 'server.crt');
8 | const serverKey = path.resolve(__dirname, 'server.key');
9 |
10 | main();
11 |
12 | async function main() {
13 | const { ip: host, port } = await getNet('dev');
14 | process.cwd = () => __dirname;
15 | liveServer.start({
16 | open: false,
17 | port,
18 | host,
19 | cors: true,
20 | root: '../',
21 | ignore: 'node_modules,platforms,plugins',
22 | file: 'index.html',
23 | https: {
24 | cert: fs.readFileSync(serverCrt),
25 | key: fs.readFileSync(serverKey),
26 | passphrase: '1234',
27 | },
28 | middleware: [(req, res, next) => {
29 | const url = req.originalUrl;
30 | const www = '../platforms/android/app/src/main/assets/www/';
31 |
32 | if (url === '/cordova.js') {
33 | const file = path.resolve(__dirname, www, 'cordova.js');
34 | sendFile(res, file);
35 | return;
36 | }
37 |
38 | if (url === '/cordova_plugins.js') {
39 | const file = path.resolve(__dirname, www, 'cordova_plugins.js');
40 | sendFile(res, file);
41 | return;
42 | }
43 |
44 | next();
45 | }],
46 | });
47 |
48 | process.send('OK');
49 | }
50 |
51 | function sendFile(res, filePath) {
52 | if (fs.existsSync(filePath)) {
53 | const stat = fs.statSync(filePath);
54 |
55 | res.writeHead(200, {
56 | 'Content-Type': 'application/javascript',
57 | 'Content-Length': stat.size,
58 | });
59 |
60 | const readStream = fs.createReadStream(filePath);
61 | readStream.pipe(res);
62 | return;
63 | }
64 |
65 | res.writeHead(404, { 'Content-Type': 'text/plain' });
66 | res.end(`ERROR cannot get ${filePath}`);
67 | }
68 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const {
2 | exec
3 | } = require('child_process');
4 | const path = require('path');
5 | const webpack = require("webpack");
6 |
7 | module.exports = (env, options) => {
8 | const {
9 | mode = 'development'
10 | } = options;
11 | const rules = [{
12 | test: /\.m?js$/,
13 | use: [
14 | 'html-tag-js/jsx/tag-loader.js',
15 | {
16 | loader: 'babel-loader',
17 | options: {
18 | presets: ['@babel/preset-env'],
19 | },
20 | },
21 | ],
22 | },
23 | ];
24 |
25 | const main = {
26 | mode,
27 | entry: {
28 | main: './src/main.js',
29 | },
30 | output: {
31 | path: path.resolve(__dirname, 'dist'),
32 | filename: '[name].js',
33 | chunkFilename: '[name].js',
34 | },
35 | module: {
36 | rules: [{
37 | test: /\.js$/,
38 | exclude: /node_modules/,
39 | use: {
40 | loader: "babel-loader",
41 | options: {
42 | babelrc: false
43 | }
44 | }
45 | },
46 | {
47 | test: /\.css$/i,
48 | use: ["style-loader",
49 | "css-loader"]
50 | }]
51 | },
52 | resolve: {
53 | fallback: {
54 | url: require.resolve("url/"),
55 | http: require.resolve("stream-http"),
56 | stream: require.resolve("stream-http"),
57 | buffer: require.resolve("buffer")
58 | }
59 | },
60 |
61 | plugins: [
62 | new webpack.ProvidePlugin({
63 | Buffer: ["buffer", "Buffer"]
64 | }),
65 | {
66 | apply: (compiler) => {
67 | compiler.hooks.afterDone.tap('pack-zip', () => {
68 | // run pack-zip.js
69 | exec('node .vscode/pack-zip.js', (err, stdout, stderr) => {
70 | if (err) {
71 | console.error(err);
72 | return;
73 | }
74 | console.log(stdout);
75 | });
76 | });
77 | }
78 | }],
79 | };
80 |
81 | return [main];
82 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CLONE REPOSITORY Plugin for Acode
2 |
3 | ## Overview
4 |
5 | the plugin allow you to clone a Git repository to your local machine . It also supports authentication for cloning private repositories.
6 |
7 |
8 | ## Updates
9 |
10 | **Date:** October 31, 2023
11 |
12 | **Version:** 1.0.3
13 |
14 | **Changes:**
15 |
16 | - Fixed a bug where files couldn't be copied if located directly in Termux.
17 |
18 | **Date:** Jan 27, 2024
19 |
20 | **Version:** 2.0.0
21 |
22 | **Changes:**
23 |
24 | - Greater control: You can now select the branch you want to clone, giving you more flexibility and customization options.
25 |
26 |
27 | Feel free to reach out if you encounter any issues or have suggestions for further improvements.
28 |
29 |
30 | ## Features
31 |
32 | - Clone Git repositories to your local machine
33 | - Support for authentication for cloning private repositories you need username and token to clone private repository
34 | - Select the directory to clone the repository to from the available directories in your workspace
35 |
36 | ## Installation
37 |
38 | To install the plugin, open Acode and go to Extensions > Manage Extensions. Search for "Git Clone" and click the Install button.
39 |
40 | ## Usage
41 |
42 | 1. To clone a Git repository, open Acode and go to File > Clone Git Repository.
43 | 2. Enter the URL of the repository you want to clone and click ok.
44 | 3. If the repository is private, you will be prompted to enter your credentials.
45 | 4. Once the repository has been cloned , a Workspace picker will appear, allowing you to select the desired directory the ,the repository will appear in the Workspace pane you just seleted. You can then open the repository by double-clicking on it.
46 |
47 | ## Contributions and Acknowledgments
48 |
49 | I would like to express my gratitude to the open-source communities behind [Lightning-FS](https://github.com/isomorphic-git/lightning-fs.git) and [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) for their incredible work in developing and maintaining these essential package. This plugin wouldn't be possible without their contributions.
50 |
51 | ## Feedback
52 |
53 | If you have any feedback on the plugin, please feel free to leave a comment below or create an issue on my plugin's GitHub repository.
54 | [CLONE REPOSITORY](https://github.com/Chaos-19/Acode-Clone-repository.git)
55 |
56 |
57 | ## License
58 |
59 | []
60 |
--------------------------------------------------------------------------------
/typings/acode.d.ts:
--------------------------------------------------------------------------------
1 | type Input = string;
2 | type Strings = string[];
3 |
4 | declare var acode: Acode;
5 |
6 | interface Acode {
7 | /**
8 | * Define a module
9 | * @param {string} name
10 | * @param {Object|function} module
11 | */
12 | define(name: string, module: any): void;
13 |
14 | require(module: string): any;
15 |
16 | exec(key: string, val: any): boolean | undefined;
17 |
18 | get exitAppMessage(): string | undefined;
19 |
20 | setLoadingMessage(message: string): void;
21 |
22 | setPluginInit(
23 | id: string,
24 | initFunction: (baseUrl: string, $page: HTMLElement, options?: any) => Promise,
25 | settings?: any
26 | ): void;
27 |
28 | getPluginSettings(id: string): any;
29 |
30 | setPluginUnmount(id: string, unmountFunction: () => void): void;
31 |
32 | /**
33 | * @param {string} id plugin id
34 | * @param {string} baseUrl local plugin url
35 | * @param {HTMLElement} $page
36 | */
37 | initPlugin(id: string, baseUrl: string, $page: HTMLElement, options?: any): Promise;
38 |
39 | unmountPlugin(id: string): void;
40 |
41 | registerFormatter(id: string, extensions: string[], format: () => Promise): void;
42 |
43 | unregisterFormatter(id: string): void;
44 |
45 | format(selectIfNull?: boolean): Promise;
46 |
47 | fsOperation(file: string): any;
48 |
49 | newEditorFile(filename: string, options?: any): void;
50 |
51 | // readonly formatters(): { id: string; name: string; exts: string[] }[];
52 |
53 | /**
54 | * @param {string[]} extensions
55 | * @returns {Array<[id: string, name: string]>} options
56 | */
57 | getFormatterFor(extensions: string[]): [id: string, name: string][];
58 |
59 | alert(title: string, message: string, onhide: ()=>void): void;
60 |
61 | loader(title: string, message: string, cancel: { timeout: number,callback: ()=>void }): void;
62 |
63 | joinUrl(...args: string[]): string;
64 |
65 | addIcon(className: string, src: string): void;
66 |
67 | prompt(
68 | message: string,
69 | defaultValue: string,
70 | type: 'textarea' | 'text' | 'number' | 'tel' | 'search' | 'email' | 'url',
71 | options?: {
72 | match: RegExp,
73 | required: boolean,
74 | placeholder: string,
75 | test: (any)=>boolean
76 | }
77 | ): Promise;
78 |
79 | confirm(title: string, message: string): Promise;
80 |
81 | select(
82 | title: string,
83 | options: [string, string, string, boolean][] | string,
84 | opts?: {
85 | onCancel?: () => void;
86 | onHide?: () => void;
87 | hideOnSelect?: boolean;
88 | textTransform?: boolean;
89 | default?: string;
90 | } | boolean
91 | ): Promise;
92 |
93 | multiPrompt(title: string, inputs: Array, help: string): Promise;
94 |
95 | fileBrowser(mode: 'file' | 'folder' | 'both', info: string, doesOpenLast: boolean): Promise<
96 | | {
97 | name: string;
98 | type: 'file';
99 | url: string;
100 | }
101 | | {
102 | list: {
103 | icon: string;
104 | isDirectory: boolean;
105 | isFile: boolean;
106 | mime: string;
107 | name: string;
108 | type: 'file' | 'folder';
109 | uri: string;
110 | url: string;
111 | }[];
112 | scroll: number;
113 | name: string;
114 | type: 'folder';
115 | url: string;
116 | }
117 | >;
118 |
119 | toInternalUrl(url: string): Promise;
120 | }
121 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import plugin from "../plugin.json";
2 |
3 | import * as git from "isomorphic-git";
4 | import LightningFS from "@isomorphic-git/lightning-fs";
5 | import http from "isomorphic-git/http/web";
6 |
7 | const Url = acode.require("Url");
8 | const loader = acode.require("loader");
9 | const cAlert = acode.require("Alert");
10 | const fsOperation = acode.require("fsOperation");
11 | //const prompt = acode.require("prompt");
12 | const select = acode.require("select");
13 | const multiPrompt = acode.require("multiPrompt");
14 |
15 | let fs = new LightningFS("fs", {
16 | wipe: true,
17 | fileDbName: "rootGitDir"
18 | });
19 | let pfs = fs.promises;
20 | let dir = "";
21 |
22 | class AcodePlugin {
23 | async init() {
24 | this.defaultBarnch = "main";
25 | editorManager.editor.commands.addCommand({
26 | name: "clone-Repo",
27 | discription: "clone repository",
28 | bindKey: {
29 | win: "Ctrl-h"
30 | },
31 | exec: this.cloneRepo.bind(this)
32 | });
33 | let command = {
34 | name: "clone-Repository",
35 | discription: "clone repository",
36 | exec: this.cloneRepo.bind(this)
37 | };
38 | editorManager.editor.commands.addCommand(command);
39 | }
40 |
41 | async run() {
42 | this.createFileStructure("/")
43 | .then(structure => {
44 | this.getDirToSaveTheFile()
45 | .then(path => {
46 | this.createFolderInDevice(structure, path);
47 | })
48 | .catch(error => cAlert(error));
49 | })
50 | .catch(error => cAlert(error));
51 | }
52 |
53 | async createFile(location, fileName, content) {
54 | const file = await fsOperation(location).createFile(fileName, content);
55 | }
56 |
57 | async createDir(location, folderName) {
58 | if (!(await fsOperation(`${location}${folderName}`).exists())) {
59 | await fsOperation(location).createDirectory(folderName);
60 | }
61 | return {
62 | folderName
63 | };
64 | }
65 |
66 | async getDirToSaveTheFile() {
67 | const fileBrowser = acode.require("fileBrowser");
68 | const folder = await fileBrowser("folder", "SELECT DIRECTORY", true);
69 | let selectedPath = folder.url;
70 | const options = [folder.url, folder.name];
71 |
72 | const check = selectedPath.includes("::primary:");
73 | if (!check && !selectedPath.includes("com.termux.documents")) {
74 | selectedPath = selectedPath + `::primary:${options[1]}`;
75 | }
76 | if (
77 | selectedPath.includes("com.termux.documents") &&
78 | !selectedPath.includes("::/data/data/com.termux/files/home/")
79 | ) {
80 | selectedPath =
81 | selectedPath + `::${Url.pathname(folder.url).slice(1)}`;
82 | }
83 |
84 | return selectedPath;
85 | }
86 |
87 | async cloneRepo() {
88 | fs = new LightningFS("fs", {
89 | wipe: true,
90 | fileDbName: "rootGitDir"
91 | });
92 | this.getURL()
93 | .then(url => {
94 | this.clone(url)
95 | .then(dir => {
96 | this.run();
97 | window.toast("succesfully cloned", 400);
98 | })
99 | .catch(error => {
100 | cAlert(error);
101 | });
102 | fs = new LightningFS("fs", {
103 | wipe: true,
104 | fileDbName: "rootGitDir"
105 | });
106 | })
107 | .catch(error => cAlert(error));
108 | }
109 |
110 | getDirName(url) {
111 | const pathArry = new URL(url).pathname.split("/");
112 | return pathArry.slice(-1).toString().replace(".git", "");
113 | }
114 |
115 | async clone(remotURL) {
116 | try {
117 | dir = "/" + this.getDirName(remotURL);
118 | await pfs.mkdir(dir);
119 |
120 | if (remotURL) {
121 | const cloneload = loader.create(
122 | "CLONE REPOSITORY",
123 | "Cloning Remote",
124 | {
125 | timeout: 5000,
126 | callback: () => window.toast("Loading cancelled", 4000)
127 | }
128 | );
129 | const corsProxy = "https://cors.isomorphic-git.org";
130 | return new Promise((resolve, reject) => {
131 | loader.show();
132 | git.clone({
133 | fs,
134 | http,
135 | dir,
136 | url: remotURL,
137 | corsProxy,
138 | onProgress: evt => {
139 | cloneload.setMessage(evt.phase);
140 | },
141 | onMessage: msg => {
142 | cloneload.setMessage(msg);
143 | },
144 | onAuth: url => {
145 | return this.fillCredentials(url);
146 | },
147 | onAuthFailure: ({ url, auth }) => {
148 | return this.rejected({
149 | url,
150 | auth
151 | });
152 | }
153 | })
154 | .then(() => {
155 | window.toast("Successfully Cloned", 4000);
156 | cloneload.destroy();
157 | resolve(dir);
158 | })
159 | .catch(error => {
160 | cloneload.destroy();
161 | reject(error);
162 | });
163 | });
164 | }
165 | } catch (e) {
166 | loader.destroy();
167 | cAlert(e);
168 | }
169 | }
170 |
171 | async fillCredentials(url) {
172 | const gitCridentailProm = await multiPrompt(
173 | "Enter You Cridential",
174 | [
175 | {
176 | type: "text",
177 | id: "username",
178 | required: true,
179 | placeholder: "Username"
180 | },
181 | {
182 | type: "password",
183 | id: "password",
184 | required: true,
185 | placeholder: "Token"
186 | }
187 | ],
188 | url
189 | );
190 |
191 | let username = gitCridentailProm["username"];
192 |
193 | let password = gitCridentailProm["password"];
194 |
195 | return {
196 | username,
197 | password
198 | };
199 | }
200 | async rejected({ url, auth }) {
201 | cAlert("Authentication rejected");
202 | return;
203 | }
204 |
205 | createFolderInDevice(structure, location) {
206 | for (let key in structure) {
207 | if (typeof structure[key] === "object") {
208 | // Create a new folder
209 | let innerFiles = Object.keys(structure[key]).filter(
210 | k => typeof structure[key][k] !== "object"
211 | );
212 | let newFolder = this.getFolder(key, location, innerFiles);
213 | newFolder.then(name => {
214 | // Recursively create nested folders
215 | this.createFolderInDevice(
216 | structure[key],
217 | location + "/" + name
218 | );
219 | });
220 | }
221 | }
222 | }
223 | async getFolder(name, location, innerFiles) {
224 | const folder = this.createDir(location, name);
225 | return folder
226 | .then(async result => {
227 | const structure = await this.createFileStructure("/");
228 |
229 | const filePromises = innerFiles.map(async file => {
230 | await structure;
231 |
232 | const filepath = this.getPathToRoot(structure, file);
233 |
234 | return this.getFileContent(this.extractPathAndDir(filepath))
235 | .then(fileContent =>
236 | this.createFile(
237 | location + "/" + name + "/",
238 | file,
239 | fileContent
240 | )
241 | )
242 | .catch(error => cAlert(error));
243 | });
244 |
245 | await Promise.all(filePromises);
246 | return name;
247 | })
248 | .catch(error => cAlert(error));
249 | }
250 |
251 | extractPathAndDir(path) {
252 | return {
253 | dir: "/" + path.slice(0, path.indexOf("/")),
254 | filepath: path.slice(path.indexOf("/") + 1),
255 | fileName: path.slice(path.lastIndexOf("/") + 1)
256 | };
257 | }
258 | getPathToRoot(structure, fileName, currentPath = []) {
259 | for (const key in structure) {
260 | if (typeof structure[key] === "object") {
261 | const newPath = currentPath.concat(key);
262 | const result = this.getPathToRoot(
263 | structure[key],
264 | fileName,
265 | newPath
266 | );
267 | if (result) {
268 | return result;
269 | }
270 | } else if (structure[key] === "file" && key === fileName) {
271 | currentPath.push(fileName);
272 | return currentPath.join("/");
273 | }
274 | }
275 | return null;
276 | }
277 |
278 | getFileContent({ dir, filepath, fileName }) {
279 | return git
280 | .resolveRef({
281 | fs,
282 | dir,
283 | ref: this.defaultBarnch
284 | })
285 | .then(commitOid => {
286 | return git
287 | .readBlob({
288 | fs,
289 | dir,
290 | oid: commitOid,
291 | filepath // You can replace with 'filepath' if needed
292 | })
293 | .then(fileContent => {
294 | return new TextDecoder("utf-8").decode(
295 | fileContent.blob
296 | );
297 | })
298 | .catch(error => {
299 | throw error; // Handle readBlob error
300 | });
301 | })
302 | .catch(error => {
303 | throw error; // Handle resolveRef error
304 | });
305 | }
306 |
307 | async createFileStructure(dir) {
308 | const { entries } = Object;
309 |
310 | let result = {};
311 | const files = await pfs.readdir(dir);
312 | for (let file of files) {
313 | const filePath = `${dir}/${file}`;
314 | const stats = await pfs.stat(filePath);
315 |
316 | if (file === ".git") {
317 | continue; // Skip the ".git" directory
318 | }
319 |
320 | if (stats.isDirectory()) {
321 | result[file] = await this.createFileStructure(filePath);
322 | } else {
323 | result[file] = "file";
324 | }
325 | }
326 | return Object.fromEntries(entries(result).sort());
327 | }
328 |
329 | async getURL() {
330 | try {
331 | const result = await multiPrompt("enter repository meta", [
332 | {
333 | type: "url",
334 | id: "url",
335 | required: true,
336 | placeholder: "URL (start with https://)",
337 | test: value =>
338 | value.startsWith("https://") && value.endsWith(".git")
339 | },
340 | {
341 | type: "text",
342 | id: "branch",
343 | required: true,
344 | placeholder: "ref",
345 | value: "main"
346 | }
347 | ]);
348 |
349 | if (!result["url"]) throw "please provide url";
350 |
351 | if (result["branch"]) {
352 | this.defaultBarnch = result["branch"];
353 | alert(this.defaultBarnch);
354 | }
355 |
356 | return result["url"];
357 | } catch (e) {
358 | throw "please provide url";
359 | }
360 | }
361 |
362 | async destroy() {
363 | editorManager.editor.commands.removeCommand("clone-Repo");
364 | let command = {
365 | name: "clone-Repository",
366 | discription: "clone repository",
367 | exec: this.cloneRepo.bind(this)
368 | };
369 | editorManager.editor.commands.removeCommand(command);
370 | }
371 | }
372 |
373 | if (window.acode) {
374 | const acodePlugin = new AcodePlugin();
375 | acode.setPluginInit(
376 | plugin.id,
377 | async (baseUrl, $page, { cacheFileUrl, cacheFile }) => {
378 | if (!baseUrl.endsWith("/")) {
379 | baseUrl += "/";
380 | }
381 | acodePlugin.baseUrl = baseUrl;
382 | await acodePlugin.init($page, cacheFile, cacheFileUrl);
383 | }
384 | );
385 | acode.setPluginUnmount(plugin.id, () => {
386 | acodePlugin.destroy();
387 | });
388 | }
389 |
--------------------------------------------------------------------------------