@carlesandres/house 0.4.5 → 0.4.6

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/src/tips.ts ADDED
@@ -0,0 +1,93 @@
1
+ import type { BrowserCtx } from "./keymap/browser.ts"
2
+ import type { KeyBinding } from "./keymap/keymap.ts"
3
+ import { displayKey } from "./keymap/displayKey.ts"
4
+
5
+ export interface TipLine {
6
+ readonly id: string
7
+ readonly text: string
8
+ }
9
+
10
+ interface TipDefinition {
11
+ readonly id: string
12
+ readonly bindingId?: string
13
+ readonly render: (parts: { readonly key: string | null }) => string
14
+ readonly when?: (ctx: BrowserCtx) => boolean
15
+ }
16
+
17
+ const tipDefinitions: readonly TipDefinition[] = [
18
+ {
19
+ id: "filter.start",
20
+ bindingId: "filter.open",
21
+ render: ({ key }) => `Press ${key ?? "/"} to start filtering files by path.`,
22
+ when: (ctx) => ctx.filterQuery.length === 0 && !ctx.filterOpen,
23
+ },
24
+ {
25
+ id: "filter.resume",
26
+ bindingId: "filter.open",
27
+ render: ({ key }) => `Press ${key ?? "/"} to reopen the current filter and keep refining it.`,
28
+ when: (ctx) => ctx.filterQuery.length > 0 && !ctx.filterOpen,
29
+ },
30
+ {
31
+ id: "filter.commit",
32
+ bindingId: "filter.open",
33
+ render: () => "Press Enter in the filter to open the selected match in the reader.",
34
+ when: (ctx) => ctx.filterQuery.length === 0,
35
+ },
36
+ {
37
+ id: "filter.clear",
38
+ bindingId: "filter.clearOrOpen",
39
+ render: ({ key }) => `Press ${key ?? "ctrl+\\"} to clear the current filter and start over.`,
40
+ when: (ctx) => ctx.filterQuery.length > 0,
41
+ },
42
+ {
43
+ id: "filtered-navigation",
44
+ render: () => "Use [ and ] to move through files while the current filter stays applied.",
45
+ when: (ctx) => ctx.filterQuery.length > 0,
46
+ },
47
+ {
48
+ id: "focus.toggle",
49
+ bindingId: "focus.toggle",
50
+ render: ({ key }) => `Press ${key ?? "tab"} to switch between the sidebar and reader.`,
51
+ },
52
+ {
53
+ id: "sidebar.toggle",
54
+ bindingId: "sidebar.toggle",
55
+ render: ({ key }) =>
56
+ `Press ${key ?? "s"} to hide or show the sidebar without losing your place.`,
57
+ },
58
+ {
59
+ id: "help.open",
60
+ bindingId: "help.toggle",
61
+ render: ({ key }) => `Press ${key ?? "?"} to open the full keyboard help at any time.`,
62
+ },
63
+ ]
64
+
65
+ const firstKeyByBindingId = <C>(bindings: readonly KeyBinding<C>[]): ReadonlyMap<string, string> =>
66
+ new Map(
67
+ bindings.flatMap((binding) => {
68
+ const firstKey = binding.keys[0]
69
+ return firstKey ? [[binding.id, displayKey(firstKey)] as const] : []
70
+ }),
71
+ )
72
+
73
+ export const buildReaderEmptyStateTips = (
74
+ bindings: readonly KeyBinding<BrowserCtx>[],
75
+ ctx: BrowserCtx,
76
+ ): readonly TipLine[] => {
77
+ const keyByBindingId = firstKeyByBindingId(bindings)
78
+
79
+ return tipDefinitions
80
+ .filter((tip) => !tip.when || tip.when(ctx))
81
+ .map((tip) => ({
82
+ id: tip.id,
83
+ text: `Tip: ${tip.render({ key: tip.bindingId ? (keyByBindingId.get(tip.bindingId) ?? null) : null })}`,
84
+ }))
85
+ }
86
+
87
+ export const pickTipByRotation = (
88
+ tips: readonly TipLine[],
89
+ rotationIndex: number,
90
+ ): TipLine | null => {
91
+ if (tips.length === 0) return null
92
+ return tips[((rotationIndex % tips.length) + tips.length) % tips.length] ?? null
93
+ }