@bysivin/jumpbar 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +241 -2
- package/dist/core.js +148 -0
- package/dist/core.js.map +1 -0
- package/dist/index.js +583 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -4
- package/tailwind.cjs +56 -0
- package/theme.css +33 -0
- package/types/core.d.ts +76 -0
- package/types/index.d.ts +77 -0
package/package.json
CHANGED
|
@@ -1,6 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bysivin/jumpbar",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Lightweight command palette for Vue 3: a framework-free ranking core with fuzzy matching, sections, pages, prefixes and frecency, plus a native <dialog> shell with async sources.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Sivin Varghese",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./types/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./types/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./core": {
|
|
18
|
+
"types": "./types/core.d.ts",
|
|
19
|
+
"import": "./dist/core.js",
|
|
20
|
+
"default": "./dist/core.js"
|
|
21
|
+
},
|
|
22
|
+
"./tailwind": "./tailwind.cjs",
|
|
23
|
+
"./theme.css": "./theme.css",
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"types",
|
|
29
|
+
"tailwind.cjs",
|
|
30
|
+
"theme.css"
|
|
31
|
+
],
|
|
32
|
+
"sideEffects": [
|
|
33
|
+
"*.css"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/iamsivin/jumpbar.git"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"command-palette",
|
|
44
|
+
"command-bar",
|
|
45
|
+
"spotlight",
|
|
46
|
+
"vue",
|
|
47
|
+
"headless",
|
|
48
|
+
"fuzzy-search"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "vite build",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"prepublishOnly": "pnpm build && pnpm test"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"vue": "^3.4.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"vue": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@vitejs/plugin-vue": "^5.2.4",
|
|
66
|
+
"@vue/test-utils": "^2.4.6",
|
|
67
|
+
"jsdom": "^27.2.0",
|
|
68
|
+
"vite": "^6.4.4",
|
|
69
|
+
"vitest": "3.0.5",
|
|
70
|
+
"vue": "^3.5.12"
|
|
71
|
+
}
|
|
72
|
+
}
|
package/tailwind.cjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const TOKENS = [
|
|
2
|
+
'background',
|
|
3
|
+
'foreground',
|
|
4
|
+
'muted-foreground',
|
|
5
|
+
'subtle-foreground',
|
|
6
|
+
'muted',
|
|
7
|
+
'subtle',
|
|
8
|
+
'border',
|
|
9
|
+
'ring',
|
|
10
|
+
'primary',
|
|
11
|
+
'primary-soft',
|
|
12
|
+
'overlay',
|
|
13
|
+
'online',
|
|
14
|
+
'busy',
|
|
15
|
+
'offline',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
theme: {
|
|
20
|
+
extend: {
|
|
21
|
+
colors: {
|
|
22
|
+
jumpbar: Object.fromEntries(
|
|
23
|
+
TOKENS.map(name => [name, `var(--jumpbar-${name})`])
|
|
24
|
+
),
|
|
25
|
+
},
|
|
26
|
+
keyframes: {
|
|
27
|
+
'pop-in': {
|
|
28
|
+
'0%': { opacity: 0, transform: 'scale(0.8) translateY(0.25rem)' },
|
|
29
|
+
'60%': { opacity: 1, transform: 'scale(1.04) translateY(0)' },
|
|
30
|
+
'100%': { opacity: 1, transform: 'scale(1) translateY(0)' },
|
|
31
|
+
},
|
|
32
|
+
'fade-in': {
|
|
33
|
+
'0%': { opacity: 0 },
|
|
34
|
+
},
|
|
35
|
+
'fade-in-up': {
|
|
36
|
+
'0%': { opacity: 0, transform: 'translateY(0.5rem)' },
|
|
37
|
+
'100%': { opacity: 1, transform: 'translateY(0)' },
|
|
38
|
+
},
|
|
39
|
+
'command-bar-out': {
|
|
40
|
+
from: { opacity: 1, transform: 'scale(1) translateY(0)' },
|
|
41
|
+
to: { opacity: 0, transform: 'scale(0.92) translateY(0.5rem)' },
|
|
42
|
+
},
|
|
43
|
+
'fade-out': {
|
|
44
|
+
to: { opacity: 0 },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
animation: {
|
|
48
|
+
'pop-in': 'pop-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) backwards',
|
|
49
|
+
'fade-in': 'fade-in 0.3s ease-out',
|
|
50
|
+
'fade-in-up': 'fade-in-up 0.3s ease-out',
|
|
51
|
+
'command-bar-out': 'command-bar-out 0.15s ease-in forwards',
|
|
52
|
+
'fade-out': 'fade-out 0.15s ease-in forwards',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
package/theme.css
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--jumpbar-background: #ffffff;
|
|
3
|
+
--jumpbar-foreground: #1c2024;
|
|
4
|
+
--jumpbar-muted-foreground: #60646c;
|
|
5
|
+
--jumpbar-subtle-foreground: #80838d;
|
|
6
|
+
--jumpbar-muted: rgb(0 0 0 / 0.05);
|
|
7
|
+
--jumpbar-subtle: rgb(0 0 0 / 0.03);
|
|
8
|
+
--jumpbar-border: rgb(0 0 0 / 0.08);
|
|
9
|
+
--jumpbar-ring: rgb(0 0 0 / 0.14);
|
|
10
|
+
--jumpbar-primary: #0090ff;
|
|
11
|
+
--jumpbar-primary-soft: rgb(0 144 255 / 0.1);
|
|
12
|
+
--jumpbar-overlay: rgb(0 0 0 / 0.45);
|
|
13
|
+
--jumpbar-online: #12a594;
|
|
14
|
+
--jumpbar-busy: #ffba18;
|
|
15
|
+
--jumpbar-offline: #80838d;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.dark {
|
|
19
|
+
--jumpbar-background: #18191b;
|
|
20
|
+
--jumpbar-foreground: #edeef0;
|
|
21
|
+
--jumpbar-muted-foreground: #b0b4ba;
|
|
22
|
+
--jumpbar-subtle-foreground: #777b84;
|
|
23
|
+
--jumpbar-muted: rgb(255 255 255 / 0.07);
|
|
24
|
+
--jumpbar-subtle: rgb(255 255 255 / 0.04);
|
|
25
|
+
--jumpbar-border: rgb(255 255 255 / 0.1);
|
|
26
|
+
--jumpbar-ring: rgb(255 255 255 / 0.16);
|
|
27
|
+
--jumpbar-primary: #0090ff;
|
|
28
|
+
--jumpbar-primary-soft: rgb(0 144 255 / 0.15);
|
|
29
|
+
--jumpbar-overlay: rgb(0 0 0 / 0.6);
|
|
30
|
+
--jumpbar-online: #12a594;
|
|
31
|
+
--jumpbar-busy: #ffba18;
|
|
32
|
+
--jumpbar-offline: #777b84;
|
|
33
|
+
}
|
package/types/core.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export type Context = { scopes?: string[] } & Record<string, unknown>;
|
|
2
|
+
|
|
3
|
+
export interface Avatar {
|
|
4
|
+
name: string;
|
|
5
|
+
src?: string | null;
|
|
6
|
+
status?: 'online' | 'busy' | 'offline';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Item {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
section?: string;
|
|
14
|
+
icon?: string;
|
|
15
|
+
avatar?: Avatar;
|
|
16
|
+
badge?: string;
|
|
17
|
+
shortcut?: string[];
|
|
18
|
+
keywords?: string[];
|
|
19
|
+
scopes?: string[];
|
|
20
|
+
prefix?: string;
|
|
21
|
+
weight?: number;
|
|
22
|
+
place?: 'first' | 'last';
|
|
23
|
+
searchFromRoot?: boolean;
|
|
24
|
+
page?: boolean;
|
|
25
|
+
placeholder?: string;
|
|
26
|
+
parent?: string;
|
|
27
|
+
run?: (context: Context) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RankedItem extends Item {
|
|
31
|
+
parentTitle?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface Section<T extends Item = RankedItem> {
|
|
35
|
+
section: string | null;
|
|
36
|
+
items: T[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Frecency {
|
|
40
|
+
record(id: string): void;
|
|
41
|
+
score(id: string): number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface FrecencyStorage {
|
|
45
|
+
getItem(key: string): string | null;
|
|
46
|
+
setItem(key: string, value: string): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface FrecencyOptions {
|
|
50
|
+
storage: FrecencyStorage;
|
|
51
|
+
key: string;
|
|
52
|
+
now?: () => number;
|
|
53
|
+
limit?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface RankOptions {
|
|
57
|
+
text?: string;
|
|
58
|
+
prefix?: string | null;
|
|
59
|
+
page?: string | null;
|
|
60
|
+
scopes?: string[];
|
|
61
|
+
frecency?: Pick<Frecency, 'score'>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function normalize(value: unknown): string;
|
|
65
|
+
export function fuzzyScore(
|
|
66
|
+
query: string,
|
|
67
|
+
text: string,
|
|
68
|
+
options?: { subsequence?: boolean }
|
|
69
|
+
): number;
|
|
70
|
+
export function parseQuery(
|
|
71
|
+
input: string,
|
|
72
|
+
prefixes?: string[]
|
|
73
|
+
): { prefix: string | null; text: string };
|
|
74
|
+
export function createFrecency(options: FrecencyOptions): Frecency;
|
|
75
|
+
export function rank(items: Item[], options: RankOptions): RankedItem[];
|
|
76
|
+
export function groupBySection<T extends Item>(items: T[]): Section<T>[];
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ComputedRef, DefineComponent, Ref } from 'vue';
|
|
2
|
+
import type { Context, Frecency, Item, RankedItem, Section } from './core';
|
|
3
|
+
|
|
4
|
+
export * from './core';
|
|
5
|
+
|
|
6
|
+
export interface Request {
|
|
7
|
+
query: string;
|
|
8
|
+
text: string;
|
|
9
|
+
prefix: string | null;
|
|
10
|
+
page: string | null;
|
|
11
|
+
context: Context;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SearchRequest extends Request {
|
|
15
|
+
matches: number;
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Source {
|
|
20
|
+
id: string;
|
|
21
|
+
minQuery?: number;
|
|
22
|
+
debounce?: number;
|
|
23
|
+
when?: (request: Request | SearchRequest) => boolean;
|
|
24
|
+
commands?: (request: Request) => Item[];
|
|
25
|
+
search?: (request: SearchRequest) => Promise<Item[]> | Item[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CommandBarOptions {
|
|
29
|
+
prefixes?: string[];
|
|
30
|
+
context?: () => Context;
|
|
31
|
+
frecency?: Frecency;
|
|
32
|
+
onSelect?: (item: Item) => void;
|
|
33
|
+
onClose?: () => void;
|
|
34
|
+
onError?: (error: unknown, source: Source) => void;
|
|
35
|
+
debounce?: number;
|
|
36
|
+
limit?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CommandBarInstance {
|
|
40
|
+
isOpen: Ref<boolean>;
|
|
41
|
+
query: Ref<string>;
|
|
42
|
+
page: ComputedRef<Item | null>;
|
|
43
|
+
pages: Ref<Item[]>;
|
|
44
|
+
visible: ComputedRef<RankedItem[]>;
|
|
45
|
+
sections: ComputedRef<Section[]>;
|
|
46
|
+
selectedIndex: Ref<number>;
|
|
47
|
+
selected: ComputedRef<RankedItem | null>;
|
|
48
|
+
isLoading: ComputedRef<boolean>;
|
|
49
|
+
open(options?: { page?: string | null; query?: string }): void;
|
|
50
|
+
close(): void;
|
|
51
|
+
toggle(): void;
|
|
52
|
+
back(): void;
|
|
53
|
+
select(item: Item | null | undefined): void;
|
|
54
|
+
setQuery(value: string): void;
|
|
55
|
+
moveSelection(delta: number): void;
|
|
56
|
+
register(source: Source): () => void;
|
|
57
|
+
configure(overrides: CommandBarOptions): void;
|
|
58
|
+
dispose(): void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createCommandBar(options?: CommandBarOptions): CommandBarInstance;
|
|
62
|
+
export function useCommandBar(): CommandBarInstance;
|
|
63
|
+
|
|
64
|
+
export interface Hint {
|
|
65
|
+
keys: string[];
|
|
66
|
+
label: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CommandBarProps {
|
|
70
|
+
placeholder?: string;
|
|
71
|
+
emptyText?: string;
|
|
72
|
+
scopeLabel?: string;
|
|
73
|
+
hints?: Hint[];
|
|
74
|
+
dir?: 'ltr' | 'rtl';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const CommandBar: DefineComponent<CommandBarProps>;
|