├── .vscode
└── extensions.json
├── og:image.png
├── packages
├── CommandProvider.vue
├── useCommandEvent.ts
├── utils.ts
├── types.ts
├── CommandGroup.vue
├── CommandInput.vue
├── useCommandState.ts
├── CommandList.vue
├── CommandDialog.vue
├── CommandItem.vue
├── index.ts
└── CommandRoot.vue
├── .github
└── FUNDING.yml
├── vue-command-palette.gif
├── .prettierrc
├── src
├── composables
│ └── useDarkmode.ts
├── vite-env.d.ts
├── components
│ ├── common
│ │ └── CmdkPlaceholder.vue
│ ├── command
│ │ ├── vercel
│ │ │ ├── Projects.vue
│ │ │ ├── Item.vue
│ │ │ ├── Vercel.vue
│ │ │ └── Home.vue
│ │ ├── raycast
│ │ │ ├── Item.vue
│ │ │ ├── Raycast.vue
│ │ │ └── Home.vue
│ │ ├── Linear.vue
│ │ └── Self.vue
│ └── icons
│ │ ├── VercelIcon.vue
│ │ ├── LinearChangePriorityIcon.vue
│ │ ├── RaycasTerminalIcon.vue
│ │ ├── VercelPlusIcon.vue
│ │ ├── VercelProjectsIcon.vue
│ │ ├── VercelContactIcon.vue
│ │ ├── MoonIcon.vue
│ │ ├── VercelTeamsIcon.vue
│ │ ├── VercelDocsIcon.vue
│ │ ├── VercelFeedbackIcon.vue
│ │ ├── VercelCopyIcon.vue
│ │ ├── YouTubeIcon.vue
│ │ ├── LinearChangeLabelsIcon.vue
│ │ ├── LinearRemoveLabelIcon.vue
│ │ ├── LinearAssignToIcon.vue
│ │ ├── RaycastWindowIcon.vue
│ │ ├── LinearChangeStatusIcon.vue
│ │ ├── FigmaIcon.vue
│ │ ├── RaycastFinderIcon.vue
│ │ ├── LinearSetDueDateIcon.vue
│ │ ├── RaycastClipboardIcon.vue
│ │ ├── LinearIcon.vue
│ │ ├── RaycastIcon.vue
│ │ ├── SunIcon.vue
│ │ ├── RaycastHammerIcon.vue
│ │ ├── RaycastDarkIcon.vue
│ │ ├── RaycastStarIcon.vue
│ │ ├── RaycastLightIcon.vue
│ │ ├── LinearAssignToMeIcon.vue
│ │ └── SlackIcon.vue
├── assets
│ └── scss
│ │ ├── highlight.scss
│ │ ├── vercel.scss
│ │ ├── linear.scss
│ │ ├── global.scss
│ │ ├── algolia.scss
│ │ ├── framer.scss
│ │ └── raycast.scss
├── main.ts
├── plugins
│ └── highlight.ts
└── App.vue
├── tsconfig.node.json
├── .gitignore
├── index.html
├── tsconfig.build.json
├── unocss.config.ts
├── tsconfig.json
├── api-extractor.json
├── LICENSE
├── package.json
├── vite.config.ts
├── components.d.ts
├── CHANGELOG.md
└── README.md
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/og:image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoluoboding/vue-command-palette/HEAD/og:image.png
--------------------------------------------------------------------------------
/packages/CommandProvider.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [xiaoluoboding]
4 |
--------------------------------------------------------------------------------
/vue-command-palette.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoluoboding/vue-command-palette/HEAD/vue-command-palette.gif
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "useTabs": false,
4 | "singleQuote": true,
5 | "semi": false,
6 | "trailingComma": "none",
7 | "printWidth": 80
8 | }
--------------------------------------------------------------------------------
/src/composables/useDarkmode.ts:
--------------------------------------------------------------------------------
1 | import { useDark, useToggle } from '@vueuse/core'
2 |
3 | export const isDark = useDark()
4 | export const toggleDarkmode = useToggle(isDark)
5 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import type { DefineComponent } from 'vue'
5 |
6 | const component: DefineComponent<{}, {}, any>
7 | export default component
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/common/CmdkPlaceholder.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/command/vercel/Projects.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | -
7 | {{ `Project ${n}` }}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/icons/VercelIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/src/assets/scss/highlight.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:meta';
2 |
3 | html {
4 | @include meta.load-css('highlight.js/styles/github');
5 | }
6 | html.dark {
7 | @include meta.load-css('highlight.js/styles/github-dark');
8 | }
9 |
10 | code.hljs {
11 | border-radius: 16px;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | import 'uno.css'
5 | import '@unocss/reset/tailwind-compat.css'
6 | import './assets/scss/global.scss'
7 | import highlight from '~/plugins/highlight'
8 |
9 | const app = createApp(App)
10 |
11 | app.use(highlight)
12 | app.mount('#app')
13 |
--------------------------------------------------------------------------------
/src/components/icons/LinearChangePriorityIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | lib
13 | dist-ssr
14 | *.local
15 |
16 | # Editor directories and files
17 | .vscode/*
18 | !.vscode/extensions.json
19 | .idea
20 | temp
21 | .DS_Store
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
27 |
--------------------------------------------------------------------------------
/src/components/icons/RaycasTerminalIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/src/components/icons/VercelPlusIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Vue Command Palette
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/useCommandEvent.ts:
--------------------------------------------------------------------------------
1 | import { ref } from 'vue'
2 | import type { ItemInfo } from './types'
3 |
4 | export interface Events {
5 | selectItem: ItemInfo
6 | rerenderList: boolean
7 | }
8 |
9 | function useCommandEvent() {
10 | const itemInfo = ref()
11 | const rerenderList = ref(false)
12 |
13 | return {
14 | itemInfo,
15 | rerenderList,
16 | }
17 | }
18 |
19 | export { useCommandEvent }
20 |
--------------------------------------------------------------------------------
/src/components/icons/VercelProjectsIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/icons/VercelContactIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/icons/MoonIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/src/components/icons/VercelTeamsIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/icons/VercelDocsIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "noEmit": false,
6 | "emitDeclarationOnly": true,
7 | "outDir": "lib",
8 | "declarationDir": "./temp",
9 | "moduleResolution": "Node",
10 | "baseUrl": "./",
11 | },
12 | "vueCompilerOptions": {
13 | "skipTemplateCodegen": true,
14 | },
15 | "include": [
16 | "packages/**/*.ts",
17 | "packages/**/*.d.ts",
18 | "packages/**/*.tsx",
19 | "packages/**/*.vue"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/icons/VercelFeedbackIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/unocss.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, presetAttributify, presetUno } from 'unocss'
2 |
3 | export default defineConfig({
4 | presets: [presetUno(), presetAttributify()],
5 | shortcuts: [
6 | {
7 | 'flex-center': 'flex justify-center items-center',
8 | 'flex-col-center': 'flex flex-col justify-center items-center',
9 | 'flex-between': 'flex justify-between items-center',
10 | 'text-neon':
11 | 'text-transparent bg-clip-text bg-gradient-to-r from-emerald-500 to-emerald-700 dark:to-emerald-300 ',
12 | },
13 | ],
14 | })
15 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "moduleResolution": "Node",
6 | "paths": {
7 | "@/*": ["packages/*"],
8 | "~/*": ["./src/*"]
9 | },
10 | "sourceMap": true,
11 | },
12 | "references": [{ "path": "./tsconfig.node.json" }],
13 | "include": [
14 | "src/**/*.ts",
15 | "src/**/*.d.ts",
16 | "src/**/*.tsx",
17 | "src/**/*.vue",
18 | "packages/**/*.ts",
19 | "packages/**/*.d.ts",
20 | "packages/**/*.tsx",
21 | "packages/**/*.vue"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Helpers
3 | */
4 |
5 | export function findNextSibling(el: Element, selector: string) {
6 | let sibling = el.nextElementSibling
7 |
8 | while (sibling) {
9 | if (sibling.matches(selector))
10 | return sibling
11 | sibling = sibling.nextElementSibling
12 | }
13 | }
14 |
15 | export function findPreviousSibling(el: Element, selector: string) {
16 | let sibling = el.previousElementSibling
17 |
18 | while (sibling) {
19 | if (sibling.matches(selector))
20 | return sibling
21 | sibling = sibling.previousElementSibling
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/icons/VercelCopyIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/icons/YouTubeIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/icons/LinearChangeLabelsIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/icons/LinearRemoveLabelIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/icons/LinearAssignToIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastWindowIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/command/vercel/Item.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 | {{ key }}
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/command/raycast/Item.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 | {{ isCommand ? 'Command' : 'Application' }}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/components/icons/LinearChangeStatusIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/icons/FigmaIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastFinderIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/types.ts:
--------------------------------------------------------------------------------
1 | import type { IFuseOptions } from 'fuse.js'
2 |
3 | export interface ItemInfo {
4 | key: string
5 | value: string
6 | }
7 |
8 | export type Noop = () => void
9 |
10 | export interface CommandRootProps {
11 | theme?: string
12 | fuseOptions?: IFuseOptions
13 | }
14 |
15 | export type CommandRootEmits = {
16 | selectItem: [item: ItemInfo]
17 | }
18 |
19 | export interface CommandItemProps {
20 | shortcut?: string[]
21 | perform?: Noop
22 | }
23 |
24 | export type CommandItemEmits = {
25 | select: [item: ItemInfo]
26 | }
27 |
28 | export interface CommandInputProps {
29 | placeholder?: string
30 | value?: string
31 | }
32 |
33 | export type CommandInputEmits = {
34 | input: [ie: InputEvent]
35 | 'update:value': [val: any]
36 | }
37 |
38 | export interface CommandGroupProps {
39 | heading: string
40 | }
41 |
--------------------------------------------------------------------------------
/api-extractor.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3 | "mainEntryPointFilePath": "/temp/index.d.ts",
4 | "bundledPackages": [],
5 | "compiler": {
6 | "tsconfigFilePath": "/tsconfig.build.json"
7 | },
8 | "apiReport": {
9 | "enabled": false
10 | },
11 | "docModel": {
12 | "enabled": false
13 | },
14 | "dtsRollup": {
15 | "enabled": true,
16 | "untrimmedFilePath": "/lib/.d.ts"
17 | },
18 | "tsdocMetadata": {
19 | "enabled": false
20 | },
21 | "messages": {
22 | "extractorMessageReporting": {
23 | "ae-forgotten-export": {
24 | "logLevel": "none"
25 | },
26 | "ae-missing-release-tag": {
27 | "logLevel": "none"
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/icons/LinearSetDueDateIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastClipboardIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/icons/LinearIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/icons/SunIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
28 |
29 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastHammerIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastDarkIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastStarIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/CommandGroup.vue:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
32 |
33 | {{ heading }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/components/icons/RaycastLightIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Yunwei Xiao
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 |
--------------------------------------------------------------------------------
/packages/CommandInput.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
33 |
34 |
35 |
49 |
50 |
--------------------------------------------------------------------------------
/packages/useCommandState.ts:
--------------------------------------------------------------------------------
1 | import { computed, reactive, toRefs } from 'vue'
2 |
3 | interface FilteredItem {
4 | count: number
5 | items: Map
6 | groups: Set
7 | }
8 |
9 | interface State {
10 | // Event State
11 | selectedNode: string
12 | selectedGroup: string
13 | shouldRerender: boolean
14 | // Input State
15 | search: string
16 | filtered: FilteredItem
17 | }
18 |
19 | const state = reactive({
20 | selectedNode: '',
21 | selectedGroup: '',
22 | shouldRerender: false,
23 | /** Value of the search query. */
24 | search: '',
25 | filtered: {
26 | /** The count of all visible items. */
27 | count: 0,
28 | /** Map from visible item id. */
29 | items: new Map(),
30 | /** Set of groups with at least one visible item. */
31 | groups: new Set(),
32 | },
33 | })
34 |
35 | function useCommandState() {
36 | const isSearching = computed(() => state.search !== '')
37 |
38 | const resetStore = () => {
39 | // reset the command state
40 | state.search = ''
41 | state.filtered.count = 0
42 | state.filtered.items = new Map()
43 | state.filtered.groups = new Set()
44 | }
45 |
46 | return {
47 | isSearching,
48 | resetStore,
49 | ...toRefs(state),
50 | }
51 | }
52 |
53 | export { useCommandState }
54 |
--------------------------------------------------------------------------------
/src/components/icons/LinearAssignToMeIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/src/components/icons/SlackIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
41 |
42 |
--------------------------------------------------------------------------------
/src/components/command/raycast/Raycast.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | No results found.
30 |
31 |
32 |
33 |
34 |
35 |
36 |
40 |
41 |
42 |
47 |
48 |
49 |
50 |
51 |
54 |
--------------------------------------------------------------------------------
/packages/CommandList.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
54 |
55 |
56 |
61 |
62 |
--------------------------------------------------------------------------------
/packages/CommandDialog.vue:
--------------------------------------------------------------------------------
1 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-command-palette",
3 | "type": "module",
4 | "version": "0.2.3",
5 | "author": "xiaoluoboding ",
6 | "homepage": "https://github.com/xiaoluoboding/vue-command-palette",
7 | "repository": {
8 | "type": "git",
9 | "url": "git+https://github.com/xiaoluoboding/vue-command-palette.git"
10 | },
11 | "exports": {
12 | ".": {
13 | "types": "./lib/index.d.ts",
14 | "import": "./lib/vue-command-palette.js",
15 | "require": "./lib/vue-command-palette.cjs"
16 | }
17 | },
18 | "main": "./lib/vue-command-palette.cjs",
19 | "module": "./lib/vue-command-palette.js",
20 | "types": "./lib/index.d.ts",
21 | "files": [
22 | "lib"
23 | ],
24 | "scripts": {
25 | "dev": "vite",
26 | "build:docs": "vite build --mode docs",
27 | "build:lib": "vite build --mode lib && pnpm run build:types",
28 | "build:types": "vue-tsc -p tsconfig.build.json && api-extractor run",
29 | "preview": "vite preview",
30 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
31 | },
32 | "dependencies": {
33 | "fuse.js": "^7.0.0",
34 | "nanoid": "^5.0.6"
35 | },
36 | "devDependencies": {
37 | "@iconify/json": "^2.2.189",
38 | "@microsoft/api-extractor": "^7.42.3",
39 | "@types/node": "^20.11.24",
40 | "@unocss/reset": "^0.58.5",
41 | "@vitejs/plugin-vue": "^5.0.4",
42 | "@vue/tsconfig": "^0.5.1",
43 | "@vueuse/core": "^10.9.0",
44 | "highlight.js": "^11.9.0",
45 | "sass": "^1.71.1",
46 | "typescript": "^5.3.3",
47 | "unocss": "^0.58.5",
48 | "unplugin-icons": "^0.18.5",
49 | "unplugin-vue-components": "^0.26.0",
50 | "vite": "^5.1.5",
51 | "vue": "^3.4.21",
52 | "vue-tsc": "^2.0.5"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { URL, fileURLToPath } from 'node:url'
2 | import { resolve } from 'node:path'
3 | import type { UserConfig } from 'vite'
4 | import { defineConfig } from 'vite'
5 | import vue from '@vitejs/plugin-vue'
6 | import UnoCSS from 'unocss/vite'
7 | import Icons from 'unplugin-icons/vite'
8 | import IconsResolver from 'unplugin-icons/resolver'
9 | import Components from 'unplugin-vue-components/vite'
10 |
11 | // https://vitejs.dev/config/
12 | export default defineConfig(({ command, mode }) => {
13 | const userConfig: UserConfig = {}
14 |
15 | console.log(command)
16 | console.log(mode)
17 |
18 | if (mode === 'lib') {
19 | userConfig.build = {
20 | lib: {
21 | entry: resolve(__dirname, 'packages/index.ts'),
22 | name: 'VueCommandPalette',
23 | fileName: 'vue-command-palette',
24 | },
25 | outDir: 'lib',
26 | emptyOutDir: true,
27 | sourcemap: true,
28 | rollupOptions: {
29 | external: ['vue', 'fuse.js'],
30 | output: [
31 | {
32 | format: 'cjs',
33 | entryFileNames: 'vue-command-palette.cjs'
34 | },
35 | {
36 | format: 'es',
37 | entryFileNames: 'vue-command-palette.js',
38 | preserveModules: false
39 | }
40 | ],
41 | },
42 | }
43 | }
44 |
45 | return {
46 | resolve: {
47 | alias: {
48 | '@': fileURLToPath(new URL('./packages', import.meta.url)),
49 | '~': fileURLToPath(new URL('./src', import.meta.url)),
50 | },
51 | },
52 | plugins: [
53 | vue(),
54 | UnoCSS(),
55 | Components({
56 | resolvers: [
57 | IconsResolver({
58 | prefix: '',
59 | }),
60 | ],
61 | }),
62 | Icons(),
63 | ],
64 | ...userConfig,
65 | }
66 | })
67 |
--------------------------------------------------------------------------------
/packages/CommandItem.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
66 |
67 |
68 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/packages/index.ts:
--------------------------------------------------------------------------------
1 | import { computed, defineComponent, h } from 'vue'
2 | import Root from './CommandRoot.vue'
3 | import Dialog from './CommandDialog.vue'
4 | import Group from './CommandGroup.vue'
5 | import Input from './CommandInput.vue'
6 | import Item from './CommandItem.vue'
7 | import List from './CommandList.vue'
8 | import { useCommandState } from './useCommandState'
9 |
10 | /**
11 | * Command Empty Node
12 | */
13 | const Empty = defineComponent({
14 | name: 'Command.Empty',
15 | setup(props, { attrs, slots }) {
16 | const { filtered } = useCommandState()
17 | const isRender = computed(() => filtered.value.count === 0)
18 | return () =>
19 | isRender.value
20 | ? h(
21 | 'div',
22 | {
23 | 'command-empty': '',
24 | 'role': 'presentation',
25 | ...attrs,
26 | },
27 | slots,
28 | )
29 | : h('div', {
30 | 'command-empty': 'hidden',
31 | 'role': 'presentation',
32 | 'style': {
33 | display: 'none',
34 | },
35 | ...attrs,
36 | })
37 | },
38 | })
39 |
40 | /**
41 | * Command Loading Node
42 | */
43 | const Loading = defineComponent({
44 | name: 'Command.Loading',
45 | setup(props, { attrs, slots }) {
46 | return () =>
47 | h(
48 | 'div',
49 | {
50 | 'command-loading': '',
51 | 'role': 'progressbar',
52 | ...attrs,
53 | },
54 | slots,
55 | )
56 | },
57 | })
58 |
59 | /**
60 | * Command Separator Node
61 | */
62 | const Separator = defineComponent({
63 | name: 'Command.Separator',
64 | setup(props, { attrs, slots }) {
65 | return () =>
66 | h('div', {
67 | 'command-separator': '',
68 | 'role': 'separator',
69 | ...attrs,
70 | })
71 | },
72 | })
73 |
74 | const Command = Object.assign(Root, {
75 | Root,
76 | Dialog,
77 | Empty,
78 | Group,
79 | Input,
80 | Item,
81 | List,
82 | Loading,
83 | Separator,
84 | }) as typeof Root & {
85 | readonly Root: typeof Root
86 | readonly Dialog: typeof Dialog
87 | readonly Empty: typeof Empty
88 | readonly Group: typeof Group
89 | readonly Input: typeof Input
90 | readonly Item: typeof Item
91 | readonly List: typeof List
92 | readonly Loading: typeof Loading
93 | readonly Separator: typeof Separator
94 | }
95 |
96 | export { Command, useCommandState }
97 | export type { CommandGroupProps, CommandInputEmits, CommandInputProps, CommandItemEmits, CommandItemProps, CommandRootEmits, CommandRootProps } from './types'
98 |
--------------------------------------------------------------------------------
/src/components/command/Linear.vue:
--------------------------------------------------------------------------------
1 |
56 |
57 |
58 |
59 |
60 |
61 | Issue - FUN-343
62 |
63 |
64 |
65 |
66 |
67 |
68 | No results found.
69 |
76 |
77 | {{ item.label }}
78 |
79 | {{ key }}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
90 |
--------------------------------------------------------------------------------
/src/components/command/vercel/Vercel.vue:
--------------------------------------------------------------------------------
1 |
55 |
56 |
57 |
62 |
63 |
64 |
67 |
68 |
72 |
73 |
74 |
75 |
76 | No results found.
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
109 |
--------------------------------------------------------------------------------
/src/plugins/highlight.ts:
--------------------------------------------------------------------------------
1 | import type { Plugin } from 'vue'
2 | import { computed, defineComponent, h, ref, watch } from 'vue'
3 | import hljs from 'highlight.js/lib/core'
4 | import javascript from 'highlight.js/lib/languages/javascript'
5 | import xml from 'highlight.js/lib/languages/xml'
6 |
7 | hljs.registerLanguage('javascript', javascript)
8 | hljs.registerLanguage('xml', xml)
9 |
10 | function escapeHtml(value: string): string {
11 | return value
12 | .replace(/&/g, '&')
13 | .replace(//g, '>')
15 | .replace(/"/g, '"')
16 | .replace(/'/g, ''')
17 | }
18 |
19 | const component = defineComponent({
20 | props: {
21 | code: {
22 | type: String,
23 | required: true,
24 | },
25 | language: {
26 | type: String,
27 | default: '',
28 | },
29 | autodetect: {
30 | type: Boolean,
31 | default: true,
32 | },
33 | ignoreIllegals: {
34 | type: Boolean,
35 | default: true,
36 | },
37 | },
38 | setup(props) {
39 | const language = ref(props.language)
40 | watch(
41 | () => props.language,
42 | (newLanguage) => {
43 | language.value = newLanguage
44 | },
45 | )
46 |
47 | const autodetect = computed(() => props.autodetect || !language.value)
48 | const cannotDetectLanguage = computed(
49 | () => !autodetect.value && !hljs.getLanguage(language.value),
50 | )
51 |
52 | const className = computed((): string => {
53 | if (cannotDetectLanguage.value)
54 | return ''
55 | else
56 | return `hljs ${language.value}`
57 | })
58 |
59 | const highlightedCode = computed((): string => {
60 | // No idea what language to use, return raw code
61 | if (cannotDetectLanguage.value) {
62 | console.warn(
63 | `The language "${language.value}" you specified could not be found.`,
64 | )
65 | return escapeHtml(props.code)
66 | }
67 |
68 | if (autodetect.value) {
69 | const result = hljs.highlightAuto(props.code)
70 | language.value = result.language ?? ''
71 | return result.value
72 | }
73 | else {
74 | const result = hljs.highlight(props.code, {
75 | language: language.value,
76 | ignoreIllegals: props.ignoreIllegals,
77 | })
78 | return result.value
79 | }
80 | })
81 |
82 | return {
83 | className,
84 | highlightedCode,
85 | }
86 | },
87 | render() {
88 | return h('pre', {}, [
89 | h('code', {
90 | class: this.className,
91 | innerHTML: this.highlightedCode,
92 | tabindex: '0',
93 | }),
94 | ])
95 | },
96 | })
97 |
98 | const plugin: Plugin & { component: typeof component } = {
99 | install(app) {
100 | app.component('Highlight', component)
101 | },
102 | component,
103 | }
104 |
105 | export default plugin
106 |
--------------------------------------------------------------------------------
/src/components/command/raycast/Home.vue:
--------------------------------------------------------------------------------
1 |
59 |
60 |
61 |
62 | -
68 |
74 | {{ item.label }}
75 |
76 |
77 |
78 | -
84 |
85 | {{ item.label }}
86 |
87 |
88 |
89 |
90 |
120 |
--------------------------------------------------------------------------------
/src/components/command/vercel/Home.vue:
--------------------------------------------------------------------------------
1 |
63 |
64 |
65 |
66 |
67 | -
73 |
74 | {{ item.label }}
75 |
76 |
77 |
78 | -
84 |
85 | {{ item.label }}
86 |
87 |
88 |
89 | -
95 |
96 | {{ item.label }}
97 |
98 |
99 |
100 | -
106 |
107 |
108 | {{ item.label }}
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/components.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* prettier-ignore */
3 | // @ts-nocheck
4 | // Generated by unplugin-vue-components
5 | // Read more: https://github.com/vuejs/core/pull/3399
6 | export {}
7 |
8 | declare module 'vue' {
9 | export interface GlobalComponents {
10 | CmdkPlaceholder: typeof import('./src/components/common/CmdkPlaceholder.vue')['default']
11 | FigmaIcon: typeof import('./src/components/icons/FigmaIcon.vue')['default']
12 | Home: typeof import('./src/components/command/raycast/Home.vue')['default']
13 | Item: typeof import('./src/components/command/raycast/Item.vue')['default']
14 | Linear: typeof import('./src/components/command/Linear.vue')['default']
15 | LinearAssignToIcon: typeof import('./src/components/icons/LinearAssignToIcon.vue')['default']
16 | LinearAssignToMeIcon: typeof import('./src/components/icons/LinearAssignToMeIcon.vue')['default']
17 | LinearChangeLabelsIcon: typeof import('./src/components/icons/LinearChangeLabelsIcon.vue')['default']
18 | LinearChangePriorityIcon: typeof import('./src/components/icons/LinearChangePriorityIcon.vue')['default']
19 | LinearChangeStatusIcon: typeof import('./src/components/icons/LinearChangeStatusIcon.vue')['default']
20 | LinearIcon: typeof import('./src/components/icons/LinearIcon.vue')['default']
21 | LinearRemoveLabelIcon: typeof import('./src/components/icons/LinearRemoveLabelIcon.vue')['default']
22 | LinearSetDueDateIcon: typeof import('./src/components/icons/LinearSetDueDateIcon.vue')['default']
23 | Logo: typeof import('./src/components/icons/Logo.vue')['default']
24 | MoonIcon: typeof import('./src/components/icons/MoonIcon.vue')['default']
25 | Projects: typeof import('./src/components/command/vercel/Projects.vue')['default']
26 | Raycast: typeof import('./src/components/command/raycast/Raycast.vue')['default']
27 | RaycastClipboardIcon: typeof import('./src/components/icons/RaycastClipboardIcon.vue')['default']
28 | RaycastDarkIcon: typeof import('./src/components/icons/RaycastDarkIcon.vue')['default']
29 | RaycasTerminalIcon: typeof import('./src/components/icons/RaycasTerminalIcon.vue')['default']
30 | RaycastFinderIcon: typeof import('./src/components/icons/RaycastFinderIcon.vue')['default']
31 | RaycastHammerIcon: typeof import('./src/components/icons/RaycastHammerIcon.vue')['default']
32 | RaycastIcon: typeof import('./src/components/icons/RaycastIcon.vue')['default']
33 | RaycastLightIcon: typeof import('./src/components/icons/RaycastLightIcon.vue')['default']
34 | RaycastStarIcon: typeof import('./src/components/icons/RaycastStarIcon.vue')['default']
35 | RaycastWindowIcon: typeof import('./src/components/icons/RaycastWindowIcon.vue')['default']
36 | Self: typeof import('./src/components/command/Self.vue')['default']
37 | SlackIcon: typeof import('./src/components/icons/SlackIcon.vue')['default']
38 | SunIcon: typeof import('./src/components/icons/SunIcon.vue')['default']
39 | Vercel: typeof import('./src/components/command/vercel/Vercel.vue')['default']
40 | VercelContactIcon: typeof import('./src/components/icons/VercelContactIcon.vue')['default']
41 | VercelCopyIcon: typeof import('./src/components/icons/VercelCopyIcon.vue')['default']
42 | VercelDocsIcon: typeof import('./src/components/icons/VercelDocsIcon.vue')['default']
43 | VercelFeedbackIcon: typeof import('./src/components/icons/VercelFeedbackIcon.vue')['default']
44 | VercelIcon: typeof import('./src/components/icons/VercelIcon.vue')['default']
45 | VercelPlusIcon: typeof import('./src/components/icons/VercelPlusIcon.vue')['default']
46 | VercelProjectsIcon: typeof import('./src/components/icons/VercelProjectsIcon.vue')['default']
47 | VercelTeamsIcon: typeof import('./src/components/icons/VercelTeamsIcon.vue')['default']
48 | YouTubeIcon: typeof import('./src/components/icons/YouTubeIcon.vue')['default']
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/assets/scss/vercel.scss:
--------------------------------------------------------------------------------
1 | .vercel {
2 | [command-root] {
3 | max-width: 640px;
4 | width: 100%;
5 | padding: 8px;
6 | background: #ffffff;
7 | border-radius: 12px;
8 | overflow: hidden;
9 | font-family: var(--font-sans);
10 | border: 1px solid var(--gray6);
11 | box-shadow: var(--command-shadow);
12 | transition: transform 100ms ease;
13 |
14 | .dark & {
15 | background: rgba(22, 22, 22, 0.7);
16 | }
17 | }
18 |
19 | [command-input] {
20 | font-family: var(--font-sans);
21 | border: none;
22 | width: 100%;
23 | font-size: 17px;
24 | padding: 8px 8px 16px 8px;
25 | outline: none;
26 | background: var(--bg);
27 | color: var(--gray12);
28 | border-bottom: 1px solid var(--gray6);
29 | margin-bottom: 16px;
30 | border-radius: 0;
31 |
32 | &::placeholder {
33 | color: var(--gray9);
34 | }
35 | }
36 |
37 | [command-vercel-badge] {
38 | height: 20px;
39 | background: var(--grayA3);
40 | display: inline-flex;
41 | align-items: center;
42 | padding: 0 8px;
43 | font-size: 12px;
44 | color: var(--grayA11);
45 | border-radius: 4px;
46 | margin: 4px 0 4px 4px;
47 | user-select: none;
48 | text-transform: capitalize;
49 | font-weight: 500;
50 | }
51 |
52 | [command-item] {
53 | content-visibility: auto;
54 |
55 | cursor: pointer;
56 | height: 48px;
57 | border-radius: 8px;
58 | font-size: 14px;
59 | display: flex;
60 | align-items: center;
61 | gap: 8px;
62 | padding: 0 16px;
63 | color: var(--gray11);
64 | user-select: none;
65 | will-change: background, color;
66 | transition: all 150ms ease;
67 | transition-property: none;
68 |
69 | &[aria-selected='true'] {
70 | background: var(--grayA3);
71 | color: var(--gray12);
72 | }
73 |
74 | &[aria-disabled='true'] {
75 | color: var(--gray8);
76 | cursor: not-allowed;
77 | }
78 |
79 | &:active {
80 | transition-property: background;
81 | background: var(--gray4);
82 | }
83 |
84 | & + [command-item] {
85 | margin-top: 4px;
86 | }
87 |
88 | svg {
89 | width: 18px;
90 | height: 18px;
91 | }
92 | }
93 |
94 | [command-list] {
95 | height: min(330px, calc(var(--command-list-height)));
96 | max-height: 400px;
97 | overflow: auto;
98 | overscroll-behavior: contain;
99 | transition: 100ms ease;
100 | transition-property: height;
101 | }
102 |
103 | [command-vercel-shortcuts] {
104 | display: flex;
105 | margin-left: auto;
106 | gap: 8px;
107 |
108 | kbd {
109 | font-family: var(--font-sans);
110 | font-size: 12px;
111 | min-width: 20px;
112 | padding: 4px;
113 | height: 20px;
114 | border-radius: 4px;
115 | color: var(--gray11);
116 | background: var(--gray4);
117 | display: inline-flex;
118 | align-items: center;
119 | justify-content: center;
120 | text-transform: uppercase;
121 | }
122 | }
123 |
124 | [command-separator] {
125 | height: 1px;
126 | width: 100%;
127 | background: var(--gray5);
128 | margin: 4px 0;
129 | }
130 |
131 | *:not([hidden]) + [command-group] {
132 | margin-top: 8px;
133 | }
134 |
135 | [command-group-heading] {
136 | user-select: none;
137 | font-size: 12px;
138 | color: var(--gray11);
139 | padding: 0 8px;
140 | display: flex;
141 | align-items: center;
142 | margin-bottom: 8px;
143 | }
144 |
145 | [command-empty] {
146 | font-size: 14px;
147 | display: flex;
148 | align-items: center;
149 | justify-content: center;
150 | height: 48px;
151 | white-space: pre-wrap;
152 | color: var(--gray11);
153 | }
154 |
155 | [command-dialog-wrapper] {
156 | padding: 8px;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/assets/scss/linear.scss:
--------------------------------------------------------------------------------
1 | .linear {
2 | margin: 0 auto;
3 | display: flex;
4 | justify-content: center;
5 | [command-root] {
6 | kbd {
7 | font-family: var(--font-sans);
8 | background: var(--gray3);
9 | color: var(--gray11);
10 | height: 20px;
11 | width: 24px;
12 | border-radius: 4px;
13 | padding: 0 4px;
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 | }
18 | }
19 |
20 | [command-linear-badge] {
21 | height: 24px;
22 | padding: 8px;
23 | font-size: 12px;
24 | color: var(--gray11);
25 | background: var(--gray3);
26 | border-radius: 4px;
27 | width: fit-content;
28 | display: flex;
29 | align-items: center;
30 | margin: 16px 16px 0;
31 | }
32 |
33 | [command-linear-shortcuts] {
34 | display: flex;
35 | margin-left: auto;
36 | gap: 8px;
37 |
38 | kbd {
39 | font-family: var(--font-sans);
40 | font-size: 13px;
41 | color: var(--gray11);
42 | }
43 | }
44 |
45 | [command-input] {
46 | font-family: var(--font-sans);
47 | border: none;
48 | width: 100%;
49 | font-size: 18px;
50 | padding: 20px;
51 | outline: none;
52 | background: var(--bg);
53 | color: var(--gray12);
54 | border-bottom: 1px solid var(--gray6);
55 | border-radius: 0;
56 | caret-color: #6e5ed2;
57 | margin: 0;
58 |
59 | &::placeholder {
60 | color: var(--gray9);
61 | }
62 | }
63 |
64 | [command-item] {
65 | content-visibility: auto;
66 |
67 | cursor: pointer;
68 | height: 48px;
69 | font-size: 14px;
70 | display: flex;
71 | align-items: center;
72 | gap: 12px;
73 | padding: 0 16px;
74 | color: var(--gray12);
75 | user-select: none;
76 | will-change: background, color;
77 | transition: all 150ms ease;
78 | transition-property: none;
79 | position: relative;
80 |
81 | &[aria-selected='true'],
82 | &:hover {
83 | background: var(--gray3);
84 |
85 | svg {
86 | color: var(--gray12);
87 | }
88 |
89 | &:after {
90 | content: '';
91 | position: absolute;
92 | left: 0;
93 | z-index: 123;
94 | width: 3px;
95 | height: 100%;
96 | background: #5f6ad2;
97 | }
98 | }
99 |
100 | &[aria-disabled='true'] {
101 | color: var(--gray8);
102 | cursor: not-allowed;
103 | }
104 |
105 | &:active {
106 | transition-property: background;
107 | background: var(--gray4);
108 | }
109 |
110 | svg {
111 | width: 16px;
112 | height: 16px;
113 | color: var(--gray10);
114 | }
115 | }
116 |
117 | [command-list] {
118 | height: min(300px, var(--command-list-height));
119 | max-height: 360px;
120 | overflow: auto;
121 | overscroll-behavior: contain;
122 | transition: 100ms ease;
123 | transition-property: height;
124 | }
125 |
126 | * + [command-group] {
127 | margin-top: 8px;
128 | }
129 |
130 | [command-group-heading] {
131 | user-select: none;
132 | font-size: 12px;
133 | color: var(--gray11);
134 | padding: 0 8px;
135 | display: flex;
136 | align-items: center;
137 | }
138 |
139 | [command-empty=''] {
140 | font-size: 14px;
141 | display: flex;
142 | align-items: center;
143 | justify-content: center;
144 | height: 64px;
145 | white-space: pre-wrap;
146 | color: var(--gray11);
147 | }
148 |
149 | [command-separator] {
150 | height: 1px;
151 | width: 100%;
152 | background: var(--gray5);
153 | margin: 4px 0;
154 | }
155 |
156 | [command-dialog-wrapper] {
157 | max-width: 640px;
158 | width: 100%;
159 | background: #ffffff;
160 | border-radius: 12px;
161 | overflow: hidden;
162 | padding: 0;
163 | font-family: var(--font-sans);
164 | box-shadow: var(--command-shadow);
165 |
166 | .dark & {
167 | background: linear-gradient(
168 | 136.61deg,
169 | rgb(39, 40, 43) 13.72%,
170 | rgb(45, 46, 49) 74.3%
171 | );
172 | }
173 | }
174 |
175 | [command-dialog-footer] ul,
176 | [command-dialog-footer] ul li {
177 | display: flex;
178 | align-items: center;
179 | }
180 |
181 | [command-dialog-footer] ul li {
182 | gap: 4px;
183 | margin-left: 4px;
184 | margin-right: 4px;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/assets/scss/global.scss:
--------------------------------------------------------------------------------
1 | @import 'highlight.scss';
2 |
3 | html,
4 | body,
5 | #app {
6 | height: 100%;
7 | }
8 | body {
9 | margin: 0;
10 | padding: 0;
11 | }
12 |
13 | ul {
14 | margin: 0;
15 | padding: 0;
16 | }
17 |
18 | :root {
19 | --font-sans: 'Inter', --apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
20 | Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
21 | --app-bg: var(--gray1);
22 | --app-text: #000000;
23 | --command-shadow: 0 16px 70px rgb(0 0 0 / 20%);
24 |
25 | --lowContrast: #ffffff;
26 | --highContrast: #000000;
27 | --vcp-c-brand: #44bd87;
28 | --vcp-c-accent: #35495e;
29 |
30 | --gray1: hsl(0, 0%, 98%);
31 | --gray2: hsl(0, 0%, 97.3%);
32 | --gray3: hsl(0, 0%, 95.1%);
33 | --gray4: hsl(0, 0%, 93%);
34 | --gray5: hsl(0, 0%, 90.9%);
35 | --gray6: hsl(0, 0%, 88.7%);
36 | --gray7: hsl(0, 0%, 85.8%);
37 | --gray8: hsl(0, 0%, 78%);
38 | --gray9: hsl(0, 0%, 56.1%);
39 | --gray10: hsl(0, 0%, 52.3%);
40 | --gray11: hsl(0, 0%, 43.5%);
41 | --gray12: hsl(0, 0%, 9%);
42 |
43 | --grayA1: hsla(0, 0%, 0%, 0.012);
44 | --grayA2: hsla(0, 0%, 0%, 0.027);
45 | --grayA3: hsla(0, 0%, 0%, 0.047);
46 | --grayA4: hsla(0, 0%, 0%, 0.071);
47 | --grayA5: hsla(0, 0%, 0%, 0.09);
48 | --grayA6: hsla(0, 0%, 0%, 0.114);
49 | --grayA7: hsla(0, 0%, 0%, 0.141);
50 | --grayA8: hsla(0, 0%, 0%, 0.22);
51 | --grayA9: hsla(0, 0%, 0%, 0.439);
52 | --grayA10: hsla(0, 0%, 0%, 0.478);
53 | --grayA11: hsla(0, 0%, 0%, 0.565);
54 | --grayA12: hsla(0, 0%, 0%, 0.91);
55 |
56 | --blue1: hsl(206, 100%, 99.2%);
57 | --blue2: hsl(210, 100%, 98%);
58 | --blue3: hsl(209, 100%, 96.5%);
59 | --blue4: hsl(210, 98.8%, 94%);
60 | --blue5: hsl(209, 95%, 90.1%);
61 | --blue6: hsl(209, 81.2%, 84.5%);
62 | --blue7: hsl(208, 77.5%, 76.9%);
63 | --blue8: hsl(206, 81.9%, 65.3%);
64 | --blue9: hsl(206, 100%, 50%);
65 | --blue10: hsl(208, 100%, 47.3%);
66 | --blue11: hsl(211, 100%, 43.2%);
67 | --blue12: hsl(211, 100%, 15%);
68 | }
69 |
70 | .dark {
71 | --app-bg: var(--gray1);
72 | --app-text: #ffffff;
73 |
74 | --lowContrast: #000000;
75 | --highContrast: #ffffff;
76 |
77 | --gray1: hsl(0, 0%, 8.5%);
78 | --gray2: hsl(0, 0%, 11%);
79 | --gray3: hsl(0, 0%, 13.6%);
80 | --gray4: hsl(0, 0%, 15.8%);
81 | --gray5: hsl(0, 0%, 17.9%);
82 | --gray6: hsl(0, 0%, 20.5%);
83 | --gray7: hsl(0, 0%, 24.3%);
84 | --gray8: hsl(0, 0%, 31.2%);
85 | --gray9: hsl(0, 0%, 43.9%);
86 | --gray10: hsl(0, 0%, 49.4%);
87 | --gray11: hsl(0, 0%, 62.8%);
88 | --gray12: hsl(0, 0%, 93%);
89 |
90 | --grayA1: hsla(0, 0%, 100%, 0);
91 | --grayA2: hsla(0, 0%, 100%, 0.026);
92 | --grayA3: hsla(0, 0%, 100%, 0.056);
93 | --grayA4: hsla(0, 0%, 100%, 0.077);
94 | --grayA5: hsla(0, 0%, 100%, 0.103);
95 | --grayA6: hsla(0, 0%, 100%, 0.129);
96 | --grayA7: hsla(0, 0%, 100%, 0.172);
97 | --grayA8: hsla(0, 0%, 100%, 0.249);
98 | --grayA9: hsla(0, 0%, 100%, 0.386);
99 | --grayA10: hsla(0, 0%, 100%, 0.446);
100 | --grayA11: hsla(0, 0%, 100%, 0.592);
101 | --grayA12: hsla(0, 0%, 100%, 0.923);
102 |
103 | --blue1: hsl(212, 35%, 9.2%);
104 | --blue2: hsl(216, 50%, 11.8%);
105 | --blue3: hsl(214, 59.4%, 15.3%);
106 | --blue4: hsl(214, 65.8%, 17.9%);
107 | --blue5: hsl(213, 71.2%, 20.2%);
108 | --blue6: hsl(212, 77.4%, 23.1%);
109 | --blue7: hsl(211, 85.1%, 27.4%);
110 | --blue8: hsl(211, 89.7%, 34.1%);
111 | --blue9: hsl(206, 100%, 50%);
112 | --blue10: hsl(209, 100%, 60.6%);
113 | --blue11: hsl(210, 100%, 66.1%);
114 | --blue12: hsl(206, 98%, 95.8%);
115 | }
116 |
117 | body {
118 | background: var(--app-bg);
119 | }
120 |
121 | div [command-dialog-mask] {
122 | background-color: rgba(0, 0, 0, 0.3);
123 | height: 100vh;
124 | left: 0;
125 | position: fixed;
126 | top: 0;
127 | width: 100vw;
128 | z-index: 200;
129 | animation: 333ms cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal none running shown;
130 | }
131 |
132 | div [command-dialog-wrapper] {
133 | position: relative;
134 | background: var(--gray2);
135 | border-radius: 6px;
136 | box-shadow: none;
137 | flex-direction: column;
138 | margin: 20vh auto auto;
139 | max-width: 560px;
140 | animation: 333ms cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal none running shown;
141 | }
142 |
143 | div [command-dialog-footer] {
144 | border-top: 1px solid var(--gray6);
145 | align-items: center;
146 | background: var(--gray4);
147 | color: var(--gray11);
148 | border-radius: 0 0 8px 8px;
149 | box-shadow: none;
150 | display: flex;
151 | flex-direction: row-reverse;
152 | flex-shrink: 0;
153 | height: 44px;
154 | justify-content: space-between;
155 | padding: 0 12px;
156 | position: relative;
157 | user-select: none;
158 | width: 100%;
159 | z-index: 300;
160 | font-size: 12px;
161 | }
162 |
--------------------------------------------------------------------------------
/src/assets/scss/algolia.scss:
--------------------------------------------------------------------------------
1 | .algolia {
2 | [command-root] {
3 | padding: 8px;
4 | }
5 |
6 | [command-input] {
7 | font-family: var(--font-sans);
8 | width: 100%;
9 | font-size: 18px;
10 | padding: 12px;
11 | outline: none;
12 | background: var(--bg);
13 | color: var(--gray12);
14 | border-bottom: 1px solid var(--gray6);
15 | border-radius: 4px;
16 | caret-color: var(--vcp-c-brand);
17 | margin: 0;
18 | border: 1px solid var(--vcp-c-brand);
19 |
20 | &::placeholder {
21 | color: var(--gray9);
22 | }
23 | }
24 |
25 | [command-list] {
26 | height: var(--command-list-height);
27 | max-height: 360px;
28 | overflow: auto;
29 | overscroll-behavior: contain;
30 | transition: 100ms ease;
31 | transition-property: height;
32 | }
33 |
34 | [command-item] {
35 | position: relative;
36 | content-visibility: auto;
37 | cursor: pointer;
38 | height: 56px;
39 | font-size: 14px;
40 | display: flex;
41 | align-items: center;
42 | gap: 12px;
43 | padding: 0px 16px;
44 | color: var(--gray12);
45 | user-select: none;
46 | will-change: background, color;
47 | transition: all 150ms ease;
48 | transition-property: none;
49 | border-radius: 4px;
50 | margin-top: 4px;
51 |
52 | &:first-child {
53 | margin-top: 0px;
54 | }
55 |
56 | &[aria-selected='true'],
57 | &:hover {
58 | background: var(--vcp-c-brand);
59 | color: #fff;
60 |
61 | svg {
62 | color: #fff;
63 | }
64 |
65 | [command-linear-shortcuts] {
66 | display: flex;
67 | margin-left: auto;
68 | gap: 8px;
69 |
70 | kbd {
71 | font-family: var(--font-sans);
72 | font-size: 13px;
73 | color: var(--gray11);
74 | }
75 | }
76 | }
77 |
78 | &[aria-disabled='true'] {
79 | color: var(--gray8);
80 | cursor: not-allowed;
81 | }
82 |
83 | &:active {
84 | transition-property: background;
85 | background: var(--gray4);
86 | }
87 |
88 | svg {
89 | width: 16px;
90 | height: 16px;
91 | color: var(--gray10);
92 | }
93 | }
94 |
95 | [command-empty=''] {
96 | font-size: 14px;
97 | display: flex;
98 | align-items: center;
99 | justify-content: center;
100 | height: 64px;
101 | white-space: pre-wrap;
102 | color: var(--gray11);
103 | }
104 |
105 | [command-dialog-mask] {
106 | background-color: rgba(75, 75, 75, 0.8);
107 | }
108 |
109 | [command-dialog-header] {
110 | padding: 12px;
111 | }
112 |
113 | [command-dialog-body] {
114 | padding: 0 12px 12px;
115 | }
116 |
117 | [command-dialog-footer] {
118 | align-items: center;
119 | border-radius: 0 0 8px 8px;
120 | box-shadow: 0 -1px 0 0 #e0e3e8, 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
121 | display: flex;
122 | flex-direction: row-reverse;
123 | flex-shrink: 0;
124 | height: 44px;
125 | justify-content: space-between;
126 | padding: 0 12px;
127 | position: relative;
128 | user-select: none;
129 | width: 100%;
130 | z-index: 300;
131 | }
132 |
133 | [command-group-heading] {
134 | color: var(--vcp-c-brand);
135 | font-size: 0.85em;
136 | font-weight: 600;
137 | line-height: 32px;
138 | margin: 0 -4px;
139 | padding: 8px 4px 0;
140 | top: 0;
141 | z-index: 10;
142 | width: 100%;
143 | }
144 |
145 | .command-palette-commands {
146 | color: var(--docsearch-muted-color);
147 | display: flex;
148 | list-style: none;
149 | margin: 0;
150 | padding: 0;
151 |
152 | li {
153 | display: flex;
154 | align-items: center;
155 | }
156 | li:not(:last-of-type) {
157 | margin-right: 0.8em;
158 | }
159 | }
160 |
161 | .command-palette-logo {
162 | a {
163 | display: flex;
164 | align-items: center;
165 | gap: 8px;
166 | }
167 | svg {
168 | height: 24px;
169 | width: 24px;
170 | }
171 | }
172 |
173 | .command-palette-commands-key {
174 | align-items: center;
175 | background: var(--gray3);
176 | border-radius: 2px;
177 | display: flex;
178 | height: 18px;
179 | justify-content: center;
180 | margin-right: 0.4em;
181 | padding: 0 0 1px;
182 | color: var(--gray11);
183 | border: 0;
184 | width: 20px;
185 | }
186 | }
187 |
188 | .dark .algolia [command-dialog-footer] {
189 | box-shadow: none;
190 | }
191 |
192 | // transition for command-dialog
193 | .command-dialog-enter-active,
194 | .command-dialog-leave-active {
195 | transition: opacity 0.2s ease-in-out;
196 | }
197 |
198 | .command-dialog-enter-from,
199 | .command-dialog-leave-to {
200 | opacity: 0;
201 | }
202 |
203 | .command-dialog-enter-active {
204 | transition-duration: 0.5s;
205 | }
206 |
207 | // transition for command-dialog-wrapper
208 | .command-dialog-enter-active [command-dialog-wrapper] {
209 | transition-delay: 0.2s;
210 | }
211 |
212 | .command-dialog-enter-from [command-dialog-wrapper],
213 | .command-dialog-leave-to [command-dialog-wrapper] {
214 | opacity: 0;
215 | transform: translateY(10px) scale(0.95);
216 | }
217 |
218 | .command-dialog-enter-active [command-dialog-wrapper] {
219 | transition: transform ease-out 0.3s;
220 | }
221 |
222 | .command-dialog-enter-to [command-dialog-wrapper],
223 | .command-dialog-leave-from [command-dialog-wrapper] {
224 | opacity: 1;
225 | transform: translateY(0) scale(1);
226 | }
227 |
228 | .command-dialog-leave-active [command-dialog-wrapper] {
229 | transition: transfrom ease-in 0.2s;
230 | }
231 |
--------------------------------------------------------------------------------
/src/assets/scss/framer.scss:
--------------------------------------------------------------------------------
1 | .framer {
2 | [command-root] {
3 | max-width: 640px;
4 | width: 100%;
5 | padding: 8px;
6 | background: #ffffff;
7 | border-radius: 16px;
8 | overflow: hidden;
9 | font-family: var(--font-sans);
10 | border: 1px solid var(--gray6);
11 | box-shadow: var(--command-shadow);
12 |
13 | .dark & {
14 | background: var(--gray2);
15 | }
16 | }
17 |
18 | [command-framer-header] {
19 | display: flex;
20 | align-items: center;
21 | gap: 8px;
22 | height: 48px;
23 | padding: 0 8px;
24 | border-bottom: 1px solid var(--gray5);
25 | margin-bottom: 12px;
26 | padding-bottom: 8px;
27 |
28 | svg {
29 | width: 20px;
30 | height: 20px;
31 | color: var(--gray9);
32 | transform: translateY(1px);
33 | }
34 | }
35 |
36 | [command-input] {
37 | font-family: var(--font-sans);
38 | border: none;
39 | width: 100%;
40 | font-size: 16px;
41 | outline: none;
42 | background: var(--bg);
43 | color: var(--gray12);
44 |
45 | &::placeholder {
46 | color: var(--gray9);
47 | }
48 | }
49 |
50 | [command-item] {
51 | content-visibility: auto;
52 |
53 | cursor: pointer;
54 | border-radius: 12px;
55 | font-size: 14px;
56 | display: flex;
57 | align-items: center;
58 | gap: 12px;
59 | color: var(--gray12);
60 | padding: 8px 8px;
61 | margin-right: 8px;
62 | font-weight: 500;
63 | transition: all 150ms ease;
64 | transition-property: none;
65 |
66 | &[aria-selected='true'] {
67 | background: var(--blue9);
68 | color: #ffffff;
69 |
70 | [command-framer-item-subtitle] {
71 | color: #ffffff;
72 | }
73 | }
74 |
75 | &[aria-disabled='true'] {
76 | color: var(--gray8);
77 | cursor: not-allowed;
78 | }
79 |
80 | & + [command-item] {
81 | margin-top: 4px;
82 | }
83 |
84 | svg {
85 | width: 16px;
86 | height: 16px;
87 | color: #ffffff;
88 | }
89 | }
90 |
91 | [command-framer-icon-wrapper] {
92 | display: flex;
93 | align-items: center;
94 | justify-content: center;
95 | min-width: 32px;
96 | height: 32px;
97 | background: orange;
98 | border-radius: 8px;
99 | }
100 |
101 | [command-framer-item-meta] {
102 | display: flex;
103 | flex-direction: column;
104 | gap: 4px;
105 | }
106 |
107 | [command-framer-item-subtitle] {
108 | font-size: 12px;
109 | font-weight: 400;
110 | color: var(--gray11);
111 | }
112 |
113 | [command-framer-items] {
114 | min-height: 308px;
115 | display: flex;
116 | }
117 |
118 | [command-framer-left] {
119 | width: 40%;
120 | }
121 |
122 | [command-framer-separator] {
123 | width: 1px;
124 | border: 0;
125 | margin-right: 8px;
126 | background: var(--gray6);
127 | }
128 |
129 | [command-framer-right] {
130 | display: flex;
131 | align-items: center;
132 | justify-content: center;
133 | border-radius: 8px;
134 | margin-left: 8px;
135 | width: 60%;
136 |
137 | button {
138 | width: 120px;
139 | height: 40px;
140 | background: var(--blue9);
141 | border-radius: 6px;
142 | font-weight: 500;
143 | color: white;
144 | font-size: 14px;
145 | }
146 |
147 | input[type='text'] {
148 | height: 40px;
149 | width: 160px;
150 | border: 1px solid var(--gray6);
151 | background: #ffffff;
152 | border-radius: 6px;
153 | padding: 0 8px;
154 | font-size: 14px;
155 | font-family: var(--font-sans);
156 | box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.08);
157 |
158 | &::placeholder {
159 | color: var(--gray9);
160 | }
161 |
162 | @media (prefers-color-scheme: dark) {
163 | background: var(--gray3);
164 | }
165 | }
166 |
167 | [command-framer-radio] {
168 | display: flex;
169 | align-items: center;
170 | gap: 4px;
171 | color: var(--gray12);
172 | font-weight: 500;
173 | font-size: 14px;
174 | accent-color: var(--blue9);
175 |
176 | input {
177 | width: 20px;
178 | height: 20px;
179 | }
180 | }
181 |
182 | img {
183 | width: 40px;
184 | height: 40px;
185 | border-radius: 9999px;
186 | border: 1px solid var(--gray6);
187 | }
188 |
189 | [command-framer-container] {
190 | width: 100px;
191 | height: 100px;
192 | background: var(--blue9);
193 | border-radius: 16px;
194 | }
195 |
196 | [command-framer-badge] {
197 | background: var(--blue3);
198 | padding: 0 8px;
199 | height: 28px;
200 | font-size: 14px;
201 | line-height: 28px;
202 | color: var(--blue11);
203 | border-radius: 9999px;
204 | font-weight: 500;
205 | }
206 |
207 | [command-framer-slider] {
208 | height: 20px;
209 | width: 200px;
210 | background: linear-gradient(90deg, var(--blue9) 40%, var(--gray3) 0%);
211 | border-radius: 9999px;
212 |
213 | div {
214 | width: 20px;
215 | height: 20px;
216 | background: #ffffff;
217 | border-radius: 9999px;
218 | box-shadow: 0 1px 3px -1px rgba(0, 0, 0, 0.32);
219 | transform: translateX(70px);
220 | }
221 | }
222 | }
223 |
224 | [command-list] {
225 | overflow: auto;
226 | }
227 |
228 | [command-separator] {
229 | height: 1px;
230 | width: 100%;
231 | background: var(--gray5);
232 | margin: 4px 0;
233 | }
234 |
235 | [command-group-heading] {
236 | user-select: none;
237 | font-size: 12px;
238 | color: var(--gray11);
239 | padding: 0 8px;
240 | display: flex;
241 | align-items: center;
242 | margin-bottom: 8px;
243 | }
244 |
245 | [command-empty] {
246 | font-size: 14px;
247 | padding: 32px;
248 | white-space: pre-wrap;
249 | color: var(--gray11);
250 | }
251 | }
252 |
253 | @media (max-width: 640px) {
254 | .framer {
255 | [command-framer-icon-wrapper] {
256 | }
257 |
258 | [command-framer-item-subtitle] {
259 | display: none;
260 | }
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.2.3](https://github.com/xiaoluoboding/vue-command-palette/compare/v0.2.2...v0.2.3) (2023-09-20)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * fixed the debounce search bug [#18](https://github.com/xiaoluoboding/vue-command-palette/issues/18) ([5d72166](https://github.com/xiaoluoboding/vue-command-palette/commit/5d72166779778a88df790b6dc7175319918e2a2f))
7 |
8 |
9 |
10 | ## [0.2.2](https://github.com/xiaoluoboding/vue-command-palette/compare/v0.2.1...v0.2.2) (2023-09-12)
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * fixed blank when filter text ([08da9ef](https://github.com/xiaoluoboding/vue-command-palette/commit/08da9ef7d1c28fcd411dfabde40c56485a55ac9b))
16 |
17 |
18 |
19 | ## [0.2.1](https://github.com/xiaoluoboding/vue-command-palette/compare/v0.2.0...v0.2.1) (2023-09-12)
20 |
21 |
22 | ### Bug Fixes
23 |
24 | * filter the item when rerender ([f26308f](https://github.com/xiaoluoboding/vue-command-palette/commit/f26308f964b5ec50f767bac98e0f833a5eae6d44))
25 | * fixed the bug [#14](https://github.com/xiaoluoboding/vue-command-palette/issues/14) ([f8e4ac8](https://github.com/xiaoluoboding/vue-command-palette/commit/f8e4ac86b3d1074bbe0ea78de259b26f2372b1ea))
26 | * fixed the bug of [#14](https://github.com/xiaoluoboding/vue-command-palette/issues/14) ([9df6def](https://github.com/xiaoluoboding/vue-command-palette/commit/9df6def24bd0414232ae7f1bd0aef0d46277aac0))
27 |
28 |
29 | ### Features
30 |
31 | * allow user reset or update the search value ([5f140ed](https://github.com/xiaoluoboding/vue-command-palette/commit/5f140edfabc776571d2b67270ab3cdc84eafea59))
32 |
33 |
34 |
35 | # [0.2.0](https://github.com/xiaoluoboding/vue-command-palette/compare/v0.1.4...v0.2.0) (2023-08-22)
36 |
37 |
38 | ### Bug Fixes
39 |
40 | * fixed the type error ([70bf1f5](https://github.com/xiaoluoboding/vue-command-palette/commit/70bf1f53469aa9e0ecf0bf32383afb1ffab3988e))
41 |
42 |
43 | ### Features
44 |
45 | * add types for command component ([e619b5e](https://github.com/xiaoluoboding/vue-command-palette/commit/e619b5ed560a924716381ce06a2e8d6fa2ca54c9))
46 | * close on clicking outside ([ca71816](https://github.com/xiaoluoboding/vue-command-palette/commit/ca71816ea4b5f6750a7388b068782629c02ede7d))
47 |
48 |
49 |
50 | ## [0.1.4](https://github.com/xiaoluoboding/vue-command-palette/compare/v0.1.3...v0.1.4) (2022-12-14)
51 |
52 |
53 | ### Bug Fixes
54 |
55 | * bottom scroll bar ([5c38a00](https://github.com/xiaoluoboding/vue-command-palette/commit/5c38a00fef77c386b158a1e601229995b3e3783b))
56 |
57 |
58 |
59 | ## [0.1.3](https://github.com/xiaoluoboding/vue-command-palette/compare/v0.1.2...v0.1.3) (2022-09-15)
60 |
61 |
62 |
63 | ## [0.1.2](https://github.com/xiaoluoboding/vue-command-palette/compare/fec87bf5042b5bfd389421524ff3ec3226c39879...v0.1.2) (2022-09-15)
64 |
65 |
66 | ### Bug Fixes
67 |
68 | * fixed the input event ([810ba79](https://github.com/xiaoluoboding/vue-command-palette/commit/810ba79da3fdbceb03deba11fd6b7de28dc32ef0))
69 | * fixed the list component types ([f60e20a](https://github.com/xiaoluoboding/vue-command-palette/commit/f60e20a117e6197c9b922e5aa24a1090937ef34f))
70 |
71 |
72 | ### Features
73 |
74 | * add fuzzy search items in group ([684f1e8](https://github.com/xiaoluoboding/vue-command-palette/commit/684f1e8aa0aa02663e99bc2d7db059daf0960507))
75 | * add rerender list event ([db85fb9](https://github.com/xiaoluoboding/vue-command-palette/commit/db85fb9cb16d65f36785bd7733c80333e51e403f))
76 | * add the cmdk it's self ([b8dc650](https://github.com/xiaoluoboding/vue-command-palette/commit/b8dc6500b19f5746669f49b8e6b725529e1f8f03))
77 | * add the code demo ([0cfb97b](https://github.com/xiaoluoboding/vue-command-palette/commit/0cfb97b3e5cb40888b928d389143aa820aff51de))
78 | * add the demo of linear ([8e98e98](https://github.com/xiaoluoboding/vue-command-palette/commit/8e98e98545dc5c749d22e900aff82036985d1930))
79 | * add the fuzzy search feature ([48969d9](https://github.com/xiaoluoboding/vue-command-palette/commit/48969d918ff6885556c1d7dac2db555685c6610c))
80 | * add the input v-model:value attribute ([1536795](https://github.com/xiaoluoboding/vue-command-palette/commit/153679526f68cdb1d52a06620893430241b86694))
81 | * add the keyboard event feature ([585ca53](https://github.com/xiaoluoboding/vue-command-palette/commit/585ca533c1ece2dcf985ff8cc1b8d76990e83424))
82 | * add the list height animation ([278ba13](https://github.com/xiaoluoboding/vue-command-palette/commit/278ba131c07ecabfdf9fc31bb433253c943d5b60))
83 | * add the perform action ([9837cc6](https://github.com/xiaoluoboding/vue-command-palette/commit/9837cc6177f93cce0965ff68ead9304a423cd6b3))
84 | * add the raycast cmdk demo ([754f7fe](https://github.com/xiaoluoboding/vue-command-palette/commit/754f7fe5d339fd5c971699615783333719b4ae01))
85 | * add the select item event on cmdk component ([1dc7b2a](https://github.com/xiaoluoboding/vue-command-palette/commit/1dc7b2a6ab07b49f1fa5a645dc825240175063fc))
86 | * add the toggle dark mode action ([8633f95](https://github.com/xiaoluoboding/vue-command-palette/commit/8633f95bc59ae0c7acff40fc7530c6ceb7113455))
87 | * add the vercel cmdk demo ([1d59805](https://github.com/xiaoluoboding/vue-command-palette/commit/1d5980503f7921c09f54ea751a8dc35a817acfe9))
88 | * add transition for self dialog ([fb2ed08](https://github.com/xiaoluoboding/vue-command-palette/commit/fb2ed08513ca56a69ba6c51a4ba43e049e899d1d))
89 | * change the demo page ([1d2b16b](https://github.com/xiaoluoboding/vue-command-palette/commit/1d2b16b6bb370d6c1ee2987d55509d7822767236))
90 | * create the lib ([fec87bf](https://github.com/xiaoluoboding/vue-command-palette/commit/fec87bf5042b5bfd389421524ff3ec3226c39879))
91 | * deal with the event emitter ([5e36efd](https://github.com/xiaoluoboding/vue-command-palette/commit/5e36efdb833f68cfc4e0bcf688c9021d1c8a6982))
92 | * fixed the dark mode toggle ([923f178](https://github.com/xiaoluoboding/vue-command-palette/commit/923f178faed6b78d8b22999c99ace78f0440ae55))
93 | * fixed the empty node ([624dc08](https://github.com/xiaoluoboding/vue-command-palette/commit/624dc081a72d693e5db17a9fddbf6d3d8607233a))
94 | * handle dispatch the event on item ([71081a4](https://github.com/xiaoluoboding/vue-command-palette/commit/71081a4f74af7cdd5406d604c9a83b995aa8ff33))
95 | * hidden the empty node ([7cae74f](https://github.com/xiaoluoboding/vue-command-palette/commit/7cae74f480019d532f7df530ad967fe9c4222777))
96 | * navigate to the group first item ([ae5a3d0](https://github.com/xiaoluoboding/vue-command-palette/commit/ae5a3d02ff9007206a78abfb3e575046f9dc459c))
97 | * no valid idx, then go to the first/last of item ([a9c6db4](https://github.com/xiaoluoboding/vue-command-palette/commit/a9c6db4d279212acad118d47e138f52226605f74))
98 | * rename cmdk to command ([9850592](https://github.com/xiaoluoboding/vue-command-palette/commit/985059217e81092094e32a97d8d8b2a0d96c5f73))
99 | * use the provide/inject api ([07868b7](https://github.com/xiaoluoboding/vue-command-palette/commit/07868b78e510f1916063c9baa64e524bfe88ddbe))
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
92 |
93 |
94 |
95 |
96 |
117 |
154 |
155 |
156 |
157 |
162 |
163 |
164 |
212 |
213 |
214 |
219 |
220 |
221 |
222 |
223 |
224 |
--------------------------------------------------------------------------------
/src/components/command/Self.vue:
--------------------------------------------------------------------------------
1 |
91 |
92 |
93 |
94 |
95 |
99 |
100 |
101 |
102 | No results found.
103 |
104 |
111 |
112 | {{ item.label }}
113 |
114 |
126 |
127 |
128 |
129 |
130 | toggleDarkmode()"
137 | >
138 |
139 |
140 | {{ item.label }}
141 |
142 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
170 |
171 | -
172 | to select
184 |
185 | -
186 | to navigate
205 |
206 | -
207 | to close
219 |
220 |
221 |
222 |
223 |
224 |
225 |
228 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Command Palette for Vue
2 |
3 | [![NPM][npmBadge]][npmUrl]
4 | [![Minzip Package][bundlePhobiaBadge]][bundlePhobiaUrl]
5 | [![NPM Download][npmDtBadge]][npmDtUrl]
6 |
7 | [npmBadge]: https://img.shields.io/npm/v/vue-command-palette.svg?maxAge=2592000
8 | [npmUrl]: https://www.npmjs.com/package/vue-command-palette
9 | [npmDtBadge]: https://img.shields.io/npm/dt/vue-command-palette.svg
10 | [npmDtUrl]: https://www.npmjs.com/package/vue-command-palette
11 | [bundlePhobiaBadge]: https://img.shields.io/bundlephobia/minzip/vue-command-palette
12 | [bundlePhobiaUrl]: https://bundlephobia.com/package/vue-command-palette@latest
13 |
14 | > A fast, composable, unstyled `Command` + `K` interface (Command Palette) for Vue.
15 |
16 | ## Preview
17 |
18 | 
19 |
20 | ## Concepts
21 |
22 | Command palette interfaces are used to create a web experience where users can quickly get in charge with keyboard shortcuts, rather than using the mouse.
23 |
24 | With macOS's `Spotlight` and `Raycast`'s command palette experience in mind, `vue-command-palette` is designed to provide a fast, composable, unstyled command palette to your site.
25 |
26 | ## Table of Contents
27 |
28 |
29 |
30 | TOC
31 |
32 | - [Command Palette for Vue](#command-palette-for-vue)
33 | - [Preview](#preview)
34 | - [Concepts](#concepts)
35 | - [Table of Contents](#table-of-contents)
36 | - [Features](#features)
37 | - [Install](#install)
38 | - [Usage](#usage)
39 | - [Command + K or ?](#command--k-or-)
40 | - [Events](#events)
41 | - [Styling](#styling)
42 | - [Animation](#animation)
43 | - [Command.Dialog](#commanddialog)
44 | - [List Item Height](#list-item-height)
45 | - [Namespaced components](#namespaced-components)
46 | - [Command `[command-root=""]`](#command-command-root)
47 | - [Command.Dialog `[command-dialog=""]`](#commanddialog-command-dialog)
48 | - [Command.Input `[command-input=""]`](#commandinput-command-input)
49 | - [Command.List `[command-list=""]`](#commandlist-command-list)
50 | - [Command.Group `[command-group=""]`](#commandgroup-command-group)
51 | - [Command.Item `[command-item=""]`](#commanditem-command-item)
52 | - [Command.Separator `[command-separator=""]`](#commandseparator-command-separator)
53 | - [Command.Empty `[command-empty=""]`](#commandempty-command-empty)
54 | - [Command.Loading `[command-loading=""]`](#commandloading-command-loading)
55 | - [Inspiration](#inspiration)
56 | - [License](#license)
57 |
58 |
59 |
60 | ## Features
61 |
62 | - 🧩 [Compound Component](https://kentcdodds.com/blog/compound-components-with-react-hooks) + [Namespaced Components](https://vuejs.org/api/sfc-script-setup.html#using-components) Design
63 | - 💄 Completely unstyled, but more customizable
64 | - 🔍 Fuzzy search support to find relevant command
65 | - ⌨️ keyboard shortcut support to bind custom keybindings to your command
66 |
67 | ## Install
68 |
69 | ```bash
70 | yarn add vue-command-palette
71 | # or
72 | pnpm add vue-command-palette
73 | ```
74 |
75 | ## Usage
76 |
77 | Then you can import the `Command` [Compound Component](https://kentcdodds.com/blog/compound-components-with-react-hooks) in your project.
78 |
79 | ```vue
80 |
84 |
85 |
86 |
87 |
88 |
89 | No results found.
90 |
91 |
92 | a
93 | b
94 |
95 | c
96 |
97 |
98 | Apple
99 |
100 |
101 |
102 |
103 |
107 | ```
108 |
109 | or in a dialog:
110 |
111 | ```vue
112 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | No results found.
127 |
128 |
129 | a
130 | b
131 |
132 | c
133 |
134 |
135 | Apple
136 |
137 |
138 |
139 |
140 |
141 |
145 | ```
146 |
147 | ### Command + K or ?
148 |
149 | Do I have to use command + K? No, it's just a convention that you can use any key binding to perform the Command Palette.
150 |
151 | > Tips, we use `@vueuse/core` to bind the keybindings
152 |
153 | ```html
154 |
168 | ```
169 |
170 | ### Events
171 |
172 | | Name | Description | Parameters |
173 | | :-----------: | --------------------------------------------------------------------- | :--------------- |
174 | | `select-item` | Every time an item is being selected in `Command` or `Command.Dialog` | `(item) => void` |
175 |
176 | ### Styling
177 |
178 | All namespaced components have a specific `data-attribute` starting with `command-` that can be used for styling.
179 |
180 | eg:
181 |
182 | ```css
183 | div[command-root=''] {
184 | background: #ffffff;
185 | }
186 | ```
187 |
188 | ### Animation
189 |
190 | #### Command.Dialog
191 |
192 | `Command.Dialog` wraped by built-in components [Transition](https://vuejs.org/guide/built-ins/transition.html), you can customize the animation using the name `command-dialog` .
193 |
194 | [Example Code](https://github.com/xiaoluoboding/vue-command-palette/blob/main/src/assets/scss/algolia.scss#L193)
195 |
196 | #### List Item Height
197 |
198 | Animate height using the `--command-list-height` CSS variable.
199 |
200 | [Example Code](https://github.com/xiaoluoboding/vue-command-palette/blob/main/src/assets/scss/algolia.scss#L26)
201 |
202 | ## Namespaced components
203 |
204 | With [Namespaced components](https://vuejs.org/api/sfc-script-setup.html#using-components), You can use component tags with dots like `` to refer to components nested under object properties. This is useful when you import multiple components from a single file.
205 |
206 | ### Command `[command-root=""]`
207 |
208 | The root component, Passes the `theme` props to set your own style.
209 |
210 | ```vue
211 |
212 |
213 |
214 | ```
215 |
216 | ### Command.Dialog `[command-dialog=""]`
217 |
218 | The root component with a dialog interface, [Teleport](https://vuejs.org/guide/built-ins/teleport.html) dialog to `body` tag. Passes the `theme` props to set your own style, and `visible` props control whether render it.
219 |
220 | ```vue
221 |
222 |
223 |
224 |
225 |
226 |
227 | ```
228 |
229 | `data-attribute` within dialog
230 |
231 | - `[command-dialog-mask]` - the mask is always rendered.
232 | - `[command-dialog-wrapper]` - the wrapper on top of mask.
233 | - `[command-dialog-header]` - the parent of dialog header slot.
234 | - `[command-dialog-body]` - the parent of dialog body slot.
235 | - `[command-dialog-footer]` - the parent of dialog footer slot.
236 |
237 | ### Command.Input `[command-input=""]`
238 |
239 | Usually we need a input in the command palette to search sth.
240 |
241 | ```vue
242 |
246 | ```
247 |
248 | ### Command.List `[command-list=""]`
249 |
250 | Contains items and groups. Animate height using the `--command-list-height` CSS variable.
251 |
252 | ```css
253 | [command-list] {
254 | min-height: 300px;
255 | height: var(--command-list-height);
256 | max-height: 500px;
257 | transition: height 100ms ease;
258 | }
259 | ```
260 |
261 | ```vue
262 |
263 |
264 |
265 | ```
266 |
267 | ### Command.Group `[command-group=""]`
268 |
269 | Group items (`[command-group-items]`) together with the given `heading` (`[command-group-heading]`)
270 |
271 | ```vue
272 |
273 | Toggle Dark Mode
274 | Change Language
275 |
276 | ```
277 |
278 | ### Command.Item `[command-item=""]`
279 |
280 | Passed the `data-value`, we use `data-value` to fetch the value.
281 |
282 | ```vue
283 | console.log('selected', itemInfo)"
289 | // the itemInfo.value is some as `data-value`
290 | >
291 | {{ item.label }}
292 |
293 | ```
294 |
295 | ### Command.Separator `[command-separator=""]`
296 |
297 | Usually used to distinguish between different groups
298 |
299 | ### Command.Empty `[command-empty=""]`
300 |
301 | Automatically renders when there are no results for the search query.
302 |
303 | ### Command.Loading `[command-loading=""]`
304 |
305 | Your should manually control `loading`
306 |
307 | ## Inspiration
308 |
309 | - [cmdk](https://github.com/pacocoursey/cmdk) - Fast, unstyled command menu React component.
310 | - [kbar](https://github.com/timc1/kbar) - fast, portable, and extensible cmd+k interface for your site.
311 |
312 | ## License
313 |
314 | MIT [@xiaoluoboding](https://github.com/xiaoluoboding)
315 |
--------------------------------------------------------------------------------
/packages/CommandRoot.vue:
--------------------------------------------------------------------------------
1 |
371 |
372 |
373 |
383 |
384 |
--------------------------------------------------------------------------------
/src/assets/scss/raycast.scss:
--------------------------------------------------------------------------------
1 | .raycast {
2 | [command-dialog-wrapper] {
3 | max-width: 640px;
4 | width: 100%;
5 | background: var(--gray1);
6 | border-radius: 12px;
7 | padding: 8px 0;
8 | font-family: var(--font-sans);
9 | box-shadow: var(--command-shadow);
10 | border: 1px solid var(--gray6);
11 | position: relative;
12 |
13 | .dark & {
14 | background: var(--gray2);
15 | border: 0;
16 |
17 | &:after {
18 | content: '';
19 | background: linear-gradient(
20 | to right,
21 | var(--gray6) 20%,
22 | var(--gray6) 40%,
23 | var(--gray10) 50%,
24 | var(--gray10) 55%,
25 | var(--gray6) 70%,
26 | var(--gray6) 100%
27 | );
28 | z-index: -1;
29 | position: absolute;
30 | border-radius: 12px;
31 | top: -1px;
32 | left: -1px;
33 | width: calc(100% + 2px);
34 | height: calc(100% + 2px);
35 | animation: shine 3s ease forwards 0.1s;
36 | background-size: 200% auto;
37 | }
38 |
39 | &:before {
40 | content: '';
41 | z-index: -1;
42 | position: absolute;
43 | border-radius: 12px;
44 | top: -1px;
45 | left: -1px;
46 | width: calc(100% + 2px);
47 | height: calc(100% + 2px);
48 | box-shadow: 0 0 0 1px transparent;
49 | animation: border 1s linear forwards 0.5s;
50 | }
51 | }
52 |
53 | kbd {
54 | font-family: var(--font-sans);
55 | background: var(--gray3);
56 | color: var(--gray11);
57 | height: 20px;
58 | width: 20px;
59 | border-radius: 4px;
60 | padding: 0 4px;
61 | display: flex;
62 | align-items: center;
63 | justify-content: center;
64 |
65 | &:first-of-type {
66 | margin-left: 8px;
67 | }
68 | }
69 | }
70 |
71 | [command-input] {
72 | font-family: var(--font-sans);
73 | border: none;
74 | width: 100%;
75 | font-size: 15px;
76 | padding: 8px 16px;
77 | outline: none;
78 | background: var(--bg);
79 | color: var(--gray12);
80 |
81 | &::placeholder {
82 | color: var(--gray9);
83 | }
84 | }
85 |
86 | [command-raycast-top-shine] {
87 | .dark & {
88 | background: linear-gradient(
89 | 90deg,
90 | rgba(56, 189, 248, 0),
91 | var(--gray5) 20%,
92 | var(--gray9) 67.19%,
93 | rgba(236, 72, 153, 0)
94 | );
95 | height: 1px;
96 | position: absolute;
97 | top: -1px;
98 | width: 100%;
99 | z-index: -1;
100 | opacity: 0;
101 | animation: showTopShine 0.1s ease forwards 0.2s;
102 | }
103 | }
104 |
105 | [command-raycast-loader] {
106 | --loader-color: var(--gray9);
107 | border: 0;
108 | width: 100%;
109 | width: 100%;
110 | left: 0;
111 | height: 1px;
112 | background: var(--gray6);
113 | position: relative;
114 | overflow: visible;
115 | display: block;
116 | margin-top: 12px;
117 | margin-bottom: 12px;
118 |
119 | &:after {
120 | content: '';
121 | width: 50%;
122 | height: 1px;
123 | position: absolute;
124 | background: linear-gradient(
125 | 90deg,
126 | transparent 0%,
127 | var(--loader-color) 50%,
128 | transparent 100%
129 | );
130 | top: -1px;
131 | opacity: 0;
132 | animation-duration: 1.5s;
133 | animation-delay: 1s;
134 | animation-timing-function: ease;
135 | animation-name: loading;
136 | }
137 | }
138 |
139 | [command-item] {
140 | content-visibility: auto;
141 |
142 | cursor: pointer;
143 | height: 40px;
144 | border-radius: 8px;
145 | font-size: 14px;
146 | display: flex;
147 | align-items: center;
148 | gap: 8px;
149 | padding: 0 8px;
150 | color: var(--gray12);
151 | user-select: none;
152 | will-change: background, color;
153 | transition: all 150ms ease;
154 | transition-property: none;
155 |
156 | &[aria-selected='true'] {
157 | background: var(--gray4);
158 | color: var(--gray12);
159 | }
160 |
161 | &[aria-disabled='true'] {
162 | color: var(--gray8);
163 | cursor: not-allowed;
164 | }
165 |
166 | &:active {
167 | transition-property: background;
168 | background: var(--gray4);
169 | }
170 |
171 | &:first-child {
172 | margin-top: 8px;
173 | }
174 |
175 | & + [command-item] {
176 | margin-top: 4px;
177 | }
178 |
179 | svg {
180 | width: 18px;
181 | height: 18px;
182 | }
183 | }
184 |
185 | [command-raycast-meta] {
186 | margin-left: auto;
187 | color: var(--gray11);
188 | font-size: 13px;
189 | }
190 |
191 | [command-list] {
192 | padding: 0 8px;
193 | height: var(--cmd-list-height);
194 | overflow: auto;
195 | overscroll-behavior: contain;
196 | scroll-padding-block-end: 48px;
197 | transition: 100ms ease;
198 | transition-property: height;
199 | padding-bottom: 40px;
200 | }
201 |
202 | [command-raycast-open-trigger],
203 | [command-raycast-subcommand-trigger] {
204 | color: var(--gray11);
205 | background: var(--grayA5);
206 | padding: 0px 4px 0px 8px;
207 | border-radius: 6px;
208 | font-weight: 500;
209 | font-size: 12px;
210 | height: 28px;
211 | letter-spacing: -0.25px;
212 | }
213 |
214 | [command-raycast-clipboard-icon],
215 | [command-raycast-hammer-icon] {
216 | width: 20px;
217 | height: 20px;
218 | border-radius: 6px;
219 | display: flex;
220 | align-items: center;
221 | justify-content: center;
222 | color: #ffffff;
223 |
224 | svg {
225 | width: 14px;
226 | height: 14px;
227 | }
228 | }
229 |
230 | [command-raycast-clipboard-icon] {
231 | background: linear-gradient(to bottom, #f55354, #eb4646);
232 | }
233 |
234 | [command-raycast-hammer-icon] {
235 | background: linear-gradient(to bottom, #6cb9a3, #2c6459);
236 | }
237 |
238 | [command-raycast-open-trigger] {
239 | display: flex;
240 | align-items: center;
241 | color: var(--gray12);
242 | }
243 |
244 | [command-raycast-subcommand-trigger] {
245 | display: flex;
246 | align-items: center;
247 | gap: 4px;
248 | right: 8px;
249 | bottom: 8px;
250 |
251 | svg {
252 | width: 14px;
253 | height: 14px;
254 | }
255 |
256 | hr {
257 | height: 100%;
258 | background: var(--gray6);
259 | border: 0;
260 | width: 1px;
261 | }
262 |
263 | &[aria-expanded='true'],
264 | &:hover {
265 | background: var(--gray4);
266 |
267 | kbd {
268 | background: var(--gray7);
269 | }
270 | }
271 | }
272 |
273 | [command-separator] {
274 | height: 1px;
275 | width: 100%;
276 | background: var(--gray5);
277 | margin: 4px 0;
278 | }
279 |
280 | * + [command-group] {
281 | margin-top: 8px;
282 | }
283 |
284 | [command-group-heading] {
285 | user-select: none;
286 | font-size: 12px;
287 | color: var(--gray11);
288 | padding: 0 8px;
289 | display: flex;
290 | align-items: center;
291 | }
292 |
293 | [command-dialog-footer] {
294 | display: flex;
295 | flex-direction: row;
296 | height: 40px;
297 | align-items: center;
298 | width: 100%;
299 | position: absolute;
300 | background: var(--gray1);
301 | bottom: 0;
302 | padding: 8px;
303 | border-top: 1px solid var(--gray6);
304 | border-radius: 0 0 12px 12px;
305 |
306 | svg {
307 | width: 20px;
308 | height: 20px;
309 | filter: grayscale(1);
310 | margin-right: auto;
311 | }
312 |
313 | hr {
314 | height: 12px;
315 | width: 1px;
316 | border: 0;
317 | background: var(--gray6);
318 | margin: 0 4px 0px 12px;
319 | }
320 |
321 | @media (prefers-color-scheme: dark) {
322 | background: var(--gray2);
323 | }
324 | }
325 |
326 | [command-popover] {
327 | z-index: var(--layer-portal);
328 | position: fixed;
329 | left: 50%;
330 | top: var(--page-top);
331 | transform: translateX(-50%);
332 |
333 | [command] {
334 | width: 640px;
335 | transform-origin: center center;
336 | animation: dialogIn var(--transition-fast) forwards;
337 | }
338 |
339 | &[data-state='closed'] [command] {
340 | animation: dialogOut var(--transition-fast) forwards;
341 | }
342 | }
343 |
344 | [command-empty] {
345 | font-size: 14px;
346 | display: flex;
347 | align-items: center;
348 | justify-content: center;
349 | height: 64px;
350 | white-space: pre-wrap;
351 | color: var(--gray11);
352 | }
353 | }
354 |
355 | @keyframes loading {
356 | 0% {
357 | opacity: 0;
358 | transform: translateX(0);
359 | }
360 |
361 | 50% {
362 | opacity: 1;
363 | transform: translateX(100%);
364 | }
365 |
366 | 100% {
367 | opacity: 0;
368 | transform: translateX(0);
369 | }
370 | }
371 |
372 | @keyframes shine {
373 | to {
374 | background-position: 200% center;
375 | opacity: 0;
376 | }
377 | }
378 |
379 | @keyframes border {
380 | to {
381 | box-shadow: 0 0 0 1px var(--gray6);
382 | }
383 | }
384 |
385 | @keyframes showTopShine {
386 | to {
387 | opacity: 1;
388 | }
389 | }
390 |
391 | .raycast-submenu {
392 | [command-root] {
393 | display: flex;
394 | flex-direction: column;
395 | width: 320px;
396 | border: 1px solid var(--gray6);
397 | background: var(--gray2);
398 | border-radius: 8px;
399 | }
400 |
401 | [command-list] {
402 | padding: 8px;
403 | overflow: auto;
404 | overscroll-behavior: contain;
405 | transition: 100ms ease;
406 | transition-property: height;
407 | }
408 |
409 | [command-item] {
410 | height: 40px;
411 |
412 | cursor: pointer;
413 | height: 40px;
414 | border-radius: 8px;
415 | font-size: 13px;
416 | display: flex;
417 | align-items: center;
418 | gap: 8px;
419 | padding: 0 8px;
420 | color: var(--gray12);
421 | user-select: none;
422 | will-change: background, color;
423 | transition: all 150ms ease;
424 | transition-property: none;
425 |
426 | &[aria-selected='true'] {
427 | background: var(--gray5);
428 | color: var(--gray12);
429 |
430 | [command-raycast-submenu-shortcuts] kbd {
431 | background: var(--gray7);
432 | }
433 | }
434 |
435 | &[aria-disabled='true'] {
436 | color: var(--gray8);
437 | cursor: not-allowed;
438 | }
439 |
440 | svg {
441 | width: 16px;
442 | height: 16px;
443 | }
444 |
445 | [command-raycast-submenu-shortcuts] {
446 | display: flex;
447 | margin-left: auto;
448 | gap: 2px;
449 |
450 | kbd {
451 | font-family: var(--font-sans);
452 | background: var(--gray5);
453 | color: var(--gray11);
454 | height: 20px;
455 | width: 20px;
456 | border-radius: 4px;
457 | padding: 0 4px;
458 | font-size: 12px;
459 | display: flex;
460 | align-items: center;
461 | justify-content: center;
462 |
463 | &:first-of-type {
464 | margin-left: 8px;
465 | }
466 | }
467 | }
468 | }
469 |
470 | [command-group-heading] {
471 | text-transform: capitalize;
472 | font-size: 12px;
473 | color: var(--gray11);
474 | font-weight: 500;
475 | margin-bottom: 8px;
476 | margin-top: 8px;
477 | margin-left: 4px;
478 | }
479 |
480 | [command-input] {
481 | padding: 12px;
482 | font-family: var(--font-sans);
483 | border: 0;
484 | border-top: 1px solid var(--gray6);
485 | font-size: 13px;
486 | background: transparent;
487 | margin-top: auto;
488 | width: 100%;
489 | outline: 0;
490 | border-radius: 0;
491 | }
492 |
493 | animation-duration: 0.2s;
494 | animation-timing-function: ease;
495 | animation-fill-mode: forwards;
496 | transform-origin: var(--radix-popover-content-transform-origin);
497 |
498 | &[data-state='open'] {
499 | animation-name: slideIn;
500 | }
501 |
502 | &[data-state='closed'] {
503 | animation-name: slideOut;
504 | }
505 |
506 | [command-empty] {
507 | display: flex;
508 | align-items: center;
509 | justify-content: center;
510 | height: 64px;
511 | white-space: pre-wrap;
512 | font-size: 14px;
513 | color: var(--gray11);
514 | }
515 | }
516 |
517 | @keyframes slideIn {
518 | 0% {
519 | opacity: 0;
520 | transform: scale(0.96);
521 | }
522 |
523 | 100% {
524 | opacity: 1;
525 | transform: scale(1);
526 | }
527 | }
528 |
529 | @keyframes slideOut {
530 | 0% {
531 | opacity: 1;
532 | transform: scale(1);
533 | }
534 |
535 | 100% {
536 | opacity: 0;
537 | transform: scale(0.96);
538 | }
539 | }
540 |
541 | @media (max-width: 640px) {
542 | .raycast {
543 | [command-input] {
544 | font-size: 16px;
545 | }
546 | }
547 | }
548 |
--------------------------------------------------------------------------------