@kehto/shell 0.16.6 → 0.17.2
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 +5 -8
- package/dist/index.d.ts +61 -464
- package/dist/index.js +224 -63
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -961,36 +961,6 @@ var audioManager = {
|
|
|
961
961
|
}
|
|
962
962
|
};
|
|
963
963
|
|
|
964
|
-
// src/keys-forwarder.ts
|
|
965
|
-
function createKeysForwarder(deps) {
|
|
966
|
-
const target = deps.target ?? (typeof window !== "undefined" ? window : new EventTarget());
|
|
967
|
-
const listener = (ev) => {
|
|
968
|
-
const ke = ev;
|
|
969
|
-
const entries = deps.sessionRegistry.getAllEntries();
|
|
970
|
-
for (const entry of entries) {
|
|
971
|
-
if (!deps.hasKeysForwardCap(entry.pubkey)) continue;
|
|
972
|
-
const iframe = deps.originRegistry.getIframeWindow(entry.windowId);
|
|
973
|
-
if (!iframe) continue;
|
|
974
|
-
const envelope = {
|
|
975
|
-
type: "keys.forward",
|
|
976
|
-
key: ke.key ?? "",
|
|
977
|
-
code: ke.code ?? "",
|
|
978
|
-
ctrl: ke.ctrlKey ?? false,
|
|
979
|
-
alt: ke.altKey ?? false,
|
|
980
|
-
shift: ke.shiftKey ?? false,
|
|
981
|
-
meta: ke.metaKey ?? false
|
|
982
|
-
};
|
|
983
|
-
iframe.postMessage(envelope, "*");
|
|
984
|
-
}
|
|
985
|
-
};
|
|
986
|
-
target.addEventListener("keydown", listener);
|
|
987
|
-
return {
|
|
988
|
-
destroy() {
|
|
989
|
-
target.removeEventListener("keydown", listener);
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
}
|
|
993
|
-
|
|
994
964
|
// src/shell-init.ts
|
|
995
965
|
var NAP_DOMAINS = [
|
|
996
966
|
"identity",
|
|
@@ -1169,23 +1139,6 @@ function createShellBridge(hooks) {
|
|
|
1169
1139
|
win.postMessage(envelope, "*");
|
|
1170
1140
|
}
|
|
1171
1141
|
}
|
|
1172
|
-
let keysForwarder = null;
|
|
1173
|
-
if (typeof window !== "undefined") {
|
|
1174
|
-
try {
|
|
1175
|
-
keysForwarder = createKeysForwarder({
|
|
1176
|
-
originRegistry,
|
|
1177
|
-
sessionRegistry,
|
|
1178
|
-
hasKeysForwardCap: (pubkey) => {
|
|
1179
|
-
const entry = sessionRegistry.getEntry(pubkey);
|
|
1180
|
-
if (!entry) return false;
|
|
1181
|
-
const acl = aclStore.getEntry(entry.pubkey, entry.dTag, entry.aggregateHash);
|
|
1182
|
-
return acl?.capabilities.includes("keys:forward") ?? false;
|
|
1183
|
-
}
|
|
1184
|
-
});
|
|
1185
|
-
} catch {
|
|
1186
|
-
keysForwarder = null;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
1142
|
return {
|
|
1190
1143
|
handleMessage(event) {
|
|
1191
1144
|
const sourceWindow = event.source;
|
|
@@ -1217,7 +1170,6 @@ function createShellBridge(hooks) {
|
|
|
1217
1170
|
runtime.injectEvent(topic, payload);
|
|
1218
1171
|
},
|
|
1219
1172
|
destroy() {
|
|
1220
|
-
keysForwarder?.destroy();
|
|
1221
1173
|
runtime.destroy();
|
|
1222
1174
|
},
|
|
1223
1175
|
registerConsentHandler(handler) {
|
|
@@ -1255,14 +1207,14 @@ function scriptJson(value) {
|
|
|
1255
1207
|
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1256
1208
|
}
|
|
1257
1209
|
function renderNappletNamespacePrelude(options) {
|
|
1258
|
-
const domains = uniqueBareDomains(options.domains);
|
|
1210
|
+
const domains = uniqueBareDomains(["shell", ...options.domains]);
|
|
1259
1211
|
return `<script data-kehto-nip5d-injection>(${nappletNamespacePrelude.toString()})(${scriptJson(domains)});</script>`;
|
|
1260
1212
|
}
|
|
1261
1213
|
function injectNappletNamespacePrelude(html, options) {
|
|
1262
1214
|
const prelude = renderNappletNamespacePrelude(options);
|
|
1263
1215
|
const cspMeta = /(<meta\s+http-equiv=["']Content-Security-Policy["'][^>]*>)/i;
|
|
1264
1216
|
if (cspMeta.test(html)) {
|
|
1265
|
-
return html.replace(cspMeta, `$
|
|
1217
|
+
return html.replace(cspMeta, (match) => `${match}${prelude}`);
|
|
1266
1218
|
}
|
|
1267
1219
|
if (/<head[^>]*>/i.test(html)) {
|
|
1268
1220
|
return html.replace(/<head[^>]*>/i, (open) => `${open}${prelude}`);
|
|
@@ -1349,6 +1301,67 @@ function nappletNamespacePrelude(domains) {
|
|
|
1349
1301
|
}
|
|
1350
1302
|
};
|
|
1351
1303
|
}
|
|
1304
|
+
function makeShell() {
|
|
1305
|
+
const emptyServices = Object.freeze([]);
|
|
1306
|
+
const readyHandlers = /* @__PURE__ */ new Set();
|
|
1307
|
+
let environment;
|
|
1308
|
+
let resolveReady = () => void 0;
|
|
1309
|
+
const ready = new Promise((resolve) => {
|
|
1310
|
+
resolveReady = resolve;
|
|
1311
|
+
});
|
|
1312
|
+
const off = listen((event) => {
|
|
1313
|
+
if (!isParentMessage(event) || environment) return;
|
|
1314
|
+
const incoming = event.data;
|
|
1315
|
+
if (typeof incoming !== "object" || incoming === null) return;
|
|
1316
|
+
const message = incoming;
|
|
1317
|
+
if (message.type !== "shell.init") return;
|
|
1318
|
+
const services = Object.freeze(
|
|
1319
|
+
Array.isArray(message.services) ? message.services.filter((service) => typeof service === "string") : []
|
|
1320
|
+
);
|
|
1321
|
+
environment = Object.freeze({
|
|
1322
|
+
capabilities: message.capabilities ?? {},
|
|
1323
|
+
services
|
|
1324
|
+
});
|
|
1325
|
+
off();
|
|
1326
|
+
resolveReady(environment);
|
|
1327
|
+
for (const handler of readyHandlers) handler(environment);
|
|
1328
|
+
readyHandlers.clear();
|
|
1329
|
+
});
|
|
1330
|
+
function supports(domain, protocol) {
|
|
1331
|
+
if (!environment || typeof environment.capabilities !== "object" || environment.capabilities === null) {
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1334
|
+
const capabilities = environment.capabilities;
|
|
1335
|
+
if (protocol !== void 0) {
|
|
1336
|
+
const protocols = capabilities.protocols;
|
|
1337
|
+
if (typeof protocols === "object" && protocols !== null) {
|
|
1338
|
+
const supported = protocols[domain];
|
|
1339
|
+
if (Array.isArray(supported) && supported.includes(protocol)) return true;
|
|
1340
|
+
}
|
|
1341
|
+
return Array.isArray(capabilities.naps) && capabilities.naps.includes(`${domain}:${protocol}`);
|
|
1342
|
+
}
|
|
1343
|
+
return Array.isArray(capabilities.domains) && capabilities.domains.includes(domain) || Array.isArray(capabilities.naps) && capabilities.naps.includes(domain);
|
|
1344
|
+
}
|
|
1345
|
+
const shell2 = {
|
|
1346
|
+
ready: () => ready,
|
|
1347
|
+
supports,
|
|
1348
|
+
get services() {
|
|
1349
|
+
return environment?.services ?? emptyServices;
|
|
1350
|
+
},
|
|
1351
|
+
set services(_services) {
|
|
1352
|
+
},
|
|
1353
|
+
onReady(handler) {
|
|
1354
|
+
if (environment) {
|
|
1355
|
+
handler(environment);
|
|
1356
|
+
return subscriptionHandle(() => void 0);
|
|
1357
|
+
}
|
|
1358
|
+
readyHandlers.add(handler);
|
|
1359
|
+
return subscriptionHandle(() => readyHandlers.delete(handler));
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
post({ type: "shell.ready" });
|
|
1363
|
+
return shell2;
|
|
1364
|
+
}
|
|
1352
1365
|
function domainEventHandle(eventType, callback, select = (message) => message.event) {
|
|
1353
1366
|
const off = listen((event) => {
|
|
1354
1367
|
if (!isParentMessage(event)) return;
|
|
@@ -1524,30 +1537,177 @@ function nappletNamespacePrelude(domains) {
|
|
|
1524
1537
|
}
|
|
1525
1538
|
};
|
|
1526
1539
|
}
|
|
1540
|
+
const keyActionCallbacks = /* @__PURE__ */ new Map();
|
|
1541
|
+
const keyBindingActions = /* @__PURE__ */ new Map();
|
|
1542
|
+
const modifierKeys = /* @__PURE__ */ new Set(["Control", "Alt", "Shift", "Meta"]);
|
|
1543
|
+
const reservedKeyNames = /* @__PURE__ */ new Set(["Tab", "Escape"]);
|
|
1544
|
+
const nonTextInputTypes = /* @__PURE__ */ new Set([
|
|
1545
|
+
"button",
|
|
1546
|
+
"checkbox",
|
|
1547
|
+
"color",
|
|
1548
|
+
"file",
|
|
1549
|
+
"hidden",
|
|
1550
|
+
"image",
|
|
1551
|
+
"radio",
|
|
1552
|
+
"range",
|
|
1553
|
+
"reset",
|
|
1554
|
+
"submit"
|
|
1555
|
+
]);
|
|
1556
|
+
let keysSmartForwardingInstalled = false;
|
|
1557
|
+
function normalizeKeyName(key) {
|
|
1558
|
+
if (typeof key !== "string") return "";
|
|
1559
|
+
return key.length === 1 ? key.toUpperCase() : key;
|
|
1560
|
+
}
|
|
1561
|
+
function keyComboKey(value) {
|
|
1562
|
+
return `${value.ctrl}|${value.alt}|${value.shift}|${value.meta}|${normalizeKeyName(value.key)}`;
|
|
1563
|
+
}
|
|
1564
|
+
function bindingComboKey(binding) {
|
|
1565
|
+
if (typeof binding !== "string" || binding.trim() === "") return null;
|
|
1566
|
+
const parts = binding.split("+");
|
|
1567
|
+
const key = parts.pop()?.trim();
|
|
1568
|
+
if (!key) return null;
|
|
1569
|
+
const combo = { ctrl: false, alt: false, shift: false, meta: false, key };
|
|
1570
|
+
for (const raw of parts) {
|
|
1571
|
+
const token = raw.trim().toLowerCase();
|
|
1572
|
+
if (!token) continue;
|
|
1573
|
+
if (token === "ctrl" || token === "control") combo.ctrl = true;
|
|
1574
|
+
else if (token === "alt" || token === "option") combo.alt = true;
|
|
1575
|
+
else if (token === "shift") combo.shift = true;
|
|
1576
|
+
else if (token === "meta" || token === "cmd" || token === "command" || token === "win" || token === "super") combo.meta = true;
|
|
1577
|
+
else return null;
|
|
1578
|
+
}
|
|
1579
|
+
return keyComboKey(combo);
|
|
1580
|
+
}
|
|
1581
|
+
function eventComboKey(event) {
|
|
1582
|
+
const key = typeof event.key === "string" ? event.key : "";
|
|
1583
|
+
if (!key) return null;
|
|
1584
|
+
return keyComboKey({
|
|
1585
|
+
ctrl: Boolean(event.ctrlKey),
|
|
1586
|
+
alt: Boolean(event.altKey),
|
|
1587
|
+
shift: Boolean(event.shiftKey),
|
|
1588
|
+
meta: Boolean(event.metaKey),
|
|
1589
|
+
key
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
function isTextEntryTarget(value) {
|
|
1593
|
+
if (!value || typeof value !== "object") return false;
|
|
1594
|
+
const targetNode = value;
|
|
1595
|
+
if (targetNode.isContentEditable === true) return true;
|
|
1596
|
+
const tagName = typeof targetNode.tagName === "string" ? targetNode.tagName.toLowerCase() : "";
|
|
1597
|
+
if (tagName === "textarea" || tagName === "select") return true;
|
|
1598
|
+
if (tagName === "input") {
|
|
1599
|
+
const type = typeof targetNode.type === "string" && targetNode.type ? targetNode.type.toLowerCase() : "text";
|
|
1600
|
+
return !nonTextInputTypes.has(type);
|
|
1601
|
+
}
|
|
1602
|
+
if (typeof targetNode.closest === "function") {
|
|
1603
|
+
try {
|
|
1604
|
+
return Boolean(targetNode.closest('[contenteditable=""],[contenteditable="true"],[contenteditable="plaintext-only"]'));
|
|
1605
|
+
} catch {
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
function rememberKeyBinding(actionId, binding) {
|
|
1612
|
+
if (typeof actionId !== "string") return;
|
|
1613
|
+
const combo = bindingComboKey(binding);
|
|
1614
|
+
if (combo) keyBindingActions.set(combo, actionId);
|
|
1615
|
+
}
|
|
1616
|
+
function removeKeyBindingsForAction(actionId) {
|
|
1617
|
+
for (const [combo, boundActionId] of keyBindingActions) {
|
|
1618
|
+
if (boundActionId === actionId) keyBindingActions.delete(combo);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
function replaceKeyBindings(bindings) {
|
|
1622
|
+
keyBindingActions.clear();
|
|
1623
|
+
if (!Array.isArray(bindings)) return;
|
|
1624
|
+
for (const binding of bindings) {
|
|
1625
|
+
if (!binding || typeof binding !== "object") continue;
|
|
1626
|
+
const entry = binding;
|
|
1627
|
+
rememberKeyBinding(entry.actionId, entry.key);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
function notifyKeyAction(actionId) {
|
|
1631
|
+
for (const callback of keyActionCallbacks.get(actionId) ?? []) callback();
|
|
1632
|
+
}
|
|
1633
|
+
function forwardKeyEvent(event = {}) {
|
|
1634
|
+
fire({
|
|
1635
|
+
type: "keys.forward",
|
|
1636
|
+
key: typeof event.key === "string" ? event.key : "",
|
|
1637
|
+
code: typeof event.code === "string" ? event.code : "",
|
|
1638
|
+
ctrl: Boolean(event.ctrl ?? event.ctrlKey),
|
|
1639
|
+
alt: Boolean(event.alt ?? event.altKey),
|
|
1640
|
+
shift: Boolean(event.shift ?? event.shiftKey),
|
|
1641
|
+
meta: Boolean(event.meta ?? event.metaKey)
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
function installKeysSmartForwarding() {
|
|
1645
|
+
if (keysSmartForwardingInstalled) return;
|
|
1646
|
+
keysSmartForwardingInstalled = true;
|
|
1647
|
+
listen((event) => {
|
|
1648
|
+
if (!isParentMessage(event)) return;
|
|
1649
|
+
const msg = event.data;
|
|
1650
|
+
if (typeof msg === "object" && msg !== null && msg.type === "keys.bindings") {
|
|
1651
|
+
replaceKeyBindings(msg.bindings);
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
const documentLike = target.document;
|
|
1655
|
+
const listenerTarget = typeof documentLike?.addEventListener === "function" ? documentLike : target;
|
|
1656
|
+
if (typeof listenerTarget.addEventListener !== "function") return;
|
|
1657
|
+
listenerTarget.addEventListener("keydown", (event) => {
|
|
1658
|
+
const keyEvent = event;
|
|
1659
|
+
if (keyEvent.isComposing) return;
|
|
1660
|
+
if (typeof keyEvent.key === "string" && modifierKeys.has(keyEvent.key)) return;
|
|
1661
|
+
if (isTextEntryTarget(keyEvent.target ?? documentLike?.activeElement)) return;
|
|
1662
|
+
const combo = eventComboKey(keyEvent);
|
|
1663
|
+
const actionId = combo ? keyBindingActions.get(combo) : void 0;
|
|
1664
|
+
const isReserved = typeof keyEvent.key === "string" && reservedKeyNames.has(keyEvent.key);
|
|
1665
|
+
if (actionId && !isReserved) {
|
|
1666
|
+
keyEvent.preventDefault?.();
|
|
1667
|
+
notifyKeyAction(actionId);
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
forwardKeyEvent(keyEvent);
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1527
1673
|
function makeKeys() {
|
|
1674
|
+
installKeysSmartForwarding();
|
|
1528
1675
|
return {
|
|
1529
|
-
forward:
|
|
1530
|
-
type: "keys.forward",
|
|
1531
|
-
key: typeof event.key === "string" ? event.key : "",
|
|
1532
|
-
code: typeof event.code === "string" ? event.code : "",
|
|
1533
|
-
ctrl: Boolean(event.ctrl ?? event.ctrlKey),
|
|
1534
|
-
alt: Boolean(event.alt ?? event.altKey),
|
|
1535
|
-
shift: Boolean(event.shift ?? event.shiftKey),
|
|
1536
|
-
meta: Boolean(event.meta ?? event.metaKey)
|
|
1537
|
-
}),
|
|
1676
|
+
forward: forwardKeyEvent,
|
|
1538
1677
|
registerAction: (action) => request(
|
|
1539
1678
|
{ type: "keys.registerAction", action },
|
|
1540
1679
|
"keys.registerAction.result",
|
|
1541
1680
|
resultOrError
|
|
1542
|
-
)
|
|
1543
|
-
|
|
1681
|
+
).then((result) => {
|
|
1682
|
+
const actionRecord = action && typeof action === "object" ? action : {};
|
|
1683
|
+
const resultRecord = result;
|
|
1684
|
+
rememberKeyBinding(
|
|
1685
|
+
typeof resultRecord.actionId === "string" ? resultRecord.actionId : actionRecord.id,
|
|
1686
|
+
resultRecord.binding
|
|
1687
|
+
);
|
|
1688
|
+
return result;
|
|
1689
|
+
}),
|
|
1690
|
+
unregisterAction: (actionId) => {
|
|
1691
|
+
removeKeyBindingsForAction(actionId);
|
|
1692
|
+
fire({ type: "keys.unregisterAction", actionId });
|
|
1693
|
+
},
|
|
1544
1694
|
onAction(actionId, callback) {
|
|
1695
|
+
let callbacks = keyActionCallbacks.get(actionId);
|
|
1696
|
+
if (!callbacks) {
|
|
1697
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
1698
|
+
keyActionCallbacks.set(actionId, callbacks);
|
|
1699
|
+
}
|
|
1700
|
+
callbacks.add(callback);
|
|
1545
1701
|
const off = listen((event) => {
|
|
1546
1702
|
if (!isParentMessage(event)) return;
|
|
1547
1703
|
const msg = event.data;
|
|
1548
1704
|
if (typeof msg === "object" && msg !== null && msg.type === "keys.action" && msg.actionId === actionId) callback();
|
|
1549
1705
|
});
|
|
1550
|
-
return subscriptionHandle(
|
|
1706
|
+
return subscriptionHandle(() => {
|
|
1707
|
+
off();
|
|
1708
|
+
callbacks.delete(callback);
|
|
1709
|
+
if (callbacks.size === 0) keyActionCallbacks.delete(actionId);
|
|
1710
|
+
});
|
|
1551
1711
|
}
|
|
1552
1712
|
};
|
|
1553
1713
|
}
|
|
@@ -1962,7 +2122,9 @@ function nappletNamespacePrelude(domains) {
|
|
|
1962
2122
|
}
|
|
1963
2123
|
};
|
|
1964
2124
|
}
|
|
2125
|
+
const shell = makeShell();
|
|
1965
2126
|
function makeDomain(domain, existing) {
|
|
2127
|
+
if (domain === "shell") return shell;
|
|
1966
2128
|
if (existing && typeof existing === "object" && Object.keys(existing).length > 0) return existing;
|
|
1967
2129
|
switch (domain) {
|
|
1968
2130
|
case "relay":
|
|
@@ -2138,7 +2300,6 @@ export {
|
|
|
2138
2300
|
buildShellCapabilities,
|
|
2139
2301
|
createEnforceGate,
|
|
2140
2302
|
createIdentityProxy,
|
|
2141
|
-
createKeysForwarder,
|
|
2142
2303
|
createKeysProxy,
|
|
2143
2304
|
createMediaProxy,
|
|
2144
2305
|
createNapEnforceGate,
|