-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: run custom scripts on element events (#62)
- Loading branch information
1 parent
aefa62b
commit 0b3deb8
Showing
14 changed files
with
329 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { convertFileSrc } from '@tauri-apps/api/core'; | ||
import { join, homeDir } from '@tauri-apps/api/path'; | ||
|
||
import { createLogger } from '~/utils'; | ||
import type { ElementContext } from '../element-context.model'; | ||
|
||
const logger = createLogger('script-manager'); | ||
|
||
/** | ||
* Map of module paths to asset paths. | ||
*/ | ||
const assetPathCache: Record<string, string> = {}; | ||
|
||
/** | ||
* Map of asset paths to promises that resolve to the module. | ||
*/ | ||
const moduleCache: Record<string, Promise<any>> = {}; | ||
|
||
/** | ||
* Abstraction over loading and invoking user-defined scripts. | ||
*/ | ||
export function getScriptManager() { | ||
return { | ||
loadScriptForFn, | ||
callFn, | ||
}; | ||
} | ||
|
||
async function loadScriptForFn(fnPath: string): Promise<any> { | ||
const { modulePath } = parseFnPath(fnPath); | ||
return resolveModule(modulePath); | ||
} | ||
|
||
async function callFn( | ||
fnPath: string, | ||
event: Event, | ||
context: ElementContext, | ||
): Promise<any> { | ||
const { modulePath, functionName } = parseFnPath(fnPath); | ||
const foundModule = await resolveModule(modulePath); | ||
const fn = foundModule[functionName]; | ||
|
||
if (!fn) { | ||
throw new Error( | ||
`No function with the name '${functionName}' at function path '${fnPath}'.`, | ||
); | ||
} | ||
|
||
return fn(event, context); | ||
} | ||
|
||
async function resolveModule(modulePath: string): Promise<any> { | ||
const assetPath = await getAssetPath(modulePath); | ||
const foundModule = moduleCache[assetPath]; | ||
|
||
if (foundModule) { | ||
return foundModule; | ||
} | ||
|
||
logger.info(`Loading script at path '${assetPath}'.`); | ||
return (moduleCache[assetPath] = import(assetPath)); | ||
} | ||
|
||
/** | ||
* Converts user-defined path to a URL that can be loaded by the webview. | ||
*/ | ||
async function getAssetPath(modulePath: string): Promise<string> { | ||
const foundAssetPath = assetPathCache[modulePath]; | ||
|
||
if (foundAssetPath) { | ||
return foundAssetPath; | ||
} | ||
|
||
return (assetPathCache[modulePath] = convertFileSrc( | ||
await join(await homeDir(), '.glzr/zebar', modulePath), | ||
)); | ||
} | ||
|
||
function parseFnPath(fnPath: string): { | ||
modulePath: string; | ||
functionName: string; | ||
} { | ||
const [modulePath, functionName] = fnPath.split('#'); | ||
|
||
// Should never been thrown, as the path is validated during config | ||
// deserialization. | ||
if (!modulePath || !functionName) { | ||
throw new Error(`Invalid function path '${fnPath}'.`); | ||
} | ||
|
||
return { modulePath, functionName }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
packages/client-api/src/user-config/window/element-events-config.model.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { z } from 'zod'; | ||
import type { Prettify } from '~/utils'; | ||
|
||
/** | ||
* All available events on HTML elements. | ||
**/ | ||
const HTML_EVENTS = [ | ||
'click', | ||
'fullscreenchange', | ||
'fullscreenerror', | ||
'abort', | ||
'animationcancel', | ||
'animationend', | ||
'animationiteration', | ||
'animationstart', | ||
'auxclick', | ||
'beforeinput', | ||
'blur', | ||
'cancel', | ||
'canplay', | ||
'canplaythrough', | ||
'change', | ||
'close', | ||
'contextmenu', | ||
'copy', | ||
'cuechange', | ||
'cut', | ||
'dblclick', | ||
'drag', | ||
'dragend', | ||
'dragenter', | ||
'dragleave', | ||
'dragover', | ||
'dragstart', | ||
'drop', | ||
'durationchange', | ||
'emptied', | ||
'ended', | ||
'error', | ||
'focus', | ||
'formdata', | ||
'gotpointercapture', | ||
'input', | ||
'invalid', | ||
'keydown', | ||
'keypress', | ||
'keyup', | ||
'load', | ||
'loadeddata', | ||
'loadedmetadata', | ||
'loadstart', | ||
'lostpointercapture', | ||
'mousedown', | ||
'mouseenter', | ||
'mouseleave', | ||
'mousemove', | ||
'mouseout', | ||
'mouseover', | ||
'mouseup', | ||
'paste', | ||
'pause', | ||
'play', | ||
'playing', | ||
'pointercancel', | ||
'pointerdown', | ||
'pointerenter', | ||
'pointerleave', | ||
'pointermove', | ||
'pointerout', | ||
'pointerover', | ||
'pointerup', | ||
'progress', | ||
'ratechange', | ||
'reset', | ||
'resize', | ||
'scroll', | ||
'scrollend', | ||
'securitypolicyviolation', | ||
'seeked', | ||
'seeking', | ||
'select', | ||
'selectionchange', | ||
'selectstart', | ||
'slotchange', | ||
'stalled', | ||
'submit', | ||
'suspend', | ||
'timeupdate', | ||
'toggle', | ||
'touchcancel', | ||
'touchend', | ||
'touchmove', | ||
'touchstart', | ||
'transitioncancel', | ||
'transitionend', | ||
'transitionrun', | ||
'transitionstart', | ||
'volumechange', | ||
'waiting', | ||
'webkitanimationend', | ||
'webkitanimationiteration', | ||
'webkitanimationstart', | ||
'webkittransitionend', | ||
'wheel', | ||
] as const; | ||
|
||
export const ElementEventsConfigSchema = z | ||
.array( | ||
z.object({ | ||
type: z.enum(HTML_EVENTS), | ||
fn_path: z | ||
.string() | ||
.regex( | ||
/^(.+)#([a-zA-Z_$][a-zA-Z0-9_$]*)$/, | ||
"Invalid function path. Needs to be in format 'path/to/my-script.js#functionName'.", | ||
), | ||
selector: z.string().optional(), | ||
}), | ||
) | ||
.default([]); | ||
|
||
export type ElementEventsConfig = Prettify< | ||
z.infer<typeof ElementEventsConfigSchema> | ||
>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.