├── .babelrc ├── .github ├── ISSUE_TEMPLATE │ ├── 0_bug_report.yml │ ├── 1_feature_request.yml │ └── config.yml └── workflows │ ├── ci.yml │ └── close-inactive-issues.yml ├── .gitignore ├── .hintrc ├── .vscode ├── launch.json ├── plugins.json ├── settings.json └── typings │ └── cordova │ └── cordova.d.ts ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── _typos.toml ├── biome.json ├── build-extras.gradle ├── config.xml ├── fastlane └── metadata │ └── android │ └── en-US │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ └── 8.png │ ├── short_description.txt │ └── title.txt ├── hooks ├── README.md ├── modify-java-files.js ├── move-files.js └── post-process.js ├── jsconfig.json ├── license.txt ├── package-lock.json ├── package.json ├── postcss.config.js ├── readme.md ├── res ├── android │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ └── themes.xml │ └── xml │ │ └── network_security_config.xml ├── logo.png └── logo_1.png ├── src ├── ace │ ├── colorView.js │ ├── commands.js │ ├── modelist.js │ ├── supportedModes.js │ └── touchHandler.js ├── components │ ├── WebComponents │ │ ├── index.js │ │ └── wcPage.js │ ├── audioPlayer │ │ ├── index.js │ │ └── style.scss │ ├── checkbox │ │ ├── index.js │ │ └── styles.scss │ ├── collapsableList.js │ ├── contextmenu │ │ ├── index.js │ │ └── style.scss │ ├── inputhints │ │ ├── index.js │ │ └── style.scss │ ├── page.js │ ├── palette │ │ ├── index.js │ │ └── style.scss │ ├── quickTools │ │ ├── footer.js │ │ ├── index.js │ │ ├── items.js │ │ └── style.scss │ ├── scrollbar │ │ ├── index.js │ │ └── style.scss │ ├── searchbar │ │ ├── index.js │ │ └── style.scss │ ├── settingsPage.js │ ├── sideButton │ │ ├── index.js │ │ └── style.scss │ ├── sidebar │ │ ├── index.js │ │ └── style.scss │ ├── tabView.js │ ├── tile │ │ ├── index.js │ │ └── style.scss │ ├── toast │ │ ├── index.js │ │ └── style.scss │ └── tutorial.js ├── dialogs │ ├── alert.js │ ├── box.js │ ├── color.js │ ├── confirm.js │ ├── loader.js │ ├── multiPrompt.js │ ├── prompt.js │ ├── rateBox.js │ ├── select.js │ └── style.scss ├── fileSystem │ ├── externalFs.js │ ├── ftp.js │ ├── index.js │ ├── internalFs.js │ └── sftp.js ├── handlers │ ├── editorFileTab.js │ ├── intent.js │ ├── keyboard.js │ ├── purchase.js │ ├── quickTools.js │ ├── quickToolsInit.js │ └── windowResize.js ├── index.d.ts ├── lang │ ├── ar-ye.json │ ├── be-by.json │ ├── bn-bd.json │ ├── cs-cz.json │ ├── de-de.json │ ├── en-us.json │ ├── es-sv.json │ ├── fr-fr.json │ ├── he-il.json │ ├── hi-in.json │ ├── hu-hu.json │ ├── id-id.json │ ├── ir-fa.json │ ├── it-it.json │ ├── ja-jp.json │ ├── ko-kr.json │ ├── ml-in.json │ ├── mm-unicode.json │ ├── mm-zawgyi.json │ ├── pl-pl.json │ ├── pt-br.json │ ├── pu-in.json │ ├── ru-ru.json │ ├── tl-ph.json │ ├── tr-tr.json │ ├── uk-ua.json │ ├── uz-uz.json │ ├── vi-vn.json │ ├── zh-cn.json │ ├── zh-hant.json │ └── zh-tw.json ├── lib │ ├── acode.js │ ├── actionStack.js │ ├── applySettings.js │ ├── auth.js │ ├── checkFiles.js │ ├── checkPluginsUpdate.js │ ├── commands.js │ ├── console.js │ ├── constants.js │ ├── editorFile.js │ ├── editorManager.js │ ├── fileList.js │ ├── fileTypeHandler.js │ ├── fonts.js │ ├── installPlugin.js │ ├── installState.js │ ├── keyBindings.js │ ├── lang.js │ ├── loadPlugin.js │ ├── loadPlugins.js │ ├── logger.js │ ├── main.js │ ├── notificationManager.js │ ├── openFile.js │ ├── openFolder.js │ ├── polyfill.js │ ├── projects.js │ ├── recents.js │ ├── remoteStorage.js │ ├── removeAds.js │ ├── restoreFiles.js │ ├── restoreTheme.js │ ├── run.js │ ├── saveFile.js │ ├── saveState.js │ ├── selectionMenu.js │ ├── settings.js │ ├── showFileInfo.js │ ├── startAd.js │ └── systemConfiguration.js ├── pages │ ├── about │ │ ├── about.hbs │ │ ├── about.js │ │ ├── about.scss │ │ └── index.js │ ├── changelog │ │ ├── changelog.js │ │ ├── index.js │ │ └── style.scss │ ├── customTheme │ │ ├── customTheme.js │ │ ├── customTheme.scss │ │ └── index.js │ ├── donate │ │ ├── donate.hbs │ │ ├── donate.js │ │ ├── donate.scss │ │ ├── index.js │ │ └── product.hbs │ ├── fileBrowser │ │ ├── add-menu-home.hbs │ │ ├── add-menu.hbs │ │ ├── fileBrowser.hbs │ │ ├── fileBrowser.js │ │ ├── fileBrowser.scss │ │ ├── index.js │ │ ├── list.hbs │ │ └── util.js │ ├── plugin │ │ ├── index.js │ │ ├── plugin.js │ │ ├── plugin.scss │ │ └── plugin.view.js │ ├── plugins │ │ ├── index.js │ │ ├── item.js │ │ ├── plugins.js │ │ └── plugins.scss │ ├── problems │ │ ├── index.js │ │ ├── problems.js │ │ └── style.scss │ ├── quickTools │ │ ├── index.js │ │ ├── quickTools.js │ │ └── style.scss │ └── themeSetting │ │ ├── index.js │ │ ├── themeSetting.js │ │ └── themeSetting.scss ├── palettes │ ├── changeEncoding │ │ └── index.js │ ├── changeMode │ │ └── index.js │ ├── changeTheme │ │ ├── index.js │ │ └── style.scss │ ├── commandPalette │ │ └── index.js │ └── findFile │ │ └── index.js ├── plugins │ ├── Executor │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── src │ │ │ └── android │ │ │ │ └── Executor.java │ │ └── www │ │ │ └── Executor.js │ ├── browser │ │ ├── android │ │ │ └── com │ │ │ │ └── foxdebug │ │ │ │ └── browser │ │ │ │ ├── Browser.java │ │ │ │ ├── BrowserActivity.java │ │ │ │ ├── Emulator.java │ │ │ │ ├── Menu.java │ │ │ │ └── Plugin.java │ │ ├── index.js │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── res │ │ │ └── android │ │ │ │ ├── menu_enter.xml │ │ │ │ ├── menu_exit.xml │ │ │ │ └── styles.xml │ │ └── utils │ │ │ └── updatePackage.js │ ├── ftp │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── src │ │ │ └── android │ │ │ │ └── com │ │ │ │ └── foxdebug │ │ │ │ └── ftp │ │ │ │ └── Ftp.java │ │ └── www │ │ │ └── ftp.js │ ├── iap │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── src │ │ │ └── com │ │ │ │ └── foxdebug │ │ │ │ └── iap │ │ │ │ └── Iap.java │ │ └── www │ │ │ └── plugin.js │ ├── sdcard │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── readme.md │ │ ├── src │ │ │ └── android │ │ │ │ └── SDcard.java │ │ └── www │ │ │ └── plugin.js │ ├── server │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── src │ │ │ └── android │ │ │ │ └── com │ │ │ │ └── foxdebug │ │ │ │ └── server │ │ │ │ ├── NanoHTTPDWebserver.java │ │ │ │ └── Server.java │ │ └── www │ │ │ └── server.js │ ├── sftp │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── src │ │ │ └── com │ │ │ │ └── foxdebug │ │ │ │ └── sftp │ │ │ │ └── Sftp.java │ │ └── www │ │ │ └── sftp.js │ └── system │ │ ├── android │ │ └── com │ │ │ └── foxdebug │ │ │ └── system │ │ │ ├── System.java │ │ │ └── Ui.java │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── readme.md │ │ ├── res │ │ └── android │ │ │ ├── file_provider.xml │ │ │ └── icon.ttf │ │ ├── system.d.ts │ │ ├── utils │ │ ├── changeProvider.js │ │ ├── fixProvider.js │ │ └── resetProvider.js │ │ └── www │ │ └── plugin.js ├── settings │ ├── appSettings.js │ ├── backupRestore.js │ ├── editorSettings.js │ ├── filesSettings.js │ ├── formatterSettings.js │ ├── helpSettings.js │ ├── mainSettings.js │ ├── previewSettings.js │ ├── scrollSettings.js │ └── searchSettings.js ├── sidebarApps │ ├── extensions │ │ ├── index.js │ │ └── style.scss │ ├── files │ │ ├── index.js │ │ └── style.scss │ ├── index.js │ ├── notification │ │ ├── index.js │ │ └── style.scss │ ├── searchInFiles │ │ ├── index.js │ │ ├── searchResultMode.js │ │ ├── styles.scss │ │ └── worker.js │ └── sidebarApp.js ├── styles │ ├── console.module.scss │ ├── fileInfo.scss │ ├── fonts.scss │ ├── keyframes.scss │ ├── list.scss │ ├── main.scss │ ├── markdown.scss │ ├── mixins.scss │ ├── overrideAceStyle.scss │ └── page.scss ├── theme │ ├── builder.js │ ├── list.js │ └── preInstalled.js ├── utils │ ├── Path.js │ ├── Uri.js │ ├── Url.js │ ├── color │ │ ├── hex.js │ │ ├── hsl.js │ │ ├── index.js │ │ ├── regex.js │ │ └── rgb.js │ ├── encodings.js │ ├── helpers.js │ ├── keyboardEvent.js │ ├── polyfill.js │ └── taskManager.js └── views │ ├── console.hbs │ ├── file-info.hbs │ ├── file-menu.hbs │ ├── markdown.hbs │ ├── menu.hbs │ └── rating.hbs ├── utils ├── config.js ├── extra-icons │ ├── cart.svg │ ├── scale.svg │ ├── tag.svg │ └── verified.svg ├── lang.js ├── loadStyles.js ├── scripts │ ├── build.sh │ ├── clean.sh │ ├── plugin.sh │ ├── setup.sh │ └── start.sh ├── setup.js └── updateAce.js ├── webpack.config.js └── www ├── favicon.ico ├── index.html ├── js ├── ace │ ├── ace.js │ ├── ext-beautify.js │ ├── ext-code_lens.js │ ├── ext-command_bar.js │ ├── ext-elastic_tabstops_lite.js │ ├── ext-emmet.js │ ├── ext-error_marker.js │ ├── ext-hardwrap.js │ ├── ext-inline_autocomplete.js │ ├── ext-keybinding_menu.js │ ├── ext-language_tools.js │ ├── ext-linking.js │ ├── ext-modelist.js │ ├── ext-options.js │ ├── ext-prompt.js │ ├── ext-rtl.js │ ├── ext-searchbox.js │ ├── ext-settings_menu.js │ ├── ext-simple_tokenizer.js │ ├── ext-spellcheck.js │ ├── ext-split.js │ ├── ext-static_highlight.js │ ├── ext-statusbar.js │ ├── ext-textarea.js │ ├── ext-themelist.js │ ├── ext-whitespace.js │ ├── keybinding-emacs.js │ ├── keybinding-sublime.js │ ├── keybinding-vim.js │ ├── keybinding-vscode.js │ ├── mode-abap.js │ ├── mode-abc.js │ ├── mode-actionscript.js │ ├── mode-ada.js │ ├── mode-alda.js │ ├── mode-apache_conf.js │ ├── mode-apex.js │ ├── mode-applescript.js │ ├── mode-aql.js │ ├── mode-asciidoc.js │ ├── mode-asl.js │ ├── mode-assembly_arm32.js │ ├── mode-assembly_x86.js │ ├── mode-astro.js │ ├── mode-autohotkey.js │ ├── mode-basic.js │ ├── mode-batchfile.js │ ├── mode-bibtex.js │ ├── mode-c9search.js │ ├── mode-c_cpp.js │ ├── mode-cirru.js │ ├── mode-clojure.js │ ├── mode-cobol.js │ ├── mode-coffee.js │ ├── mode-coldfusion.js │ ├── mode-crystal.js │ ├── mode-csharp.js │ ├── mode-csound_document.js │ ├── mode-csound_orchestra.js │ ├── mode-csound_score.js │ ├── mode-csp.js │ ├── mode-css.js │ ├── mode-csv.js │ ├── mode-curly.js │ ├── mode-cuttlefish.js │ ├── mode-d.js │ ├── mode-dart.js │ ├── mode-diff.js │ ├── mode-django.js │ ├── mode-dockerfile.js │ ├── mode-dot.js │ ├── mode-drools.js │ ├── mode-edifact.js │ ├── mode-eiffel.js │ ├── mode-ejs.js │ ├── mode-elixir.js │ ├── mode-elm.js │ ├── mode-erlang.js │ ├── mode-flix.js │ ├── mode-forth.js │ ├── mode-fortran.js │ ├── mode-fsharp.js │ ├── mode-fsl.js │ ├── mode-ftl.js │ ├── mode-gcode.js │ ├── mode-gherkin.js │ ├── mode-gitignore.js │ ├── mode-glsl.js │ ├── mode-gobstones.js │ ├── mode-golang.js │ ├── mode-graphqlschema.js │ ├── mode-groovy.js │ ├── mode-haml.js │ ├── mode-handlebars.js │ ├── mode-haskell.js │ ├── mode-haskell_cabal.js │ ├── mode-haxe.js │ ├── mode-hjson.js │ ├── mode-html.js │ ├── mode-html_elixir.js │ ├── mode-html_ruby.js │ ├── mode-ini.js │ ├── mode-io.js │ ├── mode-ion.js │ ├── mode-jack.js │ ├── mode-jade.js │ ├── mode-java.js │ ├── mode-javascript.js │ ├── mode-jexl.js │ ├── mode-json.js │ ├── mode-json5.js │ ├── mode-jsoniq.js │ ├── mode-jsp.js │ ├── mode-jssm.js │ ├── mode-jsx.js │ ├── mode-julia.js │ ├── mode-kotlin.js │ ├── mode-latex.js │ ├── mode-latte.js │ ├── mode-less.js │ ├── mode-liquid.js │ ├── mode-lisp.js │ ├── mode-livescript.js │ ├── mode-logiql.js │ ├── mode-logtalk.js │ ├── mode-lsl.js │ ├── mode-lua.js │ ├── mode-luapage.js │ ├── mode-lucene.js │ ├── mode-makefile.js │ ├── mode-markdown.js │ ├── mode-mask.js │ ├── mode-matlab.js │ ├── mode-maze.js │ ├── mode-mediawiki.js │ ├── mode-mel.js │ ├── mode-mips.js │ ├── mode-mixal.js │ ├── mode-mushcode.js │ ├── mode-mysql.js │ ├── mode-nasal.js │ ├── mode-nginx.js │ ├── mode-nim.js │ ├── mode-nix.js │ ├── mode-nsis.js │ ├── mode-nunjucks.js │ ├── mode-objectivec.js │ ├── mode-ocaml.js │ ├── mode-odin.js │ ├── mode-partiql.js │ ├── mode-pascal.js │ ├── mode-perl.js │ ├── mode-pgsql.js │ ├── mode-php.js │ ├── mode-php_laravel_blade.js │ ├── mode-pig.js │ ├── mode-plain_text.js │ ├── mode-plsql.js │ ├── mode-powershell.js │ ├── mode-praat.js │ ├── mode-prisma.js │ ├── mode-prolog.js │ ├── mode-properties.js │ ├── mode-protobuf.js │ ├── mode-prql.js │ ├── mode-puppet.js │ ├── mode-python.js │ ├── mode-qml.js │ ├── mode-r.js │ ├── mode-raku.js │ ├── mode-razor.js │ ├── mode-rdoc.js │ ├── mode-red.js │ ├── mode-redshift.js │ ├── mode-rhtml.js │ ├── mode-robot.js │ ├── mode-rst.js │ ├── mode-ruby.js │ ├── mode-rust.js │ ├── mode-sac.js │ ├── mode-sass.js │ ├── mode-scad.js │ ├── mode-scala.js │ ├── mode-scheme.js │ ├── mode-scrypt.js │ ├── mode-scss.js │ ├── mode-sh.js │ ├── mode-sjs.js │ ├── mode-slim.js │ ├── mode-smarty.js │ ├── mode-smithy.js │ ├── mode-snippets.js │ ├── mode-soy_template.js │ ├── mode-space.js │ ├── mode-sparql.js │ ├── mode-sql.js │ ├── mode-sqlserver.js │ ├── mode-stylus.js │ ├── mode-svg.js │ ├── mode-swift.js │ ├── mode-tcl.js │ ├── mode-terraform.js │ ├── mode-tex.js │ ├── mode-text.js │ ├── mode-textile.js │ ├── mode-toml.js │ ├── mode-tsv.js │ ├── mode-tsx.js │ ├── mode-turtle.js │ ├── mode-twig.js │ ├── mode-typescript.js │ ├── mode-vala.js │ ├── mode-vbscript.js │ ├── mode-velocity.js │ ├── mode-verilog.js │ ├── mode-vhdl.js │ ├── mode-visualforce.js │ ├── mode-vue.js │ ├── mode-wollok.js │ ├── mode-xml.js │ ├── mode-xquery.js │ ├── mode-yaml.js │ ├── mode-zeek.js │ ├── mode-zig.js │ ├── theme-ambiance.js │ ├── theme-chaos.js │ ├── theme-chrome.js │ ├── theme-cloud9_day.js │ ├── theme-cloud9_night.js │ ├── theme-cloud9_night_low_color.js │ ├── theme-cloud_editor.js │ ├── theme-cloud_editor_dark.js │ ├── theme-clouds.js │ ├── theme-clouds_midnight.js │ ├── theme-cobalt.js │ ├── theme-crimson_editor.js │ ├── theme-dawn.js │ ├── theme-dracula.js │ ├── theme-dreamweaver.js │ ├── theme-eclipse.js │ ├── theme-github.js │ ├── theme-github_dark.js │ ├── theme-github_light_default.js │ ├── theme-gob.js │ ├── theme-gruvbox.js │ ├── theme-gruvbox_dark_hard.js │ ├── theme-gruvbox_light_hard.js │ ├── theme-idle_fingers.js │ ├── theme-iplastic.js │ ├── theme-katzenmilch.js │ ├── theme-kr_theme.js │ ├── theme-kuroir.js │ ├── theme-merbivore.js │ ├── theme-merbivore_soft.js │ ├── theme-mono_industrial.js │ ├── theme-monokai.js │ ├── theme-nord_dark.js │ ├── theme-one_dark.js │ ├── theme-pastel_on_dark.js │ ├── theme-solarized_dark.js │ ├── theme-solarized_light.js │ ├── theme-sqlserver.js │ ├── theme-terminal.js │ ├── theme-textmate.js │ ├── theme-tomorrow.js │ ├── theme-tomorrow_night.js │ ├── theme-tomorrow_night_blue.js │ ├── theme-tomorrow_night_bright.js │ ├── theme-tomorrow_night_eighties.js │ ├── theme-twilight.js │ ├── theme-vibrant_ink.js │ └── theme-xcode.js └── emmet-core.js └── res ├── file-icons ├── file-icons.ttf └── style.css ├── fonts ├── FiraCode.ttf └── RobotoMono.ttf ├── icons ├── icons.ttf ├── li-icons.ttf └── style.css ├── logo ├── favicon.ico ├── github.svg ├── logo circle-min.png ├── logo.png └── logo.svg ├── puzzle.png └── tail-spin.svg /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3.22" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "html-tag-js/jsx/jsx-to-tag.js", 13 | "html-tag-js/jsx/syntax-parser.js", 14 | "@babel/plugin-transform-runtime", 15 | "@babel/plugin-transform-block-scoping" 16 | ], 17 | "compact": false, 18 | "sourceMaps": "inline" 19 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/0_bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: | 3 | Use this template for bug reports. 4 | labels: [] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Check for existing issues 9 | description: Check the backlog of issues to reduce duplicates; if an issue already exists, place a 👍 on it. 10 | options: 11 | - label: Completed 12 | required: true 13 | - type: textarea 14 | attributes: 15 | label: Describe the bug / provide steps to reproduce it 16 | description: A clear and concise description of what the bug is. If possible, then include a clip 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: environment 21 | attributes: 22 | label: Environment 23 | description: Provide your app version, Android version, webview version, etc (Search "Copy Device Info" in command palette to get all these info and share it) 24 | validations: 25 | required: true 26 | - type: textarea 27 | attributes: 28 | label: If applicable, add mockups / screenshots regarding your vision 29 | description: Drag issues into the text input below 30 | validations: 31 | required: false 32 | - type: textarea 33 | attributes: 34 | label: If applicable, attach your Acode.log file to this issue. 35 | description: | 36 | Search for `Open Log File` in Acode command palette, it will open Acode.log file then share it 37 | or Check this location for log file: `/storage/emulated/0/Android/com.foxdebug.acode{in case of free version there will be bit more}/files/Acode.log` 38 | value: | 39 |
Acode.log
40 |         
41 | 
42 |         
43 | validations: 44 | required: false 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1_feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: "Suggest a new feature for Acode" 3 | labels: ["enhancement"] 4 | body: 5 | - type: checkboxes 6 | attributes: 7 | label: Check for existing issues 8 | description: Check the backlog of issues to reduce duplicates; if an issue already exists, place a `+1` (👍) on it. 9 | options: 10 | - label: Completed 11 | required: true 12 | - type: textarea 13 | attributes: 14 | label: Describe the feature 15 | description: A clear and concise description of what you want to happen. 16 | validations: 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: | 21 | If applicable, add mockups / screenshots to help present your vision of the feature 22 | description: Drag images into the text input below 23 | validations: 24 | required: false 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask Questions 4 | url: https://github.com/Acode-Foundation/Acode/discussions 5 | about: Ask any questions regarding Acode and its ecosystem 6 | - name: Telegram Group 7 | url: https://t.me/foxdebug_acode 8 | about: Friendly Acode Community for helps regarding Acode, development and more 9 | - name: Discord Server 10 | url: https://discord.gg/vVxVWYUAWD 11 | about: Acode discord server 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | spell-check: 9 | timeout-minutes: 5 10 | name: Check spelling 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout Actions Repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Check spelling 18 | uses: crate-ci/typos@master 19 | with: 20 | config: ./_typos.toml 21 | 22 | quality: 23 | timeout-minutes: 5 24 | name: Linting and formatting 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | 31 | - name: Setup Biome 32 | uses: biomejs/setup-biome@v2 33 | 34 | - name: Run Biome 35 | run: biome ci . 36 | -------------------------------------------------------------------------------- /.github/workflows/close-inactive-issues.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v9 14 | with: 15 | days-before-issue-stale: 60 16 | days-before-issue-close: 14 17 | stale-issue-label: "stale" 18 | stale-issue-message: > 19 | Hi there! 👋 20 | 21 | We're working to clean up our issue tracker by closing older issues that might not be relevant anymore. If you are able to reproduce this issue in the latest version of Acode, please let us know by commenting on this issue(i.e Bump!), and we will keep it open. If you can't reproduce it, feel free to close the issue yourself. Otherwise, we'll close it in 14 days. 22 | 23 | Thanks for your help! 24 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 25 | days-before-pr-stale: -1 26 | days-before-pr-close: -1 27 | exempt-issue-labels: "new plugin idea, todo" 28 | operations-per-run: 100 29 | repo-token: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build.json 3 | /www/js/build 4 | /www/css/build 5 | /plugins 6 | /platforms 7 | /keystore.jks 8 | /platforms/android/debug-signing.properties 9 | /platforms/android/release-signing.properties 10 | **/*/.DS_Store 11 | .DS_Store 12 | pnpm-lock.yaml 13 | .zed 14 | .idea 15 | ace-builds 16 | -------------------------------------------------------------------------------- /.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "development" 4 | ], 5 | "hints": { 6 | "compat-api/css": "off", 7 | "css-prefix-order": "off", 8 | "meta-viewport": "off", 9 | "detect-css-reflows/composite": "off", 10 | "detect-css-reflows/paint": "off" 11 | } 12 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Run Android", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\utils\\run-android.js", 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/plugins.json: -------------------------------------------------------------------------------- 1 | {"plugins":["cordova-clipboard","cordova-plugin-buildinfo","cordova-plugin-device","cordova-plugin-file","cordova-plugin-sftp","cordova-plugin-server","cordova-plugin-ftp","cordova-plugin-sdcard","cordova-plugin-browser","cordova-plugin-iap","cordova-plugin-system","cordova-plugin-advanced-http"]} -------------------------------------------------------------------------------- /_typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | check-filename = true 3 | 4 | [files] 5 | extend-exclude = [ 6 | "node_modules", 7 | "www", 8 | "*.yaml", 9 | ".vscode", 10 | "fastlane", 11 | "hooks", 12 | "*.gradle", 13 | "plugins", 14 | "platforms", 15 | "src/lang/ar-ye.json", 16 | "src/lang/be-by.json", 17 | "src/lang/bn-bd.json", 18 | "src/lang/cs-cz.json", 19 | "src/lang/de-de.json", 20 | "src/lang/es-sv.json", 21 | "src/lang/fr-fr.json", 22 | "src/lang/hi-in.json", 23 | "src/lang/hu-hu.json", 24 | "src/lang/id-id.json", 25 | "src/lang/ir-fa.json", 26 | "src/lang/it-it.json", 27 | "src/lang/ja-jp.json", 28 | "src/lang/ko-kr.json", 29 | "src/lang/ml-in.json", 30 | "src/lang/mm-unicode.json", 31 | "src/lang/mm-zawgyi.json", 32 | "src/lang/pl-pl.json", 33 | "src/lang/pt-br.json", 34 | "src/lang/pu-in.json", 35 | "src/lang/ru-ru.json", 36 | "src/lang/tl-ph.json", 37 | "src/lang/tr-tr.json", 38 | "src/lang/uk-ua.json", 39 | "src/lang/uz-uz.json", 40 | "src/lang/vi-vn.json", 41 | "src/lang/zh-cn.json", 42 | "src/lang/zh-hant.json", 43 | "src/lang/zh-tw.json", 44 | ] 45 | 46 | [default.extend-words] 47 | # temporary thing to staisfy the linter 48 | strech = "strech" 49 | contaienr = "contaienr" 50 | formate = "formate" 51 | collapsable = "collapsable" 52 | styl = "styl" 53 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", 3 | "formatter": { 4 | "enabled": true, 5 | "indentStyle": "tab" 6 | }, 7 | "organizeImports": { 8 | "enabled": true 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": false, 14 | "complexity": { 15 | "noForEach": "off", 16 | "noStaticOnlyClass": "error", 17 | "noUselessSwitchCase": "error", 18 | "useFlatMap": "error" 19 | }, 20 | "style": { 21 | "noNegationElse": "off", 22 | "useForOf": "error", 23 | "useNodejsImportProtocol": "error", 24 | "useNumberNamespace": "error" 25 | }, 26 | "suspicious": { 27 | "noDoubleEquals": "error", 28 | "noThenProperty": "error", 29 | "useIsArray": "error" 30 | } 31 | } 32 | }, 33 | "javascript": { 34 | "globals": ["Global1"] 35 | }, 36 | "files": { 37 | "include": ["src/**/*", "utils/**/*.js", "www/**/*.js", "www/res/**/*.css"], 38 | "ignore": [ 39 | "ace-builds", 40 | "www/js/**/*.js", 41 | "www/css/**/*.css", 42 | "src/plugins/**/*", 43 | "plugins/**/*", 44 | "hooks/**/*", 45 | "fastlane/**/*", 46 | "res/**/*", 47 | "platforms/**/*" 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /build-extras.gradle: -------------------------------------------------------------------------------- 1 | configurations { 2 | all { 3 | exclude module: 'commons-logging' 4 | } 5 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A lightweight but powerful text/code editor. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Acode editor - Android code editor -------------------------------------------------------------------------------- /hooks/README.md: -------------------------------------------------------------------------------- 1 | 21 | # Cordova Hooks 22 | 23 | Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide. 24 | -------------------------------------------------------------------------------- /hooks/move-files.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | const gradleFilePath = path.resolve(__dirname, '../build-extras.gradle'); 5 | const newGradleFilePath = path.resolve( 6 | __dirname, 7 | '../platforms/android/app/build-extras.gradle' 8 | ); 9 | const buildFilePath = path.resolve(__dirname, '../build.json'); 10 | const newBuildFilePath = path.resolve( 11 | __dirname, 12 | '../platforms/android/build.json' 13 | ); 14 | 15 | const repeatChar = (char, times) => { 16 | let res = ''; 17 | while (--times >= 0) res += char; 18 | return res; 19 | }; 20 | 21 | let msg; 22 | if (!fs.existsSync(newBuildFilePath) && fs.existsSync(buildFilePath)) { 23 | msg = '== Moved build.json =='; 24 | console.log(repeatChar('=', msg.length)); 25 | console.log(msg); 26 | console.log(repeatChar('=', msg.length)); 27 | fs.copyFileSync(buildFilePath, newBuildFilePath); 28 | } 29 | 30 | if (!fs.existsSync(newGradleFilePath) && fs.existsSync(gradleFilePath)) { 31 | msg = '== Moved build-extras.gradle =='; 32 | console.log(repeatChar('=', msg.length)); 33 | console.log(msg); 34 | console.log(repeatChar('=', msg.length)); 35 | fs.copyFileSync(gradleFilePath, newGradleFilePath); 36 | } 37 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": ["**/node_modules", "**/platforms", "**/www", "www/js/ace/**/*"], 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "paths": { 6 | "*": ["*"] 7 | } 8 | }, 9 | "include": ["src/**/*"], 10 | "typeAcquisition": { 11 | "enable": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 Foxdebug(Ajit Kumar) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 4 | and associated documentation files (the "Software"), to deal in the Software without 5 | restriction, including without limitation the rights to use, copy, modify, merge, publish, 6 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom 7 | the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or 10 | substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 13 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 15 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 16 | OTHERWISE,ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer')({}) 4 | ] 5 | }; -------------------------------------------------------------------------------- /res/android/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /res/android/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /res/android/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /res/android/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /res/android/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /res/android/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /res/android/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /res/android/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /res/android/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /res/android/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /res/android/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /res/android/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /res/android/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/android/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /res/android/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3a3e54 4 | -------------------------------------------------------------------------------- /res/android/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3a3e54 4 | #3a3e54 5 | -------------------------------------------------------------------------------- /res/android/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /res/android/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | * 11 | localhost 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /res/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/logo.png -------------------------------------------------------------------------------- /res/logo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/res/logo_1.png -------------------------------------------------------------------------------- /src/components/WebComponents/index.js: -------------------------------------------------------------------------------- 1 | import "@ungap/custom-elements"; 2 | import WcPage from "./wcPage"; 3 | 4 | customElements.define("wc-page", WcPage); 5 | -------------------------------------------------------------------------------- /src/components/audioPlayer/style.scss: -------------------------------------------------------------------------------- 1 | .audio-player { 2 | background: var(--primary-color, #1e1e1e); 3 | border-radius: 10px; 4 | padding: 15px; 5 | display: flex; 6 | align-items: center; 7 | gap: 15px; 8 | width: 100%; 9 | max-width: 400px; 10 | user-select: none; 11 | 12 | .play-btn { 13 | background: transparent; 14 | border: none; 15 | width: 30px; 16 | height: 30px; 17 | border-radius: 50%; 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | cursor: pointer; 22 | transition: all 0.2s; 23 | 24 | span { 25 | font-size: 20px; 26 | color: var(--primary-text-color); 27 | } 28 | 29 | &:hover { 30 | background: color-mix(in srgb, var(--secondary-color) 30%, transparent); 31 | } 32 | } 33 | 34 | .timeline { 35 | flex: 1; 36 | height: 4px; 37 | background: color-mix(in srgb, var(--secondary-color) 60%, transparent); 38 | border-radius: 2px; 39 | position: relative; 40 | cursor: pointer; 41 | 42 | &:hover .progress-handle { 43 | opacity: 1; 44 | } 45 | } 46 | 47 | .progress { 48 | background: var(--primary-text-color, #fff); 49 | width: 0%; 50 | height: 100%; 51 | border-radius: 2px; 52 | transition: width 0.1s linear; 53 | } 54 | 55 | .progress-handle { 56 | width: 12px; 57 | height: 12px; 58 | background: var(--primary-text-color, #fff); 59 | border-radius: 50%; 60 | position: absolute; 61 | top: 50%; 62 | transform: translate(-50%, -50%); 63 | pointer-events: none; 64 | opacity: 0; 65 | transition: opacity 0.2s; 66 | } 67 | 68 | .time { 69 | color: var(--primary-text-color, #fff); 70 | font-family: monospace; 71 | font-size: 12px; 72 | min-width: 45px; 73 | } 74 | 75 | .volume-control { 76 | display: flex; 77 | align-items: center; 78 | gap: 8px; 79 | } 80 | 81 | .volume-btn { 82 | background: transparent; 83 | border: none; 84 | cursor: pointer; 85 | 86 | svg { 87 | width: 20px; 88 | height: 20px; 89 | fill: var(--primary-text-color, #fff); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/components/checkbox/index.js: -------------------------------------------------------------------------------- 1 | import "./styles.scss"; 2 | import Ref from "html-tag-js/ref"; 3 | 4 | /** 5 | * @typedef {Object} Checkbox 6 | * @property {string} text 7 | * @property {Ref} ref 8 | * @property {boolean} checked 9 | * @property {string} [name] 10 | * @property {string} [id] 11 | * @property {string} [size] 12 | * @property {"checkbox"|"radio"} [type] 13 | */ 14 | 15 | /** 16 | * Create a checkbox 17 | * @param {string | Checkbox} text Checkbox label 18 | * @param {Boolean} checked Whether checkbox is checked or not 19 | * @param {string} [name] Name of checkbox 20 | * @param {string} [id] Id of checkbox 21 | * @param {"checkbox"|"radio"} [type] Type of checkbox 22 | * @param {Ref} [ref] A reference to the input element 23 | * @param {string} [size] Size of checkbox 24 | * @returns {Checkbox} 25 | */ 26 | function Checkbox(text, checked, name, id, type, ref, size) { 27 | if (typeof text === "object") { 28 | ({ text, checked, name, id, type, ref, size } = text); 29 | } 30 | 31 | size = size || "1rem"; 32 | 33 | const $input = ref || new Ref(); 34 | const $checkbox = ( 35 | 46 | ); 47 | 48 | Object.defineProperties($checkbox, { 49 | checked: { 50 | get() { 51 | return !!$input.el.checked; 52 | }, 53 | set(value) { 54 | $input.el.checked = value; 55 | }, 56 | }, 57 | onclick: { 58 | get() { 59 | return $input.el.onclick; 60 | }, 61 | set(onclick) { 62 | $input.el.onclick = onclick; 63 | }, 64 | }, 65 | onchange: { 66 | get() { 67 | return $input.el.onchange; 68 | }, 69 | set(onchange) { 70 | $input.el.onchange = onchange; 71 | }, 72 | }, 73 | value: { 74 | get() { 75 | return this.checked; 76 | }, 77 | set(value) { 78 | this.checked = value; 79 | }, 80 | }, 81 | toggle: { 82 | value() { 83 | this.checked = !this.checked; 84 | }, 85 | }, 86 | }); 87 | 88 | return $checkbox; 89 | } 90 | 91 | export default Checkbox; 92 | -------------------------------------------------------------------------------- /src/components/checkbox/styles.scss: -------------------------------------------------------------------------------- 1 | .input-checkbox { 2 | display: inline-flex; 3 | align-items: center; 4 | 5 | input { 6 | display: none; 7 | } 8 | 9 | .box { 10 | display: flex; 11 | height: 1.2rem; 12 | width: 1.2rem; 13 | border-radius: 4px; 14 | border: solid 1px #252525; 15 | border: solid 1px var(--secondary-text-color); 16 | margin: 0 5px; 17 | 18 | &::after { 19 | content: ''; 20 | display: block; 21 | height: 80%; 22 | width: 80%; 23 | background-color: #3399ff; 24 | background-color: var(--button-background-color); 25 | margin: auto; 26 | border-radius: 2px; 27 | } 28 | } 29 | 30 | input:checked { 31 | &+.box::after { 32 | opacity: 1; 33 | } 34 | } 35 | 36 | input:not(:checked) { 37 | &+.box::after { 38 | opacity: 0; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/components/inputhints/style.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/mixins.scss"; 2 | 3 | #hints { 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | padding: 0; 8 | margin: 0; 9 | border-radius: 0 0 4px 4px; 10 | background-color: rgb(255, 255, 255); 11 | background-color: var(--secondary-color); 12 | color: rgb(37, 37, 37); 13 | color: var(--secondary-text-color); 14 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.2); 15 | box-shadow: 0 0 4px var(--box-shadow-color); 16 | border: solid 1px transparent; 17 | border: solid 1px var(--popup-border-color); 18 | height: fit-content; 19 | min-height: 30px; 20 | max-height: 70vh; 21 | z-index: 999; 22 | overflow-y: scroll; 23 | 24 | &.bottom { 25 | border-radius: 4px 4px 0 0; 26 | } 27 | 28 | &.all { 29 | border-radius: 4px; 30 | } 31 | 32 | [data-action="hint"], 33 | [action="hint"] { 34 | font-size: 0.9rem; 35 | min-height: 30px; 36 | height: fit-content; 37 | display: flex; 38 | align-items: center; 39 | box-sizing: border-box; 40 | padding: 5px; 41 | overflow-x: hidden; 42 | text-overflow: ellipsis; 43 | 44 | * { 45 | pointer-events: none; 46 | } 47 | 48 | [data-str]::after { 49 | content: attr(data-str); 50 | font-size: 0.6rem; 51 | opacity: 0.5; 52 | margin-left: 10px; 53 | } 54 | 55 | small:not(:empty) { 56 | margin-left: auto; 57 | color: rgb(51, 153, 255); 58 | color: var(--active-color); 59 | padding: 2px; 60 | border-radius: 2px; 61 | font-size: 0.6rem; 62 | } 63 | 64 | &.active { 65 | background-color: rgb(51, 153, 255); 66 | background-color: var(--active-color); 67 | color: rgb(27, 26, 26); 68 | color: var(--primary-text-color); 69 | 70 | small { 71 | color: rgb(255, 215, 0); 72 | color: var(--active-text-color); 73 | } 74 | } 75 | 76 | &:hover { 77 | background-color: rgb(107, 168, 229); 78 | background-color: var(--primary-color); 79 | color: rgb(27, 26, 26); 80 | color: var(--secondary-text-color); 81 | } 82 | } 83 | 84 | &.loading { 85 | @include loader(18px); 86 | } 87 | 88 | &:empty { 89 | display: none; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/components/page.js: -------------------------------------------------------------------------------- 1 | import WCPage from "./WebComponents/wcPage"; 2 | 3 | /** 4 | * 5 | * @param {string} title 6 | * @param {object} options 7 | * @param {HTMLElement} [options.lead] type of page 8 | * @param {HTMLElement} [options.tail] type of page 9 | * @returns {WCPage} 10 | */ 11 | function Page(title, options = {}) { 12 | let page = ; 13 | page.append = page.appendBody; 14 | page.initializeIfNotAlreadyInitialized(); 15 | page.settitle(title); 16 | 17 | if (options.tail) { 18 | page.header.append(options.tail); 19 | } 20 | if (options.lead) { 21 | page.lead = options.lead; 22 | } 23 | 24 | return page; 25 | } 26 | 27 | export default Page; 28 | -------------------------------------------------------------------------------- /src/components/palette/style.scss: -------------------------------------------------------------------------------- 1 | #palette { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | margin: auto; 7 | height: 40px; 8 | width: 90vw; 9 | max-width: 600px; 10 | z-index: 99; 11 | display: flex; 12 | align-items: center; 13 | background-color: inherit; 14 | box-shadow: 0 0 4px black; 15 | z-index: 999; 16 | 17 | &~#hints { 18 | width: 90vw; 19 | box-sizing: border-box; 20 | z-index: 999; 21 | } 22 | 23 | &~.mask { 24 | opacity: 0.4; 25 | z-index: 998; 26 | // pointer-events: none; 27 | } 28 | 29 | input { 30 | width: 100%; 31 | border: none; 32 | color: var(--primary-text-color); 33 | } 34 | } -------------------------------------------------------------------------------- /src/components/quickTools/index.js: -------------------------------------------------------------------------------- 1 | import "./style.scss"; 2 | import Ref from "html-tag-js/ref"; 3 | import settings from "lib/settings"; 4 | import { 5 | $footer, 6 | $input, 7 | $toggler, 8 | Row, 9 | SearchRow1, 10 | SearchRow2, 11 | } from "./footer"; 12 | 13 | /**@type {HTMLElement} */ 14 | let $row1; 15 | /**@type {HTMLElement} */ 16 | let $row2; 17 | /**@type {HTMLElement} */ 18 | let $searchRow1; 19 | /**@type {HTMLElement} */ 20 | let $searchRow2; 21 | 22 | const $searchInput = new Ref(); 23 | const $replaceInput = new Ref(); 24 | const $searchPos = new Ref(); 25 | const $searchTotal = new Ref(); 26 | 27 | export default { 28 | get $footer() { 29 | return $footer; 30 | }, 31 | get $row1() { 32 | if ($row1) return $row1; 33 | $row1 = ; 34 | 35 | settings.on("update:quicktoolsItems:after", () => { 36 | $row1 = ; 37 | }); 38 | 39 | return $row1; 40 | }, 41 | get $row2() { 42 | if ($row2) return $row2; 43 | $row2 = ; 44 | 45 | settings.on("update:quicktoolsItems:after", () => { 46 | $row2 = ; 47 | }); 48 | 49 | return $row2; 50 | }, 51 | get $searchRow1() { 52 | if ($searchRow1) return $searchRow1; 53 | $searchRow1 = ; 54 | return $searchRow1; 55 | }, 56 | get $searchRow2() { 57 | if ($searchRow2) return $searchRow2; 58 | $searchRow2 = ( 59 | 64 | ); 65 | return $searchRow2; 66 | }, 67 | get $input() { 68 | return $input; 69 | }, 70 | get $toggler() { 71 | return $toggler; 72 | }, 73 | get $searchInput() { 74 | return $searchInput; 75 | }, 76 | get $replaceInput() { 77 | return $replaceInput; 78 | }, 79 | get $searchPos() { 80 | return $searchPos; 81 | }, 82 | get $searchTotal() { 83 | return $searchTotal; 84 | }, 85 | }; 86 | -------------------------------------------------------------------------------- /src/components/quickTools/style.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/mixins.scss'; 2 | 3 | #quick-tools { 4 | .icon.click { 5 | @include active-icon; 6 | transition: all 0.3s ease-in-out; 7 | transform: scale(1.2); 8 | } 9 | 10 | &[data-alt="true"] { 11 | [data-id="alt-key"] { 12 | @include active-icon; 13 | } 14 | } 15 | 16 | &[data-shift="true"] { 17 | [data-id="shift-key"] { 18 | @include active-icon; 19 | } 20 | } 21 | 22 | &[data-ctrl="true"] { 23 | [data-id="ctrl-key"] { 24 | @include active-icon; 25 | } 26 | } 27 | 28 | &[data-meta="true"] { 29 | [data-id="meta-key"] { 30 | @include active-icon; 31 | } 32 | } 33 | 34 | &[data-unsaved="true"] { 35 | [data-id="save"] { 36 | @include icon-badge; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/components/scrollbar/style.scss: -------------------------------------------------------------------------------- 1 | .scrollbar-container { 2 | position: absolute; 3 | background-color: transparent; 4 | padding: 10px; 5 | opacity: 0.5; 6 | z-index: 9999; 7 | animation: scrollbar-show 300ms ease 1; 8 | transition: all 300ms ease; 9 | opacity: 1; 10 | 11 | &.hide { 12 | opacity: 0; 13 | } 14 | 15 | &.active { 16 | opacity: 1; 17 | 18 | .thumb { 19 | box-shadow: 0 0 0 4px rgba($color: #fff, $alpha: 0.2); 20 | } 21 | } 22 | 23 | .container { 24 | height: 100%; 25 | width: 100%; 26 | position: relative; 27 | 28 | .thumb, 29 | .scroll-cursor { 30 | position: absolute; 31 | display: block; 32 | } 33 | 34 | .thumb { 35 | background-color: #39f; 36 | background-color: var(--button-background-color); 37 | border-radius: 4px; 38 | } 39 | } 40 | 41 | &.right, 42 | &.left { 43 | height: calc(100% - (50px * 2)); 44 | width: 10px; 45 | top: 0; 46 | bottom: 0; 47 | margin: auto; 48 | 49 | .thumb, 50 | .cursor { 51 | height: 1px; 52 | width: 100%; 53 | } 54 | 55 | .thumb { 56 | height: 50px; 57 | margin-top: -25px; 58 | } 59 | } 60 | 61 | &.right { 62 | left: auto; 63 | right: 0; 64 | padding: 10px 17px 10px 10px; 65 | } 66 | 67 | &.left { 68 | left: 0; 69 | right: auto; 70 | } 71 | 72 | &.top, 73 | &.bottom { 74 | width: calc(100% - (50px * 2)); 75 | height: 10px; 76 | left: 0; 77 | right: 0; 78 | margin: auto; 79 | 80 | .thumb, 81 | .cursor { 82 | height: 100%; 83 | width: 1px; 84 | } 85 | 86 | .thumb { 87 | width: 50px; 88 | margin-left: -25px; 89 | } 90 | } 91 | 92 | &.top { 93 | top: 0; 94 | bottom: auto; 95 | } 96 | 97 | &.bottom { 98 | bottom: 0; 99 | top: auto; 100 | } 101 | } -------------------------------------------------------------------------------- /src/components/searchbar/style.scss: -------------------------------------------------------------------------------- 1 | #search-bar { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | height: 45px; 6 | width: 100%; 7 | animation: show-searchbar 300ms ease 1; 8 | z-index: 200; 9 | background-color: inherit; 10 | display: flex; 11 | 12 | input { 13 | flex: 1; 14 | border-radius: 4px; 15 | box-shadow: rgba(0, 0, 0, 0.2); 16 | box-shadow: var(--box-shadow-color); 17 | border: none; 18 | margin: 5px; 19 | box-sizing: border-box; 20 | color: var(--primary-text-color); 21 | 22 | &:focus { 23 | outline: none; 24 | box-shadow: rgba(0, 0, 0, 0.5); 25 | } 26 | 27 | &::-webkit-search-decoration, 28 | &::-webkit-search-cancel-button, 29 | &::-webkit-search-results-button, 30 | &::-webkit-search-results-decoration { 31 | display: none; 32 | } 33 | } 34 | 35 | .icon { 36 | height: 45px; 37 | width: 45px; 38 | font-size: 1.2em; 39 | color: rgb(255, 255, 255); 40 | } 41 | 42 | &.hide { 43 | transition: all 300ms ease; 44 | opacity: 0; 45 | transform: translate(0, -100%, 0); 46 | } 47 | } -------------------------------------------------------------------------------- /src/components/sideButton/index.js: -------------------------------------------------------------------------------- 1 | import "./style.scss"; 2 | 3 | /**@type {HTMLDivElement} */ 4 | export const sideButtonContainer =
; 5 | 6 | export default function SideButtons({ 7 | text, 8 | icon, 9 | onclick, 10 | backgroundColor, 11 | textColor, 12 | }) { 13 | const $button = ( 14 | 22 | ); 23 | 24 | return { 25 | show() { 26 | sideButtonContainer.append($button); 27 | }, 28 | hide() { 29 | $button.remove(); 30 | }, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/components/sideButton/style.scss: -------------------------------------------------------------------------------- 1 | .side-button { 2 | position: relative; 3 | width: 15px; 4 | padding: 2.5px 0; 5 | border: none; 6 | box-sizing: border-box; 7 | 8 | .icon { 9 | margin: 0 0 5px 0; 10 | } 11 | 12 | span { 13 | writing-mode: vertical-rl; 14 | } 15 | 16 | &:active::before { 17 | content: ''; 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | width: 100%; 22 | height: 100%; 23 | background-color: rgba($color: #000000, $alpha: 0.5); 24 | } 25 | } 26 | 27 | .side-buttons { 28 | position: absolute; 29 | right: 0; 30 | top: 0; 31 | z-index: 999; 32 | display: flex; 33 | flex-direction: column; 34 | gap: 5px; 35 | } -------------------------------------------------------------------------------- /src/components/tile/index.js: -------------------------------------------------------------------------------- 1 | import "./style.scss"; 2 | 3 | /** 4 | * @typedef {object} Tile 5 | * @property {String} text 6 | * @property {String} subText 7 | * @property {function(HTMLElement):void} lead 8 | * @property {function(HTMLElement):void} tail 9 | */ 10 | 11 | /** 12 | * 13 | * @param {object} [options] 14 | * @param {HTMLElement} [options.lead] 15 | * @param {HTMLElement} [options.tail] 16 | * @param {string | HTMLElement} [options.text] 17 | * @param {string} [options.subText] 18 | * @param {string} [options.type] 19 | * @returns {HTMLElement & Tile} 20 | */ 21 | function tile(options = {}) { 22 | const $el = tag(options.type || "li", { 23 | className: "tile", 24 | }); 25 | 26 | const $titleEl = 27 | typeof options.text === "string" 28 | ? tag("span", { 29 | textContent: options.text || "", 30 | className: "text", 31 | }) 32 | : options.text; 33 | const leadEl = 34 | options.lead || 35 | tag("span", { 36 | className: "lead", 37 | }); 38 | const tailEl = 39 | options.tail || 40 | tag("span", { 41 | className: "tail", 42 | }); 43 | 44 | if (options.subText) { 45 | $titleEl.setAttribute("data-subtext", options.subText); 46 | $titleEl.classList.add("sub-text"); 47 | } 48 | 49 | $el.append(leadEl, $titleEl, tailEl); 50 | 51 | Object.defineProperties($el, { 52 | text: { 53 | get() { 54 | return $titleEl.textContent; 55 | }, 56 | set(text) { 57 | $titleEl.textContent = text; 58 | }, 59 | }, 60 | subText: { 61 | get() { 62 | return $titleEl.getAttribute("data-subtext"); 63 | }, 64 | set(text) { 65 | if (text) { 66 | $titleEl.setAttribute("data-subtext", text); 67 | $titleEl.classList.add("sub-text"); 68 | } else { 69 | $titleEl.classList.remove("sub-text"); 70 | } 71 | }, 72 | }, 73 | lead: { 74 | value($newLead) { 75 | $el.replaceChild($newLead, $el.firstChild); 76 | }, 77 | }, 78 | tail: { 79 | value($newTail) { 80 | $el.replaceChild($newTail, $el.lastChild); 81 | }, 82 | }, 83 | }); 84 | 85 | return $el; 86 | } 87 | 88 | export default tile; 89 | -------------------------------------------------------------------------------- /src/components/tile/style.scss: -------------------------------------------------------------------------------- 1 | .header, 2 | header { 3 | &.tile { 4 | z-index: 98; 5 | height: 45px; 6 | background-color: rgb(153, 153, 255); 7 | background-color: var(--primary-color); 8 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 9 | box-shadow: 0 2px 4px var(--box-shadow-color); 10 | color: rgb(255, 255, 255); 11 | color: var(--primary-text-color); 12 | 13 | >.text { 14 | pointer-events: all !important; 15 | margin: auto; 16 | font-weight: bold; 17 | font-size: 1.2em; 18 | white-space: nowrap; 19 | overflow: auto; 20 | } 21 | } 22 | } 23 | 24 | .tile { 25 | display: flex; 26 | flex-wrap: nowrap; 27 | align-items: center; 28 | box-sizing: border-box; 29 | 30 | &.drag { 31 | pointer-events: none; 32 | border: solid 1px rgb(255, 215, 0); 33 | background-color: inherit !important; 34 | pointer-events: none; 35 | position: fixed; 36 | font-size: 0.8em; 37 | z-index: 9999; 38 | 39 | .file { 40 | padding: 0 !important; 41 | } 42 | 43 | .cancel { 44 | display: none; 45 | } 46 | } 47 | 48 | &:disabled, 49 | &.disabled { 50 | opacity: 0.6; 51 | pointer-events: none; 52 | } 53 | 54 | * { 55 | pointer-events: none; 56 | } 57 | 58 | [data-action], 59 | [action] { 60 | pointer-events: all !important; 61 | } 62 | 63 | &.cut { 64 | opacity: 0.6; 65 | } 66 | 67 | >.text { 68 | flex: 1; 69 | white-space: nowrap; 70 | overflow: hidden; 71 | text-overflow: ellipsis; 72 | 73 | &.sub-text { 74 | display: block; 75 | 76 | &::after { 77 | content: attr(data-subText); 78 | font-size: 0.58em; 79 | opacity: 0.6; 80 | display: block; 81 | position: sticky; 82 | left: 0; 83 | } 84 | } 85 | } 86 | 87 | .icon { 88 | height: 45px; 89 | width: 45px; 90 | font-size: 2em; 91 | 92 | background-repeat: no-repeat; 93 | background-position: center; 94 | background-size: 1.5em; 95 | 96 | &:active { 97 | transition: all 100ms ease; 98 | transform: scale(0.95) translateZ(0); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/components/toast/index.js: -------------------------------------------------------------------------------- 1 | import "./style.scss"; 2 | 3 | /**@type {Array} */ 4 | const toastQueue = []; 5 | 6 | /** 7 | * Show a toast message 8 | * @param {string|HTMLElement} message 9 | * @param {number|false} duration 10 | */ 11 | export default function toast(message, duration = 0, bgColor, color) { 12 | const $oldToast = tag.get("#toast"); 13 | const $toast = ( 14 |
19 | {message} 20 | {duration === false ? ( 21 | 25 | ) : ( 26 | "" 27 | )} 28 |
29 | ); 30 | 31 | Object.defineProperties($toast, { 32 | hide: { 33 | value() { 34 | this.classList.add("hide"); 35 | setTimeout(() => { 36 | this.remove(); 37 | const $toast = toastQueue.splice(0, 1)[0]; 38 | if ($toast) $toast.show(); 39 | }, 500); 40 | }, 41 | }, 42 | show: { 43 | value() { 44 | app.append(this); 45 | 46 | if (typeof duration === "number") { 47 | setTimeout(() => { 48 | this.hide(); 49 | }, duration || 3000); 50 | } 51 | }, 52 | }, 53 | }); 54 | 55 | if (!$oldToast) { 56 | $toast.show(); 57 | } else { 58 | toastQueue.push($toast); 59 | } 60 | } 61 | 62 | toast.hide = () => { 63 | const $toast = tag.get("#toast"); 64 | if ($toast) $toast.hide(); 65 | }; 66 | -------------------------------------------------------------------------------- /src/components/toast/style.scss: -------------------------------------------------------------------------------- 1 | #toast { 2 | position: fixed; 3 | bottom: 10px; 4 | left: 0; 5 | right: 0; 6 | margin: 0 auto; 7 | background-color: rgba(9, 14, 29, 0.9); 8 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.5); 9 | color: white; 10 | font-size: 0.95rem; 11 | font-weight: 300; 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | max-height: 50vh; 16 | max-width: 80vw; 17 | width: fit-content; 18 | overflow: auto; 19 | animation: slow-appear 500ms linear 1; 20 | z-index: 9999; 21 | border-radius: 4px; 22 | pointer-events: none; 23 | 24 | &.hide { 25 | transition: all 1s linear; 26 | opacity: 0; 27 | } 28 | 29 | &[clickable='true'] { 30 | pointer-events: all; 31 | } 32 | 33 | .message { 34 | padding: 10px; 35 | } 36 | 37 | .icon { 38 | background-color: inherit; 39 | color: inherit; 40 | border: none; 41 | min-height: 30px; 42 | min-width: 30px; 43 | } 44 | } -------------------------------------------------------------------------------- /src/components/tutorial.js: -------------------------------------------------------------------------------- 1 | import toast from "./toast"; 2 | 3 | /** 4 | * 5 | * @param {string} id 6 | * @param {string|HTMLElement|(hide: ()=>void)=>HTMLElement} message 7 | * @returns 8 | */ 9 | export default function tutorial(id, message) { 10 | if (localStorage.getItem(id) === "true") return; 11 | localStorage.setItem(id, "true"); 12 | 13 | if (typeof message === "function") { 14 | message = message(toast.hide); 15 | } 16 | 17 | toast(message, false, "#17c", "#fff"); 18 | } 19 | -------------------------------------------------------------------------------- /src/dialogs/alert.js: -------------------------------------------------------------------------------- 1 | import DOMPurify from "dompurify"; 2 | import actionStack from "lib/actionStack"; 3 | import restoreTheme from "lib/restoreTheme"; 4 | 5 | /** 6 | * Alert dialog 7 | * @param {string} titleText Title text 8 | * @param {string} message Alert message 9 | * @param {function():void} [onhide] Callback function 10 | */ 11 | function alert(titleText, message, onhide) { 12 | if (!message && titleText) { 13 | message = titleText; 14 | titleText = ""; 15 | } 16 | 17 | const regex = /(https?:\/\/[^\s]+)/g; 18 | if (regex.test(message)) { 19 | message = message.replace(regex, function (url) { 20 | return `${url}`; 21 | }); 22 | } 23 | 24 | const titleSpan = tag("strong", { 25 | className: "title", 26 | textContent: titleText, 27 | }); 28 | const messageSpan = tag("span", { 29 | className: "message scroll", 30 | innerHTML: DOMPurify.sanitize(message), 31 | }); 32 | const okBtn = tag("button", { 33 | textContent: strings.ok, 34 | onclick: hide, 35 | }); 36 | const alertDiv = tag("div", { 37 | className: "prompt alert", 38 | children: [ 39 | titleSpan, 40 | messageSpan, 41 | tag("div", { 42 | className: "button-container", 43 | child: okBtn, 44 | }), 45 | ], 46 | }); 47 | const mask = tag("span", { 48 | className: "mask", 49 | onclick: hide, 50 | }); 51 | 52 | actionStack.push({ 53 | id: "alert", 54 | action: hideAlert, 55 | }); 56 | 57 | app.append(alertDiv, mask); 58 | restoreTheme(true); 59 | 60 | function hideAlert() { 61 | alertDiv.classList.add("hide"); 62 | restoreTheme(); 63 | setTimeout(() => { 64 | app.removeChild(alertDiv); 65 | app.removeChild(mask); 66 | }, 300); 67 | } 68 | 69 | function hide() { 70 | if (onhide) onhide(); 71 | actionStack.remove("alert"); 72 | hideAlert(); 73 | } 74 | } 75 | 76 | export default alert; 77 | -------------------------------------------------------------------------------- /src/dialogs/confirm.js: -------------------------------------------------------------------------------- 1 | import DOMPurify from "dompurify"; 2 | import actionStack from "lib/actionStack"; 3 | import restoreTheme from "lib/restoreTheme"; 4 | 5 | /** 6 | * Confirm dialog box 7 | * @param {string} titleText Title text 8 | * @param {string} message Alert message 9 | * @param {boolean} isHTML Whether the message is HTML 10 | * @returns {Promise} 11 | */ 12 | function confirm(titleText, message, isHTML) { 13 | return new Promise((resolve) => { 14 | if (!message && titleText) { 15 | message = titleText; 16 | titleText = ""; 17 | } 18 | 19 | const titleSpan = tag("strong", { 20 | className: "title", 21 | textContent: titleText, 22 | }); 23 | const messageSpan = tag("span", { 24 | className: "message scroll", 25 | innerHTML: isHTML ? DOMPurify.sanitize(message) : undefined, 26 | textContent: isHTML ? undefined : message, 27 | }); 28 | const okBtn = tag("button", { 29 | textContent: strings.ok, 30 | onclick: function () { 31 | hide(); 32 | resolve(true); 33 | }, 34 | }); 35 | const cancelBtn = tag("button", { 36 | textContent: strings.cancel, 37 | onclick: function () { 38 | hide(); 39 | resolve(false); 40 | }, 41 | }); 42 | const confirmDiv = tag("div", { 43 | className: "prompt confirm", 44 | children: [ 45 | titleSpan, 46 | messageSpan, 47 | tag("div", { 48 | className: "button-container", 49 | children: [cancelBtn, okBtn], 50 | }), 51 | ], 52 | }); 53 | const mask = tag("span", { 54 | className: "mask", 55 | }); 56 | 57 | actionStack.push({ 58 | id: "confirm", 59 | action: hideAlert, 60 | }); 61 | 62 | app.append(confirmDiv, mask); 63 | restoreTheme(true); 64 | 65 | function hideAlert() { 66 | confirmDiv.classList.add("hide"); 67 | restoreTheme(); 68 | setTimeout(() => { 69 | app.removeChild(confirmDiv); 70 | app.removeChild(mask); 71 | }, 300); 72 | } 73 | 74 | function hide() { 75 | actionStack.remove("confirm"); 76 | hideAlert(); 77 | } 78 | }); 79 | } 80 | 81 | export default confirm; 82 | -------------------------------------------------------------------------------- /src/dialogs/rateBox.js: -------------------------------------------------------------------------------- 1 | import constants from "lib/constants"; 2 | import template from "views/rating.hbs"; 3 | import box from "./box"; 4 | 5 | function rateBox() { 6 | const $box = box("Did you like the app?", template, strings.cancel).onclick( 7 | onInteract, 8 | ); 9 | 10 | function onInteract(e) { 11 | /** 12 | * @type {HTMLSpanElement} 13 | */ 14 | const $el = e.target; 15 | if (!$el) return; 16 | let val = $el.getAttribute("value"); 17 | if (val) val = Number.parseInt(val); 18 | const siblings = $el.parentElement.children; 19 | const len = siblings.length; 20 | for (let i = 0; i < len; ++i) { 21 | const star = siblings[i]; 22 | star.classList.remove("stargrade", "star_outline"); 23 | if (i < val) star.classList.add("stargrade"); 24 | else star.classList.add("star_outline"); 25 | } 26 | 27 | setTimeout(() => { 28 | if (val === 5) { 29 | system.openInBrowser( 30 | `https://play.google.com/store/apps/details?id=${BuildInfo.packageName}`, 31 | ); 32 | localStorage.dontAskForRating = true; 33 | } else { 34 | const stars = getStars(val); 35 | const subject = "feedback - Acode editor"; 36 | const textBody = stars + "
%0A" + getFeedbackBody("
%0A"); 37 | const email = constants.FEEDBACK_EMAIL; 38 | system.openInBrowser( 39 | `mailto:${email}?subject=${subject}&body=${textBody}`, 40 | ); 41 | } 42 | }, 100); 43 | 44 | $box.hide(); 45 | } 46 | } 47 | 48 | /** 49 | * Gets body for feedback email 50 | * @param {String} eol 51 | * @returns 52 | */ 53 | function getFeedbackBody(eol) { 54 | const buildInfo = window.BuildInfo || {}; 55 | const device = window.device || {}; 56 | return ( 57 | "Version: " + 58 | `${buildInfo.version} (${buildInfo.versionCode})` + 59 | eol + 60 | "Device: " + 61 | (device.model || "") + 62 | eol + 63 | "Manufacturer: " + 64 | (device.manufacturer || "") + 65 | eol + 66 | "Android version: " + 67 | device.version + 68 | eol + 69 | "Info: " 70 | ); 71 | } 72 | 73 | /** 74 | * 75 | * @param {number} num 76 | */ 77 | function getStars(num) { 78 | let star = num; 79 | let noStar = 5 - num; 80 | let str = ""; 81 | 82 | while (star--) str += "★"; 83 | while (noStar--) str += "☆"; 84 | 85 | return str; 86 | } 87 | 88 | export default rateBox; 89 | -------------------------------------------------------------------------------- /src/handlers/intent.js: -------------------------------------------------------------------------------- 1 | import fsOperation from "fileSystem"; 2 | import openFile from "lib/openFile"; 3 | import helpers from "utils/helpers"; 4 | 5 | const handlers = []; 6 | 7 | /** 8 | * 9 | * @param {Intent} intent 10 | */ 11 | export default async function HandleIntent(intent = {}) { 12 | const type = intent.action.split(".").slice(-1)[0]; 13 | 14 | if (["SEND", "VIEW", "EDIT"].includes(type)) { 15 | /**@type {string} */ 16 | const url = intent.fileUri || intent.data; 17 | if (!url) return; 18 | 19 | if (url.startsWith("acode://")) { 20 | const path = url.replace("acode://", ""); 21 | const [module, action, value] = path.split("/"); 22 | 23 | let defaultPrevented = false; 24 | const event = new IntentEvent(module, action, value); 25 | for (const handler of handlers) { 26 | handler(event); 27 | if (event.defaultPrevented) defaultPrevented = true; 28 | if (event.propagationStopped) break; 29 | } 30 | 31 | if (defaultPrevented) return; 32 | 33 | if (module === "plugin") { 34 | const { default: Plugin } = await import("pages/plugin"); 35 | const installed = await fsOperation(PLUGIN_DIR, value).exists(); 36 | Plugin({ id: value, installed, install: action === "install" }); 37 | } 38 | 39 | return; 40 | } 41 | 42 | await openFile(url, { 43 | mode: "single", 44 | render: true, 45 | }); 46 | } 47 | } 48 | 49 | HandleIntent.onError = (error) => { 50 | helpers.error(error); 51 | }; 52 | 53 | export function addIntentHandler(handler) { 54 | handlers.push(handler); 55 | } 56 | 57 | export function removeIntentHandler(handler) { 58 | const index = handlers.indexOf(handler); 59 | if (index > -1) handlers.splice(index, 1); 60 | } 61 | 62 | class IntentEvent { 63 | module; 64 | action; 65 | value; 66 | 67 | #defaultPrevented = false; 68 | #propagationStopped = false; 69 | 70 | /** 71 | * Creates an instance of IntentEvent. 72 | * @param {string} module 73 | * @param {string} action 74 | * @param {string} value 75 | */ 76 | constructor(module, action, value) { 77 | this.module = module; 78 | this.action = action; 79 | this.value = value; 80 | } 81 | 82 | preventDefault() { 83 | this.#defaultPrevented = true; 84 | } 85 | 86 | stopPropagation() { 87 | this.#propagationStopped = true; 88 | } 89 | 90 | get defaultPrevented() { 91 | return this.#defaultPrevented; 92 | } 93 | 94 | get propagationStopped() { 95 | return this.#propagationStopped; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/handlers/purchase.js: -------------------------------------------------------------------------------- 1 | import helpers from "utils/helpers"; 2 | 3 | export default function purchaseListener(onpurchase, onerror) { 4 | return [ 5 | (purchases) => { 6 | const [purchase] = purchases; 7 | if (purchase.purchaseState === iap.PURCHASE_STATE_PURCHASED) { 8 | if (!purchase.isAcknowledged) { 9 | iap.acknowledgePurchase( 10 | purchase.purchaseToken, 11 | () => { 12 | onpurchase(); 13 | }, 14 | (error) => { 15 | if (typeof onerror === "function") onerror(error); 16 | }, 17 | ); 18 | return; 19 | } 20 | onpurchase(); 21 | return; 22 | } 23 | 24 | const message = 25 | purchase.purchaseState === iap.PURCHASE_STATE_PENDING 26 | ? strings["purchase pending"] 27 | : strings.failed; 28 | 29 | helpers.error(message); 30 | if (typeof onerror === "function") onerror(message); 31 | }, 32 | (error) => { 33 | if (error === iap.ITEM_ALREADY_OWNED) { 34 | onpurchase(); 35 | return; 36 | } 37 | 38 | let message = 39 | error === iap.USER_CANCELED ? strings.failed : strings.canceled; 40 | 41 | if (typeof onerror === "function") onerror(message); 42 | }, 43 | ]; 44 | } 45 | -------------------------------------------------------------------------------- /src/handlers/windowResize.js: -------------------------------------------------------------------------------- 1 | let resizeTimeout; 2 | 3 | /** 4 | * @typedef {'resize'|'resizeStart'} ResizeEventName 5 | */ 6 | 7 | const event = { 8 | resize: [], 9 | resizeStart: [], 10 | }; 11 | 12 | /** 13 | * *external keyboard* 14 | * -> config.hardKeyboardHidden = HARDKEYBOARDHIDDEN_NO 15 | * This means that external keyboard is connected. If external keyboard is connected, 16 | * we don't need to do anything. 17 | * 18 | * *floating keyboard is active* 19 | * -> No way to detect this. 20 | * 21 | * *keyboard is hidden* 22 | * -> If window height is smaller than earlier, keyboard is visible. and vice versa. 23 | * If keyboard is not visible, we need to blur the editor, and hide the ad. 24 | * Else we need to focus the editor, and show the ad. 25 | */ 26 | 27 | export default function windowResize() { 28 | if (!resizeTimeout) { 29 | emit("resizeStart"); 30 | } 31 | 32 | clearTimeout(resizeTimeout); 33 | resizeTimeout = setTimeout(onResize, 100); 34 | } 35 | 36 | /** 37 | * Add event listener for window done resizing. 38 | * @param {ResizeEventName} eventName 39 | * @param {Function} callback 40 | * @returns 41 | */ 42 | windowResize.on = (eventName, callback) => { 43 | if (!event[eventName]) return; 44 | event[eventName].push(callback); 45 | }; 46 | 47 | /** 48 | * Remove event listener for window done resizing. 49 | * @param {ResizeEventName} eventName 50 | * @param {Function} callback 51 | * @returns 52 | */ 53 | windowResize.off = (eventName, callback) => { 54 | if (!event[eventName]) return; 55 | event[eventName] = event[eventName].filter((cb) => cb !== callback); 56 | }; 57 | 58 | /** 59 | * Timeout function for window done resizing. 60 | */ 61 | function onResize() { 62 | resizeTimeout = null; 63 | emit("resize"); 64 | } 65 | 66 | /** 67 | * Emit event 68 | * @param {ResizeEventName} eventName 69 | * @returns 70 | */ 71 | function emit(eventName) { 72 | if (!event[eventName]) return; 73 | event[eventName].forEach((cb) => cb()); 74 | } 75 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | type LanguageMap = { [key: string]: string }; 2 | declare const strings: LanguageMap; 3 | declare const ASSETS_DIRECTORY: string; 4 | declare const DATA_STORAGE: string; 5 | declare const CACHE_STORAGE: string; 6 | declare const PLUGIN_DIR: string; 7 | declare const KEYBINDING_FILE: string; 8 | declare const IS_FREE_VERSION: string; 9 | declare const ANDROID_SDK_INT: number; 10 | declare const DOES_SUPPORT_THEME: boolean; 11 | declare const acode: object; 12 | 13 | interface Window { 14 | ASSETS_DIRECTORY: string; 15 | DATA_STORAGE: string; 16 | CACHE_STORAGE: string; 17 | PLUGIN_DIR: string; 18 | KEYBINDING_FILE: string; 19 | IS_FREE_VERSION: string; 20 | ANDROID_SDK_INT: number; 21 | DOES_SUPPORT_THEME: boolean; 22 | acode: object; 23 | } 24 | 25 | interface String { 26 | /** 27 | * Capitalize the first letter of a string 28 | */ 29 | capitalize(): string; 30 | /** 31 | * Generate a hash from a string 32 | */ 33 | hash(): string; 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/applySettings.js: -------------------------------------------------------------------------------- 1 | import actions from "../handlers/quickTools"; 2 | import appSettings from "../lib/settings"; 3 | import themes from "../theme/list"; 4 | import constants from "./constants"; 5 | import fonts from "./fonts"; 6 | 7 | export default { 8 | beforeRender() { 9 | //animation 10 | appSettings.applyAnimationSetting(); 11 | 12 | //full-screen 13 | if (appSettings.value.fullscreen) { 14 | acode.exec("enable-fullscreen"); 15 | } 16 | 17 | //setup vibration 18 | app.addEventListener("click", function (e) { 19 | const $target = e.target; 20 | if ($target.hasAttribute("vibrate") && appSettings.value.vibrateOnTap) { 21 | navigator.vibrate(constants.VIBRATION_TIME); 22 | } 23 | }); 24 | 25 | system.setInputType(appSettings.value.keyboardMode); 26 | }, 27 | afterRender() { 28 | const { value: settings } = appSettings; 29 | if (!settings.floatingButton) { 30 | root.classList.add("hide-floating-button"); 31 | } 32 | 33 | actions("set-height", settings.quickTools); 34 | fonts.setFont(settings.editorFont); 35 | if (!themes.applied) { 36 | themes.apply("dark"); 37 | } 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /src/lib/checkPluginsUpdate.js: -------------------------------------------------------------------------------- 1 | import ajax from "@deadlyjack/ajax"; 2 | import fsOperation from "../fileSystem"; 3 | import Url from "../utils/Url"; 4 | 5 | export default async function checkPluginsUpdate() { 6 | const plugins = await fsOperation(PLUGIN_DIR).lsDir(); 7 | const promises = []; 8 | const updates = []; 9 | 10 | plugins.forEach((pluginDir) => { 11 | promises.push( 12 | (async () => { 13 | const plugin = await fsOperation( 14 | Url.join(pluginDir.url, "plugin.json"), 15 | ).readFile("json"); 16 | 17 | const res = await ajax({ 18 | url: `https://acode.app/api/plugin/check-update/${plugin.id}/${plugin.version}`, 19 | }); 20 | 21 | if (res.update) { 22 | updates.push(plugin.id); 23 | } 24 | })(), 25 | ); 26 | }); 27 | 28 | await Promise.allSettled(promises); 29 | return updates; 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/constants.js: -------------------------------------------------------------------------------- 1 | export default { 2 | FILE_NAME_REGEX: /^((?![:<>"\\\|\?\*]).)*$/, 3 | FONT_SIZE: /^[0-9\.]{1,3}(px|rem|em|pt|mm|pc|in)$/, 4 | DEFAULT_FILE_SESSION: "default-session", 5 | DEFAULT_FILE_NAME: "untitled.txt", 6 | CONSOLE_PORT: 8159, 7 | SERVER_PORT: 8158, 8 | PREVIEW_PORT: 8158, 9 | VIBRATION_TIME: 30, 10 | VIBRATION_TIME_LONG: 150, 11 | SCROLL_SPEED_FAST_X2: "FAST_X2", 12 | SCROLL_SPEED_NORMAL: "NORMAL", 13 | SCROLL_SPEED_FAST: "FAST", 14 | SCROLL_SPEED_SLOW: "SLOW", 15 | SIDEBAR_SLIDE_START_THRESHOLD_PX: 20, 16 | CUSTOM_THEME: 'body[theme="custom"]', 17 | FEEDBACK_EMAIL: "acode@foxdebug.com", 18 | ERUDA_CDN: "https://cdn.jsdelivr.net/npm/eruda", 19 | get PLAY_STORE_URL() { 20 | return `https://play.google.com/store/apps/details?id=${BuildInfo.packageName}`; 21 | }, 22 | API_BASE: "https://acode.app/api", 23 | // API_BASE: 'https://192.168.0.102:3001/api', // test api 24 | SKU_LIST: ["basic", "bronze", "silver", "gold", "platinum"], 25 | LOG_FILE_NAME: "Acode.log", 26 | }; 27 | -------------------------------------------------------------------------------- /src/lib/loadPlugin.js: -------------------------------------------------------------------------------- 1 | import Page from "components/page"; 2 | import fsOperation from "fileSystem"; 3 | import Url from "utils/Url"; 4 | import helpers from "utils/helpers"; 5 | import actionStack from "./actionStack"; 6 | 7 | export default async function loadPlugin(pluginId, justInstalled = false) { 8 | const baseUrl = await helpers.toInternalUri(Url.join(PLUGIN_DIR, pluginId)); 9 | const cacheFile = Url.join(CACHE_STORAGE, pluginId); 10 | 11 | const pluginJson = await fsOperation( 12 | Url.join(PLUGIN_DIR, pluginId, "plugin.json"), 13 | ).readFile("json"); 14 | 15 | let mainUrl; 16 | if ( 17 | await fsOperation(Url.join(PLUGIN_DIR, pluginId, pluginJson.main)).exists() 18 | ) { 19 | mainUrl = Url.join(baseUrl, pluginJson.main); 20 | } else { 21 | mainUrl = Url.join(baseUrl, "main.js"); 22 | } 23 | 24 | return new Promise((resolve, reject) => { 25 | const $script = ; 26 | 27 | $script.onerror = (error) => { 28 | reject( 29 | new Error( 30 | `Failed to load script for plugin ${pluginId}: ${error.message || error}`, 31 | ), 32 | ); 33 | }; 34 | 35 | $script.onload = async () => { 36 | const $page = Page("Plugin"); 37 | $page.show = () => { 38 | actionStack.push({ 39 | id: pluginId, 40 | action: $page.hide, 41 | }); 42 | 43 | app.append($page); 44 | }; 45 | 46 | $page.onhide = function () { 47 | actionStack.remove(pluginId); 48 | }; 49 | 50 | try { 51 | if (!(await fsOperation(cacheFile).exists())) { 52 | await fsOperation(CACHE_STORAGE).createFile(pluginId); 53 | } 54 | 55 | await acode.initPlugin(pluginId, baseUrl, $page, { 56 | cacheFileUrl: await helpers.toInternalUri(cacheFile), 57 | cacheFile: fsOperation(cacheFile), 58 | firstInit: justInstalled, 59 | }); 60 | 61 | resolve(); 62 | } catch (error) { 63 | reject(error); 64 | } 65 | }; 66 | 67 | document.head.append($script); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /src/lib/projects.js: -------------------------------------------------------------------------------- 1 | const projects = { 2 | html() { 3 | acode.addIcon( 4 | "html-project-icon", 5 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABIUExURUdwTORPJuRPJuNOJeRPJuNQJ+RPJuNOJuNPJuROJeRPJuNOJuRPJuRQJONPJuNPJeVQI+NPJeROJuNPJuZPJ+NOJuRPJuNPJkmKsooAAAAXdFJOUwA6h5uxKGh/60/VE8BBll8izqXdDHT3jnqTYwAAAQRJREFUGBl9wY22azAURtGFhMS/Vvu9/5veHeGMMrhzAvoPkqBHgWTRo4XE6ZEjqfSoImn0qCGpZQYuBpmaJMpMXESZSFLIfLioZQoSLzMCzYmMJ+lkXsBbVx0bmR546YosSGqBUheBbJEUuFgkLWROpuMsSHJklYznTKYiK2WaHwWsMiXZRxceZpkP2SQzGO1mKGQmsigTwWvXQZSJZIVMDZ12K9QyBdks0wBDuUjvVw00MjNZJ1OxmWc2o0zHLkhynl9OUuDQyoS+jGx8PfZfSS2HXrvg6unVatdzcLrlOIy6NXIog26Ekj9+qlqdtNXkOSua/qvNt28Kbq1xfL/HuPLjH4f8MW+juHZUAAAAAElFTkSuQmCC", 6 | ); 7 | return { 8 | async files() { 9 | return { 10 | "index.html": 11 | '\n\n\n \n \n \n \n <%name%>\n\n\n\t

<%name%>

\n\n', 12 | "css/index.css": "", 13 | "js/index.js": "", 14 | }; 15 | }, 16 | icon: "html-project-icon", 17 | }; 18 | }, 19 | }; 20 | 21 | export default { 22 | list() { 23 | return Object.keys(projects).map((project) => ({ 24 | name: project, 25 | icon: projects[project]().icon, 26 | })); 27 | }, 28 | get(project) { 29 | return projects[project]?.(); 30 | }, 31 | /** 32 | * 33 | * @param {string} project Project name 34 | * @param {()=>Promise>} files Async function that returns a map of files 35 | * @param {string} iconSrc Icon source (data url) 36 | */ 37 | set(project, files, iconSrc) { 38 | const icon = `${project}-project-icon`; 39 | acode.addIcon(`${project}-project-icon`, iconSrc); 40 | projects[project] = () => ({ files, icon }); 41 | }, 42 | delete(project) { 43 | delete projects[project]; 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /src/lib/removeAds.js: -------------------------------------------------------------------------------- 1 | import purchaseListener from "handlers/purchase"; 2 | import helpers from "utils/helpers"; 3 | 4 | /** 5 | * Remove ads after purchase 6 | * @returns {Promise} 7 | */ 8 | export default function removeAds() { 9 | return new Promise((resolve, reject) => { 10 | iap.getProducts(["acode_pro_new"], (products) => { 11 | const [product] = products; 12 | 13 | iap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, reject)); 14 | 15 | iap.purchase( 16 | product.json, 17 | (code) => { 18 | // ignore 19 | }, 20 | (err) => { 21 | alert(strings.error, err); 22 | }, 23 | ); 24 | }); 25 | 26 | function onpurchase() { 27 | resolve(null); 28 | helpers.hideAd(true); 29 | localStorage.setItem("acode_pro", "true"); 30 | window.IS_FREE_VERSION = false; 31 | toast(strings["thank you :)"]); 32 | } 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/restoreFiles.js: -------------------------------------------------------------------------------- 1 | import EditorFile from "./editorFile"; 2 | 3 | /** 4 | * 5 | * @param {import('./editorFile').FileOptions[]} files 6 | * @param {(count: number)=>void} callback 7 | */ 8 | export default async function restoreFiles(files) { 9 | let rendered = false; 10 | 11 | await Promise.all( 12 | files.map(async (file, i) => { 13 | rendered = file.render; 14 | 15 | if (i === files.length - 1 && !rendered) { 16 | file.render = true; 17 | } 18 | 19 | const { filename, render = false } = file; 20 | const options = { 21 | ...file, 22 | render, 23 | emitUpdate: false, 24 | }; 25 | new EditorFile(filename, options); 26 | }), 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/restoreTheme.js: -------------------------------------------------------------------------------- 1 | import themes from "theme/list"; 2 | import Color from "utils/color"; 3 | import appSettings from "./settings"; 4 | 5 | let count = 0; 6 | 7 | /** 8 | * Restores the theme or darkens the status bar and navigation bar 9 | * Used when dialogs are opened which has mask that darkens the background 10 | * @param {boolean} darken Whether to darken the status bar and navigation bar 11 | * @returns 12 | */ 13 | export default function restoreTheme(darken = false) { 14 | if (!count && !darken) return; 15 | count += darken ? 1 : -1; 16 | if (darken !== !!count) return; 17 | if (darken && document.body.classList.contains("loading")) return; 18 | 19 | let themeName = DOES_SUPPORT_THEME ? appSettings.value.appTheme : "default"; 20 | let theme = themes.get(themeName); 21 | 22 | if (theme?.version !== "free" && IS_FREE_VERSION) { 23 | themeName = "default"; 24 | theme = themes.get(themeName); 25 | appSettings.value.appTheme = themeName; 26 | appSettings.update(); 27 | } 28 | 29 | if ( 30 | !theme.darkenedPrimaryColor || 31 | theme.darkenedPrimaryColor === theme.primaryColor 32 | ) { 33 | theme.darkenPrimaryColor(); 34 | } 35 | const color = darken ? theme.darkenedPrimaryColor : theme.primaryColor; 36 | const hexColor = Color(color).hex.toString(); 37 | system.setUiTheme(hexColor, theme.toJSON("hex")); 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/saveState.js: -------------------------------------------------------------------------------- 1 | import constants from "./constants"; 2 | import { addedFolder } from "./openFolder"; 3 | import appSettings from "./settings"; 4 | 5 | export default () => { 6 | if (!window.editorManager) return; 7 | 8 | const filesToSave = []; 9 | const folders = []; 10 | const { editor, files, activeFile } = editorManager; 11 | const { value: settings } = appSettings; 12 | 13 | files.forEach((file) => { 14 | if (file.type !== "editor") return; 15 | if (file.id === constants.DEFAULT_FILE_SESSION) return; 16 | 17 | const fileJson = { 18 | id: file.id, 19 | uri: file.uri, 20 | type: file.type, 21 | filename: file.filename, 22 | isUnsaved: file.isUnsaved, 23 | readOnly: file.readOnly, 24 | SAFMode: file.SAFMode, 25 | deletedFile: file.deletedFile, 26 | cursorPos: editor.getCursorPosition(), 27 | scrollTop: editor.session.getScrollTop(), 28 | scrollLeft: editor.session.getScrollLeft(), 29 | editable: file.editable, 30 | encoding: file.encoding, 31 | render: activeFile.id === file.id, 32 | folds: parseFolds(file.session.getAllFolds()), 33 | }; 34 | 35 | if (settings.rememberFiles || fileJson.isUnsaved) 36 | filesToSave.push(fileJson); 37 | }); 38 | 39 | if (settings.rememberFolders) { 40 | addedFolder.forEach((folder) => { 41 | const { url, saveState, title, listState, listFiles } = folder; 42 | folders.push({ 43 | url, 44 | opts: { 45 | saveState, 46 | name: title, 47 | listState, 48 | listFiles, 49 | }, 50 | }); 51 | }); 52 | } 53 | 54 | localStorage.files = JSON.stringify(filesToSave); 55 | localStorage.folders = JSON.stringify(folders); 56 | }; 57 | 58 | function parseFolds(folds) { 59 | if (!Array.isArray(folds)) return []; 60 | 61 | return folds 62 | .map((fold) => { 63 | if (!fold || !fold.range) return null; 64 | 65 | const { range, ranges, placeholder } = fold; 66 | return { 67 | range, 68 | ranges: parseFolds(ranges || []), 69 | placeholder, 70 | }; 71 | }) 72 | .filter(Boolean); 73 | } 74 | -------------------------------------------------------------------------------- /src/lib/selectionMenu.js: -------------------------------------------------------------------------------- 1 | const exec = (command) => { 2 | const { editor } = editorManager; 3 | editor.execCommand(command); 4 | 5 | if (command === "selectall") { 6 | editor.scrollToRow(Number.POSITIVE_INFINITY); 7 | editor.setSelection(true); 8 | editor.setMenu(true); 9 | } 10 | editor.focus(); 11 | }; 12 | 13 | const items = []; 14 | 15 | export default function selectionMenu() { 16 | return [ 17 | item( 18 | () => exec("copy"), 19 | , 20 | "selected", 21 | true, 22 | ), 23 | item(() => exec("cut"), , "selected"), 24 | item(() => exec("paste"), , "all"), 25 | item( 26 | () => exec("selectall"), 27 | , 28 | "all", 29 | true, 30 | ), 31 | item( 32 | (color) => acode.exec("insert-color", color), 33 | , 34 | "all", 35 | ), 36 | ...items, 37 | ]; 38 | } 39 | 40 | /** 41 | * 42 | * @param {function} onclick function to be called when the item is clicked 43 | * @param {string | HTMLElement} text content of the item 44 | * @param {'selected'|'all'} mode mode supported by the item 45 | * @param {boolean} readOnly whether to show the item in readOnly mode 46 | */ 47 | selectionMenu.add = (onclick, text, mode, readOnly) => { 48 | items.push(item(onclick, text, mode, readOnly)); 49 | }; 50 | 51 | selectionMenu.exec = (command) => { 52 | exec(command); 53 | }; 54 | 55 | function item(onclick, text, mode = "all", readOnly = false) { 56 | return { onclick, text, mode, readOnly }; 57 | } 58 | -------------------------------------------------------------------------------- /src/lib/showFileInfo.js: -------------------------------------------------------------------------------- 1 | import box from "dialogs/box"; 2 | import fsOperation from "fileSystem"; 3 | import { filesize } from "filesize"; 4 | import mustache from "mustache"; 5 | import Url from "utils/Url"; 6 | import helpers from "utils/helpers"; 7 | import $_fileInfo from "views/file-info.hbs"; 8 | import settings from "./settings"; 9 | 10 | /** 11 | * Shows file info 12 | * @param {String} [url] 13 | */ 14 | export default async function showFileInfo(url) { 15 | if (!url) url = editorManager.activeFile.uri; 16 | app.classList.add("title-loading"); 17 | try { 18 | const fs = fsOperation(url); 19 | const stats = await fs.stat(); 20 | 21 | let { name, lastModified, length, type } = stats; 22 | length = filesize(length); 23 | lastModified = new Date(lastModified).toLocaleString(); 24 | 25 | const protocol = Url.getProtocol(url); 26 | const fileType = type.toLowerCase(); 27 | const options = { 28 | name: name.slice(0, name.length - Url.extname(name).length), 29 | extension: Url.extname(name), 30 | lastModified, 31 | length, 32 | type, 33 | lang: strings, 34 | showUri: helpers.getVirtualPath(url), 35 | isEditor: 36 | fileType === "text/plain" || editorManager.activeFile.type === "editor", 37 | }; 38 | 39 | if (editorManager.activeFile.type === "editor") { 40 | const value = await fs.readFile(settings.value.defaultFileEncoding); 41 | options.lineCount = value.split(/\n+/).length; 42 | options.wordCount = value.split(/\s+|\n+/).length; 43 | 44 | if (/s?ftp:/.test(protocol)) { 45 | options.shareUri = Url.join(CACHE_STORAGE, name); 46 | const fs = fsOperation(options.shareUri); 47 | if (await fs.exists()) { 48 | await fs.delete(); 49 | } 50 | await fsOperation(CACHE_STORAGE).createFile(name, value); 51 | } 52 | } 53 | 54 | box("", mustache.render($_fileInfo, options), true).onclick((e) => { 55 | const $target = e.target; 56 | if ($target instanceof HTMLElement) { 57 | const action = $target.getAttribute("action"); 58 | 59 | if (action === "copy") { 60 | cordova.plugins.clipboard.copy($target.textContent); 61 | toast(strings["copied to clipboard"]); 62 | } 63 | } 64 | }); 65 | } catch (err) { 66 | helpers.error(err); 67 | } 68 | 69 | app.classList.remove("title-loading"); 70 | } 71 | -------------------------------------------------------------------------------- /src/lib/startAd.js: -------------------------------------------------------------------------------- 1 | let adUnitIdBanner = "ca-app-pub-5911839694379275/9157899592"; // Production 2 | let adUnitIdInterstitial = "ca-app-pub-5911839694379275/9570937608"; // Production 3 | let initialized = false; 4 | 5 | export default async function startAd() { 6 | if (!IS_FREE_VERSION || !admob) return; 7 | 8 | if (!initialized) { 9 | initialized = true; 10 | 11 | if (BuildInfo.type === "debug") { 12 | adUnitIdBanner = "ca-app-pub-3940256099942544/6300978111"; // Test 13 | adUnitIdInterstitial = "ca-app-pub-3940256099942544/5224354917"; // Test 14 | } 15 | } 16 | 17 | const consentStatus = await consent.getConsentStatus(); 18 | if (consentStatus === consent.ConsentStatus.Required) { 19 | await consent.requestInfoUpdate(); 20 | } 21 | 22 | const formStatus = await consent.getFormStatus(); 23 | if (formStatus === consent.FormStatus.Available) { 24 | const form = await consent.loadForm(); 25 | form.show(); 26 | } 27 | 28 | await admob.start(); 29 | 30 | const banner = new admob.BannerAd({ 31 | adUnitId: adUnitIdBanner, 32 | position: "bottom", 33 | }); 34 | 35 | const interstitial = new admob.InterstitialAd({ 36 | adUnitId: adUnitIdInterstitial, 37 | }); 38 | 39 | interstitial.load(); 40 | 41 | interstitial.on("dismiss", () => { 42 | interstitial.load(); 43 | }); 44 | window.ad = banner; 45 | window.iad = interstitial; 46 | } 47 | -------------------------------------------------------------------------------- /src/lib/systemConfiguration.js: -------------------------------------------------------------------------------- 1 | export const HARDKEYBOARDHIDDEN_NO = 1; 2 | export const HARDKEYBOARDHIDDEN_YES = 2; 3 | export const HARDKEYBOARDHIDDEN_UNDEFINED = 0; 4 | 5 | export const KEYBOARDHIDDEN_NO = 1; 6 | export const KEYBOARDHIDDEN_YES = 2; 7 | export const KEYBOARDHIDDEN_UNDEFINED = 0; 8 | 9 | export const KEYBOARD_12KEY = 3; 10 | export const KEYBOARD_QWERTY = 2; 11 | export const KEYBOARD_UNDEFINED = 0; 12 | export const KEYBOARD_NOKEYS = 1; 13 | 14 | export const NAVIGATIONHIDDEN_NO = 1; 15 | export const NAVIGATIONHIDDEN_YES = 2; 16 | export const NAVIGATIONHIDDEN_UNDEFINED = 0; 17 | 18 | export const NAVIGATION_DPAD = 2; 19 | export const NAVIGATION_TRACKBALL = 3; 20 | export const NAVIGATION_WHEEL = 4; 21 | export const NAVIGATION_UNDEFINED = 0; 22 | 23 | export const ORIENTATION_LANDSCAPE = 2; 24 | export const ORIENTATION_PORTRAIT = 1; 25 | export const ORIENTATION_SQUARE = 3; 26 | export const ORIENTATION_UNDEFINED = 0; 27 | 28 | export const TOUCHSCREEN_FINGER = 3; 29 | export const TOUCHSCREEN_NOTOUCH = 1; 30 | export const TOUCHSCREEN_STYLUS = 2; 31 | export const TOUCHSCREEN_UNDEFINED = 0; 32 | 33 | /** 34 | * @typedef {Object} SystemConfiguration 35 | * @property {number} hardKeyboardHidden 36 | * @property {number} navigationHidden 37 | * @property {number} keyboardHidden 38 | * @property {number} keyboardHeight 39 | * @property {number} orientation 40 | * @property {number} navigation 41 | * @property {number} fontScale 42 | * @property {number} keyboard 43 | * @property {string} locale 44 | */ 45 | 46 | /** 47 | * Get the system configuration 48 | * @returns {Promise} 49 | */ 50 | export function getSystemConfiguration() { 51 | return new Promise((resolve, reject) => { 52 | cordova.exec(resolve, reject, "System", "get-configuration", []); 53 | }); 54 | } 55 | 56 | export function isDeviceDarkTheme() { 57 | return window.matchMedia("(prefers-color-scheme: dark)").matches; 58 | } 59 | -------------------------------------------------------------------------------- /src/pages/about/about.js: -------------------------------------------------------------------------------- 1 | import "./about.scss"; 2 | import Page from "components/page"; 3 | import actionStack from "lib/actionStack"; 4 | import mustache from "mustache"; 5 | import helpers from "utils/helpers"; 6 | import _template from "./about.hbs"; 7 | 8 | export default function AboutInclude() { 9 | const $page = Page(strings.about.capitalize()); 10 | 11 | system.getWebviewInfo( 12 | (res) => render(res), 13 | () => render(), 14 | ); 15 | 16 | actionStack.push({ 17 | id: "about", 18 | action: $page.hide, 19 | }); 20 | 21 | $page.onhide = function () { 22 | actionStack.remove("about"); 23 | helpers.hideAd(); 24 | }; 25 | 26 | app.append($page); 27 | helpers.showAd(); 28 | 29 | function render(webview) { 30 | const $content = helpers.parseHTML( 31 | mustache.render(_template, { 32 | ...BuildInfo, 33 | webview, 34 | }), 35 | ); 36 | 37 | $page.classList.add("about-us"); 38 | $page.body = $content; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/pages/about/index.js: -------------------------------------------------------------------------------- 1 | //jshint ignore:start 2 | 3 | function About() { 4 | import(/* webpackChunkName: "about" */ "./about").then((res) => { 5 | res.default(); 6 | }); 7 | } 8 | export default About; 9 | -------------------------------------------------------------------------------- /src/pages/changelog/index.js: -------------------------------------------------------------------------------- 1 | function plugin({ id, installed }, onInstall, onUninstall) { 2 | import(/* webpackChunkName: "changelog" */ "./changelog").then((res) => { 3 | const Changelog = res.default; 4 | Changelog(); 5 | }); 6 | } 7 | 8 | export default plugin; 9 | -------------------------------------------------------------------------------- /src/pages/changelog/style.scss: -------------------------------------------------------------------------------- 1 | #changelog { 2 | max-width: 800px; 3 | margin: auto; 4 | overflow: auto; 5 | padding: 0 1rem; 6 | } 7 | 8 | .changelog-version-selector { 9 | display: flex; 10 | align-items: center; 11 | gap: 8px; 12 | background-color: var(--popup-background-color); 13 | border: none; 14 | border-radius: 8px; 15 | padding: 8px 16px; 16 | font-weight: 500; 17 | cursor: pointer; 18 | transition: all 0.2s ease; 19 | margin-right: 6px; 20 | 21 | &:hover { 22 | background-color: var(--secondary-color); 23 | } 24 | } 25 | 26 | .status-indicator { 27 | display: inline-block; 28 | width: 8px; 29 | height: 8px; 30 | border-radius: 50%; 31 | } 32 | .status-latest { 33 | background-color: #10b981; 34 | } 35 | .status-prerelease { 36 | background-color: var(--danger-color); 37 | } 38 | .status-current { 39 | background-color: var(--active-icon-color); 40 | } 41 | 42 | .loading { 43 | display: flex; 44 | justify-content: center; 45 | align-items: center; 46 | height: 100%; 47 | color: var(--primary-text-color); 48 | font-size: 1.2em; 49 | animation: pulse 1.5s ease-in-out infinite; 50 | } 51 | 52 | @keyframes pulse { 53 | 0% { 54 | opacity: 0.6; 55 | } 56 | 50% { 57 | opacity: 1; 58 | } 59 | 100% { 60 | opacity: 0.6; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/pages/customTheme/customTheme.scss: -------------------------------------------------------------------------------- 1 | #custom-theme { 2 | overflow: auto; 3 | 4 | .icon.color::before { 5 | content: ''; 6 | height: 30px; 7 | width: 30px; 8 | border: solid 1px rgb(255, 255, 255); 9 | border-radius: 50%; 10 | box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2); 11 | background-color: currentColor; 12 | } 13 | } -------------------------------------------------------------------------------- /src/pages/customTheme/index.js: -------------------------------------------------------------------------------- 1 | export default async function CustomTheme(...args) { 2 | const customTheme = ( 3 | await import(/* webpackChunkName: "customTheme" */ "./customTheme") 4 | ).default; 5 | customTheme(); 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/donate/donate.hbs: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/pages/donate/donate.scss: -------------------------------------------------------------------------------- 1 | #donate-page { 2 | overflow: auto; 3 | padding: 2rem; 4 | 5 | .products { 6 | display: grid; 7 | grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); 8 | gap: 1.5rem; 9 | 10 | .support-card { 11 | background: var(--primary-color); 12 | border-radius: 16px; 13 | padding: 2rem; 14 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); 15 | transition: 16 | transform 0.2s, 17 | box-shadow 0.2s; 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | text-align: center; 22 | &:hover { 23 | transform: translateY(-4px); 24 | box-shadow: 0 8px 12px rgba(0, 0, 0, 0.3); 25 | } 26 | .support-icon { 27 | width: 120px; 28 | height: 120px; 29 | margin-bottom: 1.5rem; 30 | img { 31 | width: 100%; 32 | min-height: 167px; 33 | height: auto; 34 | } 35 | svg { 36 | width: 100%; 37 | height: 100%; 38 | } 39 | } 40 | .support-info { 41 | font-size: 1.1rem; 42 | line-height: 1.5; 43 | color: color-mix(in oklch, var(--primary-text-color), black 20%); 44 | margin-bottom: 1.5rem; 45 | flex-grow: 1; 46 | } 47 | .author { 48 | font-size: 0.9rem; 49 | color: color-mix(in oklch, var(--primary-text-color), black 40%); 50 | margin-bottom: 1.5rem; 51 | } 52 | 53 | .donate-button { 54 | background: var(--button-background-color); 55 | color: var(--button-text-color); 56 | border: none; 57 | padding: 0.75rem 2rem; 58 | border-radius: 8px; 59 | font-weight: 500; 60 | cursor: pointer; 61 | transition: all 0.2s; 62 | width: 100%; 63 | &:hover { 64 | background: var(--button-active-color); 65 | transform: translateY(-2px); 66 | } 67 | } 68 | } 69 | .coffee .donate-button { 70 | background: #bb7d3d; 71 | } 72 | 73 | .coffee .donate-button:hover { 74 | background: #d3934f; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/pages/donate/index.js: -------------------------------------------------------------------------------- 1 | export default function Donate() { 2 | import(/* webpackChunkName: "donate" */ "./donate").then((res) => { 3 | const Donate = res.default; 4 | Donate(); 5 | }); 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/donate/product.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{#isCoffee}} 4 | 5 | {{/isCoffee}} 6 | {{^isCoffee}} 7 | 8 | 9 | 10 | {{/isCoffee}} 11 |
12 |

{{description}}

13 | {{#author}} 14 |

- {{author}}

15 | {{/author}} 16 | 19 |
20 | -------------------------------------------------------------------------------- /src/pages/fileBrowser/add-menu-home.hbs: -------------------------------------------------------------------------------- 1 |
  • {{add path}}
  • 2 |
  • {{add ftp}}
  • 3 |
  • {{add sftp}}
  • -------------------------------------------------------------------------------- /src/pages/fileBrowser/add-menu.hbs: -------------------------------------------------------------------------------- 1 |
  • {{new file}}
  • 2 |
  • {{new folder}}
  • 3 |
  • {{new project}}
  • 4 |
  • {{import project zip}}
  • 5 | -------------------------------------------------------------------------------- /src/pages/fileBrowser/fileBrowser.hbs: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    ⓘ {{info}}
    4 |
    -------------------------------------------------------------------------------- /src/pages/fileBrowser/list.hbs: -------------------------------------------------------------------------------- 1 |
      {{#list}} 2 | {{#.}} 3 |
    • 16 | 20 | 21 |
      22 | {{name}} 23 |
      24 | {{url}} 25 |
    • 26 | {{/.}} 27 | {{/list}} 28 |
    29 | -------------------------------------------------------------------------------- /src/pages/fileBrowser/util.js: -------------------------------------------------------------------------------- 1 | import multiPrompt from "dialogs/multiPrompt"; 2 | import helpers from "utils/helpers"; 3 | 4 | export default { 5 | /** 6 | * 7 | * @param {Array} list 8 | * @param {String} name 9 | * @param {String} url 10 | * @param {Object} extra 11 | */ 12 | pushFolder(list, name, url, extra = {}) { 13 | list.push({ 14 | url: url, 15 | name: name, 16 | isDirectory: true, 17 | parent: true, 18 | type: "dir", 19 | ...extra, 20 | }); 21 | }, 22 | /** 23 | * Save a new path using storage access framework 24 | * @param {String} name 25 | * @returns {Promise<{name: String, uri: String, uuid: string}>} 26 | */ 27 | async addPath(name, uuid) { 28 | const res = await multiPrompt( 29 | strings["add path"], 30 | [ 31 | { 32 | id: "uri", 33 | placeholder: strings["select folder"], 34 | type: "text", 35 | required: true, 36 | readOnly: true, 37 | onclick() { 38 | sdcard.getStorageAccessPermission( 39 | uuid, 40 | (res) => { 41 | const $name = tag.get("#name"); 42 | if (!$name.value && res) { 43 | const name = window 44 | .decodeURIComponent(res) 45 | ?.split(":") 46 | .pop() 47 | ?.split("/") 48 | .pop(); 49 | $name.value = name ?? ""; 50 | } 51 | this.value = res; 52 | }, 53 | (err) => { 54 | helpers.error(err); 55 | }, 56 | ); 57 | }, 58 | }, 59 | { 60 | id: "name", 61 | placeholder: strings["folder name"], 62 | type: "text", 63 | required: true, 64 | value: name ?? "", 65 | }, 66 | ], 67 | "https://acode.app/faqs/224761680", 68 | ); 69 | 70 | if (!res) return; 71 | 72 | return { 73 | name: res.name, 74 | uri: res.uri, 75 | uuid: helpers.uuid(), 76 | }; 77 | }, 78 | }; 79 | -------------------------------------------------------------------------------- /src/pages/plugin/index.js: -------------------------------------------------------------------------------- 1 | function plugin({ id, installed, install }, onInstall, onUninstall) { 2 | import(/* webpackChunkName: "plugins" */ "./plugin").then((res) => { 3 | const Plugin = res.default; 4 | Plugin(id, installed, onInstall, onUninstall, install); 5 | }); 6 | } 7 | 8 | export default plugin; 9 | -------------------------------------------------------------------------------- /src/pages/plugins/index.js: -------------------------------------------------------------------------------- 1 | function plugins(updates) { 2 | import(/* webpackChunkName: "plugins" */ './plugins').then( 3 | (res) => { 4 | const Plugins = res.default; 5 | Plugins(updates); 6 | }, 7 | ); 8 | } 9 | 10 | export default plugins; 11 | -------------------------------------------------------------------------------- /src/pages/problems/index.js: -------------------------------------------------------------------------------- 1 | function plugin({ id, installed }, onInstall, onUninstall) { 2 | import(/* webpackChunkName: "problems" */ "./problems").then((res) => { 3 | const Problems = res.default; 4 | Problems(); 5 | }); 6 | } 7 | 8 | export default plugin; 9 | -------------------------------------------------------------------------------- /src/pages/problems/style.scss: -------------------------------------------------------------------------------- 1 | #problems { 2 | height: 100%; 3 | width: 100%; 4 | 5 | .single-file { 6 | padding: 10px; 7 | box-sizing: border-box; 8 | 9 | summary { 10 | height: 40px; 11 | width: 100%; 12 | font-weight: bold; 13 | line-height: 40px; 14 | } 15 | 16 | .problem { 17 | display: flex; 18 | padding: 5px; 19 | border-bottom: solid 1px var(--border-color); 20 | 21 | * { 22 | pointer-events: none; 23 | } 24 | 25 | .icon { 26 | margin: 0 5px; 27 | font-size: 0.9rem; 28 | color: var(--primary-text-color) !important; 29 | } 30 | 31 | .problem-line { 32 | display: flex; 33 | align-items: center; 34 | margin-left: 10px; 35 | font-size: 0.9rem; 36 | } 37 | 38 | .problem-message { 39 | flex: 1; 40 | 41 | &[data-type='error'] { 42 | color: var(--danger-color); 43 | } 44 | 45 | &[data-type='warning'] { 46 | color: var(--error-text-color); 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/pages/quickTools/index.js: -------------------------------------------------------------------------------- 1 | export default async function QuickToolsSettings() { 2 | const { default: Settings } = await import("./quickTools.js"); 3 | Settings(); 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/quickTools/style.scss: -------------------------------------------------------------------------------- 1 | #quicktools-settings { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | .row { 6 | height: 40px; 7 | display: flex; 8 | flex-direction: row; 9 | 10 | .icon { 11 | flex: 1; 12 | 13 | &:active { 14 | background-color: inherit !important; 15 | color: var(--active-color); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/pages/themeSetting/index.js: -------------------------------------------------------------------------------- 1 | export default function themeSetting(...args) { 2 | import(/* webpackChunkName: "themeSetting" */ "./themeSetting").then( 3 | (module) => { 4 | module.default(...args); 5 | }, 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/themeSetting/themeSetting.scss: -------------------------------------------------------------------------------- 1 | #theme-setting { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | #theme-preview:not(:empty) { 6 | height: 120px; 7 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.2); 8 | box-shadow: 0 0 4px var(--box-shadow-color); 9 | pointer-events: none; 10 | } 11 | 12 | .icon.color.custom::before { 13 | background-color: var(--primary-color); 14 | } 15 | 16 | #theme-list { 17 | flex: 1; 18 | } 19 | } -------------------------------------------------------------------------------- /src/palettes/changeEncoding/index.js: -------------------------------------------------------------------------------- 1 | import palette from "components/palette"; 2 | import confirm from "dialogs/confirm"; 3 | import fsOperation from "fileSystem"; 4 | import encodings from "utils/encodings"; 5 | 6 | export default function changeEncoding() { 7 | palette(generateHints, reopenWithNewEncoding, strings.encoding); 8 | } 9 | 10 | function generateHints() { 11 | return Object.keys(encodings).map((id) => { 12 | const encoding = encodings[id]; 13 | const aliases = encoding.aliases.join(", "); 14 | return { 15 | value: id, 16 | text: `
    17 | ${encoding.label} 18 | ${aliases} 19 |
    `, 20 | }; 21 | }); 22 | } 23 | 24 | export async function reopenWithNewEncoding(encoding) { 25 | const file = editorManager.activeFile; 26 | const editor = editorManager.editor; 27 | const message = strings["change encoding"] 28 | .replace("{file}", file.filename) 29 | .replace("{encoding}", encoding); 30 | const confirmation = await confirm(strings.warning, message); 31 | 32 | if (!confirmation) return; 33 | 34 | const text = await fsOperation(file.uri).readFile(encoding); 35 | const cursorPosition = editor.getCursorPosition(); 36 | 37 | file.encoding = encoding; 38 | file.session.setValue(text); 39 | file.isUnsaved = false; 40 | file.markChanged = false; 41 | editor.moveCursorToPosition(cursorPosition); 42 | 43 | editorManager.onupdate("encoding"); 44 | editorManager.emit("update", "encoding"); 45 | } 46 | -------------------------------------------------------------------------------- /src/palettes/changeMode/index.js: -------------------------------------------------------------------------------- 1 | import palette from "components/palette"; 2 | import Path from "utils/Path"; 3 | 4 | export default function changeMode() { 5 | palette(generateHints, onselect, strings["syntax highlighting"]); 6 | } 7 | 8 | function generateHints() { 9 | const { modes } = ace.require("ace/ext/modelist"); 10 | 11 | return modes.map(({ caption, mode, extensions }) => { 12 | return { 13 | value: mode, 14 | text: `
    15 | ${caption} 16 | ${mode} 17 |
    `, 18 | }; 19 | }); 20 | } 21 | 22 | function onselect(mode) { 23 | const activeFile = editorManager.activeFile; 24 | 25 | let modeAssociated; 26 | try { 27 | modeAssociated = JSON.parse(localStorage.modeassoc || "{}"); 28 | } catch (error) { 29 | modeAssociated = {}; 30 | } 31 | 32 | modeAssociated[Path.extname(activeFile.filename)] = mode; 33 | localStorage.modeassoc = JSON.stringify(modeAssociated); 34 | 35 | activeFile.setMode(mode); 36 | } 37 | -------------------------------------------------------------------------------- /src/palettes/changeTheme/style.scss: -------------------------------------------------------------------------------- 1 | .theme-item { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | padding: 4px; 6 | width: 100%; 7 | 8 | span { 9 | font-size: 1rem; 10 | } 11 | 12 | .current { 13 | color: var(--error-text-color); 14 | background-color: var(--primary-color); 15 | border-radius: 5px; 16 | padding: 2px 6px; 17 | font-size: 0.8rem; 18 | margin-left: 8px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/palettes/commandPalette/index.js: -------------------------------------------------------------------------------- 1 | import palette from "components/palette"; 2 | import helpers from "utils/helpers"; 3 | 4 | export default async function commandPalette() { 5 | const recentCommands = RecentlyUsedCommands(); 6 | const { editor } = editorManager; 7 | const commands = Object.values(editor.commands.commands); 8 | 9 | const isEditorFocused = editor.isFocused(); 10 | 11 | palette(generateHints, onselect, strings["type command"], () => { 12 | if (isEditorFocused) editor.focus(); 13 | }); 14 | 15 | function generateHints() { 16 | const hints = []; 17 | 18 | commands.forEach(({ name, description, bindKey }) => { 19 | /** 20 | * @param {boolean} recentlyUsed Is the command recently used 21 | * @returns {{value: string, text: string}} 22 | */ 23 | const item = (recentlyUsed) => ({ 24 | value: name, 25 | text: `${description ?? name}${bindKey?.win ?? ""}`, 26 | }); 27 | if (recentCommands.commands.includes(name)) { 28 | hints.unshift(item(true)); 29 | return; 30 | } 31 | hints.push(item()); 32 | }); 33 | 34 | return hints; 35 | } 36 | 37 | function onselect(value) { 38 | const command = commands.find(({ name }) => name === value); 39 | if (!command) return; 40 | recentCommands.push(value); 41 | command.exec(editorManager.editor); 42 | } 43 | } 44 | 45 | function RecentlyUsedCommands() { 46 | return { 47 | /** 48 | * @returns {string[]} 49 | */ 50 | get commands() { 51 | return ( 52 | helpers.parseJSON(localStorage.getItem("recentlyUsedCommands")) || [] 53 | ); 54 | }, 55 | /** 56 | * Saves command to recently used commands 57 | * @param {string} command Command name 58 | * @returns {void} 59 | */ 60 | push(command) { 61 | const { commands } = this; 62 | if (commands.length > 10) { 63 | commands.pop(); 64 | } 65 | if (commands.includes(command)) { 66 | commands.splice(commands.indexOf(command), 1); 67 | } 68 | commands.unshift(command); 69 | localStorage.setItem("recentlyUsedCommands", JSON.stringify(commands)); 70 | }, 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /src/plugins/Executor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.foxdebug.acode.exec", 3 | "version": "1.0.0", 4 | "description": "Execute linux commands", 5 | "cordova": { 6 | "id": "com.foxdebug.acode.exec", 7 | "platforms": [ 8 | "android" 9 | ] 10 | }, 11 | "keywords": [ 12 | "ecosystem:cordova", 13 | "cordova-android" 14 | ], 15 | "author": "", 16 | "license": "ISC" 17 | } 18 | -------------------------------------------------------------------------------- /src/plugins/Executor/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | Executor 3 | -------------------------------------------------------------------------------- /src/plugins/Executor/www/Executor.js: -------------------------------------------------------------------------------- 1 | var exec = require('cordova/exec'); 2 | 3 | module.exports.execute = function (cmd,success,failure) { 4 | exec(success, failure, 'Executor', 'exec', [cmd]); 5 | } -------------------------------------------------------------------------------- /src/plugins/browser/android/com/foxdebug/browser/Plugin.java: -------------------------------------------------------------------------------- 1 | package com.foxdebug.browser; 2 | 3 | import android.content.Intent; 4 | import com.foxdebug.browser.BrowserActivity; 5 | import org.apache.cordova.CallbackContext; 6 | import org.apache.cordova.CordovaPlugin; 7 | import org.json.JSONArray; 8 | import org.json.JSONException; 9 | import org.json.JSONObject; 10 | 11 | public class Plugin extends CordovaPlugin { 12 | 13 | @Override 14 | public boolean execute( 15 | String action, 16 | JSONArray args, 17 | CallbackContext callbackContext 18 | ) throws JSONException { 19 | if (action.equals("open")) { 20 | String url = args.getString(0); 21 | JSONObject theme = args.getJSONObject(1); 22 | boolean onlyConsole = args.optBoolean(2, false); 23 | String themeString = theme.toString(); 24 | Intent intent = new Intent(cordova.getActivity(), BrowserActivity.class); 25 | 26 | intent.putExtra("url", url); 27 | intent.putExtra("theme", themeString); 28 | intent.putExtra("onlyConsole", onlyConsole); 29 | cordova.getActivity().startActivity(intent); 30 | callbackContext.success("Opened browser"); 31 | return true; 32 | } 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/plugins/browser/index.js: -------------------------------------------------------------------------------- 1 | import settings from 'lib/settings'; 2 | import themes from 'theme/list'; 3 | 4 | const SERVICE = 'Browser'; 5 | 6 | function open(url, isConsole = false) { 7 | const ACTION = 'open'; 8 | const success = () => { }; 9 | const error = () => { }; 10 | const theme = themes.get(settings.value.appTheme).toJSON('hex'); 11 | cordova.exec(success, error, SERVICE, ACTION, [url, theme, isConsole]); 12 | } 13 | 14 | export default { 15 | open, 16 | }; -------------------------------------------------------------------------------- /src/plugins/browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-browser", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } -------------------------------------------------------------------------------- /src/plugins/browser/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | cordova-plugin-browser 5 | In app browser 6 | Apache 2.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/plugins/browser/res/android/menu_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/plugins/browser/res/android/menu_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/plugins/browser/res/android/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /src/plugins/browser/utils/updatePackage.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const configXML = path.resolve(__dirname, "../../../config.xml"); 5 | const menuJava = path.resolve(__dirname, "../../../platforms/android/app/src/main/java/com/foxdebug/browser/Menu.java"); 6 | const repeatChar = (char, times) => { 7 | let res = ""; 8 | while (--times >= 0) res += char; 9 | return res; 10 | }; 11 | 12 | try { 13 | const config = fs.readFileSync(configXML, "utf8"); 14 | const fileData = fs.readFileSync(menuJava, "utf8"); 15 | const appName = /widget id="([0-9a-zA-Z\.\-_]*)"/.exec(config)[1].split(".").pop(); 16 | const newFileData = fileData.replace(/(import com\.foxdebug\.)(acode|acodefree)(.R;)/, `$1${appName}$3`); 17 | fs.writeFileSync(menuJava, newFileData); 18 | 19 | const msg = `==== Changed package to com.foxdebug.${appName} ====`; 20 | 21 | console.log(""); 22 | console.log(repeatChar("=", msg.length)); 23 | console.log(msg); 24 | console.log(repeatChar("=", msg.length)); 25 | console.log(""); 26 | 27 | } catch (error) { 28 | console.error(error); 29 | process.exit(1); 30 | } -------------------------------------------------------------------------------- /src/plugins/ftp/LICENSE.md: -------------------------------------------------------------------------------- 1 | - GoldRaccoon is under [original author's license](https://github.com/albertodebortoli/GoldRaccoon/blob/master/LICENSE.markdown) 2 | - ftp4j is under [LGPL](http://opensource.org/licenses/LGPL-2.1) 3 | - All other codes (writen by me) are under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 4 | -------------------------------------------------------------------------------- /src/plugins/ftp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-ftp", 3 | "version": "1.1.1", 4 | "description": "This cordova plugin is created to use ftp (client) in web/js.", 5 | "cordova": { 6 | "id": "cordova-plugin-ftp", 7 | "platforms": [ 8 | "android", 9 | "ios" 10 | ] 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/xfally/cordova-plugin-ftp" 15 | }, 16 | "keywords": [ 17 | "cordova", 18 | "ftp", 19 | "cordova-android", 20 | "cordova-ios" 21 | ], 22 | "author": "pax", 23 | "license": "Apache-2.0", 24 | "bugs": { 25 | "url": "https://github.com/xfally/cordova-plugin-ftp/issues" 26 | }, 27 | "homepage": "https://github.com/xfally/cordova-plugin-ftp", 28 | "scripts": { 29 | "test": "echo \"Error: no test specified\" && exit 1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/plugins/ftp/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | Ftp 5 | Cordova Ftp Plugin 6 | MIT 7 | cordova,ftp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/plugins/iap/index.d.ts: -------------------------------------------------------------------------------- 1 | interface Iap { 2 | getProducts( 3 | skuList: Array, 4 | onSuccess: (skuList: Array) => void, 5 | onError: (err: String) => Error, 6 | ): void; 7 | setPurchaseUpdatedListener( 8 | onSuccess: (purchase: Object) => void, 9 | onError: (err: string) => void, 10 | ): void; 11 | startConnection( 12 | onSuccess: (responseCode: number) => void, 13 | onError: (err: string) => void, 14 | ): void; 15 | consume( 16 | purchaseToken: string, 17 | onSuccess: (responseCode: number) => void, 18 | onError: (err: string) => void, 19 | ): void; 20 | purchase( 21 | skuId: string, 22 | onSuccess: (responseCode: number) => void, 23 | onError: (err: string) => void, 24 | ): void; 25 | getPurchases( 26 | onSuccess: (purchaseList: Array) => void, 27 | onError: (err: string) => void, 28 | ): void; 29 | OK: 0; 30 | BILLING_UNAVAILABLE: 3; 31 | DEVELOPER_ERROR: 5; 32 | ERROR: 6; 33 | FEATURE_NOT_SUPPORTED: -2; 34 | ITEM_ALREADY_OWNED: 7; 35 | ITEM_NOT_OWNED: 8; 36 | ITEM_UNAVAILABLE: 4; 37 | SERVICE_DISCONNECTED: -1; 38 | SERVICE_TIMEOUT: -3; 39 | SERVICE_UNAVAILABLE: 2; 40 | USER_CANCELED: 1; 41 | PURCHASE_STATE_PURCHASED: 1; 42 | PURCHASE_STATE_PENDING: 2; 43 | PURCHASE_STATE_UNKNOWN: 0; 44 | } 45 | 46 | declare var iap: Iap; 47 | -------------------------------------------------------------------------------- /src/plugins/iap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-iap", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC" 12 | } -------------------------------------------------------------------------------- /src/plugins/iap/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | cordova-plugin-iap 5 | In app purchase for Android. 6 | Apache 2.0 7 | cordova,plugin,in app purchase 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/plugins/iap/www/plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getProducts: function (productIds, onSuccess, onFail) { 3 | cordova.exec(onSuccess, onFail, 'Iap', 'getProducts', [productIds]); 4 | }, 5 | setPurchaseUpdatedListener: function (onSuccess, onFail) { 6 | cordova.exec(onSuccess, onFail, 'Iap', 'setPurchaseUpdatedListener', []); 7 | }, 8 | startConnection: function (onSuccess, onFail) { 9 | cordova.exec(onSuccess, onFail, 'Iap', 'startConnection', []); 10 | }, 11 | consume: function (purchaseToken, onSuccess, onFail) { 12 | cordova.exec(onSuccess, onFail, 'Iap', 'consume', [purchaseToken]); 13 | }, 14 | purchase: function (productId, onSuccess, onFail) { 15 | cordova.exec(onSuccess, onFail, 'Iap', 'purchase', [productId]); 16 | }, 17 | getPurchases: function (onSuccess, onFail) { 18 | cordova.exec(onSuccess, onFail, 'Iap', 'getPurchases', []); 19 | }, 20 | acknowledgePurchase: function (purchaseToken, onSuccess, onFail) { 21 | cordova.exec(onSuccess, onFail, 'Iap', 'acknowledgePurchase', [purchaseToken]); 22 | }, 23 | BILLING_UNAVAILABLE: 3, 24 | DEVELOPER_ERROR: 5, 25 | ERROR: 6, 26 | FEATURE_NOT_SUPPORTED: -2, 27 | ITEM_ALREADY_OWNED: 7, 28 | ITEM_NOT_OWNED: 8, 29 | ITEM_UNAVAILABLE: 4, 30 | OK: 0, 31 | SERVICE_DISCONNECTED: -1, 32 | SERVICE_TIMEOUT: -3, 33 | SERVICE_TIMEOUT: 2, 34 | USER_CANCELED: 1, 35 | PURCHASE_STATE_PURCHASED: 1, 36 | PURCHASE_STATE_PENDING: 2, 37 | PURCHASE_STATE_UNKNOWN: 0, 38 | }; -------------------------------------------------------------------------------- /src/plugins/sdcard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "", 3 | "bundleDependencies": false, 4 | "deprecated": false, 5 | "description": "Using this plugin, cordova apps can check for external storages and write/modify files.", 6 | "keywords": [ 7 | "cordova", 8 | "plugin", 9 | "sdcard", 10 | "external storage", 11 | "access external storage", 12 | "android" 13 | ], 14 | "license": "ISC", 15 | "main": "index.js", 16 | "name": "cordova-plugin-sdcard", 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "version": "1.1.0" 21 | } -------------------------------------------------------------------------------- /src/plugins/sdcard/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | cordova-plugin-sdcard 5 | Choose folder to get read/write access to the document tree 6 | Apache 2.0 7 | cordova,plugin,Folder 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/plugins/server/index.d.ts: -------------------------------------------------------------------------------- 1 | interface Server{ 2 | stop(onSuccess: () => void, onError: (error: any) => void): void; 3 | send(id: string, data: any, onSuccess: () => void, onError: (error: any) => void): void; 4 | port: number; 5 | } 6 | 7 | declare var CreateServer: (port: number, onSuccess: (msg: any) => void, onError: (err: any) => void) => Server; -------------------------------------------------------------------------------- /src/plugins/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-server", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT" 6 | } -------------------------------------------------------------------------------- /src/plugins/server/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | Webserver for Cordova Apps 9 | HTTP server, webserver 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/plugins/server/www/server.js: -------------------------------------------------------------------------------- 1 | module.exports = function (port, onRequest, onError) { 2 | cordova.exec(onRequest, onError, 'Server', 'start', [port]); 3 | return { 4 | stop: function (onSuccess, onError) { 5 | onSuccess = onSuccess || function () { }; 6 | onError = onError || console.error.bind(console); 7 | cordova.exec(onSuccess, onError, 'Server', 'stop', [port]); 8 | }, 9 | send: function (req_id, data, onSuccess, onError) { 10 | onSuccess = onSuccess || function () { }; 11 | onError = onError || console.error.bind(console); 12 | cordova.exec(onSuccess, onError, 'Server', 'send', [port, req_id, data]); 13 | }, 14 | setOnRequestHandler: function (onRequest, onError) { 15 | onError = onError || console.error.bind(console); 16 | cordova.exec(onRequest, onError, 'Server', 'setOnRequestHandler', [port]); 17 | }, 18 | port: port 19 | } 20 | } -------------------------------------------------------------------------------- /src/plugins/sftp/LICENSE.md: -------------------------------------------------------------------------------- 1 | - GoldRaccoon is under [original author's license](https://github.com/albertodebortoli/GoldRaccoon/blob/master/LICENSE.markdown) 2 | - ftp4j is under [LGPL](http://opensource.org/licenses/LGPL-2.1) 3 | - All other codes (writen by me) are under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 4 | -------------------------------------------------------------------------------- /src/plugins/sftp/README.md: -------------------------------------------------------------------------------- 1 | # cordova-plugin-sftp 2 | 3 | ## Description 4 | 5 | This cordova plugin is created to use sftp (client) in Cordova apps. 6 | 7 | Support **Android** platform for now. 8 | 9 | You can do the following things: 10 | 11 | - Execute a command 12 | - Download a file (with percent info) 13 | - Upload a file (with percent info) 14 | - Connect using private key or password 15 | 16 | ## Installation 17 | 18 | ```sh 19 | $ cordova plugin add cordova-plugin-sftp 20 | $ cordova prepare 21 | ``` 22 | -------------------------------------------------------------------------------- /src/plugins/sftp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-sftp", 3 | "version": "1.1.1", 4 | "description": "This cordova plugin is created to use sftp (client) in web/js.", 5 | "cordova": { 6 | "id": "cordova-plugin-sftp", 7 | "platforms": [ 8 | "android" 9 | ] 10 | }, 11 | "keywords": [ 12 | "cordova", 13 | "sftp", 14 | "cordova-android" 15 | ], 16 | "author": "pax", 17 | "license": "Apache-2.0", 18 | "scripts": { 19 | "test": "echo \"Error: no test specified\" && exit 1" 20 | } 21 | } -------------------------------------------------------------------------------- /src/plugins/sftp/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | Sftp 7 | Cordova Sftp Plugin 8 | MIT 9 | cordova,ftp 10 | https://github.com/foxdebug/cordova-plugin-sftp.git 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/plugins/sftp/www/sftp.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | exec: function (command, onSuccess, onFail) { 3 | cordova.exec(onSuccess, onFail, 'Sftp', 'exec', [command]); 4 | }, 5 | connectUsingPassword: function (host, port, username, password, onSuccess, onFail) { 6 | if (typeof port != 'number') { 7 | throw new Error('Port must be number'); 8 | } 9 | 10 | port = Number.parseInt(port); 11 | cordova.exec(onSuccess, onFail, 'Sftp', 'connectUsingPassword', [host, port, username, password]); 12 | }, 13 | connectUsingKeyFile: function (host, port, username, keyFile, passphrase, onSuccess, onFail) { 14 | if (typeof port != 'number') { 15 | throw new Error('Port must be number'); 16 | } 17 | 18 | port = Number.parseInt(port); 19 | cordova.exec(onSuccess, onFail, 'Sftp', 'connectUsingKeyFile', [host, port, username, keyFile, passphrase]); 20 | }, 21 | getFile: function (filename, localFilename, onSuccess, onFail) { 22 | cordova.exec(onSuccess, onFail, 'Sftp', 'getFile', [filename, localFilename]); 23 | }, 24 | putFile: function (filename, localFilename, onSuccess, onFail) { 25 | cordova.exec(onSuccess, onFail, 'Sftp', 'putFile', [filename, localFilename]); 26 | }, 27 | lsDir: function (path, onSuccess, onFail) { 28 | cordova.exec(onSuccess, onFail, 'Sftp', 'lsDir', [path]); 29 | }, 30 | stat: function (path, onSuccess, onFail) { 31 | cordova.exec(onSuccess, onFail, 'Sftp', 'stat', [path]); 32 | }, 33 | mkdir: function (path, onSuccess, onFail) { 34 | cordova.exec(onSuccess, onFail, 'Sftp', 'mkdir', [path]); 35 | }, 36 | rm: function (path, force, recurse, onSuccess, onFail) { 37 | cordova.exec(onSuccess, onFail, 'Sftp', 'rm', [path, force, recurse]); 38 | }, 39 | createFile: function (path, content, onSuccess, onFail) { 40 | cordova.exec(onSuccess, onFail, 'Sftp', 'createFile', [path, content]); 41 | }, 42 | rename: function (oldpath, newpath, onSuccess, onFail) { 43 | cordova.exec(onSuccess, onFail, 'Sftp', 'rename', [oldpath, newpath]); 44 | }, 45 | pwd: function (onSuccess, onFail) { 46 | cordova.exec(onSuccess, onFail, 'Sftp', 'pwd', []); 47 | }, 48 | close: function (onSuccess, onFail) { 49 | cordova.exec(onSuccess, onFail, 'Sftp', 'close', []); 50 | }, 51 | isConnected: function (onSuccess, onFail) { 52 | cordova.exec(onSuccess, onFail, 'Sftp', 'isConnected', []); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /src/plugins/system/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-system", 3 | "version": "1.0.3", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /src/plugins/system/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | cordova-plugin-system 5 | Utility methods for Android. 6 | Apache 2.0 7 | cordova,plugin,system 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/plugins/system/readme.md: -------------------------------------------------------------------------------- 1 | # Util plugin for cordova apps 2 | 3 | Using this plugin, cordova apps can: 4 | 5 | - Enable/disable full screen 6 | - Share file 7 | - Get webview information 8 | - Send email 9 | - Clear cache 10 | 11 | ## Installation 12 | -------------------------------------------------------------------------------- /src/plugins/system/res/android/file_provider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/plugins/system/res/android/icon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/src/plugins/system/res/android/icon.ttf -------------------------------------------------------------------------------- /src/plugins/system/utils/changeProvider.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | changeProvider(reset) { 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | 7 | const androidManifest = path.resolve(__dirname, "../../../platforms/android/app/src/main/AndroidManifest.xml"); 8 | const configXML = path.resolve(__dirname, "../../../config.xml"); 9 | const repeatChar = (char, times) => { 10 | let res = ""; 11 | while (--times >= 0) res += char; 12 | return res; 13 | }; 14 | 15 | try { 16 | const fileData = fs.readFileSync(configXML, "utf8"); 17 | const manifest = fs.readFileSync(androidManifest, "utf8"); 18 | const ID = reset ? "com.foxdebug" : /widget id="([0-9a-zA-Z\.\-_]*)"/.exec(fileData)[1]; 19 | const newFileData = manifest.replace( 20 | /(android:authorities=")([0-9a-zA-Z\.\-_]*)(")/, 21 | `$1${reset ? "com.foxdebug" : ID}.provider$3` 22 | ); 23 | fs.writeFileSync(androidManifest, newFileData); 24 | 25 | const msg = "==== Changed provider to " + ID + ".provider ===="; 26 | 27 | console.log(""); 28 | console.log(repeatChar("=", msg.length)); 29 | console.log(msg); 30 | console.log(repeatChar("=", msg.length)); 31 | console.log(""); 32 | 33 | } catch (error) { 34 | console.error(error); 35 | process.exit(1); 36 | } 37 | } 38 | }; -------------------------------------------------------------------------------- /src/plugins/system/utils/fixProvider.js: -------------------------------------------------------------------------------- 1 | const { 2 | changeProvider 3 | } = require("./changeProvider"); 4 | 5 | changeProvider(); -------------------------------------------------------------------------------- /src/plugins/system/utils/resetProvider.js: -------------------------------------------------------------------------------- 1 | const { 2 | changeProvider 3 | } = require("./changeProvider"); 4 | 5 | changeProvider(true); -------------------------------------------------------------------------------- /src/settings/filesSettings.js: -------------------------------------------------------------------------------- 1 | import settingsPage from "components/settingsPage"; 2 | import appSettings from "lib/settings"; 3 | 4 | export default function filesSettings() { 5 | const title = strings.settings; 6 | const values = appSettings.value.fileBrowser; 7 | 8 | const items = [ 9 | { 10 | key: "sortByName", 11 | text: strings["sort by name"], 12 | checkbox: values.sortByName, 13 | }, 14 | { 15 | key: "showHiddenFiles", 16 | text: strings["show hidden files"], 17 | checkbox: values.showHiddenFiles, 18 | info: "Show hidden files and folders. (Start with .)", 19 | }, 20 | ]; 21 | 22 | return settingsPage(title, items, callback); 23 | 24 | function callback(key, value) { 25 | appSettings.value.fileBrowser[key] = value; 26 | appSettings.update(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/settings/formatterSettings.js: -------------------------------------------------------------------------------- 1 | import settingsPage from "components/settingsPage"; 2 | import appSettings from "lib/settings"; 3 | 4 | export default function formatterSettings(languageName) { 5 | const title = strings.formatter; 6 | const values = appSettings.value; 7 | const { formatters } = acode; 8 | const { modes } = ace.require("ace/ext/modelist"); 9 | 10 | const items = modes.map((mode) => { 11 | const { name, caption } = mode; 12 | const formatterID = values.formatter[name] || null; 13 | const extensions = mode.extensions.split("|"); 14 | const options = acode.getFormatterFor(extensions); 15 | 16 | return { 17 | key: name, 18 | text: caption, 19 | icon: `file file_type_default file_type_${name}`, 20 | value: formatterID, 21 | valueText: (value) => { 22 | const formatter = formatters.find(({ id }) => id === value); 23 | if (formatter) { 24 | return formatter.name; 25 | } 26 | return strings.none; 27 | }, 28 | select: options, 29 | }; 30 | }); 31 | 32 | const page = settingsPage(title, items, callback, "separate"); 33 | page.show(languageName); 34 | 35 | function callback(key, value) { 36 | values.formatter[key] = value; 37 | appSettings.update(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/settings/helpSettings.js: -------------------------------------------------------------------------------- 1 | import settingsPage from "components/settingsPage"; 2 | 3 | export default function help() { 4 | const title = strings.help; 5 | const items = [ 6 | { 7 | key: "help", 8 | text: strings.help, 9 | link: "https://telegram.me/foxdebug_acode", 10 | }, 11 | { 12 | key: "faqs", 13 | text: strings.faqs, 14 | link: "https://acode.app/faqs", 15 | }, 16 | { 17 | key: "bug_report", 18 | text: strings.bug_report, 19 | link: "https://github.com/deadlyjack/Acode/issues", 20 | }, 21 | ]; 22 | 23 | const page = settingsPage(title, items, () => {}, "separate"); 24 | page.show(); 25 | } 26 | -------------------------------------------------------------------------------- /src/settings/previewSettings.js: -------------------------------------------------------------------------------- 1 | import Checkbox from "components/checkbox"; 2 | import settingsPage from "components/settingsPage"; 3 | import appSettings from "lib/settings"; 4 | 5 | export default function previewSettings() { 6 | const values = appSettings.value; 7 | const title = strings["preview settings"]; 8 | const PORT_REGEX = 9 | /^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/; 10 | const items = [ 11 | { 12 | key: "previewPort", 13 | text: strings["preview port"], 14 | value: values.previewPort, 15 | prompt: strings["preview port"], 16 | promptType: "number", 17 | promptOptions: { 18 | test(value) { 19 | return PORT_REGEX.test(value); 20 | }, 21 | }, 22 | }, 23 | { 24 | key: "serverPort", 25 | text: strings["server port"], 26 | value: values.serverPort, 27 | prompt: strings["server port"], 28 | promptType: "number", 29 | promptOptions: { 30 | test(value) { 31 | return PORT_REGEX.test(value); 32 | }, 33 | }, 34 | }, 35 | { 36 | key: "previewMode", 37 | text: strings["preview mode"], 38 | value: values.previewMode, 39 | select: [ 40 | [appSettings.PREVIEW_MODE_BROWSER, strings.browser], 41 | [appSettings.PREVIEW_MODE_INAPP, strings.inapp], 42 | ], 43 | }, 44 | { 45 | key: "host", 46 | text: strings.host, 47 | value: values.host, 48 | prompt: strings.host, 49 | promptType: "text", 50 | promptOptions: { 51 | test(value) { 52 | try { 53 | new URL(`http://${value}:${values.previewPort}`); 54 | return true; 55 | } catch (error) { 56 | return false; 57 | } 58 | }, 59 | }, 60 | }, 61 | { 62 | key: "disableCache", 63 | text: strings["disable in-app-browser caching"], 64 | checkbox: values.disableCache, 65 | }, 66 | { 67 | key: "useCurrentFileForPreview", 68 | text: strings["should_use_current_file_for_preview"], 69 | checkbox: !!values.useCurrentFileForPreview, 70 | }, 71 | { 72 | key: "showConsoleToggler", 73 | text: strings["show console toggler"], 74 | checkbox: values.showConsoleToggler, 75 | }, 76 | { 77 | note: strings["preview settings note"], 78 | }, 79 | ]; 80 | 81 | return settingsPage(title, items, callback); 82 | 83 | function callback(key, value) { 84 | appSettings.update({ 85 | [key]: value, 86 | }); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/settings/scrollSettings.js: -------------------------------------------------------------------------------- 1 | import settingsPage from "components/settingsPage"; 2 | import constants from "lib/constants"; 3 | import appSettings from "lib/settings"; 4 | 5 | export default function scrollSettings() { 6 | const values = appSettings.value; 7 | const title = strings["scroll settings"]; 8 | 9 | const items = [ 10 | { 11 | key: "scrollSpeed", 12 | text: strings["scroll speed"], 13 | value: values.scrollSpeed, 14 | valueText: getScrollSpeedString, 15 | select: [ 16 | [constants.SCROLL_SPEED_FAST_X2, `${strings.fast} x2`], 17 | [constants.SCROLL_SPEED_FAST, strings.fast], 18 | [constants.SCROLL_SPEED_NORMAL, strings.normal], 19 | [constants.SCROLL_SPEED_SLOW, strings.slow], 20 | ], 21 | }, 22 | { 23 | key: "reverseScrolling", 24 | text: strings["reverse scrolling"], 25 | checkbox: values.reverseScrolling, 26 | }, 27 | { 28 | key: "diagonalScrolling", 29 | text: strings["diagonal scrolling"], 30 | checkbox: values.diagonalScrolling, 31 | }, 32 | { 33 | key: "scrollbarSize", 34 | text: strings["scrollbar size"], 35 | value: values.scrollbarSize, 36 | valueText: (size) => `${size}px`, 37 | select: [5, 10, 15, 20], 38 | }, 39 | { 40 | key: "textWrap", 41 | text: strings["text wrap"], 42 | checkbox: values.textWrap, 43 | }, 44 | ]; 45 | 46 | return settingsPage(title, items, callback); 47 | 48 | function callback(key, value) { 49 | appSettings.update({ 50 | [key]: value, 51 | }); 52 | } 53 | } 54 | 55 | function getScrollSpeedString(speed) { 56 | switch (speed) { 57 | case constants.SCROLL_SPEED_FAST: 58 | return strings.fast; 59 | case constants.SCROLL_SPEED_SLOW: 60 | return strings.slow; 61 | case constants.SCROLL_SPEED_FAST_X2: 62 | return `${strings.fast} x2`; 63 | case constants.SCROLL_SPEED_NORMAL: 64 | return strings.normal; 65 | default: 66 | return strings.normal; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/settings/searchSettings.js: -------------------------------------------------------------------------------- 1 | import settingsPage from "../components/settingsPage"; 2 | import appSettings from "../lib/settings"; 3 | 4 | export default function searchSettings() { 5 | const title = strings.search; 6 | const values = appSettings.value.search; 7 | const items = [ 8 | { 9 | key: "caseSensitive", 10 | text: strings["case sensitive"], 11 | checkbox: values.caseSensitive, 12 | }, 13 | { 14 | key: "regExp", 15 | text: strings["regular expression"], 16 | checkbox: values.regExp, 17 | }, 18 | { 19 | key: "wholeWord", 20 | text: strings["whole word"], 21 | checkbox: values.wholeWord, 22 | }, 23 | ]; 24 | 25 | return settingsPage(title, items, callback); 26 | 27 | function callback(key, value) { 28 | values[key] = value; 29 | appSettings.update(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/sidebarApps/files/style.scss: -------------------------------------------------------------------------------- 1 | .container.files { 2 | &:empty { 3 | height: 100%; 4 | width: 100%; 5 | display: flex; 6 | align-items: center; 7 | justify-content: center; 8 | 9 | &::after { 10 | content: attr(data-msg); 11 | font-weight: 600; 12 | } 13 | } 14 | 15 | /* Make the container horizontally scrollable */ 16 | overflow-x: auto !important; 17 | max-width: 100%; 18 | 19 | &::-webkit-scrollbar { 20 | height: 5px; 21 | width: 5px; 22 | } 23 | 24 | &::-webkit-scrollbar-thumb { 25 | background-color: rgba(0, 0, 0, 0.3); 26 | border-radius: 3px; 27 | } 28 | 29 | &::-webkit-scrollbar-corner { 30 | background: transparent; 31 | } 32 | 33 | scrollbar-width: thin; 34 | scrollbar-color: rgba(0, 0, 0, 0.3) transparent; 35 | 36 | .list { 37 | min-width: 100%; 38 | width: max-content; 39 | max-width: none; 40 | } 41 | 42 | ul { 43 | min-width: 100%; 44 | width: max-content; 45 | overflow-x: visible !important; 46 | max-width: none; 47 | margin-left: 0; 48 | &::-webkit-scrollbar-corner { 49 | background: transparent; 50 | } 51 | } 52 | 53 | li { 54 | min-width: 100%; 55 | width: max-content; 56 | } 57 | 58 | .tile { 59 | > .text { 60 | white-space: nowrap !important; 61 | overflow: visible !important; 62 | width: max-content !important; 63 | text-overflow: clip !important; 64 | } 65 | } 66 | 67 | /* Add indent guides for folders (excluding first level) */ 68 | .list.collapsible > ul > .collapsible > ul { 69 | position: relative; 70 | padding-left: 24px; 71 | 72 | &::before { 73 | content: ""; 74 | position: absolute; 75 | left: 14px; 76 | top: 0; 77 | height: 100%; 78 | width: 1px; 79 | background: var(--border-color); 80 | z-index: 0; 81 | } 82 | 83 | /* Add guides for deeper nesting */ 84 | .collapsible > ul { 85 | padding-left: 24px; 86 | 87 | &::before { 88 | content: ""; 89 | position: absolute; 90 | left: 14px; 91 | top: 0; 92 | height: 100%; 93 | width: 1px; 94 | background: var(--border-color); 95 | z-index: 0; 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/sidebarApps/notification/index.js: -------------------------------------------------------------------------------- 1 | import NotificationManager from "lib/notificationManager"; 2 | import "./style.scss"; 3 | import Sidebar from "components/sidebar"; 4 | 5 | /**@type {HTMLElement} */ 6 | let container; 7 | /** @type {HTMLElement} */ 8 | let $notificationContainer = null; 9 | 10 | let notificationManager; 11 | 12 | export default [ 13 | "notifications", // icon 14 | "notification", // id 15 | strings["notifications"], // title 16 | initApp, // init function 17 | false, // prepend 18 | onSelected, // onSelected function 19 | ]; 20 | 21 | const $header = ( 22 |
    23 |
    24 | {strings["notifications"]} 25 | notificationManager.clearAll()} 28 | > 29 |
    30 |
    31 | ); 32 | 33 | /** 34 | * Initialize files app 35 | * @param {HTMLElement} el 36 | */ 37 | function initApp(el) { 38 | container = el; 39 | container.classList.add("notifications"); 40 | container.content = $header; 41 | $notificationContainer = ( 42 |
    43 | ); 44 | container.append($notificationContainer); 45 | 46 | notificationManager = new NotificationManager(); 47 | 48 | Sidebar.on("show", onSelected); 49 | } 50 | 51 | /** 52 | * On selected handler for files app 53 | * @param {HTMLElement} el 54 | */ 55 | function onSelected(el) { 56 | const $scrollableLists = container.getAll(":scope .scroll[data-scroll-top]"); 57 | $scrollableLists.forEach(($el) => { 58 | $el.scrollTop = $el.dataset.scrollTop; 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /src/styles/fileInfo.scss: -------------------------------------------------------------------------------- 1 | #file-info { 2 | overflow-x: hidden; 3 | padding: 0.8rem; 4 | 5 | .file-header { 6 | margin-bottom: 1.5rem; 7 | 8 | .file-name { 9 | font-size: 1.25rem; 10 | font-weight: 500; 11 | color: var(--popup-text-color); 12 | display: flex; 13 | align-items: center; 14 | gap: 0.5rem; 15 | flex-wrap: wrap; 16 | 17 | .name-part { 18 | max-width: 100%; 19 | overflow: hidden; 20 | text-overflow: ellipsis; 21 | white-space: nowrap; 22 | position: relative; 23 | 24 | &:hover { 25 | overflow: visible; 26 | white-space: normal; 27 | word-break: break-all; 28 | z-index: 1; 29 | border-radius: 4px; 30 | background: var(--popup-background-color); 31 | } 32 | } 33 | .file-extension { 34 | background: color-mix( 35 | in srgb, 36 | var(--button-background-color) 40%, 37 | transparent 38 | ); 39 | padding: 0.25rem 0.5rem; 40 | border-radius: 4px; 41 | font-size: 0.875rem; 42 | color: var(--active-color); 43 | font-weight: 600; 44 | letter-spacing: 0.5px; 45 | text-transform: uppercase; 46 | flex-shrink: 0; 47 | } 48 | } 49 | } 50 | 51 | .info-grid { 52 | display: grid; 53 | grid-template-columns: repeat(2, 1fr); 54 | gap: 1.5rem; 55 | } 56 | 57 | .info-item { 58 | display: flex; 59 | flex-direction: column; 60 | gap: 0.5rem; 61 | 62 | .info-label { 63 | font-size: 0.875rem; 64 | color: color-mix(in srgb, var(--popup-text-color) 60%, transparent); 65 | text-transform: uppercase; 66 | letter-spacing: 0.5px; 67 | } 68 | 69 | .info-value { 70 | font-size: 1rem; 71 | color: var(--popup-text-color); 72 | font-weight: 500; 73 | } 74 | } 75 | 76 | .path-section { 77 | margin-top: 1.5rem; 78 | padding-top: 1.5rem; 79 | border-top: 1px solid var(--border-color); 80 | 81 | .path-value { 82 | word-break: break-all; 83 | } 84 | } 85 | 86 | @media (max-width: 480px) { 87 | .file-card { 88 | padding: 1.5rem; 89 | } 90 | 91 | .info-grid { 92 | grid-template-columns: 1fr; 93 | gap: 1rem; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/styles/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin circular-loader($size) { 2 | display: block; 3 | width: $size; 4 | height: $size; 5 | border-radius: 50%; 6 | border: 3px solid rgb(153, 153, 255); 7 | border: 3px solid var(--popup-icon-color); 8 | border-top-color: transparent; 9 | animation: circular-loader-animation 1s linear infinite; 10 | box-sizing: border-box; 11 | } 12 | 13 | @mixin loader($size) { 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | 18 | &::after { 19 | content: ''; 20 | @include circular-loader($size); 21 | } 22 | } 23 | 24 | @mixin linear-loader($width, $height) { 25 | &::after { 26 | content: ''; 27 | display: block; 28 | position: absolute; 29 | bottom: 0; 30 | left: 0; 31 | right: 0; 32 | margin: 0 auto; 33 | width: 30%; 34 | height: 2px; 35 | background-color: rgb(0, 0, 0); 36 | animation: linear-loader-animation ease 1s infinite; 37 | border-radius: 1px; 38 | } 39 | } 40 | 41 | @mixin active-icon() { 42 | background-color: inherit !important; 43 | color: rgba(0, 0, 0, 0.2); 44 | color: var(--active-color); 45 | text-shadow: 0 0 0.5rem var(--box-shadow-color); 46 | } 47 | 48 | @mixin icon-badge() { 49 | position: relative; 50 | 51 | &::after { 52 | content: '•'; 53 | position: absolute; 54 | top: 5px; 55 | right: 5px; 56 | color: #ffda0c; 57 | font-size: 1.4em; 58 | height: fit-content; 59 | line-height: 4px; 60 | text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5); 61 | } 62 | } -------------------------------------------------------------------------------- /src/styles/overrideAceStyle.scss: -------------------------------------------------------------------------------- 1 | .ace_mobile-menu, 2 | .ace_tooltip.ace_doc-tooltip { 3 | display: none !important; 4 | } 5 | 6 | .ace_editor { 7 | &[data-font="Fira Code"] { 8 | font-feature-settings: 'liga' on, 'calt' on; 9 | -webkit-font-feature-settings: 'liga' on, 'calt' on; 10 | -webkit-font-smoothing: antialiased; 11 | text-rendering: optimizeLegibility; 12 | unicode-bidi: isolate; 13 | } 14 | } 15 | 16 | .ace_tooltip { 17 | background-color: rgb(255, 255, 255); 18 | background-color: var(--secondary-color); 19 | color: rgb(37, 37, 37); 20 | color: var(--secondary-text-color); 21 | max-width: 68%; 22 | white-space: pre-wrap; 23 | } 24 | 25 | main .ace_editor { 26 | textarea { 27 | user-select: none !important; 28 | pointer-events: none !important; 29 | transform: translate(-100000px, -10000px) !important; 30 | } 31 | } 32 | 33 | .ace-container { 34 | height: 100%; 35 | } 36 | 37 | .ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line { 38 | background-color: var(--active-color); 39 | } 40 | 41 | .ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight { 42 | color: var(--popup-active-color); 43 | } 44 | 45 | .ace_dark.ace_editor.ace_autocomplete { 46 | border: 1px solid var(--popup-border-color); 47 | box-shadow: 2px 3px 5px var(--box-shadow-color); 48 | line-height: 1.4; 49 | background: var(--primary-color); 50 | color: var(--primary-text-color); 51 | } 52 | 53 | .ace_hidden-cursors .ace_cursor { 54 | opacity: 0.8 !important; 55 | } -------------------------------------------------------------------------------- /src/utils/color/hex.js: -------------------------------------------------------------------------------- 1 | import Rgb from "./rgb"; 2 | 3 | export default class Hex { 4 | r = 0; 5 | g = 0; 6 | b = 0; 7 | a = 1; 8 | 9 | /** 10 | * Hex color constructor 11 | * @param {number} r Red value in hexadecimals 12 | * @param {number} g Green value in hexadecimals 13 | * @param {number} b Blue value in hexadecimals 14 | * @param {number} a Alpha value between 0 and 1 15 | */ 16 | constructor(r, g, b, a = 1) { 17 | this.r = r; 18 | this.g = g; 19 | this.b = b; 20 | this.a = a; 21 | } 22 | 23 | /** 24 | * Creates a Hex color from an RGB color 25 | * @param {Rgb} rgb 26 | */ 27 | static fromRgb(rgb) { 28 | const { r, g, b, a } = rgb; 29 | return new Hex(r, g, b, a * 255); 30 | } 31 | 32 | /** 33 | * Gets the color as a string 34 | * @param {boolean} alpha Whether to include alpha 35 | */ 36 | toString(alpha) { 37 | let r = this.r.toString(16); 38 | let g = this.g.toString(16); 39 | let b = this.b.toString(16); 40 | let a = this.a.toString(16); 41 | 42 | if (r.length === 1) r = `0${r}`; 43 | if (g.length === 1) g = `0${g}`; 44 | if (b.length === 1) b = `0${b}`; 45 | if (a.length === 1) a = `0${a}`; 46 | 47 | const hex = () => `#${r}${g}${b}`.toUpperCase(); 48 | const hexA = () => `#${r}${g}${b}${a}`.toUpperCase(); 49 | 50 | if (alpha === undefined) { 51 | return this.a === 255 ? hex() : hexA(); 52 | } 53 | 54 | return alpha ? hexA() : hex(); 55 | } 56 | 57 | /** 58 | * Gets the color as an RGB object 59 | * @returns {Rgb} 60 | */ 61 | get rgb() { 62 | return new Rgb(this.r, this.g, this.b, this.a); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/utils/color/index.js: -------------------------------------------------------------------------------- 1 | import Hex from "./hex"; 2 | import Hsl from "./hsl"; 3 | import Rgb from "./rgb"; 4 | 5 | /**@type {CanvasRenderingContext2D} */ 6 | const ctx = ().getContext("2d", { 7 | willReadFrequently: true, 8 | }); 9 | 10 | export default (/**@type {string}*/ color) => { 11 | return new Color(color); 12 | }; 13 | 14 | class Color { 15 | rgb = new Rgb(0, 0, 0, 1); 16 | 17 | /** 18 | * Create a color from a string 19 | * @param {string} color 20 | */ 21 | constructor(color) { 22 | const { canvas } = ctx; 23 | ctx.clearRect(0, 0, canvas.width, canvas.height); 24 | ctx.fillStyle = color; 25 | ctx.fillRect(0, 0, 1, 1); 26 | const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data; 27 | this.rgb = new Rgb(r, g, b, a / 255); 28 | } 29 | 30 | darken(ratio) { 31 | const hsl = Hsl.fromRgb(this.rgb); 32 | hsl.l = Math.max(0, hsl.l - ratio * hsl.l); 33 | this.rgb = hsl.rgb; 34 | return this; 35 | } 36 | 37 | lighten(ratio) { 38 | const hsl = Hsl.fromRgb(this.rgb); 39 | hsl.l = Math.min(1, hsl.l + ratio * hsl.l); 40 | this.rgb = hsl.rgb; 41 | return this; 42 | } 43 | 44 | get isDark() { 45 | return this.luminance < 0.5; 46 | } 47 | 48 | get isLight() { 49 | return this.luminance >= 0.5; 50 | } 51 | 52 | get lightness() { 53 | return this.hsl.l; 54 | } 55 | 56 | /** 57 | * Get the luminance of the color 58 | * Returns a value between 0 and 1 59 | */ 60 | get luminance() { 61 | let { r, g, b } = this.rgb; 62 | r /= 255; 63 | g /= 255; 64 | b /= 255; 65 | return 0.2126 * r + 0.7152 * g + 0.0722 * b; 66 | } 67 | 68 | get hex() { 69 | return Hex.fromRgb(this.rgb); 70 | } 71 | 72 | get hsl() { 73 | return Hsl.fromRgb(this.rgb); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/utils/color/rgb.js: -------------------------------------------------------------------------------- 1 | export default class Rgb { 2 | r = 0; 3 | g = 0; 4 | b = 0; 5 | a = 1; 6 | 7 | constructor(r, g, b, a = 1) { 8 | this.r = r; 9 | this.g = g; 10 | this.b = b; 11 | this.a = a; 12 | } 13 | 14 | /** 15 | * Get the color as a string 16 | * @param {boolean} alpha Whether to include alpha channel 17 | * @returns 18 | */ 19 | toString(alpha) { 20 | const rgb = () => `rgb(${this.r}, ${this.g}, ${this.b})`; 21 | const rgba = () => `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`; 22 | if (alpha === undefined) { 23 | return this.a === 1 ? rgb() : rgba(); 24 | } 25 | return alpha ? rgba() : rgb(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/taskManager.js: -------------------------------------------------------------------------------- 1 | export default class TaskManager { 2 | /** 3 | * @typedef {'linear'|'parallel'} TaskManagerMode 4 | */ 5 | 6 | /** 7 | * @type {Array<()=>Promise>} 8 | */ 9 | #queue = []; 10 | /** 11 | * @type {TaskManagerMode} 12 | */ 13 | #mode = "linear"; 14 | /** 15 | * @type {boolean} 16 | */ 17 | #busy = false; 18 | /** 19 | * @type {TaskCallback[]} 20 | */ 21 | #listeners = []; 22 | #count = 0; 23 | 24 | /** 25 | * Create new TaskManager 26 | * @param {TaskManagerMode} mode 27 | */ 28 | constructor(mode) { 29 | this.#mode = mode; 30 | 31 | this.queueTask = this.queueTask.bind(this); 32 | } 33 | 34 | /** 35 | * Add task to queue 36 | * @param {()=>Promise} task 37 | */ 38 | async queueTask(task) { 39 | this.#queue.push(task); 40 | this.#execNext(); 41 | return new Promise((resolve, reject) => { 42 | const listener = (t, result, error) => { 43 | if (t !== task) return; 44 | 45 | this.#listeners = this.#listeners.filter((l) => l !== listener); 46 | 47 | if (error) reject(error); 48 | else resolve(result); 49 | }; 50 | 51 | this.#listeners.push(listener); 52 | }); 53 | } 54 | 55 | async #execNext() { 56 | if (this.#mode === "linear" && this.#busy) { 57 | return; 58 | } 59 | 60 | const task = this.#queue.shift(); 61 | if (!task) return; 62 | 63 | let result; 64 | let error; 65 | 66 | try { 67 | this.#busy = true; 68 | const id = this.#count++; 69 | result = await task(id); 70 | } catch (err) { 71 | error = err; 72 | } finally { 73 | this.#busy = false; 74 | } 75 | 76 | this.#listeners.forEach((l) => l(task, result, error)); 77 | if (this.#mode === "linear") this.#execNext(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/views/file-info.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | {{name}} 5 | {{extension}} 6 |
    7 |
    8 | 9 |
    10 |
    11 | {{lang.size}} 12 | {{length}} 13 |
    14 |
    15 | {{lang.type}} 16 | {{type}} 17 |
    18 |
    19 | {{lang.last modified}} 20 | {{lastModified}} 21 |
    22 | {{#isEditor}} 23 |
    24 | {{lang.line count}} 25 | {{lineCount}} 26 |
    27 |
    28 | {{lang.word count}} 29 | {{wordCount}} 30 |
    31 | {{/isEditor}} 32 |
    33 | 34 |
    35 |
    36 | {{lang.path}} 37 | {{showUri}} 38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /src/views/markdown.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{filename}} 9 | 10 | 11 | {{{html}}} 12 | 13 | -------------------------------------------------------------------------------- /src/views/menu.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | {{new file}} 3 | 4 |
  • 5 |
  • 6 | {{save}} 7 | 8 |
  • 9 |
  • 10 | {{save as}} 11 | 12 |
  • 13 |
  • 14 | {{files}} 15 | 16 |
  • 17 |
  • 18 | {{close file}} 19 | 20 |
  • 21 |
  • 22 | {{open recent}} 23 | 24 |
  • 25 |
  • 26 | {{find file}} 27 | 28 |
  • 29 |
  • 30 | {{console}} 31 | 32 |
  • 33 |
    34 |
  • 35 | {{settings}} 36 | 37 |
  • 38 |
  • 39 | {{help}} 40 | 41 |
  • 42 |
    43 |
  • 44 | {{exit}} 45 | 46 |
  • -------------------------------------------------------------------------------- /src/views/rating.hbs: -------------------------------------------------------------------------------- 1 | 5 |
    6 | 7 | 8 | 9 | 10 | 11 |
    -------------------------------------------------------------------------------- /utils/extra-icons/cart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /utils/extra-icons/scale.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /utils/extra-icons/tag.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /utils/extra-icons/verified.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /utils/loadStyles.js: -------------------------------------------------------------------------------- 1 | const fs = require("node:fs"); 2 | const path = require("node:path"); 3 | 4 | const WWW = path.resolve(__dirname, "../www"); 5 | const CSS = path.resolve(WWW, "css/build"); 6 | const CSS_PATH = "./css/build/"; 7 | 8 | const cssFiles = fs.readdirSync(CSS).filter((file) => file.endsWith(".css")); 9 | const htmlFiles = fs.readdirSync(WWW).filter((file) => file.endsWith(".html")); 10 | 11 | try { 12 | for (let htmlFile of htmlFiles) { 13 | loadStyles(path.resolve(WWW, htmlFile)); 14 | } 15 | } catch (error) { 16 | console.error(error); 17 | process.exit(1); 18 | } 19 | 20 | console.log("Styles loaded"); 21 | process.exit(0); 22 | 23 | /** 24 | * 25 | * @param {String} htmlFile 26 | */ 27 | function loadStyles(htmlFile) { 28 | let styles = ""; 29 | 30 | for (let cssFile of cssFiles) { 31 | styles += `\n`; 32 | } 33 | 34 | styles += "\n\n"; 35 | 36 | const rgx = 37 | /([^<]*(?:<(?!!--styles_end-->)[^<]*)*)\n*/gim; 38 | let html = fs.readFileSync(htmlFile, "utf8"); 39 | html = html.replace(rgx, ""); 40 | html = html.replace("", styles + ""); 41 | fs.writeFileSync(htmlFile, html); 42 | } 43 | -------------------------------------------------------------------------------- /utils/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | platform="$1" 4 | app="$2" 5 | mode="$3" 6 | webpackmode="development" 7 | cordovamode="" 8 | 9 | if [ -z "$platform" ] 10 | then 11 | platform="android" 12 | fi 13 | 14 | if [ -z "$mode" ] 15 | then 16 | mode="d" 17 | fi 18 | 19 | if [ -z "$app" ] 20 | then 21 | app="paid" 22 | fi 23 | 24 | if [ "$mode" = "p" ] || [ "$mode" = "prod" ] 25 | then 26 | mode="p" 27 | webpackmode="production" 28 | cordovamode="--release" 29 | fi 30 | 31 | RED='' 32 | NC='' 33 | script1="node ./utils/config.js $mode $app" 34 | script2="webpack --progress --mode $webpackmode " 35 | script3="node ./utils/loadStyles.js" 36 | script4="cordova build $platform $cordovamode" 37 | eval " 38 | echo \"${RED}$script1${NC}\"; 39 | $script1; 40 | echo \"${RED}$script2${NC}\"; 41 | $script2&& 42 | echo \"${RED}$script3${NC}\"; 43 | $script3; 44 | echo \"${RED}$script4${NC}\"; 45 | $script4; 46 | " 47 | -------------------------------------------------------------------------------- /utils/scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | platform_rm=$1 4 | platform_add=$2 5 | 6 | if [ -z "$platform_rm" ] 7 | then 8 | platform_rm="android" 9 | fi 10 | 11 | if [ -z "$platform_add" ] 12 | then 13 | platform_add="$platform_rm" 14 | fi 15 | 16 | eval " 17 | cordova platform rm $platform_rm; 18 | cordova platform add $platform_add 19 | " -------------------------------------------------------------------------------- /utils/scripts/plugin.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | if [ -z "$2" ] 4 | then 5 | eval "cordova plugin rm $1; cordova plugin add $1" 6 | else 7 | eval "cordova plugin rm $1; cordova plugin add $2" 8 | fi -------------------------------------------------------------------------------- /utils/scripts/setup.sh: -------------------------------------------------------------------------------- 1 | echo "Setting up the project..." 2 | 3 | npm install 4 | cordova platform add android@10 5 | cordova prepare 6 | mkdir -p www/css/build www/js/build -------------------------------------------------------------------------------- /utils/scripts/start.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | platform="$1" 4 | app="$2" 5 | mode="$3" 6 | webpackmode="development" 7 | cordovamode="" 8 | 9 | if [ -z "$platform" ] 10 | then 11 | platform="android" 12 | fi 13 | 14 | if [ -z "$mode" ] 15 | then 16 | mode="d" 17 | fi 18 | 19 | if [ -z "$app" ] 20 | then 21 | app="paid" 22 | fi 23 | 24 | if [ "$mode" = "p" ] 25 | then 26 | webpackmode="production" 27 | cordovamode="--release" 28 | fi 29 | 30 | RED='' 31 | NC='' 32 | script1="node ./utils/config.js $mode $app" 33 | script2="webpack --progress --mode $webpackmode " 34 | script3="node ./utils/loadStyles.js" 35 | script4="cordova run $platform $cordovamode" 36 | eval " 37 | echo \"${RED}$script1${NC}\"; 38 | $script1; 39 | echo \"${RED}$script2${NC}\"; 40 | $script2&& 41 | echo \"${RED}$script3${NC}\"; 42 | $script3; 43 | echo \"${RED}$script4${NC}\"; 44 | $script4 45 | " -------------------------------------------------------------------------------- /utils/setup.js: -------------------------------------------------------------------------------- 1 | // setup acode for the first time 2 | // 1. install dependencies 3 | // 2. add cordova platform android@10.2 4 | // 3. install cordova plugins 5 | // cordova-plugin-buildinfo 6 | // cordova-plugin-device 7 | // cordova-plugin-file 8 | // all the plugins in ./src/plugins 9 | 10 | const { execSync } = require("node:child_process"); 11 | const fs = require("node:fs"); 12 | const path = require("node:path"); 13 | const PLATFORM_FILES = [".DS_Store"]; 14 | 15 | execSync("npm install", { stdio: "inherit" }); 16 | try { 17 | execSync("cordova platform add android", { stdio: "inherit" }); 18 | } catch (error) { 19 | // ignore 20 | } 21 | 22 | try { 23 | execSync("mkdir -p www/css/build www/js/build", { stdio: "inherit" }); 24 | } catch (error) { 25 | console.log( 26 | "Failed to create www/css/build & www/js/build directories (You may Try after reading The Error)", 27 | error, 28 | ); 29 | } 30 | 31 | execSync("cordova plugin add cordova-plugin-buildinfo", { stdio: "inherit" }); 32 | execSync("cordova plugin add cordova-plugin-device", { stdio: "inherit" }); 33 | execSync("cordova plugin add cordova-plugin-file", { stdio: "inherit" }); 34 | 35 | const plugins = fs.readdirSync(path.join(__dirname, "../src/plugins")); 36 | plugins.forEach((plugin) => { 37 | if (PLATFORM_FILES.includes(plugin) || plugin.startsWith(".")) return; 38 | execSync(`cordova plugin add ./src/plugins/${plugin}`, { stdio: "inherit" }); 39 | }); 40 | -------------------------------------------------------------------------------- /utils/updateAce.js: -------------------------------------------------------------------------------- 1 | const fs = require("node:fs"); 2 | const path = require("node:path"); 3 | 4 | const sourceDir = "./ace-builds/src-min"; 5 | const destDir = "./www/js/ace"; 6 | 7 | function updateAce() { 8 | try { 9 | // Remove existing destination directory if it exists 10 | if (fs.existsSync(destDir)) { 11 | fs.rmSync(destDir, { recursive: true }); 12 | console.log("Removed existing destination directory"); 13 | } 14 | 15 | // Create destination directory 16 | fs.mkdirSync(destDir, { recursive: true }); 17 | console.log("Created new destination directory"); 18 | 19 | // Read all files from source directory 20 | const files = fs.readdirSync(sourceDir); 21 | 22 | files.forEach((file) => { 23 | const sourcePath = path.join(sourceDir, file); 24 | const destPath = path.join(destDir, file); 25 | 26 | // Skip snippets directory and worker files 27 | if (file === "snippets" || file.startsWith("worker-")) { 28 | console.log(`Skipping: ${file}`); 29 | return; 30 | } 31 | 32 | // Copy if it's a file 33 | if (fs.statSync(sourcePath).isFile()) { 34 | fs.copyFileSync(sourcePath, destPath); 35 | console.log(`Copied: ${file}`); 36 | } 37 | }); 38 | 39 | console.log("Ace editor files updated successfully!"); 40 | } catch (error) { 41 | console.error("Error updating Ace editor files:", error); 42 | } 43 | } 44 | 45 | updateAce(); 46 | -------------------------------------------------------------------------------- /www/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/favicon.ico -------------------------------------------------------------------------------- /www/js/ace/ext-error_marker.js: -------------------------------------------------------------------------------- 1 | ; (function() { 2 | window.require(["ace/ext/error_marker"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/ext-hardwrap.js: -------------------------------------------------------------------------------- 1 | define("ace/ext/hardwrap",["require","exports","module","ace/range","ace/editor","ace/config"],function(e,t,n){"use strict";function i(e,t){function m(e,t,n){if(e.lengthn)return{start:o.index,end:o.index+o[2].length};if(s&&s[2])return u=t+s[2].length,{start:u,end:u+s[3].length}}var n=t.column||e.getOption("printMarginColumn"),i=t.allowMerge!=0,s=Math.min(t.startRow,t.endRow),o=Math.max(t.startRow,t.endRow),u=e.session;while(s<=o){var a=u.getLine(s);if(a.length>n){var f=m(a,n,5);if(f){var l=/^\s*/.exec(a)[0];u.replace(new r(s,f.start,s,f.end),"\n"+l)}o++}else if(i&&/\S/.test(a)&&s!=o){var c=u.getLine(s+1);if(c&&/\S/.test(c)){var h=a.replace(/\s+$/,""),p=c.replace(/^\s+/,""),d=h+" "+p,f=m(d,n,5);if(f&&f.start>h.length||d.length0?t.getSelection().moveCursorTo(n.row-1,t.session.getLine(n.row-1).length):t.getSelection().isEmpty()?n.column+=1:n.setPosition(n.row,n.column+1))}function o(e){e.editor.session.$bidiHandler.isMoveLeftOperation=/gotoleft|selectleft|backspace|removewordleft/.test(e.command.name)}function u(e,t){var n=t.session;n.$bidiHandler.currentRow=null;if(n.$bidiHandler.isRtlLine(e.start.row)&&e.action==="insert"&&e.lines.length>1)for(var r=e.start.row;r'\"&#])+)"},{token:["punctuation.definition.variable.lisp","variable.other.global.lisp","punctuation.definition.variable.lisp"],regex:"(\\*)(\\S*)(\\*)"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+(?:L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?(?:L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"string",regex:'"(?=.)',next:"qqstring"}],qqstring:[{token:"constant.character.escape.lisp",regex:"\\\\."},{token:"string",regex:'[^"\\\\]+'},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"}]}};r.inherits(s,i),t.LispHighlightRules=s}),define("ace/mode/lisp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/lisp_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./lisp_highlight_rules").LispHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart=";",this.$id="ace/mode/lisp"}.call(o.prototype),t.Mode=o}); (function() { 2 | window.require(["ace/mode/lisp"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/mode-plain_text.js: -------------------------------------------------------------------------------- 1 | define("ace/mode/plain_text",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./text_highlight_rules").TextHighlightRules,o=e("./behaviour").Behaviour,u=function(){this.HighlightRules=s,this.$behaviour=new o};r.inherits(u,i),function(){this.type="text",this.getNextLineIndent=function(e,t,n){return""},this.$id="ace/mode/plain_text"}.call(u.prototype),t.Mode=u}); (function() { 2 | window.require(["ace/mode/plain_text"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/mode-properties.js: -------------------------------------------------------------------------------- 1 | define("ace/mode/properties_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=/\\u[0-9a-fA-F]{4}|\\/;this.$rules={start:[{token:"comment",regex:/[!#].*$/},{token:"keyword",regex:/[=:]$/},{token:"keyword",regex:/[=:]/,next:"value"},{token:"constant.language.escape",regex:e},{defaultToken:"variable"}],value:[{regex:/\\$/,token:"string",next:"value"},{regex:/$/,token:"string",next:"start"},{token:"constant.language.escape",regex:e},{defaultToken:"string"}]}};r.inherits(s,i),t.PropertiesHighlightRules=s}),define("ace/mode/properties",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/properties_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./properties_highlight_rules").PropertiesHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.$id="ace/mode/properties"}.call(o.prototype),t.Mode=o}); (function() { 2 | window.require(["ace/mode/properties"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/mode-text.js: -------------------------------------------------------------------------------- 1 | ; (function() { 2 | window.require(["ace/mode/text"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/mode-textile.js: -------------------------------------------------------------------------------- 1 | define("ace/mode/textile_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:function(e){return e.charAt(0)=="h"?"markup.heading."+e.charAt(1):"markup.heading"},regex:"h1|h2|h3|h4|h5|h6|bq|p|bc|pre",next:"blocktag"},{token:"keyword",regex:"[\\*]+|[#]+"},{token:"text",regex:".+"}],blocktag:[{token:"keyword",regex:"\\. ",next:"start"},{token:"keyword",regex:"\\(",next:"blocktagproperties"}],blocktagproperties:[{token:"keyword",regex:"\\)",next:"blocktag"},{token:"string",regex:"[a-zA-Z0-9\\-_]+"},{token:"keyword",regex:"#"}]}};r.inherits(s,i),t.TextileHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/textile",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/textile_highlight_rules","ace/mode/matching_brace_outdent"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./textile_highlight_rules").TextileHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.type="text",this.getNextLineIndent=function(e,t,n){return e=="intag"?n:""},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/textile",this.snippetFileId="ace/snippets/textile"}.call(u.prototype),t.Mode=u}); (function() { 2 | window.require(["ace/mode/textile"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/mode-toml.js: -------------------------------------------------------------------------------- 1 | define("ace/mode/toml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=this.createKeywordMapper({"constant.language.boolean":"true|false"},"identifier"),t="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b";this.$rules={start:[{token:"comment.toml",regex:/#.*$/},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:["variable.keygroup.toml"],regex:"(?:^\\s*)(\\[\\[([^\\]]+)\\]\\])"},{token:["variable.keygroup.toml"],regex:"(?:^\\s*)(\\[([^\\]]+)\\])"},{token:e,regex:t},{token:"support.date.toml",regex:"\\d{4}-\\d{2}-\\d{2}(T)\\d{2}:\\d{2}:\\d{2}(Z)"},{token:"constant.numeric.toml",regex:"-?\\d+(\\.?\\d+)?"}],qqstring:[{token:"string",regex:"\\\\$",next:"qqstring"},{token:"constant.language.escape",regex:'\\\\[0tnr"\\\\]'},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}]}};r.inherits(s,i),t.TomlHighlightRules=s}),define("ace/mode/folding/ini",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(){};r.inherits(o,s),function(){this.foldingStartMarker=/^\s*\[([^\])]*)]\s*(?:$|[;#])/,this.getFoldWidgetRange=function(e,t,n){var r=this.foldingStartMarker,s=e.getLine(n),o=s.match(r);if(!o)return;var u=o[1]+".",a=s.length,f=e.getLength(),l=n,c=n;while(++nl){var h=e.getLine(c).length;return new i(l,a,c,h)}}}.call(o.prototype)}),define("ace/mode/toml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/toml_highlight_rules","ace/mode/folding/ini"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./toml_highlight_rules").TomlHighlightRules,o=e("./folding/ini").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart="#",this.$id="ace/mode/toml"}.call(u.prototype),t.Mode=u}); (function() { 2 | window.require(["ace/mode/toml"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/mode-tsv.js: -------------------------------------------------------------------------------- 1 | define("ace/mode/csv_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){i.call(this)};r.inherits(s,i),t.CsvHighlightRules=s}),define("ace/mode/csv",["require","exports","module","ace/lib/oop","ace/mode/text","ace/lib/lang","ace/mode/csv_highlight_rules"],function(e,t,n){"use strict";function f(e,t,n){var r=[],i=e.split(n.separatorRegex),s=n.spliter,o=n.quote||'"',u=(t||"start").split("-"),f=parseInt(u[1])||0,l=u[0]=="string",c=!l;for(var h=0;h|\\||=>|\\*\\*|:=|\\/=|>=|<=|<>"},{token:"punctuation.operator",regex:"\\'|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[(]"},{token:"paren.rparen",regex:"[\\])]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.VHDLHighlightRules=s}),define("ace/mode/vhdl",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/vhdl_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./vhdl_highlight_rules").VHDLHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.$id="ace/mode/vhdl"}.call(o.prototype),t.Mode=o}); (function() { 2 | window.require(["ace/mode/vhdl"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/js/ace/theme-textmate.js: -------------------------------------------------------------------------------- 1 | define("ace/theme/textmate",["require","exports","module","ace/theme/textmate-css","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText=e("./textmate-css"),t.$id="ace/theme/textmate";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass,!1)}); (function() { 2 | window.require(["ace/theme/textmate"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /www/res/file-icons/file-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/file-icons/file-icons.ttf -------------------------------------------------------------------------------- /www/res/fonts/FiraCode.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/fonts/FiraCode.ttf -------------------------------------------------------------------------------- /www/res/fonts/RobotoMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/fonts/RobotoMono.ttf -------------------------------------------------------------------------------- /www/res/icons/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/icons/icons.ttf -------------------------------------------------------------------------------- /www/res/icons/li-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/icons/li-icons.ttf -------------------------------------------------------------------------------- /www/res/logo/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/logo/favicon.ico -------------------------------------------------------------------------------- /www/res/logo/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /www/res/logo/logo circle-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/logo/logo circle-min.png -------------------------------------------------------------------------------- /www/res/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/logo/logo.png -------------------------------------------------------------------------------- /www/res/puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acode-Foundation/Acode/65248baf962b014818ad9ca73b9984c0069b28cd/www/res/puzzle.png -------------------------------------------------------------------------------- /www/res/tail-spin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | --------------------------------------------------------------------------------