>(): T {
+ return getEntrypointPreferences()
}
-export interface GeneratedEntrypoint {
+export interface GeneratedCommand {
+ id: string
name: string
- actions: GeneratedEntrypointAction[]
icon?: ArrayBuffer
- accessories?: GeneratedEntrypointAccessory[]
+ fn: () => void
}
-export type GeneratedEntrypointAction = GeneratedEntrypointActionRun | GeneratedEntrypointActionView
-
-export interface GeneratedEntrypointActionRun {
- ref?: string
- label: string
- run: () => void
-}
-
-export interface GeneratedEntrypointActionView {
- ref?: string
- label: string
- view: FC
-}
-
-export type GeneratedEntrypointAccessory = GeneratedEntrypointTextAccessory | GeneratedEntrypointIconAccessory;
-
-export interface GeneratedEntrypointTextAccessory {
- text: string
- icon?: string
- tooltip?: string
-}
-
-export interface GeneratedEntrypointIconAccessory {
- icon: string
- tooltip?: string
-}
-
-export type GeneratorContext = {
- add: (id: string, data: GeneratedEntrypoint) => void,
- remove: (id: string) => void,
- get: (id: string) => GeneratedEntrypoint | undefined
- getAll: () => { [id: string]: GeneratedEntrypoint },
- pluginPreferences: P,
- entrypointPreferences: E,
-};
-
-export type CommandContext
= {
- pluginPreferences: P,
- entrypointPreferences: E,
-};
-
export const Clipboard: Clipboard = {
- read: async function (): Promise<{ "text/plain"?: string | undefined; "image/png"?: ArrayBuffer | undefined; }> {
- const data = await clipboard_read();
+ read: async function (): Promise<{ "text/plain"?: string | undefined; "image/png"?: Blob | undefined; }> {
+ const data = await InternalApi.clipboard_read();
- const result: { "text/plain"?: string; "image/png"?: ArrayBuffer; } = {};
+ const result: { "text/plain"?: string; "image/png"?: Blob; } = {};
if (data.text_data) {
result["text/plain"] = data.text_data;
}
if (data.png_data) {
- result["image/png"] = data.png_data;
+ result["image/png"] = data.png_data; // TODO arraybuffer? fix when migrating to deno's op2
}
return result
},
readText: async function (): Promise {
- return await clipboard_read_text()
+ return await InternalApi.clipboard_read_text()
},
- write: async function (data: { "text/plain"?: string | undefined; "image/png"?: ArrayBuffer | undefined; }): Promise {
+ write: async function (data: { "text/plain"?: string | undefined; "image/png"?: Blob | undefined; }): Promise {
const text_data = data["text/plain"];
const png_data = data["image/png"];
-
- const write_data: { text_data?: string, png_data?: ArrayBuffer } = {};
-
- if (text_data) {
- write_data.text_data = text_data;
- }
-
- if (png_data) {
- write_data.png_data = png_data;
- }
-
- return await clipboard_write(write_data)
+ return await InternalApi.clipboard_write({
+ text_data: text_data,
+ png_data: png_data != undefined ? Array.from(new Uint8Array(png_data as any)) : undefined, // TODO arraybuffer? fix when migrating to deno's op2
+ })
},
writeText: async function (data: string): Promise {
- return await clipboard_write_text(data)
+ return await InternalApi.clipboard_write_text(data)
},
clear: async function (): Promise {
- await clipboard_clear()
+ await InternalApi.clipboard_clear()
}
}
export interface Clipboard {
- read(): Promise<{ ["text/plain"]?: string, ["image/png"]?: ArrayBuffer }>;
+ read(): Promise<{ ["text/plain"]?: string, ["image/png"]?: Blob }>;
readText(): Promise;
- write(data: { ["text/plain"]?: string, ["image/png"]?: ArrayBuffer }): Promise;
+ write(data: { ["text/plain"]?: string, ["image/png"]?: Blob }): Promise;
writeText(data: string): Promise;
clear(): Promise;
}
-
-export const Environment: Environment = {
- get gauntletVersion(): number {
- return environment_gauntlet_version()
- },
- get isDevelopment(): boolean {
- return environment_is_development()
- },
- get pluginDataDir(): string {
- return environment_plugin_data_dir()
- },
- get pluginCacheDir(): string {
- return environment_plugin_cache_dir()
- },
-}
-
-export interface Environment {
- get gauntletVersion(): number;
- get isDevelopment(): boolean;
- get pluginDataDir(): string;
- get pluginCacheDir(): string;
-}
-
diff --git a/js/api/src/hooks.ts b/js/api/src/hooks.ts
index a68d87b..8e6c399 100644
--- a/js/api/src/hooks.ts
+++ b/js/api/src/hooks.ts
@@ -1,6 +1,6 @@
-import { ReactNode, useRef, useId, useState, useCallback, useEffect, MutableRefObject, Dispatch, SetStateAction } from 'react';
+import { ReactNode } from 'react';
// @ts-ignore TODO how to add declaration for this?
-import { useGauntletContext } from "ext:gauntlet/renderer.js";
+import { useGauntletContext } from "gauntlet:renderer";
export function useNavigation(): { popView: () => void, pushView: (component: ReactNode) => void } {
const { popView, pushView }: { popView: () => void, pushView: (component: ReactNode) => void } = useGauntletContext();
@@ -14,389 +14,3 @@ export function useNavigation(): { popView: () => void, pushView: (component: Re
}
}
}
-
-export function usePluginPreferences>(): T {
- const { pluginPreferences }: { pluginPreferences: () => T } = useGauntletContext();
-
- return pluginPreferences()
-}
-
-export function useEntrypointPreferences>(): T {
- const { entrypointPreferences }: { entrypointPreferences: () => T } = useGauntletContext();
-
- return entrypointPreferences()
-}
-
-export type AsyncState = {
- isLoading: boolean;
- error?: unknown;
- data?: T;
-};
-
-export type MutatePromiseFn = (
- asyncUpdate: Promise,
- options?: {
- optimisticUpdate?: (data: T | undefined) => T; // undefined, if options.execute is false and function was never called, needs to be pure
- rollbackOnError?: boolean | ((data: T | undefined) => T); // only used if optimisticUpdate is specified, needs to be pure
- shouldRevalidateAfter?: boolean; // only matters for successful updates
- },
-) => Promise;
-
-export function usePromise(
- fn: (...args: Args) => Promise,
- args?: Args,
- options?: {
- abortable?: MutableRefObject;
- execute?: boolean;
- onError?: (error: unknown) => void;
- onData?: (data: Return) => void;
- onWillExecute?: (...args: Args) => void;
- },
-): AsyncState & {
- revalidate: () => void;
- mutate: MutatePromiseFn;
-} {
- const execute = options?.execute !== false; // execute by default
-
- const [state, setState] = useState>({ isLoading: execute });
-
- return usePromiseInternal(
- fn,
- state,
- setState,
- args || ([] as any),
- execute,
- options?.abortable,
- options?.onError,
- options?.onData,
- options?.onWillExecute
- )
-}
-
-export function useCachedPromise(
- fn: (...args: Args) => Promise,
- args?: Args,
- options?: {
- initialState?: Return | (() => Return),
- abortable?: MutableRefObject;
- execute?: boolean;
- onError?: (error: unknown) => void;
- onData?: (data: Return) => void;
- onWillExecute?: (...args: Args) => void;
- },
-): AsyncState & {
- revalidate: () => void;
- mutate: MutatePromiseFn;
-} {
- const execute = options?.execute !== false; // execute by default
-
- const id = useId();
-
- const { entrypointId }: { entrypointId: () => string } = useGauntletContext();
-
- // same store is fetched and updated between command runs
- const [state, setState] = useCache>("useCachedPromise" + entrypointId() + id, (): AsyncState => {
- const initialState = options?.initialState;
- if (initialState) {
- if (initialState instanceof Function) {
- return { isLoading: execute, data: initialState() }
- } else {
- return { isLoading: execute, data: initialState }
- }
- } else {
- return { isLoading: execute }
- }
- });
-
- return usePromiseInternal(
- fn,
- state,
- setState,
- args || ([] as any),
- execute,
- options?.abortable,
- options?.onError,
- options?.onData,
- options?.onWillExecute
- )
-}
-
-function usePromiseInternal(
- fn: (...args: Args) => Promise,
- state: AsyncState,
- setState: Dispatch>>,
- args: Args,
- execute: boolean,
- abortable?: MutableRefObject,
- onError?: (error: unknown) => void,
- onData?: (data: Return) => void,
- onWillExecute?: (...args: Args) => void,
-): AsyncState & {
- revalidate: () => void; // will execute even if options.execute is false
- mutate: MutatePromiseFn; // will execute even if options.execute is false
-} {
-
- const promiseRef = useRef>();
-
- useEffect(() => {
- return () => {
- abortable?.current?.abort();
- };
- }, [abortable]);
-
- const callback = useCallback(async (...args: Args): Promise => {
- if (abortable) {
- abortable.current?.abort();
- abortable.current = new AbortController()
- }
-
- onWillExecute?.(...args);
-
- const promise = fn(...args);
-
- setState(prevState => ({ ...prevState, isLoading: true }));
-
- promiseRef.current = promise;
-
- let promiseResult: Return;
- try {
- promiseResult = await promise;
- } catch (error) {
- // We dont want to handle result/error of non-latest function
- // this approach helps to avoid race conditions
- if (promise === promiseRef.current) {
- setState({ error, isLoading: false })
-
- if (abortable) {
- abortable.current = undefined;
- }
-
- console.error("Error happened when executing promise: ", error)
-
- onError?.(error);
- }
- return
- }
-
- // We dont want to handle result/error of non-latest function
- // this approach helps to avoid race conditions
- if (promise === promiseRef.current) {
- setState({ data: promiseResult, isLoading: false });
-
- if (abortable) {
- abortable.current = undefined;
- }
-
- onData?.(promiseResult)
- }
- }, args);
-
- useEffect(() => {
- if (execute) {
- callback(...args);
- }
- }, [callback, execute]);
-
- return {
- revalidate: () => {
- callback(...args);
- },
- mutate: async (
- asyncUpdate: Promise,
- options?: {
- optimisticUpdate?: (data: Return | undefined) => Return;
- rollbackOnError?: boolean | ((data: Return | undefined) => Return);
- shouldRevalidateAfter?: boolean;
- },
- ): Promise => {
- const prevData = state.data;
-
- const optimisticUpdate = options?.optimisticUpdate;
- const rollbackOnError = options?.rollbackOnError;
- const shouldRevalidateAfter = options?.shouldRevalidateAfter !== false;
-
- if (optimisticUpdate) {
- const newData = optimisticUpdate(state.data);
- setState({ data: newData, isLoading: true })
-
- try {
- const asyncUpdateResult = await asyncUpdate;
-
- if (shouldRevalidateAfter) {
- callback(...args);
- } else {
- // set loading false, only when not revalidating, because revalidate will unset it itself
- setState(prevState => ({ ...prevState, isLoading: false }));
- }
-
- return asyncUpdateResult
- } catch (e) {
- switch (typeof rollbackOnError) {
- case "undefined": {
- setState({ data: prevData, isLoading: false })
- break;
- }
- case "boolean": {
- if (rollbackOnError) {
- setState({ data: prevData, isLoading: false })
- }
- break;
- }
- case "function": {
- const rolledBackData = rollbackOnError(state.data);
- setState({ data: rolledBackData, isLoading: false })
- break;
- }
- }
-
- throw e
- }
- } else {
- setState(prevState => ({ ...prevState, isLoading: true }));
-
- const asyncUpdateResult = await asyncUpdate;
-
- if (shouldRevalidateAfter) {
- callback(...args);
- } else {
- // set loading false, only when not revalidating, because revalidate will unset it itself
- setState(prevState => ({ ...prevState, isLoading: false }));
- }
-
- return asyncUpdateResult
- }
- },
- ...state
- };
-}
-
-// persistent, uses localStorage under the hood
-export function useStorage(key: string, initialState: T | (() => T)): [T, Dispatch>] {
- return useWebStorage(key, initialState, localStorage)
-}
-
-// ephemeral, uses sessionStorage under the hood
-export function useCache(key: string, initialState: T | (() => T)): [T, Dispatch>] {
- return useWebStorage(key, initialState, sessionStorage)
-}
-
-// keys are shared per plugin, across all entrypoints
-// uses JSON.serialize
-function useWebStorage(
- key: string,
- initialState: T | (() => T),
- storageObject: Storage
-): [T, Dispatch>] {
-
- const [value, setValue] = useState(() => {
- const jsonValue = storageObject.getItem(key)
-
- if (jsonValue != null) {
- return JSON.parse(jsonValue) as T
- }
-
- if (initialState instanceof Function) {
- return initialState()
- } else {
- return initialState
- }
- })
-
- useEffect(() => {
- if (value === undefined) {
- storageObject.removeItem(key)
- return
- }
- storageObject.setItem(key, JSON.stringify(value))
- }, [key, value, storageObject])
-
- return [value, setValue]
-}
-
-export function useFetch(
- url: RequestInfo | URL,
- options?: {
- request?: RequestInit,
- parse?: (response: Response) => T | Promise;
- initialState?: T | (() => T),
- execute?: boolean;
- onError?: (error: unknown) => void;
- onData?: (data: T) => void;
- onWillExecute?: (input: RequestInfo | URL, init?: RequestInit) => void;
- },
-): AsyncState & {
- revalidate: () => void;
- mutate: MutatePromiseFn;
-};
-export function useFetch(
- url: RequestInfo | URL,
- options: {
- request?: RequestInit,
- parse?: (response: Response) => V | Promise;
- map: (result: V) => T | Promise;
- initialState?: T | (() => T),
- execute?: boolean;
- onError?: (error: unknown) => void;
- onData?: (data: T) => void;
- onWillExecute?: (input: RequestInfo | URL, init?: RequestInit) => void;
- },
-): AsyncState & {
- revalidate: () => void;
- mutate: MutatePromiseFn;
-};
-export function useFetch(
- url: RequestInfo | URL,
- options?: {
- request?: RequestInit,
- parse?: (response: Response) => V | Promise;
- map?: (result: V) => T | Promise;
- initialState?: T | V | (() => T | V),
- execute?: boolean;
- onError?: (error: unknown) => void;
- onData?: (data: T | V) => void;
- onWillExecute?: (input: RequestInfo | URL, init?: RequestInit) => void;
- },
-): AsyncState & {
- revalidate: () => void;
- mutate: MutatePromiseFn;
-} {
- const abortable = useRef();
-
- return useCachedPromise(
- async (inputParam: RequestInfo | URL): Promise => {
- const response = await fetch(inputParam, { ...options?.request, signal: abortable.current?.signal });
-
- if (options?.parse) {
- const parsed: V = await options?.parse(response)
-
- if (options?.map) {
- return options?.map(parsed)
- } else {
- return parsed
- }
- } else {
- const content = response.headers.get("content-type");
- if (!content || !content.includes("application/json")) {
- throw new Error("Content-Type is not 'application/json', please specify custom options.parse")
- }
-
- const parsed: V = await response.json()
-
- if (options?.map) {
- return options?.map(parsed)
- } else {
- return parsed
- }
- }
- },
- [url],
- {
- initialState: options?.initialState,
- abortable,
- execute: options?.execute,
- onError: options?.onError,
- onData: options?.onData,
- onWillExecute: options?.onWillExecute,
- }
- )
-}
diff --git a/js/api_build/package.json b/js/api_build/package.json
index 6d732f8..813d333 100644
--- a/js/api_build/package.json
+++ b/js/api_build/package.json
@@ -4,12 +4,12 @@
"type": "module",
"scripts": {
"build": "npm run generate-json && npm run build-generator && npm run run-generator",
- "generate-json": "cd ../.. && cargo run --package gauntlet-component-model -- ./js/api_build/component_model.json",
+ "generate-json": "cd ../.. && cargo run --package component_model -- ./js/api_build/component_model.json",
"build-generator": "tsc",
"run-generator": "node dist/index.js"
},
"devDependencies": {
- "@types/node": "^22.10.2",
- "typescript": "^5.7.2"
+ "@types/node": "^18.17.1",
+ "typescript": "^5.3.3"
}
}
diff --git a/js/api_build/src/index.ts b/js/api_build/src/index.ts
index dae197c..7322513 100644
--- a/js/api_build/src/index.ts
+++ b/js/api_build/src/index.ts
@@ -241,6 +241,31 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
const root = modelInput.find((component): component is RootComponent => component.type === "root");
if (root != null) {
+ // image special case
+ // export type ImageSource = { asset: string } | { url: string };
+
+ const imageSourceDeclaration = ts.factory.createTypeAliasDeclaration(
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
+ ts.factory.createIdentifier("ImageSource"),
+ undefined,
+ ts.factory.createUnionTypeNode([
+ ts.factory.createTypeLiteralNode([ts.factory.createPropertySignature(
+ undefined,
+ ts.factory.createIdentifier("asset"),
+ undefined,
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
+ )]),
+ ts.factory.createTypeLiteralNode([ts.factory.createPropertySignature(
+ undefined,
+ ts.factory.createIdentifier("url"),
+ undefined,
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
+ )])
+ ])
+ );
+
+ publicDeclarations.push(imageSourceDeclaration)
+
for (const [name, sharedType] of Object.entries(root.sharedTypes)) {
switch (sharedType.type) {
@@ -270,7 +295,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
undefined,
ts.factory.createIdentifier(propName),
undefined,
- makeType(type, "no")
+ makeType(type)
)
})
)
@@ -279,19 +304,6 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
publicDeclarations.push(declaration)
break;
}
- case "union": {
- const declaration = ts.factory.createTypeAliasDeclaration(
- [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
- ts.factory.createIdentifier(name),
- undefined,
- ts.factory.createUnionTypeNode(
- sharedType.items.map(type => makeType(type, "no"))
- )
- )
-
- publicDeclarations.push(declaration)
- break;
- }
default: {
throw new Error("unreachable");
}
@@ -342,7 +354,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
const properties = component.props
.map(prop => {
- if (!isInProperty(prop.type)) {
+ if (prop.type.type === "component") {
return null
}
@@ -360,23 +372,22 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
.filter((prop): prop is ts.JsxAttribute => prop != null);
const children = []
-
- const componentProps = component.props.filter(prop => !isInProperty(prop.type));
- if (componentProps.length !== 0) {
- children.push(
- ...componentProps.map(prop => (
- ts.factory.createAsExpression(
- ts.factory.createPropertyAccessExpression(
- ts.factory.createIdentifier("props"),
- ts.factory.createIdentifier(prop.name)
- ),
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
- )
- ))
- );
- }
-
if (component.children.type != "none") {
+ const componentProps = component.props.filter(prop => prop.type.type === "component");
+ if (componentProps.length !== 0) {
+ children.push(
+ ...componentProps.map(prop => (
+ ts.factory.createAsExpression(
+ ts.factory.createPropertyAccessExpression(
+ ts.factory.createIdentifier("props"),
+ ts.factory.createIdentifier(prop.name)
+ ),
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
+ )
+ ))
+ );
+ }
+
children.push(ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier("props"),
ts.factory.createIdentifier("children")
@@ -395,11 +406,10 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
let componentType: ts.TypeReferenceNode | ts.IntersectionTypeNode;
if (component.children.type == "members" || component.children.type == "string_or_members") {
- const members = { ...component.children.ordered_members, ...component.children.per_type_members }
componentType = ts.factory.createIntersectionTypeNode([
componentFCType,
ts.factory.createTypeLiteralNode(
- Object.entries(members).map(([memberName, member]) => {
+ Object.entries(component.children.members).map(([memberName, member]) => {
return ts.factory.createPropertySignature(
undefined,
ts.factory.createIdentifier(memberName),
@@ -421,8 +431,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
switch (component.children.type) {
case "string_or_members":
case "members": {
- const members = { ...component.children.ordered_members, ...component.children.per_type_members }
- memberAssignments = Object.entries(members).map(([memberName, member]) => {
+ memberAssignments = Object.entries(component.children.members).map(([memberName, member]) => {
return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier(component.name),
@@ -536,26 +545,22 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
function makePropertyTypes(component: StandardComponent, componentPropsInChildren: boolean): ts.TypeElement[] {
const props = component.props
- .filter(property => !isInProperty(property.type) ? !componentPropsInChildren : true)
+ .filter(property => property.type.type === "component" ? !componentPropsInChildren : true)
.map(property => {
return ts.factory.createPropertySignature(
undefined,
ts.factory.createIdentifier(property.name),
- property.optional == "no" ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
- makeType(property.type, property.optional)
+ !property.optional ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
+ makeType(property.type)
)
});
- let additionalComponentRefs: ComponentRef[];
- if (componentPropsInChildren) {
- additionalComponentRefs = component.props
- .map(property => property.type)
- .flatMap(type => collectAllComponentRefs(type));
- } else {
- additionalComponentRefs = [];
- }
+ const additionalComponentRefs = component.props
+ .map(property => property.type)
+ .filter((type): type is TypeComponent => componentPropsInChildren && type.type === "component")
+ .map(type => type.reference)
- if (component.children.type != "none" || additionalComponentRefs.length > 0) {
+ if (component.children.type != "none") {
props.unshift(ts.factory.createPropertySignature(
undefined,
ts.factory.createIdentifier("children"),
@@ -571,13 +576,11 @@ function makePropertyTypes(component: StandardComponent, componentPropsInChildre
function makeChildrenType(type: Children, additionalComponentRefs: ComponentRef[]): ts.TypeNode {
switch (type.type) {
case "members": {
- const members = { ...type.ordered_members, ...type.per_type_members }
-
return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier("ElementComponent"),
[
ts.factory.createUnionTypeNode(
- [...additionalComponentRefs, ...Object.values(members)].map(member => (
+ [...additionalComponentRefs, ...Object.values(type.members)].map(member => (
ts.factory.createTypeQueryNode(
ts.factory.createIdentifier(member.componentName),
undefined
@@ -588,13 +591,11 @@ function makeChildrenType(type: Children, additionalComponentRefs: ComponentRef[
)
}
case "string_or_members": {
- const members = { ...type.ordered_members, ...type.ordered_members }
-
return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier("StringOrElementComponent"),
[
ts.factory.createUnionTypeNode(
- [...additionalComponentRefs, ...Object.values(members)].map(member => (
+ [...additionalComponentRefs, ...Object.values(type.members)].map(member => (
ts.factory.createTypeQueryNode(
ts.factory.createIdentifier(member.componentName),
undefined
@@ -611,68 +612,43 @@ function makeChildrenType(type: Children, additionalComponentRefs: ComponentRef[
)
}
case "none": {
- if (additionalComponentRefs.length > 0) {
- return ts.factory.createTypeReferenceNode(
- ts.factory.createIdentifier("ElementComponent"),
- [
- ts.factory.createUnionTypeNode(
- additionalComponentRefs.map(member => (
- ts.factory.createTypeQueryNode(
- ts.factory.createIdentifier(member.componentName),
- undefined
- )
- ))
- )
- ]
- )
- } else {
- throw new Error("Cannot construct none children")
- }
+ throw new Error("Cannot construct none children")
}
}
}
-function makeType(type: PropertyType, optional: Property["optional"]): ts.TypeNode {
- let result: ts.TypeNode
+function makeType(type: PropertyType): ts.TypeNode {
switch (type.type) {
case "boolean": {
- result = ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword)
- break;
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword)
}
case "number": {
- result = ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
- break;
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
}
case "string": {
- result = ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
- break;
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
}
case "function": {
const params = type.arguments.map(arg => {
- if (arg.optional != "no" && arg.optional != "yes") {
- throw new Error("following optional type is not supported here: " + arg.optional)
- }
-
return ts.factory.createParameterDeclaration(
undefined,
undefined,
ts.factory.createIdentifier(arg.name),
undefined,
- arg.optional == "no" ? makeType(arg.type, "no") : ts.factory.createUnionTypeNode([makeType(arg.type, arg.optional), ts.factory.createLiteralTypeNode(ts.factory.createNull())]),
+ !arg.optional ? makeType(arg.type) : ts.factory.createUnionTypeNode([makeType(arg.type), ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)]),
undefined
)
});
- result = ts.factory.createFunctionTypeNode(
+ return ts.factory.createFunctionTypeNode(
undefined,
params,
ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)
)
- break;
}
case "component": {
- result = ts.factory.createTypeReferenceNode(
+ return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier("ElementComponent"),
[
ts.factory.createTypeQueryNode(
@@ -681,103 +657,33 @@ function makeType(type: PropertyType, optional: Property["optional"]): ts.TypeNo
)
]
)
- break;
}
- case "array": {
- result = ts.factory.createArrayTypeNode(makeType(type.item, "no"))
- break;
+ case "image_source": {
+ return ts.factory.createTypeReferenceNode(
+ ts.factory.createIdentifier("ImageSource"),
+ undefined
+ )
}
- case "shared_type_ref": {
- result = ts.factory.createTypeReferenceNode(
+ case "enum": {
+ return ts.factory.createTypeReferenceNode(
+ ts.factory.createIdentifier(type.name),
+ undefined
+ )
+ }
+ case "object": {
+ return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier(type.name),
undefined
)
- break;
}
case "union": {
- result = ts.factory.createUnionTypeNode(type.items.map(value => makeType(value, "no")))
- break
+ return ts.factory.createUnionTypeNode(type.items.map(value => makeType(value)))
}
default: {
throw new Error(`unsupported type ${JSON.stringify(type)}`)
}
}
- if (optional == "yes_but_complicated") {
- return ts.factory.createUnionTypeNode([result, ts.factory.createLiteralTypeNode(ts.factory.createNull())]);
- } else {
- return result
- }
-}
-
-function isInProperty(propertyType: PropertyType) {
- switch (propertyType.type) {
- case "boolean": {
- return true
- }
- case "number": {
- return true
- }
- case "string": {
- return true
- }
- case "function": {
- return true // different from the rust side
- }
- case "component": {
- return false
- }
- case "array": {
- return isInProperty(propertyType.item)
- }
- case "shared_type_ref": {
- return true
- }
- case "union": {
- if (propertyType.items.every(value => isInProperty(value))) {
- return true
- } else if (propertyType.items.every(value => !isInProperty(value))) {
- return false
- } else {
- throw new Error("")
- }
- }
- default: {
- throw new Error(`unsupported type ${JSON.stringify(propertyType)}`)
- }
- }
-}
-
-function collectAllComponentRefs(propertyType: PropertyType): ComponentRef[] {
- switch (propertyType.type) {
- case "boolean": {
- return []
- }
- case "number": {
- return []
- }
- case "string": {
- return []
- }
- case "function": {
- return []
- }
- case "component": {
- return [propertyType.reference]
- }
- case "array": {
- return collectAllComponentRefs(propertyType.item)
- }
- case "shared_type_ref": {
- return []
- }
- case "union": {
- return propertyType.items.flatMap(value => collectAllComponentRefs(value))
- }
- default: {
- throw new Error(`unsupported type ${JSON.stringify(propertyType)}`)
- }
- }
}
const genDir = "../api/src/gen";
@@ -785,4 +691,4 @@ if (!existsSync(genDir)) {
mkdirSync(genDir);
}
-generate("./component_model.json", `${genDir}/components.tsx`)
+generate("./component_model.json", `${genDir}/components.tsx`)
\ No newline at end of file
diff --git a/js/bridge_build/.gitignore b/js/bridge_build/.gitignore
deleted file mode 100644
index d77bc98..0000000
--- a/js/bridge_build/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-component_model.json
-dist
\ No newline at end of file
diff --git a/js/bridge_build/package.json b/js/bridge_build/package.json
deleted file mode 100644
index dfbc31d..0000000
--- a/js/bridge_build/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "@project-gauntlet/bridge-build",
- "version": "0.1.0",
- "type": "module",
- "scripts": {
- "build": "npm run build-generator && npm run run-generator",
- "build-generator": "tsc",
- "run-generator": "node dist/index.js"
- },
- "devDependencies": {
- "@types/node": "^22.10.2",
- "typescript": "^5.7.2"
- }
-}
diff --git a/js/bridge_build/src/index.ts b/js/bridge_build/src/index.ts
deleted file mode 100644
index 111bd0d..0000000
--- a/js/bridge_build/src/index.ts
+++ /dev/null
@@ -1,218 +0,0 @@
-import ts, {
- ExpressionStatement,
- ImportDeclaration,
- isExportDeclaration,
- isExportSpecifier,
- isIdentifier,
- isNamedExports,
- ScriptKind,
- Statement
-} from "typescript";
-import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
-
-
-function generate(outFile: string, sourceFile: ts.SourceFile) {
- const resultFile = ts.createSourceFile("unused", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.JS);
- const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
-
- const result = printer.printNode(ts.EmitHint.Unspecified, sourceFile, resultFile);
-
- writeFileSync(outFile, result)
-}
-
-function collectExports(inputFile: string): string[] {
- const sourceFile = ts.createSourceFile(
- "input.js",
- readFileSync(inputFile).toString(),
- ts.ScriptTarget.ESNext,
- /*setParentNodes */ false,
- ScriptKind.JS
- );
-
- const result: string[] = []
-
- for (const statement of sourceFile.statements) {
- if (isExportDeclaration(statement)) {
- const exportClause = statement.exportClause;
- if (exportClause) {
- if (isNamedExports(exportClause)) {
- for (const element of exportClause.elements) {
- if (isExportSpecifier(element)) {
- if (isIdentifier(element.name)) {
- if (typeof element.name.escapedText === "string") {
- if (element.name.escapedText.startsWith("___")) {
- result.push(element.name.escapedText.slice(1)) // remove one _, typescript special case
- } else {
- result.push(element.name.escapedText)
- }
- } else {
- throw new Error(`unexpected export clause element element name type: ${JSON.stringify(element)}`)
- }
- } else {
- throw new Error(`unknown export clause element element name type: ${JSON.stringify(element)}`)
- }
- } else {
- throw new Error(`unknown export clause element: ${JSON.stringify(element)}`)
- }
- }
- } else {
- throw new Error(`unknown export clause: ${JSON.stringify(exportClause)}`)
- }
- }
- }
- }
-
- return result
-}
-
-
-function generateInternal(exportConfig: Record): ts.SourceFile {
-
- function createImport(exports: string[], namespace:string, importString: string): ImportDeclaration {
- return ts.factory.createImportDeclaration(
- undefined,
- ts.factory.createImportClause(
- false,
- undefined,
- ts.factory.createNamedImports(exports.map(value => {
- return ts.factory.createImportSpecifier(
- false,
- ts.factory.createIdentifier(value),
- ts.factory.createIdentifier(`${namespace}_${value}`)
- )
- }))
- ),
- ts.factory.createStringLiteral(importString),
- undefined
- )
- }
-
- const initialDeclarations: Statement[] = Object.entries(exportConfig)
- .map(([namespace, { importUrl, exports }]) => createImport(exports, namespace, importUrl));
-
- function createAssignment(namespace: string, variableName: string): ExpressionStatement {
- return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
- ts.factory.createPropertyAccessExpression(
- ts.factory.createIdentifier("globalThis"),
- ts.factory.createIdentifier(`${namespace}_${variableName}`)
- ),
- ts.factory.createToken(ts.SyntaxKind.EqualsToken),
- ts.factory.createIdentifier(`${namespace}_${variableName}`)
- ))
- }
-
- const assignments: Statement[] = Object.entries(exportConfig)
- .flatMap(([namespace, { exports }]) => exports.map(value => createAssignment(namespace, value)));
-
- return ts.factory.createSourceFile(
- [
- ...initialDeclarations,
- ...assignments,
- ],
- ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
- ts.NodeFlags.None
- )
-}
-
-function generateExternal(namespace: string, exports: string[]): ts.SourceFile {
-
- const assignments = exports.map(value => {
-
- return ts.factory.createVariableStatement(
- undefined,
- ts.factory.createVariableDeclarationList(
- [ts.factory.createVariableDeclaration(
- ts.factory.createIdentifier(`${namespace}_${value}`),
- undefined,
- undefined,
- ts.factory.createPropertyAccessExpression(
- ts.factory.createIdentifier("globalThis"),
- ts.factory.createIdentifier(`${namespace}_${value}`)
- )
- )],
- ts.NodeFlags.Const
- )
- );
- });
-
- const exportDeclaration = ts.factory.createExportDeclaration(
- undefined,
- false,
- ts.factory.createNamedExports(exports.map(value => {
- return ts.factory.createExportSpecifier(
- false,
- ts.factory.createIdentifier(`${namespace}_${value}`),
- ts.factory.createIdentifier(value)
- )
- })),
- undefined,
- undefined
- );
-
- return ts.factory.createSourceFile(
- [...assignments, exportDeclaration],
- ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
- ts.NodeFlags.None
- )
-}
-
-const outDir = `./dist`;
-
-if (!existsSync(outDir)) {
- mkdirSync(outDir);
-}
-
-const componentExports = collectExports(`../api/dist/gen/components.js`);
-const helpersExports = collectExports(`../api/dist/helpers.js`);
-const hooksExports = collectExports(`../api/dist/hooks.js`);
-const coreExports = collectExports(`../core/dist/core.js`);
-
-// prod bundle exports are identical and hopefully it stays like this in future
-const reactExports = collectExports(`../react/dist/dev/react.development.js`);
-const reactJsxRuntimeExports = collectExports(`../react/dist/dev/react-jsx-runtime.development.js`);
-
-const internalAllExports = collectExports(`../core/dist/internal-all.js`);
-const internalLinuxExports = collectExports(`../core/dist/internal-linux.js`);
-const internalMacosExports = collectExports(`../core/dist/internal-macos.js`);
-const internalWindowsExports = collectExports(`../core/dist/internal-windows.js`);
-
-generate(
- `${outDir}/bridge-bootstrap.js`,
- generateInternal({
- "GauntletComponents": { importUrl: "ext:gauntlet/api/components.js", exports: componentExports },
- "GauntletHelpers": { importUrl: "ext:gauntlet/api/helpers.js", exports: helpersExports },
- "GauntletHooks": { importUrl: "ext:gauntlet/api/hooks.js", exports: hooksExports },
- "GauntletCore": { importUrl: "ext:gauntlet/core.js", exports: coreExports },
- "GauntletReact": { importUrl: "ext:gauntlet/react.js", exports: reactExports },
- "GauntletReactJsxRuntime": { importUrl: "ext:gauntlet/react-jsx-runtime.js", exports: reactJsxRuntimeExports },
- })
-)
-
-generate(`${outDir}/bridge-internal-all-bootstrap.js`, generateInternal({
- "GauntletInternalAll": { importUrl: "ext:gauntlet/internal-all.js", exports: internalAllExports }
-}))
-generate(`${outDir}/bridge-internal-linux-bootstrap.js`, generateInternal({
- "GauntletInternalLinux": { importUrl: "ext:gauntlet/internal-linux.js", exports: internalLinuxExports }
-}))
-generate(`${outDir}/bridge-internal-macos-bootstrap.js`, generateInternal({
- "GauntletInternalMacos": { importUrl: "ext:gauntlet/internal-macos.js", exports: internalMacosExports }
-}))
-
-generate(`${outDir}/bridge-internal-windows-bootstrap.js`, generateInternal({
- "GauntletInternalWindows": { importUrl: "ext:gauntlet/internal-windows.js", exports: internalWindowsExports }
-}))
-
-
-generate(`${outDir}/bridge-components.js`, generateExternal("GauntletComponents", componentExports))
-generate(`${outDir}/bridge-helpers.js`, generateExternal("GauntletHelpers", helpersExports))
-generate(`${outDir}/bridge-hooks.js`, generateExternal("GauntletHooks", hooksExports))
-generate(`${outDir}/bridge-core.js`, generateExternal("GauntletCore", coreExports))
-generate(`${outDir}/bridge-react.js`, generateExternal("GauntletReact", reactExports))
-generate(`${outDir}/bridge-react-jsx-runtime.js`, generateExternal("GauntletReactJsxRuntime", reactJsxRuntimeExports))
-
-generate(`${outDir}/bridge-internal-all.js`, generateExternal("GauntletInternalAll", internalAllExports))
-generate(`${outDir}/bridge-internal-linux.js`, generateExternal("GauntletInternalLinux", internalLinuxExports))
-generate(`${outDir}/bridge-internal-macos.js`, generateExternal("GauntletInternalMacos", internalMacosExports))
-generate(`${outDir}/bridge-internal-windows.js`, generateExternal("GauntletInternalWindows", internalWindowsExports))
-
-
diff --git a/js/bridge_build/tsconfig.json b/js/bridge_build/tsconfig.json
deleted file mode 100644
index 1433d49..0000000
--- a/js/bridge_build/tsconfig.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "compilerOptions": {
- "strict": true,
- "module": "ES2022",
- "esModuleInterop": true,
- "target": "ES2022",
- "moduleResolution": "bundler",
- "jsx": "react",
- "types": ["@types/node"],
- "outDir": "./dist"
- },
- "lib": ["ES2020"],
- "include": ["./src"]
-}
\ No newline at end of file
diff --git a/js/build/package.json b/js/build/package.json
index 2b7b24e..0828044 100644
--- a/js/build/package.json
+++ b/js/build/package.json
@@ -13,19 +13,19 @@
},
"type": "module",
"dependencies": {
- "@actions/core": "^1.11.1",
- "commander": "^12.1.0",
- "octokit": "^4.0.2",
- "simple-git": "^3.27.0",
- "cross-spawn": "^7.0.6"
+ "@actions/core": "^1.10.1",
+ "commander": "^11.1.0",
+ "octokit": "^3.1.2",
+ "simple-git": "^3.22.0",
+ "cross-spawn": "^7.0.3"
},
"devDependencies": {
- "@rollup/plugin-commonjs": "^28.0.2",
- "@rollup/plugin-node-resolve": "^16.0.0",
- "@rollup/plugin-typescript": "^12.1.2",
- "@types/node": "^22.10.2",
+ "@rollup/plugin-commonjs": "^25.0.7",
+ "@rollup/plugin-node-resolve": "^15.2.3",
+ "@rollup/plugin-typescript": "^11.1.5",
+ "@types/node": "^18.17.1",
"@types/cross-spawn": "^6.0.6",
- "tslib": "^2.8.1",
- "typescript": "^5.7.2"
+ "tslib": "^2.6.2",
+ "typescript": "^5.3.3"
}
}
diff --git a/js/build/src/main.ts b/js/build/src/main.ts
index 28d0a37..9569bba 100644
--- a/js/build/src/main.ts
+++ b/js/build/src/main.ts
@@ -6,7 +6,7 @@ import { Octokit } from 'octokit';
import { sync as spawnSync } from "cross-spawn";
import path from "node:path";
import { mkdirSync, readFileSync } from "fs";
-import { copyFileSync, existsSync, rmdirSync, writeFileSync } from "node:fs";
+import { copyFileSync, writeFileSync } from "node:fs";
import * as core from '@actions/core';
import { SpawnSyncOptions } from "child_process";
@@ -58,10 +58,10 @@ program.command('build-windows')
await program.parseAsync(process.argv);
-async function doBuild(projectRoot: string, arch: string, profile: string) {
+async function doBuild(projectRoot: string, arch: string) {
console.log("Building Gauntlet...")
- build(projectRoot, arch, profile)
+ build(projectRoot, arch)
}
async function doPublishInit() {
@@ -85,18 +85,15 @@ async function doPublishInit() {
}
async function doPublishLinux() {
+ console.log("Publishing Gauntlet... Linux...")
+
const projectRoot = getProjectRoot()
- const git = simpleGit(projectRoot);
-
- console.log("git pull...")
- await git.pull()
-
const arch = 'x86_64-unknown-linux-gnu';
- const profile = 'release-size';
- build(projectRoot, arch, profile)
- const { fileName, filePath } = packageForLinux(projectRoot, arch, profile)
+ build(projectRoot, arch)
+
+ const { fileName, filePath } = packageForLinux(projectRoot, arch)
await addFileToRelease(filePath, fileName)
}
@@ -104,72 +101,39 @@ async function doPublishLinux() {
async function doBuildLinux() {
const arch = 'x86_64-unknown-linux-gnu';
const projectRoot = getProjectRoot();
- const profile = 'release';
- await doBuild(projectRoot, arch, profile)
- packageForLinux(projectRoot, arch, profile)
+ await doBuild(projectRoot, arch)
+ packageForLinux(projectRoot, arch)
}
async function doPublishMacOS() {
const projectRoot = getProjectRoot();
- const git = simpleGit(projectRoot);
+ const arch = 'aarch64-apple-darwin';
- console.log("git pull...")
- await git.pull()
+ build(projectRoot, arch)
- const archArm = 'aarch64-apple-darwin';
- const archIntel = 'x86_64-apple-darwin';
- const profile = 'release-size';
-
- buildJs(projectRoot)
- buildRust(projectRoot, archArm, profile)
- buildRust(projectRoot, archIntel, profile)
-
- const { fileName, filePath } = await packageForMacos(
- projectRoot,
- [archArm, archIntel],
- profile,
- true,
- true
- )
+ const { fileName, filePath } = await packageForMacos(projectRoot, arch, true, true)
await addFileToRelease(filePath, fileName)
}
async function doBuildMacOS() {
const projectRoot = getProjectRoot();
- const archArm = 'aarch64-apple-darwin';
- const archIntel = 'x86_64-apple-darwin';
- const profile = 'release';
+ const arch = 'aarch64-apple-darwin';
- buildJs(projectRoot)
- buildRust(projectRoot, archArm, profile)
- buildRust(projectRoot, archIntel, profile)
-
- await packageForMacos(
- projectRoot,
- [archArm, archIntel],
- profile,
- false,
- false
- )
+ await doBuild(projectRoot, arch)
+ await packageForMacos(projectRoot, arch, true, false)
}
async function doPublishWindows() {
const projectRoot = getProjectRoot();
- const git = simpleGit(projectRoot);
-
- console.log("git pull...")
- await git.pull()
-
const arch = 'x86_64-pc-windows-msvc';
- const profile = 'release-size';
- build(projectRoot, arch, profile)
+ build(projectRoot, arch)
- const { fileName, filePath } = await packageForWindows(projectRoot, arch, profile)
+ const { fileName, filePath } = await packageForWindows(projectRoot, arch)
await addFileToRelease(filePath, fileName)
}
@@ -177,36 +141,25 @@ async function doPublishWindows() {
async function doBuildWindows() {
const projectRoot = getProjectRoot();
const arch = 'x86_64-pc-windows-msvc';
- const profile = 'release';
- await doBuild(projectRoot, arch, profile)
- await packageForWindows(projectRoot, arch, profile)
+ await doBuild(projectRoot, arch)
+ await packageForWindows(projectRoot, arch)
}
async function doPublishFinal() {
+ console.log("Publishing Gauntlet npm packages...")
const projectRoot = getProjectRoot()
- const git = simpleGit(projectRoot);
-
- console.log("git pull...")
- await git.pull()
-
- console.log("Publishing Gauntlet npm packages...")
-
buildJs(projectRoot)
- await publishNpmPackage(projectRoot)
+ publishNpmPackage(projectRoot)
}
-function build(projectRoot: string, arch: string, profile: string) {
+function build(projectRoot: string, arch: string) {
buildJs(projectRoot)
- buildRust(projectRoot, arch, profile)
-}
-
-function buildRust(projectRoot: string, arch: string, profile: string) {
console.log("Building rust...")
- spawnWithErrors('cargo', ['build', '--profile', profile, '--features', 'release', '--target', arch], {
+ spawnWithErrors('cargo', ['build', '--release', '--features', 'release', '--target', arch], {
cwd: projectRoot
});
}
@@ -281,6 +234,18 @@ async function makeRepoChanges(projectRoot: string): Promise<{ releaseNotes: str
console.log("Writing changelog file...")
await writeFile(changelogFilePath, newChangelog.join(EOL))
+ const bumpNpmPackage = (packageDir: string) => {
+ spawnWithErrors('npm', ['version', `0.${newVersion}.0`], { cwd: packageDir })
+ }
+
+ console.log("Bump version for deno subproject...")
+ const denoProjectPath = path.join(projectRoot, "js", "deno");
+ bumpNpmPackage(denoProjectPath)
+
+ console.log("Bump version for api subproject...")
+ const apiProjectPath = path.join(projectRoot, "js", "api");
+ bumpNpmPackage(apiProjectPath)
+
console.log("git add all files...")
await git.raw('add', '-A')
console.log("git commit...")
@@ -298,8 +263,8 @@ async function makeRepoChanges(projectRoot: string): Promise<{ releaseNotes: str
}
}
-function packageForLinux(projectRoot: string, arch: string, profile: string): { filePath: string; fileName: string } {
- const releaseDirPath = path.join(projectRoot, 'target', arch, profile);
+function packageForLinux(projectRoot: string, arch: string): { filePath: string; fileName: string } {
+ const releaseDirPath = path.join(projectRoot, 'target', arch, 'release');
const assetsDirPath = path.join(projectRoot, 'assets', 'linux');
const sourceExecutableFilePath = path.join(releaseDirPath, 'gauntlet');
@@ -341,11 +306,11 @@ function packageForLinux(projectRoot: string, arch: string, profile: string): {
}
}
-async function packageForMacos(projectRoot: string, arch: string[], profile: string, sign: boolean, notarize: boolean): Promise<{ filePath: string; fileName: string }> {
- const targetDirPath = path.join(projectRoot, 'target');
- const outDirPath = path.join(targetDirPath, 'out');
- const outFileName = "gauntlet-universal-macos.dmg"
- const outFilePath = path.join(targetDirPath, outFileName);
+async function packageForMacos(projectRoot: string, arch: string, sign: boolean, notarize: boolean): Promise<{ filePath: string; fileName: string }> {
+ const releaseDirPath = path.join(projectRoot, 'target', arch, 'release');
+ const sourceExecutableFilePath = path.join(releaseDirPath, 'gauntlet');
+ const outFileName = "gauntlet-aarch64-macos.dmg"
+ const outFilePath = path.join(releaseDirPath, outFileName);
const assetsDirPath = path.join(projectRoot, 'assets', 'macos');
const sourceInfoFilePath = path.join(assetsDirPath, 'Info.plist');
@@ -353,7 +318,7 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
const dmgBackground = path.join(assetsDirPath, 'dmg-background.png');
const entitlementsPath = path.join(assetsDirPath, 'entitlements.plist');
- const bundleDir = path.join(outDirPath, 'Gauntlet.app');
+ const bundleDir = path.join(releaseDirPath, 'Gauntlet.app');
const contentsDir = path.join(bundleDir, 'Contents');
const macosContentsDir = path.join(contentsDir, 'MacOS');
const resourcesContentsDir = path.join(contentsDir, 'Resources');
@@ -361,29 +326,14 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
const targetInfoFilePath = path.join(contentsDir, 'Info.plist');
const targetIconFilePath = path.join(resourcesContentsDir, 'AppIcon.icns');
- const sourceExecutableFilePaths = arch.map(arch => path.join(targetDirPath, arch, profile, 'gauntlet'));
-
const version = await readVersion(projectRoot)
- if (existsSync(outDirPath)) {
- rmdirSync(outDirPath)
- }
-
- mkdirSync(outDirPath)
mkdirSync(bundleDir)
mkdirSync(contentsDir)
mkdirSync(macosContentsDir)
mkdirSync(resourcesContentsDir)
- spawnWithErrors(`lipo`, [
- ...sourceExecutableFilePaths,
- '-create',
- '-output',
- targetExecutableFilePath
- ], {
- cwd: outDirPath
- })
-
+ copyFileSync(sourceExecutableFilePath, targetExecutableFilePath)
copyFileSync(sourceInfoFilePath, targetInfoFilePath)
copyFileSync(sourceIconFilePath, targetIconFilePath)
@@ -391,9 +341,9 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
const infoResult = infoSource.replace('__VERSION__', `${version}.0.0`);
writeFileSync(targetInfoFilePath, infoResult,'utf8');
- const signKeyPath = path.join(outDirPath, 'signKey.pem');
- const signCertPath = path.join(outDirPath, 'signCert.pem');
- const connectApiKeyPath = path.join(outDirPath, 'connectApiKey.json');
+ const signKeyPath = path.join(releaseDirPath, 'signKey.pem');
+ const signCertPath = path.join(releaseDirPath, 'signCert.pem');
+ const connectApiKeyPath = path.join(releaseDirPath, 'connectApiKey.json');
const signKeyContent = process.env.APPLE_SIGNING_KEY_PEM;
const signCertContent = process.env.APPLE_SIGNING_CERT_PEM;
@@ -417,7 +367,7 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
entitlementsPath,
bundleDir
], {
- cwd: outDirPath
+ cwd: releaseDirPath
})
}
@@ -432,7 +382,7 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
outFileName,
bundleDir
], {
- cwd: targetDirPath
+ cwd: releaseDirPath
})
if (sign) {
@@ -445,7 +395,7 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
'--for-notarization',
outFilePath
], {
- cwd: outDirPath
+ cwd: releaseDirPath
})
}
@@ -459,7 +409,7 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
'--staple',
outFilePath
], {
- cwd: outDirPath
+ cwd: releaseDirPath
})
}
@@ -469,8 +419,8 @@ async function packageForMacos(projectRoot: string, arch: string[], profile: str
}
}
-async function packageForWindows(projectRoot: string, arch: string, profile: string): Promise<{ filePath: string; fileName: string }> {
- const releaseDirPath = path.join(projectRoot, 'target', arch, profile);
+async function packageForWindows(projectRoot: string, arch: string): Promise<{ filePath: string; fileName: string }> {
+ const releaseDirPath = path.join(projectRoot, 'target', arch, 'release');
const sourceExecutableFilePath = path.join(releaseDirPath, 'gauntlet.exe');
const outFileName = "gauntlet-x86_64-windows.msi"
const outFilePath = path.join(releaseDirPath, outFileName);
@@ -510,15 +460,13 @@ async function packageForWindows(projectRoot: string, arch: string, profile: str
}
-async function publishNpmPackage(projectRoot: string) {
- const version = await readVersion(projectRoot)
-
- const apiProjectPath = path.join(projectRoot, "js", "api");
-
- console.log("Bump version for api subproject...")
- spawnWithErrors('npm', ['version', `0.${version}.0`], { cwd: apiProjectPath })
+function publishNpmPackage(projectRoot: string) {
+ console.log("Publishing npm deno package...")
+ const denoProjectPath = path.join(projectRoot, "js", "deno");
+ spawnWithErrors('npm', ['publish'], { cwd: denoProjectPath })
console.log("Publishing npm api package...")
+ const apiProjectPath = path.join(projectRoot, "js", "api");
spawnWithErrors('npm', ['publish'], { cwd: apiProjectPath })
}
@@ -597,4 +545,4 @@ function spawnWithErrors(command: string, args: string[], options: SpawnSyncOpti
if (npmRunResult.status !== 0) {
throw new Error(`Unable to run ${command} ${args}, status: ${JSON.stringify(npmRunResult, null, 2)}`);
}
-}
+}
\ No newline at end of file
diff --git a/js/core/package.json b/js/core/package.json
index 1398cae..59445a3 100644
--- a/js/core/package.json
+++ b/js/core/package.json
@@ -5,16 +5,15 @@
"build": "tsc --noEmit && rollup --config rollup.config.ts --configPlugin typescript"
},
"devDependencies": {
+ "@rollup/plugin-commonjs": "^25.0.7",
+ "@rollup/plugin-node-resolve": "^15.2.3",
+ "@rollup/plugin-typescript": "^11.1.5",
+ "@types/react": "^18.2.35",
"@project-gauntlet/api": "*",
"@project-gauntlet/typings": "*",
- "@rollup/plugin-alias": "^5.1.1",
- "@rollup/plugin-commonjs": "^28.0.2",
- "@rollup/plugin-node-resolve": "^16.0.0",
- "@rollup/plugin-typescript": "^12.1.2",
- "@types/deno": "^2.0.0",
- "@types/react": "^18.3.18",
- "rollup": "^4.28.1",
- "tslib": "^2.8.1",
- "typescript": "^5.7.2"
+ "@project-gauntlet/deno": "*",
+ "rollup": "^4.3.0",
+ "tslib": "^2.6.2",
+ "typescript": "^5.3.3"
}
}
diff --git a/js/core/rollup.config.ts b/js/core/rollup.config.ts
index 760a198..68a33d4 100644
--- a/js/core/rollup.config.ts
+++ b/js/core/rollup.config.ts
@@ -2,16 +2,10 @@ import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from "rollup";
-import alias from '@rollup/plugin-alias';
export default defineConfig({
input: [
- 'src/core.tsx',
- 'src/init.ts',
- 'src/internal-all.ts',
- 'src/internal-linux.ts',
- 'src/internal-macos.ts',
- 'src/internal-windows.ts',
+ 'src/init.tsx',
],
output: [
{
@@ -20,18 +14,12 @@ export default defineConfig({
sourcemap: 'inline',
}
],
- external: [/^ext:.+/],
+ external: ["react", "react/jsx-runtime"],
plugins: [
nodeResolve(),
commonjs(),
typescript({
tsconfig: './tsconfig.json',
}),
- alias({
- entries: [
- { find: 'react/jsx-runtime', replacement: 'ext:gauntlet/react-jsx-runtime.js' },
- { find: 'react', replacement: 'ext:gauntlet/react.js' },
- ]
- }),
]
})
diff --git a/js/core/src/command-generator.ts b/js/core/src/command-generator.ts
new file mode 100644
index 0000000..fdf4009
--- /dev/null
+++ b/js/core/src/command-generator.ts
@@ -0,0 +1,63 @@
+// @ts-expect-error does typescript support such symbol declarations?
+const denoCore: DenoCore = Deno[Deno.internal].core;
+const InternalApi = denoCore.ops;
+
+interface GeneratedCommand { // TODO is it possible to import api here
+ id: string
+ name: string
+ icon?: ArrayBuffer
+ fn: () => void
+}
+
+type ProcessedGeneratedCommand = GeneratedCommand & { lookupId: string, uuid: string };
+
+let storedGeneratedCommands: ProcessedGeneratedCommand[] = []
+
+export async function runCommandGenerators(): Promise {
+ let localGeneratedCommands: ProcessedGeneratedCommand[] = []
+
+ const entrypointIds = await InternalApi.get_command_generator_entrypoint_ids();
+ for (const generatorEntrypointId of entrypointIds) {
+ try {
+ const generator: () => Promise | GeneratedCommand[] = (await import(`gauntlet:entrypoint?${generatorEntrypointId}`)).default;
+
+ InternalApi.op_log_info("command_generator", `Running command generator for entrypoint ${generatorEntrypointId}`)
+
+ const generatedCommands = (await generator())
+ .map(value => {
+ return {
+ lookupId: generatorEntrypointId + ":" + value.id,
+ uuid: crypto.randomUUID(),
+ ...value
+ }
+ });
+
+ InternalApi.op_log_info("command_generator", `Finished running command generator for entrypoint ${generatorEntrypointId}, amount: ${generatedCommands.length}`)
+
+ localGeneratedCommands.push(...generatedCommands)
+ } catch (e) {
+ console.error("Error occurred when calling command generator for entrypoint: " + generatorEntrypointId, e)
+ }
+ }
+
+ storedGeneratedCommands = localGeneratedCommands
+}
+
+export function generatedCommandSearchIndex(): AdditionalSearchItem[] {
+ return storedGeneratedCommands.map(value => ({
+ entrypoint_id: value.lookupId,
+ entrypoint_uuid: value.uuid,
+ entrypoint_name: value.name,
+ entrypoint_icon: value.icon,
+ }))
+}
+
+export function runGeneratedCommand(entrypointId: string) {
+ const generatedCommand = storedGeneratedCommands.find(value => value.lookupId === entrypointId);
+
+ if (generatedCommand) {
+ generatedCommand.fn()
+ } else {
+ throw new Error("Generated command with entrypoint id '" + entrypointId + "' not found")
+ }
+}
\ No newline at end of file
diff --git a/js/core/src/core.tsx b/js/core/src/core.tsx
deleted file mode 100644
index 2afa162..0000000
--- a/js/core/src/core.tsx
+++ /dev/null
@@ -1,179 +0,0 @@
-import type { FC } from "react";
-import { runEntrypointGenerators, runGeneratedEntrypoint, runGeneratedEntrypointAction } from "./entrypoint-generator";
-import { reloadSearchIndex } from "./search-index";
-import {
- closeView,
- handleEvent,
- handlePluginViewKeyboardEvent, popMainView,
- renderInlineView,
- renderView,
-} from "./render";
-import {
- entrypoint_preferences_required,
- get_entrypoint_preferences,
- get_plugin_preferences,
- op_entrypoint_names,
- op_inline_view_entrypoint_id,
- op_log_trace,
- op_plugin_get_pending_event,
- plugin_preferences_required,
- show_plugin_error_view,
- show_preferences_required_view
-} from "ext:core/ops";
-
-
-async function handleKeyboardEvent(event: NotReactsKeyboardEvent) {
- op_log_trace("plugin_event_handler", `Handling keyboard event: ${Deno.inspect(event)}`);
- switch (event.origin) {
- case "MainView": {
- runGeneratedEntrypointAction(event.entrypointId, event.key, event.modifierShift, event.modifierControl, event.modifierAlt, event.modifierMeta)
- break;
- }
- case "PluginView": {
- handlePluginViewKeyboardEvent(event.entrypointId, event.key, event.modifierShift, event.modifierControl, event.modifierAlt, event.modifierMeta)
- break;
- }
- }
-}
-
-async function checkRequiredPreferences(entrypointId: string): Promise {
- const pluginPreferencesRequired = plugin_preferences_required();
- const entrypointPreferencesRequired = entrypoint_preferences_required(entrypointId);
-
- return pluginPreferencesRequired || entrypointPreferencesRequired;
-}
-
-async function checkRequiredPreferencesAndAsk(entrypointId: string): Promise {
- const pluginPreferencesRequired = await plugin_preferences_required();
- const entrypointPreferencesRequired = await entrypoint_preferences_required(entrypointId);
-
- const required = pluginPreferencesRequired || entrypointPreferencesRequired;
- if (required) {
- show_preferences_required_view(entrypointId, pluginPreferencesRequired, entrypointPreferencesRequired)
- }
-
- return required;
-}
-
-export async function runPluginLoop() {
- await runEntrypointGenerators();
-
- // runtime is stopped using tokio cancellation
- // noinspection InfiniteLoopJS
- while (true) {
- op_log_trace("plugin_loop", "Waiting for next plugin event...")
- const pluginEvent = await op_plugin_get_pending_event();
- op_log_trace("plugin_loop", `Received plugin event: ${Deno.inspect(pluginEvent)}`)
- switch (pluginEvent.type) {
- case "ViewEvent": {
- try {
- handleEvent(pluginEvent)
- } catch (e) {
- console.error("Error occurred when receiving view event to handle", e)
- }
- break;
- }
- case "KeyboardEvent": {
- try {
- await handleKeyboardEvent(pluginEvent)
- } catch (e) {
- console.error("Error occurred when receiving keyboard event to handle", e)
- }
- break;
- }
- case "OpenView": {
- const entrypointId = pluginEvent.entrypointId
- try {
- if (await checkRequiredPreferencesAndAsk(entrypointId)) {
- break;
- }
-
- const view: FC = (await import(`gauntlet:entrypoint?${entrypointId}`)).default;
- renderView(entrypointId, getEntrypointName(entrypointId), view)
- } catch (e) {
- console.error("Error occurred when rendering view", entrypointId, e)
- show_plugin_error_view(entrypointId, "View")
- }
- break;
- }
- case "CloseView": {
- closeView()
- break;
- }
- case "PopView": {
- const entrypointId = pluginEvent.entrypointId
- try {
- popMainView()
- } catch (e) {
- console.error("Error occurred when popping view", entrypointId, e)
- show_plugin_error_view(entrypointId, "View")
- }
- break;
- }
- case "RunCommand": {
- try {
- if (await checkRequiredPreferencesAndAsk(pluginEvent.entrypointId)) {
- break;
- }
-
- type CommandContext = {
- pluginPreferences: P,
- entrypointPreferences: E,
- };
-
- const pluginPreferences = get_plugin_preferences();
- const entrypointPreferences = get_entrypoint_preferences(pluginEvent.entrypointId);
-
- const command: (context: CommandContext) => Promise | void = (await import(`gauntlet:entrypoint?${pluginEvent.entrypointId}`)).default;
- command({ pluginPreferences, entrypointPreferences })
- } catch (e) {
- console.error("Error occurred when running a command", pluginEvent.entrypointId, e)
- }
- break;
- }
- case "RunGeneratedEntrypoint": {
- try {
- runGeneratedEntrypoint(pluginEvent.entrypointId, pluginEvent.actionIndex)
- } catch (e) {
- console.error("Error occurred when running a generated command", pluginEvent.entrypointId, e)
- }
- break;
- }
- case "OpenInlineView": {
- const entrypointId = op_inline_view_entrypoint_id();
-
- if (entrypointId) {
- if (await checkRequiredPreferences(entrypointId)) {
- break;
- }
-
- try {
- type InlineView = { text: string };
- const handler: FC = (await import(`gauntlet:entrypoint?${entrypointId}`)).default;
-
- renderInlineView(entrypointId, getEntrypointName(entrypointId), handler, pluginEvent.text)
- } catch (e) {
- console.error("Error occurred when rendering inline view", e)
- }
- }
- break;
- }
- case "RefreshSearchIndex": {
- // noinspection ES6MissingAwait
- reloadSearchIndex(false)
- break;
- }
- }
- }
-}
-
-function getEntrypointName(entrypointId: string): string {
- const entrypointNames = op_entrypoint_names();
- const entrypointName = entrypointNames[entrypointId];
-
- if (entrypointName) {
- return entrypointName
- }
-
- throw new Error(`Unable to get entrypoint name for entrypoint id: ${entrypointId}`)
-}
diff --git a/js/core/src/entrypoint-generator.ts b/js/core/src/entrypoint-generator.ts
deleted file mode 100644
index 2e9ab6b..0000000
--- a/js/core/src/entrypoint-generator.ts
+++ /dev/null
@@ -1,263 +0,0 @@
-import {
- fetch_action_id_for_shortcut,
- get_entrypoint_generator_entrypoint_ids,
- op_log_info,
- op_log_debug,
- update_loading_bar,
- get_plugin_preferences,
- get_entrypoint_preferences
-} from "ext:core/ops";
-import { reloadSearchIndex } from "./search-index";
-import type { FC } from "react";
-import { renderView } from "./render";
-
-interface GeneratedEntrypoint { // TODO is it possible to import api here
- name: string
- actions: GeneratedEntrypointAction[]
- icon?: ArrayBuffer
- accessories?: GeneratedEntrypointAccessory[]
-}
-
-type GeneratedEntrypointAction = GeneratedEntrypointActionRun | GeneratedEntrypointActionView
-
-interface GeneratedEntrypointActionRun {
- ref?: string
- label: string
- run: () => void
-}
-
-interface GeneratedEntrypointActionView {
- ref?: string
- label: string
- view: FC
-}
-
-export type GeneratorContext = {
- add: (id: string, data: GeneratedEntrypoint) => void,
- remove: (id: string) => void,
- get: (id: string) => GeneratedEntrypoint | undefined
- getAll: () => { [id: string]: GeneratedEntrypoint },
- pluginPreferences: P,
- entrypointPreferences: E,
-};
-
-type Generator = (props: GeneratorContext) => void | (() => (void | Promise)) | Promise (void | Promise))>
-
-type ProcessedGeneratedEntrypoint = {
- generatorEntrypointId: string,
- id: string,
- uuid: string,
- command: GeneratedEntrypoint
- derivedActions: GeneratedEntrypointDerivedAction[]
-};
-
-type GeneratedEntrypointDerivedAction = GeneratedEntrypointDerivedActionRun | GeneratedEntrypointDerivedActionView
-
-interface GeneratedEntrypointDerivedActionRun {
- type: "Command"
- ref?: string
- label: string
- run: () => void
-}
-
-interface GeneratedEntrypointDerivedActionView {
- type: "View"
- ref?: string
- label: string
- view: FC
-}
-
-
-type ProcessedGeneratedEntrypoints = { [lookupEntrypointId: string]: ProcessedGeneratedEntrypoint };
-type GeneratorCleanups = { [generatorEntrypointId: string]: () => (void | Promise) };
-
-let storedGeneratedEntrypoints: ProcessedGeneratedEntrypoints = {}
-let generatorCleanups: GeneratorCleanups = {}
-
-export async function runEntrypointGenerators(): Promise {
- for (let [generatorEntrypointId, cleanup] of Object.entries(generatorCleanups)) {
- try {
- await cleanup()
- } catch (err) {
- console.error(`Error occurred when calling cleanup function of generator entrypoint: ${generatorEntrypointId}`, err)
- }
- }
-
- storedGeneratedEntrypoints = {}
- generatorCleanups = {}
-
- await reloadSearchIndex(true)
-
- const entrypointIds = await get_entrypoint_generator_entrypoint_ids();
- for (const generatorEntrypointId of entrypointIds) {
- try {
- const generator: Generator = (await import(`gauntlet:entrypoint?${generatorEntrypointId}`)).default;
-
- op_log_info("entrypoint_generator", `Running entrypoint generator entrypoint ${generatorEntrypointId}`)
-
- const add = (id: string, data: GeneratedEntrypoint) => {
- op_log_info("entrypoint_generator", `Adding entry '${id}' by entrypoint generator entrypoint '${generatorEntrypointId}'`)
-
- if (data.actions.length < 1) {
- throw new Error(`Error when adding entry '${id}': at least one action should be provided`)
- }
-
- const derivedActions: GeneratedEntrypointDerivedAction[] = []
- for (const action of data.actions) {
- const label = action.label;
-
- const run = "run" in action;
- const view = "view" in action;
-
- if (run && view) {
- throw new Error(`only one of 'run' or 'view' properties can be specified in action: '${label}'`)
- }
-
- if (!run && !view) {
- throw new Error(`one of 'run' or 'view' properties has to be specified in action: '${label}'`)
- }
-
- if (run) {
- derivedActions.push({
- type: "Command",
- ref: action.ref,
- label: action.label,
- run: action.run,
- })
- } else if (view) {
- derivedActions.push({
- type: "View",
- ref: action.ref,
- label: action.label,
- view: action.view,
- })
- }
- }
-
- const lookupId = generatorEntrypointId + ":" + id;
-
- storedGeneratedEntrypoints[lookupId] = {
- generatorEntrypointId: generatorEntrypointId,
- id: id,
- uuid: crypto.randomUUID(),
- command: data,
- derivedActions,
- }
-
- reloadSearchIndex(true)
- }
- const remove = (id: string) => {
- op_log_info("entrypoint_generator", `Removing entry '${id}' by entrypoint generator entrypoint '${generatorEntrypointId}'`)
- const lookupId = generatorEntrypointId + ":" + id;
-
- delete storedGeneratedEntrypoints[lookupId]
-
- reloadSearchIndex(true)
- }
-
- const get = (id: string) => {
- op_log_debug("entrypoint_generator", `Getting entry '${id}' by entrypoint generator entrypoint '${generatorEntrypointId}'`)
- const lookupId = generatorEntrypointId + ":" + id;
-
- const generatedEntrypoint = storedGeneratedEntrypoints[lookupId];
- if (generatedEntrypoint) {
- return generatedEntrypoint.command
- } else {
- return undefined
- }
- }
-
- const getAll = (): { [id: string]: GeneratedEntrypoint } => {
- op_log_debug("entrypoint_generator", `Getting all entries by entrypoint generator entrypoint '${generatorEntrypointId}'`)
-
- return Object.fromEntries(
- Object.entries(storedGeneratedEntrypoints)
- .map(([_lookupId, value]) => [value.id, value.command])
- )
- }
-
- const pluginPreferences = get_plugin_preferences();
- const entrypointPreferences = get_entrypoint_preferences(generatorEntrypointId);
-
- // noinspection ES6MissingAwait
- (async () => {
- try {
- update_loading_bar(generatorEntrypointId, true)
- let cleanup = await generator({ add, remove, get, getAll, pluginPreferences, entrypointPreferences })
- update_loading_bar(generatorEntrypointId, false)
- if (typeof cleanup === "function") {
- generatorCleanups[generatorEntrypointId] = cleanup
- }
- } catch (e) {
- console.error(`Error occurred when calling entrypoint generator for entrypoint: ${generatorEntrypointId}`, e)
- }
- })()
- } catch (e) {
- console.error(`Error occurred when importing entrypoint generator for entrypoint: ${generatorEntrypointId}`, e)
- }
- }
-}
-
-export function generatedEntrypointSearchIndex(): GeneratedSearchItem[] {
- return Object.entries(storedGeneratedEntrypoints).map(([entrypointLookupId, value]) => ({
- generator_entrypoint_id: value.generatorEntrypointId,
- entrypoint_id: entrypointLookupId,
- entrypoint_uuid: value.uuid,
- entrypoint_name: value.command.name,
- entrypoint_icon: value.command.icon,
- entrypoint_actions: value.derivedActions
- .map(action => ({
- id: action.ref,
- action_type: action.type,
- label: action.label
- })),
- entrypoint_accessories: value.command.accessories || []
- }))
-}
-
-export async function runGeneratedEntrypointAction(entrypointId: string, key: string, modifierShift: boolean, modifierControl: boolean, modifierAlt: boolean, modifierMeta: boolean) {
- const command = storedGeneratedEntrypoints[entrypointId];
-
- if (command) {
- const id = await fetch_action_id_for_shortcut(command.generatorEntrypointId, key, modifierShift, modifierControl, modifierAlt, modifierMeta);
- if (id) {
- const action = command.derivedActions.find(value => value.ref == id);
- if (action) {
- runAction(entrypointId, action)
- }
- }
- }
-}
-
-export function runGeneratedEntrypoint(entrypointId: string, action_index: number) {
- const generatedEntrypoint = storedGeneratedEntrypoints[entrypointId];
-
- if (generatedEntrypoint) {
- const action = generatedEntrypoint.derivedActions[action_index];
- if (action) {
- runAction(entrypointId, action)
- } else {
- throw new Error("Generated command with entrypoint id '" + entrypointId + "' doesn't have action with index: " + action_index)
- }
- } else {
- throw new Error("Generated command with entrypoint id '" + entrypointId + "' not found")
- }
-}
-
-function runAction(entrypointId: string, action: GeneratedEntrypointDerivedAction) {
- switch (action.type) {
- case "Command": {
- action.run()
-
- break;
- }
- case "View": {
- const entrypointName = storedGeneratedEntrypoints[entrypointId]
- .command
- .name
-
- renderView(entrypointId, entrypointName, action.view)
- break;
- }
- }
-}
\ No newline at end of file
diff --git a/js/core/src/init.ts b/js/core/src/init.ts
deleted file mode 100644
index 4dfbad5..0000000
--- a/js/core/src/init.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { runPluginLoop } from "gauntlet:core";
-
-globalThis.addEventListener("unhandledrejection", (event) => {
- event.preventDefault()
- console.error("Rejected promise, reason:", event.reason);
-});
-
-(async () => {
- await runPluginLoop()
-})();
diff --git a/js/core/src/init.tsx b/js/core/src/init.tsx
new file mode 100644
index 0000000..62a88f9
--- /dev/null
+++ b/js/core/src/init.tsx
@@ -0,0 +1,233 @@
+import { FC } from "react";
+import { runCommandGenerators, runGeneratedCommand } from "./command-generator";
+import { reloadSearchIndex } from "./search-index";
+import { clearRenderer } from "gauntlet:renderer";
+
+// @ts-expect-error does typescript support such symbol declarations?
+const denoCore: DenoCore = Deno[Deno.internal].core;
+const InternalApi = denoCore.ops;
+
+let latestRootUiWidget: UiWidget | undefined = undefined
+
+function findWidgetWithId(widget: UiWidget, widgetId: number): UiWidget | undefined {
+ if (widget.widgetId === widgetId) {
+ return widget
+ }
+
+ for (let widgetChild of widget.widgetChildren) {
+ const widgetWithId = findWidgetWithId(widgetChild, widgetId);
+ if (widgetWithId) {
+ return widgetWithId
+ }
+ }
+
+ return undefined;
+}
+
+function findAllActionHandlers(widget: UiWidget): { id: string, onAction: () => void }[] {
+ if (widget.widgetType === "gauntlet:action") {
+ const id = widget.widgetProperties["id"];
+ const onAction = widget.widgetProperties["onAction"];
+ if (!!id && !!onAction) {
+ return [{ id, onAction }]
+ } else {
+ return []
+ }
+ }
+
+ let result: { id: string, onAction: () => void }[] = []
+ for (let widgetChild of widget.widgetChildren) {
+ const actionHandler = findAllActionHandlers(widgetChild);
+
+ result.push(...actionHandler)
+ }
+
+ return result;
+}
+
+function handleEvent(event: ViewEvent) {
+ InternalApi.op_log_trace("plugin_event_handler", `Handling view event: ${Deno.inspect(event)}`);
+ InternalApi.op_log_trace("plugin_event_handler", `Root widget: ${Deno.inspect(latestRootUiWidget)}`);
+ if (latestRootUiWidget) {
+ const widgetWithId = findWidgetWithId(latestRootUiWidget, event.widgetId);
+ InternalApi.op_log_trace("plugin_event_handler", `Found widget with id ${event.widgetId}: ${Deno.inspect(widgetWithId)}`)
+
+ if (widgetWithId) {
+ const property = widgetWithId.widgetProperties[event.eventName];
+
+ InternalApi.op_log_trace("plugin_event_handler", `Found event handler with name ${event.eventName}: ${Deno.inspect(property)}`)
+
+ if (property) {
+ if (typeof property === "function") {
+
+ const eventArgs = event.eventArguments
+ .map(arg => {
+ switch (arg.type) {
+ case "Undefined": {
+ return undefined
+ }
+ case "String": {
+ return arg.value
+ }
+ case "Number": {
+ return arg.value
+ }
+ case "Bool": {
+ return arg.value
+ }
+ }
+ });
+
+ InternalApi.op_log_trace("plugin_event_handler", `Calling handler with arguments ${Deno.inspect(eventArgs)}`)
+
+ property(...eventArgs);
+ } else {
+ throw new Error(`Event handler has type ${typeof property}, but should be function`)
+ }
+ }
+ }
+ }
+}
+
+async function handleKeyboardEvent(event: NotReactsKeyboardEvent) {
+ InternalApi.op_log_trace("plugin_event_handler", `Handling keyboard event: ${Deno.inspect(event)}`);
+ if (latestRootUiWidget) {
+ const actionHandlers = findAllActionHandlers(latestRootUiWidget);
+
+ const id = await InternalApi.fetch_action_id_for_shortcut(event.entrypointId, event.key, event.modifierShift, event.modifierControl, event.modifierAlt, event.modifierMeta);
+
+ const actionHandler = actionHandlers.find(value => value.id === id);
+
+ if (actionHandler) {
+ actionHandler.onAction()
+ }
+ }
+}
+
+async function checkRequiredPreferences(entrypointId: string): Promise {
+ const pluginPreferencesRequired = InternalApi.plugin_preferences_required();
+ const entrypointPreferencesRequired = InternalApi.entrypoint_preferences_required(entrypointId);
+
+ return pluginPreferencesRequired || entrypointPreferencesRequired;
+}
+
+async function checkRequiredPreferencesAndAsk(entrypointId: string): Promise {
+ const pluginPreferencesRequired = await InternalApi.plugin_preferences_required();
+ const entrypointPreferencesRequired = await InternalApi.entrypoint_preferences_required(entrypointId);
+
+ const required = pluginPreferencesRequired || entrypointPreferencesRequired;
+ if (required) {
+ InternalApi.show_preferences_required_view(entrypointId, pluginPreferencesRequired, entrypointPreferencesRequired)
+ }
+
+ return required;
+}
+
+async function runLoop() {
+ // runtime is stopped using tokio cancellation
+ // noinspection InfiniteLoopJS
+ while (true) {
+ InternalApi.op_log_trace("plugin_loop", "Waiting for next plugin event...")
+ const pluginEvent = await denoCore.opAsync("op_plugin_get_pending_event");
+ InternalApi.op_log_trace("plugin_loop", `Received plugin event: ${Deno.inspect(pluginEvent)}`)
+ switch (pluginEvent.type) {
+ case "ViewEvent": {
+ try {
+ handleEvent(pluginEvent)
+ } catch (e) {
+ console.error("Error occurred when receiving view event to handle", e)
+ }
+ break;
+ }
+ case "KeyboardEvent": {
+ try {
+ await handleKeyboardEvent(pluginEvent)
+ } catch (e) {
+ console.error("Error occurred when receiving keyboard event to handle", e)
+ }
+ break;
+ }
+ case "OpenView": {
+ try {
+ if (await checkRequiredPreferencesAndAsk(pluginEvent.entrypointId)) {
+ break;
+ }
+
+ const View: FC = (await import(`gauntlet:entrypoint?${pluginEvent.entrypointId}`)).default;
+ const { render } = await import("gauntlet:renderer");
+ latestRootUiWidget = render(pluginEvent.entrypointId, "View", );
+ } catch (e) {
+ console.error("Error occurred when rendering view", pluginEvent.entrypointId, e)
+ InternalApi.show_plugin_error_view(pluginEvent.entrypointId, "View")
+ }
+ break;
+ }
+ case "CloseView": {
+ clearRenderer()
+ break;
+ }
+ case "RunCommand": {
+ try {
+ if (await checkRequiredPreferencesAndAsk(pluginEvent.entrypointId)) {
+ break;
+ }
+
+ const command: () => Promise | void = (await import(`gauntlet:entrypoint?${pluginEvent.entrypointId}`)).default;
+ command()
+ } catch (e) {
+ console.error("Error occurred when running a command", pluginEvent.entrypointId, e)
+ }
+ break;
+ }
+ case "RunGeneratedCommand": {
+ try {
+ runGeneratedCommand(pluginEvent.entrypointId)
+ } catch (e) {
+ console.error("Error occurred when running a generated command", pluginEvent.entrypointId, e)
+ }
+ break;
+ }
+ case "OpenInlineView": {
+ const endpointId = InternalApi.op_inline_view_endpoint_id();
+
+ if (endpointId) {
+ if (await checkRequiredPreferences(endpointId)) {
+ break;
+ }
+
+ try {
+ const Handler: FC<{ text: string }> = (await import(`gauntlet:entrypoint?${endpointId}`)).default;
+ const { render } = await import("gauntlet:renderer");
+
+ latestRootUiWidget = render(endpointId, "InlineView", );
+
+ if (latestRootUiWidget.widgetChildren.length === 0) {
+ InternalApi.op_log_debug("plugin_loop", `Inline view rendered no children, clearing inline view...`)
+ InternalApi.clear_inline_view()
+ }
+ } catch (e) {
+ console.error("Error occurred when rendering inline view", e)
+ }
+ }
+ break;
+ }
+ case "ReloadSearchIndex": {
+ runCommandGenerators()
+ .then(() => reloadSearchIndex(false));
+ break;
+ }
+ case "RefreshSearchIndex": {
+ // noinspection ES6MissingAwait
+ reloadSearchIndex(false)
+ break;
+ }
+ }
+ }
+}
+
+runCommandGenerators()
+ .then(() => reloadSearchIndex(true));
+
+(async () => {
+ await runLoop()
+})();
diff --git a/js/core/src/internal-all.ts b/js/core/src/internal-all.ts
deleted file mode 100644
index fac9402..0000000
--- a/js/core/src/internal-all.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export {
- run_numbat,
- open_settings,
- current_os,
- wayland,
-} from "ext:core/ops";
diff --git a/js/core/src/internal-linux.ts b/js/core/src/internal-linux.ts
deleted file mode 100644
index 503605f..0000000
--- a/js/core/src/internal-linux.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export {
- linux_app_from_path,
- linux_application_dirs,
- linux_open_application,
- linux_x11_focus_window,
- linux_wayland_focus_window,
- application_x11_pending_event,
- application_wayland_pending_event,
-} from "ext:core/ops";
diff --git a/js/core/src/internal-macos.ts b/js/core/src/internal-macos.ts
deleted file mode 100644
index 8bd72e2..0000000
--- a/js/core/src/internal-macos.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export {
- macos_app_from_arbitrary_path,
- macos_app_from_path,
- macos_application_dirs,
- macos_major_version,
- macos_open_application,
- macos_get_localized_language,
- macos_open_setting_13_and_post,
- macos_open_setting_pre_13,
- macos_settings_13_and_post,
- macos_settings_pre_13,
- macos_system_applications,
-} from "ext:core/ops";
diff --git a/js/core/src/internal-windows.ts b/js/core/src/internal-windows.ts
deleted file mode 100644
index 77f40d5..0000000
--- a/js/core/src/internal-windows.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export {
- windows_app_from_path,
- windows_application_dirs,
- windows_open_application
-} from "ext:core/ops";
diff --git a/js/core/src/render.tsx b/js/core/src/render.tsx
deleted file mode 100644
index 22936da..0000000
--- a/js/core/src/render.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-import {
- fetch_action_id_for_shortcut,
- op_log_trace,
- hide_window
-} from "ext:core/ops";
-import { clearRenderer, rerender, render, popView } from "ext:gauntlet/renderer.js";
-import type { FC } from "react";
-
-let latestRootUiWidget: UiWidget | undefined = undefined
-let latestRootUiRenderLocation: RenderLocation | undefined = undefined
-
-export function renderView(entrypointId: string, entrypointName: string, View: FC) {
- latestRootUiRenderLocation = "View";
- latestRootUiWidget = render(entrypointId, entrypointName, "View", );
-}
-
-export function popMainView() {
- popView()
-}
-
-export function renderInlineView(entrypointId: string, entrypointName: string, Handler: FC<{ text: string }>, text: string) {
- switch (latestRootUiRenderLocation) {
- case "InlineView": {
- rerender();
- break
- }
- default: {
- latestRootUiRenderLocation = "InlineView";
- latestRootUiWidget = render(entrypointId, entrypointName, "InlineView", );
- break
- }
- }
-}
-
-export function closeView() {
- latestRootUiRenderLocation = undefined;
- clearRenderer()
-}
-
-export async function handlePluginViewKeyboardEvent(entrypointId: string, key: string, modifierShift: boolean, modifierControl: boolean, modifierAlt: boolean, modifierMeta: boolean) {
- if (latestRootUiWidget) {
- const actionHandlers = findAllActionHandlers(latestRootUiWidget);
-
- const id = await fetch_action_id_for_shortcut(entrypointId, key, modifierShift, modifierControl, modifierAlt, modifierMeta);
-
- if (id) {
- const actionHandler = actionHandlers.find(value => value.id === id);
-
- if (actionHandler) {
- actionHandler.onAction()
- }
- }
- }
-}
-
-function findAllActionHandlers(widget: UiWidget): { id: string, onAction: () => void }[] {
- if (widget.widgetType === "gauntlet:action") {
- const id = widget.widgetProperties["id"];
- const onAction = widget.widgetProperties["onAction"];
- if (!!id && !!onAction) {
- return [{ id, onAction }]
- } else {
- return []
- }
- }
-
- let result: { id: string, onAction: () => void }[] = []
- for (let widgetChild of widget.widgetChildren) {
- const actionHandler = findAllActionHandlers(widgetChild);
-
- result.push(...actionHandler)
- }
-
- return result;
-}
-
-export function handleEvent(event: ViewEvent) {
- op_log_trace("plugin_event_handler", `Handling view event: ${Deno.inspect(event)}`);
- op_log_trace("plugin_event_handler", `Root widget: ${Deno.inspect(latestRootUiWidget)}`);
- if (latestRootUiWidget) {
- const widgetWithId = findWidgetWithId(latestRootUiWidget, event.widgetId);
- op_log_trace("plugin_event_handler", `Found widget with id ${event.widgetId}: ${Deno.inspect(widgetWithId)}`)
-
- if (widgetWithId) {
- const property = widgetWithId.widgetProperties[event.eventName];
-
- op_log_trace("plugin_event_handler", `Found event handler with name ${event.eventName}: ${Deno.inspect(property)}`)
-
- if (property) {
- if (typeof property === "function") {
-
- const eventArgs = event.eventArguments
- .map(arg => {
- switch (arg.type) {
- case "Undefined": {
- return undefined
- }
- case "Null": {
- return null
- }
- case "String": {
- return arg.value
- }
- case "Number": {
- return arg.value
- }
- case "Bool": {
- return arg.value
- }
- }
- });
-
- op_log_trace("plugin_event_handler", `Calling handler with arguments ${Deno.inspect(eventArgs)}`);
-
- (async () => {
- const result = await property(...eventArgs);
-
- // special case for action results
- if (event.eventName == "onAction") {
- if (result?.close === true) {
- hide_window()
- }
- }
- })();
- } else {
- throw new Error(`Event handler has type ${typeof property}, but should be function`)
- }
- }
- }
- }
-}
-
-function findWidgetWithId(widget: UiWidget, widgetId: number): UiWidget | undefined {
- if (widget.widgetId === widgetId) {
- return widget
- }
-
- for (let widgetChild of widget.widgetChildren) {
- const widgetWithId = findWidgetWithId(widgetChild, widgetId);
- if (widgetWithId) {
- return widgetWithId
- }
- }
-
- return undefined;
-}
diff --git a/js/core/src/search-index.ts b/js/core/src/search-index.ts
index f7fc547..31caaec 100644
--- a/js/core/src/search-index.ts
+++ b/js/core/src/search-index.ts
@@ -1,6 +1,9 @@
-import { generatedEntrypointSearchIndex } from "./entrypoint-generator";
-import { reload_search_index } from "ext:core/ops";
+import { generatedCommandSearchIndex } from "./command-generator";
+
+// @ts-expect-error does typescript support such symbol declarations?
+const denoCore: DenoCore = Deno[Deno.internal].core;
+const InternalApi = denoCore.ops;
export async function reloadSearchIndex(refreshSearchList: boolean) {
- await reload_search_index(generatedEntrypointSearchIndex(), refreshSearchList);
+ await InternalApi.reload_search_index(generatedCommandSearchIndex(), refreshSearchList);
}
\ No newline at end of file
diff --git a/js/core/tsconfig.json b/js/core/tsconfig.json
index 7b10001..7f14964 100644
--- a/js/core/tsconfig.json
+++ b/js/core/tsconfig.json
@@ -3,10 +3,10 @@
"strict": true,
"module": "ES2022",
"esModuleInterop": true,
- "target": "ESNext",
+ "target": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
- "types": ["@project-gauntlet/typings", "@types/deno"]
+ "types": ["@project-gauntlet/typings", "@project-gauntlet/deno"]
},
"lib": ["ES2020"]
}
\ No newline at end of file
diff --git a/js/core/typings/index.d.ts b/js/core/typings/index.d.ts
new file mode 100644
index 0000000..c17ef8c
--- /dev/null
+++ b/js/core/typings/index.d.ts
@@ -0,0 +1,7 @@
+declare module "gauntlet:renderer" {
+ import { ReactNode } from "react";
+
+ const render: (entrypointId: string, renderLocation: RenderLocation, component: ReactNode) => UiWidget;
+ const clearRenderer: () => void;
+ export { render, clearRenderer };
+}
\ No newline at end of file
diff --git a/js/deno/.gitignore b/js/deno/.gitignore
new file mode 100644
index 0000000..18ce544
--- /dev/null
+++ b/js/deno/.gitignore
@@ -0,0 +1,2 @@
+dist
+builddist
\ No newline at end of file
diff --git a/js/deno/generator/index.ts b/js/deno/generator/index.ts
new file mode 100644
index 0000000..8f3c6d7
--- /dev/null
+++ b/js/deno/generator/index.ts
@@ -0,0 +1,16 @@
+import { existsSync, mkdirSync, writeFileSync } from "node:fs";
+
+// https://github.com/denoland/deno/releases/tag/v1.36.4
+const LIB_DENO_DECLARATION_URL = "https://github.com/denoland/deno/releases/download/v1.36.4/lib.deno.d.ts";
+
+const res = await fetch(LIB_DENO_DECLARATION_URL);
+const content = await res.text();
+
+const fixedContent = content.replaceAll(/\/\/\/ /g, "")
+
+const distDir = "./dist";
+if (!existsSync(distDir)) {
+ mkdirSync(distDir);
+}
+
+writeFileSync(`${distDir}/lib.deno.d.ts`, fixedContent)
diff --git a/js/deno/package.json b/js/deno/package.json
new file mode 100644
index 0000000..5516d4d
--- /dev/null
+++ b/js/deno/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "@project-gauntlet/deno",
+ "version": "0.8.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/lib.deno.d.ts"
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/project-gauntlet/gauntlet.git",
+ "directory": "js/deno"
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "npm run run-generator-source",
+ "run-generator-source": "tsc --project tsconfig.json && node builddist/index.js"
+ },
+ "devDependencies": {
+ "@types/node": "^18.17.1",
+ "typescript": "^5.3.3"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/tsconfig.json b/js/deno/tsconfig.json
similarity index 67%
rename from example_plugins/plugins/entrypoint_generator_action_shortcut/tsconfig.json
rename to js/deno/tsconfig.json
index f9bb627..2854811 100644
--- a/example_plugins/plugins/entrypoint_generator_action_shortcut/tsconfig.json
+++ b/js/deno/tsconfig.json
@@ -5,7 +5,8 @@
"esModuleInterop": true,
"target": "ES2022",
"moduleResolution": "bundler",
- "jsx": "react-jsx"
+ "outDir": "./builddist"
},
- "lib": ["ES2020"]
+ "lib": ["ES2020"],
+ "include": ["./generator"]
}
\ No newline at end of file
diff --git a/js/react/package.json b/js/react/package.json
index e744a6c..5a0d165 100644
--- a/js/react/package.json
+++ b/js/react/package.json
@@ -5,14 +5,14 @@
"build": "rollup --config rollup.config.ts --configPlugin typescript"
},
"dependencies": {
- "react": "^18.3.1"
+ "react": "^18.2.0"
},
"devDependencies": {
- "@rollup/plugin-commonjs": "^28.0.2",
- "@rollup/plugin-replace": "^6.0.2",
- "@rollup/plugin-typescript": "^12.1.2",
- "rollup": "^4.28.1",
- "tslib": "^2.8.1",
- "typescript": "^5.7.2"
+ "@rollup/plugin-commonjs": "^25.0.7",
+ "@rollup/plugin-replace": "^5.0.5",
+ "@rollup/plugin-typescript": "^11.1.5",
+ "rollup": "^4.3.0",
+ "tslib": "^2.6.2",
+ "typescript": "^5.3.3"
}
}
diff --git a/js/react/rollup.config.ts b/js/react/rollup.config.ts
index a24467a..5df2085 100644
--- a/js/react/rollup.config.ts
+++ b/js/react/rollup.config.ts
@@ -1,7 +1,6 @@
import commonjs from '@rollup/plugin-commonjs';
import replace from "@rollup/plugin-replace";
import { defineConfig, RollupOptions } from "rollup";
-import alias from "@rollup/plugin-alias";
const fixedDevExports = `
@@ -55,11 +54,9 @@ const config = (nodeEnv: string, reactBundle: string, outDir: string): RollupOpt
dir: outDir,
format: 'esm',
},
- external: [/^ext:.+/],
+ external: ['react'],
plugins: [
- commonjs({
- strictRequires: "auto"
- }),
+ commonjs(),
replace({
delimiters: ['', ''],
values: {
@@ -68,13 +65,7 @@ const config = (nodeEnv: string, reactBundle: string, outDir: string): RollupOpt
// To fix exports in development bundle https://github.com/rollup/plugins/issues/1546
'export { react_development as default };': fixedDevExports,
}
- }),
- alias({
- entries: [
- { find: 'react/jsx-runtime', replacement: 'ext:gauntlet/react-jsx-runtime.js' },
- { find: 'react', replacement: 'ext:gauntlet/react.js' },
- ]
- }),
+ })
]
}
}
diff --git a/js/react_renderer/package.json b/js/react_renderer/package.json
index 1649ffe..9745046 100644
--- a/js/react_renderer/package.json
+++ b/js/react_renderer/package.json
@@ -5,20 +5,19 @@
"build": "tsc --noEmit && rollup --config rollup.config.ts --configPlugin typescript"
},
"dependencies": {
- "react-reconciler": "^0.29.2"
+ "react-reconciler": "^0.29.0"
},
"devDependencies": {
- "@rollup/plugin-alias": "^5.1.1",
- "@rollup/plugin-commonjs": "^28.0.2",
- "@rollup/plugin-node-resolve": "^16.0.0",
- "@rollup/plugin-replace": "^6.0.2",
- "@rollup/plugin-typescript": "^12.1.2",
- "@types/deno": "^2.0.0",
- "@types/react": "^18.3.18",
- "@types/react-reconciler": "^0.28.9",
+ "@rollup/plugin-commonjs": "^25.0.7",
+ "@rollup/plugin-node-resolve": "^15.2.3",
+ "@rollup/plugin-replace": "^5.0.5",
+ "@rollup/plugin-typescript": "^11.1.5",
+ "@types/react": "^18.2.35",
+ "@types/react-reconciler": "^0.28.6",
"@project-gauntlet/typings": "*",
- "rollup": "^4.28.1",
- "tslib": "^2.8.1",
- "typescript": "^5.7.2"
+ "@project-gauntlet/deno": "*",
+ "rollup": "^4.3.0",
+ "tslib": "^2.6.2",
+ "typescript": "^5.3.3"
}
}
diff --git a/js/react_renderer/rollup.config.ts b/js/react_renderer/rollup.config.ts
index e44ffd9..41587a3 100644
--- a/js/react_renderer/rollup.config.ts
+++ b/js/react_renderer/rollup.config.ts
@@ -3,7 +3,6 @@ import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import replace from "@rollup/plugin-replace";
import { defineConfig, RollupOptions } from "rollup";
-import alias from '@rollup/plugin-alias';
const config = (nodeEnv: string, outDir: string): RollupOptions => {
return {
@@ -17,7 +16,7 @@ const config = (nodeEnv: string, outDir: string): RollupOptions => {
sourcemap: 'inline',
}
],
- external: [/^ext:.+/],
+ external: ["react", "react/jsx-runtime"],
plugins: [
nodeResolve(),
commonjs({
@@ -35,13 +34,7 @@ const config = (nodeEnv: string, outDir: string): RollupOptions => {
'–': "-",
'—': "-"
}
- }),
- alias({
- entries: [
- { find: 'react/jsx-runtime', replacement: 'ext:gauntlet/react-jsx-runtime.js' },
- { find: 'react', replacement: 'ext:gauntlet/react.js' },
- ]
- }),
+ })
]
}
}
diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts
index c7ce931..6e35e20 100644
--- a/js/react_renderer/src/renderer.ts
+++ b/js/react_renderer/src/renderer.ts
@@ -1,16 +1,10 @@
import ReactReconciler, { HostConfig, OpaqueHandle } from "react-reconciler";
-import { createContext, ReactNode, useContext } from 'react';
+import { createContext, FC, ReactNode, useContext } from 'react';
import { DefaultEventPriority } from 'react-reconciler/constants';
-import {
- asset_data,
- asset_data_blocking,
- get_entrypoint_preferences,
- get_plugin_preferences,
- op_component_model,
- op_log_trace,
- op_react_replace_view,
- show_hud
-} from "ext:core/ops";
+
+// @ts-expect-error does typescript support such symbol declarations?
+const denoCore: DenoCore = Deno[Deno.internal].core;
+const InternalApi = denoCore.ops;
// Usage of MessageChannel seems to block Deno runtime from exiting
// causing plugin to be in stuck state where it is disabled but still have running runtime
@@ -47,12 +41,10 @@ class GauntletContextValue {
private _renderLocation: RenderLocation | undefined
private _rerender: ((node: ReactNode) => void) | undefined
private _entrypointId: string | undefined;
- private _entrypointName: string | undefined;
private _clear: (() => void) | undefined;
- reset(entrypointId: string, entrypointName: string, renderLocation: RenderLocation, view: ReactNode, rerender: (node: ReactNode) => void, clear: () => void) {
+ reset(entrypointId: string, renderLocation: RenderLocation, view: ReactNode, rerender: (node: ReactNode) => void, clear: () => void) {
this._entrypointId = entrypointId
- this._entrypointName = entrypointName
this._renderLocation = renderLocation
this._rerender = rerender
this._clear = clear
@@ -60,26 +52,22 @@ class GauntletContextValue {
this._navStack.push(view)
}
- renderLocation = (): RenderLocation => {
+ renderLocation(): RenderLocation {
return this._renderLocation!!
}
- isBottommostView = () => {
+ isBottommostView() {
return this._navStack.length === 1
}
- topmostView = () => {
+ topmostView() {
return this._navStack[this._navStack.length - 1]
}
- entrypointId = () => {
+ entrypointId() {
return this._entrypointId!!
}
- entrypointName = () => {
- return this._entrypointName!!
- }
-
rerender = (component: ReactNode) => {
this._rerender!!(component)
};
@@ -101,11 +89,11 @@ class GauntletContextValue {
};
entrypointPreferences = () => {
- return get_entrypoint_preferences(this.entrypointId())
+ return InternalApi.get_entrypoint_preferences(this.entrypointId())
}
pluginPreferences = () => {
- return get_plugin_preferences()
+ return InternalApi.get_plugin_preferences()
}
}
@@ -117,11 +105,13 @@ export function useGauntletContext() {
}
export async function getAssetData(path: string): Promise {
- return await asset_data(path);
+ const vecU8 = await InternalApi.asset_data(path);
+ return new Uint8Array(vecU8).buffer; // FIXME move array creation into rust if possible
}
export function getAssetDataSync(path: string): ArrayBuffer {
- return asset_data_blocking(path);
+ const vecU8 = InternalApi.asset_data_blocking(path);
+ return new Uint8Array(vecU8).buffer;
}
export function getPluginPreferences(): Record {
@@ -132,33 +122,23 @@ export function getEntrypointPreferences(): Record {
return gauntletContextValue.entrypointPreferences()
}
-export function showHudWindow(display: string): void {
- show_hud(display)
-}
-
-function createWidget(id: number | undefined, hostContext: HostContext, type: ComponentType, properties: Props, children: UiWidget[]): Instance {
+function createWidget(hostContext: HostContext, type: ComponentType, properties: Props, children: UiWidget[] = []): Instance {
const props = Object.fromEntries(
Object.entries(properties)
.filter(([key, _]) => key !== "children")
);
const instance: Instance = {
- widgetId: id != undefined ? id : hostContext.nextId,
+ widgetId: hostContext.nextId,
widgetType: type,
widgetProperties: props,
widgetChildren: children,
hostContext
};
-
- if (id == undefined) {
- hostContext.nextId += 1
- }
-
+ hostContext.nextId += 1
return instance
}
-const componentModel = op_component_model();
-
export const createHostConfig = (): HostConfig<
ComponentType,
PropsWithChildren,
@@ -184,9 +164,9 @@ export const createHostConfig = (): HostConfig<
hostContext: HostContext,
_internalHandle: OpaqueHandle,
): Instance => {
- op_log_trace("renderer_js_common", `createInstance is called, type: ${type}, props: ${Deno.inspect(props)}, rootContainer: ${Deno.inspect(rootContainer)}`)
- const instance = createWidget(undefined, hostContext, type, props, [])
- op_log_trace("renderer_js_common", `createInstance returned, widget: ${Deno.inspect(instance)}`)
+ InternalApi.op_log_trace("renderer_js_common", `createInstance is called, type: ${type}, props: ${Deno.inspect(props)}, rootContainer: ${Deno.inspect(rootContainer)}`)
+ const instance = createWidget(hostContext, type, props)
+ InternalApi.op_log_trace("renderer_js_common", `createInstance returned, widget: ${Deno.inspect(instance)}`)
return instance;
},
@@ -197,15 +177,15 @@ export const createHostConfig = (): HostConfig<
hostContext: HostContext,
_internalHandle: OpaqueHandle
): TextInstance => {
- op_log_trace("renderer_js_common", `createTextInstance is called, text: ${text}, rootContainer: ${Deno.inspect(rootContainer)}`)
- const textInstance = createWidget(undefined, hostContext, "gauntlet:text_part", { value: text }, [])
- op_log_trace("renderer_js_common", `createTextInstance returned, widget: ${Deno.inspect(textInstance)}`)
+ InternalApi.op_log_trace("renderer_js_common", `createTextInstance is called, text: ${text}, rootContainer: ${Deno.inspect(rootContainer)}`)
+ const textInstance = createWidget(hostContext, "gauntlet:text_part", { value: text })
+ InternalApi.op_log_trace("renderer_js_common", `createTextInstance returned, widget: ${Deno.inspect(textInstance)}`)
return textInstance;
},
appendInitialChild: (parentInstance: Instance, child: Instance | TextInstance): void => {
- op_log_trace("renderer_js_common", `appendInitialChild is called, parentInstance: ${Deno.inspect(parentInstance)}, child: ${Deno.inspect(child)}`)
+ InternalApi.op_log_trace("renderer_js_common", `appendInitialChild is called, parentInstance: ${Deno.inspect(parentInstance)}, child: ${Deno.inspect(child)}`)
parentInstance.widgetChildren.push(child)
},
@@ -217,7 +197,7 @@ export const createHostConfig = (): HostConfig<
_rootContainer: RootUiWidget,
_hostContext: HostContext
): boolean => {
- op_log_trace("renderer_js_common", `finalizeInitialChildren is called, instance: ${Deno.inspect(instance)}, type: ${type}, props: ${Deno.inspect(props)}`)
+ InternalApi.op_log_trace("renderer_js_common", `finalizeInitialChildren is called, instance: ${Deno.inspect(instance)}, type: ${type}, props: ${Deno.inspect(props)}`)
return false;
},
@@ -229,15 +209,16 @@ export const createHostConfig = (): HostConfig<
_rootContainer: RootUiWidget,
_hostContext: HostContext,
): UpdatePayload | null => {
- op_log_trace("renderer_js_common", `prepareUpdate is called, instance: ${Deno.inspect(instance)}, type: ${type}, oldProps: ${Deno.inspect(oldProps)}, newProps: ${Deno.inspect(newProps)}`)
+ InternalApi.op_log_trace("renderer_js_common", `prepareUpdate is called, instance: ${Deno.inspect(instance)}, type: ${type}, oldProps: ${Deno.inspect(oldProps)}, newProps: ${Deno.inspect(newProps)}`)
const diff = shallowDiff(oldProps, newProps);
- op_log_trace("renderer_js_common", `prepareUpdate shallowDiff returned: ${Deno.inspect(diff)}`)
+ InternalApi.op_log_trace("renderer_js_common", `prepareUpdate shallowDiff returned: ${Deno.inspect(diff)}`)
return diff;
},
shouldSetTextContent: (_type: ComponentType, _props: PropsWithChildren): boolean => {
return false;
},
getRootHostContext: (_rootContainer: RootUiWidget): HostContext | null => {
+ const componentModel = InternalApi.op_component_model();
return new HostContext(1, componentModel);
},
@@ -301,63 +282,51 @@ export const createHostConfig = (): HostConfig<
keepChildren: boolean,
recyclableInstance: null | Instance,
): Instance {
- op_log_trace("renderer_js_persistence", `cloneInstance is called, instance: ${Deno.inspect(instance)}, updatePayload: ${Deno.inspect(updatePayload)}, type: ${type}, oldProps: ${Deno.inspect(oldProps)}, newProps: ${Deno.inspect(newProps)}, keepChildren: ${keepChildren}, recyclableInstance: ${Deno.inspect(recyclableInstance)}`)
-
- const recyclableId = recyclableInstance != null ? recyclableInstance.widgetId : undefined;
+ InternalApi.op_log_trace("renderer_js_persistence", `cloneInstance is called, instance: ${Deno.inspect(instance)}, updatePayload: ${Deno.inspect(updatePayload)}, type: ${type}, oldProps: ${Deno.inspect(oldProps)}, newProps: ${Deno.inspect(newProps)}, keepChildren: ${keepChildren}, recyclableInstance: ${Deno.inspect(recyclableInstance)}`)
let clonedInstance: Instance;
if (keepChildren) {
if (updatePayload !== null) {
- clonedInstance = createWidget(recyclableId, instance.hostContext, type, newProps, instance.widgetChildren)
+ clonedInstance = createWidget(instance.hostContext, type, newProps, instance.widgetChildren)
} else {
- clonedInstance = createWidget(recyclableId, instance.hostContext, type, oldProps, instance.widgetChildren)
+ clonedInstance = createWidget(instance.hostContext, type, oldProps, instance.widgetChildren)
}
} else {
if (updatePayload !== null) {
- clonedInstance = createWidget(recyclableId, instance.hostContext, type, newProps, [])
+ clonedInstance = createWidget(instance.hostContext, type, newProps, [])
} else {
- clonedInstance = createWidget(recyclableId, instance.hostContext, type, oldProps, [])
+ clonedInstance = createWidget(instance.hostContext, type, oldProps, [])
}
}
- op_log_trace("renderer_js_persistence", `cloneInstance returned, widget: ${Deno.inspect(clonedInstance)}`)
+ InternalApi.op_log_trace("renderer_js_persistence", `cloneInstance returned, widget: ${Deno.inspect(clonedInstance)}`)
return clonedInstance;
},
createContainerChildSet(container: RootUiWidget): ChildSet {
- op_log_trace("renderer_js_persistence", `createContainerChildSet is called, container: ${Deno.inspect(container)}`)
+ InternalApi.op_log_trace("renderer_js_persistence", `createContainerChildSet is called, container: ${Deno.inspect(container)}`)
return []
},
appendChildToContainerChildSet(childSet: ChildSet, child: Instance | TextInstance): void {
- op_log_trace("renderer_js_persistence", `appendChildToContainerChildSet is called, childSet: ${Deno.inspect(childSet)}, child: ${Deno.inspect(child)}`)
+ InternalApi.op_log_trace("renderer_js_persistence", `appendChildToContainerChildSet is called, childSet: ${Deno.inspect(childSet)}, child: ${Deno.inspect(child)}`)
childSet.push(child);
},
finalizeContainerChildren(container: RootUiWidget, newChildren: ChildSet): void {
- op_log_trace("renderer_js_persistence", `finalizeContainerChildren is called, container: ${Deno.inspect(container)}, newChildren: ${Deno.inspect(newChildren)}`)
+ InternalApi.op_log_trace("renderer_js_persistence", `finalizeContainerChildren is called, container: ${Deno.inspect(container)}, newChildren: ${Deno.inspect(newChildren)}`)
},
replaceContainerChildren(container: RootUiWidget, newChildren: ChildSet): void {
- // op_log_info("renderer_js_persistence", `replaceContainerChildren is called, container: ${Deno.inspect(container)}, newChildren: ${Deno.inspect(newChildren, { depth: Number.MAX_VALUE })}`)
+ InternalApi.op_log_trace("renderer_js_persistence", `replaceContainerChildren is called, container: ${Deno.inspect(container)}, newChildren: ${Deno.inspect(newChildren)}`)
container.widgetChildren = newChildren
- const containerComponent = { content: newChildren.map(value => convertComponents(value)) }
-
- // op_log_info("renderer_js_persistence", `Converted container: ${Deno.inspect(containerComponent, { depth: Number.MAX_VALUE })}`)
-
- op_react_replace_view(
- gauntletContextValue.renderLocation(),
- gauntletContextValue.isBottommostView(),
- gauntletContextValue.entrypointId(),
- gauntletContextValue.entrypointName(),
- containerComponent
- )
+ InternalApi.op_react_replace_view(gauntletContextValue.renderLocation(), gauntletContextValue.isBottommostView(), gauntletContextValue.entrypointId(), container)
},
cloneHiddenInstance(
@@ -379,24 +348,6 @@ export const createHostConfig = (): HostConfig<
supportsHydration: false
});
-
-function convertComponents(widget: UiWidget): any {
- const widgetProperties = Object.fromEntries(
- Object.entries(widget.widgetProperties)
- .filter(([_, value]) => typeof value !== "function")
- );
-
- const widgetChildren = widget.widgetChildren
- .map(child => convertComponents(child))
-
- return {
- widgetId: widget.widgetId,
- widgetType: widget.widgetType,
- widgetProperties: widgetProperties,
- widgetChildren: widgetChildren,
- }
-}
-
function shallowDiff(oldObj: Record, newObj: Record): string[] | null {
const uniqueProps = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
const diff = Array.from(uniqueProps)
@@ -416,7 +367,7 @@ const createTracedHostConfig = (hostConfig: any) => new Proxy(hostConfig, {
return function _noop(...args: any[]) {
console.log('MethodTrace Stub:', propKey, ...args.map(function (arg) {
- return Deno.inspect(arg, { depth: 1 });
+ return Deno.inspect(arg, {depth: 1});
}));
}
}
@@ -424,7 +375,7 @@ const createTracedHostConfig = (hostConfig: any) => new Proxy(hostConfig, {
if (typeof f === 'function') {
return function _traced(this: any, ...args: any[]) {
console.log('MethodTrace:', propKey, ...args.map(function (arg) {
- return Deno.inspect(arg, { depth: 1 });
+ return Deno.inspect(arg, {depth: 1});
}));
return f.apply(this, args);
@@ -439,15 +390,7 @@ export function clearRenderer() {
gauntletContextValue.clear()
}
-export function rerender(view: ReactNode) {
- gauntletContextValue.rerender(view)
-}
-
-export function popView() {
- gauntletContextValue.popView()
-}
-
-export function render(entrypointId: string, entrypointName: string, renderLocation: RenderLocation, view: ReactNode): UiWidget {
+export function render(entrypointId: string, renderLocation: RenderLocation, view: ReactNode): UiWidget {
const hostConfig = createHostConfig();
// const reconciler = ReactReconciler(createTracedHostConfig(hostConfig));
@@ -462,7 +405,6 @@ export function render(entrypointId: string, entrypointName: string, renderLocat
gauntletContextValue.reset(
entrypointId,
- entrypointName,
renderLocation,
view,
(node: ReactNode) => {
diff --git a/js/react_renderer/tsconfig.json b/js/react_renderer/tsconfig.json
index 292e4bb..7f14964 100644
--- a/js/react_renderer/tsconfig.json
+++ b/js/react_renderer/tsconfig.json
@@ -3,10 +3,10 @@
"strict": true,
"module": "ES2022",
"esModuleInterop": true,
- "target": "ESNext",
+ "target": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
- "types": ["@project-gauntlet/typings", "@types/deno"],
+ "types": ["@project-gauntlet/typings", "@project-gauntlet/deno"]
},
"lib": ["ES2020"]
}
\ No newline at end of file
diff --git a/js/scenario_runner_cli/package.json b/js/scenario_runner_cli/package.json
index 932a58c..09b97d8 100644
--- a/js/scenario_runner_cli/package.json
+++ b/js/scenario_runner_cli/package.json
@@ -7,14 +7,14 @@
},
"type": "module",
"dependencies": {
- "commander": "^12.1.0"
+ "commander": "^11.1.0"
},
"devDependencies": {
- "@rollup/plugin-commonjs": "^28.0.2",
- "@rollup/plugin-node-resolve": "^16.0.0",
- "@rollup/plugin-typescript": "^12.1.2",
- "@types/node": "^22.10.2",
- "tslib": "^2.8.1",
- "typescript": "^5.7.2"
+ "@rollup/plugin-commonjs": "^25.0.7",
+ "@rollup/plugin-node-resolve": "^15.2.3",
+ "@rollup/plugin-typescript": "^11.1.5",
+ "@types/node": "^18.17.1",
+ "tslib": "^2.6.2",
+ "typescript": "^5.3.3"
}
}
diff --git a/js/scenario_runner_cli/src/main.ts b/js/scenario_runner_cli/src/main.ts
index dc53787..bb4b93f 100644
--- a/js/scenario_runner_cli/src/main.ts
+++ b/js/scenario_runner_cli/src/main.ts
@@ -1,7 +1,7 @@
import { Command } from 'commander';
import { spawnSync } from "node:child_process";
import path from "node:path";
-import { existsSync, rmSync } from "node:fs";
+import { existsSync, readdirSync, rmSync } from "node:fs";
const program = new Command();
@@ -10,63 +10,146 @@ program
.description('Gauntlet Scenario Runner Tool');
program.command('run-scenarios')
+ .argument('[plugin]')
+ .action(async (plugin) => {
+ await runScenarios(plugin)
+ });
+
+program.command('run-screenshot-gen')
.argument('[plugin]')
.argument('[entrypoint]')
.action(async (plugin, entrypoint) => {
- await runScenarios(plugin, entrypoint)
+ await runScreenshotGen(plugin, entrypoint)
});
await program.parseAsync(process.argv);
-async function runScenarios(
- expectedPlugin: string | undefined,
- expectedEntrypoint: string | undefined
-) {
+async function sleep(ms: number) {
+ return new Promise((r) => setTimeout(r, ms));
+}
+
+async function runScenarios(expectedPlugin: string | undefined) {
const projectRoot = path.resolve(process.cwd(), '..', '..');
- console.log("Building scenario plugins...")
- buildScenarioPlugins(projectRoot)
-
- console.log("Running scenario runner...")
- const scenarios = path.join(projectRoot, "example_plugins");
+ const scenarios = path.join(projectRoot, "scenarios");
+ const scenariosData = path.join(scenarios, "data");
const scenariosRun = path.join(scenarios, "run");
- const env: Record = {
- RUST_BACKTRACE: "1",
- RUST_LOG: "gauntlet_server=INFO,gauntlet_client=INFO,gauntlet_scenario_runner=INFO",
- XDG_DATA_HOME: path.join(scenariosRun, "data"),
- XDG_CONFIG_HOME: path.join(scenariosRun, "config"),
- XDG_CACHE_HOME: path.join(scenariosRun, "cache"),
- XDG_STATE_HOME: path.join(scenariosRun, "state"),
- GAUNTLET_SCENARIOS_DIR: path.join(scenarios, "scenarios"),
- GAUNTLET_SCENARIOS_PLUGINS_DIR: path.join(scenarios, "plugins"),
- GAUNTLET_SCENARIOS_SCREENSHOTS_DIR: path.join(scenarios, "out_screenshot"),
- };
+ console.log("Building server")
+ buildServer(projectRoot)
- if (expectedPlugin) {
- env.GAUNTLET_SCENARIOS_ONLY_PLUGIN = expectedPlugin;
- }
- if (expectedEntrypoint) {
- env.GAUNTLET_SCENARIOS_ONLY_ENTRYPOINT = expectedEntrypoint;
- }
+ console.log("Building scenario plugins")
+ buildScenarioPlugins(projectRoot)
- const runReturn = spawnSync('cargo', ['run', '--package', 'gauntlet-scenario-runner', '--features', 'scenario_runner'], {
+ for (const pluginName of readdirSync(scenariosData)) {
+ if (expectedPlugin) {
+ if (pluginName != expectedPlugin) {
+ continue
+ }
+ }
+
+ console.log("Starting runner")
+
+ const backendProcess = spawnSync('target/debug/gauntlet', {
+ stdio: "inherit",
+ cwd: projectRoot,
+ env: Object.assign(process.env, {
+ RUST_LOG: "server=info",
+ GAUNTLET_SCENARIO_RUNNER_TYPE: "scenario_runner",
+ GAUNTLET_SCENARIOS_DIR: scenarios,
+ GAUNTLET_SCENARIO_PLUGIN_NAME: pluginName,
+ XDG_DATA_HOME: path.join(scenariosRun, "data"),
+ XDG_CONFIG_HOME: path.join(scenariosRun, "config"),
+ XDG_CACHE_HOME: path.join(scenariosRun, "cache"),
+ XDG_STATE_HOME: path.join(scenariosRun, "state"),
+ })
+ })
+
+ if (backendProcess.status !== 0) {
+ throw new Error(`Unable to run scenario runner, status: ${JSON.stringify(backendProcess)}`);
+ }
+
+ if (existsSync(scenariosRun)) {
+ rmSync(scenariosRun, { recursive: true })
+ }
+
+ await sleep(1000)
+ }
+}
+
+async function runScreenshotGen(expectedPlugin: string | undefined, expectedEntrypoint: string | undefined) {
+ const projectRoot = path.resolve(process.cwd(), '..', '..');
+ const scenarios = path.join(projectRoot, "scenarios");
+ const scenariosOut = path.join(scenarios, "out");
+
+ buildServer(projectRoot)
+
+ for (const plugin of readdirSync(scenariosOut)) {
+ if (expectedPlugin) {
+ if (plugin != expectedPlugin) {
+ continue
+ }
+ }
+
+ const pluginDir = path.join(scenariosOut, plugin);
+
+ for (const entrypoint of readdirSync(pluginDir)) {
+ if (expectedEntrypoint) {
+ if (entrypoint != expectedEntrypoint) {
+ continue
+ }
+ }
+
+ const entrypointDir = path.join(pluginDir, entrypoint);
+
+ for (const scenario of readdirSync(entrypointDir)) {
+ const scenarioFile = path.join(entrypointDir, scenario);
+
+ console.log("Starting screenshot generating runner for scenario: " + scenarioFile)
+
+ const scenarioName = path.parse(scenario).name;
+ const entrypointName = path.parse(entrypoint).name;
+
+ let scenarioNameTitle = entrypointName
+ .split("-")
+ .filter(x => x.length > 0)
+ .map(x => (x.charAt(0).toUpperCase() + x.slice(1)))
+ .join(" ");
+
+ const frontendReturn = spawnSync('target/debug/gauntlet', {
+ stdio: "inherit",
+ cwd: projectRoot,
+ env: Object.assign(process.env, {
+ RUST_LOG: "client=info",
+ GAUNTLET_SCENARIO_RUNNER_TYPE: "screenshot_gen",
+ GAUNTLET_SCREENSHOT_GEN_IN: scenarioFile,
+ GAUNTLET_SCREENSHOT_GEN_OUT: path.join(scenarios, "out-screenshot", plugin, entrypoint, scenarioName + ".png"),
+ GAUNTLET_SCREENSHOT_GEN_NAME: scenarioNameTitle,
+ })
+ });
+
+ if (frontendReturn.status !== 0) {
+ throw new Error(`Unable to run frontend, status: ${JSON.stringify(frontendReturn)}`);
+ }
+
+ console.log("Runner exited")
+ }
+ }
+ }
+}
+
+function buildServer(projectRoot: string) {
+ const serverBuildResult = spawnSync('cargo', ['build', '--features', 'scenario_runner'], {
stdio: "inherit",
cwd: projectRoot,
- env: Object.assign(process.env, env)
+ env: Object.assign(process.env, {
+ RUST_BACKTRACE: "1"
+ })
});
- if (runReturn.status !== 0) {
- throw new Error(`Unable to run scenario, status: ${JSON.stringify(runReturn)}`);
+ if (serverBuildResult.status !== 0) {
+ throw new Error(`Unable to compile server, status: ${JSON.stringify(serverBuildResult)}`);
}
-
- console.log("Runner is done, cleaning up...")
-
- if (existsSync(scenariosRun)) {
- rmSync(scenariosRun, { recursive: true })
- }
-
- console.log("Done")
}
function buildScenarioPlugins(projectRoot: string) {
diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts
index afa8470..61d2791 100644
--- a/js/typings/index.d.ts
+++ b/js/typings/index.d.ts
@@ -1,51 +1,12 @@
// js runtime types
-type DesktopPathAction = DesktopPathActionAdd | DesktopPathActionRemove
-
-type DesktopPathActionAdd = {
- type: "add",
- id: string,
- data: DATA
+interface DenoCore {
+ opAsync: (op: "op_plugin_get_pending_event") => Promise
+ ops: InternalApi
}
-type DesktopPathActionRemove = {
- type: "remove"
- id: string
-}
-
-type LinuxDesktopApplicationData = {
- name: string
- icon: ArrayBuffer | undefined,
- desktop_file_path: string,
- startup_wm_class: string | undefined,
-}
-
-type MacOSDesktopApplicationData = {
- name: string
- path: string,
- icon: ArrayBuffer | undefined,
-}
-
-type WindowsDesktopApplicationData = {
- name: string
- path: string,
- icon: ArrayBuffer | undefined,
-}
-
-type MacOSDesktopSettingsPre13Data = {
- name: string
- path: string,
- icon: ArrayBuffer | undefined,
-}
-
-type MacOSDesktopSettings13AndPostData = {
- name: string
- preferences_id: string
- icon: ArrayBuffer | undefined,
-}
-
-type PluginEvent = ViewEvent | NotReactsKeyboardEvent | RunCommand | RunGeneratedEntrypoint | OpenView | CloseView | PopView | OpenInlineView | RefreshSearchIndex
+type PluginEvent = ViewEvent | NotReactsKeyboardEvent | RunCommand | RunGeneratedCommand | OpenView | CloseView | OpenInlineView | ReloadSearchIndex | RefreshSearchIndex
type RenderLocation = "InlineView" | "View"
type ViewEvent = {
@@ -55,13 +16,10 @@ type ViewEvent = {
eventArguments: PropertyValue[]
}
-type KeyboardEventOrigin = "MainView" | "PluginView"
-
// naming to avoid collision
type NotReactsKeyboardEvent = {
type: "KeyboardEvent"
entrypointId: string
- origin: KeyboardEventOrigin
key: string
modifierShift: boolean
modifierControl: boolean
@@ -78,20 +36,14 @@ type CloseView = {
type: "CloseView"
}
-type PopView = {
- type: "PopView"
- entrypointId: string
-}
-
type RunCommand = {
type: "RunCommand"
entrypointId: string
}
-type RunGeneratedEntrypoint = {
- type: "RunGeneratedEntrypoint"
+type RunGeneratedCommand = {
+ type: "RunGeneratedCommand"
entrypointId: string
- actionIndex: number
}
type OpenInlineView = {
@@ -99,16 +51,19 @@ type OpenInlineView = {
text: string
}
+type ReloadSearchIndex = {
+ type: "ReloadSearchIndex"
+}
+
type RefreshSearchIndex = {
type: "RefreshSearchIndex"
}
-type PropertyValue = PropertyValueString | PropertyValueNumber | PropertyValueBool | PropertyValueUndefined | PropertyValueNull
+type PropertyValue = PropertyValueString | PropertyValueNumber | PropertyValueBool | PropertyValueUndefined
type PropertyValueString = { type: "String", value: string }
type PropertyValueNumber = { type: "Number", value: number }
type PropertyValueBool = { type: "Bool", value: boolean }
type PropertyValueUndefined = { type: "Undefined" }
-type PropertyValueNull = { type: "Null" }
type UiWidget = {
widgetId: number,
@@ -120,160 +75,46 @@ type UiWidget = {
type Props = { [key: string]: any };
type PropsWithChildren = { children?: UiWidget[] } & Props;
-type GeneratedEntrypointAccessory = GeneratedEntrypointTextAccessory | GeneratedEntrypointIconAccessory;
-
-interface GeneratedEntrypointTextAccessory {
- text: string
- icon?: string
- tooltip?: string
-}
-
-interface GeneratedEntrypointIconAccessory {
- icon: string
- tooltip?: string
-}
-
-type GeneratedSearchItem = {
+type AdditionalSearchItem = {
entrypoint_name: string,
entrypoint_id: string,
entrypoint_uuid: string,
entrypoint_icon: ArrayBuffer | undefined,
- entrypoint_actions: GeneratedSearchItemAction[],
- entrypoint_accessories: GeneratedEntrypointAccessory[],
}
-type GeneratedSearchItemAction = {
- id?: string,
- action_type: "Command" | "View"
- label: string,
-}
+interface InternalApi {
+ op_log_trace(target: string, message: string): void;
+ op_log_debug(target: string, message: string): void;
+ op_log_info(target: string, message: string): void;
+ op_log_warn(target: string, message: string): void;
+ op_log_error(target: string, message: string): void;
-declare module "gauntlet:core" {
- export function runPluginLoop(): Promise;
-}
+ op_component_model(): Record;
+ asset_data(path: string): Promise;
+ asset_data_blocking(path: string): number[];
-declare module "gauntlet:bridge/internal-all" {
- function open_settings(): void
- function run_numbat(input: string): { left: string, right: string }
- function current_os(): string
- function wayland(): boolean
-}
+ op_inline_view_endpoint_id(): string | null;
+ clear_inline_view(): void;
-declare module "gauntlet:bridge/internal-linux" {
- function linux_open_application(desktop_id: string): void
- function linux_x11_focus_window(window_id: string): void
- function linux_wayland_focus_window(window_id: string): void
- function linux_application_dirs(): string[]
- function linux_app_from_path(path: string): Promise>
- function application_x11_pending_event(): Promise
- function application_wayland_pending_event(): Promise
-}
+ get_command_generator_entrypoint_ids(): Promise
+ get_plugin_preferences(): Record;
+ get_entrypoint_preferences(entrypointId: string): Record;
+ plugin_preferences_required(): Promise