├── 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 | --------------------------------------------------------------------------------