@arthony/keybook 0.4.1 → 0.6.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/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn, spawnSync } from "node:child_process";
3
- import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { basename, dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { Command } from "commander";
@@ -868,6 +868,52 @@ function copyToClipboard(text) {
868
868
  }
869
869
  }
870
870
  //#endregion
871
+ //#region src/data/favorites.ts
872
+ const SEP = "\0";
873
+ function favKey(app, action) {
874
+ return `${app}${SEP}${action}`;
875
+ }
876
+ function favoritesPath(dir) {
877
+ return join(dir, "favorites.json");
878
+ }
879
+ /** Read favorites.json -> Set of favKeys. Missing/malformed -> empty set (never throws). */
880
+ function loadFavorites(dir) {
881
+ const set = /* @__PURE__ */ new Set();
882
+ try {
883
+ const data = JSON.parse(readFileSync(favoritesPath(dir), "utf8"));
884
+ const list = Array.isArray(data?.favorites) ? data.favorites : [];
885
+ for (const f of list) if (f && typeof f.app === "string" && typeof f.action === "string") set.add(favKey(f.app, f.action));
886
+ } catch {}
887
+ return set;
888
+ }
889
+ /** Atomic write (temp + rename) of the favKey set back to favorites.json. */
890
+ function saveFavorites(dir, keys) {
891
+ const data = {
892
+ version: 1,
893
+ favorites: [...keys].map((k) => {
894
+ const i = k.indexOf(SEP);
895
+ return {
896
+ app: k.slice(0, i),
897
+ action: k.slice(i + 1)
898
+ };
899
+ })
900
+ };
901
+ const path = favoritesPath(dir);
902
+ const tmp = `${path}.tmp`;
903
+ writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
904
+ `, "utf8");
905
+ renameSync(tmp, path);
906
+ }
907
+ /** Toggle one favorite, persist, and return the new set. */
908
+ function toggleFavorite(dir, app, action) {
909
+ const keys = loadFavorites(dir);
910
+ const k = favKey(app, action);
911
+ if (keys.has(k)) keys.delete(k);
912
+ else keys.add(k);
913
+ saveFavorites(dir, keys);
914
+ return keys;
915
+ }
916
+ //#endregion
871
917
  //#region src/search.ts
872
918
  function haystack(e) {
873
919
  return [
@@ -888,8 +934,109 @@ function search(entries, query) {
888
934
  }).find(q).map((r) => r.item);
889
935
  }
890
936
  //#endregion
937
+ //#region src/tui/FilterPicker.tsx
938
+ function FilterPicker({ apps, onSelect, onCancel, height = 16, width = 40 }) {
939
+ const [query, setQuery] = useState("");
940
+ const [selected, setSelected] = useState(0);
941
+ const matchedApps = useMemo(() => {
942
+ const q = query.trim().toLowerCase();
943
+ return q ? apps.filter((a) => a.toLowerCase().includes(q)) : apps;
944
+ }, [apps, query]);
945
+ const total = 2 + matchedApps.length;
946
+ const sel = Math.min(selected, Math.max(0, total - 1));
947
+ useInput((input, key) => {
948
+ if (key.escape || key.ctrl && input === "f") return onCancel();
949
+ if (key.downArrow || key.ctrl && input === "n") {
950
+ setSelected((s) => Math.min(s + 1, total - 1));
951
+ return;
952
+ }
953
+ if (key.upArrow || key.ctrl && input === "p") {
954
+ setSelected((s) => Math.max(s - 1, 0));
955
+ return;
956
+ }
957
+ if (key.return) {
958
+ if (sel === 0) return onSelect({ type: "favorites" });
959
+ if (sel === 1) return onSelect({ type: "all" });
960
+ const app = matchedApps[sel - 2];
961
+ if (app) return onSelect({
962
+ type: "app",
963
+ app
964
+ });
965
+ return;
966
+ }
967
+ if (key.backspace || key.delete) {
968
+ setQuery((q) => q.slice(0, -1));
969
+ setSelected(0);
970
+ return;
971
+ }
972
+ if (input && !key.ctrl && !key.meta) {
973
+ setQuery((q) => q + input);
974
+ setSelected(2);
975
+ }
976
+ });
977
+ const bodyHeight = Math.max(1, height - 2);
978
+ const appSel = Math.max(0, sel - 2);
979
+ const startApp = matchedApps.length > bodyHeight ? Math.max(0, Math.min(appSel - Math.floor(bodyHeight / 2), matchedApps.length - bodyHeight)) : 0;
980
+ const visibleApps = matchedApps.slice(startApp, startApp + bodyHeight);
981
+ const row = (text, idx) => {
982
+ const isSel = idx === sel;
983
+ return /* @__PURE__ */ jsxs(Text, {
984
+ wrap: "truncate-end",
985
+ color: isSel ? "cyan" : void 0,
986
+ inverse: isSel,
987
+ children: [isSel ? "▸ " : " ", text]
988
+ }, `${idx}:${text}`);
989
+ };
990
+ return /* @__PURE__ */ jsxs(Box, {
991
+ flexDirection: "column",
992
+ borderStyle: "round",
993
+ paddingX: 1,
994
+ width,
995
+ children: [
996
+ /* @__PURE__ */ jsx(Text, {
997
+ wrap: "truncate-end",
998
+ color: "cyan",
999
+ children: "Filter by app"
1000
+ }),
1001
+ /* @__PURE__ */ jsxs(Text, {
1002
+ wrap: "truncate-end",
1003
+ children: [query ? query : /* @__PURE__ */ jsx(Text, {
1004
+ color: "gray",
1005
+ children: "type to filter…"
1006
+ }), /* @__PURE__ */ jsx(Text, {
1007
+ inverse: true,
1008
+ children: " "
1009
+ })]
1010
+ }),
1011
+ row("★ Favorites", 0),
1012
+ row("All apps", 1),
1013
+ /* @__PURE__ */ jsx(Text, {
1014
+ wrap: "truncate-end",
1015
+ color: "gray",
1016
+ children: "──────────────"
1017
+ }),
1018
+ startApp > 0 ? /* @__PURE__ */ jsxs(Text, {
1019
+ wrap: "truncate-end",
1020
+ color: "gray",
1021
+ children: [" ", "↑ more"]
1022
+ }) : null,
1023
+ visibleApps.map((app, i) => row(app, startApp + i + 2)),
1024
+ startApp + bodyHeight < matchedApps.length ? /* @__PURE__ */ jsxs(Text, {
1025
+ wrap: "truncate-end",
1026
+ color: "gray",
1027
+ children: [" ", "↓ more"]
1028
+ }) : null,
1029
+ /* @__PURE__ */ jsx(Text, {
1030
+ wrap: "truncate-end",
1031
+ color: "gray",
1032
+ children: "↑↓ move ⏎ select ⎋ cancel"
1033
+ })
1034
+ ]
1035
+ });
1036
+ }
1037
+ //#endregion
891
1038
  //#region src/tui/Footer.tsx
892
- function Footer({ flash, errorCount, resultCount, confirm }) {
1039
+ function Footer({ flash, errorCount, resultCount, confirm, filterActive = false }) {
893
1040
  if (confirm) return /* @__PURE__ */ jsxs(Box, {
894
1041
  marginTop: 1,
895
1042
  justifyContent: "space-between",
@@ -906,19 +1053,17 @@ function Footer({ flash, errorCount, resultCount, confirm }) {
906
1053
  return /* @__PURE__ */ jsxs(Box, {
907
1054
  marginTop: 1,
908
1055
  justifyContent: "space-between",
909
- children: [/* @__PURE__ */ jsxs(Text, {
1056
+ children: [/* @__PURE__ */ jsx(Text, {
910
1057
  color: "gray",
911
1058
  wrap: "truncate-end",
912
- children: [
913
- "↑↓ move ⏎ copy ⌃O add ⌃E edit ⌃X del ⎋ quit ⌃U clear (",
914
- resultCount,
915
- ")"
916
- ]
1059
+ children: `↑↓ move ⏎ copy ⌃O add ⌃E edit ⌃X del ⌃F filter ⌃S star ${filterActive ? "⎋ clear filter" : "⎋ quit"} ⌃U clear (${resultCount})`
917
1060
  }), flash ? /* @__PURE__ */ jsx(Text, {
918
1061
  color: "green",
1062
+ wrap: "truncate-end",
919
1063
  children: flash
920
1064
  }) : errorCount > 0 ? /* @__PURE__ */ jsxs(Text, {
921
1065
  color: "yellow",
1066
+ wrap: "truncate-end",
922
1067
  children: [
923
1068
  "⚠ ",
924
1069
  errorCount,
@@ -973,7 +1118,7 @@ function PreviewPane({ entry, width = "50%" }) {
973
1118
  }
974
1119
  //#endregion
975
1120
  //#region src/tui/ResultRow.tsx
976
- function ResultRow({ entry, selected }) {
1121
+ function ResultRow({ entry, selected, favorite = false }) {
977
1122
  const right = entry.keys ?? "recipe";
978
1123
  return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, {
979
1124
  wrap: "truncate-end",
@@ -982,6 +1127,7 @@ function ResultRow({ entry, selected }) {
982
1127
  inverse: selected,
983
1128
  children: [
984
1129
  selected ? "▸ " : " ",
1130
+ favorite ? "★ " : " ",
985
1131
  entry.app,
986
1132
  " · ",
987
1133
  entry.action
@@ -994,17 +1140,13 @@ function ResultRow({ entry, selected }) {
994
1140
  }
995
1141
  //#endregion
996
1142
  //#region src/tui/ResultList.tsx
997
- function ResultList({ results, selected, query, height = 12, width = "50%" }) {
1143
+ function ResultList({ results, selected, query, height = 12, width = "50%", favorites, emptyMessage }) {
998
1144
  if (results.length === 0) return /* @__PURE__ */ jsx(Box, {
999
1145
  width,
1000
1146
  justifyContent: "center",
1001
- children: /* @__PURE__ */ jsxs(Text, {
1147
+ children: /* @__PURE__ */ jsx(Text, {
1002
1148
  color: "gray",
1003
- children: [
1004
- "No matches for \"",
1005
- query,
1006
- "\""
1007
- ]
1149
+ children: emptyMessage ?? `No matches for "${query}"`
1008
1150
  })
1009
1151
  });
1010
1152
  const start = Math.max(0, Math.min(selected - Math.floor(height / 2), results.length - height));
@@ -1013,27 +1155,35 @@ function ResultList({ results, selected, query, height = 12, width = "50%" }) {
1013
1155
  width,
1014
1156
  children: results.slice(Math.max(0, start), Math.max(0, start) + height).map((e, i) => /* @__PURE__ */ jsx(ResultRow, {
1015
1157
  entry: e,
1016
- selected: Math.max(0, start) + i === selected
1158
+ selected: Math.max(0, start) + i === selected,
1159
+ favorite: favorites?.has(favKey(e.app, e.action)) ?? false
1017
1160
  }, `${e.app}:${e.action}`))
1018
1161
  });
1019
1162
  }
1020
1163
  //#endregion
1021
1164
  //#region src/tui/SearchInput.tsx
1022
- function SearchInput({ query }) {
1023
- return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, {
1024
- wrap: "truncate-end",
1025
- children: [
1026
- /* @__PURE__ */ jsx(Text, {
1027
- color: "cyan",
1028
- children: "search: "
1029
- }),
1030
- /* @__PURE__ */ jsx(Text, { children: query }),
1031
- /* @__PURE__ */ jsx(Text, {
1032
- inverse: true,
1033
- children: " "
1034
- })
1035
- ]
1036
- }) });
1165
+ function SearchInput({ query, filterLabel }) {
1166
+ return /* @__PURE__ */ jsxs(Box, {
1167
+ justifyContent: "space-between",
1168
+ children: [/* @__PURE__ */ jsxs(Text, {
1169
+ wrap: "truncate-end",
1170
+ children: [
1171
+ /* @__PURE__ */ jsx(Text, {
1172
+ color: "cyan",
1173
+ children: "search: "
1174
+ }),
1175
+ /* @__PURE__ */ jsx(Text, { children: query }),
1176
+ /* @__PURE__ */ jsx(Text, {
1177
+ inverse: true,
1178
+ children: " "
1179
+ })
1180
+ ]
1181
+ }), filterLabel ? /* @__PURE__ */ jsx(Text, {
1182
+ color: "gray",
1183
+ wrap: "truncate-end",
1184
+ children: filterLabel
1185
+ }) : null]
1186
+ });
1037
1187
  }
1038
1188
  //#endregion
1039
1189
  //#region src/tui/input.ts
@@ -1078,6 +1228,8 @@ function App({ entries: initial, errorCount = 0, dataDir, onCopy = copyToClipboa
1078
1228
  const { stdout } = useStdout();
1079
1229
  const [entries, setEntries] = useState(initial);
1080
1230
  const [mode, setMode] = useState("search");
1231
+ const [filter, setFilter] = useState({ type: "all" });
1232
+ const [favorites, setFavorites] = useState(() => dataDir ? loadFavorites(dataDir) : /* @__PURE__ */ new Set());
1081
1233
  const [editTarget, setEditTarget] = useState(null);
1082
1234
  const [pendingDelete, setPendingDelete] = useState(null);
1083
1235
  const [query, setQuery] = useState("");
@@ -1086,7 +1238,16 @@ function App({ entries: initial, errorCount = 0, dataDir, onCopy = copyToClipboa
1086
1238
  const reload = useCallback(() => {
1087
1239
  if (dataDir) setEntries(loadEntries(dataDir).entries);
1088
1240
  }, [dataDir]);
1089
- const results = useMemo(() => search(entries, query), [entries, query]);
1241
+ const scoped = useMemo(() => {
1242
+ if (filter.type === "app") return entries.filter((e) => e.app === filter.app);
1243
+ if (filter.type === "favorites") return entries.filter((e) => favorites.has(favKey(e.app, e.action)));
1244
+ return entries;
1245
+ }, [
1246
+ entries,
1247
+ filter,
1248
+ favorites
1249
+ ]);
1250
+ const results = useMemo(() => search(scoped, query), [scoped, query]);
1090
1251
  const sel = results.length ? Math.min(selected, results.length - 1) : 0;
1091
1252
  const current = results[sel];
1092
1253
  const listHeight = visibleListHeight(stdout?.rows);
@@ -1110,6 +1271,11 @@ function App({ entries: initial, errorCount = 0, dataDir, onCopy = copyToClipboa
1110
1271
  }
1111
1272
  if (!key.return && flash) setFlash("");
1112
1273
  if (key.escape) {
1274
+ if (filter.type !== "all") {
1275
+ setFilter({ type: "all" });
1276
+ setSelected(0);
1277
+ return;
1278
+ }
1113
1279
  exit();
1114
1280
  return;
1115
1281
  }
@@ -1126,6 +1292,16 @@ function App({ entries: initial, errorCount = 0, dataDir, onCopy = copyToClipboa
1126
1292
  setPendingDelete(current);
1127
1293
  return;
1128
1294
  }
1295
+ if (key.ctrl && input === "f") {
1296
+ setMode("filter");
1297
+ return;
1298
+ }
1299
+ if (dataDir && current && key.ctrl && input === "s") {
1300
+ const next = toggleFavorite(dataDir, current.app, current.action);
1301
+ setFavorites(next);
1302
+ setFlash(next.has(favKey(current.app, current.action)) ? "★ starred" : "☆ unstarred");
1303
+ return;
1304
+ }
1129
1305
  if (key.downArrow || key.ctrl && input === "n") {
1130
1306
  setSelected((s) => Math.min(s + 1, results.length - 1));
1131
1307
  return;
@@ -1159,6 +1335,17 @@ function App({ entries: initial, errorCount = 0, dataDir, onCopy = copyToClipboa
1159
1335
  }
1160
1336
  }, { isActive: mode === "search" });
1161
1337
  const existingTags = [...new Set(entries.flatMap((e) => e.tags ?? []))].sort();
1338
+ if (mode === "filter") return /* @__PURE__ */ jsx(FilterPicker, {
1339
+ apps: [...new Set(entries.map((e) => e.app))].sort(),
1340
+ height: listHeight,
1341
+ width: left,
1342
+ onSelect: (f) => {
1343
+ setFilter(f);
1344
+ setSelected(0);
1345
+ setMode("search");
1346
+ },
1347
+ onCancel: () => setMode("search")
1348
+ });
1162
1349
  if (mode === "add" && dataDir) return /* @__PURE__ */ jsx(AddEntryForm, {
1163
1350
  apps: listApps(dataDir),
1164
1351
  existingTags,
@@ -1202,13 +1389,18 @@ function App({ entries: initial, errorCount = 0, dataDir, onCopy = copyToClipboa
1202
1389
  return /* @__PURE__ */ jsxs(Box, {
1203
1390
  flexDirection: "column",
1204
1391
  children: [
1205
- /* @__PURE__ */ jsx(SearchInput, { query }),
1392
+ /* @__PURE__ */ jsx(SearchInput, {
1393
+ query,
1394
+ filterLabel: filter.type === "app" ? `(filter: ${filter.app})` : filter.type === "favorites" ? "(★ Favorites)" : void 0
1395
+ }),
1206
1396
  /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(ResultList, {
1207
1397
  results,
1208
1398
  selected: sel,
1209
1399
  query,
1210
1400
  height: listHeight,
1211
- width: left
1401
+ width: left,
1402
+ favorites,
1403
+ emptyMessage: filter.type === "favorites" ? "No favorites yet — ⌃S stars an entry." : void 0
1212
1404
  }), /* @__PURE__ */ jsx(PreviewPane, {
1213
1405
  entry: current,
1214
1406
  width: right
@@ -1217,6 +1409,7 @@ function App({ entries: initial, errorCount = 0, dataDir, onCopy = copyToClipboa
1217
1409
  flash,
1218
1410
  errorCount,
1219
1411
  resultCount: results.length,
1412
+ filterActive: filter.type !== "all",
1220
1413
  confirm: pendingDelete ? `Delete '${pendingDelete.app}: ${pendingDelete.action}'?` : void 0
1221
1414
  })
1222
1415
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arthony/keybook",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -0,0 +1,48 @@
1
+ app: Google Chrome
2
+ entries:
3
+ - action: Open a new tab
4
+ keys: "⌘T"
5
+ tags: [new tab]
6
+ - action: Reopen the last closed tab
7
+ keys: "⌘⇧T"
8
+ tags: [restore tab]
9
+ - action: Open a new window
10
+ keys: "⌘N"
11
+ - action: Open a new Incognito window
12
+ keys: "⌘⇧N"
13
+ tags: [incognito, private]
14
+ - action: Close the current tab
15
+ keys: "⌘W"
16
+ - action: Next / previous tab
17
+ keys: "⌃⇥, ⌃⇧⇥"
18
+ - action: Jump to tab 1–8
19
+ keys: "⌘1"
20
+ notes: ⌘1–⌘8 jump to that tab; ⌘9 jumps to the last tab.
21
+ - action: Focus the address bar
22
+ keys: "⌘L"
23
+ tags: [omnibox, url]
24
+ - action: Find on the page
25
+ keys: "⌘F"
26
+ - action: Reload / hard reload
27
+ keys: "⌘R, ⌘⇧R"
28
+ - action: Open Developer Tools
29
+ keys: "⌘⌥I"
30
+ tags: [devtools, inspect]
31
+ - action: View page source
32
+ keys: "⌘⌥U"
33
+ tags: [source]
34
+ - action: Open History
35
+ keys: "⌘Y"
36
+ - action: Open Downloads
37
+ keys: "⌘⇧J"
38
+ tags: [downloads]
39
+ - action: Bookmark this page
40
+ keys: "⌘D"
41
+ tags: [favorite]
42
+ - action: Zoom in / out / reset
43
+ keys: "⌘+, ⌘-, ⌘0"
44
+ - action: Back / forward
45
+ keys: "⌘[, ⌘]"
46
+ - action: Toggle full screen
47
+ keys: "⌃⌘F"
48
+ tags: [fullscreen]
@@ -0,0 +1,31 @@
1
+ app: Cursor
2
+ entries:
3
+ - action: Inherits the VS Code keymap
4
+ steps:
5
+ - Cursor is a VS Code fork — all standard editing/navigation shortcuts match VS Code
6
+ - See the "VS Code" entries in keybook for those
7
+ notes: This file lists only Cursor's AI-specific additions.
8
+ tags: [info, vscode]
9
+ - action: Inline edit (edit selection with AI)
10
+ keys: "⌘K"
11
+ tags: [ai, edit]
12
+ - action: Toggle the AI chat / sidepanel
13
+ keys: "⌘I, ⌘L"
14
+ notes: Both shortcuts toggle the same sidepanel. ⌘L also has a secondary role — when code is selected it adds the selection to a new chat.
15
+ tags: [ai, chat]
16
+ - action: Toggle Agent layout
17
+ keys: "⌘E"
18
+ tags: [ai, agent]
19
+ - action: Add the current selection to chat
20
+ keys: "⌘⇧L"
21
+ tags: [ai, chat, context]
22
+ - action: Accept an inline (Tab) completion
23
+ keys: "⇥"
24
+ tags: [ai, autocomplete]
25
+ - action: Accept / reject all AI-suggested changes
26
+ keys: "⌘⏎, ⌘⌫"
27
+ notes: ⌘⏎ accepts all suggested changes; ⌘⌫ rejects them.
28
+ tags: [ai, diff]
29
+ - action: Open a new AI chat
30
+ keys: "⌘N"
31
+ tags: [ai, chat]
@@ -0,0 +1,63 @@
1
+ app: JetBrains
2
+ entries:
3
+ - action: Search Everywhere
4
+ steps:
5
+ - Press ⇧ twice (double-Shift)
6
+ - Type a class, file, action, or setting name
7
+ notes: The universal search across the IDE. This is the default macOS keymap, shared by IntelliJ IDEA, WebStorm, PyCharm, etc.
8
+ tags: [search, navigate]
9
+ - action: Find Action
10
+ keys: "⌘⇧A"
11
+ tags: [action, command]
12
+ - action: Recent files
13
+ keys: "⌘E"
14
+ tags: [navigate, recent]
15
+ - action: Go to class
16
+ keys: "⌘O"
17
+ tags: [navigate, goto]
18
+ - action: Go to file
19
+ keys: "⌘⇧O"
20
+ tags: [navigate, goto]
21
+ - action: Go to symbol
22
+ keys: "⌥⌘O"
23
+ tags: [navigate, goto]
24
+ - action: Go to declaration / definition
25
+ keys: "⌘B"
26
+ tags: [navigate]
27
+ - action: Find usages
28
+ keys: "⌥F7"
29
+ tags: [search, references]
30
+ - action: Rename (refactor)
31
+ keys: "⇧F6"
32
+ tags: [refactor]
33
+ - action: Reformat code
34
+ keys: "⌥⌘L"
35
+ tags: [format]
36
+ - action: Show intention actions / quick-fix
37
+ keys: "⌥⏎"
38
+ tags: [quickfix, lightbulb]
39
+ - action: Run
40
+ keys: "⌃R"
41
+ tags: [run]
42
+ - action: Debug
43
+ keys: "⌃D"
44
+ tags: [debug]
45
+ - action: Toggle line comment
46
+ keys: "⌘/"
47
+ tags: [comment]
48
+ - action: Duplicate the current line / selection
49
+ keys: "⌘D"
50
+ tags: [edit]
51
+ - action: Delete the current line
52
+ keys: "⌘⌫"
53
+ tags: [delete line]
54
+ - action: Generate code
55
+ keys: "⌘N"
56
+ notes: Constructors, getters/setters, overrides, etc.
57
+ tags: [generate]
58
+ - action: Extend / shrink selection
59
+ keys: "⌥↑, ⌥↓"
60
+ tags: [select]
61
+ - action: Code completion
62
+ keys: "⌃␣"
63
+ tags: [autocomplete]
@@ -0,0 +1,62 @@
1
+ app: Notion
2
+ entries:
3
+ - action: Open Quick Find / search
4
+ keys: "⌘P, ⌘K"
5
+ tags: [search, find, navigate]
6
+ - action: Create a new page
7
+ keys: "⌘N"
8
+ tags: [new page]
9
+ - action: Open a new tab
10
+ keys: "⌘T"
11
+ tags: [tab]
12
+ - action: Open a new Notion window
13
+ keys: "⌘⇧N"
14
+ tags: [window]
15
+ - action: Reopen last closed tab
16
+ keys: "⌘⇧T"
17
+ tags: [tab, restore]
18
+ - action: Close the current tab
19
+ keys: "⌘W"
20
+ tags: [tab]
21
+ - action: Copy link to the current page
22
+ keys: "⌘L"
23
+ tags: [link, share]
24
+ - action: Navigate back / forward
25
+ keys: "⌘[, ⌘]"
26
+ tags: [navigate, history]
27
+ - action: Toggle dark / light mode
28
+ keys: "⌘⇧L"
29
+ tags: [appearance, theme]
30
+ - action: Bold selected text
31
+ keys: "⌘B"
32
+ tags: [format, text]
33
+ - action: Italicize selected text
34
+ keys: "⌘I"
35
+ tags: [format, text]
36
+ - action: Underline selected text
37
+ keys: "⌘U"
38
+ tags: [format, text]
39
+ - action: Strikethrough selected text
40
+ keys: "⌘⇧S"
41
+ tags: [format, text]
42
+ - action: Inline code for selected text
43
+ keys: "⌘E"
44
+ tags: [format, code]
45
+ - action: Add link to selected text
46
+ keys: "⌘K"
47
+ tags: [link]
48
+ - action: Indent / un-indent a block
49
+ keys: "⇥, ⇧⇥"
50
+ tags: [indent, nest, block]
51
+ - action: Duplicate selected blocks
52
+ keys: "⌘D"
53
+ tags: [block, duplicate]
54
+ - action: Add a comment
55
+ keys: "⌘⇧M"
56
+ tags: [comment]
57
+ - action: Insert a block (slash menu)
58
+ steps:
59
+ - Type / at the start of a line
60
+ - Pick a block type (heading, to-do, toggle, code, …)
61
+ notes: The slash menu is the main way to insert and transform blocks.
62
+ tags: [block, insert, tip]
@@ -0,0 +1,51 @@
1
+ app: Safari
2
+ entries:
3
+ - action: Open a new tab
4
+ keys: "⌘T"
5
+ tags: [new tab]
6
+ - action: Open a new window
7
+ keys: "⌘N"
8
+ - action: Open a new private window
9
+ keys: "⌘⇧N"
10
+ tags: [private, incognito]
11
+ - action: Reopen the last closed tab
12
+ keys: "⌘⇧T"
13
+ tags: [restore tab]
14
+ - action: Close the current tab
15
+ keys: "⌘W"
16
+ - action: Next / previous tab
17
+ keys: "⌃⇥, ⌃⇧⇥"
18
+ - action: Jump to tab 1–8
19
+ keys: "⌘1"
20
+ notes: ⌘1–⌘8 select the first eight tabs; ⌘9 selects the last tab.
21
+ - action: Focus the Smart Search field (address bar)
22
+ keys: "⌘L"
23
+ tags: [url, search]
24
+ - action: Find on the page
25
+ keys: "⌘F"
26
+ - action: Reload / reload from origin
27
+ keys: "⌘R, ⌥⌘R"
28
+ notes: ⌥⌘R reloads ignoring the cache (from origin).
29
+ - action: Show / hide the sidebar
30
+ keys: "⌘⇧L"
31
+ tags: [sidebar, bookmarks]
32
+ - action: Show Reader
33
+ keys: "⌘⇧R"
34
+ tags: [reader]
35
+ - action: Add a bookmark for this page
36
+ keys: "⌘D"
37
+ tags: [favorite]
38
+ - action: Back / forward
39
+ keys: "⌘[, ⌘]"
40
+ - action: Zoom in / out / reset
41
+ keys: "⌘+, ⌘-, ⌘0"
42
+ - action: Show the tab overview
43
+ keys: "⌘⇧\\"
44
+ tags: [tabs, overview]
45
+ - action: Open the Web Inspector
46
+ keys: "⌥⌘I"
47
+ notes: Requires the Develop menu — enable it in Settings → Advanced → "Show features for web developers".
48
+ tags: [devtools, inspect]
49
+ - action: Toggle full screen
50
+ keys: "⌃⌘F"
51
+ tags: [fullscreen]
package/seed/vscode.yaml CHANGED
@@ -63,3 +63,27 @@ entries:
63
63
  - action: Open Keyboard Shortcuts
64
64
  keys: "⌘K, ⌘S"
65
65
  tags: [keybindings, shortcuts]
66
+ - action: Go to Symbol in the file
67
+ keys: "⌘⇧O"
68
+ tags: [navigate, symbol]
69
+ - action: Go to Symbol in the workspace
70
+ keys: "⌘T"
71
+ tags: [navigate, symbol]
72
+ - action: Find All References
73
+ keys: "⇧F12"
74
+ tags: [references]
75
+ - action: Toggle the panel
76
+ keys: "⌘J"
77
+ tags: [panel]
78
+ - action: Toggle word wrap
79
+ keys: "⌥Z"
80
+ tags: [wrap]
81
+ - action: Show Source Control
82
+ keys: "⌃⇧G"
83
+ tags: [git, scm]
84
+ - action: Show the Problems panel
85
+ keys: "⌘⇧M"
86
+ tags: [errors, diagnostics]
87
+ - action: Reopen the last closed editor
88
+ keys: "⌘⇧T"
89
+ tags: [restore]
package/seed/zed.yaml ADDED
@@ -0,0 +1,45 @@
1
+ app: Zed
2
+ entries:
3
+ - action: Open the command palette
4
+ keys: "⌘⇧P"
5
+ tags: [commands, palette]
6
+ - action: Open a file by name (file finder)
7
+ keys: "⌘P"
8
+ tags: [open file, goto]
9
+ - action: Project-wide search
10
+ keys: "⌘⇧F"
11
+ tags: [search, find]
12
+ - action: Find in buffer / replace in buffer
13
+ keys: "⌘F, ⌘⌥F"
14
+ notes: ⌘F opens find; ⌘⌥F opens find-and-replace.
15
+ tags: [find, replace]
16
+ - action: Add a cursor above / below
17
+ keys: "⌘⌥↑, ⌘⌥↓"
18
+ tags: [multi cursor]
19
+ - action: Select the next occurrence
20
+ keys: "⌘D"
21
+ tags: [multi cursor, select]
22
+ - action: Go to definition
23
+ keys: "F12"
24
+ tags: [navigate]
25
+ - action: Rename symbol
26
+ keys: "F2"
27
+ tags: [refactor]
28
+ - action: Toggle the integrated terminal panel
29
+ keys: "⌃`"
30
+ tags: [terminal]
31
+ - action: Toggle the project panel
32
+ keys: "⌘⇧E"
33
+ tags: [explorer, files]
34
+ - action: Go to symbol in the buffer (outline)
35
+ keys: "⌘⇧O"
36
+ tags: [navigate, outline]
37
+ - action: Toggle line comment
38
+ keys: "⌘/"
39
+ tags: [comment]
40
+ - action: Format the buffer
41
+ keys: "⌘⇧I"
42
+ tags: [format]
43
+ - action: Toggle the AI agent panel
44
+ keys: "⌘?"
45
+ tags: [ai, assistant]