@witchcraft/spellcraft 0.0.3 → 0.1.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/README.md +113 -14
- package/dist/helpers/getKeyCodesFromKeys.d.ts +13 -0
- package/dist/helpers/getKeyCodesFromKeys.js +16 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -8
- package/dist/runtime/types.d.ts +2 -4
- package/package.json +156 -155
- package/src/helpers/getKeyCodesFromKeys.ts +28 -0
- package/src/module.ts +1 -3
- package/src/runtime/types.ts +17 -4
package/README.md
CHANGED
|
@@ -123,7 +123,7 @@ You can choose in your execute function what exactly to do at that point for bot
|
|
|
123
123
|
|
|
124
124
|
If non-modifier keys are still being held at this point, the manager will not allow triggering a shortcut until they are released (see `state.isAwaitingKeyup`). Modifiers are not affect by this. We usually want the user to be able to keep the modifier pressed and do, for example, `Ctrl+B` then `Ctrl+I` to bold and italicize text, without having to release `Ctrl`, only `B` and `I`.
|
|
125
125
|
|
|
126
|
-
## Errors and
|
|
126
|
+
## Errors and Checking if Actions can be Performed
|
|
127
127
|
|
|
128
128
|
Note the use of `unwrap()`. Because many actions can throw "soft" errors, to better help deal with all the errors the library uses a Result monad in most of the return types. `unwrap` is like rust's unwrap and will throw the error if there was one, otherwise "unwrap" and return the value within.
|
|
129
129
|
|
|
@@ -278,12 +278,58 @@ export { }
|
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
Additionally, when you create a condition, you can pass a function to `parse` it and add these needed properties:
|
|
281
|
+
|
|
282
|
+
You'll probably want to create a wrapping function to do this. Here's an example with the expressit library:
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
<details>
|
|
286
|
+
|
|
287
|
+
<summary>Click to Expand</summary>
|
|
288
|
+
|
|
281
289
|
```ts
|
|
282
|
-
|
|
283
|
-
|
|
290
|
+
import { ShortcutContextParser } from "@witchcraft/expressit/examples/ShortcutContextParser"
|
|
291
|
+
import { createCondition as _createCondition } from "@witchcraft/spellcraft"
|
|
292
|
+
|
|
293
|
+
const dummyContext = { a: { }, b: { c: {} } }
|
|
294
|
+
const shortcutParser = new ShortcutContextParser(dummyContext) // see docs for details
|
|
295
|
+
function parser(text: string, _: Condition) {
|
|
296
|
+
const res = shortcutsParser.parse(text)
|
|
297
|
+
// === undefined is to narrow out the ErrorToken type
|
|
298
|
+
if (!res.valid || res.type === undefined) {
|
|
299
|
+
throw new Error(`Shortcut context parser failed to parse condition ${_.text}`)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
_.ast = res
|
|
284
303
|
return _
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Creates a condition with the ast saved in it. */
|
|
307
|
+
function createCondition(text: string) {
|
|
308
|
+
return _createCondition(text, parser)
|
|
309
|
+
}
|
|
310
|
+
const condition = createCondition("a || b")
|
|
311
|
+
condition.ast // should exist
|
|
312
|
+
|
|
313
|
+
// tell the manager how to evaluare the condition:
|
|
314
|
+
const manager = createmanager({
|
|
315
|
+
context: createcontext<context<map<string, boolean>>>(new map()),
|
|
316
|
+
options: {
|
|
317
|
+
evaluatecondition(condition, context) {
|
|
318
|
+
if (condition.ast === undefined) throw new Error("condition ast is undefined")
|
|
319
|
+
|
|
320
|
+
const res = condition.ast.valid
|
|
321
|
+
? conditionParser.evaluate(
|
|
322
|
+
conditionParser.normalize(condition.ast),
|
|
323
|
+
context.value.isActive
|
|
324
|
+
)
|
|
325
|
+
: false
|
|
326
|
+
return res
|
|
327
|
+
},
|
|
328
|
+
}
|
|
285
329
|
})
|
|
330
|
+
|
|
286
331
|
```
|
|
332
|
+
</details>
|
|
287
333
|
|
|
288
334
|
## Contexts
|
|
289
335
|
|
|
@@ -292,10 +338,10 @@ Similarly with contexts, you can use any sort of object or type that you like.
|
|
|
292
338
|
You can tell the manager it's type when you create it. For example, say we wanted to use a map:
|
|
293
339
|
|
|
294
340
|
```ts
|
|
295
|
-
const manager =
|
|
296
|
-
context:
|
|
341
|
+
const manager = createmanager({
|
|
342
|
+
context: createcontext<context<map<string, boolean>>>(new map()),
|
|
297
343
|
options: {
|
|
298
|
-
|
|
344
|
+
evaluatecondition(condition, context) {
|
|
299
345
|
// context is now correctly typed
|
|
300
346
|
return context.value.has(condition.text)
|
|
301
347
|
},
|
|
@@ -308,10 +354,10 @@ const manager = createManager({
|
|
|
308
354
|
Creating a shortcut requires a the key/commands we created and the manager options to create a valid shortcut.
|
|
309
355
|
|
|
310
356
|
```ts
|
|
311
|
-
const shortcut =createShortcut({
|
|
357
|
+
const shortcut = createShortcut({
|
|
312
358
|
command: "test",
|
|
313
359
|
chain: [["a"]],
|
|
314
|
-
condition: createCondition("a || b"
|
|
360
|
+
condition: createCondition("a || b"),
|
|
315
361
|
enabled: true,
|
|
316
362
|
}, {options, keys, commands}).unwrap()
|
|
317
363
|
|
|
@@ -379,6 +425,10 @@ Note that while the built in errors are property specific, custom errors are not
|
|
|
379
425
|
|
|
380
426
|
A helper class `ShortcutManagerManager` is provided to help manage multiple managers.
|
|
381
427
|
|
|
428
|
+
<details>
|
|
429
|
+
|
|
430
|
+
<summary>Click to Expand</summary>
|
|
431
|
+
|
|
382
432
|
```ts
|
|
383
433
|
import { ShortcutManagerManager } from "@witchcraft/spellcraft"
|
|
384
434
|
|
|
@@ -435,6 +485,9 @@ managerManager.duplicateManager("myManagerName", "myDuplicateManagerName")
|
|
|
435
485
|
managerManager.deleteManager("myManagerName")
|
|
436
486
|
managerManager.renameManager("myManagerName", "myNewManagerName")
|
|
437
487
|
```
|
|
488
|
+
|
|
489
|
+
</details>
|
|
490
|
+
|
|
438
491
|
You can then use the active manager's `onSet*Prop` hooks to call debouncedSave. You can wrap only the active manager to intercept all it's `onSet*Prop` calls. See the `useMultipleManagers` composable in the demo.
|
|
439
492
|
|
|
440
493
|
## Other Helpers and Utilities
|
|
@@ -453,6 +506,7 @@ There are many helpers provided to simplify common use cases under `/helpers`. S
|
|
|
453
506
|
There's also some smaller utility functions in `/utils`:
|
|
454
507
|
- `equals/dedupe/clone/*Key` These are particularly important for manipulating chords. This is because keys which are variants of eachother (see `Key.variants`) do not have matching ids and we usually want to be able to dedupe by the variants as well.
|
|
455
508
|
- `isAny/Trigger/Wheel/MouseKey`.
|
|
509
|
+
- `getKeyCodesFromKeys` for getting the raw key codes from a list of keys for use with third-party libraries.
|
|
456
510
|
|
|
457
511
|
|
|
458
512
|
There's also a few other functions that in the future might be moved from the demo were I created them and into the library. See [demo/src/common](https://github.com/AlansCodeLog/spellcraft/tree/master/demo/src/common).
|
|
@@ -490,18 +544,28 @@ Under gnome at least, if a key (usually Ctrl) is set to locate the cursor, it wi
|
|
|
490
544
|
|
|
491
545
|
# FAQ
|
|
492
546
|
|
|
493
|
-
|
|
547
|
+
|
|
548
|
+
<details>
|
|
549
|
+
|
|
550
|
+
<summary>Browser shortcuts interfere with certain shortcuts, how can this be avoided?</summary>
|
|
494
551
|
|
|
495
552
|
You can use a listener on the manager to e.preventDefault() some of these, but this doesn't work for all of them.
|
|
496
553
|
|
|
497
554
|
If available you can also try using the [Keyboard API's](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard_API) lock method (see [Keyboard Locking](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard_API#keyboard_locking) ).
|
|
498
555
|
|
|
499
|
-
|
|
556
|
+
</details>
|
|
557
|
+
|
|
558
|
+
<details>
|
|
559
|
+
|
|
560
|
+
<summary>How to label keys with their local names?</summary>
|
|
500
561
|
|
|
501
562
|
If the [Keyboard API](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard_API) is available, you can use it's [navigator.keyboard.getLayoutMap method.](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap). Helpers (getKeyboardLayoutMap and labelWithNavigator) are provided for this purpose, see them for details.
|
|
502
563
|
|
|
564
|
+
</details>
|
|
503
565
|
|
|
504
|
-
|
|
566
|
+
<details>
|
|
567
|
+
|
|
568
|
+
<summary>How to set multiple manager properties safely (i.e. batch replace shortcuts/commands/keys)?</summary>
|
|
505
569
|
|
|
506
570
|
This can be an issue because there isn't a way to tell the manager you want to replace *multiple* properties and it might be impossible to, for example, replace commands with a smaller subset but not have it error even if you're planning to replace the shortcuts so they don't contain missing commands.
|
|
507
571
|
|
|
@@ -519,7 +583,11 @@ if (isValidManager(manager)) {
|
|
|
519
583
|
}
|
|
520
584
|
|
|
521
585
|
```
|
|
522
|
-
|
|
586
|
+
</details>
|
|
587
|
+
|
|
588
|
+
<details>
|
|
589
|
+
|
|
590
|
+
<summary>How to create `modifier-only` shortcuts? (e.g. a shortcut `Ctrl` that changes the some state like enabling multiple selection).</summary>
|
|
523
591
|
|
|
524
592
|
To do this, instead of clearing the manager's chain, you just set the state directly.
|
|
525
593
|
|
|
@@ -563,17 +631,48 @@ function addToSelected(item) {
|
|
|
563
631
|
}
|
|
564
632
|
</script>
|
|
565
633
|
```
|
|
566
|
-
|
|
634
|
+
</details>
|
|
635
|
+
|
|
636
|
+
<details>
|
|
637
|
+
|
|
638
|
+
<summary>How to show pretty shortcut strings to the user?</summary>
|
|
639
|
+
|
|
640
|
+
To show a pretty stringified version of a shortcut, use `manager.options.stringifier.stringify(shortcut.chain, manager)`.
|
|
641
|
+
|
|
642
|
+
Here's an advanced example in vue that shows a hint only when needed:
|
|
643
|
+
|
|
644
|
+
```ts
|
|
645
|
+
const usageInstructions = computed(() => {
|
|
646
|
+
const manager = shortcuts.activeManager!.value!
|
|
647
|
+
const s = manager.options.stringifier
|
|
648
|
+
const context = shortcuts.context!
|
|
649
|
+
const isDragging = context.value.layout.drag.isDragging
|
|
650
|
+
const splitChain = manager.shortcuts.entries.find(_ => _.command === LAYOUT_COMMANDS.dragModifierSplit)?.chain
|
|
651
|
+
|
|
652
|
+
return isDragging && splitChain
|
|
653
|
+
`Hold ${s.stringify(splitChain, manager)} to Split`
|
|
654
|
+
: undefined,
|
|
655
|
+
})
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
</details>
|
|
659
|
+
|
|
660
|
+
<details>
|
|
661
|
+
|
|
662
|
+
<summary>How to type the context?</summary>
|
|
663
|
+
|
|
664
|
+
Types can be extended multiple times from different files so long as the `NAME` part below is unique for each extension.
|
|
567
665
|
|
|
568
666
|
```ts [global.ts]
|
|
569
667
|
declare module "@witchcraft/spellcraft/types" {
|
|
570
668
|
// or for nuxt
|
|
571
669
|
// declare module "#witchcraft/spellcraft/types.js" {
|
|
572
670
|
export interface Register {
|
|
573
|
-
|
|
671
|
+
ShortcutManagerContextNAME: { //replace NAME with something
|
|
574
672
|
// extend types here
|
|
575
673
|
}
|
|
576
674
|
}
|
|
577
675
|
}
|
|
578
676
|
```
|
|
677
|
+
</details>
|
|
579
678
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given an list of keys, returns an array of deduped key codes.
|
|
3
|
+
*
|
|
4
|
+
* Useful for when the codes must be passed to a third-party library (for example, a dragging library that takes a list modifiers).
|
|
5
|
+
*
|
|
6
|
+
* This properly takes a look at the key and all it's variants.
|
|
7
|
+
*
|
|
8
|
+
* `skipIdIfHasVariants` is true by default and will skip adding the key's id if it has variants as it's assumed the id is not a valid name.
|
|
9
|
+
*/
|
|
10
|
+
import type { Key } from "../types/keys.js";
|
|
11
|
+
export declare function getKeyCodesFromKeys(keys: Key[], { skipIdIfHasVariants }?: {
|
|
12
|
+
skipIdIfHasVariants?: boolean;
|
|
13
|
+
}): string[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function getKeyCodesFromKeys(keys, { skipIdIfHasVariants = true } = {}) {
|
|
2
|
+
const res = /* @__PURE__ */ new Set();
|
|
3
|
+
for (const key of keys) {
|
|
4
|
+
if (key.variants) {
|
|
5
|
+
if (!skipIdIfHasVariants) {
|
|
6
|
+
res.add(key.id);
|
|
7
|
+
}
|
|
8
|
+
for (const variant of key.variants) {
|
|
9
|
+
res.add(variant);
|
|
10
|
+
}
|
|
11
|
+
} else {
|
|
12
|
+
res.add(key.id);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return Array.from(res);
|
|
16
|
+
}
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { createResolver, defineNuxtModule, addTypeTemplate, addImportsDir } from '@nuxt/kit';
|
|
2
2
|
|
|
3
|
-
const dependencies = {
|
|
4
|
-
"@witchcraft/ui": "^0.3.2"};
|
|
5
|
-
const pkg = {
|
|
6
|
-
dependencies: dependencies};
|
|
7
|
-
|
|
8
3
|
const { resolve } = createResolver(import.meta.url);
|
|
9
|
-
const module = defineNuxtModule({
|
|
4
|
+
const module$1 = defineNuxtModule({
|
|
10
5
|
meta: {
|
|
11
6
|
name: "witchcraftSpellcraft",
|
|
12
7
|
configKey: "witchcraftSpellcraft"
|
|
@@ -14,7 +9,7 @@ const module = defineNuxtModule({
|
|
|
14
9
|
defaults: {},
|
|
15
10
|
moduleDependencies: {
|
|
16
11
|
"@witchcraft/ui/nuxt": {
|
|
17
|
-
version:
|
|
12
|
+
version: "^0.3.6"
|
|
18
13
|
}
|
|
19
14
|
},
|
|
20
15
|
async setup(_options, nuxt) {
|
|
@@ -37,4 +32,4 @@ const module = defineNuxtModule({
|
|
|
37
32
|
}
|
|
38
33
|
});
|
|
39
34
|
|
|
40
|
-
export { module as default };
|
|
35
|
+
export { module$1 as default };
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export interface Register {
|
|
2
2
|
}
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
} ? T : unknown;
|
|
6
|
-
export type ContextInfo = ExtendedContextInfo & {
|
|
3
|
+
export type ExtendedShortcutManagerInfo = keyof Register extends `ShortcutManagerContext${infer T}` ? Register extends Record<`ShortcutManagerContext${T}`, infer U> ? U : {} : {};
|
|
4
|
+
export type ContextInfo = ExtendedShortcutManagerInfo & {
|
|
7
5
|
count: Record<string, number>;
|
|
8
6
|
isActive: Record<string, boolean>;
|
|
9
7
|
};
|
package/package.json
CHANGED
|
@@ -1,156 +1,157 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
2
|
+
"name": "@witchcraft/spellcraft",
|
|
3
|
+
"description": "A shortcut manager library for handling ALL the shortcut needs of an application.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/core/index.js",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"build": {
|
|
9
|
+
"failOnWarn": false
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/core/index.d.ts",
|
|
14
|
+
"import": "./dist/core/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./utils": {
|
|
17
|
+
"types": "./dist/utils/index.d.ts",
|
|
18
|
+
"import": "./dist/utils/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./helpers": {
|
|
21
|
+
"types": "./dist/helpers/index.d.ts",
|
|
22
|
+
"import": "./dist/helpers/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./types": {
|
|
25
|
+
"types": "./dist/types/index.d.ts",
|
|
26
|
+
"import": "./dist/types/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./core": {
|
|
29
|
+
"types": "./dist/core/index.d.ts",
|
|
30
|
+
"import": "./dist/core/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./nuxt": {
|
|
33
|
+
"types": "./dist/types.d.mts",
|
|
34
|
+
"import": "./dist/module.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./*": {
|
|
37
|
+
"types": "./dist/*.js",
|
|
38
|
+
"import": "./dist/*.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@witchcraft/expressit": "^0.4.1",
|
|
43
|
+
"@witchcraft/ui": "^0.3.20"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"@witchcraft/ui": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"@witchcraft/expressit": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@alanscodelog/utils": "^6.0.2",
|
|
55
|
+
"@witchcraft/expressit": "^0.4.1",
|
|
56
|
+
"@witchcraft/ui": "^0.3.20",
|
|
57
|
+
"reka-ui": "^2.8.0",
|
|
58
|
+
"vue": "^3.5.27"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@alanscodelog/commitlint-config": "^3.1.2",
|
|
62
|
+
"@alanscodelog/eslint-config": "^6.3.1",
|
|
63
|
+
"@alanscodelog/semantic-release-config": "^6.0.0",
|
|
64
|
+
"@alanscodelog/tsconfigs": "^6.2.0",
|
|
65
|
+
"@alanscodelog/vite-config": "^0.0.7",
|
|
66
|
+
"@commitlint/cli": "^20.4.1",
|
|
67
|
+
"@iconify/json": "^2.2.436",
|
|
68
|
+
"@nuxt/eslint-config": "^1.13.0",
|
|
69
|
+
"@nuxt/kit": "^4.3.0",
|
|
70
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
71
|
+
"@nuxt/schema": "^4.3.0",
|
|
72
|
+
"@nuxt/types": "^2.18.1",
|
|
73
|
+
"@types/node": "^25.2.1",
|
|
74
|
+
"@vitejs/plugin-vue": "^6.0.4",
|
|
75
|
+
"@vitest/coverage-c8": "^0.33.0",
|
|
76
|
+
"concurrently": "^9.2.1",
|
|
77
|
+
"cross-env": "^10.1.0",
|
|
78
|
+
"defu": "^6.1.4",
|
|
79
|
+
"fast-glob": "^3.3.3",
|
|
80
|
+
"http-server": "^14.1.1",
|
|
81
|
+
"husky": "^9.1.7",
|
|
82
|
+
"indexit": "2.1.0-beta.3",
|
|
83
|
+
"madge": "^8.0.0",
|
|
84
|
+
"nuxt": "^4.3.0",
|
|
85
|
+
"onchange": "^7.1.0",
|
|
86
|
+
"semantic-release": "^25.0.3",
|
|
87
|
+
"tailwindcss": "^4.1.18",
|
|
88
|
+
"typedoc": "0.28.16",
|
|
89
|
+
"typescript": "^5.9.3",
|
|
90
|
+
"unbuild": "^3.6.1",
|
|
91
|
+
"unplugin-icons": "^23.0.1",
|
|
92
|
+
"vite-tsconfig-paths": "^6.0.5",
|
|
93
|
+
"vitest": "^4.0.18",
|
|
94
|
+
"vue-tsc": "^3.2.4"
|
|
95
|
+
},
|
|
96
|
+
"author": "Alan <alanscodelog@gmail.com>",
|
|
97
|
+
"repository": "https://github.com/witchcraftjs/spellcraft",
|
|
98
|
+
"license": "MIT",
|
|
99
|
+
"files": [
|
|
100
|
+
"src",
|
|
101
|
+
"dist"
|
|
102
|
+
],
|
|
103
|
+
"release": {
|
|
104
|
+
"extends": [
|
|
105
|
+
"@alanscodelog/semantic-release-config"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"commitlint": {
|
|
109
|
+
"extends": [
|
|
110
|
+
"@alanscodelog"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"babel": {
|
|
114
|
+
"presets": [
|
|
115
|
+
"@alanscodelog"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
"madge": {
|
|
119
|
+
"detectiveOptions": {
|
|
120
|
+
"ts": {
|
|
121
|
+
"skipTypeImports": true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"browserslist": "> 0.5%, last 2 versions, not dead, not < 0.25%, not IE > 0, maintained node versions",
|
|
126
|
+
"engines": {
|
|
127
|
+
"node": ">=20.0.0"
|
|
128
|
+
},
|
|
129
|
+
"publishConfig": {
|
|
130
|
+
"access": "public",
|
|
131
|
+
"provenance": true
|
|
132
|
+
},
|
|
133
|
+
"scripts": {
|
|
134
|
+
"build": "nuxt-module-build prepare && nuxt-module-build build && nuxi generate playground",
|
|
135
|
+
"build:only": "nuxt-module-build build",
|
|
136
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
137
|
+
"dev:playground": "nuxi dev playground",
|
|
138
|
+
"dev": "nuxi dev playground",
|
|
139
|
+
"test": "pnpm lint:types && vitest run --exclude '.direnv/**/*'",
|
|
140
|
+
"test:watch": "vitest --watch --exclude '.direnv/**/*'",
|
|
141
|
+
"test:inspect-errors": "cross-env INSPECT_ERRORS=true npm run test",
|
|
142
|
+
"coverage": "vitest --exclude '.direnv/**/*' --coverage",
|
|
143
|
+
"coverage:dev": "vitest --exclude '.direnv/**/*' --watch --coverage",
|
|
144
|
+
"doc": "typedoc",
|
|
145
|
+
"doc:watch": "onchange -i \"src/**/*.ts\" \"typedoc.config.js\" -- pnpm doc",
|
|
146
|
+
"doc:serve": "http-server docs --port=5001",
|
|
147
|
+
"doc:dev": "concurrently \"pnpm doc:watch\" \"pnpm doc:serve\"",
|
|
148
|
+
"doc:check-invalid": "typedoc --listInvalidSymbolLinks",
|
|
149
|
+
"lint:eslint": "eslint \"{src,test,playground/app}/**/*.{js,ts,vue,cjs}\" \"*.{js,ts}\" --max-warnings=0 --report-unused-disable-directives",
|
|
150
|
+
"lint:types": "vue-tsc --noEmit && echo 'todo cd playground && vue-tsc --noEmit'",
|
|
151
|
+
"lint:commits": "commitlint --from-last-tag --to HEAD --verbose",
|
|
152
|
+
"lint:imports": "madge --circular --extensions ts ./src",
|
|
153
|
+
"lint": "pnpm lint:types && pnpm lint:eslint && pnpm lint:commits && pnpm lint:imports",
|
|
154
|
+
"actions:debug": "act -r -v",
|
|
155
|
+
"gen:exports": "indexit update -o '${path}.js'"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given an list of keys, returns an array of deduped key codes.
|
|
3
|
+
*
|
|
4
|
+
* Useful for when the codes must be passed to a third-party library (for example, a dragging library that takes a list modifiers).
|
|
5
|
+
*
|
|
6
|
+
* This properly takes a look at the key and all it's variants.
|
|
7
|
+
*
|
|
8
|
+
* `skipIdIfHasVariants` is true by default and will skip adding the key's id if it has variants as it's assumed the id is not a valid name.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Key } from "../types/keys.js"
|
|
12
|
+
|
|
13
|
+
export function getKeyCodesFromKeys(keys: Key[], { skipIdIfHasVariants = true}: { skipIdIfHasVariants?: boolean } = {}): string[] {
|
|
14
|
+
const res = new Set<string>()
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
if (key.variants) {
|
|
17
|
+
if (!skipIdIfHasVariants) {
|
|
18
|
+
res.add(key.id)
|
|
19
|
+
}
|
|
20
|
+
for (const variant of key.variants) {
|
|
21
|
+
res.add(variant)
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
res.add(key.id)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return Array.from(res)
|
|
28
|
+
}
|
package/src/module.ts
CHANGED
|
@@ -5,8 +5,6 @@ import {
|
|
|
5
5
|
defineNuxtModule
|
|
6
6
|
} from "@nuxt/kit"
|
|
7
7
|
|
|
8
|
-
import pkg from "../package.json" with { type: "json" }
|
|
9
|
-
|
|
10
8
|
|
|
11
9
|
const { resolve } = createResolver(import.meta.url)
|
|
12
10
|
|
|
@@ -30,7 +28,7 @@ export default defineNuxtModule<ModuleOptions>({
|
|
|
30
28
|
},
|
|
31
29
|
moduleDependencies: {
|
|
32
30
|
"@witchcraft/ui/nuxt": {
|
|
33
|
-
version:
|
|
31
|
+
version: "^0.3.6"
|
|
34
32
|
}
|
|
35
33
|
},
|
|
36
34
|
async setup(_options, nuxt) {
|
package/src/runtime/types.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
// allows users to register multiple contexts
|
|
2
|
+
// @eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
3
|
+
export interface Register {}
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
export type ExtendedShortcutManagerInfo = keyof Register extends `ShortcutManagerContext${infer T}`
|
|
6
|
+
? Register extends Record<`ShortcutManagerContext${T}`, infer U>
|
|
7
|
+
? U
|
|
8
|
+
// we can further constrain the type users can register if needed
|
|
9
|
+
// ? U extends { }
|
|
10
|
+
// ? U
|
|
11
|
+
// : {}
|
|
12
|
+
// : {}
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
14
|
+
: {}
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
16
|
+
: {}
|
|
5
17
|
|
|
6
|
-
|
|
18
|
+
|
|
19
|
+
export type ContextInfo = ExtendedShortcutManagerInfo & {
|
|
7
20
|
count: Record<string, number>
|
|
8
21
|
isActive: Record<string, boolean>
|
|
9
22
|
}
|