├── .eslintignore ├── docs ├── robots.txt ├── favicon.ico ├── img │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── msapplication-icon-144x144.png │ │ ├── android-chrome-maskable-192x192.png │ │ ├── android-chrome-maskable-512x512.png │ │ └── safari-pinned-tab.svg ├── manifest.json ├── precache-manifest.8447f70ffda560461010d0256f0c08d6.js ├── service-worker.js ├── index.html ├── css │ ├── app.85170694.css.map │ └── app.85170694.css └── js │ └── app.6128f5a3.js ├── public ├── robots.txt ├── favicon.ico ├── img │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── apple-touch-icon.png │ │ ├── mstile-150x150.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── msapplication-icon-144x144.png │ │ ├── android-chrome-maskable-192x192.png │ │ ├── android-chrome-maskable-512x512.png │ │ └── safari-pinned-tab.svg └── index.html ├── .browserslistrc ├── commitlint.config.js ├── babel.config.js ├── jest.config.js ├── .husky └── commit-msg ├── .editorconfig ├── src ├── main.js ├── hosted │ ├── NanoEditor.vue │ ├── ChuckNorris.vue │ └── App.vue ├── registerServiceWorker.js ├── utils │ └── index.js ├── library.js └── components │ ├── VueCommandQuery.vue │ └── VueCommand.vue ├── vue.config.js ├── .gitignore ├── release.config.js ├── tests └── unit │ ├── library.spec.js │ ├── utils.spec.js │ ├── vue-command-query.spec.js │ └── vue-command.spec.js ├── .github ├── dependabot.yml └── workflows │ └── publish.yml ├── LICENSE ├── CONTRIBUTING.md ├── .eslintrc.js ├── package.json ├── CODE_OF_CONDUCT.md ├── CHANGELOG.md └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | ./dist 2 | ./docs -------------------------------------------------------------------------------- /docs/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] } 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /docs/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest', 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit ${1} 5 | yarn commitlint --edit $1 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-maskable-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/android-chrome-maskable-192x192.png -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-maskable-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/docs/img/icons/android-chrome-maskable-512x512.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/android-chrome-maskable-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndabAP/vue-command/HEAD/public/img/icons/android-chrome-maskable-512x512.png -------------------------------------------------------------------------------- /docs/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // This mounts the documentation and not the library 2 | import { createApp } from 'vue' 3 | import HostedApp from '@/hosted/App.vue' 4 | import '@/registerServiceWorker' 5 | 6 | const app = createApp(HostedApp) 7 | 8 | app.config.unwrapInjectedRef = true 9 | 10 | app.mount('#app') 11 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See https://stackoverflow.com/questions/57916549/vue-cli-run-build-typeerror-name-undefined 3 | css: { 4 | sourceMap: true 5 | }, 6 | 7 | publicPath: '', 8 | chainWebpack: config => { 9 | config.module.rule('eslint').use('eslint-loader').options({ fix: true }) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /release.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | branches: ['main'], 3 | plugins: [ 4 | '@semantic-release/commit-analyzer', 5 | '@semantic-release/release-notes-generator', 6 | '@semantic-release/changelog', 7 | ['@semantic-release/npm', { 8 | tarballDir: 'release' 9 | }], 10 | ['@semantic-release/github', { 11 | assets: 'release/*.tgz' 12 | }], 13 | '@semantic-release/git' 14 | ], 15 | 16 | preset: 'conventionalcommits' 17 | } 18 | -------------------------------------------------------------------------------- /tests/unit/library.spec.js: -------------------------------------------------------------------------------- 1 | import { defaultParser } from '../../src/library' 2 | 3 | describe('library', () => { 4 | describe('defaultParser', () => { 5 | it('parses one argument', () => { 6 | const parsedQuery = defaultParser('a') 7 | expect(parsedQuery).toStrictEqual(['a']) 8 | }) 9 | 10 | it('parses multiple arguments', () => { 11 | const parsedQuery = defaultParser('a --b c=d') 12 | expect(parsedQuery).toStrictEqual(['a', '--b', 'c=d']) 13 | }) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | {"name":"vue-command","short_name":"vue-command","theme_color":"#4DBA87","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":".","display":"standalone","background_color":"#000000"} -------------------------------------------------------------------------------- /src/hosted/NanoEditor.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | 23 | 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | ignore: 13 | # Ignore all breaking changes 14 | - dependency-name: "*" 15 | update-types: ["version-update:semver-major"] 16 | versioning-strategy: increase 17 | -------------------------------------------------------------------------------- /docs/precache-manifest.8447f70ffda560461010d0256f0c08d6.js: -------------------------------------------------------------------------------- 1 | self.__precacheManifest = (self.__precacheManifest || []).concat([ 2 | { 3 | "revision": "b7d58fccea516ac1a6cc", 4 | "url": "css/app.85170694.css" 5 | }, 6 | { 7 | "revision": "4576e67933e04f70d7c8024e483aabf7", 8 | "url": "index.html" 9 | }, 10 | { 11 | "revision": "b7d58fccea516ac1a6cc", 12 | "url": "js/app.6128f5a3.js" 13 | }, 14 | { 15 | "revision": "3554254d8a3563496f24", 16 | "url": "js/chunk-vendors.37e4d34f.js" 17 | }, 18 | { 19 | "revision": "24534015ca4137a647e4fff735c9a671", 20 | "url": "manifest.json" 21 | }, 22 | { 23 | "revision": "b6216d61c03e6ce0c9aea6ca7808f7ca", 24 | "url": "robots.txt" 25 | } 26 | ]); -------------------------------------------------------------------------------- /tests/unit/utils.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | newEventBus, 3 | PUBLISH_SYMBOL 4 | } from '../../src/utils' 5 | 6 | describe('utils', () => { 7 | describe('newEventBus', () => { 8 | it('publishes an event', () => { 9 | const eventBus = newEventBus() 10 | 11 | const event = 'TEST_EVENT' 12 | const callback = jest.fn() 13 | eventBus.on(event, callback) 14 | eventBus[PUBLISH_SYMBOL](event) 15 | expect(callback).toHaveBeenCalled() 16 | }) 17 | 18 | it('unsubscribes from an event', () => { 19 | const eventBus = newEventBus() 20 | 21 | const event = 'TEST_EVENT' 22 | const callback = jest.fn() 23 | eventBus.on(event, callback) 24 | eventBus.off(event, callback) 25 | eventBus[PUBLISH_SYMBOL](event) 26 | expect(callback).toHaveBeenCalledTimes(0) 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%= htmlWebpackPlugin.options.title %> 11 | 12 | 14 | 15 | 16 | 17 | 21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready () { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered () { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached () { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound () { 20 | console.log('New content is downloading.') 21 | }, 22 | updated () { 23 | console.log('New content is available; please refresh.') 24 | }, 25 | offline () { 26 | console.log('No internet connection found. App is running in offline mode.') 27 | }, 28 | error (error) { 29 | console.error('Error during service worker registration:', error) 30 | } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /tests/unit/vue-command-query.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import VueCommand from '@/components/VueCommand.vue' 3 | import VueCommandQuery from '@/components/VueCommandQuery.vue' 4 | import { 5 | noop, 6 | wrap 7 | } from 'lodash' 8 | import { 9 | computed, 10 | ref 11 | } from 'vue' 12 | 13 | // Mock 14 | class ResizeObserver { 15 | observe () { } 16 | unobserve () { } 17 | disconnect () { } 18 | } 19 | 20 | window.ResizeObserver = ResizeObserver 21 | 22 | describe('VueCommandQuery', () => { 23 | it('shows multiline query', async () => { 24 | const vueCommandWrapper = mount(VueCommand) 25 | 26 | const vueCommandQueryWrapper = vueCommandWrapper.findComponent(VueCommandQuery) 27 | const queryRef = vueCommandQueryWrapper.find({ ref: 'queryRef' }) 28 | 29 | queryRef.setValue('TEST_QUERY \\') 30 | await queryRef.trigger('keyup.enter') 31 | 32 | const multilineQueriesWrapper = vueCommandQueryWrapper.find({ ref: 'multilineQueryRefs' }) 33 | expect(multilineQueriesWrapper.exists()).toBe(true) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /tests/unit/vue-command.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import VueCommand from '@/components/VueCommand.vue' 3 | import VueCommandQuery from '@/components/VueCommandQuery.vue' 4 | 5 | // Mock 6 | class ResizeObserver { 7 | observe () { } 8 | unobserve () { } 9 | disconnect () { } 10 | } 11 | 12 | window.ResizeObserver = ResizeObserver 13 | 14 | describe('VueCommand', () => { 15 | describe('props', () => { 16 | it('hides the bar', () => { 17 | const wrapper = mount(VueCommand, { 18 | props: { 19 | hideBar: true 20 | } 21 | }) 22 | 23 | expect(wrapper.find('.vue-command__bar').exists()).toBe(false) 24 | }) 25 | 26 | it('renders the given prompt', () => { 27 | const prompt = 'TEST_PROMPT' 28 | const wrapper = mount(VueCommand, { 29 | props: { 30 | prompt 31 | } 32 | }) 33 | const query = wrapper.findComponent(VueCommandQuery) 34 | const span = query.find('span') 35 | 36 | expect(span.text()).toMatch(prompt) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Julian Claus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Test, publish 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test 8 | if: github.event_name == 'push' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Use Node.js 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version: "22" 16 | - name: Lint 17 | run: | 18 | yarn --ignore-engines 19 | yarn lint 20 | - name: Test 21 | run: | 22 | yarn --ignore-engines 23 | yarn test 24 | 25 | publish: 26 | name: Publish 27 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 28 | runs-on: ubuntu-latest 29 | needs: [test] 30 | steps: 31 | - uses: actions/checkout@v3 32 | - name: Use Node.js 33 | uses: actions/setup-node@v3 34 | with: 35 | node-version: "22" 36 | - name: Publish 37 | run: yarn --ignore-engines; yarn build:lib; npx semantic-release 38 | env: 39 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 40 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 41 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## General 4 | 5 | - Use [JavaScript standard style](https://standardjs.com/) 6 | - Use [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) 7 | 8 | ## Conventional commits 9 | 10 | You must follow the [conventional commits](https://github.com/conventional-changelog/commitlint/tree/master/@commitlint/config-conventional) schema. Commits are linted and will fail if 11 | they are not following the schema. This helps to be consistent across developers 12 | and makes 13 | [semantic-release](https://github.com/semantic-release/semantic-release) 14 | possible. 15 | 16 | If you change the `README.md`, use `docs!` to trigger a breaking change 17 | (increases major version) to make the change visible at the npm registry. 18 | 19 | ## Build and release 20 | 21 | You don't need to take care of any library building when you are finished. 22 | Releases are done automatically by 23 | [semantic-release](https://github.com/semantic-release/semantic-release). You 24 | must follow the given commit schema 25 | (see [Conventional commits](#Conventional-commits)) to trigger an automatic 26 | release with the correct versioning. 27 | -------------------------------------------------------------------------------- /docs/service-worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to your Workbox-powered service worker! 3 | * 4 | * You'll need to register this file in your web app and you should 5 | * disable HTTP caching for this file too. 6 | * See https://goo.gl/nhQhGp 7 | * 8 | * The rest of the code is auto-generated. Please don't update this file 9 | * directly; instead, make changes to your Workbox build configuration 10 | * and re-run your build process. 11 | * See https://goo.gl/2aRDsh 12 | */ 13 | 14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); 15 | 16 | importScripts( 17 | "precache-manifest.8447f70ffda560461010d0256f0c08d6.js" 18 | ); 19 | 20 | workbox.core.setCacheNameDetails({prefix: "vue-command"}); 21 | 22 | self.addEventListener('message', (event) => { 23 | if (event.data && event.data.type === 'SKIP_WAITING') { 24 | self.skipWaiting(); 25 | } 26 | }); 27 | 28 | /** 29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to 30 | * requests for URLs in the manifest. 31 | * See https://goo.gl/S9QRab 32 | */ 33 | self.__precacheManifest = [].concat(self.__precacheManifest || []); 34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); 35 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-recommended', 8 | '@vue/standard' 9 | ], 10 | parserOptions: { 11 | parser: 'babel-eslint' 12 | }, 13 | plugins: [ 14 | 'modules-newline' 15 | ], 16 | rules: { 17 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 18 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 19 | quotes: ['error', 'single'], 20 | 'no-unused-vars': 'off', 21 | 22 | 'modules-newline/import-declaration-newline': 'error', 23 | 24 | 'vue/no-mutating-props': 'off', 25 | 'vue/html-closing-bracket-newline': ['error', { 26 | singleline: 'never', 27 | multiline: 'never' 28 | }], 29 | 'vue/max-attributes-per-line': ['error', { 30 | singleline: 1, 31 | multiline: 1 32 | }], 33 | 'vue/attributes-order': 'error', 34 | 'vue/order-in-components': 'error', 35 | 'vue/html-indent': 'error', 36 | 'vue/one-component-per-file': 'off', 37 | }, 38 | overrides: [ 39 | { 40 | files: [ 41 | '**/__tests__/*.{j,t}s?(x)', 42 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 43 | ], 44 | env: { 45 | jest: true 46 | } 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /src/hosted/ChuckNorris.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 59 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | // These are helpers for the package 2 | 3 | import { 4 | entries, 5 | eq, 6 | get, 7 | isUndefined, 8 | set 9 | } from 'lodash' 10 | 11 | export const PUBLISH_SYMBOL = Symbol('publish') 12 | 13 | // Creats a new event bus to publish, subscribe and unsubscribe from events 14 | export const newEventBus = () => { 15 | const events = {} 16 | return { 17 | [PUBLISH_SYMBOL] (event) { 18 | const callbacks = get(events, event) 19 | if (isUndefined(callbacks)) { 20 | return 21 | } 22 | 23 | for (const callback of callbacks) { 24 | callback() 25 | } 26 | }, 27 | 28 | on (event, callback) { 29 | if (isUndefined(get(events, event))) { 30 | set(events, event, []) 31 | } 32 | 33 | events[event].push(callback) 34 | }, 35 | 36 | off (event, xCallback) { 37 | const callbacks = get(events, event) 38 | if (isUndefined(callbacks)) { 39 | return 40 | } 41 | 42 | for (const [index, yCallback] of entries(callbacks)) { 43 | if (eq(xCallback, yCallback)) { 44 | events[event].splice(index, 1) 45 | return 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | export const and = (...operands) => { 53 | for (const operand of operands) { 54 | if (!operand) { 55 | return false 56 | } 57 | } 58 | 59 | return true 60 | } 61 | 62 | export const or = (...operands) => { 63 | for (const operand of operands) { 64 | if (operand) { 65 | return true 66 | } 67 | } 68 | 69 | return false 70 | } 71 | 72 | export const xor = (x, y) => x ^ y 73 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | vue-command
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-command", 3 | "version": "36.0.1", 4 | "description": "A fully working, most feature-rich Vue.js terminal emulator", 5 | "author": { 6 | "email": "kontakt@julian-claus.de", 7 | "name": "Julian Claus", 8 | "url": "https://www.julian-claus.de" 9 | }, 10 | "private": false, 11 | "engines": { 12 | "node": ">=22.0.0" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/ndabAP/vue-command.git" 17 | }, 18 | "license": "MIT", 19 | "keywords": [ 20 | "bash", 21 | "emulator", 22 | "sh", 23 | "shell", 24 | "terminal", 25 | "vue", 26 | "vuejs", 27 | "xterm" 28 | ], 29 | "main": "./dist/vue-command.common.js", 30 | "files": [ 31 | "dist/*.css", 32 | "dist/*.css.map", 33 | "dist/*.js", 34 | "dist/*.js.map" 35 | ], 36 | "scripts": { 37 | "build": "yarn lint && yarn build:docs && yarn build:lib", 38 | "build:docs": "vue-cli-service build --dest docs", 39 | "build:lib": "vue-cli-service build --target lib --name vue-command ./src/library.js", 40 | "lint": "vue-cli-service lint", 41 | "serve": "vue-cli-service serve", 42 | "test": "vue-cli-service test:unit" 43 | }, 44 | "dependencies": { 45 | "core-js": "^3.41.0", 46 | "lodash": "^4.17.21", 47 | "register-service-worker": "^1.7.2", 48 | "vue": "^3.5.24" 49 | }, 50 | "devDependencies": { 51 | "@achrinza/node-ipc": "^9.2.9", 52 | "@commitlint/cli": "^17.8.1", 53 | "@commitlint/config-conventional": "^17.6.6", 54 | "@semantic-release/changelog": "^6.0.3", 55 | "@semantic-release/commit-analyzer": "^9.0.2", 56 | "@semantic-release/git": "^10.0.1", 57 | "@semantic-release/github": "^8.1.0", 58 | "@semantic-release/npm": "^9.0.2", 59 | "@semantic-release/release-notes-generator": "^10.0.3", 60 | "@vue/cli-plugin-babel": "~4.5.13", 61 | "@vue/cli-plugin-eslint": "~4.5.13", 62 | "@vue/cli-plugin-pwa": "~4.5.13", 63 | "@vue/cli-plugin-unit-jest": "~4.5.13", 64 | "@vue/cli-service": "~4.5.13", 65 | "@vue/compiler-sfc": "^3.5.13", 66 | "@vue/cli-shared-utils": "5.0.9", 67 | "@vue/eslint-config-standard": "^5.1.2", 68 | "@vue/test-utils": "^2.4.5", 69 | "babel-eslint": "^10.1.0", 70 | "eslint": "^6.8.0", 71 | "eslint-plugin-import": "^2.31.0", 72 | "eslint-plugin-modules-newline": "^0.0.6", 73 | "eslint-plugin-node": "^11.1.0", 74 | "eslint-plugin-promise": "^4.3.1", 75 | "eslint-plugin-standard": "^4.1.0", 76 | "eslint-plugin-vue": "^7.20.0", 77 | "husky": "^8.0.3", 78 | "sass": "^1.93.3", 79 | "sass-loader": "^8.0.2", 80 | "semantic-release": "^19.0.5", 81 | "vue-jest": "^5.0.0-0" 82 | }, 83 | "husky": { 84 | "hooks": { 85 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS; yarn lint --fix", 86 | "pre-push": "yarn test" 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kontakt@julian-claus.de. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /src/library.js: -------------------------------------------------------------------------------- 1 | import { 2 | defineComponent, 3 | h, 4 | inject, 5 | markRaw, 6 | onMounted 7 | } from 'vue' 8 | import VueCommand from '@/components/VueCommand' 9 | import VueCommandQuery from '@/components/VueCommandQuery' 10 | import { 11 | forEach, 12 | isFunction 13 | } from 'lodash' 14 | 15 | // Suffix "KEY" is added to avoid collisions 16 | const ARROW_UP_KEY = 'ArrowUp' 17 | const ARROW_DOWN_KEY = 'ArrowDown' 18 | const C_KEY = 'c' 19 | 20 | // TODO Implement keyboard combinations, e. g. Ctrl + u 21 | 22 | // Creates a command not found component 23 | export const createCommandNotFound = (command, notFoundText = 'command not found', name = 'VueCommandNotFound') => { 24 | const text = `${command}: ${notFoundText}` 25 | return createStdout(text, name) 26 | } 27 | 28 | // Creates a "stderr" with the given formatter or text and name. It exits as 29 | // soon as the component has been mounted 30 | export const createStderr = (formatterOrText, name = 'VueCommandStderr') => { 31 | return createStdout(formatterOrText, name) 32 | } 33 | 34 | // Creates a "stdout" with the given formatter or text and name. It exits as 35 | // soon as the component has been mounted 36 | export const createStdout = (formatterOrText, name = 'VueCommandStdout') => markRaw(defineComponent({ 37 | name, 38 | setup () { 39 | // This tears down the component automatically 40 | const exit = inject('exit') 41 | onMounted(exit) 42 | }, 43 | 44 | render () { 45 | if (isFunction(formatterOrText)) { 46 | // This is automatically called with the bound arguments 47 | return formatterOrText() 48 | } 49 | 50 | return h('div', formatterOrText) 51 | } 52 | })) 53 | 54 | // Creates a new query component 55 | export const createQuery = () => markRaw(VueCommandQuery) 56 | 57 | // A simple query parser which splits the arguments by spaces 58 | export const defaultParser = query => { 59 | return query.split(/[ ]+/) 60 | } 61 | 62 | // Cycles through dispatched queries with arrow keys 63 | export const defaultHistoryEventResolver = (refs, { decrementHistory, incrementHistory }) => { 64 | const vueCommandRef = refs.vueCommandRef 65 | 66 | const eventResolver = event => { 67 | switch (event.key) { 68 | // Validate history event 69 | case ARROW_UP_KEY: 70 | case ARROW_DOWN_KEY: 71 | 72 | // TODO Check if arrows keys are pressed exclusively 73 | 74 | event.preventDefault() 75 | 76 | switch (event.key) { 77 | // Back in history, index down 78 | case ARROW_UP_KEY: 79 | decrementHistory() 80 | break 81 | 82 | // Back in history, index up 83 | case ARROW_DOWN_KEY: 84 | incrementHistory() 85 | break 86 | } 87 | } 88 | } 89 | 90 | vueCommandRef.addEventListener('keydown', eventResolver) 91 | } 92 | 93 | // Sends common signals based on certain events 94 | export const defaultSignalEventResolver = (_, { sendSignal }) => { 95 | const eventResolver = event => { 96 | switch (event.ctrlKey) { 97 | case true: 98 | switch (event.key) { 99 | // SIGINT, Ctrl + c 100 | case C_KEY: 101 | event.preventDefault() 102 | 103 | sendSignal('SIGINT') 104 | } 105 | break 106 | 107 | case false: 108 | break 109 | } 110 | } 111 | 112 | window.addEventListener('keydown', eventResolver) 113 | } 114 | 115 | // Formats the value as json 116 | export const jsonFormatter = value => { 117 | return h('div', JSON.stringify(value, null, 2)) 118 | } 119 | 120 | // Formats the given elements as a list 121 | export const listFormatter = (...lis) => { 122 | return () => { 123 | const ul = [] 124 | forEach(lis, li => { 125 | ul.push(h('li', li)) 126 | }) 127 | 128 | return h('ul', ul) 129 | } 130 | } 131 | 132 | // Returns a history with one query as first input 133 | export const newDefaultHistory = () => [createQuery()] 134 | 135 | // Returns a list of default event resolver 136 | export const newDefaultEventResolver = () => [defaultHistoryEventResolver, defaultSignalEventResolver] 137 | 138 | // Formats the rows as HTML table 139 | export const tableFormatter = rows => { 140 | return () => { 141 | const tbody = [] 142 | forEach(rows, row => { 143 | const trs = [] 144 | forEach(row, td => { 145 | trs.push(h('td', td)) 146 | }) 147 | 148 | tbody.push(h('tr', trs)) 149 | }) 150 | 151 | return h('table', tbody) 152 | } 153 | } 154 | 155 | // Formats the given text. Optionally as inner HTML 156 | export const textFormatter = (text, innerHtml = false) => { 157 | return () => { 158 | if (innerHtml) { 159 | return h('div', { innerHTML: text }) 160 | } 161 | 162 | return h('div', text) 163 | } 164 | } 165 | 166 | export default VueCommand 167 | -------------------------------------------------------------------------------- /docs/css/app.85170694.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///src/components/VueCommandQuery.vue","webpack:///src/components/VueCommand.vue","webpack:///src/hosted/NanoEditor.vue","webpack:///src/hosted/App.vue"],"names":[],"mappings":"AAOI,4KACE,uBAAA,CACA,SAAA,CACA,OAAA,CAIJ,gXAIE,YAAA,CAEA,gaACE,2DAAA,CAQF,g1HAME,WAAA,CACA,YAAA,CAGF,g9EAIE,UAAA,CAGF,g4DAIE,mBAAA,CAIJ,4MAEE,YAAA,CAEA,4SAEE,2DAAA,CAFF,4fAEE,2DAAA,CAQF,guBAEE,WAAA,CACA,YAAA,CACA,MAAA,CACA,UAAA,CAUA,gWACE,wBAAA,CADF,4UACE,wBAAA,CAWF,gaACE,oBAAA,CADF,4YACE,oBAAA,CC7FR,kCAEE,6DAAA,CAkBA,iBAAA,CACA,iBAAA,CAXE,4WAEE,aAAA,CAGF,oLACE,UAAA,CAOJ,4JAIE,YAAA,CACA,uDAAA,CACA,6BAAA,CACA,mBAAA,CACA,iBAAA,CACA,kBAAA,CACA,gBAAA,CACA,gBAAA,CAGF,4LAEE,oBAAA,CACA,kBAAA,CAEA,wNACE,WAAA,CACA,aAAA,CACA,WAAA,CACA,UAAA,CAGF,gQACE,gBAAA,CAIJ,4KAEE,aAAA,CACA,WAAA,CACA,gBAAA,CACA,QAAA,CACA,aAAA,CACA,2BAAA,CACA,oBAAA,CACA,oBAAA,CAGA,wLACE,oBAAA,CACA,QAAA,CACA,SAAA,CAGF,oZAEE,eAAA,CACA,WAAA,CACA,MAAA,CACA,cAAA,CACA,YAAA,CACA,eAAA,CACA,WAAA,CACA,UAAA,CAIJ,oHACE,WAAA,CASF,+BACE,aAHS,CAIT,wBAAA,CAGF,sCACE,aARS,CAWX,8CACE,wBAAA,CAGF,iDACE,wBAAA,CAGF,mDACE,wBAAA,CAGF,mCACE,wBAAA,CACA,aAzBS,CA4BT,qFAEE,eAAA,CACA,UAAA,CAEA,yHACE,wBAAA,CADF,+GACE,wBAAA,CASN,+CACE,wBAAA,CAGF,sDACE,aAPgB,CAUlB,8DACE,wBAAA,CAGF,iEACE,wBAAA,CAGF,mEACE,wBAAA,CAGF,mDACE,wBAAA,CACA,aAxBgB,CA2BhB,qHAEE,eAAA,CACA,UAAA,CAEA,yJACE,oBAAA,CADF,+IACE,oBAAA,CCnJR,+CAEE,WACF,CCzBA,yBACE,WACE,eAAA,CAAA,CAIJ,kCAEE,UAAA,CAEA,0EACE,SAAA,CAGF,4JAEE,2BAAA,CACA,0BAAA,CAGF,4KAEE,YAAA,CACA,8BAAA,CACA,6BAAA,CAKF,uCACE,kBAAA,CAGF,uCACE,kBAAA,CAGF,6CACE,eAAA,CAKF,+CACE,kBAAA,CAGF,+CACE,kBAAA,CAGF,qDACE,eAAA","file":"app.85170694.css","sourcesContent":["\n.vue-command,\n.vue-command--invert {\n\n /* TODO Separate, simplify */\n\n .vue-command__reverse-i-search {\n .vue-command__reverse-i-search__input {\n caret-color: transparent;\n padding: 0;\n width: 0ch;\n }\n }\n\n .vue-command__query,\n .vue-command__query--invert,\n .vue-command__multiline-query,\n .vue-command__multiline-query--invert {\n display: flex;\n\n input {\n font: 1rem Consolas,\n Monaco,\n 'Andale Mono',\n 'Ubuntu Mono',\n monospace;\n ;\n }\n\n .vue-command__query__input,\n .vue-command__query__input--invert,\n .vue-command__multiline-query,\n .vue-command__multiline-query--invert,\n .vue-command__reverse-i-search__input,\n .vue-command__reverse-i-search__input--invert {\n border: none;\n outline: none;\n }\n\n .vue-command__query__input,\n .vue-command__query__input--invert,\n .vue-command__multiline-query,\n .vue-command__multiline-query--invert {\n width: 100%;\n }\n\n .vue-command__query__prompt,\n .vue-command__query__prompt--invert,\n .vue-command__multiline-query__prompt,\n .vue-command__multiline-query__prompt {\n margin-right: 0.25rem;\n }\n }\n\n .vue-command__multiline-query,\n .vue-command__multiline-query--invert {\n display: flex;\n\n input::placeholder,\n input {\n font: 1rem Consolas,\n Monaco,\n 'Andale Mono',\n 'Ubuntu Mono',\n monospace;\n ;\n }\n\n .vue-command__multiline-query__input,\n .vue-command__multiline-query__input--invert {\n border: none;\n outline: none;\n flex: 1;\n width: 100%;\n }\n }\n}\n\n.vue-command {\n .vue-command__multiline-query,\n .vue-command__query {\n .vue-command__multiline-query__input,\n .vue-command__query__input {\n &::placeholder {\n color: rgba(255, 255, 255, 0.5);\n }\n }\n }\n}\n\n.vue-command--invert {\n .vue-command__multiline-query,\n .vue-command__query--invert {\n .vue-command__multiline-query__input,\n .vue-command__query__input--invert {\n &::placeholder {\n color: rgba(0, 0, 0, 0.5);\n }\n }\n }\n}\n","\n/* Common attribues */\n\n.vue-command,\n.vue-command--invert {\n font-family: Consolas,\n Monaco,\n 'Andale Mono',\n 'Ubuntu Mono',\n monospace;\n\n @mixin clearfix() {\n\n &:before,\n &:after {\n display: table;\n }\n\n &:after {\n clear: both;\n }\n }\n\n overflow-x: hidden;\n overflow-y: hidden;\n\n .vue-command__bar,\n .vue-command__bar--invert {\n @include clearfix();\n\n display: flex;\n font-family: -apple-system, BlinkMacSystemFont, sans-serif;\n justify-content: space-between;\n padding-bottom: 10px;\n padding-left: 10px;\n padding-right: 10px;\n padding-top: 10px;\n position: inherit;\n }\n\n .vue-command__bar__button,\n .vue-command__bar__button--invert {\n display: inline-block;\n border-radius: 100%;\n\n &:before {\n content: ' ';\n display: block;\n height: 12px;\n width: 12px;\n }\n\n &:not(:last-child) {\n margin-right: 8px;\n }\n }\n\n .vue-command__history--invert,\n .vue-command__history {\n display: block;\n height: 100%;\n line-height: 1.33;\n margin: 0;\n overflow: auto;\n padding: 12px 12px 12px 12px;\n white-space: pre-line;\n word-break: break-all;\n\n /* Provide reasonable default values */\n ul {\n list-style-type: none;\n margin: 0;\n padding: 0;\n }\n\n input,\n textarea {\n background: none;\n border: none;\n flex: 1;\n font-size: 1rem;\n outline: none;\n overflow: hidden;\n resize: none;\n width: 100%;\n }\n }\n\n .vue-command__history__entry--fullscreen {\n height: 100%;\n }\n}\n\n/* Individual attribues */\n\n.vue-command {\n $seashell: #f1f1f1;\n\n .vue-command__bar {\n color: $seashell;\n background-color: #111316;\n }\n\n .vue-command__bar__title {\n color: $seashell;\n }\n\n .vue-command__bar__button--close {\n background-color: #ff5f58;\n }\n\n .vue-command__bar__button--minimize {\n background-color: #ffbd2e;\n }\n\n .vue-command__bar__button--fullscreen {\n background-color: #29ca41;\n }\n\n .vue-command__history {\n background-color: #111316;\n color: $seashell;\n\n /* Provide reasonable default values */\n input,\n textarea {\n background: none;\n color: #ffffff;\n\n &::placeholder {\n color: rgba(255, 255, 255, 0.5);\n }\n }\n }\n}\n\n.vue-command--invert {\n $seashell-invert: #0e0e0e;\n\n .vue-command__bar--invert {\n background-color: #eeece9;\n }\n\n .vue-command__bar__title--invert {\n color: $seashell-invert;\n }\n\n .vue-command__bar__button--close--invert {\n background-color: #00a0a7;\n }\n\n .vue-command__bar__button--minimize--invert {\n background-color: #0042d1;\n }\n\n .vue-command__bar__button--fullscreen--invert {\n background-color: #d635be;\n }\n\n .vue-command__history--invert {\n background-color: #eeece9;\n color: $seashell-invert;\n\n /* Provide reasonable default values */\n input,\n textarea {\n background: none;\n color: #000000;\n\n &::placeholder {\n color: rgba(0, 0, 0, 0.5);\n }\n }\n }\n}\n","\n\n\n\n\n","\n@media (min-width: 1200px) {\n .container {\n max-width: 720px;\n }\n}\n\n.vue-command,\n.vue-command--invert {\n width: 100%;\n\n ::-webkit-scrollbar {\n width: 6px;\n }\n\n .vue-command__bar,\n .vue-command__bar--invert {\n border-top-right-radius: 6px;\n border-top-left-radius: 6px;\n }\n\n .vue-command__history,\n .vue-command__history--invert {\n height: 350px;\n border-bottom-right-radius: 6px;\n border-bottom-left-radius: 6px;\n }\n}\n\n.vue-command {\n ::-webkit-scrollbar-track {\n background: #252525;\n }\n\n ::-webkit-scrollbar-thumb {\n background: #f1f1f1;\n }\n\n ::-webkit-scrollbar-thumb:hover {\n background: #333;\n }\n}\n\n.vue-command--invert {\n ::-webkit-scrollbar-track {\n background: #dadada;\n }\n\n ::-webkit-scrollbar-thumb {\n background: #0e0e0e;\n }\n\n ::-webkit-scrollbar-thumb:hover {\n background: #cccccc;\n }\n}\n"]} -------------------------------------------------------------------------------- /src/hosted/App.vue: -------------------------------------------------------------------------------- 1 | 200 | 201 | 355 | 356 | 413 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [36.0.1](https://github.com/ndabAP/vue-command/compare/v36.0.0...v36.0.1) (2025-11-18) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * avoid crash when closing terminal ([ab4c703](https://github.com/ndabAP/vue-command/commit/ab4c7031f126485f100013b2932b5089e53f2e4e)) 7 | 8 | ## [36.0.0](https://github.com/ndabAP/vue-command/compare/v35.2.1...v36.0.0) (2025-10-25) 9 | 10 | 11 | ### ⚠ BREAKING CHANGES 12 | 13 | * improve readme 14 | 15 | ### Documentation 16 | 17 | * improve readme ([49e4165](https://github.com/ndabAP/vue-command/commit/49e4165fed84021931437dc7b062ffccaafe6193)) 18 | 19 | ## [35.2.1](https://github.com/ndabAP/vue-command/compare/v35.2.0...v35.2.1) (2023-07-01) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * stderr component name ([859769e](https://github.com/ndabAP/vue-command/commit/859769ecfd3983b55fba73e1e76ebf8783d572de)) 25 | 26 | ## [35.2.0](https://github.com/ndabAP/vue-command/compare/v35.1.0...v35.2.0) (2023-06-25) 27 | 28 | 29 | ### Features 30 | 31 | * add stderr component ([fb280c0](https://github.com/ndabAP/vue-command/commit/fb280c00a9ac4f572d0d4e0ca8a01096151c1c7b)) 32 | 33 | 34 | ### Bug Fixes 35 | 36 | * set font ([f92d85c](https://github.com/ndabAP/vue-command/commit/f92d85cc7e97eb45bd1ebb72b37f1bf536bac25a)) 37 | 38 | ## [35.1.0](https://github.com/ndabAP/vue-command/compare/v35.0.0...v35.1.0) (2023-05-30) 39 | 40 | 41 | ### Features 42 | 43 | * add font api and command ([e8111f3](https://github.com/ndabAP/vue-command/commit/e8111f392061676719f488c7ba2f47f610f26e07)) 44 | 45 | ## [35.0.0](https://github.com/ndabAP/vue-command/compare/v34.0.0...v35.0.0) (2023-05-27) 46 | 47 | 48 | ### ⚠ BREAKING CHANGES 49 | 50 | * update 51 | 52 | ### Documentation 53 | 54 | * update ([9fe4674](https://github.com/ndabAP/vue-command/commit/9fe4674f85393e8335ff4b691791f0349185fb44)) 55 | 56 | ## [34.0.0](https://github.com/ndabAP/vue-command/compare/v33.0.1...v34.0.0) (2023-05-07) 57 | 58 | 59 | ### ⚠ BREAKING CHANGES 60 | 61 | * fix features bullet point escape 62 | * fix features bullet point 63 | 64 | ### Features 65 | 66 | * hide buttons ([a71bf81](https://github.com/ndabAP/vue-command/commit/a71bf81d2a133884889a47776db1598ff0aa7bda)) 67 | 68 | 69 | ### Documentation 70 | 71 | * fix features bullet point ([60faa9a](https://github.com/ndabAP/vue-command/commit/60faa9af2b45807e5ce45cb3d1a0d87e5f57d967)) 72 | * fix features bullet point escape ([c7850a5](https://github.com/ndabAP/vue-command/commit/c7850a531da197982121fbb32233777cd4dd28ca)) 73 | 74 | ## [33.1.0](https://github.com/ndabAP/vue-command/compare/v33.0.1...v33.1.0) (2023-05-07) 75 | 76 | 77 | ### Features 78 | 79 | * hide buttons ([a71bf81](https://github.com/ndabAP/vue-command/commit/a71bf81d2a133884889a47776db1598ff0aa7bda)) 80 | 81 | ## [33.0.1](https://github.com/ndabAP/vue-command/compare/v33.0.0...v33.0.1) (2023-02-12) 82 | 83 | 84 | ### Bug Fixes 85 | 86 | * dynamic prompt ([4f16289](https://github.com/ndabAP/vue-command/commit/4f16289f5661dad25a3b4202d86cd35ef592c387)) 87 | 88 | ## [33.0.0](https://github.com/ndabAP/vue-command/compare/v32.4.0...v33.0.0) (2023-02-12) 89 | 90 | 91 | ### ⚠ BREAKING CHANGES 92 | 93 | * code style, explain signals 94 | 95 | ### Documentation 96 | 97 | * code style, explain signals ([e7f34c0](https://github.com/ndabAP/vue-command/commit/e7f34c0eb7b416b2059357c599aac633f97df435)) 98 | 99 | ## [32.4.0](https://github.com/ndabAP/vue-command/compare/v32.3.0...v32.4.0) (2023-02-12) 100 | 101 | 102 | ### Features 103 | 104 | * prompt slot ([996b95d](https://github.com/ndabAP/vue-command/commit/996b95d122902fd25016045ba4849aa730c2e800)) 105 | 106 | ## [32.3.0](https://github.com/ndabAP/vue-command/compare/v32.2.1...v32.3.0) (2023-02-10) 107 | 108 | 109 | ### Features 110 | 111 | * interpreter ([7b7258c](https://github.com/ndabAP/vue-command/commit/7b7258cbcb10cc313987403ab61169ced2b2d43c)) 112 | 113 | ## [32.2.1](https://github.com/ndabAP/vue-command/compare/v32.2.0...v32.2.1) (2023-01-11) 114 | 115 | 116 | ### Bug Fixes 117 | 118 | * text formatter inner html ([32cbcbc](https://github.com/ndabAP/vue-command/commit/32cbcbc76f65b540f006453201bafe3bfac69fb2)) 119 | 120 | ## [32.2.0](https://github.com/ndabAP/vue-command/compare/v32.1.3...v32.2.0) (2023-01-08) 121 | 122 | 123 | ### Features 124 | 125 | * focus reverse i search ([290c00f](https://github.com/ndabAP/vue-command/commit/290c00f2a2a72ddc56c90d87779de5e496ff6fba)) 126 | 127 | ## [32.1.3](https://github.com/ndabAP/vue-command/compare/v32.1.2...v32.1.3) (2023-01-08) 128 | 129 | 130 | ### Bug Fixes 131 | 132 | * multiline query reverse i search ([a995d52](https://github.com/ndabAP/vue-command/commit/a995d527031c7f438e855f4e8edc1e69093e9af1)) 133 | 134 | ## [32.1.2](https://github.com/ndabAP/vue-command/compare/v32.1.1...v32.1.2) (2023-01-08) 135 | 136 | 137 | ### Bug Fixes 138 | 139 | * multiline query reverse i search ([7f075fb](https://github.com/ndabAP/vue-command/commit/7f075fbdd26b778b5652c57839aaae3461adb924)) 140 | 141 | ## [32.1.1](https://github.com/ndabAP/vue-command/compare/v32.1.0...v32.1.1) (2023-01-08) 142 | 143 | 144 | ### Bug Fixes 145 | 146 | * docs ([3d15c04](https://github.com/ndabAP/vue-command/commit/3d15c04cdbcf7c7eab3f73b75df35f788cdb56d6)) 147 | 148 | ## [32.1.0](https://github.com/ndabAP/vue-command/compare/v32.0.3...v32.1.0) (2023-01-08) 149 | 150 | 151 | ### Features 152 | 153 | * reverse i search ([f8891e6](https://github.com/ndabAP/vue-command/commit/f8891e63b6db762620d07ad1bf7d81c27d22da11)) 154 | 155 | ## [32.0.3](https://github.com/ndabAP/vue-command/compare/v32.0.2...v32.0.3) (2023-01-07) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * remove log ([e469f82](https://github.com/ndabAP/vue-command/commit/e469f8286fcdb72c0771749d20cf77c0a4aaa99d)) 161 | 162 | ## [32.0.2](https://github.com/ndabAP/vue-command/compare/v32.0.1...v32.0.2) (2023-01-07) 163 | 164 | 165 | ### Bug Fixes 166 | 167 | * docs type ([d9c0043](https://github.com/ndabAP/vue-command/commit/d9c0043106fa240085922c12cab3cfbd69b6b274)) 168 | 169 | ## [32.0.1](https://github.com/ndabAP/vue-command/compare/v32.0.0...v32.0.1) (2023-01-06) 170 | 171 | 172 | ### Bug Fixes 173 | 174 | * set cursor position for end ([d2a7506](https://github.com/ndabAP/vue-command/commit/d2a75061b646f73089fa2700e8b3e781353ca826)) 175 | * unsubscribe query ([a90303b](https://github.com/ndabAP/vue-command/commit/a90303b8e7dd7446d40c0b5dc77e36a97bcbb1cc)) 176 | * use terminal prompt ([e513e3e](https://github.com/ndabAP/vue-command/commit/e513e3e4526156a8d62f0ccc273f274c433a204b)) 177 | 178 | ## [32.0.0](https://github.com/ndabAP/vue-command/compare/v31.0.0...v32.0.0) (2023-01-06) 179 | 180 | 181 | ### ⚠ BREAKING CHANGES 182 | 183 | * add vue.js 3 support 184 | 185 | ### Documentation 186 | 187 | * add vue.js 3 support ([0511fc1](https://github.com/ndabAP/vue-command/commit/0511fc153ec7f84188c1f76e3b16d7ae3c7ae264)) 188 | 189 | ## [31.0.0](https://github.com/ndabAP/vue-command/compare/v30.0.1...v31.0.0) (2023-01-06) 190 | 191 | 192 | ### ⚠ BREAKING CHANGES 193 | 194 | * fix cd 195 | 196 | ### Documentation 197 | 198 | * fix cd ([f993e15](https://github.com/ndabAP/vue-command/commit/f993e152c9e85a515d8af7482bfd6f04cf1b2f98)) 199 | 200 | ## [30.0.1](https://github.com/ndabAP/vue-command/compare/v30.0.0...v30.0.1) (2023-01-06) 201 | 202 | 203 | ### Bug Fixes 204 | 205 | * dependabot ([83fcca0](https://github.com/ndabAP/vue-command/commit/83fcca00a80a1dc9d193848db88a1524cc8689fc)) 206 | 207 | ## [30.0.0](https://github.com/ndabAP/vue-command/compare/v29.2.0...v30.0.0) (2023-01-06) 208 | 209 | 210 | ### ⚠ BREAKING CHANGES 211 | 212 | * support multiline queries 213 | 214 | ### Features 215 | 216 | * support multiline queries ([df09dde](https://github.com/ndabAP/vue-command/commit/df09dded80f19530fad39f606f65fc38bf382a44)) 217 | 218 | ## [29.2.0](https://github.com/ndabAP/vue-command/compare/v29.1.1...v29.2.0) (2022-12-26) 219 | 220 | 221 | ### Features 222 | 223 | * set terminal title ([77c2f02](https://github.com/ndabAP/vue-command/commit/77c2f02412a001709f4599737becab1322ad53cc)) 224 | 225 | ## [29.1.1](https://github.com/ndabAP/vue-command/compare/v29.1.0...v29.1.1) (2022-12-26) 226 | 227 | 228 | ### Bug Fixes 229 | 230 | * slot bar documentation ([6dfc73c](https://github.com/ndabAP/vue-command/commit/6dfc73c5bc4af89c8ad99101a90845b960dc2928)) 231 | 232 | ## [29.1.0](https://github.com/ndabAP/vue-command/compare/v29.0.0...v29.1.0) (2022-12-26) 233 | 234 | 235 | ### Features 236 | 237 | * add new slots ([d8f6de7](https://github.com/ndabAP/vue-command/commit/d8f6de72cf8b54848fa9f4ff44a8a670a368a4f2)) 238 | 239 | 240 | ### Bug Fixes 241 | 242 | * add missing prevent default ([ca0583a](https://github.com/ndabAP/vue-command/commit/ca0583ae8be6dc45c30c4fadb8163e39d135c85a)) 243 | * component out name ([b49d6db](https://github.com/ndabAP/vue-command/commit/b49d6db714c8814120d90974518ecccfd4284ad7)) 244 | 245 | ## [29.0.0](https://github.com/ndabAP/vue-command/compare/v28.2.0...v29.0.0) (2022-12-25) 246 | 247 | 248 | ### ⚠ BREAKING CHANGES 249 | 250 | * add missing signal resolver 251 | 252 | ### Documentation 253 | 254 | * add missing signal resolver ([63ac8f2](https://github.com/ndabAP/vue-command/commit/63ac8f2e34f5cf904bbd2890d2ff2695da0ae28c)) 255 | 256 | ## [28.2.0](https://github.com/ndabAP/vue-command/compare/v28.1.0...v28.2.0) (2022-12-25) 257 | 258 | 259 | ### Features 260 | 261 | * finalize signals ([22a0bfa](https://github.com/ndabAP/vue-command/commit/22a0bfa8a4a9d71f853f791613a3a9dc23300adb)) 262 | * send signals ([1e31d32](https://github.com/ndabAP/vue-command/commit/1e31d32ed588821790d6410622b36061bf5245be)) 263 | 264 | ## [28.1.0](https://github.com/ndabAP/vue-command/compare/v28.0.0...v28.1.0) (2022-12-22) 265 | 266 | 267 | ### Features 268 | 269 | * add ctrl + c ([37911a1](https://github.com/ndabAP/vue-command/commit/37911a168cb2cb769136059bfd10fc1d1e8c71d2)) 270 | * add json formatter ([9a24401](https://github.com/ndabAP/vue-command/commit/9a24401d514f77ca0d19d8a0ccd92b75324deb2a)) 271 | 272 | ## [28.0.0](https://github.com/ndabAP/vue-command/compare/v27.1.0...v28.0.0) (2022-12-21) 273 | 274 | 275 | ### ⚠ BREAKING CHANGES 276 | 277 | * add missing invert 278 | 279 | ### Documentation 280 | 281 | * add missing invert ([4d2a5de](https://github.com/ndabAP/vue-command/commit/4d2a5deaf953f89309f62fff216ddaaaa023247f)) 282 | 283 | ## [27.1.0](https://github.com/ndabAP/vue-command/compare/v27.0.0...v27.1.0) (2022-12-21) 284 | 285 | 286 | ### Features 287 | 288 | * bootstrap ([0a20838](https://github.com/ndabAP/vue-command/commit/0a208389d36c993336e665ab7fe81542aab79593)) 289 | * invert ([313ee39](https://github.com/ndabAP/vue-command/commit/313ee391c21bcea3fc26efc09a38c445e9e16dfb)) 290 | * invert ([937dbc1](https://github.com/ndabAP/vue-command/commit/937dbc1b83a594780c60c7dd9202faa6721817a2)) 291 | 292 | ## [27.0.0](https://github.com/ndabAP/vue-command/compare/v26.0.0...v27.0.0) (2022-12-21) 293 | 294 | 295 | ### ⚠ BREAKING CHANGES 296 | 297 | * shorten description 298 | 299 | ### Features 300 | 301 | * allow text as stdout helper argument ([e322c40](https://github.com/ndabAP/vue-command/commit/e322c40dbfb00777b154cbfe03b79066429f7836)) 302 | * emit button events ([240a1ef](https://github.com/ndabAP/vue-command/commit/240a1efadf2118c57d45f7b46f7db2ebfc7df4fb)) 303 | * set cursor position on arrow left/right ([c5ceeb0](https://github.com/ndabAP/vue-command/commit/c5ceeb0d404ff74c73ae19b7f3380ccf3fc6cf47)) 304 | 305 | 306 | ### Bug Fixes 307 | 308 | * dispatch without argument ([5982182](https://github.com/ndabAP/vue-command/commit/5982182d8c87cf8c5e86d15aed267a6e7da2fc71)) 309 | * use recommended event wording ([567f04a](https://github.com/ndabAP/vue-command/commit/567f04afb1ab3cee63044da38a5cfb4adb4dccf5)) 310 | 311 | 312 | ### Documentation 313 | 314 | * shorten description ([86bf74f](https://github.com/ndabAP/vue-command/commit/86bf74f59c522c102f54a861189917653673ab83)) 315 | 316 | ## [26.0.0](https://github.com/ndabAP/vue-command/compare/v25.0.0...v26.0.0) (2022-12-17) 317 | 318 | 319 | ### ⚠ BREAKING CHANGES 320 | 321 | * fix library parameter 322 | 323 | ### Documentation 324 | 325 | * fix library parameter ([39308e5](https://github.com/ndabAP/vue-command/commit/39308e5bf1fc34cff6bd0cffb2549096d60134f8)) 326 | 327 | ## [25.0.0](https://github.com/ndabAP/vue-command/compare/v24.0.0...v25.0.0) (2022-12-17) 328 | 329 | 330 | ### ⚠ BREAKING CHANGES 331 | 332 | * enforce release 333 | 334 | ### Miscellaneous Chores 335 | 336 | * enforce release ([0fcaba4](https://github.com/ndabAP/vue-command/commit/0fcaba4450ecb12dc1c49ebde207fb8c8892bcb7)) 337 | 338 | ## [24.0.0](https://github.com/ndabAP/vue-command/compare/v23.0.1...v24.0.0) (2022-12-17) 339 | 340 | 341 | ### ⚠ BREAKING CHANGES 342 | 343 | * fixes, features 344 | * minor improvement 345 | 346 | ### Documentation 347 | 348 | * minor improvement ([39073a8](https://github.com/ndabAP/vue-command/commit/39073a84ab68a6ecfdade543985fa573c6d1fdee)) 349 | 350 | 351 | ### Miscellaneous Chores 352 | 353 | * fixes, features ([8421c68](https://github.com/ndabAP/vue-command/commit/8421c68370a89c22a9231d9f843eddd827267604)) 354 | -------------------------------------------------------------------------------- /src/components/VueCommandQuery.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 493 | 494 | 596 | -------------------------------------------------------------------------------- /docs/css/app.85170694.css: -------------------------------------------------------------------------------- 1 | .vue-command--invert .vue-command__reverse-i-search .vue-command__reverse-i-search__input,.vue-command .vue-command__reverse-i-search .vue-command__reverse-i-search__input{caret-color:transparent;padding:0;width:0}.vue-command--invert .vue-command__multiline-query,.vue-command--invert .vue-command__multiline-query--invert,.vue-command--invert .vue-command__query,.vue-command--invert .vue-command__query--invert,.vue-command .vue-command__multiline-query,.vue-command .vue-command__multiline-query--invert,.vue-command .vue-command__query,.vue-command .vue-command__query--invert{display:flex}.vue-command--invert .vue-command__multiline-query--invert input,.vue-command--invert .vue-command__multiline-query input,.vue-command--invert .vue-command__query--invert input,.vue-command--invert .vue-command__query input,.vue-command .vue-command__multiline-query--invert input,.vue-command .vue-command__multiline-query input,.vue-command .vue-command__query--invert input,.vue-command .vue-command__query input{font:1rem Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace}.vue-command--invert .vue-command__multiline-query--invert .vue-command__multiline-query,.vue-command--invert .vue-command__multiline-query--invert .vue-command__multiline-query--invert,.vue-command--invert .vue-command__multiline-query--invert .vue-command__query__input,.vue-command--invert .vue-command__multiline-query--invert .vue-command__query__input--invert,.vue-command--invert .vue-command__multiline-query--invert .vue-command__reverse-i-search__input,.vue-command--invert .vue-command__multiline-query--invert .vue-command__reverse-i-search__input--invert,.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query,.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query--invert,.vue-command--invert .vue-command__multiline-query .vue-command__query__input,.vue-command--invert .vue-command__multiline-query .vue-command__query__input--invert,.vue-command--invert .vue-command__multiline-query .vue-command__reverse-i-search__input,.vue-command--invert .vue-command__multiline-query .vue-command__reverse-i-search__input--invert,.vue-command--invert .vue-command__query--invert .vue-command__multiline-query,.vue-command--invert .vue-command__query--invert .vue-command__multiline-query--invert,.vue-command--invert .vue-command__query--invert .vue-command__query__input,.vue-command--invert .vue-command__query--invert .vue-command__query__input--invert,.vue-command--invert .vue-command__query--invert .vue-command__reverse-i-search__input,.vue-command--invert .vue-command__query--invert .vue-command__reverse-i-search__input--invert,.vue-command--invert .vue-command__query .vue-command__multiline-query,.vue-command--invert .vue-command__query .vue-command__multiline-query--invert,.vue-command--invert .vue-command__query .vue-command__query__input,.vue-command--invert .vue-command__query .vue-command__query__input--invert,.vue-command--invert .vue-command__query .vue-command__reverse-i-search__input,.vue-command--invert .vue-command__query .vue-command__reverse-i-search__input--invert,.vue-command .vue-command__multiline-query--invert .vue-command__multiline-query,.vue-command .vue-command__multiline-query--invert .vue-command__multiline-query--invert,.vue-command .vue-command__multiline-query--invert .vue-command__query__input,.vue-command .vue-command__multiline-query--invert .vue-command__query__input--invert,.vue-command .vue-command__multiline-query--invert .vue-command__reverse-i-search__input,.vue-command .vue-command__multiline-query--invert .vue-command__reverse-i-search__input--invert,.vue-command .vue-command__multiline-query .vue-command__multiline-query,.vue-command .vue-command__multiline-query .vue-command__multiline-query--invert,.vue-command .vue-command__multiline-query .vue-command__query__input,.vue-command .vue-command__multiline-query .vue-command__query__input--invert,.vue-command .vue-command__multiline-query .vue-command__reverse-i-search__input,.vue-command .vue-command__multiline-query .vue-command__reverse-i-search__input--invert,.vue-command .vue-command__query--invert .vue-command__multiline-query,.vue-command .vue-command__query--invert .vue-command__multiline-query--invert,.vue-command .vue-command__query--invert .vue-command__query__input,.vue-command .vue-command__query--invert .vue-command__query__input--invert,.vue-command .vue-command__query--invert .vue-command__reverse-i-search__input,.vue-command .vue-command__query--invert .vue-command__reverse-i-search__input--invert,.vue-command .vue-command__query .vue-command__multiline-query,.vue-command .vue-command__query .vue-command__multiline-query--invert,.vue-command .vue-command__query .vue-command__query__input,.vue-command .vue-command__query .vue-command__query__input--invert,.vue-command .vue-command__query .vue-command__reverse-i-search__input,.vue-command .vue-command__query .vue-command__reverse-i-search__input--invert{border:none;outline:none}.vue-command--invert .vue-command__multiline-query--invert .vue-command__multiline-query,.vue-command--invert .vue-command__multiline-query--invert .vue-command__multiline-query--invert,.vue-command--invert .vue-command__multiline-query--invert .vue-command__query__input,.vue-command--invert .vue-command__multiline-query--invert .vue-command__query__input--invert,.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query,.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query--invert,.vue-command--invert .vue-command__multiline-query .vue-command__query__input,.vue-command--invert .vue-command__multiline-query .vue-command__query__input--invert,.vue-command--invert .vue-command__query--invert .vue-command__multiline-query,.vue-command--invert .vue-command__query--invert .vue-command__multiline-query--invert,.vue-command--invert .vue-command__query--invert .vue-command__query__input,.vue-command--invert .vue-command__query--invert .vue-command__query__input--invert,.vue-command--invert .vue-command__query .vue-command__multiline-query,.vue-command--invert .vue-command__query .vue-command__multiline-query--invert,.vue-command--invert .vue-command__query .vue-command__query__input,.vue-command--invert .vue-command__query .vue-command__query__input--invert,.vue-command .vue-command__multiline-query--invert .vue-command__multiline-query,.vue-command .vue-command__multiline-query--invert .vue-command__multiline-query--invert,.vue-command .vue-command__multiline-query--invert .vue-command__query__input,.vue-command .vue-command__multiline-query--invert .vue-command__query__input--invert,.vue-command .vue-command__multiline-query .vue-command__multiline-query,.vue-command .vue-command__multiline-query .vue-command__multiline-query--invert,.vue-command .vue-command__multiline-query .vue-command__query__input,.vue-command .vue-command__multiline-query .vue-command__query__input--invert,.vue-command .vue-command__query--invert .vue-command__multiline-query,.vue-command .vue-command__query--invert .vue-command__multiline-query--invert,.vue-command .vue-command__query--invert .vue-command__query__input,.vue-command .vue-command__query--invert .vue-command__query__input--invert,.vue-command .vue-command__query .vue-command__multiline-query,.vue-command .vue-command__query .vue-command__multiline-query--invert,.vue-command .vue-command__query .vue-command__query__input,.vue-command .vue-command__query .vue-command__query__input--invert{width:100%}.vue-command--invert .vue-command__multiline-query--invert .vue-command__multiline-query__prompt,.vue-command--invert .vue-command__multiline-query--invert .vue-command__query__prompt,.vue-command--invert .vue-command__multiline-query--invert .vue-command__query__prompt--invert,.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query__prompt,.vue-command--invert .vue-command__multiline-query .vue-command__query__prompt,.vue-command--invert .vue-command__multiline-query .vue-command__query__prompt--invert,.vue-command--invert .vue-command__query--invert .vue-command__multiline-query__prompt,.vue-command--invert .vue-command__query--invert .vue-command__query__prompt,.vue-command--invert .vue-command__query--invert .vue-command__query__prompt--invert,.vue-command--invert .vue-command__query .vue-command__multiline-query__prompt,.vue-command--invert .vue-command__query .vue-command__query__prompt,.vue-command--invert .vue-command__query .vue-command__query__prompt--invert,.vue-command .vue-command__multiline-query--invert .vue-command__multiline-query__prompt,.vue-command .vue-command__multiline-query--invert .vue-command__query__prompt,.vue-command .vue-command__multiline-query--invert .vue-command__query__prompt--invert,.vue-command .vue-command__multiline-query .vue-command__multiline-query__prompt,.vue-command .vue-command__multiline-query .vue-command__query__prompt,.vue-command .vue-command__multiline-query .vue-command__query__prompt--invert,.vue-command .vue-command__query--invert .vue-command__multiline-query__prompt,.vue-command .vue-command__query--invert .vue-command__query__prompt,.vue-command .vue-command__query--invert .vue-command__query__prompt--invert,.vue-command .vue-command__query .vue-command__multiline-query__prompt,.vue-command .vue-command__query .vue-command__query__prompt,.vue-command .vue-command__query .vue-command__query__prompt--invert{margin-right:.25rem}.vue-command--invert .vue-command__multiline-query,.vue-command--invert .vue-command__multiline-query--invert,.vue-command .vue-command__multiline-query,.vue-command .vue-command__multiline-query--invert{display:flex}.vue-command--invert .vue-command__multiline-query--invert input::-moz-placeholder,.vue-command--invert .vue-command__multiline-query input::-moz-placeholder,.vue-command .vue-command__multiline-query--invert input::-moz-placeholder,.vue-command .vue-command__multiline-query input::-moz-placeholder{font:1rem Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace}.vue-command--invert .vue-command__multiline-query--invert input,.vue-command--invert .vue-command__multiline-query--invert input::placeholder,.vue-command--invert .vue-command__multiline-query input,.vue-command--invert .vue-command__multiline-query input::placeholder,.vue-command .vue-command__multiline-query--invert input,.vue-command .vue-command__multiline-query--invert input::placeholder,.vue-command .vue-command__multiline-query input,.vue-command .vue-command__multiline-query input::placeholder{font:1rem Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace}.vue-command--invert .vue-command__multiline-query--invert .vue-command__multiline-query__input,.vue-command--invert .vue-command__multiline-query--invert .vue-command__multiline-query__input--invert,.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query__input,.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query__input--invert,.vue-command .vue-command__multiline-query--invert .vue-command__multiline-query__input,.vue-command .vue-command__multiline-query--invert .vue-command__multiline-query__input--invert,.vue-command .vue-command__multiline-query .vue-command__multiline-query__input,.vue-command .vue-command__multiline-query .vue-command__multiline-query__input--invert{border:none;outline:none;flex:1;width:100%}.vue-command .vue-command__multiline-query .vue-command__multiline-query__input::-moz-placeholder,.vue-command .vue-command__multiline-query .vue-command__query__input::-moz-placeholder,.vue-command .vue-command__query .vue-command__multiline-query__input::-moz-placeholder,.vue-command .vue-command__query .vue-command__query__input::-moz-placeholder{color:hsla(0,0%,100%,.5)}.vue-command .vue-command__multiline-query .vue-command__multiline-query__input::placeholder,.vue-command .vue-command__multiline-query .vue-command__query__input::placeholder,.vue-command .vue-command__query .vue-command__multiline-query__input::placeholder,.vue-command .vue-command__query .vue-command__query__input::placeholder{color:hsla(0,0%,100%,.5)}.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query__input::-moz-placeholder,.vue-command--invert .vue-command__multiline-query .vue-command__query__input--invert::-moz-placeholder,.vue-command--invert .vue-command__query--invert .vue-command__multiline-query__input::-moz-placeholder,.vue-command--invert .vue-command__query--invert .vue-command__query__input--invert::-moz-placeholder{color:rgba(0,0,0,.5)}.vue-command--invert .vue-command__multiline-query .vue-command__multiline-query__input::placeholder,.vue-command--invert .vue-command__multiline-query .vue-command__query__input--invert::placeholder,.vue-command--invert .vue-command__query--invert .vue-command__multiline-query__input::placeholder,.vue-command--invert .vue-command__query--invert .vue-command__query__input--invert::placeholder{color:rgba(0,0,0,.5)}.vue-command,.vue-command--invert{font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;overflow-x:hidden;overflow-y:hidden}.vue-command--invert .vue-command__bar--invert:after,.vue-command--invert .vue-command__bar--invert:before,.vue-command--invert .vue-command__bar:after,.vue-command--invert .vue-command__bar:before,.vue-command .vue-command__bar--invert:after,.vue-command .vue-command__bar--invert:before,.vue-command .vue-command__bar:after,.vue-command .vue-command__bar:before{display:table}.vue-command--invert .vue-command__bar--invert:after,.vue-command--invert .vue-command__bar:after,.vue-command .vue-command__bar--invert:after,.vue-command .vue-command__bar:after{clear:both}.vue-command--invert .vue-command__bar,.vue-command--invert .vue-command__bar--invert,.vue-command .vue-command__bar,.vue-command .vue-command__bar--invert{display:flex;font-family:-apple-system,BlinkMacSystemFont,sans-serif;justify-content:space-between;padding-bottom:10px;padding-left:10px;padding-right:10px;padding-top:10px;position:inherit}.vue-command--invert .vue-command__bar__button,.vue-command--invert .vue-command__bar__button--invert,.vue-command .vue-command__bar__button,.vue-command .vue-command__bar__button--invert{display:inline-block;border-radius:100%}.vue-command--invert .vue-command__bar__button--invert:before,.vue-command--invert .vue-command__bar__button:before,.vue-command .vue-command__bar__button--invert:before,.vue-command .vue-command__bar__button:before{content:" ";display:block;height:12px;width:12px}.vue-command--invert .vue-command__bar__button--invert:not(:last-child),.vue-command--invert .vue-command__bar__button:not(:last-child),.vue-command .vue-command__bar__button--invert:not(:last-child),.vue-command .vue-command__bar__button:not(:last-child){margin-right:8px}.vue-command--invert .vue-command__history,.vue-command--invert .vue-command__history--invert,.vue-command .vue-command__history,.vue-command .vue-command__history--invert{display:block;height:100%;line-height:1.33;margin:0;overflow:auto;padding:12px 12px 12px 12px;white-space:pre-line;word-break:break-all}.vue-command--invert .vue-command__history--invert ul,.vue-command--invert .vue-command__history ul,.vue-command .vue-command__history--invert ul,.vue-command .vue-command__history ul{list-style-type:none;margin:0;padding:0}.vue-command--invert .vue-command__history--invert input,.vue-command--invert .vue-command__history--invert textarea,.vue-command--invert .vue-command__history input,.vue-command--invert .vue-command__history textarea,.vue-command .vue-command__history--invert input,.vue-command .vue-command__history--invert textarea,.vue-command .vue-command__history input,.vue-command .vue-command__history textarea{background:none;border:none;flex:1;font-size:1rem;outline:none;overflow:hidden;resize:none;width:100%}.vue-command--invert .vue-command__history__entry--fullscreen,.vue-command .vue-command__history__entry--fullscreen{height:100%}.vue-command .vue-command__bar{color:#f1f1f1;background-color:#111316}.vue-command .vue-command__bar__title{color:#f1f1f1}.vue-command .vue-command__bar__button--close{background-color:#ff5f58}.vue-command .vue-command__bar__button--minimize{background-color:#ffbd2e}.vue-command .vue-command__bar__button--fullscreen{background-color:#29ca41}.vue-command .vue-command__history{background-color:#111316;color:#f1f1f1}.vue-command .vue-command__history input,.vue-command .vue-command__history textarea{background:none;color:#fff}.vue-command .vue-command__history input::-moz-placeholder,.vue-command .vue-command__history textarea::-moz-placeholder{color:hsla(0,0%,100%,.5)}.vue-command .vue-command__history input::placeholder,.vue-command .vue-command__history textarea::placeholder{color:hsla(0,0%,100%,.5)}.vue-command--invert .vue-command__bar--invert{background-color:#eeece9}.vue-command--invert .vue-command__bar__title--invert{color:#0e0e0e}.vue-command--invert .vue-command__bar__button--close--invert{background-color:#00a0a7}.vue-command--invert .vue-command__bar__button--minimize--invert{background-color:#0042d1}.vue-command--invert .vue-command__bar__button--fullscreen--invert{background-color:#d635be}.vue-command--invert .vue-command__history--invert{background-color:#eeece9;color:#0e0e0e}.vue-command--invert .vue-command__history--invert input,.vue-command--invert .vue-command__history--invert textarea{background:none;color:#000}.vue-command--invert .vue-command__history--invert input::-moz-placeholder,.vue-command--invert .vue-command__history--invert textarea::-moz-placeholder{color:rgba(0,0,0,.5)}.vue-command--invert .vue-command__history--invert input::placeholder,.vue-command--invert .vue-command__history--invert textarea::placeholder{color:rgba(0,0,0,.5)}div[data-v-d2d1666e],textarea[data-v-d2d1666e]{height:100%}@media(min-width:1200px){.container{max-width:720px}}.vue-command,.vue-command--invert{width:100%}.vue-command--invert ::-webkit-scrollbar,.vue-command ::-webkit-scrollbar{width:6px}.vue-command--invert .vue-command__bar,.vue-command--invert .vue-command__bar--invert,.vue-command .vue-command__bar,.vue-command .vue-command__bar--invert{border-top-right-radius:6px;border-top-left-radius:6px}.vue-command--invert .vue-command__history,.vue-command--invert .vue-command__history--invert,.vue-command .vue-command__history,.vue-command .vue-command__history--invert{height:350px;border-bottom-right-radius:6px;border-bottom-left-radius:6px}.vue-command ::-webkit-scrollbar-track{background:#252525}.vue-command ::-webkit-scrollbar-thumb{background:#f1f1f1}.vue-command ::-webkit-scrollbar-thumb:hover{background:#333}.vue-command--invert ::-webkit-scrollbar-track{background:#dadada}.vue-command--invert ::-webkit-scrollbar-thumb{background:#0e0e0e}.vue-command--invert ::-webkit-scrollbar-thumb:hover{background:#ccc} 2 | /*# sourceMappingURL=app.85170694.css.map */ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-command 2 | 3 | A fully working, most feature-rich Vue.js terminal emulator. See the 4 | [demo](https://ndabap.github.io/vue-command/) and check the demo 5 | [source code](https://github.com/ndabAP/vue-command/blob/master/src/hosted/App.vue). 6 | In contrast to other terminal emulators, commands are not just 7 | mappings to strings but are native components with injectable 8 | environment variables. 9 | 10 | ## Features 11 | 12 | - Simple, yet extensible API 13 | - Supports asynchronous commands 14 | - Supports fullscreen mode 15 | - Customize the terminal with slots 16 | - Provide your own parser (falls back to simple one) 17 | - Multiline support (with \\) 18 | - Autocompletion resolver (with ) 19 | - Browse history (with /) 20 | - Search history (with Ctrl + r) 21 | - Provide your own event resolver to support additional keyboard events 22 | 23 | ## Installation 24 | 25 | ```bash 26 | $ npm install vue-command --save 27 | ``` 28 | 29 | ## Usage 30 | 31 | Let's start with a dead simple example. We want to send "Hello world" to 32 | `stdout` when entering `hello-world`. 33 | 34 | ```vue 35 | 38 | 39 | 55 | ``` 56 | 57 | Now a more complex one. Let's assume we want to build the nano editor available 58 | in many shells. 59 | 60 | We inject `terminal` to make sure the editor is only visible when the terminal 61 | is in fullscreen mode and also a function called `exit` to tell the terminal 62 | that the command has been finished when the user enters 63 | Ctrl + x. Furthermore, we use `setFullscreen` to 64 | switch the terminal into fullscreen mode. 65 | 66 | ```vue 67 | 74 | 75 | 88 | 89 | 95 | ``` 96 | 97 | Now the command has to return the component. 98 | 99 | ```vue 100 | 103 | 104 | 121 | ``` 122 | 123 | ## Properties 124 | 125 | Some properties can be mutated by the terminal. Therefore, adding the `v-model` 126 | directive is required. 127 | 128 | | Property | Description | Type | Default value | Required | Two-way binding | 129 | | -------------------- | ------------------------------------------- | ---------- | ------------------------- | -------- | --------------- | 130 | | `commands` | See [Commands](#commands) | `Object` | `{}` | No | No | 131 | | `cursor-position` | Cursor position | `Number` | `0` | No | Yes | 132 | | `dispatched-queries` | Non-empty dispatched queries | `Set` | `new Set()` | No | Yes | 133 | | `event-resolver` | See [Event resolver](#event-resolver) | `Function` | `newDefaultEventResolver` | No | No | 134 | | `font` | Terminal font | `String` | `''` | No | No | 135 | | `help-text` | Command help | `String` | `''` | No | Yes | 136 | | `help-timeout` | Command help timeout | `Number` | `3000` | No | No | 137 | | `hide-bar` | Hides the bar | `Boolean` | `false` | No | No | 138 | | `hide-buttons` | Hides the buttons | `Boolean` | `false` | No | No | 139 | | `hide-prompt` | Hides the prompt | `Boolean` | `false` | No | No | 140 | | `hide-title` | Hides the title | `Boolean` | `false` | No | No | 141 | | `history` | Terminal history | `Array` | `[]` | No | Yes | 142 | | `history-position` | Points to the latest dispatched query entry | `Number` | `0` | No | Yes | 143 | | `interpreter` | See [Interpreter](#interpreter) | `Function` | `null` | No | No | 144 | | `invert` | Inverts the terminals colors | `Boolean` | `false` | No | No | 145 | | `is-fullscreen` | Terminal fullscreen mode | `Boolean` | `false` | No | Yes | 146 | | `options-resolver` | See [Options resolver](#options-resolver) | `Function` | `null` | No | No | 147 | | `parser` | Query parser | `Function` | `defaultParser` | No | No | 148 | | `prompt` | Terminal prompt | `String` | `~$` | No | No | 149 | | `show-help` | Show query help | `Boolean` | `false` | No | No | 150 | | `title` | Terminal title | `String` | `~$` | No | No | 151 | | `query` | Terminal query | `String` | `''` | No | Yes | 152 | 153 | ### Commands 154 | 155 | `commands` must be an object containing key-value pairs where key is the command 156 | and the value is a function that will be called with the parsed arguments. The 157 | function can return a `Promise` and must return or resolve a Vue.js component. 158 | To return strings or a new query, use one of the convenient helper methods. 159 | 160 | Any component that is not the query component can inject the context. The 161 | context includes the parsed and raw query as fields. 162 | 163 | ### Event resolver 164 | 165 | It's possible to provide an array property `eventResolver` which is called when 166 | the terminal is mounted. Each event resolver will be called with the terminals 167 | references and exposed values. 168 | 169 | The libraries `defaultHistoryEventResolver` makes usage of that and allows to 170 | cycle through commands with /. 171 | 172 | ### Options resolver 173 | 174 | The terminal provides a built-in autocompletion for the given commands. As soon 175 | as the query has been autocompleted by the terminal, it's calling the options 176 | resolver provided as property. The resolver is called with the program, parsed 177 | query and a setter to update the query. 178 | 179 | ### Interpreter 180 | 181 | An interpreter allows to execute arbitrary code after the query has been 182 | dispatched and to not rely on missing functionality which includes pipes, 183 | streams or running multiple commands in parallel. 184 | 185 | The interpreter is a property function that is called with the unparsed query 186 | right after the query component calls `dispatch` and terminates it at the same 187 | time. After the call, you must use the [properties](#properties) and 188 | [exposed functions](#exposed) to reach the desired behaviour. 189 | 190 | ## Slots 191 | 192 | ### Bar 193 | 194 | You can replace the whole terminal bar with the named slot `bar`. This will 195 | replace the whole element, including the action buttons and its assigned CSS 196 | classes. Example: 197 | 198 | ```vue 199 | 200 | 203 | 204 | ``` 205 | 206 | ### Buttons 207 | 208 | Inside the bar, you can customize the buttons. If you use this slot, 209 | `hideButtons` property has no effect. Example: 210 | 211 | ```vue 212 | 213 | 216 | 217 | ``` 218 | 219 | ### Title 220 | 221 | Inside the bar, you can customize the title. If you use this slot, `hideTitle` 222 | and `title` property have no effect. Example: 223 | 224 | ```vue 225 | 226 | 229 | 230 | ``` 231 | 232 | ### Prompt 233 | 234 | You can overwrite the prompt with the prompt slot. If you use this slot, 235 | `hidePrompt` and `prompt` property have no effect. Example: 236 | 237 | ```vue 238 | 239 | 242 | 243 | ``` 244 | 245 | ## Library 246 | 247 | Library provides helper methods to render terminal related content. 248 | 249 | | Function | Parameters | Description | 250 | | ----------------------------- | ------------------------------------------------------------------ | ------------------------------------- | 251 | | `createCommandNotFound` | `command, text = 'command not found', name = 'VueCommandNotFound'` | Creates a command not found component | 252 | | `createStderr` | `formatterOrText, name = 'VueCommandStderr'` | Creates a "stderr" component | 253 | | `createStdout` | `formatterOrText, name = 'VueCommandStdout'` | Creates a "stdout" component | 254 | | `createQuery` | | Creates a query component | 255 | | `defaultHistoryEventResolver` | `refs, eventProvider` | The default history event resolver | 256 | | `defaultParser` | `query` | The default parser | 257 | | `defaultSignalEventResolver` | `refs, eventProvider` | The default signal event resolver | 258 | | `jsonFormatter` | `value` | See [Formatters](#formatters) | 259 | | `listFormatter` | `...lis` | See [Formatters](#formatters) | 260 | | `newDefaultEventResolver` | | Returns a new default event resolver | 261 | | `newDefaultHistory` | | Returns a new default history | 262 | | `tableFormatter` | `rows` | See [Formatters](#formatters) | 263 | | `textFormatter` | `text, innerHtml = false` | See [Formatters](#formatters) | 264 | 265 | Helper methods can be imported by name: 266 | 267 | ```js 268 | import { createStdout, createQuery } from 'vue-command' 269 | ``` 270 | 271 | ### Formatters 272 | 273 | The first argument of `createStdout` can be either a primitive 274 | (`Boolean`, `Number` or `String`) or a formatter. A formatter formats the 275 | content as a list or table or something else. 276 | 277 | | Function | Parameters | 278 | | ---------------- | ------------------------- | 279 | | `jsonFormatter` | `value` | 280 | | `listFormatter` | `...lis` | 281 | | `tableFormatter` | `rows` | 282 | | `textFormatter` | `text, innerHtml = false` | 283 | 284 | Formatters can be imported by name: 285 | 286 | ```js 287 | import { listFormatter } from 'vue-command' 288 | ``` 289 | 290 | ## Provided 291 | 292 | | Identifier | Type | Parameters | 293 | | -------------------- | ---------- | -------------------------------- | 294 | | `addDispatchedQuery` | `Function` | `dispatchedQuery` | 295 | | `appendToHistory` | `Function` | `...components` | 296 | | `dispatch` | `Function` | `query` | 297 | | `decrementHistory` | `Function` | | 298 | | `exit` | `Function` | | 299 | | `context` | `Object` | | 300 | | `helpText` | `String` | | 301 | | `helpTimeout` | `Number` | | 302 | | `hidePrompt` | `Boolean` | | 303 | | `incrementHistory` | `Function` | | 304 | | `optionsResolver` | `Function` | `program, parsedQuery, setQuery` | 305 | | `parser` | `Function` | `query` | 306 | | `programs` | `Array` | | 307 | | `sendSignal` | `Function` | `signal` | 308 | | `setCursorPosition` | `Function` | `cursorPosition` | 309 | | `setFullscreen` | `Function` | `isFullscreen` | 310 | | `setHistoryPosition` | `Function` | `historyPosition` | 311 | | `setQuery` | `Function` | `query` | 312 | | `showHelp` | `Boolean` | | 313 | | `signals` | `Object` | | 314 | | `slots` | `Object` | | 315 | | `terminal` | `Object` | | 316 | 317 | Provider can be injected into your component by name: 318 | 319 | ```js 320 | inject: ['exit', 'terminal'] 321 | ``` 322 | 323 | ## Exposed 324 | 325 | | Identifier | Type | Parameters | 326 | | -------------------- | ---------- | ----------------- | 327 | | `addDispatchedQuery` | `Function` | `dispatchedQuery` | 328 | | `appendToHistory` | `Function` | `...components` | 329 | | `decrementHistory` | `Function` | | 330 | | `dispatch` | `Function` | `query` | 331 | | `exit` | `Function` | | 332 | | `incrementHistory` | `Function` | | 333 | | `programs` | `Array` | | 334 | | `sendSignal` | `Function` | `signal` | 335 | | `setCursorPosition` | `Function` | `cursorPosition` | 336 | | `setFullscreen` | `Function` | `isFullscreen` | 337 | | `setHistoryPosition` | `Function` | `historyPosition` | 338 | | `setQuery` | `Function` | `query` | 339 | | `signals` | `Object` | | 340 | | `terminal` | `Object` | | 341 | 342 | ## Events 343 | 344 | | Name | Description | 345 | | ------------------- | ---------------------------------- | 346 | | `closeClicked` | Emitted on button close click | 347 | | `minimizeClicked` | Emitted on button minimize click | 348 | | `fullscreenClicked` | Emitted on button fullscreen click | 349 | 350 | ## Signals 351 | 352 | You can send and receive signals like `SIGINT`, `SIGTERM` or `SIGKILL`. `SIGINT` 353 | is the only implemented signal for now. When the user presses 354 | Ctrl + c, you can listen to this event by providing a 355 | signal name and a callback: 356 | 357 | ```js 358 | const signals = inject('signals') 359 | const sigint = () => { 360 | // Tear down component 361 | }; 362 | signals.on('SIGINT', sigint) 363 | ``` 364 | 365 | To unsubscribe from the signal, pass the same signal name and callback you used 366 | to subscribe to the signal. 367 | 368 | ```js 369 | signals.off('SIGINT', sigint) 370 | ``` 371 | 372 | The libraries query component makes usage of that and allows to cancel a query 373 | with `SIGINT` and appending `^C` to the query. 374 | 375 | ## Nice-to-haves 376 | 377 | These features didn't make it into the last release. If you would like to 378 | contribute please consult `CONTRIBUTING.md`. 379 | 380 | - Draggable terminal 381 | - More events (like query dispatched) 382 | - More key combinations 383 | 384 | ## Browser support 385 | 386 | This library uses the `ResizeObserver` to track if the terminal needs to scroll 387 | to the bottom. You may need a pollyfill to support your target browser. 388 | 389 | ## Projects using vue-command 390 | 391 | - [escrcpy](https://github.com/viarotel-org/escrcpy) - Display and control your Android device graphically with scrcpy (6k stars) 392 | - [linkandroid](https://github.com/modstart-lib/linkandroid) - Link Android and PC easily (1k stars) 393 | - [curvy-idle-game](https://github.com/n4n0GH/curvy-idle-game) - Short idle game 394 | where you get to pat her 395 | - [docker-management-dashboard](https://github.com/zero4994/docker-management-dashboard) - A management dashboard for your local docker containers 396 | - [saber-theme-klieh](https://github.com/krmax44/saber-theme-klieh) - A Saber 397 | theme mimicking a terminal 398 | - [ts-git](https://github.com/nfriend/ts-git) - A naïve implementation of git, 399 | written in TypeScript 400 | - [Venom](https://github.com/J0LGER/Venom) - Venom is a Command and Control framework 401 | 402 | ## Chuck Norris API 403 | 404 | The Chuck Norris jokes are comming from [this](https://api.chucknorris.io/) API. 405 | This library has no relation to Chuck Norris or the services provided by the 406 | API. 407 | 408 | ## Author 409 | 410 | [Julian Claus](https://www.julian-claus.de) and contributors. Special thanks to 411 | [krmax44](https://github.com/krmax44) for the amazing work! 412 | 413 | I apologize to some contributors that are not in the Git history anymore since I 414 | had to delete the repository because of problems with 415 | [semantic-release](https://github.com/semantic-release/semantic-release). 416 | 417 | ## License 418 | 419 | MIT 420 | -------------------------------------------------------------------------------- /src/components/VueCommand.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 586 | 587 | 763 | -------------------------------------------------------------------------------- /docs/js/app.6128f5a3.js: -------------------------------------------------------------------------------- 1 | (function(e){function t(t){for(var r,i,l=t[0],s=t[1],u=t[2],b=0,j=[];bu.cursorPosition=e),"dispatched-queries":u.dispatchedQueries,"onUpdate:dispatchedQueries":t[1]||(t[1]=e=>u.dispatchedQueries=e),"is-fullscreen":u.isFullscreen,"onUpdate:isFullscreen":t[2]||(t[2]=e=>u.isFullscreen=e),history:u.history,"onUpdate:history":t[3]||(t[3]=e=>u.history=e),"history-position":u.historyPosition,"onUpdate:historyPosition":t[4]||(t[4]=e=>u.historyPosition=e),query:u.query,"onUpdate:query":t[5]||(t[5]=e=>u.query=e),commands:u.commands,font:u.font,"help-text":u.helpText,"help-timeout":u.helpTimeout,"hide-bar":u.hideBar,"hide-buttons":u.hideButtons,"hide-prompt":u.hidePrompt,"hide-title":u.hideTitle,invert:u.invert,prompt:u.prompt,"options-resolver":u.optionsResolver,"show-help":u.showHelp,title:u.title},null,8,["cursor-position","dispatched-queries","is-fullscreen","history","history-position","query","commands","font","help-text","help-timeout","hide-bar","hide-buttons","hide-prompt","hide-title","invert","prompt","options-resolver","show-help","title"])]),Object(r["g"])("div",l,[Object(r["g"])("table",s,[t[26]||(t[26]=Object(r["g"])("thead",null,[Object(r["g"])("tr",null,[Object(r["g"])("th",{scope:"col"}," Property "),Object(r["g"])("th",{scope:"col"}," Value ")])],-1)),Object(r["g"])("tbody",null,[Object(r["g"])("tr",null,[t[10]||(t[10]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"cursor-position")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.cursorPosition),1)])])]),Object(r["g"])("tr",null,[t[11]||(t[11]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"dispatched-queries")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.dispatchedQueries),1)])])]),Object(r["g"])("tr",null,[t[12]||(t[12]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"help-text")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.helpText),1)])])]),Object(r["g"])("tr",null,[t[13]||(t[13]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"help-timeout")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.helpTimeout),1)])])]),Object(r["g"])("tr",null,[t[14]||(t[14]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"hide-bar")])],-1)),Object(r["g"])("td",null,[Object(r["I"])(Object(r["g"])("input",{"onUpdate:modelValue":t[6]||(t[6]=e=>u.hideBar=e),class:"form-check-input",type:"checkbox",value:""},null,512),[[r["E"],u.hideBar]])])]),Object(r["g"])("tr",null,[t[15]||(t[15]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"hide-buttons")])],-1)),Object(r["g"])("td",null,[Object(r["I"])(Object(r["g"])("input",{"onUpdate:modelValue":t[7]||(t[7]=e=>u.hideButtons=e),class:"form-check-input",type:"checkbox",value:""},null,512),[[r["E"],u.hideButtons]])])]),Object(r["g"])("tr",null,[t[16]||(t[16]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"hide-prompt")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.hidePrompt),1)])])]),Object(r["g"])("tr",null,[t[17]||(t[17]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"hide-title")])],-1)),Object(r["g"])("td",null,[Object(r["I"])(Object(r["g"])("input",{"onUpdate:modelValue":t[8]||(t[8]=e=>u.hideTitle=e),class:"form-check-input",type:"checkbox",value:""},null,512),[[r["E"],u.hideTitle]])])]),Object(r["g"])("tr",null,[t[18]||(t[18]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"help-timeout")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.helpTimeout),1)])])]),Object(r["g"])("tr",null,[t[19]||(t[19]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"history")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.history),1)])])]),Object(r["g"])("tr",null,[t[20]||(t[20]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"history-position")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.historyPosition),1)])])]),Object(r["g"])("tr",null,[t[21]||(t[21]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"invert")])],-1)),Object(r["g"])("td",null,[Object(r["I"])(Object(r["g"])("input",{"onUpdate:modelValue":t[9]||(t[9]=e=>u.invert=e),class:"form-check-input",type:"checkbox",value:""},null,512),[[r["E"],u.invert]])])]),Object(r["g"])("tr",null,[t[22]||(t[22]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"is-fullscreen")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.isFullscreen),1)])])]),Object(r["g"])("tr",null,[t[23]||(t[23]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"prompt")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.prompt),1)])])]),Object(r["g"])("tr",null,[t[24]||(t[24]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"query")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.query),1)])])]),Object(r["g"])("tr",null,[t[25]||(t[25]=Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,"title")])],-1)),Object(r["g"])("td",null,[Object(r["g"])("pre",null,[Object(r["g"])("code",null,Object(r["B"])(u.title),1)])])])])])])])])])}c("33d1"),c("14d9"),c("1e70"),c("79a4"),c("c1a1"),c("8b00"),c("a4e7"),c("1e5a"),c("72c3"),c("ea98"),c("5b81");var a=c("2ef0");const b=Symbol("publish"),j=()=>{const e={};return{[b](t){const c=Object(a["get"])(e,t);if(!Object(a["isUndefined"])(c))for(const e of c)e()},on(t,c){Object(a["isUndefined"])(Object(a["get"])(e,t))&&Object(a["set"])(e,t,[]),e[t].push(c)},off(t,c){const r=Object(a["get"])(e,t);if(!Object(a["isUndefined"])(r))for(const[n,o]of Object(a["entries"])(r))if(Object(a["eq"])(c,o))return void e[t].splice(n,1)}}},O=(...e)=>{for(const t of e)if(!t)return!1;return!0},d=(...e)=>{for(const t of e)if(t)return!0;return!1},p=(e,t)=>e^t,h={class:"vue-command__query"},m=["disabled","placeholder","onKeydown","onKeyup"],f=["disabled","value","onClick","onKeyup"],v={key:0,class:"vue-command__reverse-i-search"},y={class:"vue-command__reverse-i-search-status"},g=["disabled","onKeydown","onKeyup"];var _={__name:"VueCommandQuery",setup(e,{expose:t}){const c=Object(r["m"])("appendToHistory"),n=Object(r["m"])("dispatch"),o=Object(r["m"])("hidePrompt"),i=Object(r["m"])("helpText"),l=Object(r["m"])("helpTimeout"),s=Object(r["m"])("optionsResolver"),u=Object(r["m"])("parser"),b=Object(r["m"])("programs"),j=Object(r["m"])("setCursorPosition"),_=Object(r["m"])("setQuery"),w=Object(r["m"])("showHelp"),q=Object(r["m"])("signals"),k=Object(r["m"])("slots"),x=Object(r["m"])("terminal"),P=Object(r["w"])(!1),C=Object(r["w"])(!1),S=Object(r["w"])(null),Q=Object(r["w"])(""),H=Object(r["w"])(""),K=Object(r["w"])(null),E=Object(r["w"])("reverse-i-search"),R=Object(r["w"])(null),$=x.value.prompt,z=Object(r["b"])(()=>o?Object(r["l"])(r["a"]):Object(a["isUndefined"])(k.prompt)?Object(r["l"])(r["a"],Object(r["l"])("span",{class:"vue-command__query__prompt"},$)):Object(r["l"])(r["a"],k.prompt())),J=Object(r["b"])(()=>Object(a["isEmpty"])(x.value.font)?{}:{font:"1rem "+x.value.font}),U=Object(r["v"])({query:""}),V=Object(r["v"])([]),N=Object(r["b"])(()=>e=>p(!C.value,O(C.value,Object(a["lt"])(e,Object(a["size"])(V)-1)))),A=Object(r["b"])(()=>e=>d(P.value,O(!Object(a["isEmpty"])(V),!Object(a["eq"])(e,Object(a["size"])(V)-1)))),G=Object(r["b"])(()=>d(P.value,!Object(a["isEmpty"])(V))),L=Object(r["b"])(()=>{if(Object(a["isEmpty"])(V))return U.query;const e=Object(a["last"])(V);return e}),D=Object(r["b"])(()=>d(!C.value,O(C.value,!Object(a["isEmpty"])(V)))),M=async()=>{const e=U.query;if(Object(a["isEmpty"])(e))return;const t=F(e),n=Object(a["head"])(t),o=[];for(const c of b.value)c.startsWith(n)&&o.push(c);switch(Object(a["size"])(o)){case 0:break;case 1:{const t=Object(a["head"])(o);if(O(Object(a["lt"])(Object(a["size"])(t),Object(a["size"])(Object(a["trimStart"])(e))),Object(a["isFunction"])(s),Object(a["isFunction"])(u)))return void s(t,u(e),_);Object(a["gt"])(Object(a["size"])(t),Object(a["size"])(Object(a["trimStart"])(e)))&&_(t);break}default:P.value=!0,c(T(I(...o))),await Object(r["o"])(),_(U.query)}},W=()=>{},X=()=>{if(C.value)return void K.value.focus();if(Object(a["isEmpty"])(V))return void R.value.focus();const e=Object(a["last"])(S.value);e.focus()},Y=async()=>{C.value=!1,await Object(r["o"])(),X()},Z=()=>{K.value.style.width=parseInt(H.value.length)+"ch"},ee=e=>{Object(a["set"])(V,Object(a["size"])(V)-1,e)},te=async()=>{C.value=!0,await Object(r["o"])(),K.value.focus()},ce=()=>{if(Object(a["isEmpty"])(V)&&(U.query=U.query+"^C"),!Object(a["isEmpty"])(V)){const e=Object(a["last"])(V);ee(e+"^C")}P.value=!0,c(B())},re=async()=>{if(O(Object(a["eq"])(L.value.at(-1),"\\"),!Object(a["eq"])(L.value.slice(-2),"\\\\"))){V.push(""),await Object(r["o"])();const e=Object(a["last"])(S.value);return void e.focus()}P.value=!0,C.value=!1;const e=U.query.concat(Object(a["join"])(V,"")).replaceAll(/(?{q.off("SIGINT",ce),oe(),ue(),se(),Q.value="",ie(),ne(),le()}),oe=Object(r["H"])(()=>U.query,async()=>{await Object(r["o"])(),j(R.value.selectionStart)}),ie=Object(r["H"])(V,async()=>{await Object(r["o"])();const e=Object(a["last"])(S.value);j(e.selectionStart)}),le=Object(r["H"])(H,()=>{for(const e of x.value.dispatchedQueries)if(e.startsWith(H.value))return Object(a["isEmpty"])(V)&&_(e),Object(a["isEmpty"])(V)||ee(e),void(E.value="reverse-i-search");E.value="failed reverse-i-search"}),se=Object(r["H"])(()=>x.value.cursorPosition,async e=>{await Object(r["o"])(),R.value.setSelectionRange(e,e)}),ue=Object(r["H"])(()=>x.value.query,async e=>{await Object(r["o"])(),U.query=e});return Object(r["s"])(()=>{if(q.on("SIGINT",ce),_(""),j(0),R.value.focus(),w){const e=setTimeout(()=>{P.value||(Q.value=i)},l),t=Object(r["H"])(P,()=>{clearTimeout(e),t()})}}),Object(r["r"])(()=>{q.off("SIGINT",ce)}),t({focus:X}),(e,t)=>(Object(r["t"])(),Object(r["f"])("div",null,[Object(r["I"])(Object(r["g"])("div",h,[Object(r["i"])(Object(r["C"])(z)),Object(r["I"])(Object(r["g"])("input",{ref_key:"queryRef",ref:R,"onUpdate:modelValue":t[0]||(t[0]=e=>U.query=e),class:"vue-command__query__input",disabled:G.value,placeholder:Q.value,style:Object(r["q"])(J.value),autocapitalize:"none",autocorrect:"off",type:"text",onClick:t[1]||(t[1]=t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart)),onKeydown:[Object(r["J"])(Object(r["K"])(M,["exact","prevent"]),["tab"]),t[2]||(t[2]=Object(r["J"])(Object(r["K"])(e=>te(),["ctrl","exact","prevent"]),["r"]))],onKeyup:[t[3]||(t[3]=Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart),["exact"]),["arrow-left"])),t[4]||(t[4]=Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart),["exact"]),["arrow-right"])),t[5]||(t[5]=Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart),["exact"]),["end"])),Object(r["J"])(Object(r["K"])(re,["exact"]),["enter"])]},null,44,m),[[r["F"],U.query]])],512),[[r["G"],D.value]]),(Object(r["t"])(!0),Object(r["f"])(r["a"],null,Object(r["x"])(V,(c,n)=>Object(r["I"])((Object(r["t"])(),Object(r["f"])("div",{key:n,class:"vue-command__multiline-query"},[t[14]||(t[14]=Object(r["g"])("span",{class:"vue-command__multiline-query__prompt"},">",-1)),Object(r["g"])("input",{ref_for:!0,ref_key:"multilineQueryRefs",ref:S,class:"vue-command__multiline-query__input",disabled:A.value(n),style:Object(r["q"])(J.value),value:c,autocapitalize:"none",autocorrect:"off",type:"text",onClick:t=>Object(r["C"])(j)(e.$refs.multilineQueryRefs[n].selectionStart),onInput:t[6]||(t[6]=e=>ee(e.target.value)),onKeydown:t[7]||(t[7]=Object(r["J"])(Object(r["K"])(e=>te(),["ctrl","exact","prevent"]),["r"])),onKeyup:[Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.multilineQueryRefs[n].selectionStart),["exact"]),["arrow-left"]),Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.multilineQueryRefs[n].selectionStart),["exact"]),["arrow-right"]),Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.multilineQueryRefs[n].selectionStart),["exact"]),["end"]),Object(r["J"])(Object(r["K"])(re,["exact"]),["enter"])]},null,44,f)])),[[r["G"],N.value(n)]])),128)),C.value?(Object(r["t"])(),Object(r["f"])("div",v,[Object(r["g"])("span",y,"("+Object(r["B"])(E.value)+")`",1),Object(r["I"])(Object(r["g"])("input",{ref_key:"reverseISearchRef",ref:K,"onUpdate:modelValue":t[8]||(t[8]=e=>H.value=e),class:"vue-command__reverse-i-search__input",disabled:P.value,style:Object(r["q"])(J.value),autocapitalize:"none",autocorrect:"off",type:"text",onClick:t[9]||(t[9]=t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart)),onInput:Z,onKeydown:[Object(r["J"])(Object(r["K"])(W,["ctrl","exact","prevent"]),["r"]),t[10]||(t[10]=Object(r["J"])(Object(r["K"])(e=>Y(),["exact"]),["esc"]))],onKeyup:[t[11]||(t[11]=Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart),["exact"]),["arrow-left"])),t[12]||(t[12]=Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart),["exact"]),["arrow-right"])),t[13]||(t[13]=Object(r["J"])(Object(r["K"])(t=>Object(r["C"])(j)(e.$refs.queryRef.selectionStart),["exact"]),["end"])),Object(r["J"])(Object(r["K"])(re,["exact"]),["enter"])]},null,44,g),[[r["F"],H.value]]),Object(r["h"])("': "+Object(r["B"])(L.value),1)])):Object(r["e"])("",!0)]))}};c("bb75");const w=_;var q=w;const k="ArrowUp",x="ArrowDown",P="c",C=(e,t="command not found",c="VueCommandNotFound")=>{const r=`${e}: ${t}`;return T(r,c)},S=(e,t="VueCommandStderr")=>T(e,t),T=(e,t="VueCommandStdout")=>Object(r["n"])(Object(r["j"])({name:t,setup(){const e=Object(r["m"])("exit");Object(r["s"])(e)},render(){return Object(a["isFunction"])(e)?e():Object(r["l"])("div",e)}})),B=()=>Object(r["n"])(q),F=e=>e.split(/[ ]+/),Q=(e,{decrementHistory:t,incrementHistory:c})=>{const r=e.vueCommandRef,n=e=>{switch(e.key){case k:case x:switch(e.preventDefault(),e.key){case k:t();break;case x:c();break}}};r.addEventListener("keydown",n)},H=(e,{sendSignal:t})=>{const c=e=>{switch(e.ctrlKey){case!0:switch(e.key){case P:e.preventDefault(),t("SIGINT")}break;case!1:break}};window.addEventListener("keydown",c)},I=(...e)=>()=>{const t=[];return Object(a["forEach"])(e,e=>{t.push(Object(r["l"])("li",e))}),Object(r["l"])("ul",t)},K=()=>[B()],E=()=>[Q,H],R=e=>()=>{const t=[];return Object(a["forEach"])(e,e=>{const c=[];Object(a["forEach"])(e,e=>{c.push(Object(r["l"])("td",e))}),t.push(Object(r["l"])("tr",c))}),Object(r["l"])("table",t)};var $={__name:"VueCommand",props:{commands:{default:()=>({}),required:!1,type:Object},cursorPosition:{default:0,required:!1,type:Number},dispatchedQueries:{default:new Set,required:!1,type:Set},eventResolver:{default:()=>E(),required:!1,type:Array},font:{default:"",required:!1,type:String},helpText:{default:null,required:!1,type:String},helpTimeout:{default:3e3,required:!1,type:Number},hideBar:{default:!1,required:!1,type:Boolean},hideButtons:{default:!1,required:!1,type:Boolean},hidePrompt:{default:!1,required:!1,type:Boolean},hideTitle:{default:!1,required:!1,type:Boolean},history:{default:()=>K(),required:!1,type:Array},historyPosition:{default:0,required:!1,type:Number},invert:{default:!1,required:!1,type:Boolean},interpreter:{default:null,required:!1,type:Function},isFullscreen:{default:!1,required:!1,type:Boolean},optionsResolver:{default:null,required:!1,type:Function},parser:{default:e=>F(e),required:!1,type:Function},prompt:{default:"~$",required:!1,type:String},showHelp:{default:!1,required:!1,type:Boolean},title:{default:"~$",required:!1,type:String},query:{default:"",required:!1,type:String}},emits:["closeClicked","minimizeClicked","fullscreenClicked","update:cursorPosition","update:dispatchedQueries","update:history","update:historyPosition","update:isFullscreen","update:query"],setup(e,{expose:t,emit:c}){const n=Object(r["D"])(),o=e,i=c,l=Object(r["w"])(null),s=Object(r["w"])(null),u=Object(r["w"])(null),p=Object(r["v"])({cursorPosition:o.cursorPosition,dispatchedQueries:o.dispatchedQueries,history:o.history,historyPosition:o.historyPosition,isFullscreen:o.isFullscreen,prompt:o.prompt,query:o.query}),h=Object(r["v"])(j()),m=Object(r["b"])(()=>({cursorPosition:p.cursorPosition,dispatchedQueries:p.dispatchedQueries,font:o.font,history:p.history,historyPosition:p.historyPosition,invert:o.invert,isFullscreen:p.isFullscreen,prompt:p.prompt,query:p.query})),f=Object(r["b"])(()=>Object(a["keys"])(o.commands)),v=Object(r["b"])(()=>e=>O(p.isFullscreen,Object(a["eq"])(e,Object(a["size"])(p.history)-1))),y=Object(r["b"])(()=>e=>d(!p.isFullscreen,O(p.isFullscreen,Object(a["eq"])(e,Object(a["size"])(p.history)-1)))),g=e=>{p.dispatchedQueries.delete(e),p.dispatchedQueries.add(e),i("update:dispatchedQueries",p.dispatchedQueries)},_=()=>{if(p.isFullscreen)return;const e=Object(a["last"])(p.history);if(!Object(a["eq"])(Object(a["get"])(e,"__name"),"VueCommandQuery"))return;const t=Object(a["last"])(l.value);t.focus()},w=(...e)=>{p.history.push(...e),i("update:history",p.history)},q=async e=>{if(Object(a["isFunction"])(o.interpreter))return void o.interpreter(e);if(Object(a["isEmpty"])(e))return void w(B());g(e);const t=o.parser(e),c=Object(a["head"])(F(e)),n=Object(a["get"])(o.commands,c);if(!Object(a["isFunction"])(n))return void w(C(c));const i=await Promise.resolve(n(t));if(Object(a["eq"])(Object(a["get"])(i,"__name"),"VueCommandQuery"))return void k();const l=Object(r["j"])({name:"VueCommandOut",provide(){return{context:{rawQuery:e,parsedQuery:t}}},render:()=>Object(r["l"])(i)});w(Object(r["n"])(l))},k=()=>{w(B()),T(0),Q(!1),H(p.dispatchedQueries.size),I("")},x=()=>{if(Object(a["eq"])(p.historyPosition,0))return;H(p.historyPosition-1);const e=Object(a["nth"])([...p.dispatchedQueries],p.historyPosition);I(e)},P=()=>{if(!Object(a["lt"])(p.historyPosition,p.dispatchedQueries.size))return;H(p.historyPosition+1);const e=Object(a["nth"])([...p.dispatchedQueries],p.historyPosition);I(e)},S=e=>{h[b](e)},T=e=>{p.cursorPosition=e,i("update:cursorPosition",e)},Q=e=>{p.isFullscreen=e,i("update:isFullscreen",e)},H=e=>{p.historyPosition=e,i("update:historyPosition",e)},I=e=>{p.query=e,i("update:query",e)};return Object(r["H"])(()=>o.cursorPosition,e=>{p.cursorPosition=e}),Object(r["H"])(()=>o.dispatchedQueries,e=>{p.dispatchedQueries=e}),Object(r["H"])(()=>o.history,e=>{p.history=e}),Object(r["H"])(()=>o.historyPosition,e=>{p.historyPosition=e}),Object(r["H"])(()=>o.isFullscreen,e=>{p.isFullscreen=e}),Object(r["H"])(()=>o.prompt,e=>{p.prompt=e}),Object(r["H"])(()=>o.query,e=>{p.query=e}),Object(r["s"])(()=>{const e=Object(r["k"])();for(const c of o.eventResolver)c(e.refs,e.exposed);const t=new ResizeObserver(()=>{s.value.scrollTop=s.value.scrollHeight});for(const c of s.value.children)t.observe(c);Object(r["H"])(p.history,async()=>{await Object(r["o"])(),t.disconnect();for(const e of s.value.children)t.observe(e)})}),Object(r["u"])("addDispatchedQuery",g),Object(r["u"])("appendToHistory",w),Object(r["u"])("dispatch",q),Object(r["u"])("decrementHistory",x),Object(r["u"])("exit",k),Object(r["u"])("helpText",o.helpText),Object(r["u"])("helpTimeout",o.helpTimeout),Object(r["u"])("hidePrompt",o.hidePrompt),Object(r["u"])("incrementHistory",P),Object(r["u"])("optionsResolver",o.optionsResolver),Object(r["u"])("parser",o.parser),Object(r["u"])("programs",f),Object(r["u"])("sendSignal",S),Object(r["u"])("setCursorPosition",T),Object(r["u"])("setFullscreen",Q),Object(r["u"])("setHistoryPosition",H),Object(r["u"])("setQuery",I),Object(r["u"])("showHelp",o.showHelp),Object(r["u"])("signals",h),Object(r["u"])("slots",n),Object(r["u"])("terminal",m),t({addDispatchedQuery:g,appendToHistory:w,decrementHistory:x,dispatch:q,exit:k,incrementHistory:P,programs:f,sendSignal:S,setCursorPosition:T,setFullscreen:Q,setHistoryPosition:H,setQuery:I,signals:h,terminal:m}),(t,c)=>(Object(r["t"])(),Object(r["f"])("div",{ref_key:"vueCommandRef",ref:u,class:Object(r["p"])({"vue-command":!e.invert,"vue-command--invert":e.invert}),style:Object(r["q"])(e.font?{"font-family":e.font}:{})},[e.hideBar?Object(r["e"])("",!0):Object(r["y"])(t.$slots,"bar",{key:0},()=>[Object(r["g"])("div",{class:Object(r["p"])({"vue-command__bar":!e.invert,"vue-command__bar--invert":e.invert})},[Object(r["g"])("div",null,[e.hideButtons?Object(r["e"])("",!0):Object(r["y"])(t.$slots,"buttons",{key:0},()=>[Object(r["g"])("span",{class:Object(r["p"])({"vue-command__bar__button":!e.invert,"vue-command__bar__button--invert":e.invert,"vue-command__bar__button--fullscreen":!e.invert,"vue-command__bar__button--fullscreen--invert":e.invert}),onClick:c[0]||(c[0]=e=>i("closeClicked"))},null,2),Object(r["g"])("span",{class:Object(r["p"])({"vue-command__bar__button":!e.invert,"vue-command__bar__button--invert":e.invert,"vue-command__bar__button--minimize":!e.invert,"vue-command__bar__button--minimize--invert":e.invert}),onClick:c[1]||(c[1]=e=>i("minimizeClicked"))},null,2),Object(r["g"])("span",{class:Object(r["p"])({"vue-command__bar__button":!e.invert,"vue-command__bar__button--invert":e.invert,"vue-command__bar__button--close":!e.invert,"vue-command__bar__button--close--invert":e.invert}),onClick:c[2]||(c[2]=e=>i("fullscreenClicked"))},null,2)])]),Object(r["g"])("div",null,[e.hideTitle?Object(r["e"])("",!0):Object(r["y"])(t.$slots,"title",{key:0},()=>[Object(r["g"])("span",{class:Object(r["p"])({"vue-command__bar__title":!e.invert,"vue-command__bar__title--invert":e.invert})},Object(r["B"])(e.title),3)])]),c[3]||(c[3]=Object(r["g"])("div",null,"​",-1))],2)]),Object(r["g"])("div",{ref_key:"vueCommandHistoryRef",ref:s,class:Object(r["p"])({"vue-command__history":!e.invert,"vue-command__history--invert":e.invert}),onClick:_},[(Object(r["t"])(!0),Object(r["f"])(r["a"],null,Object(r["x"])(p.history,(t,c)=>Object(r["I"])((Object(r["t"])(),Object(r["f"])("div",{key:c,class:Object(r["p"])({"vue-command__history__entry":!e.invert,"vue-command__history__entry--invert":e.invert,"vue-command__history__entry--fullscreen":v.value(c),"vue-command__history__entry--fullscreen--invert":Object(r["C"])(O)(e.invert,v.value(c))})},[(Object(r["t"])(),Object(r["d"])(Object(r["A"])(t),{ref_for:!0,ref_key:"vueCommandHistoryEntryComponentRefs",ref:l,class:Object(r["p"])({"vue-command__history__entry__component":!e.invert,"vue-command__history__entry__component--invert":e.invert})},null,8,["class"]))],2)),[[r["G"],y.value(c)]])),128))],2)],6))}};c("b5c6");const z=$;var J=z;const U={key:0},V={key:1},N={key:2};function A(e,t,c,n,o,i){return Object(r["t"])(),Object(r["f"])("div",null,[e.isLoading||e.isError?Object(r["e"])("",!0):(Object(r["t"])(),Object(r["f"])("span",U,Object(r["B"])(e.joke),1)),e.isLoading&&!e.isError?(Object(r["t"])(),Object(r["f"])("span",V,Object(r["B"])(e.loadingText),1)):Object(r["e"])("",!0),e.isError?(Object(r["t"])(),Object(r["f"])("span",N,"There was an error getting the joke")):Object(r["e"])("",!0)])}const G="https://api.chucknorris.io/jokes/random";var L={inject:["exit","signals"],data:()=>({isError:!1,isLoading:!0,joke:"",loadingText:"Loading ..."}),async mounted(){const e=new AbortController,t=()=>{e.abort(),this.signals.off("SIGINT")};this.signals.on("SIGINT",t);try{const c=await fetch(G,{signal:e.signal});if(this.signals.off("SIGINT",t),!c.ok)return this.isLoading=!1,this.isError=!0,void this.exit();const{value:r}=await c.json();this.joke=r,this.isLoading=!1}catch(c){"AbortError"===c.name?this.loadingText=this.loadingText+"^C":(this.isError=!0,this.isLoading=!1)}finally{this.signals.off("SIGINT",t),this.exit()}}},D=c("6b0d"),M=c.n(D);const W=M()(L,[["render",A]]);var X=W;function Y(e,t,c,n,o,i){return Object(r["I"])((Object(r["t"])(),Object(r["f"])("div",null,[Object(r["g"])("textarea",{ref:"nano",onKeyup:t[0]||(t[0]=Object(r["J"])(Object(r["K"])((...e)=>i.exit&&i.exit(...e),["ctrl","exact"]),["x"]))},"This is a nano text editor emulator! Press Ctrl + x to leave.",544)],512)),[[r["G"],i.terminal.isFullscreen]])}var Z={inject:["exit","setFullscreen","terminal"],created(){this.setFullscreen(!0)},mounted(){this.$refs.nano.focus()}};c("7787");const ee=M()(Z,[["render",Y],["__scopeId","data-v-d2d1666e"]]);var te=ee;const ce="neil@moon~$";var re={components:{VueCommand:J},setup(){const e=Object(r["w"])(0),t=Object(r["w"])(new Set),c=Object(r["w"])(""),n=Object(r["w"])("Type in help"),o=Object(r["w"])(3500),i=Object(r["w"])(!1),l=Object(r["w"])(!1),s=Object(r["w"])(!1),u=Object(r["w"])(!1),a=Object(r["w"])(K()),b=Object(r["w"])(0),j=Object(r["w"])(!1),O=Object(r["w"])(!1),d=Object(r["w"])(ce),p=Object(r["w"])(""),h=Object(r["w"])(!0),m=Object(r["w"])("bash - 720x350"),f=(e,t,c)=>{const r=t[t.length-1];switch(e){case"cd":switch(t.length){case 0:break;case 1:c("cd home");break;default:"home".startsWith(r)&&"home"!==r&&c("cd home");break}break}},v={cd:e=>{if(e.length<2||"."===e[e.length-1])return B();const t=e[e.length-1];return"home"===t&&(d.value=ce+"/home"),"../"!==t&&".."!==t||d.value!==ce+"/home"||(d.value=""+ce),"home"!==t&&"../"!==t&&".."!==t?S(`bash: cd: ${t}: No such file or directory`):B()},clear:()=>(a.value.splice(0,a.value.length),B()),"hello-world":()=>T("Hello world"),history:()=>{const e=[];for(const[c,r]of[...t.value].entries())e.push([c,r]);return T(R(e))},nano:()=>te,norris:()=>X,"set-font":e=>{if(e.length<2)return S("Missing font");const t=e.at(1);return t?(c.value=t.replace(/['"]+/g,""),B()):S("Missing font")},help:()=>{const e=Object.keys(v);return T(I(...e))}};return{commands:v,cursorPosition:e,dispatchedQueries:t,helpText:n,helpTimeout:o,hideBar:i,hideButtons:l,hidePrompt:s,hideTitle:u,history:a,historyPosition:b,invert:j,isFullscreen:O,prompt:d,query:p,showHelp:h,title:m,font:c,optionsResolver:f}}};c("c6fa");const ne=M()(re,[["render",u]]);var oe=ne,ie=c("9483");Object(ie["a"])("service-worker.js",{ready(){console.log("App is being served from cache by a service worker.\nFor more details, visit https://goo.gl/AFskqB")},registered(){console.log("Service worker has been registered.")},cached(){console.log("Content has been cached for offline use.")},updatefound(){console.log("New content is downloading.")},updated(){console.log("New content is available; please refresh.")},offline(){console.log("No internet connection found. App is running in offline mode.")},error(e){console.error("Error during service worker registration:",e)}});const le=Object(r["c"])(oe);le.config.unwrapInjectedRef=!0,le.mount("#app")},"714a":function(e,t,c){},7787:function(e,t,c){"use strict";c("8689")},8689:function(e,t,c){},b5c6:function(e,t,c){"use strict";c("714a")},bb75:function(e,t,c){"use strict";c("d0f3")},c6fa:function(e,t,c){"use strict";c("3091")},d0f3:function(e,t,c){}}); 2 | //# sourceMappingURL=app.6128f5a3.js.map --------------------------------------------------------------------------------