From 519a148a283ae291de14fb0bee0a945b66a6668d Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Tue, 7 Jul 2026 10:02:05 -0300 Subject: [PATCH] fix: Fixes forwarding of keyboard shortcut events --- dist/@types/ComponentRelay.d.ts | 16 +++----- dist/dist.js | 2 +- dist/dist.js.map | 2 +- lib/ComponentRelay.ts | 70 ++++++++++++++++----------------- 4 files changed, 42 insertions(+), 48 deletions(-) diff --git a/dist/@types/ComponentRelay.d.ts b/dist/@types/ComponentRelay.d.ts index 8974489..8ea4752 100644 --- a/dist/@types/ComponentRelay.d.ts +++ b/dist/@types/ComponentRelay.d.ts @@ -21,6 +21,12 @@ export default class ComponentRelay { deinit(): void; private registerMessageHandler; private registerKeyboardEventListeners; + /** + * Only forward keyboard events involving shortcut modifiers (Ctrl, Shift, Meta, Alt). + * Plain typing should stay in the editor and not be sent to the parent app. + */ + private shouldForwardKeyboardEvent; + private forwardKeyboardEvent; private registerMouseEventListeners; private handleMessage; private onReady; @@ -149,16 +155,6 @@ export default class ComponentRelay { * @param height The new height. */ setSize(width: string | number, height: string | number): void; - /** - * Sends the KeyDown keyboard event to the Standard Notes parent application. - * @param keyboardModifier The keyboard modifier that was pressed. - */ - private keyDownEvent; - /** - * Sends the KeyUp keyboard event to the Standard Notes parent application. - * @param keyboardModifier The keyboard modifier that was released. - */ - private keyUpEvent; /** * Sends the Click mouse event to the Standard Notes parent application. */ diff --git a/dist/dist.js b/dist/dist.js index 8973be4..05fa503 100644 --- a/dist/dist.js +++ b/dist/dist.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("ComponentRelay",[],t):"object"==typeof exports?exports.ComponentRelay=t():e.ComponentRelay=t()}(self,(()=>(()=>{"use strict";var e={d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};let n;var s;e.d(t,{default:()=>w}),function(e){e[e.Web=1]="Web",e[e.Desktop=2]="Desktop",e[e.Mobile=3]="Mobile"}(n||(n={}));var i=new Uint8Array(16);function o(){if(!s&&!(s="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return s(i)}const a=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,r=function(e){return"string"==typeof e&&a.test(e)};for(var m=[],c=0;c<256;++c)m.push((c+256).toString(16).substr(1));const h=function(e,t,n){var s=(e=e||{}).random||(e.rng||o)();if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,t){n=n||0;for(var i=0;i<16;++i)t[n+i]=s[i];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(m[e[t+0]]+m[e[t+1]]+m[e[t+2]]+m[e[t+3]]+"-"+m[e[t+4]]+m[e[t+5]]+"-"+m[e[t+6]]+m[e[t+7]]+"-"+m[e[t+8]]+m[e[t+9]]+"-"+m[e[t+10]]+m[e[t+11]]+m[e[t+12]]+m[e[t+13]]+m[e[t+14]]+m[e[t+15]]).toLowerCase();if(!r(n))throw TypeError("Stringified UUID is invalid");return n}(s)},d=e=>{var t;const s={[n.Web]:"web",[n.Desktop]:"desktop",[n.Mobile]:"mobile"};return null!==(t=s[e])&&void 0!==t?t:s[n.Web]},p=()=>{};class l{static get isSupported(){return!(!window.console&&!console)}static get info(){return l.isSupported&&this.enabled?console.log.bind(console):p}static get error(){return l.isSupported?console.error.bind(console):p}}var u,v;let g,f,y;function b(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);t&&(s=s.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,s)}return n}function S(e){for(var t=1;t{if(l.info("Components API Message received:",e.data),document.referrer&&new URL(document.referrer).origin!==new URL(e.origin).origin)return;const{data:t}=e,n=(e=>{if("string"!=typeof e)return!1;try{const t=JSON.parse(e),n=Object.prototype.toString.call(t);return"[object Object]"===n||"[object Array]"===n}catch(e){return!1}})(t)?JSON.parse(t):t;if(n){if(void 0===this.component.origin&&n.action===y.ComponentRegistered)this.component.origin=e.origin;else if(e.origin!==this.component.origin)return;this.handleMessage(n)}else l.error("Invalid data received. Skipping...")},this.contentWindow.document.addEventListener("message",this.messageHandler,!1),this.contentWindow.addEventListener("message",this.messageHandler,!1),l.info("Waiting for messages...")}registerKeyboardEventListeners(){this.keyDownEventListener=e=>{l.info("A key has been pressed: ".concat(e.key)),e.ctrlKey?this.keyDownEvent(g.Ctrl):e.shiftKey?this.keyDownEvent(g.Shift):(e.metaKey||"Meta"===e.key)&&this.keyDownEvent(g.Meta)},this.keyUpEventListener=e=>{l.info("A key has been released: ".concat(e.key)),"Control"===e.key?this.keyUpEvent(g.Ctrl):"Shift"===e.key?this.keyUpEvent(g.Shift):"Meta"===e.key&&this.keyUpEvent(g.Meta)},this.contentWindow.addEventListener("keydown",this.keyDownEventListener,!1),this.contentWindow.addEventListener("keyup",this.keyUpEventListener,!1)}registerMouseEventListeners(){this.clickEventListener=e=>{l.info("A click has been performed."),this.mouseClickEvent()},this.contentWindow.addEventListener("click",this.clickEventListener,!1)}handleMessage(e){switch(e.action){case y.ComponentRegistered:this.component.sessionKey=e.sessionKey,e.componentData&&(this.component.data=e.componentData),this.onReady(e.data),l.info("Component successfully registered with payload:",e);break;case y.ActivateThemes:this.activateThemes(e.data.themes);break;default:{var t,n;if(!e.original)return;const s=null===(t=this.sentMessages)||void 0===t?void 0:t.filter((t=>{var n;return t.messageId===(null===(n=e.original)||void 0===n?void 0:n.messageId)}))[0];if(!s){const e=this.contentWindow.document.title,t=("The extension '".concat(e,"' is attempting to communicate with Standard Notes, ")+"but an error is preventing it from doing so. Please restart this extension and try again.").replace(" "," ");return void l.info(t)}null==s||null===(n=s.callback)||void 0===n||n.call(s,e.data);break}}}onReady(e){this.component.environment=e.environment,this.component.platform=e.platform,this.component.uuid=e.uuid;for(const e of this.messageQueue)this.postMessage(e.action,e.data,e.callback);this.messageQueue=[],l.info("Data passed to onReady:",e),this.activateThemes(e.activeThemeUrls||[]),this.postMessage(y.ThemesActivated,{}),this.params.onReady&&this.params.onReady()}getSelfComponentUUID(){return this.component.uuid}isRunningInDesktopApplication(){return this.component.environment===d(n.Desktop)}isRunningInMobileApplication(){return this.component.environment===d(n.Mobile)}getComponentDataValueForKey(e){if(this.component.data)return this.component.data[e]}setComponentDataValueForKey(e,t){if(!this.component.data)throw new Error("The component has not been initialized.");if(!e||e&&0===e.length)throw new Error("The key for the data value should be a valid string.");this.component.data=S(S({},this.component.data),{},{[e]:t}),this.postMessage(y.SetComponentData,{componentData:this.component.data})}clearComponentData(){this.component.data={},this.postMessage(y.SetComponentData,{componentData:this.component.data})}postMessage(e,t,n){if(!this.component.sessionKey)return void this.messageQueue.push({action:e,data:t,api:f.Component,callback:n});e===y.SaveItems&&(t.height=this.params.handleRequestForContentHeight());const s={action:e,data:t,messageId:this.generateUUID(),sessionKey:this.component.sessionKey,api:f.Component},i=JSON.parse(JSON.stringify(s));let o;i.callback=n,this.sentMessages.push(i),o=this.isRunningInMobileApplication()?JSON.stringify(s):s,l.info("Posting message:",o);const a=this.component.origin&&"null"!==this.component.origin?this.component.origin:"*";this.contentWindow.parent.postMessage(o,a)}activateThemes(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];if(!this.component.acceptsThemes)return;l.info("Incoming themes:",e);const{activeThemes:t}=this.component;if(t&&t.sort().toString()==e.sort().toString())return;let n=e;const s=[];for(const i of t)e.includes(i)?n=n.filter((e=>e!==i)):s.push(i);l.info("Deactivating themes:",s),l.info("Activating themes:",n);for(const e of s)this.deactivateTheme(e);this.component.activeThemes=e;for(const e of n){if(!e)continue;const t=this.contentWindow.document.createElement("link");t.id=btoa(e),t.href=e,t.type="text/css",t.rel="stylesheet",t.media="screen,print",t.className="custom-theme",this.contentWindow.document.getElementsByTagName("head")[0].appendChild(t)}this.params.onThemesChange&&this.params.onThemesChange()}themeElementForUrl(e){return Array.from(this.contentWindow.document.getElementsByClassName("custom-theme")).slice().find((t=>t.id==btoa(e)))}deactivateTheme(e){const t=this.themeElementForUrl(e);t&&t.parentNode&&(t.setAttribute("disabled","true"),t.parentNode.removeChild(t))}generateUUID(){return h()}get platform(){return this.component.platform}get environment(){return this.component.environment}streamItems(e,t){this.postMessage(y.StreamItems,{content_types:e},(e=>{t(e.items)}))}streamContextItem(e){this.postMessage(y.StreamContextItem,{},(t=>{const{item:n}=t;(!this.lastStreamedItem||this.lastStreamedItem.uuid!==n.uuid)&&this.pendingSaveTimeout&&(clearTimeout(this.pendingSaveTimeout),this.performSavingOfItems(this.pendingSaveParams),this.pendingSaveTimeout=void 0,this.pendingSaveParams=void 0),this.lastStreamedItem=n,e(this.lastStreamedItem)}))}createItem(e,t){this.postMessage(y.CreateItem,{item:this.jsonObjectForItem(e)},(e=>{let{item:n}=e;!n&&e.items&&e.items.length>0&&(n=e.items[0]),t&&t(n)}))}createItems(e,t){const n=e.map((e=>this.jsonObjectForItem(e)));this.postMessage(y.CreateItems,{items:n},(e=>{t&&t(e.items)}))}deleteItem(e,t){this.deleteItems([e],t)}deleteItems(e,t){const n={items:e.map((e=>this.jsonObjectForItem(e)))};this.postMessage(y.DeleteItems,n,(e=>{t&&t(e)}))}sendCustomEvent(e,t,n){this.postMessage(e,t,(e=>{n&&n(e)}))}saveItem(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.saveItems([e],t,n)}saveItemWithPresave(e,t,n){this.saveItemsWithPresave([e],t,n)}saveItemsWithPresave(e,t,n){this.saveItems(e,n,!1,t)}performSavingOfItems(e){let{items:t,presave:n,callback:s}=e;const i=setTimeout((()=>{this.concernTimeouts.forEach((e=>clearTimeout(e))),alert("This editor is unable to communicate with Standard Notes. Your changes may not be saved. Please backup your changes, then restart the application and try again.")}),5e3);this.concernTimeouts.push(i),n&&n();const o=[];for(const e of t)o.push(this.jsonObjectForItem(e));this.postMessage(y.SaveItems,{items:o},(()=>{this.concernTimeouts.forEach((e=>clearTimeout(e))),null==s||s()}))}saveItems(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],s=arguments.length>3?arguments[3]:void 0;if(this.pendingSaveItems||(this.pendingSaveItems=[]),this.options.coallesedSaving&&!n){this.pendingSaveTimeout&&clearTimeout(this.pendingSaveTimeout);const n=e.map((e=>e.uuid)),i=this.pendingSaveItems.filter((e=>!n.includes(e.uuid)));this.pendingSaveItems=i.concat(e),this.pendingSaveParams={items:this.pendingSaveItems,presave:s,callback:t},this.pendingSaveTimeout=setTimeout((()=>{this.performSavingOfItems(this.pendingSaveParams),this.pendingSaveItems=[],this.pendingSaveTimeout=void 0,this.pendingSaveParams=null}),this.options.coallesedSavingDelay)}else this.performSavingOfItems({items:e,presave:s,callback:t})}setSize(e,t){this.postMessage(y.SetSize,{type:"container",width:e,height:t})}keyDownEvent(e){this.postMessage(y.KeyDown,{keyboardModifier:e})}keyUpEvent(e){this.postMessage(y.KeyUp,{keyboardModifier:e})}mouseClickEvent(){this.postMessage(y.Click,{})}jsonObjectForItem(e){const t=Object.assign({},e);return t.children=null,t.parent=null,t}getItemAppDataValue(e,t){var n,s;const i=null==e||null===(n=e.content)||void 0===n||null===(s=n.appData)||void 0===s?void 0:s["org.standardnotes.sn"];return null==i?void 0:i[t]}}return t.default})())); +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("ComponentRelay",[],t):"object"==typeof exports?exports.ComponentRelay=t():e.ComponentRelay=t()}(self,(()=>(()=>{"use strict";var e={d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};let n;var s;e.d(t,{default:()=>w}),function(e){e[e.Web=1]="Web",e[e.Desktop=2]="Desktop",e[e.Mobile=3]="Mobile"}(n||(n={}));var i=new Uint8Array(16);function o(){if(!s&&!(s="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return s(i)}const a=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,r=function(e){return"string"==typeof e&&a.test(e)};for(var c=[],m=0;m<256;++m)c.push((m+256).toString(16).substr(1));const d=function(e,t,n){var s=(e=e||{}).random||(e.rng||o)();if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,t){n=n||0;for(var i=0;i<16;++i)t[n+i]=s[i];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(c[e[t+0]]+c[e[t+1]]+c[e[t+2]]+c[e[t+3]]+"-"+c[e[t+4]]+c[e[t+5]]+"-"+c[e[t+6]]+c[e[t+7]]+"-"+c[e[t+8]]+c[e[t+9]]+"-"+c[e[t+10]]+c[e[t+11]]+c[e[t+12]]+c[e[t+13]]+c[e[t+14]]+c[e[t+15]]).toLowerCase();if(!r(n))throw TypeError("Stringified UUID is invalid");return n}(s)},h=e=>{var t;const s={[n.Web]:"web",[n.Desktop]:"desktop",[n.Mobile]:"mobile"};return null!==(t=s[e])&&void 0!==t?t:s[n.Web]},p=()=>{};class l{static get isSupported(){return!(!window.console&&!console)}static get info(){return l.isSupported&&this.enabled?console.log.bind(console):p}static get error(){return l.isSupported?console.error.bind(console):p}}var u,v;let g,f;function y(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);t&&(s=s.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,s)}return n}function b(e){for(var t=1;t{if(l.info("Components API Message received:",e.data),document.referrer&&new URL(document.referrer).origin!==new URL(e.origin).origin)return;const{data:t}=e,n=(e=>{if("string"!=typeof e)return!1;try{const t=JSON.parse(e),n=Object.prototype.toString.call(t);return"[object Object]"===n||"[object Array]"===n}catch(e){return!1}})(t)?JSON.parse(t):t;if(n){if(void 0===this.component.origin&&n.action===f.ComponentRegistered)this.component.origin=e.origin;else if(e.origin!==this.component.origin)return;this.handleMessage(n)}else l.error("Invalid data received. Skipping...")},this.contentWindow.document.addEventListener("message",this.messageHandler,!1),this.contentWindow.addEventListener("message",this.messageHandler,!1),l.info("Waiting for messages...")}registerKeyboardEventListeners(){this.keyDownEventListener=e=>{this.shouldForwardKeyboardEvent(e)&&(l.info("A key has been pressed: ".concat(e.key)),this.forwardKeyboardEvent(f.KeyDown,e))},this.keyUpEventListener=e=>{this.shouldForwardKeyboardEvent(e)&&(l.info("A key has been released: ".concat(e.key)),this.forwardKeyboardEvent(f.KeyUp,e))},this.contentWindow.addEventListener("keydown",this.keyDownEventListener,!1),this.contentWindow.addEventListener("keyup",this.keyUpEventListener,!1)}shouldForwardKeyboardEvent(e){return!!(e.ctrlKey||e.metaKey||e.shiftKey||e.altKey)||"Control"===e.key||"Shift"===e.key||"Meta"===e.key||"Alt"===e.key}forwardKeyboardEvent(e,t){this.postMessage(e,{key:t.key,code:t.code,ctrlKey:t.ctrlKey,metaKey:t.metaKey,shiftKey:t.shiftKey,altKey:t.altKey})}registerMouseEventListeners(){this.clickEventListener=e=>{l.info("A click has been performed."),this.mouseClickEvent()},this.contentWindow.addEventListener("click",this.clickEventListener,!1)}handleMessage(e){switch(e.action){case f.ComponentRegistered:this.component.sessionKey=e.sessionKey,e.componentData&&(this.component.data=e.componentData),this.onReady(e.data),l.info("Component successfully registered with payload:",e);break;case f.ActivateThemes:this.activateThemes(e.data.themes);break;default:{var t,n;if(!e.original)return;const s=null===(t=this.sentMessages)||void 0===t?void 0:t.filter((t=>{var n;return t.messageId===(null===(n=e.original)||void 0===n?void 0:n.messageId)}))[0];if(!s){const e=this.contentWindow.document.title,t=("The extension '".concat(e,"' is attempting to communicate with Standard Notes, ")+"but an error is preventing it from doing so. Please restart this extension and try again.").replace(" "," ");return void l.info(t)}null==s||null===(n=s.callback)||void 0===n||n.call(s,e.data);break}}}onReady(e){this.component.environment=e.environment,this.component.platform=e.platform,this.component.uuid=e.uuid;for(const e of this.messageQueue)this.postMessage(e.action,e.data,e.callback);this.messageQueue=[],l.info("Data passed to onReady:",e),this.activateThemes(e.activeThemeUrls||[]),this.postMessage(f.ThemesActivated,{}),this.params.onReady&&this.params.onReady()}getSelfComponentUUID(){return this.component.uuid}isRunningInDesktopApplication(){return this.component.environment===h(n.Desktop)}isRunningInMobileApplication(){return this.component.environment===h(n.Mobile)}getComponentDataValueForKey(e){if(this.component.data)return this.component.data[e]}setComponentDataValueForKey(e,t){if(!this.component.data)throw new Error("The component has not been initialized.");if(!e||e&&0===e.length)throw new Error("The key for the data value should be a valid string.");this.component.data=b(b({},this.component.data),{},{[e]:t}),this.postMessage(f.SetComponentData,{componentData:this.component.data})}clearComponentData(){this.component.data={},this.postMessage(f.SetComponentData,{componentData:this.component.data})}postMessage(e,t,n){if(!this.component.sessionKey)return void this.messageQueue.push({action:e,data:t,api:g.Component,callback:n});e===f.SaveItems&&(t.height=this.params.handleRequestForContentHeight());const s={action:e,data:t,messageId:this.generateUUID(),sessionKey:this.component.sessionKey,api:g.Component},i=JSON.parse(JSON.stringify(s));let o;i.callback=n,this.sentMessages.push(i),o=this.isRunningInMobileApplication()?JSON.stringify(s):s,l.info("Posting message:",o);const a=this.component.origin&&"null"!==this.component.origin?this.component.origin:"*";this.contentWindow.parent.postMessage(o,a)}activateThemes(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];if(!this.component.acceptsThemes)return;l.info("Incoming themes:",e);const{activeThemes:t}=this.component;if(t&&t.sort().toString()==e.sort().toString())return;let n=e;const s=[];for(const i of t)e.includes(i)?n=n.filter((e=>e!==i)):s.push(i);l.info("Deactivating themes:",s),l.info("Activating themes:",n);for(const e of s)this.deactivateTheme(e);this.component.activeThemes=e;for(const e of n){if(!e)continue;const t=this.contentWindow.document.createElement("link");t.id=btoa(e),t.href=e,t.type="text/css",t.rel="stylesheet",t.media="screen,print",t.className="custom-theme",this.contentWindow.document.getElementsByTagName("head")[0].appendChild(t)}this.params.onThemesChange&&this.params.onThemesChange()}themeElementForUrl(e){return Array.from(this.contentWindow.document.getElementsByClassName("custom-theme")).slice().find((t=>t.id==btoa(e)))}deactivateTheme(e){const t=this.themeElementForUrl(e);t&&t.parentNode&&(t.setAttribute("disabled","true"),t.parentNode.removeChild(t))}generateUUID(){return d()}get platform(){return this.component.platform}get environment(){return this.component.environment}streamItems(e,t){this.postMessage(f.StreamItems,{content_types:e},(e=>{t(e.items)}))}streamContextItem(e){this.postMessage(f.StreamContextItem,{},(t=>{const{item:n}=t;(!this.lastStreamedItem||this.lastStreamedItem.uuid!==n.uuid)&&this.pendingSaveTimeout&&(clearTimeout(this.pendingSaveTimeout),this.performSavingOfItems(this.pendingSaveParams),this.pendingSaveTimeout=void 0,this.pendingSaveParams=void 0),this.lastStreamedItem=n,e(this.lastStreamedItem)}))}createItem(e,t){this.postMessage(f.CreateItem,{item:this.jsonObjectForItem(e)},(e=>{let{item:n}=e;!n&&e.items&&e.items.length>0&&(n=e.items[0]),t&&t(n)}))}createItems(e,t){const n=e.map((e=>this.jsonObjectForItem(e)));this.postMessage(f.CreateItems,{items:n},(e=>{t&&t(e.items)}))}deleteItem(e,t){this.deleteItems([e],t)}deleteItems(e,t){const n={items:e.map((e=>this.jsonObjectForItem(e)))};this.postMessage(f.DeleteItems,n,(e=>{t&&t(e)}))}sendCustomEvent(e,t,n){this.postMessage(e,t,(e=>{n&&n(e)}))}saveItem(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.saveItems([e],t,n)}saveItemWithPresave(e,t,n){this.saveItemsWithPresave([e],t,n)}saveItemsWithPresave(e,t,n){this.saveItems(e,n,!1,t)}performSavingOfItems(e){let{items:t,presave:n,callback:s}=e;const i=setTimeout((()=>{this.concernTimeouts.forEach((e=>clearTimeout(e))),alert("This editor is unable to communicate with Standard Notes. Your changes may not be saved. Please backup your changes, then restart the application and try again.")}),5e3);this.concernTimeouts.push(i),n&&n();const o=[];for(const e of t)o.push(this.jsonObjectForItem(e));this.postMessage(f.SaveItems,{items:o},(()=>{this.concernTimeouts.forEach((e=>clearTimeout(e))),null==s||s()}))}saveItems(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],s=arguments.length>3?arguments[3]:void 0;if(this.pendingSaveItems||(this.pendingSaveItems=[]),this.options.coallesedSaving&&!n){this.pendingSaveTimeout&&clearTimeout(this.pendingSaveTimeout);const n=e.map((e=>e.uuid)),i=this.pendingSaveItems.filter((e=>!n.includes(e.uuid)));this.pendingSaveItems=i.concat(e),this.pendingSaveParams={items:this.pendingSaveItems,presave:s,callback:t},this.pendingSaveTimeout=setTimeout((()=>{this.performSavingOfItems(this.pendingSaveParams),this.pendingSaveItems=[],this.pendingSaveTimeout=void 0,this.pendingSaveParams=null}),this.options.coallesedSavingDelay)}else this.performSavingOfItems({items:e,presave:s,callback:t})}setSize(e,t){this.postMessage(f.SetSize,{type:"container",width:e,height:t})}mouseClickEvent(){this.postMessage(f.Click,{})}jsonObjectForItem(e){const t=Object.assign({},e);return t.children=null,t.parent=null,t}getItemAppDataValue(e,t){var n,s;const i=null==e||null===(n=e.content)||void 0===n||null===(s=n.appData)||void 0===s?void 0:s["org.standardnotes.sn"];return null==i?void 0:i[t]}}return t.default})())); //# sourceMappingURL=dist.js.map \ No newline at end of file diff --git a/dist/dist.js.map b/dist/dist.js.map index ceddd48..ffd70a4 100644 --- a/dist/dist.js.map +++ b/dist/dist.js.map @@ -1 +1 @@ -{"version":3,"file":"dist.js","mappings":"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","sources":["webpack://ComponentRelay/webpack/universalModuleDefinition","webpack://ComponentRelay/webpack/bootstrap","webpack://ComponentRelay/webpack/runtime/define property getters","webpack://ComponentRelay/webpack/runtime/hasOwnProperty shorthand","webpack://ComponentRelay/./lib/Types/Environment.ts","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/rng.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/regex.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/validate.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/stringify.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/v4.js","webpack://ComponentRelay/./lib/Utils.ts","webpack://ComponentRelay/./lib/Logger.ts","webpack://ComponentRelay/./lib/Types/KeyboardModifier.ts","webpack://ComponentRelay/./lib/Types/MessagePayloadApi.ts","webpack://ComponentRelay/./lib/Types/ComponentAction.ts","webpack://ComponentRelay/./lib/ComponentRelay.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"ComponentRelay\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ComponentRelay\"] = factory();\n\telse\n\t\troot[\"ComponentRelay\"] = factory();\n})(self, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","export enum Environment {\n Web = 1,\n Desktop = 2,\n Mobile = 3,\n}\n","// Unique ID creation requires a high quality random # generator. In the browser we therefore\n// require the crypto API and do not support built-in fallback to lower quality random number\n// generators (like Math.random()).\nvar getRandomValues;\nvar rnds8 = new Uint8Array(16);\nexport default function rng() {\n // lazy load so that environments that need to polyfill have a chance to do so\n if (!getRandomValues) {\n // getRandomValues needs to be invoked in a context where \"this\" is a Crypto implementation. Also,\n // find the complete implementation of crypto (msCrypto) on IE11.\n getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);\n\n if (!getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n }\n\n return getRandomValues(rnds8);\n}","export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;","import REGEX from './regex.js';\n\nfunction validate(uuid) {\n return typeof uuid === 'string' && REGEX.test(uuid);\n}\n\nexport default validate;","import validate from './validate.js';\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\n\nvar byteToHex = [];\n\nfor (var i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).substr(1));\n}\n\nfunction stringify(arr) {\n var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n // Note: Be careful editing this code! It's been tuned for performance\n // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n var uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one\n // of the following:\n // - One or more input array values don't map to a hex octet (leading to\n // \"undefined\" in the uuid)\n // - Invalid input values for the RFC `version` or `variant` fields\n\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n\n return uuid;\n}\n\nexport default stringify;","import rng from './rng.js';\nimport stringify from './stringify.js';\n\nfunction v4(options, buf, offset) {\n options = options || {};\n var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n\n rnds[6] = rnds[6] & 0x0f | 0x40;\n rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided\n\n if (buf) {\n offset = offset || 0;\n\n for (var i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n\n return buf;\n }\n\n return stringify(rnds);\n}\n\nexport default v4;","import { Environment } from './Types/Environment'\nimport { v4 as uuidv4 } from 'uuid'\n\ndeclare global {\n interface Window {\n msCrypto: unknown\n }\n}\n\nexport const generateUuid = (): string => {\n return uuidv4()\n}\n\nexport const isValidJsonString = (str: unknown): boolean => {\n if (typeof str !== 'string') {\n return false\n }\n try {\n const result = JSON.parse(str)\n const type = Object.prototype.toString.call(result)\n return type === '[object Object]' || type === '[object Array]'\n } catch (e) {\n return false\n }\n}\n\nexport const environmentToString = (environment: Environment): string => {\n const map = {\n [Environment.Web]: 'web',\n [Environment.Desktop]: 'desktop',\n [Environment.Mobile]: 'mobile',\n }\n return map[environment] ?? map[Environment.Web]\n}\n\nexport const isNotUndefinedOrNull = (value: any): boolean => {\n return value !== null && value !== undefined\n}\n","const noop = () => undefined\n\nexport default class Logger {\n static enabled = false\n\n private static get isSupported() {\n return window.console || console ? true : false\n }\n\n static get info(): any {\n if (!Logger.isSupported || !this.enabled) {\n return noop\n }\n return console.log.bind(console)\n }\n\n static get error(): any {\n if (!Logger.isSupported) {\n return noop\n }\n return console.error.bind(console)\n }\n}\n","export enum KeyboardModifier {\n Shift = 'Shift',\n Ctrl = 'Control',\n Meta = 'Meta',\n}\n","export enum MessagePayloadApi {\n Component = 'component',\n}\n","export enum ComponentAction {\n SetSize = 'set-size',\n StreamItems = 'stream-items',\n StreamContextItem = 'stream-context-item',\n SaveItems = 'save-items',\n CreateItem = 'create-item',\n CreateItems = 'create-items',\n DeleteItems = 'delete-items',\n SetComponentData = 'set-component-data',\n RequestPermissions = 'request-permissions',\n DuplicateItem = 'duplicate-item',\n ComponentRegistered = 'component-registered',\n ActivateThemes = 'themes',\n Reply = 'reply',\n ThemesActivated = 'themes-activated',\n KeyDown = 'key-down',\n KeyUp = 'key-up',\n Click = 'click',\n}\n","import type {\n OutgoingItemMessagePayload,\n MessageData,\n DecryptedItem,\n AppDataField,\n DecryptedTransferPayload,\n ItemContent,\n ContentType,\n} from '@standardnotes/snjs'\nimport { environmentToString, generateUuid, isValidJsonString } from './Utils'\nimport Logger from './Logger'\nimport { KeyboardModifier } from './Types/KeyboardModifier'\nimport { ComponentRelayParams } from './Types/ComponentRelayParams'\nimport { MessagePayload } from './Types/MessagePayload'\nimport { Component } from './Types/Component'\nimport { MessagePayloadApi } from './Types/MessagePayloadApi'\nimport { ComponentRelayOptions } from './Types/ComponentRelayOptions'\nimport { ComponentAction } from './Types/ComponentAction'\nimport { Environment } from './Types/Environment'\n\nconst DEFAULT_COALLESED_SAVING_DELAY = 250\n\nexport default class ComponentRelay {\n private contentWindow: Window\n private component: Component = { activeThemes: [], acceptsThemes: true }\n private sentMessages: MessagePayload[] = []\n private messageQueue: MessagePayload[] = []\n private lastStreamedItem?: DecryptedTransferPayload\n private pendingSaveItems?: DecryptedTransferPayload[]\n private pendingSaveTimeout?: NodeJS.Timeout\n private pendingSaveParams?: any\n private messageHandler?: (event: any) => void\n private keyDownEventListener?: (event: KeyboardEvent) => void\n private keyUpEventListener?: (event: KeyboardEvent) => void\n private clickEventListener?: (event: MouseEvent) => void\n private concernTimeouts: NodeJS.Timeout[] = []\n private options: ComponentRelayOptions\n private params: Omit\n\n constructor(params: ComponentRelayParams) {\n if (!params || !params.targetWindow) {\n throw new Error('contentWindow must be a valid Window object.')\n }\n\n this.params = params\n\n this.options = params.options || {}\n\n if (this.options.coallesedSaving == undefined) {\n this.options.coallesedSaving = true\n }\n if (this.options.coallesedSavingDelay == undefined) {\n this.options.coallesedSavingDelay = DEFAULT_COALLESED_SAVING_DELAY\n }\n if (this.options.acceptsThemes != undefined) {\n this.component.acceptsThemes = this.options.acceptsThemes ?? true\n }\n\n Logger.enabled = this.options.debug ?? false\n\n this.contentWindow = params.targetWindow\n this.registerMessageHandler()\n this.registerKeyboardEventListeners()\n this.registerMouseEventListeners()\n }\n\n public deinit(): void {\n this.params.onReady = undefined\n this.component = {\n acceptsThemes: true,\n activeThemes: [],\n }\n this.messageQueue = []\n this.sentMessages = []\n this.lastStreamedItem = undefined\n this.pendingSaveItems = undefined\n this.pendingSaveTimeout = undefined\n this.pendingSaveParams = undefined\n\n if (this.messageHandler) {\n this.contentWindow.document.removeEventListener('message', this.messageHandler)\n this.contentWindow.removeEventListener('message', this.messageHandler)\n }\n\n if (this.keyDownEventListener) {\n this.contentWindow.removeEventListener('keydown', this.keyDownEventListener)\n }\n\n if (this.keyUpEventListener) {\n this.contentWindow.removeEventListener('keyup', this.keyUpEventListener)\n }\n\n if (this.clickEventListener) {\n this.contentWindow.removeEventListener('click', this.clickEventListener)\n }\n }\n\n private registerMessageHandler() {\n this.messageHandler = (event: MessageEvent) => {\n Logger.info('Components API Message received:', event.data)\n\n /**\n * We don't have access to window.parent.origin due to cross-domain restrictions.\n * Check referrer if available, otherwise defer to checking for first-run value.\n * Craft URL objects so that example.com === example.com/\n */\n if (document.referrer) {\n const referrer = new URL(document.referrer).origin\n const eventOrigin = new URL(event.origin).origin\n\n if (referrer !== eventOrigin) {\n return\n }\n }\n\n // Mobile environment sends data as JSON string.\n const { data } = event\n const parsedData = isValidJsonString(data) ? JSON.parse(data) : data\n\n if (!parsedData) {\n Logger.error('Invalid data received. Skipping...')\n return\n }\n\n /**\n * The Component Registered message will be the most reliable one, so we won't change it after any subsequent events,\n * in case you receive an event from another window.\n */\n if (typeof this.component.origin === 'undefined' && parsedData.action === ComponentAction.ComponentRegistered) {\n this.component.origin = event.origin\n } else if (event.origin !== this.component.origin) {\n // If event origin doesn't match first-run value, return.\n return\n }\n\n this.handleMessage(parsedData)\n }\n\n /**\n * Mobile (React Native) uses `document`, web/desktop uses `window`.addEventListener\n * for postMessage API to work properly.\n * Update May 2019:\n * As part of transitioning React Native webview into the community package,\n * we'll now only need to use window.addEventListener.\n * However, we want to maintain backward compatibility for Mobile < v3.0.5, so we'll keep document.addEventListener\n * Also, even with the new version of react-native-webview, Android may still require document.addEventListener (while iOS still only requires window.addEventListener)\n * https://github.com/react-native-community/react-native-webview/issues/323#issuecomment-467767933\n */\n this.contentWindow.document.addEventListener('message', this.messageHandler, false)\n this.contentWindow.addEventListener('message', this.messageHandler, false)\n\n Logger.info('Waiting for messages...')\n }\n\n private registerKeyboardEventListeners() {\n this.keyDownEventListener = (event: KeyboardEvent) => {\n Logger.info(`A key has been pressed: ${event.key}`)\n\n if (event.ctrlKey) {\n this.keyDownEvent(KeyboardModifier.Ctrl)\n } else if (event.shiftKey) {\n this.keyDownEvent(KeyboardModifier.Shift)\n } else if (event.metaKey || event.key === 'Meta') {\n this.keyDownEvent(KeyboardModifier.Meta)\n }\n }\n\n this.keyUpEventListener = (event: KeyboardEvent) => {\n Logger.info(`A key has been released: ${event.key}`)\n\n /**\n * Checking using event.key instead of the corresponding boolean properties.\n */\n if (event.key === 'Control') {\n this.keyUpEvent(KeyboardModifier.Ctrl)\n } else if (event.key === 'Shift') {\n this.keyUpEvent(KeyboardModifier.Shift)\n } else if (event.key === 'Meta') {\n this.keyUpEvent(KeyboardModifier.Meta)\n }\n }\n\n this.contentWindow.addEventListener('keydown', this.keyDownEventListener, false)\n this.contentWindow.addEventListener('keyup', this.keyUpEventListener, false)\n }\n\n private registerMouseEventListeners() {\n this.clickEventListener = (_event: MouseEvent) => {\n Logger.info('A click has been performed.')\n\n this.mouseClickEvent()\n }\n\n this.contentWindow.addEventListener('click', this.clickEventListener, false)\n }\n\n private handleMessage(payload: MessagePayload) {\n switch (payload.action) {\n case ComponentAction.ComponentRegistered:\n this.component.sessionKey = payload.sessionKey\n if (payload.componentData) {\n this.component.data = payload.componentData\n }\n this.onReady(payload.data)\n Logger.info('Component successfully registered with payload:', payload)\n break\n\n case ComponentAction.ActivateThemes:\n this.activateThemes(payload.data.themes)\n break\n\n default: {\n if (!payload.original) {\n return\n }\n\n // Get the callback from queue.\n const originalMessage = this.sentMessages?.filter((message: MessagePayload) => {\n return message.messageId === payload.original?.messageId\n })[0]\n\n if (!originalMessage) {\n // Connection must have been reset. We should alert the user unless it's a reply,\n // in which case we may have been deallocated and reinitialized and lost the\n // original message\n const extensionName = this.contentWindow.document.title\n const alertMessage = (\n `The extension '${extensionName}' is attempting to communicate with Standard Notes, ` +\n 'but an error is preventing it from doing so. Please restart this extension and try again.'\n ).replace(' ', ' ')\n\n Logger.info(alertMessage)\n return\n }\n\n originalMessage?.callback?.(payload.data)\n break\n }\n }\n }\n\n private onReady(data: MessageData) {\n this.component.environment = data.environment\n this.component.platform = data.platform\n this.component.uuid = data.uuid\n\n for (const message of this.messageQueue) {\n this.postMessage(message.action as ComponentAction, message.data, message.callback)\n }\n\n this.messageQueue = []\n\n Logger.info('Data passed to onReady:', data)\n\n this.activateThemes(data.activeThemeUrls || [])\n\n // After activateThemes is done, we want to send a message with the ThemesActivated action.\n this.postMessage(ComponentAction.ThemesActivated, {})\n\n if (this.params.onReady) {\n this.params.onReady()\n }\n }\n\n /**\n * Gets the component UUID.\n */\n public getSelfComponentUUID(): string | undefined {\n return this.component.uuid\n }\n\n /**\n * Checks if the component is running in a Desktop application.\n */\n public isRunningInDesktopApplication(): boolean {\n return this.component.environment === environmentToString(Environment.Desktop)\n }\n\n /**\n * Checks if the component is running in a Mobile application.\n */\n public isRunningInMobileApplication(): boolean {\n return this.component.environment === environmentToString(Environment.Mobile)\n }\n\n /**\n * Gets the component's data value for the specified key.\n * @param key The key for the data object.\n * @returns `undefined` if the value for the key does not exist. Returns the stored value otherwise.\n */\n public getComponentDataValueForKey(key: string): any {\n if (!this.component.data) {\n return\n }\n return this.component.data[key]\n }\n\n /**\n * Sets the component's data value for the specified key.\n * @param key The key for the data object.\n * @param value The value to store under the specified key.\n */\n public setComponentDataValueForKey(key: string, value: any): void {\n if (!this.component.data) {\n throw new Error('The component has not been initialized.')\n }\n if (!key || (key && key.length === 0)) {\n throw new Error('The key for the data value should be a valid string.')\n }\n this.component.data = {\n ...this.component.data,\n [key]: value,\n }\n this.postMessage(ComponentAction.SetComponentData, {\n componentData: this.component.data,\n })\n }\n\n /**\n * Clears the component's data object.\n */\n public clearComponentData(): void {\n this.component.data = {}\n this.postMessage(ComponentAction.SetComponentData, {\n componentData: this.component.data,\n })\n }\n\n private postMessage(action: ComponentAction, data: MessageData, callback?: (...params: any) => void) {\n /**\n * If the sessionKey is not set, we push the message to queue\n * that will be processed later on.\n */\n if (!this.component.sessionKey) {\n this.messageQueue.push({\n action,\n data,\n api: MessagePayloadApi.Component,\n callback: callback,\n })\n return\n }\n\n if (action === ComponentAction.SaveItems) {\n data.height = this.params.handleRequestForContentHeight()\n }\n\n const message = {\n action,\n data,\n messageId: this.generateUUID(),\n sessionKey: this.component.sessionKey,\n api: MessagePayloadApi.Component,\n }\n\n const sentMessage = JSON.parse(JSON.stringify(message))\n sentMessage.callback = callback\n this.sentMessages.push(sentMessage)\n\n let postMessagePayload\n\n // Mobile (React Native) requires a string for the postMessage API.\n if (this.isRunningInMobileApplication()) {\n postMessagePayload = JSON.stringify(message)\n } else {\n postMessagePayload = message\n }\n\n Logger.info('Posting message:', postMessagePayload)\n const targetOrigin = this.component.origin && this.component.origin !== 'null' ? this.component.origin : '*'\n this.contentWindow.parent.postMessage(postMessagePayload, targetOrigin)\n }\n\n private activateThemes(incomingUrls: string[] = []) {\n if (!this.component.acceptsThemes) {\n return\n }\n\n Logger.info('Incoming themes:', incomingUrls)\n\n const { activeThemes } = this.component\n\n if (activeThemes && activeThemes.sort().toString() == incomingUrls.sort().toString()) {\n // Incoming theme URLs are same as active, do nothing.\n return\n }\n\n let themesToActivate = incomingUrls\n const themesToDeactivate = []\n\n for (const activeUrl of activeThemes) {\n if (!incomingUrls.includes(activeUrl)) {\n // Active not present in incoming, deactivate it.\n themesToDeactivate.push(activeUrl)\n } else {\n // Already present in active themes, remove it from themesToActivate.\n themesToActivate = themesToActivate.filter((candidate) => {\n return candidate !== activeUrl\n })\n }\n }\n\n Logger.info('Deactivating themes:', themesToDeactivate)\n Logger.info('Activating themes:', themesToActivate)\n\n for (const themeUrl of themesToDeactivate) {\n this.deactivateTheme(themeUrl)\n }\n\n this.component.activeThemes = incomingUrls\n\n for (const themeUrl of themesToActivate) {\n if (!themeUrl) {\n continue\n }\n\n const link = this.contentWindow.document.createElement('link')\n link.id = btoa(themeUrl)\n link.href = themeUrl\n link.type = 'text/css'\n link.rel = 'stylesheet'\n link.media = 'screen,print'\n link.className = 'custom-theme'\n this.contentWindow.document.getElementsByTagName('head')[0].appendChild(link)\n }\n\n this.params.onThemesChange && this.params.onThemesChange()\n }\n\n private themeElementForUrl(themeUrl: string) {\n const elements = Array.from(this.contentWindow.document.getElementsByClassName('custom-theme')).slice()\n return elements.find((element) => {\n // We used to search here by `href`, but on desktop, with local file:// urls, that didn't work for some reason.\n return element.id == btoa(themeUrl)\n })\n }\n\n private deactivateTheme(themeUrl: string) {\n const element = this.themeElementForUrl(themeUrl)\n if (element && element.parentNode) {\n element.setAttribute('disabled', 'true')\n element.parentNode.removeChild(element)\n }\n }\n\n private generateUUID() {\n return generateUuid()\n }\n\n /**\n * Gets the current platform where the component is running.\n */\n public get platform(): string | undefined {\n return this.component.platform\n }\n\n /**\n * Gets the current environment where the component is running.\n */\n public get environment(): string | undefined {\n return this.component.environment\n }\n\n /**\n * Streams a collection of Items, filtered by content type.\n * New items are passed to the callback as they come.\n * @param contentTypes A collection of Content Types.\n * @param callback A callback to process the streamed items.\n */\n public streamItems(contentTypes: ContentType[], callback: (data: any) => void): void {\n this.postMessage(ComponentAction.StreamItems, { content_types: contentTypes }, (data: any) => {\n callback(data.items)\n })\n }\n\n /**\n * Streams the current Item in context.\n * @param callback A callback to process the streamed item.\n */\n public streamContextItem(callback: (data: any) => void): void {\n this.postMessage(ComponentAction.StreamContextItem, {}, (data) => {\n const { item } = data\n /**\n * If this is a new context item than the context item the component was currently entertaining,\n * we want to immediately commit any pending saves, because if you send the new context item to the\n * component before it has commited its presave, it will end up first replacing the UI with new context item,\n * and when the debouncer executes to read the component UI, it will be reading the new UI for the previous item.\n */\n const isNewItem = !this.lastStreamedItem || this.lastStreamedItem.uuid !== item.uuid\n\n if (isNewItem && this.pendingSaveTimeout) {\n clearTimeout(this.pendingSaveTimeout)\n this.performSavingOfItems(this.pendingSaveParams)\n this.pendingSaveTimeout = undefined\n this.pendingSaveParams = undefined\n }\n\n this.lastStreamedItem = item\n callback(this.lastStreamedItem)\n })\n }\n\n /**\n * Creates and stores an Item in the item store.\n * @param item The Item's payload content.\n * @param callback The callback to process the created Item.\n */\n public createItem(item: DecryptedTransferPayload, callback: (data: any) => void): void {\n this.postMessage(ComponentAction.CreateItem, { item: this.jsonObjectForItem(item) }, (data: any) => {\n let { item } = data\n /**\n * A previous version of the SN app had an issue where the item in the reply to ComponentActions.CreateItems\n * would be nested inside \"items\" and not \"item\". So handle both cases here.\n */\n if (!item && data.items && data.items.length > 0) {\n item = data.items[0]\n }\n\n callback && callback(item)\n })\n }\n\n /**\n * Creates and stores a collection of Items in the item store.\n * @param items The Item(s) payload collection.\n * @param callback The callback to process the created Item(s).\n */\n public createItems(items: DecryptedTransferPayload[], callback: (data: any) => void): void {\n const mapped = items.map((item) => this.jsonObjectForItem(item))\n this.postMessage(ComponentAction.CreateItems, { items: mapped }, (data: any) => {\n callback && callback(data.items)\n })\n }\n\n /**\n * Deletes an Item from the item store.\n * @param item The Item to delete.\n * @param callback The callback with the result of the operation.\n */\n public deleteItem(item: DecryptedTransferPayload, callback: (data: OutgoingItemMessagePayload) => void): void {\n this.deleteItems([item], callback)\n }\n\n /**\n * Deletes a collection of Items from the item store.\n * @param items The Item(s) to delete.\n * @param callback The callback with the result of the operation.\n */\n public deleteItems(items: DecryptedTransferPayload[], callback: (data: OutgoingItemMessagePayload) => void): void {\n const params = {\n items: items.map((item) => {\n return this.jsonObjectForItem(item)\n }),\n }\n this.postMessage(ComponentAction.DeleteItems, params, (data) => {\n callback && callback(data)\n })\n }\n\n /**\n * Performs a custom action to the component manager.\n * @param action\n * @param data\n * @param callback The callback with the result of the operation.\n */\n public sendCustomEvent(action: ComponentAction, data: any, callback?: (data: any) => void): void {\n this.postMessage(action, data, (data: any) => {\n callback && callback(data)\n })\n }\n\n /**\n * Saves an existing Item in the item store.\n * @param item An existing Item to be saved.\n * @param callback\n * @param skipDebouncer\n */\n public saveItem(item: DecryptedTransferPayload, callback?: () => void, skipDebouncer = false): void {\n this.saveItems([item], callback, skipDebouncer)\n }\n\n /**\n * Runs a callback before saving an Item.\n * @param item An existing Item to be saved.\n * @param presave Allows clients to perform any actions last second before the save actually occurs (like setting previews).\n * Saves debounce by default, so if a client needs to compute a property on an item before saving, it's best to\n * hook into the debounce cycle so that clients don't have to implement their own debouncing.\n * @param callback\n */\n public saveItemWithPresave(\n item: DecryptedTransferPayload,\n presave: any,\n callback?: () => void,\n ): void {\n this.saveItemsWithPresave([item], presave, callback)\n }\n\n /**\n * Runs a callback before saving a collection of Items.\n * @param items A collection of existing Items to be saved.\n * @param presave Allows clients to perform any actions last second before the save actually occurs (like setting previews).\n * Saves debounce by default, so if a client needs to compute a property on an item before saving, it's best to\n * hook into the debounce cycle so that clients don't have to implement their own debouncing.\n * @param callback\n */\n public saveItemsWithPresave(items: DecryptedTransferPayload[], presave: any, callback?: () => void): void {\n this.saveItems(items, callback, false, presave)\n }\n\n private performSavingOfItems({\n items,\n presave,\n callback,\n }: {\n items: DecryptedTransferPayload[]\n presave: () => void\n callback?: () => void\n }) {\n const ConcernIntervalMS = 5000\n const concernTimeout = setTimeout(() => {\n this.concernTimeouts.forEach((timeout) => clearTimeout(timeout))\n alert(\n 'This editor is unable to communicate with Standard Notes. ' +\n 'Your changes may not be saved. Please backup your changes, then restart the ' +\n 'application and try again.',\n )\n }, ConcernIntervalMS)\n\n this.concernTimeouts.push(concernTimeout)\n\n /**\n * Presave block allows client to gain the benefit of performing something in the debounce cycle.\n */\n presave && presave()\n\n const mappedItems = []\n for (const item of items) {\n mappedItems.push(this.jsonObjectForItem(item))\n }\n\n const wrappedCallback = () => {\n this.concernTimeouts.forEach((timeout) => clearTimeout(timeout))\n callback?.()\n }\n\n this.postMessage(ComponentAction.SaveItems, { items: mappedItems }, wrappedCallback)\n }\n\n /**\n * Saves a collection of existing Items.\n * @param items The items to be saved.\n * @param callback\n * @param skipDebouncer Allows saves to go through right away rather than waiting for timeout.\n * This should be used when saving items via other means besides keystrokes.\n * @param presave\n */\n public saveItems(\n items: DecryptedTransferPayload[],\n callback?: () => void,\n skipDebouncer = false,\n presave?: any,\n ): void {\n /**\n * We need to make sure that when we clear a pending save timeout,\n * we carry over those pending items into the new save.\n */\n if (!this.pendingSaveItems) {\n this.pendingSaveItems = []\n }\n\n if (this.options.coallesedSaving && !skipDebouncer) {\n if (this.pendingSaveTimeout) {\n clearTimeout(this.pendingSaveTimeout)\n }\n\n const incomingIds = items.map((item) => item.uuid)\n /**\n * Replace any existing save items with incoming values.\n * Only keep items here who are not in incomingIds.\n */\n const preexistingItems = this.pendingSaveItems.filter((item) => {\n return !incomingIds.includes(item.uuid)\n })\n\n // Add new items, now that we've made sure it's cleared of incoming items.\n this.pendingSaveItems = preexistingItems.concat(items)\n\n // We'll potentially need to commit early if stream-context-item message comes in.\n this.pendingSaveParams = {\n items: this.pendingSaveItems,\n presave,\n callback,\n }\n\n this.pendingSaveTimeout = setTimeout(() => {\n this.performSavingOfItems(this.pendingSaveParams)\n this.pendingSaveItems = []\n this.pendingSaveTimeout = undefined\n this.pendingSaveParams = null\n }, this.options.coallesedSavingDelay)\n } else {\n this.performSavingOfItems({ items, presave, callback })\n }\n }\n\n /**\n * Sets a new container size for the current component.\n * @param width The new width.\n * @param height The new height.\n */\n public setSize(width: string | number, height: string | number): void {\n this.postMessage(ComponentAction.SetSize, {\n type: 'container',\n width,\n height,\n })\n }\n\n /**\n * Sends the KeyDown keyboard event to the Standard Notes parent application.\n * @param keyboardModifier The keyboard modifier that was pressed.\n */\n private keyDownEvent(keyboardModifier: KeyboardModifier): void {\n this.postMessage(ComponentAction.KeyDown, { keyboardModifier })\n }\n\n /**\n * Sends the KeyUp keyboard event to the Standard Notes parent application.\n * @param keyboardModifier The keyboard modifier that was released.\n */\n private keyUpEvent(keyboardModifier: KeyboardModifier): void {\n this.postMessage(ComponentAction.KeyUp, { keyboardModifier })\n }\n\n /**\n * Sends the Click mouse event to the Standard Notes parent application.\n */\n private mouseClickEvent(): void {\n this.postMessage(ComponentAction.Click, {})\n }\n\n private jsonObjectForItem(item: DecryptedItem | DecryptedTransferPayload) {\n const copy = Object.assign({}, item) as any\n copy.children = null\n copy.parent = null\n return copy\n }\n\n /**\n * Gets the Item's appData value for the specified key.\n * Uses the default domain (org.standardnotes.sn).\n * This function is used with Items returned from streamContextItem() and streamItems()\n * @param item The Item to get the appData value from.\n * @param key The key to get the value from.\n */\n public getItemAppDataValue(item: OutgoingItemMessagePayload | undefined, key: AppDataField | string): any {\n const defaultDomain = 'org.standardnotes.sn'\n const domainData = item?.content?.appData?.[defaultDomain]\n return domainData?.[key as AppDataField]\n }\n}\n"],"names":["root","factory","exports","module","define","amd","self","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Environment","getRandomValues","rnds8","Uint8Array","rng","crypto","bind","msCrypto","Error","uuid","byteToHex","i","push","toString","substr","options","buf","offset","rnds","random","arr","arguments","length","undefined","toLowerCase","TypeError","environmentToString","environment","map","Web","Desktop","Mobile","noop","Logger","isSupported","window","console","info","this","enabled","log","error","KeyboardModifier","MessagePayloadApi","ComponentAction","ComponentRelay","constructor","params","activeThemes","acceptsThemes","targetWindow","coallesedSaving","coallesedSavingDelay","component","debug","contentWindow","registerMessageHandler","registerKeyboardEventListeners","registerMouseEventListeners","deinit","onReady","messageQueue","sentMessages","lastStreamedItem","pendingSaveItems","pendingSaveTimeout","pendingSaveParams","messageHandler","document","removeEventListener","keyDownEventListener","keyUpEventListener","clickEventListener","event","data","referrer","URL","origin","parsedData","str","result","JSON","parse","type","e","isValidJsonString","action","ComponentRegistered","handleMessage","addEventListener","ctrlKey","keyDownEvent","Ctrl","shiftKey","Shift","metaKey","Meta","keyUpEvent","_event","mouseClickEvent","payload","sessionKey","componentData","ActivateThemes","activateThemes","themes","original","originalMessage","filter","message","messageId","extensionName","title","alertMessage","replace","callback","platform","postMessage","activeThemeUrls","ThemesActivated","getSelfComponentUUID","isRunningInDesktopApplication","isRunningInMobileApplication","getComponentDataValueForKey","setComponentDataValueForKey","value","SetComponentData","clearComponentData","api","Component","SaveItems","height","handleRequestForContentHeight","generateUUID","sentMessage","stringify","postMessagePayload","targetOrigin","parent","incomingUrls","sort","themesToActivate","themesToDeactivate","activeUrl","includes","candidate","themeUrl","deactivateTheme","link","createElement","id","btoa","href","rel","media","className","getElementsByTagName","appendChild","onThemesChange","themeElementForUrl","Array","from","getElementsByClassName","slice","find","element","parentNode","setAttribute","removeChild","uuidv4","streamItems","contentTypes","StreamItems","content_types","items","streamContextItem","StreamContextItem","item","clearTimeout","performSavingOfItems","createItem","CreateItem","jsonObjectForItem","createItems","mapped","CreateItems","deleteItem","deleteItems","DeleteItems","sendCustomEvent","saveItem","skipDebouncer","saveItems","saveItemWithPresave","presave","saveItemsWithPresave","concernTimeout","setTimeout","concernTimeouts","forEach","timeout","alert","mappedItems","incomingIds","preexistingItems","concat","setSize","width","SetSize","keyboardModifier","KeyDown","KeyUp","Click","copy","assign","children","getItemAppDataValue","domainData","content","appData"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"dist.js","mappings":"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","sources":["webpack://ComponentRelay/webpack/universalModuleDefinition","webpack://ComponentRelay/webpack/bootstrap","webpack://ComponentRelay/webpack/runtime/define property getters","webpack://ComponentRelay/webpack/runtime/hasOwnProperty shorthand","webpack://ComponentRelay/./lib/Types/Environment.ts","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/rng.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/regex.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/validate.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/stringify.js","webpack://ComponentRelay/./node_modules/uuid/dist/esm-browser/v4.js","webpack://ComponentRelay/./lib/Utils.ts","webpack://ComponentRelay/./lib/Logger.ts","webpack://ComponentRelay/./lib/Types/MessagePayloadApi.ts","webpack://ComponentRelay/./lib/Types/ComponentAction.ts","webpack://ComponentRelay/./lib/ComponentRelay.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"ComponentRelay\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ComponentRelay\"] = factory();\n\telse\n\t\troot[\"ComponentRelay\"] = factory();\n})(self, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","export enum Environment {\n Web = 1,\n Desktop = 2,\n Mobile = 3,\n}\n","// Unique ID creation requires a high quality random # generator. In the browser we therefore\n// require the crypto API and do not support built-in fallback to lower quality random number\n// generators (like Math.random()).\nvar getRandomValues;\nvar rnds8 = new Uint8Array(16);\nexport default function rng() {\n // lazy load so that environments that need to polyfill have a chance to do so\n if (!getRandomValues) {\n // getRandomValues needs to be invoked in a context where \"this\" is a Crypto implementation. Also,\n // find the complete implementation of crypto (msCrypto) on IE11.\n getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);\n\n if (!getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n }\n\n return getRandomValues(rnds8);\n}","export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;","import REGEX from './regex.js';\n\nfunction validate(uuid) {\n return typeof uuid === 'string' && REGEX.test(uuid);\n}\n\nexport default validate;","import validate from './validate.js';\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\n\nvar byteToHex = [];\n\nfor (var i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).substr(1));\n}\n\nfunction stringify(arr) {\n var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n // Note: Be careful editing this code! It's been tuned for performance\n // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n var uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one\n // of the following:\n // - One or more input array values don't map to a hex octet (leading to\n // \"undefined\" in the uuid)\n // - Invalid input values for the RFC `version` or `variant` fields\n\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n\n return uuid;\n}\n\nexport default stringify;","import rng from './rng.js';\nimport stringify from './stringify.js';\n\nfunction v4(options, buf, offset) {\n options = options || {};\n var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n\n rnds[6] = rnds[6] & 0x0f | 0x40;\n rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided\n\n if (buf) {\n offset = offset || 0;\n\n for (var i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n\n return buf;\n }\n\n return stringify(rnds);\n}\n\nexport default v4;","import { Environment } from './Types/Environment'\nimport { v4 as uuidv4 } from 'uuid'\n\ndeclare global {\n interface Window {\n msCrypto: unknown\n }\n}\n\nexport const generateUuid = (): string => {\n return uuidv4()\n}\n\nexport const isValidJsonString = (str: unknown): boolean => {\n if (typeof str !== 'string') {\n return false\n }\n try {\n const result = JSON.parse(str)\n const type = Object.prototype.toString.call(result)\n return type === '[object Object]' || type === '[object Array]'\n } catch (e) {\n return false\n }\n}\n\nexport const environmentToString = (environment: Environment): string => {\n const map = {\n [Environment.Web]: 'web',\n [Environment.Desktop]: 'desktop',\n [Environment.Mobile]: 'mobile',\n }\n return map[environment] ?? map[Environment.Web]\n}\n\nexport const isNotUndefinedOrNull = (value: any): boolean => {\n return value !== null && value !== undefined\n}\n","const noop = () => undefined\n\nexport default class Logger {\n static enabled = false\n\n private static get isSupported() {\n return window.console || console ? true : false\n }\n\n static get info(): any {\n if (!Logger.isSupported || !this.enabled) {\n return noop\n }\n return console.log.bind(console)\n }\n\n static get error(): any {\n if (!Logger.isSupported) {\n return noop\n }\n return console.error.bind(console)\n }\n}\n","export enum MessagePayloadApi {\n Component = 'component',\n}\n","export enum ComponentAction {\n SetSize = 'set-size',\n StreamItems = 'stream-items',\n StreamContextItem = 'stream-context-item',\n SaveItems = 'save-items',\n CreateItem = 'create-item',\n CreateItems = 'create-items',\n DeleteItems = 'delete-items',\n SetComponentData = 'set-component-data',\n RequestPermissions = 'request-permissions',\n DuplicateItem = 'duplicate-item',\n ComponentRegistered = 'component-registered',\n ActivateThemes = 'themes',\n Reply = 'reply',\n ThemesActivated = 'themes-activated',\n KeyDown = 'key-down',\n KeyUp = 'key-up',\n Click = 'click',\n}\n","import type {\n OutgoingItemMessagePayload,\n MessageData,\n DecryptedItem,\n AppDataField,\n DecryptedTransferPayload,\n ItemContent,\n ContentType,\n} from '@standardnotes/snjs'\nimport { environmentToString, generateUuid, isValidJsonString } from './Utils'\nimport Logger from './Logger'\nimport { ComponentRelayParams } from './Types/ComponentRelayParams'\nimport { MessagePayload } from './Types/MessagePayload'\nimport { Component } from './Types/Component'\nimport { MessagePayloadApi } from './Types/MessagePayloadApi'\nimport { ComponentRelayOptions } from './Types/ComponentRelayOptions'\nimport { ComponentAction } from './Types/ComponentAction'\nimport { Environment } from './Types/Environment'\n\nconst DEFAULT_COALLESED_SAVING_DELAY = 250\n\nexport default class ComponentRelay {\n private contentWindow: Window\n private component: Component = { activeThemes: [], acceptsThemes: true }\n private sentMessages: MessagePayload[] = []\n private messageQueue: MessagePayload[] = []\n private lastStreamedItem?: DecryptedTransferPayload\n private pendingSaveItems?: DecryptedTransferPayload[]\n private pendingSaveTimeout?: NodeJS.Timeout\n private pendingSaveParams?: any\n private messageHandler?: (event: any) => void\n private keyDownEventListener?: (event: KeyboardEvent) => void\n private keyUpEventListener?: (event: KeyboardEvent) => void\n private clickEventListener?: (event: MouseEvent) => void\n private concernTimeouts: NodeJS.Timeout[] = []\n private options: ComponentRelayOptions\n private params: Omit\n\n constructor(params: ComponentRelayParams) {\n if (!params || !params.targetWindow) {\n throw new Error('contentWindow must be a valid Window object.')\n }\n\n this.params = params\n\n this.options = params.options || {}\n\n if (this.options.coallesedSaving == undefined) {\n this.options.coallesedSaving = true\n }\n if (this.options.coallesedSavingDelay == undefined) {\n this.options.coallesedSavingDelay = DEFAULT_COALLESED_SAVING_DELAY\n }\n if (this.options.acceptsThemes != undefined) {\n this.component.acceptsThemes = this.options.acceptsThemes ?? true\n }\n\n Logger.enabled = this.options.debug ?? false\n\n this.contentWindow = params.targetWindow\n this.registerMessageHandler()\n this.registerKeyboardEventListeners()\n this.registerMouseEventListeners()\n }\n\n public deinit(): void {\n this.params.onReady = undefined\n this.component = {\n acceptsThemes: true,\n activeThemes: [],\n }\n this.messageQueue = []\n this.sentMessages = []\n this.lastStreamedItem = undefined\n this.pendingSaveItems = undefined\n this.pendingSaveTimeout = undefined\n this.pendingSaveParams = undefined\n\n if (this.messageHandler) {\n this.contentWindow.document.removeEventListener('message', this.messageHandler)\n this.contentWindow.removeEventListener('message', this.messageHandler)\n }\n\n if (this.keyDownEventListener) {\n this.contentWindow.removeEventListener('keydown', this.keyDownEventListener)\n }\n\n if (this.keyUpEventListener) {\n this.contentWindow.removeEventListener('keyup', this.keyUpEventListener)\n }\n\n if (this.clickEventListener) {\n this.contentWindow.removeEventListener('click', this.clickEventListener)\n }\n }\n\n private registerMessageHandler() {\n this.messageHandler = (event: MessageEvent) => {\n Logger.info('Components API Message received:', event.data)\n\n /**\n * We don't have access to window.parent.origin due to cross-domain restrictions.\n * Check referrer if available, otherwise defer to checking for first-run value.\n * Craft URL objects so that example.com === example.com/\n */\n if (document.referrer) {\n const referrer = new URL(document.referrer).origin\n const eventOrigin = new URL(event.origin).origin\n\n if (referrer !== eventOrigin) {\n return\n }\n }\n\n // Mobile environment sends data as JSON string.\n const { data } = event\n const parsedData = isValidJsonString(data) ? JSON.parse(data) : data\n\n if (!parsedData) {\n Logger.error('Invalid data received. Skipping...')\n return\n }\n\n /**\n * The Component Registered message will be the most reliable one, so we won't change it after any subsequent events,\n * in case you receive an event from another window.\n */\n if (typeof this.component.origin === 'undefined' && parsedData.action === ComponentAction.ComponentRegistered) {\n this.component.origin = event.origin\n } else if (event.origin !== this.component.origin) {\n // If event origin doesn't match first-run value, return.\n return\n }\n\n this.handleMessage(parsedData)\n }\n\n /**\n * Mobile (React Native) uses `document`, web/desktop uses `window`.addEventListener\n * for postMessage API to work properly.\n * Update May 2019:\n * As part of transitioning React Native webview into the community package,\n * we'll now only need to use window.addEventListener.\n * However, we want to maintain backward compatibility for Mobile < v3.0.5, so we'll keep document.addEventListener\n * Also, even with the new version of react-native-webview, Android may still require document.addEventListener (while iOS still only requires window.addEventListener)\n * https://github.com/react-native-community/react-native-webview/issues/323#issuecomment-467767933\n */\n this.contentWindow.document.addEventListener('message', this.messageHandler, false)\n this.contentWindow.addEventListener('message', this.messageHandler, false)\n\n Logger.info('Waiting for messages...')\n }\n\n private registerKeyboardEventListeners() {\n this.keyDownEventListener = (event: KeyboardEvent) => {\n if (!this.shouldForwardKeyboardEvent(event)) {\n return\n }\n\n Logger.info(`A key has been pressed: ${event.key}`)\n this.forwardKeyboardEvent(ComponentAction.KeyDown, event)\n }\n\n this.keyUpEventListener = (event: KeyboardEvent) => {\n if (!this.shouldForwardKeyboardEvent(event)) {\n return\n }\n\n Logger.info(`A key has been released: ${event.key}`)\n this.forwardKeyboardEvent(ComponentAction.KeyUp, event)\n }\n\n this.contentWindow.addEventListener('keydown', this.keyDownEventListener, false)\n this.contentWindow.addEventListener('keyup', this.keyUpEventListener, false)\n }\n\n /**\n * Only forward keyboard events involving shortcut modifiers (Ctrl, Shift, Meta, Alt).\n * Plain typing should stay in the editor and not be sent to the parent app.\n */\n private shouldForwardKeyboardEvent(event: KeyboardEvent): boolean {\n if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {\n return true\n }\n\n // On keyup, modifier flags may already be false when the modifier key itself is released.\n return event.key === 'Control' || event.key === 'Shift' || event.key === 'Meta' || event.key === 'Alt'\n }\n\n private forwardKeyboardEvent(action: ComponentAction.KeyDown | ComponentAction.KeyUp, event: KeyboardEvent): void {\n this.postMessage(action, {\n key: event.key,\n code: event.code,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n shiftKey: event.shiftKey,\n altKey: event.altKey,\n } as MessageData)\n }\n\n private registerMouseEventListeners() {\n this.clickEventListener = (_event: MouseEvent) => {\n Logger.info('A click has been performed.')\n\n this.mouseClickEvent()\n }\n\n this.contentWindow.addEventListener('click', this.clickEventListener, false)\n }\n\n private handleMessage(payload: MessagePayload) {\n switch (payload.action) {\n case ComponentAction.ComponentRegistered:\n this.component.sessionKey = payload.sessionKey\n if (payload.componentData) {\n this.component.data = payload.componentData\n }\n this.onReady(payload.data)\n Logger.info('Component successfully registered with payload:', payload)\n break\n\n case ComponentAction.ActivateThemes:\n this.activateThemes(payload.data.themes)\n break\n\n default: {\n if (!payload.original) {\n return\n }\n\n // Get the callback from queue.\n const originalMessage = this.sentMessages?.filter((message: MessagePayload) => {\n return message.messageId === payload.original?.messageId\n })[0]\n\n if (!originalMessage) {\n // Connection must have been reset. We should alert the user unless it's a reply,\n // in which case we may have been deallocated and reinitialized and lost the\n // original message\n const extensionName = this.contentWindow.document.title\n const alertMessage = (\n `The extension '${extensionName}' is attempting to communicate with Standard Notes, ` +\n 'but an error is preventing it from doing so. Please restart this extension and try again.'\n ).replace(' ', ' ')\n\n Logger.info(alertMessage)\n return\n }\n\n originalMessage?.callback?.(payload.data)\n break\n }\n }\n }\n\n private onReady(data: MessageData) {\n this.component.environment = data.environment\n this.component.platform = data.platform\n this.component.uuid = data.uuid\n\n for (const message of this.messageQueue) {\n this.postMessage(message.action as ComponentAction, message.data, message.callback)\n }\n\n this.messageQueue = []\n\n Logger.info('Data passed to onReady:', data)\n\n this.activateThemes(data.activeThemeUrls || [])\n\n // After activateThemes is done, we want to send a message with the ThemesActivated action.\n this.postMessage(ComponentAction.ThemesActivated, {})\n\n if (this.params.onReady) {\n this.params.onReady()\n }\n }\n\n /**\n * Gets the component UUID.\n */\n public getSelfComponentUUID(): string | undefined {\n return this.component.uuid\n }\n\n /**\n * Checks if the component is running in a Desktop application.\n */\n public isRunningInDesktopApplication(): boolean {\n return this.component.environment === environmentToString(Environment.Desktop)\n }\n\n /**\n * Checks if the component is running in a Mobile application.\n */\n public isRunningInMobileApplication(): boolean {\n return this.component.environment === environmentToString(Environment.Mobile)\n }\n\n /**\n * Gets the component's data value for the specified key.\n * @param key The key for the data object.\n * @returns `undefined` if the value for the key does not exist. Returns the stored value otherwise.\n */\n public getComponentDataValueForKey(key: string): any {\n if (!this.component.data) {\n return\n }\n return this.component.data[key]\n }\n\n /**\n * Sets the component's data value for the specified key.\n * @param key The key for the data object.\n * @param value The value to store under the specified key.\n */\n public setComponentDataValueForKey(key: string, value: any): void {\n if (!this.component.data) {\n throw new Error('The component has not been initialized.')\n }\n if (!key || (key && key.length === 0)) {\n throw new Error('The key for the data value should be a valid string.')\n }\n this.component.data = {\n ...this.component.data,\n [key]: value,\n }\n this.postMessage(ComponentAction.SetComponentData, {\n componentData: this.component.data,\n })\n }\n\n /**\n * Clears the component's data object.\n */\n public clearComponentData(): void {\n this.component.data = {}\n this.postMessage(ComponentAction.SetComponentData, {\n componentData: this.component.data,\n })\n }\n\n private postMessage(action: ComponentAction, data: MessageData, callback?: (...params: any) => void) {\n /**\n * If the sessionKey is not set, we push the message to queue\n * that will be processed later on.\n */\n if (!this.component.sessionKey) {\n this.messageQueue.push({\n action,\n data,\n api: MessagePayloadApi.Component,\n callback: callback,\n })\n return\n }\n\n if (action === ComponentAction.SaveItems) {\n data.height = this.params.handleRequestForContentHeight()\n }\n\n const message = {\n action,\n data,\n messageId: this.generateUUID(),\n sessionKey: this.component.sessionKey,\n api: MessagePayloadApi.Component,\n }\n\n const sentMessage = JSON.parse(JSON.stringify(message))\n sentMessage.callback = callback\n this.sentMessages.push(sentMessage)\n\n let postMessagePayload\n\n // Mobile (React Native) requires a string for the postMessage API.\n if (this.isRunningInMobileApplication()) {\n postMessagePayload = JSON.stringify(message)\n } else {\n postMessagePayload = message\n }\n\n Logger.info('Posting message:', postMessagePayload)\n const targetOrigin = this.component.origin && this.component.origin !== 'null' ? this.component.origin : '*'\n this.contentWindow.parent.postMessage(postMessagePayload, targetOrigin)\n }\n\n private activateThemes(incomingUrls: string[] = []) {\n if (!this.component.acceptsThemes) {\n return\n }\n\n Logger.info('Incoming themes:', incomingUrls)\n\n const { activeThemes } = this.component\n\n if (activeThemes && activeThemes.sort().toString() == incomingUrls.sort().toString()) {\n // Incoming theme URLs are same as active, do nothing.\n return\n }\n\n let themesToActivate = incomingUrls\n const themesToDeactivate = []\n\n for (const activeUrl of activeThemes) {\n if (!incomingUrls.includes(activeUrl)) {\n // Active not present in incoming, deactivate it.\n themesToDeactivate.push(activeUrl)\n } else {\n // Already present in active themes, remove it from themesToActivate.\n themesToActivate = themesToActivate.filter((candidate) => {\n return candidate !== activeUrl\n })\n }\n }\n\n Logger.info('Deactivating themes:', themesToDeactivate)\n Logger.info('Activating themes:', themesToActivate)\n\n for (const themeUrl of themesToDeactivate) {\n this.deactivateTheme(themeUrl)\n }\n\n this.component.activeThemes = incomingUrls\n\n for (const themeUrl of themesToActivate) {\n if (!themeUrl) {\n continue\n }\n\n const link = this.contentWindow.document.createElement('link')\n link.id = btoa(themeUrl)\n link.href = themeUrl\n link.type = 'text/css'\n link.rel = 'stylesheet'\n link.media = 'screen,print'\n link.className = 'custom-theme'\n this.contentWindow.document.getElementsByTagName('head')[0].appendChild(link)\n }\n\n this.params.onThemesChange && this.params.onThemesChange()\n }\n\n private themeElementForUrl(themeUrl: string) {\n const elements = Array.from(this.contentWindow.document.getElementsByClassName('custom-theme')).slice()\n return elements.find((element) => {\n // We used to search here by `href`, but on desktop, with local file:// urls, that didn't work for some reason.\n return element.id == btoa(themeUrl)\n })\n }\n\n private deactivateTheme(themeUrl: string) {\n const element = this.themeElementForUrl(themeUrl)\n if (element && element.parentNode) {\n element.setAttribute('disabled', 'true')\n element.parentNode.removeChild(element)\n }\n }\n\n private generateUUID() {\n return generateUuid()\n }\n\n /**\n * Gets the current platform where the component is running.\n */\n public get platform(): string | undefined {\n return this.component.platform\n }\n\n /**\n * Gets the current environment where the component is running.\n */\n public get environment(): string | undefined {\n return this.component.environment\n }\n\n /**\n * Streams a collection of Items, filtered by content type.\n * New items are passed to the callback as they come.\n * @param contentTypes A collection of Content Types.\n * @param callback A callback to process the streamed items.\n */\n public streamItems(contentTypes: ContentType[], callback: (data: any) => void): void {\n this.postMessage(ComponentAction.StreamItems, { content_types: contentTypes }, (data: any) => {\n callback(data.items)\n })\n }\n\n /**\n * Streams the current Item in context.\n * @param callback A callback to process the streamed item.\n */\n public streamContextItem(callback: (data: any) => void): void {\n this.postMessage(ComponentAction.StreamContextItem, {}, (data) => {\n const { item } = data\n /**\n * If this is a new context item than the context item the component was currently entertaining,\n * we want to immediately commit any pending saves, because if you send the new context item to the\n * component before it has commited its presave, it will end up first replacing the UI with new context item,\n * and when the debouncer executes to read the component UI, it will be reading the new UI for the previous item.\n */\n const isNewItem = !this.lastStreamedItem || this.lastStreamedItem.uuid !== item.uuid\n\n if (isNewItem && this.pendingSaveTimeout) {\n clearTimeout(this.pendingSaveTimeout)\n this.performSavingOfItems(this.pendingSaveParams)\n this.pendingSaveTimeout = undefined\n this.pendingSaveParams = undefined\n }\n\n this.lastStreamedItem = item\n callback(this.lastStreamedItem)\n })\n }\n\n /**\n * Creates and stores an Item in the item store.\n * @param item The Item's payload content.\n * @param callback The callback to process the created Item.\n */\n public createItem(item: DecryptedTransferPayload, callback: (data: any) => void): void {\n this.postMessage(ComponentAction.CreateItem, { item: this.jsonObjectForItem(item) }, (data: any) => {\n let { item } = data\n /**\n * A previous version of the SN app had an issue where the item in the reply to ComponentActions.CreateItems\n * would be nested inside \"items\" and not \"item\". So handle both cases here.\n */\n if (!item && data.items && data.items.length > 0) {\n item = data.items[0]\n }\n\n callback && callback(item)\n })\n }\n\n /**\n * Creates and stores a collection of Items in the item store.\n * @param items The Item(s) payload collection.\n * @param callback The callback to process the created Item(s).\n */\n public createItems(items: DecryptedTransferPayload[], callback: (data: any) => void): void {\n const mapped = items.map((item) => this.jsonObjectForItem(item))\n this.postMessage(ComponentAction.CreateItems, { items: mapped }, (data: any) => {\n callback && callback(data.items)\n })\n }\n\n /**\n * Deletes an Item from the item store.\n * @param item The Item to delete.\n * @param callback The callback with the result of the operation.\n */\n public deleteItem(item: DecryptedTransferPayload, callback: (data: OutgoingItemMessagePayload) => void): void {\n this.deleteItems([item], callback)\n }\n\n /**\n * Deletes a collection of Items from the item store.\n * @param items The Item(s) to delete.\n * @param callback The callback with the result of the operation.\n */\n public deleteItems(items: DecryptedTransferPayload[], callback: (data: OutgoingItemMessagePayload) => void): void {\n const params = {\n items: items.map((item) => {\n return this.jsonObjectForItem(item)\n }),\n }\n this.postMessage(ComponentAction.DeleteItems, params, (data) => {\n callback && callback(data)\n })\n }\n\n /**\n * Performs a custom action to the component manager.\n * @param action\n * @param data\n * @param callback The callback with the result of the operation.\n */\n public sendCustomEvent(action: ComponentAction, data: any, callback?: (data: any) => void): void {\n this.postMessage(action, data, (data: any) => {\n callback && callback(data)\n })\n }\n\n /**\n * Saves an existing Item in the item store.\n * @param item An existing Item to be saved.\n * @param callback\n * @param skipDebouncer\n */\n public saveItem(item: DecryptedTransferPayload, callback?: () => void, skipDebouncer = false): void {\n this.saveItems([item], callback, skipDebouncer)\n }\n\n /**\n * Runs a callback before saving an Item.\n * @param item An existing Item to be saved.\n * @param presave Allows clients to perform any actions last second before the save actually occurs (like setting previews).\n * Saves debounce by default, so if a client needs to compute a property on an item before saving, it's best to\n * hook into the debounce cycle so that clients don't have to implement their own debouncing.\n * @param callback\n */\n public saveItemWithPresave(\n item: DecryptedTransferPayload,\n presave: any,\n callback?: () => void,\n ): void {\n this.saveItemsWithPresave([item], presave, callback)\n }\n\n /**\n * Runs a callback before saving a collection of Items.\n * @param items A collection of existing Items to be saved.\n * @param presave Allows clients to perform any actions last second before the save actually occurs (like setting previews).\n * Saves debounce by default, so if a client needs to compute a property on an item before saving, it's best to\n * hook into the debounce cycle so that clients don't have to implement their own debouncing.\n * @param callback\n */\n public saveItemsWithPresave(items: DecryptedTransferPayload[], presave: any, callback?: () => void): void {\n this.saveItems(items, callback, false, presave)\n }\n\n private performSavingOfItems({\n items,\n presave,\n callback,\n }: {\n items: DecryptedTransferPayload[]\n presave: () => void\n callback?: () => void\n }) {\n const ConcernIntervalMS = 5000\n const concernTimeout = setTimeout(() => {\n this.concernTimeouts.forEach((timeout) => clearTimeout(timeout))\n alert(\n 'This editor is unable to communicate with Standard Notes. ' +\n 'Your changes may not be saved. Please backup your changes, then restart the ' +\n 'application and try again.',\n )\n }, ConcernIntervalMS)\n\n this.concernTimeouts.push(concernTimeout)\n\n /**\n * Presave block allows client to gain the benefit of performing something in the debounce cycle.\n */\n presave && presave()\n\n const mappedItems = []\n for (const item of items) {\n mappedItems.push(this.jsonObjectForItem(item))\n }\n\n const wrappedCallback = () => {\n this.concernTimeouts.forEach((timeout) => clearTimeout(timeout))\n callback?.()\n }\n\n this.postMessage(ComponentAction.SaveItems, { items: mappedItems }, wrappedCallback)\n }\n\n /**\n * Saves a collection of existing Items.\n * @param items The items to be saved.\n * @param callback\n * @param skipDebouncer Allows saves to go through right away rather than waiting for timeout.\n * This should be used when saving items via other means besides keystrokes.\n * @param presave\n */\n public saveItems(\n items: DecryptedTransferPayload[],\n callback?: () => void,\n skipDebouncer = false,\n presave?: any,\n ): void {\n /**\n * We need to make sure that when we clear a pending save timeout,\n * we carry over those pending items into the new save.\n */\n if (!this.pendingSaveItems) {\n this.pendingSaveItems = []\n }\n\n if (this.options.coallesedSaving && !skipDebouncer) {\n if (this.pendingSaveTimeout) {\n clearTimeout(this.pendingSaveTimeout)\n }\n\n const incomingIds = items.map((item) => item.uuid)\n /**\n * Replace any existing save items with incoming values.\n * Only keep items here who are not in incomingIds.\n */\n const preexistingItems = this.pendingSaveItems.filter((item) => {\n return !incomingIds.includes(item.uuid)\n })\n\n // Add new items, now that we've made sure it's cleared of incoming items.\n this.pendingSaveItems = preexistingItems.concat(items)\n\n // We'll potentially need to commit early if stream-context-item message comes in.\n this.pendingSaveParams = {\n items: this.pendingSaveItems,\n presave,\n callback,\n }\n\n this.pendingSaveTimeout = setTimeout(() => {\n this.performSavingOfItems(this.pendingSaveParams)\n this.pendingSaveItems = []\n this.pendingSaveTimeout = undefined\n this.pendingSaveParams = null\n }, this.options.coallesedSavingDelay)\n } else {\n this.performSavingOfItems({ items, presave, callback })\n }\n }\n\n /**\n * Sets a new container size for the current component.\n * @param width The new width.\n * @param height The new height.\n */\n public setSize(width: string | number, height: string | number): void {\n this.postMessage(ComponentAction.SetSize, {\n type: 'container',\n width,\n height,\n })\n }\n\n /**\n * Sends the Click mouse event to the Standard Notes parent application.\n */\n private mouseClickEvent(): void {\n this.postMessage(ComponentAction.Click, {})\n }\n\n private jsonObjectForItem(item: DecryptedItem | DecryptedTransferPayload) {\n const copy = Object.assign({}, item) as any\n copy.children = null\n copy.parent = null\n return copy\n }\n\n /**\n * Gets the Item's appData value for the specified key.\n * Uses the default domain (org.standardnotes.sn).\n * This function is used with Items returned from streamContextItem() and streamItems()\n * @param item The Item to get the appData value from.\n * @param key The key to get the value from.\n */\n public getItemAppDataValue(item: OutgoingItemMessagePayload | undefined, key: AppDataField | string): any {\n const defaultDomain = 'org.standardnotes.sn'\n const domainData = item?.content?.appData?.[defaultDomain]\n return domainData?.[key as AppDataField]\n }\n}\n"],"names":["root","factory","exports","module","define","amd","self","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Environment","getRandomValues","rnds8","Uint8Array","rng","crypto","bind","msCrypto","Error","uuid","byteToHex","i","push","toString","substr","options","buf","offset","rnds","random","arr","arguments","length","undefined","toLowerCase","TypeError","environmentToString","environment","map","Web","Desktop","Mobile","noop","Logger","isSupported","window","console","info","this","enabled","log","error","MessagePayloadApi","ComponentAction","ComponentRelay","constructor","params","activeThemes","acceptsThemes","targetWindow","coallesedSaving","coallesedSavingDelay","component","debug","contentWindow","registerMessageHandler","registerKeyboardEventListeners","registerMouseEventListeners","deinit","onReady","messageQueue","sentMessages","lastStreamedItem","pendingSaveItems","pendingSaveTimeout","pendingSaveParams","messageHandler","document","removeEventListener","keyDownEventListener","keyUpEventListener","clickEventListener","event","data","referrer","URL","origin","parsedData","str","result","JSON","parse","type","e","isValidJsonString","action","ComponentRegistered","handleMessage","addEventListener","shouldForwardKeyboardEvent","forwardKeyboardEvent","KeyDown","KeyUp","ctrlKey","metaKey","shiftKey","altKey","postMessage","code","_event","mouseClickEvent","payload","sessionKey","componentData","ActivateThemes","activateThemes","themes","original","originalMessage","filter","message","messageId","extensionName","title","alertMessage","replace","callback","platform","activeThemeUrls","ThemesActivated","getSelfComponentUUID","isRunningInDesktopApplication","isRunningInMobileApplication","getComponentDataValueForKey","setComponentDataValueForKey","value","SetComponentData","clearComponentData","api","Component","SaveItems","height","handleRequestForContentHeight","generateUUID","sentMessage","stringify","postMessagePayload","targetOrigin","parent","incomingUrls","sort","themesToActivate","themesToDeactivate","activeUrl","includes","candidate","themeUrl","deactivateTheme","link","createElement","id","btoa","href","rel","media","className","getElementsByTagName","appendChild","onThemesChange","themeElementForUrl","Array","from","getElementsByClassName","slice","find","element","parentNode","setAttribute","removeChild","uuidv4","streamItems","contentTypes","StreamItems","content_types","items","streamContextItem","StreamContextItem","item","clearTimeout","performSavingOfItems","createItem","CreateItem","jsonObjectForItem","createItems","mapped","CreateItems","deleteItem","deleteItems","DeleteItems","sendCustomEvent","saveItem","skipDebouncer","saveItems","saveItemWithPresave","presave","saveItemsWithPresave","concernTimeout","setTimeout","concernTimeouts","forEach","timeout","alert","mappedItems","incomingIds","preexistingItems","concat","setSize","width","SetSize","Click","copy","assign","children","getItemAppDataValue","domainData","content","appData"],"sourceRoot":""} \ No newline at end of file diff --git a/lib/ComponentRelay.ts b/lib/ComponentRelay.ts index a824140..893d6d7 100644 --- a/lib/ComponentRelay.ts +++ b/lib/ComponentRelay.ts @@ -9,7 +9,6 @@ import type { } from '@standardnotes/snjs' import { environmentToString, generateUuid, isValidJsonString } from './Utils' import Logger from './Logger' -import { KeyboardModifier } from './Types/KeyboardModifier' import { ComponentRelayParams } from './Types/ComponentRelayParams' import { MessagePayload } from './Types/MessagePayload' import { Component } from './Types/Component' @@ -154,36 +153,51 @@ export default class ComponentRelay { private registerKeyboardEventListeners() { this.keyDownEventListener = (event: KeyboardEvent) => { - Logger.info(`A key has been pressed: ${event.key}`) - - if (event.ctrlKey) { - this.keyDownEvent(KeyboardModifier.Ctrl) - } else if (event.shiftKey) { - this.keyDownEvent(KeyboardModifier.Shift) - } else if (event.metaKey || event.key === 'Meta') { - this.keyDownEvent(KeyboardModifier.Meta) + if (!this.shouldForwardKeyboardEvent(event)) { + return } + + Logger.info(`A key has been pressed: ${event.key}`) + this.forwardKeyboardEvent(ComponentAction.KeyDown, event) } this.keyUpEventListener = (event: KeyboardEvent) => { - Logger.info(`A key has been released: ${event.key}`) - - /** - * Checking using event.key instead of the corresponding boolean properties. - */ - if (event.key === 'Control') { - this.keyUpEvent(KeyboardModifier.Ctrl) - } else if (event.key === 'Shift') { - this.keyUpEvent(KeyboardModifier.Shift) - } else if (event.key === 'Meta') { - this.keyUpEvent(KeyboardModifier.Meta) + if (!this.shouldForwardKeyboardEvent(event)) { + return } + + Logger.info(`A key has been released: ${event.key}`) + this.forwardKeyboardEvent(ComponentAction.KeyUp, event) } this.contentWindow.addEventListener('keydown', this.keyDownEventListener, false) this.contentWindow.addEventListener('keyup', this.keyUpEventListener, false) } + /** + * Only forward keyboard events involving shortcut modifiers (Ctrl, Shift, Meta, Alt). + * Plain typing should stay in the editor and not be sent to the parent app. + */ + private shouldForwardKeyboardEvent(event: KeyboardEvent): boolean { + if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) { + return true + } + + // On keyup, modifier flags may already be false when the modifier key itself is released. + return event.key === 'Control' || event.key === 'Shift' || event.key === 'Meta' || event.key === 'Alt' + } + + private forwardKeyboardEvent(action: ComponentAction.KeyDown | ComponentAction.KeyUp, event: KeyboardEvent): void { + this.postMessage(action, { + key: event.key, + code: event.code, + ctrlKey: event.ctrlKey, + metaKey: event.metaKey, + shiftKey: event.shiftKey, + altKey: event.altKey, + } as MessageData) + } + private registerMouseEventListeners() { this.clickEventListener = (_event: MouseEvent) => { Logger.info('A click has been performed.') @@ -716,22 +730,6 @@ export default class ComponentRelay { }) } - /** - * Sends the KeyDown keyboard event to the Standard Notes parent application. - * @param keyboardModifier The keyboard modifier that was pressed. - */ - private keyDownEvent(keyboardModifier: KeyboardModifier): void { - this.postMessage(ComponentAction.KeyDown, { keyboardModifier }) - } - - /** - * Sends the KeyUp keyboard event to the Standard Notes parent application. - * @param keyboardModifier The keyboard modifier that was released. - */ - private keyUpEvent(keyboardModifier: KeyboardModifier): void { - this.postMessage(ComponentAction.KeyUp, { keyboardModifier }) - } - /** * Sends the Click mouse event to the Standard Notes parent application. */