hotkey-router 0.2.2 → 0.3.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/ADDITIONAL_TERMS.md +122 -122
- package/LICENSE +694 -694
- package/README.md +103 -3
- package/data/browser-hotkeys.json +349 -349
- package/data/site-hotkeys.json +4571 -0
- package/data/site-hotkeys.runtime.json +1 -0
- package/dist/auto.js +57 -8
- package/dist/auto.js.map +2 -2
- package/dist/auto.min.js +2 -2
- package/dist/hotkey-router.cjs +60 -9
- package/dist/hotkey-router.cjs.map +2 -2
- package/dist/hotkey-router.js +60 -9
- package/dist/hotkey-router.js.map +2 -2
- package/dist/hotkey-router.min.js +2 -2
- package/dist/reservations.js +1 -1
- package/dist/reservations.js.map +1 -1
- package/dist/sites.js +185 -0
- package/dist/sites.js.map +7 -0
- package/dist/types/auto.d.ts +2 -0
- package/dist/types/hotkey-router.d.ts +196 -0
- package/dist/types/reservations.d.ts +80 -0
- package/dist/types/sites.d.ts +109 -0
- package/dist/types/validate.d.ts +42 -0
- package/dist/validate.js +106 -0
- package/dist/validate.js.map +7 -0
- package/package.json +18 -7
- package/robots.txt +79 -79
package/dist/validate.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// hotkey-router v0.3.0 | SEE LICENSE IN LICENSE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// validate.js
|
|
5
|
+
import { lookupReservation, severityIsHard } from "./reservations.js";
|
|
6
|
+
import { parseComboString, listSites } from "./sites.js";
|
|
7
|
+
var ALL_PLATFORMS = ["mac", "windows", "linux"];
|
|
8
|
+
var ALL_BROWSERS = ["chrome", "firefox", "edge", "safari"];
|
|
9
|
+
var PLATFORM_LABEL = { mac: "macOS", windows: "Windows", linux: "Linux" };
|
|
10
|
+
var BROWSER_LABEL = { chrome: "Chrome", firefox: "Firefox", edge: "Edge", safari: "Safari" };
|
|
11
|
+
var SITE_COL = { windows: 0, mac: 1, linux: 0 };
|
|
12
|
+
function validateCombo(combo, {
|
|
13
|
+
platforms = ALL_PLATFORMS,
|
|
14
|
+
browsers = ALL_BROWSERS,
|
|
15
|
+
requirePrimary = false,
|
|
16
|
+
minModifiers = 1,
|
|
17
|
+
taken = [],
|
|
18
|
+
sites = true
|
|
19
|
+
} = {}) {
|
|
20
|
+
const reasons = [];
|
|
21
|
+
const lookupKeys = {};
|
|
22
|
+
const add = (reason) => {
|
|
23
|
+
const same = reasons.find((r) => r.code === reason.code && r.message === reason.message);
|
|
24
|
+
if (same) {
|
|
25
|
+
for (const p of reason.platforms || []) if (!same.platforms?.includes(p)) same.platforms = [...same.platforms || [], p];
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
reasons.push(reason);
|
|
29
|
+
};
|
|
30
|
+
if (typeof combo !== "string" || !combo.trim()) {
|
|
31
|
+
return { ok: false, reasons: [{ code: "invalid", severity: "error", message: "No keys were recorded." }], lookupKeys };
|
|
32
|
+
}
|
|
33
|
+
for (const platform of platforms) {
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = parseComboString(combo, platform);
|
|
37
|
+
} catch {
|
|
38
|
+
parsed = null;
|
|
39
|
+
}
|
|
40
|
+
if (!parsed) {
|
|
41
|
+
add({ code: "invalid", severity: "error", message: "These keys could not be read.", platforms: [platform] });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!parsed.base) {
|
|
45
|
+
add({ code: "no-key", severity: "error", message: "Add a key to the modifiers, for example a letter or punctuation key.", platforms: [platform] });
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const mods = ["ctrl", "meta", "alt", "shift"].filter((m) => parsed[m]);
|
|
49
|
+
const key = [...mods, parsed.base].join("+");
|
|
50
|
+
lookupKeys[platform] = key;
|
|
51
|
+
if (mods.length === 0) {
|
|
52
|
+
add({ code: "no-modifier", severity: "error", message: "Use at least one modifier (Ctrl, Alt, Shift or Cmd), or the key would fire while typing.", platforms: [platform] });
|
|
53
|
+
}
|
|
54
|
+
if (platform !== "mac" && mods.length === 1 && parsed.alt) {
|
|
55
|
+
add({ code: "alt-only", severity: "error", message: "Alt on its own moves focus to the browser menu when released. Add Ctrl or Shift.", platforms: [platform] });
|
|
56
|
+
}
|
|
57
|
+
const primary = platform === "mac" ? parsed.meta : parsed.ctrl;
|
|
58
|
+
if (requirePrimary && !primary) {
|
|
59
|
+
add({ code: "needs-primary", severity: "error", message: `Include ${platform === "mac" ? "Cmd" : "Ctrl"}.`, platforms: [platform] });
|
|
60
|
+
}
|
|
61
|
+
if (mods.length > 0 && mods.length < minModifiers) {
|
|
62
|
+
add({ code: "too-few-modifiers", severity: "error", message: `Use at least ${minModifiers} modifiers together.`, platforms: [platform] });
|
|
63
|
+
}
|
|
64
|
+
for (const other of taken) {
|
|
65
|
+
let otherKey = null;
|
|
66
|
+
try {
|
|
67
|
+
const o = parseComboString(other, platform);
|
|
68
|
+
if (o?.base) otherKey = [...["ctrl", "meta", "alt", "shift"].filter((m) => o[m]), o.base].join("+");
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
if (otherKey === key) {
|
|
72
|
+
add({ code: "taken", severity: "error", message: "These keys are already used for something else here.", platforms: [platform], details: { combo: other } });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const shape = { ctrl: parsed.ctrl, meta: parsed.meta, alt: parsed.alt, shift: parsed.shift, key: parsed.base, code: null, bareModifier: null };
|
|
76
|
+
for (const browser of browsers) {
|
|
77
|
+
if (platform !== "mac" && browser === "safari") continue;
|
|
78
|
+
const hit = lookupReservation(shape, { platform, browser });
|
|
79
|
+
if (!hit) continue;
|
|
80
|
+
const where = hit.source === "system" ? PLATFORM_LABEL[platform] : `${BROWSER_LABEL[browser]} on ${PLATFORM_LABEL[platform]}`;
|
|
81
|
+
if (severityIsHard(hit.severity)) {
|
|
82
|
+
add({ code: "reserved", severity: "error", message: `${where} uses these keys (${hit.action}), so a page never sees them.`, platforms: [platform], details: { browser, action: hit.action } });
|
|
83
|
+
} else {
|
|
84
|
+
add({ code: "reserved-soft", severity: "warning", message: `${where} uses these keys in some situations (${hit.action}).`, platforms: [platform], details: { browser, action: hit.action, severity: hit.severity } });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (platform === "windows" && parsed.ctrl && parsed.alt && !parsed.meta) {
|
|
88
|
+
add({ code: "altgr", severity: "warning", message: "On some keyboard layouts Ctrl+Alt acts as AltGr and types characters; those keystrokes are skipped.", platforms: [platform] });
|
|
89
|
+
}
|
|
90
|
+
if (sites) {
|
|
91
|
+
const col = SITE_COL[platform];
|
|
92
|
+
const names = [];
|
|
93
|
+
for (const site of listSites()) {
|
|
94
|
+
if (site.shortcuts.some((row) => row[col] === key)) names.push(site.name);
|
|
95
|
+
}
|
|
96
|
+
if (names.length) {
|
|
97
|
+
add({ code: "site-conflict", severity: "warning", message: `Also used by ${names.join(", ")}.`, platforms: [platform], details: { sites: names } });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { ok: !reasons.some((r) => r.severity === "error"), reasons, lookupKeys };
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
validateCombo
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../validate.js"],
|
|
4
|
+
"sourcesContent": ["// validate.js\n// Check a hotkey before an app saves it, e.g. from a \"press your keys\"\n// recorder. Returns plain-language reasons the app can show as they are.\n//\n// import { comboFromEvent } from 'hotkey-router'\n// import { validateCombo } from 'hotkey-router/validate'\n//\n// const combo = comboFromEvent(e, { useMod: true }) // 'mod+shift+code:Period'\n// const { ok, reasons } = validateCombo(combo, { requirePrimary: true, minModifiers: 2 })\n//\n// Errors make `ok` false (the combo would not work, or breaks a rule the\n// app asked for). Warnings leave `ok` true (it works, with a caveat worth\n// telling the user: AltGr on some layouts, sites that use the same keys).\n//\n// Data: data/browser-hotkeys.json (keys the browser or OS take before the\n// page sees them) and data/site-hotkeys.runtime.json (keys popular sites use).\n\nimport { lookupReservation, severityIsHard } from './reservations.js'\nimport { parseComboString, listSites } from './sites.js'\n\nconst ALL_PLATFORMS = ['mac', 'windows', 'linux']\nconst ALL_BROWSERS = ['chrome', 'firefox', 'edge', 'safari']\nconst PLATFORM_LABEL = { mac: 'macOS', windows: 'Windows', linux: 'Linux' }\nconst BROWSER_LABEL = { chrome: 'Chrome', firefox: 'Firefox', edge: 'Edge', safari: 'Safari' }\n// Runtime site rows: [windows, mac, chromeos, action, verified]\nconst SITE_COL = { windows: 0, mac: 1, linux: 0 }\n\n/**\n * @typedef {{\n * code: 'invalid' | 'no-key' | 'no-modifier' | 'alt-only' | 'needs-primary' |\n * 'too-few-modifiers' | 'taken' | 'reserved' | 'reserved-soft' |\n * 'altgr' | 'site-conflict',\n * severity: 'error' | 'warning',\n * message: string,\n * platforms?: string[],\n * details?: object\n * }} ComboReason\n */\n\n/**\n * @param {string} combo hotkey string, e.g. `mod+shift+code:Period`\n * @param {{\n * platforms?: Array<'mac' | 'windows' | 'linux'>,\n * browsers?: Array<'chrome' | 'firefox' | 'edge' | 'safari'>,\n * requirePrimary?: boolean, // must include Ctrl (Cmd on macOS)\n * minModifiers?: number, // default 1\n * taken?: string[], // combos the app already uses\n * sites?: boolean, // warn about sites that use the keys (default true)\n * }} [opts]\n * @returns {{ ok: boolean, reasons: ComboReason[], lookupKeys: Record<string, string> }}\n */\nexport function validateCombo(combo, {\n platforms = ALL_PLATFORMS,\n browsers = ALL_BROWSERS,\n requirePrimary = false,\n minModifiers = 1,\n taken = [],\n sites = true,\n} = {}) {\n /** @type {ComboReason[]} */\n const reasons = []\n const lookupKeys = {}\n const add = (reason) => {\n const same = reasons.find((r) => r.code === reason.code && r.message === reason.message)\n if (same) {\n for (const p of reason.platforms || []) if (!same.platforms?.includes(p)) same.platforms = [...(same.platforms || []), p]\n return\n }\n reasons.push(reason)\n }\n\n if (typeof combo !== 'string' || !combo.trim()) {\n return { ok: false, reasons: [{ code: 'invalid', severity: 'error', message: 'No keys were recorded.' }], lookupKeys }\n }\n\n for (const platform of platforms) {\n let parsed\n try {\n parsed = parseComboString(combo, platform)\n } catch {\n parsed = null\n }\n if (!parsed) {\n add({ code: 'invalid', severity: 'error', message: 'These keys could not be read.', platforms: [platform] })\n continue\n }\n if (!parsed.base) {\n add({ code: 'no-key', severity: 'error', message: 'Add a key to the modifiers, for example a letter or punctuation key.', platforms: [platform] })\n continue\n }\n const mods = ['ctrl', 'meta', 'alt', 'shift'].filter((m) => parsed[m])\n const key = [...mods, parsed.base].join('+')\n lookupKeys[platform] = key\n\n if (mods.length === 0) {\n add({ code: 'no-modifier', severity: 'error', message: 'Use at least one modifier (Ctrl, Alt, Shift or Cmd), or the key would fire while typing.', platforms: [platform] })\n }\n if (platform !== 'mac' && mods.length === 1 && parsed.alt) {\n add({ code: 'alt-only', severity: 'error', message: 'Alt on its own moves focus to the browser menu when released. Add Ctrl or Shift.', platforms: [platform] })\n }\n const primary = platform === 'mac' ? parsed.meta : parsed.ctrl\n if (requirePrimary && !primary) {\n add({ code: 'needs-primary', severity: 'error', message: `Include ${platform === 'mac' ? 'Cmd' : 'Ctrl'}.`, platforms: [platform] })\n }\n if (mods.length > 0 && mods.length < minModifiers) {\n add({ code: 'too-few-modifiers', severity: 'error', message: `Use at least ${minModifiers} modifiers together.`, platforms: [platform] })\n }\n\n for (const other of taken) {\n let otherKey = null\n try {\n const o = parseComboString(other, platform)\n if (o?.base) otherKey = [...['ctrl', 'meta', 'alt', 'shift'].filter((m) => o[m]), o.base].join('+')\n } catch { /* ignore unreadable entries */ }\n if (otherKey === key) {\n add({ code: 'taken', severity: 'error', message: 'These keys are already used for something else here.', platforms: [platform], details: { combo: other } })\n }\n }\n\n const shape = { ctrl: parsed.ctrl, meta: parsed.meta, alt: parsed.alt, shift: parsed.shift, key: parsed.base, code: null, bareModifier: null }\n for (const browser of browsers) {\n if (platform !== 'mac' && browser === 'safari') continue\n const hit = lookupReservation(shape, { platform, browser })\n if (!hit) continue\n const where = hit.source === 'system' ? PLATFORM_LABEL[platform] : `${BROWSER_LABEL[browser]} on ${PLATFORM_LABEL[platform]}`\n if (severityIsHard(hit.severity)) {\n add({ code: 'reserved', severity: 'error', message: `${where} uses these keys (${hit.action}), so a page never sees them.`, platforms: [platform], details: { browser, action: hit.action } })\n } else {\n add({ code: 'reserved-soft', severity: 'warning', message: `${where} uses these keys in some situations (${hit.action}).`, platforms: [platform], details: { browser, action: hit.action, severity: hit.severity } })\n }\n }\n\n if (platform === 'windows' && parsed.ctrl && parsed.alt && !parsed.meta) {\n add({ code: 'altgr', severity: 'warning', message: 'On some keyboard layouts Ctrl+Alt acts as AltGr and types characters; those keystrokes are skipped.', platforms: [platform] })\n }\n\n if (sites) {\n const col = SITE_COL[platform]\n const names = []\n for (const site of listSites()) {\n if (site.shortcuts.some((row) => row[col] === key)) names.push(site.name)\n }\n if (names.length) {\n add({ code: 'site-conflict', severity: 'warning', message: `Also used by ${names.join(', ')}.`, platforms: [platform], details: { sites: names } })\n }\n }\n }\n\n return { ok: !reasons.some((r) => r.severity === 'error'), reasons, lookupKeys }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hotkey-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A tiny, deterministic keyboard routing engine for modern web apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "WATT3D <WATT3D@protonmail.com>",
|
|
@@ -29,7 +29,17 @@
|
|
|
29
29
|
"types": "./dist/types/reservations.d.ts",
|
|
30
30
|
"import": "./dist/reservations.js"
|
|
31
31
|
},
|
|
32
|
-
"./
|
|
32
|
+
"./sites": {
|
|
33
|
+
"types": "./dist/types/sites.d.ts",
|
|
34
|
+
"import": "./dist/sites.js"
|
|
35
|
+
},
|
|
36
|
+
"./validate": {
|
|
37
|
+
"types": "./dist/types/validate.d.ts",
|
|
38
|
+
"import": "./dist/validate.js"
|
|
39
|
+
},
|
|
40
|
+
"./data/browser-hotkeys.json": "./data/browser-hotkeys.json",
|
|
41
|
+
"./data/site-hotkeys.json": "./data/site-hotkeys.json",
|
|
42
|
+
"./data/site-hotkeys.runtime.json": "./data/site-hotkeys.runtime.json"
|
|
33
43
|
},
|
|
34
44
|
"sideEffects": false,
|
|
35
45
|
"files": [
|
|
@@ -43,21 +53,22 @@
|
|
|
43
53
|
"scripts": {
|
|
44
54
|
"build": "node build.js",
|
|
45
55
|
"build:types": "tsc -p tsconfig.types.json",
|
|
46
|
-
"prepare": "node build.js",
|
|
47
|
-
"prepublishOnly": "
|
|
56
|
+
"prepare": "node build.js && npm run build:types",
|
|
57
|
+
"prepublishOnly": "npm test",
|
|
48
58
|
"test": "vitest run --environment jsdom",
|
|
49
59
|
"test:watch": "vitest --environment jsdom",
|
|
50
60
|
"scrape:diff": "node scripts/scrape-reservations.mjs",
|
|
51
|
-
"scrape:apply": "node scripts/scrape-reservations.mjs --apply"
|
|
61
|
+
"scrape:apply": "node scripts/scrape-reservations.mjs --apply",
|
|
62
|
+
"sites:build": "node scripts/build-site-hotkeys.cjs"
|
|
52
63
|
},
|
|
53
64
|
"devDependencies": {
|
|
54
|
-
"esbuild": "^0.
|
|
65
|
+
"esbuild": "^0.28.1",
|
|
55
66
|
"jsdom": "^24.0.0",
|
|
56
67
|
"typescript": "^5.9.3",
|
|
57
68
|
"vitest": "^3.1.2"
|
|
58
69
|
},
|
|
59
70
|
"engines": {
|
|
60
|
-
"node": ">=
|
|
71
|
+
"node": ">=18"
|
|
61
72
|
},
|
|
62
73
|
"repository": {
|
|
63
74
|
"type": "git",
|
package/robots.txt
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
# WATT3D — AI scraping policy
|
|
2
|
-
#
|
|
3
|
-
# This file enumerates known AI / LLM training and inference crawlers
|
|
4
|
-
# that the WATT3D AI Training Rider does not consent to. See
|
|
5
|
-
# ./ADDITIONAL_TERMS.md for the legal scope and definitions.
|
|
6
|
-
#
|
|
7
|
-
# Honoring robots.txt is voluntary. The rider applies whether or not
|
|
8
|
-
# a bot respects this file. The enumeration is non-exhaustive — bots
|
|
9
|
-
# not listed here are still bound by the rider if they perform
|
|
10
|
-
# Training Use as defined in ADDITIONAL_TERMS.md §1.
|
|
11
|
-
|
|
12
|
-
User-agent: GPTBot
|
|
13
|
-
Disallow: /
|
|
14
|
-
|
|
15
|
-
User-agent: ChatGPT-User
|
|
16
|
-
Disallow: /
|
|
17
|
-
|
|
18
|
-
User-agent: OAI-SearchBot
|
|
19
|
-
Disallow: /
|
|
20
|
-
|
|
21
|
-
User-agent: anthropic-ai
|
|
22
|
-
Disallow: /
|
|
23
|
-
|
|
24
|
-
User-agent: ClaudeBot
|
|
25
|
-
Disallow: /
|
|
26
|
-
|
|
27
|
-
User-agent: Claude-Web
|
|
28
|
-
Disallow: /
|
|
29
|
-
|
|
30
|
-
User-agent: Claude-SearchBot
|
|
31
|
-
Disallow: /
|
|
32
|
-
|
|
33
|
-
User-agent: Google-Extended
|
|
34
|
-
Disallow: /
|
|
35
|
-
|
|
36
|
-
User-agent: Applebot-Extended
|
|
37
|
-
Disallow: /
|
|
38
|
-
|
|
39
|
-
User-agent: Meta-ExternalAgent
|
|
40
|
-
Disallow: /
|
|
41
|
-
|
|
42
|
-
User-agent: Meta-ExternalFetcher
|
|
43
|
-
Disallow: /
|
|
44
|
-
|
|
45
|
-
User-agent: FacebookBot
|
|
46
|
-
Disallow: /
|
|
47
|
-
|
|
48
|
-
User-agent: CCBot
|
|
49
|
-
Disallow: /
|
|
50
|
-
|
|
51
|
-
User-agent: PerplexityBot
|
|
52
|
-
Disallow: /
|
|
53
|
-
|
|
54
|
-
User-agent: Perplexity-User
|
|
55
|
-
Disallow: /
|
|
56
|
-
|
|
57
|
-
User-agent: cohere-ai
|
|
58
|
-
Disallow: /
|
|
59
|
-
|
|
60
|
-
User-agent: Bytespider
|
|
61
|
-
Disallow: /
|
|
62
|
-
|
|
63
|
-
User-agent: Amazonbot
|
|
64
|
-
Disallow: /
|
|
65
|
-
|
|
66
|
-
User-agent: Omgilibot
|
|
67
|
-
Disallow: /
|
|
68
|
-
|
|
69
|
-
User-agent: AI2Bot
|
|
70
|
-
Disallow: /
|
|
71
|
-
|
|
72
|
-
User-agent: Diffbot
|
|
73
|
-
Disallow: /
|
|
74
|
-
|
|
75
|
-
# Everyone else: full access. Search engines and human-driven crawlers
|
|
76
|
-
# remain welcome.
|
|
77
|
-
|
|
78
|
-
User-agent: *
|
|
79
|
-
Allow: /
|
|
1
|
+
# WATT3D — AI scraping policy
|
|
2
|
+
#
|
|
3
|
+
# This file enumerates known AI / LLM training and inference crawlers
|
|
4
|
+
# that the WATT3D AI Training Rider does not consent to. See
|
|
5
|
+
# ./ADDITIONAL_TERMS.md for the legal scope and definitions.
|
|
6
|
+
#
|
|
7
|
+
# Honoring robots.txt is voluntary. The rider applies whether or not
|
|
8
|
+
# a bot respects this file. The enumeration is non-exhaustive — bots
|
|
9
|
+
# not listed here are still bound by the rider if they perform
|
|
10
|
+
# Training Use as defined in ADDITIONAL_TERMS.md §1.
|
|
11
|
+
|
|
12
|
+
User-agent: GPTBot
|
|
13
|
+
Disallow: /
|
|
14
|
+
|
|
15
|
+
User-agent: ChatGPT-User
|
|
16
|
+
Disallow: /
|
|
17
|
+
|
|
18
|
+
User-agent: OAI-SearchBot
|
|
19
|
+
Disallow: /
|
|
20
|
+
|
|
21
|
+
User-agent: anthropic-ai
|
|
22
|
+
Disallow: /
|
|
23
|
+
|
|
24
|
+
User-agent: ClaudeBot
|
|
25
|
+
Disallow: /
|
|
26
|
+
|
|
27
|
+
User-agent: Claude-Web
|
|
28
|
+
Disallow: /
|
|
29
|
+
|
|
30
|
+
User-agent: Claude-SearchBot
|
|
31
|
+
Disallow: /
|
|
32
|
+
|
|
33
|
+
User-agent: Google-Extended
|
|
34
|
+
Disallow: /
|
|
35
|
+
|
|
36
|
+
User-agent: Applebot-Extended
|
|
37
|
+
Disallow: /
|
|
38
|
+
|
|
39
|
+
User-agent: Meta-ExternalAgent
|
|
40
|
+
Disallow: /
|
|
41
|
+
|
|
42
|
+
User-agent: Meta-ExternalFetcher
|
|
43
|
+
Disallow: /
|
|
44
|
+
|
|
45
|
+
User-agent: FacebookBot
|
|
46
|
+
Disallow: /
|
|
47
|
+
|
|
48
|
+
User-agent: CCBot
|
|
49
|
+
Disallow: /
|
|
50
|
+
|
|
51
|
+
User-agent: PerplexityBot
|
|
52
|
+
Disallow: /
|
|
53
|
+
|
|
54
|
+
User-agent: Perplexity-User
|
|
55
|
+
Disallow: /
|
|
56
|
+
|
|
57
|
+
User-agent: cohere-ai
|
|
58
|
+
Disallow: /
|
|
59
|
+
|
|
60
|
+
User-agent: Bytespider
|
|
61
|
+
Disallow: /
|
|
62
|
+
|
|
63
|
+
User-agent: Amazonbot
|
|
64
|
+
Disallow: /
|
|
65
|
+
|
|
66
|
+
User-agent: Omgilibot
|
|
67
|
+
Disallow: /
|
|
68
|
+
|
|
69
|
+
User-agent: AI2Bot
|
|
70
|
+
Disallow: /
|
|
71
|
+
|
|
72
|
+
User-agent: Diffbot
|
|
73
|
+
Disallow: /
|
|
74
|
+
|
|
75
|
+
# Everyone else: full access. Search engines and human-driven crawlers
|
|
76
|
+
# remain welcome.
|
|
77
|
+
|
|
78
|
+
User-agent: *
|
|
79
|
+
Allow: /
|