diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a7b29a29..184a971d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,10 +38,6 @@ jobs: with: node-version: 20 - - uses: actions/setup-go@v5 - with: - go-version: '1.22' - - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: @@ -81,7 +77,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install yaak CLI - run: go install github.com/yaakapp/yaakcli@latest + run: npm install -g @yaakapp/cli - name: Run lint run: npm run lint diff --git a/package-lock.json b/package-lock.json index de74b602..3b3bdd1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@tauri-apps/plugin-log": "^2.0.0-rc.1", "@tauri-apps/plugin-os": "^2.0.0-rc.1", "@tauri-apps/plugin-shell": "^2.0.0-rc.1", - "@yaakapp/api": "^0.1.15", + "@yaakapp/api": "^0.1.16", "buffer": "^6.0.3", "classnames": "^2.3.2", "cm6-graphql": "^0.0.9", @@ -59,7 +59,7 @@ "devDependencies": { "@tailwindcss/nesting": "^0.0.0-insiders.565cd3e", "@tanstack/react-query-devtools": "^5.45.1", - "@tauri-apps/cli": "^2.0.0-rc.2", + "@tauri-apps/cli": "^2.0.0-rc.12", "@types/node": "^18.7.10", "@types/papaparse": "^5.3.7", "@types/parse-color": "^1.0.1", @@ -2354,9 +2354,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.0.0-rc.3.tgz", - "integrity": "sha512-iNF95pieBmverl1EmQyqh+fhcIClS544fN5Ex5lAbYLTiHZ/gm3lOfVBhF6NPaKd/sfLuy7K1tfDXlHztBfANw==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.0.0-rc.12.tgz", + "integrity": "sha512-rNcVSyGHGz8vNk542isYKPk5fEMAsgmzER+1s9YYbGZCH7m4e0rH89p/P9W40I/Z4AZk4ZqjpEeajeS5izDI4g==", "dev": true, "bin": { "tauri": "tauri.js" @@ -2369,22 +2369,22 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.0.0-rc.3", - "@tauri-apps/cli-darwin-x64": "2.0.0-rc.3", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.0.0-rc.3", - "@tauri-apps/cli-linux-arm64-gnu": "2.0.0-rc.3", - "@tauri-apps/cli-linux-arm64-musl": "2.0.0-rc.3", - "@tauri-apps/cli-linux-x64-gnu": "2.0.0-rc.3", - "@tauri-apps/cli-linux-x64-musl": "2.0.0-rc.3", - "@tauri-apps/cli-win32-arm64-msvc": "2.0.0-rc.3", - "@tauri-apps/cli-win32-ia32-msvc": "2.0.0-rc.3", - "@tauri-apps/cli-win32-x64-msvc": "2.0.0-rc.3" + "@tauri-apps/cli-darwin-arm64": "2.0.0-rc.12", + "@tauri-apps/cli-darwin-x64": "2.0.0-rc.12", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.0.0-rc.12", + "@tauri-apps/cli-linux-arm64-gnu": "2.0.0-rc.12", + "@tauri-apps/cli-linux-arm64-musl": "2.0.0-rc.12", + "@tauri-apps/cli-linux-x64-gnu": "2.0.0-rc.12", + "@tauri-apps/cli-linux-x64-musl": "2.0.0-rc.12", + "@tauri-apps/cli-win32-arm64-msvc": "2.0.0-rc.12", + "@tauri-apps/cli-win32-ia32-msvc": "2.0.0-rc.12", + "@tauri-apps/cli-win32-x64-msvc": "2.0.0-rc.12" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-rc.3.tgz", - "integrity": "sha512-szYCSr/ChbCF+S6Wnr15TYpI2cZR07d+AQOiFGuScP0preM8Pbsk/sb0hfLwqzepjVFFNVWQba9sG7FEW2Y2XA==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-rc.12.tgz", + "integrity": "sha512-zYxcAH4reyqKkqCAybggszFWkBvC+ZyZPTWFKXXVQ20MZc1q+e/0UJYC8UKsaumrbi1uptgamvnM8yql56x5QQ==", "cpu": [ "arm64" ], @@ -2398,9 +2398,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-rc.3.tgz", - "integrity": "sha512-BJv6EJOY1DJbRzVtfg8CcBAlnS5OjhBAc5YKjh4BT7EyOcop8HStBSxhL6yjWrUP7eLR1iIsW/uSehVJwzYIdQ==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-rc.12.tgz", + "integrity": "sha512-eme7pQzEzeGCk13V3uxUNRnkVZJukqwHotqEb2RdovXqJWSyESrighBy4PBG5Xn6wNYTOyoquY9+In4TOfJAzw==", "cpu": [ "x64" ], @@ -2414,9 +2414,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-rc.3.tgz", - "integrity": "sha512-fwx805/xL4sF/EdMYqcUHQHzMYwo+OVTBTz5x/JWK8D57rnmLHAP+ZhnfFsZQLRo2QRT2l1Ye3bDyU+QRA1JFA==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-rc.12.tgz", + "integrity": "sha512-113T2NsLeoy6GXsqc0yjMoozt+KXzkAtUB7DL9Kcvx9IMfA87cUVaTNjnb2GFsoQqpCWGfHei3nr9n1PGEbwMg==", "cpu": [ "arm" ], @@ -2430,9 +2430,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-rc.3.tgz", - "integrity": "sha512-3KauzO1Ls4kuY0nr82S4X8XFxlQAMN+Mqp8LLqvQ+PPMp92XQAkPH7osQdoHIEoW5gsE69U2JaiQ5tHSqNM9og==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-rc.12.tgz", + "integrity": "sha512-9TrUyNg0vmsYF7IbG+/sybEeiz6ikA1Kjd6JjC4iwfXjRff8fuTR7CIOb06imabxbLzGP79qSAnGAeTXz8E7qA==", "cpu": [ "arm64" ], @@ -2446,9 +2446,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-rc.3.tgz", - "integrity": "sha512-ngHS0foffm1xO5gqnDKGeYMKj8ceGmrFP5dDldoaaMQubw1SyFa0pRUjb7fZSYiO7F4SOSa8NYeMqlF9peZmnQ==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-rc.12.tgz", + "integrity": "sha512-YvE40+wdkNcXhwUAJNPyhNzJ8YS3saJoxGj7mtNQeNeNrKhxyj6hA5T6gw9KtMkwBOp+HGtqn+eDXiu0X7BBHQ==", "cpu": [ "arm64" ], @@ -2462,9 +2462,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-rc.3.tgz", - "integrity": "sha512-0/am9pVvuUHGmz32M8ffz1fpLnc08j3nzcRe5wUdL2AxfT+wKMII+Dn99GtCVgcdDW4jSXDMRUwrBkGocGC2OA==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-rc.12.tgz", + "integrity": "sha512-q+MJp/lSA5WINs78dCFMlU0/jQeUkGr9GHbKeppcVcpkcY/1vog70b4KhneyvbuklKBn/V8kd0FtIKCn8VP+KQ==", "cpu": [ "x64" ], @@ -2478,9 +2478,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-rc.3.tgz", - "integrity": "sha512-r7mRi8q8TqTFVjb9kAsU7IgwUgno2s8Ip4xwq9psQhlRE3JGEZQmSEcy1jqTjfl6KFh6lJcDR7l+9/EMhL/D3Q==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-rc.12.tgz", + "integrity": "sha512-5zodtleH2GFsB9lszDYrzPTLcr+MMqtpQpJWHATC1K03bLEA8ia8zSdBqRwm7u8NraMLl8TE7hc7hwq0uxGEcg==", "cpu": [ "x64" ], @@ -2494,9 +2494,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.0.0-rc.3.tgz", - "integrity": "sha512-2J6KjmDIQCw6HF1X6/yPcd+JLl7pxrH2zVMGmNllaoWhHeByvRobqFWnT7gcdHaA3dGTo432CwWvOgTgrINQpQ==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.0.0-rc.12.tgz", + "integrity": "sha512-nSu6VHpuq61DYM2YowLDLDwkK8im7dzYxIHXs+h8/rhkmadTujGhbyUhHPI1STA6hNyITUtSFpo6P2mEbfpAIg==", "cpu": [ "arm64" ], @@ -2510,9 +2510,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-rc.3.tgz", - "integrity": "sha512-8q75CsHDSEDdgi6xPwim+BaQZFCswK2Dn/qL38V3Mh9kmVvC8oGJMPC66bC20dF+v3KWeFm2FNNGQqOSXCveHg==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-rc.12.tgz", + "integrity": "sha512-d/4y57OisMuB+MUkTpZsryQRi9ZQXQ8SsMhrvEgu8sbX8/WRm0iZyGuIZ01RlZZHLIasXbKTkPX+hPQC5Juk8Q==", "cpu": [ "ia32" ], @@ -2526,9 +2526,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-rc.3.tgz", - "integrity": "sha512-qeBRJYalahxEXolekcpZJ/HBrIJacG2NWJBGhhi797mIwnbmlpbHMc8blIJtNNNwVUb2BjXuxKQVfojQ5YYrcg==", + "version": "2.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-rc.12.tgz", + "integrity": "sha512-RsPUvsbFza03ysh0nU2nM3P2CVWz9cu7CRHwOEdtXjWWNREHUYCaVpqQKz0tn2sG19yXiNIB40iqrIBUmb/IoA==", "cpu": [ "x64" ], @@ -2986,9 +2986,9 @@ "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" }, "node_modules/@yaakapp/api": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/@yaakapp/api/-/api-0.1.15.tgz", - "integrity": "sha512-4nrImM9r4Afih0CcG6PWtGA6Luap/Ki5ZVl56WejWA8WPpy8AhEpC0KErpJChNzzqRjgK9ZEWdQNsBHppzAs8A==", + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@yaakapp/api/-/api-0.1.16.tgz", + "integrity": "sha512-rWXXb0iUiUk6jtLhZFcbdMRIf6dYXY9wyIvFG9rVpyMxB0LWjim8VpvwF4IK4Y/paxvZ9bjvnaD5PAB4pH6prg==", "dependencies": { "@types/node": "^22.0.0" } diff --git a/package.json b/package.json index b26539fe..15ddb2db 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@tauri-apps/plugin-log": "^2.0.0-rc.1", "@tauri-apps/plugin-os": "^2.0.0-rc.1", "@tauri-apps/plugin-shell": "^2.0.0-rc.1", - "@yaakapp/api": "^0.1.15", + "@yaakapp/api": "^0.1.16", "buffer": "^6.0.3", "classnames": "^2.3.2", "cm6-graphql": "^0.0.9", @@ -74,7 +74,7 @@ "devDependencies": { "@tailwindcss/nesting": "^0.0.0-insiders.565cd3e", "@tanstack/react-query-devtools": "^5.45.1", - "@tauri-apps/cli": "^2.0.0-rc.2", + "@tauri-apps/cli": "^2.0.0-rc.12", "@types/node": "^18.7.10", "@types/papaparse": "^5.3.7", "@types/parse-color": "^1.0.1", diff --git a/plugin-runtime-types/package.json b/plugin-runtime-types/package.json index e8122205..5a045d8e 100644 --- a/plugin-runtime-types/package.json +++ b/plugin-runtime-types/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp/api", - "version": "0.1.15", + "version": "0.1.16", "main": "lib/index.js", "typings": "./lib/index.d.ts", "files": [ diff --git a/plugin-runtime-types/src/gen/GrpcEvent.ts b/plugin-runtime-types/src/gen/GrpcEvent.ts index 67028185..60eb4fd6 100644 --- a/plugin-runtime-types/src/gen/GrpcEvent.ts +++ b/plugin-runtime-types/src/gen/GrpcEvent.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { GrpcEventType } from "./GrpcEventType"; -export type GrpcEvent = { id: string, model: "grpc_event", workspaceId: string, requestId: string, connectionId: string, createdAt: string, content: string, eventType: GrpcEventType, metadata: { [key: string]: string }, status: number | null, error: string | null, }; +export type GrpcEvent = { id: string, model: "grpc_event", workspaceId: string, requestId: string, connectionId: string, createdAt: string, updatedAt: string, content: string, eventType: GrpcEventType, metadata: { [key: string]: string }, status: number | null, error: string | null, }; diff --git a/plugin-runtime-types/src/gen/InternalEventPayload.ts b/plugin-runtime-types/src/gen/InternalEventPayload.ts index 0d195899..444b4eb1 100644 --- a/plugin-runtime-types/src/gen/InternalEventPayload.ts +++ b/plugin-runtime-types/src/gen/InternalEventPayload.ts @@ -1,6 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { BootRequest } from "./BootRequest"; -import type { BootResponse } from "./BootResponse"; import type { CallHttpRequestActionRequest } from "./CallHttpRequestActionRequest"; import type { CallTemplateFunctionRequest } from "./CallTemplateFunctionRequest"; import type { CallTemplateFunctionResponse } from "./CallTemplateFunctionResponse"; @@ -19,10 +17,12 @@ import type { GetHttpRequestByIdResponse } from "./GetHttpRequestByIdResponse"; import type { GetTemplateFunctionsResponse } from "./GetTemplateFunctionsResponse"; import type { ImportRequest } from "./ImportRequest"; import type { ImportResponse } from "./ImportResponse"; +import type { PluginBootRequest } from "./PluginBootRequest"; +import type { PluginBootResponse } from "./PluginBootResponse"; import type { RenderHttpRequestRequest } from "./RenderHttpRequestRequest"; import type { RenderHttpRequestResponse } from "./RenderHttpRequestResponse"; import type { SendHttpRequestRequest } from "./SendHttpRequestRequest"; import type { SendHttpRequestResponse } from "./SendHttpRequestResponse"; import type { ShowToastRequest } from "./ShowToastRequest"; -export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" } & EmptyResponse; +export type InternalEventPayload = { "type": "boot_request" } & PluginBootRequest | { "type": "boot_response" } & PluginBootResponse | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" } & EmptyResponse; diff --git a/plugin-runtime-types/src/gen/Model.ts b/plugin-runtime-types/src/gen/Model.ts index b60d1670..93110f52 100644 --- a/plugin-runtime-types/src/gen/Model.ts +++ b/plugin-runtime-types/src/gen/Model.ts @@ -8,7 +8,8 @@ import type { GrpcRequest } from "./GrpcRequest"; import type { HttpRequest } from "./HttpRequest"; import type { HttpResponse } from "./HttpResponse"; import type { KeyValue } from "./KeyValue"; +import type { Plugin } from "./Plugin"; import type { Settings } from "./Settings"; import type { Workspace } from "./Workspace"; -export type Model = Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | KeyValue | Workspace | CookieJar | Settings; +export type Model = Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | KeyValue | Workspace | CookieJar | Settings | Plugin; diff --git a/plugin-runtime-types/src/gen/Plugin.ts b/plugin-runtime-types/src/gen/Plugin.ts new file mode 100644 index 00000000..a3abb0d5 --- /dev/null +++ b/plugin-runtime-types/src/gen/Plugin.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Plugin = { id: string, model: "plugin", createdAt: string, updatedAt: string, checkedAt: string | null, name: string, version: string, uri: string, enabled: boolean, }; diff --git a/plugin-runtime-types/src/gen/BootRequest.ts b/plugin-runtime-types/src/gen/PluginBootRequest.ts similarity index 68% rename from plugin-runtime-types/src/gen/BootRequest.ts rename to plugin-runtime-types/src/gen/PluginBootRequest.ts index 398ca72c..dcbc7b7c 100644 --- a/plugin-runtime-types/src/gen/BootRequest.ts +++ b/plugin-runtime-types/src/gen/PluginBootRequest.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type BootRequest = { dir: string, }; +export type PluginBootRequest = { dir: string, }; diff --git a/plugin-runtime-types/src/gen/BootResponse.ts b/plugin-runtime-types/src/gen/PluginBootResponse.ts similarity index 52% rename from plugin-runtime-types/src/gen/BootResponse.ts rename to plugin-runtime-types/src/gen/PluginBootResponse.ts index 1c1654de..b9023922 100644 --- a/plugin-runtime-types/src/gen/BootResponse.ts +++ b/plugin-runtime-types/src/gen/PluginBootResponse.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type BootResponse = { name: string, version: string, capabilities: Array, }; +export type PluginBootResponse = { name: string, version: string, capabilities: Array, }; diff --git a/plugin-runtime-types/src/index.ts b/plugin-runtime-types/src/index.ts index ad2cb96f..9772e09f 100644 --- a/plugin-runtime-types/src/index.ts +++ b/plugin-runtime-types/src/index.ts @@ -2,8 +2,6 @@ export type * from './plugins'; export type * from './themes'; // TODO: The next ts-rs release includes the ability to put everything in 1 file! -export * from './gen/BootRequest'; -export * from './gen/BootResponse'; export * from './gen/CallHttpRequestActionArgs'; export * from './gen/CallHttpRequestActionRequest'; export * from './gen/CallTemplateFunctionRequest'; @@ -63,3 +61,4 @@ export * from './gen/TemplateFunctionSelectOption'; export * from './gen/TemplateFunctionTextArg'; export * from './gen/ToastVariant'; export * from './gen/Workspace'; +export * from './gen/Plugin'; diff --git a/plugin-runtime-types/src/plugins/index.ts b/plugin-runtime-types/src/plugins/index.ts index 6bfe7f35..95f77053 100644 --- a/plugin-runtime-types/src/plugins/index.ts +++ b/plugin-runtime-types/src/plugins/index.ts @@ -9,7 +9,7 @@ export type { Context } from './Context'; /** * The global structure of a Yaak plugin */ -export type Plugin = { +export type PluginDefinition = { importer?: ImporterPlugin; theme?: ThemePlugin; filter?: FilterPlugin; diff --git a/src-tauri/migrations/20240829131004_plugins.sql b/src-tauri/migrations/20240829131004_plugins.sql new file mode 100644 index 00000000..da8adb00 --- /dev/null +++ b/src-tauri/migrations/20240829131004_plugins.sql @@ -0,0 +1,12 @@ +CREATE TABLE plugins +( + id TEXT NOT NULL + PRIMARY KEY, + model TEXT DEFAULT 'plugin' NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + checked_at DATETIME NULL, + enabled BOOLEAN NOT NULL, + directory TEXT NULL NOT NULL, + url TEXT NULL +); diff --git a/src-tauri/src/analytics.rs b/src-tauri/src/analytics.rs index 59638cf9..dda9a053 100644 --- a/src-tauri/src/analytics.rs +++ b/src-tauri/src/analytics.rs @@ -28,6 +28,7 @@ pub enum AnalyticsResource { HttpRequest, HttpResponse, KeyValue, + Plugin, Setting, Sidebar, Theme, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5f49c458..0ecde01d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -40,7 +40,7 @@ use crate::updates::{UpdateMode, YaakUpdater}; use crate::window_menu::app_menu; use yaak_models::models::{ CookieJar, Environment, EnvironmentVariable, Folder, GrpcConnection, GrpcEvent, GrpcEventType, - GrpcRequest, HttpRequest, HttpResponse, KeyValue, ModelType, Settings, Workspace, + GrpcRequest, HttpRequest, HttpResponse, KeyValue, ModelType, Plugin, Settings, Workspace, }; use yaak_models::queries::{ cancel_pending_grpc_connections, cancel_pending_responses, create_default_http_response, @@ -49,16 +49,17 @@ use yaak_models::queries::{ delete_http_response, delete_workspace, duplicate_grpc_request, duplicate_http_request, generate_model_id, get_cookie_jar, get_environment, get_folder, get_grpc_connection, get_grpc_request, get_http_request, get_http_response, get_key_value_raw, - get_or_create_settings, get_workspace, list_cookie_jars, list_environments, list_folders, - list_grpc_connections, list_grpc_events, list_grpc_requests, list_http_requests, - list_http_responses, list_workspaces, set_key_value_raw, update_response_if_id, + get_or_create_settings, get_plugin, get_workspace, list_cookie_jars, list_environments, + list_folders, list_grpc_connections, list_grpc_events, list_grpc_requests, list_http_requests, + list_http_responses, list_plugins, list_workspaces, set_key_value_raw, update_response_if_id, update_settings, upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection, - upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_workspace, + upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace, }; use yaak_plugin_runtime::events::{ CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse, GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, - InternalEvent, InternalEventPayload, RenderHttpRequestResponse, SendHttpRequestResponse, + InternalEvent, InternalEventPayload, PluginBootResponse, RenderHttpRequestResponse, + SendHttpRequestResponse, }; use yaak_templates::{Parser, Tokens}; @@ -77,6 +78,9 @@ mod window_menu; const DEFAULT_WINDOW_WIDTH: f64 = 1100.0; const DEFAULT_WINDOW_HEIGHT: f64 = 600.0; +const MIN_WINDOW_WIDTH: f64 = 300.0; +const MIN_WINDOW_HEIGHT: f64 = 300.0; + #[derive(serde::Serialize)] #[serde(default, rename_all = "camelCase")] struct AppMetaData { @@ -1165,6 +1169,20 @@ async fn cmd_create_workspace(name: &str, w: WebviewWindow) -> Result, w: WebviewWindow) -> Result { + upsert_plugin( + &w, + Plugin { + directory: directory.into(), + url, + ..Default::default() + }, + ) + .await + .map_err(|e| e.to_string()) +} + #[tauri::command] async fn cmd_update_cookie_jar( cookie_jar: CookieJar, @@ -1408,10 +1426,9 @@ async fn cmd_list_grpc_requests( workspace_id: &str, w: WebviewWindow, ) -> Result, String> { - let requests = list_grpc_requests(&w, workspace_id) + list_grpc_requests(&w, workspace_id) .await - .map_err(|e| e.to_string())?; - Ok(requests) + .map_err(|e| e.to_string()) } #[tauri::command] @@ -1419,11 +1436,9 @@ async fn cmd_list_http_requests( workspace_id: &str, w: WebviewWindow, ) -> Result, String> { - let requests = list_http_requests(&w, workspace_id) + list_http_requests(&w, workspace_id) .await - .expect("Failed to find requests"); - // .map_err(|e| e.to_string()) - Ok(requests) + .map_err(|e| e.to_string()) } #[tauri::command] @@ -1431,11 +1446,27 @@ async fn cmd_list_environments( workspace_id: &str, w: WebviewWindow, ) -> Result, String> { - let environments = list_environments(&w, workspace_id) + list_environments(&w, workspace_id) .await - .expect("Failed to find environments"); + .map_err(|e| e.to_string()) +} - Ok(environments) +#[tauri::command] +async fn cmd_list_plugins(w: WebviewWindow) -> Result, String> { + list_plugins(&w).await.map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_plugin_info( + id: &str, + w: WebviewWindow, + plugin_manager: State<'_, PluginManager>, +) -> Result { + let plugin = get_plugin(&w, id).await.map_err(|e| e.to_string())?; + plugin_manager + .get_plugin_info(plugin.directory.as_str()) + .await + .ok_or("Failed to find plugin info".to_string()) } #[tauri::command] @@ -1689,6 +1720,7 @@ pub fn run() { cmd_create_folder, cmd_create_grpc_request, cmd_create_http_request, + cmd_create_plugin, cmd_create_workspace, cmd_curl_to_request, cmd_delete_all_grpc_connections, @@ -1730,6 +1762,8 @@ pub fn run() { cmd_list_grpc_requests, cmd_list_http_requests, cmd_list_http_responses, + cmd_list_plugins, + cmd_plugin_info, cmd_list_workspaces, cmd_metadata, cmd_new_nested_window, @@ -1831,6 +1865,7 @@ fn create_nested_window( .title(title) .parent(&window) .unwrap() + .min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) .inner_size(DEFAULT_WINDOW_WIDTH * 0.7, DEFAULT_WINDOW_HEIGHT * 0.9); // Add macOS-only things @@ -1841,7 +1876,7 @@ fn create_nested_window( .title_bar_style(TitleBarStyle::Overlay); } - // Add non-MacOS things + // Add non-macOS things #[cfg(not(target_os = "macos"))] { win_builder = win_builder.decorations(false); @@ -1874,7 +1909,7 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow { 100.0 + random::() * 30.0, 100.0 + random::() * 30.0, ) - .min_inner_size(300.0, 300.0) + .min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) .title(handle.package_info().name.to_string()); // Add macOS-only things @@ -1900,7 +1935,8 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow { return; } - match event.id().0.as_str() { + let event_id = event.id().0.as_str(); + match event_id { "quit" => exit(0), "close" => w.close().unwrap(), "zoom_reset" => w.emit("zoom_reset", true).unwrap(), @@ -1971,7 +2007,7 @@ fn monitor_plugin_events(app_handle: &AppHandle) { } async fn handle_plugin_event(app_handle: &AppHandle, event: &InternalEvent) { - info!("Got event to app {}", event.id); + // info!("Got event to app {}", event.id); let response_event: Option = match event.clone().payload { InternalEventPayload::CopyTextRequest(req) => { app_handle diff --git a/src-tauri/yaak_models/src/models.rs b/src-tauri/yaak_models/src/models.rs index 0bdd04bc..d14abbf3 100644 --- a/src-tauri/yaak_models/src/models.rs +++ b/src-tauri/yaak_models/src/models.rs @@ -655,6 +655,7 @@ pub struct GrpcEvent { pub request_id: String, pub connection_id: String, pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, pub content: String, pub event_type: GrpcEventType, pub metadata: HashMap, @@ -693,6 +694,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent { request_id: r.get("request_id")?, connection_id: r.get("connection_id")?, created_at: r.get("created_at")?, + updated_at: r.get("updated_at")?, content: r.get("content")?, event_type: serde_json::from_str(event_type.as_str()).unwrap_or_default(), metadata: serde_json::from_str(metadata.as_str()).unwrap_or_default(), @@ -702,6 +704,51 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent { } } +#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] +#[serde(default, rename_all = "camelCase")] +pub struct Plugin { + pub id: String, + #[ts(type = "\"plugin\"")] + pub model: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub checked_at: Option, + pub directory: String, + pub url: Option, + pub enabled: bool, +} + +#[derive(Iden)] +pub enum PluginIden { + #[iden = "plugins"] + Table, + Id, + Model, + CreatedAt, + UpdatedAt, + CheckedAt, + Directory, + Url, + Enabled, +} + +impl<'s> TryFrom<&Row<'s>> for Plugin { + type Error = rusqlite::Error; + + fn try_from(r: &Row<'s>) -> Result { + Ok(Plugin { + id: r.get("id")?, + model: r.get("model")?, + created_at: r.get("created_at")?, + updated_at: r.get("updated_at")?, + checked_at: r.get("checked_at")?, + url: r.get("url")?, + directory: r.get("directory")?, + enabled: r.get("enabled")?, + }) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] pub struct KeyValue { @@ -758,6 +805,7 @@ pub enum ModelType { TypeGrpcRequest, TypeHttpRequest, TypeHttpResponse, + TypePlugin, TypeWorkspace, } @@ -772,6 +820,7 @@ impl ModelType { ModelType::TypeGrpcRequest => "gr", ModelType::TypeHttpRequest => "rq", ModelType::TypeHttpResponse => "rs", + ModelType::TypePlugin => "pg", ModelType::TypeWorkspace => "wk", } .to_string() diff --git a/src-tauri/yaak_models/src/queries.rs b/src-tauri/yaak_models/src/queries.rs index 74a65495..eec3acfd 100644 --- a/src-tauri/yaak_models/src/queries.rs +++ b/src-tauri/yaak_models/src/queries.rs @@ -1,12 +1,7 @@ use std::fs; use crate::error::Result; -use crate::models::{ - CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection, - GrpcConnectionIden, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest, - HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, KeyValue, KeyValueIden, - ModelType, Settings, SettingsIden, Workspace, WorkspaceIden, -}; +use crate::models::{CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection, GrpcConnectionIden, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, KeyValue, KeyValueIden, ModelType, Plugin, PluginIden, Settings, SettingsIden, Workspace, WorkspaceIden}; use crate::plugin::SqliteConnection; use log::{debug, error}; use rand::distributions::{Alphanumeric, DistString}; @@ -848,7 +843,7 @@ pub async fn upsert_environment( serde_json::to_string(&environment.variables)?.into(), ]) .on_conflict( - OnConflict::column(GrpcEventIden::Id) + OnConflict::column(EnvironmentIden::Id) .update_columns([ EnvironmentIden::UpdatedAt, EnvironmentIden::Name, @@ -877,6 +872,88 @@ pub async fn get_environment(mgr: &impl Manager, id: &str) -> Res Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?) } +pub async fn get_plugin( + mgr: &impl Manager, + id: &str +) -> Result { + let dbm = &*mgr.state::(); + let db = dbm.0.lock().await.get().unwrap(); + + let (sql, params) = Query::select() + .from(PluginIden::Table) + .column(Asterisk) + .cond_where(Expr::col(EnvironmentIden::Id).eq(id)) + .build_rusqlite(SqliteQueryBuilder); + let mut stmt = db.prepare(sql.as_str())?; + Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?) +} + +pub async fn list_plugins( + mgr: &impl Manager, +) -> Result> { + let dbm = &*mgr.state::(); + let db = dbm.0.lock().await.get().unwrap(); + + let (sql, params) = Query::select() + .from(PluginIden::Table) + .column(Asterisk) + .order_by(PluginIden::CreatedAt, Order::Desc) + .build_rusqlite(SqliteQueryBuilder); + let mut stmt = db.prepare(sql.as_str())?; + let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?; + Ok(items.map(|v| v.unwrap()).collect()) +} + +pub async fn upsert_plugin( + window: &WebviewWindow, + plugin: Plugin, +) -> Result { + let id = match plugin.id.as_str() { + "" => generate_model_id(ModelType::TypePlugin), + _ => plugin.id.to_string(), + }; + let dbm = &*window.app_handle().state::(); + let db = dbm.0.lock().await.get().unwrap(); + + let (sql, params) = Query::insert() + .into_table(PluginIden::Table) + .columns([ + PluginIden::Id, + PluginIden::CreatedAt, + PluginIden::UpdatedAt, + PluginIden::CheckedAt, + PluginIden::Directory, + PluginIden::Url, + PluginIden::Enabled, + ]) + .values_panic([ + id.as_str().into(), + CurrentTimestamp.into(), + CurrentTimestamp.into(), + plugin.checked_at.into(), + plugin.directory.into(), + plugin.url.into(), + plugin.enabled.into(), + ]) + .on_conflict( + OnConflict::column(PluginIden::Id) + .update_columns([ + PluginIden::UpdatedAt, + PluginIden::CheckedAt, + PluginIden::Directory, + PluginIden::Url, + PluginIden::Enabled, + ]) + .to_owned(), + ) + .returning_all() + .build_rusqlite(SqliteQueryBuilder); + + let mut stmt = db.prepare(sql.as_str())?; + let m = stmt.query_row(&*params.as_params(), |row| row.try_into())?; + Ok(emit_upserted_model(window, m)) +} + pub async fn get_folder(mgr: &impl Manager, id: &str) -> Result { let dbm = &*mgr.state::(); let db = dbm.0.lock().await.get().unwrap(); diff --git a/src-tauri/yaak_plugin_runtime/src/events.rs b/src-tauri/yaak_plugin_runtime/src/events.rs index fb82fd3a..92724344 100644 --- a/src-tauri/yaak_plugin_runtime/src/events.rs +++ b/src-tauri/yaak_plugin_runtime/src/events.rs @@ -2,10 +2,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use ts_rs::TS; -use yaak_models::models::{ - CookieJar, Environment, Folder, GrpcConnection, GrpcEvent, GrpcRequest, HttpRequest, - HttpResponse, KeyValue, Settings, Workspace, -}; +use yaak_models::models::{CookieJar, Environment, Folder, GrpcConnection, GrpcEvent, GrpcRequest, HttpRequest, HttpResponse, KeyValue, Plugin, Settings, Workspace}; #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] @@ -21,8 +18,8 @@ pub struct InternalEvent { #[serde(rename_all = "snake_case", tag = "type")] #[ts(export)] pub enum InternalEventPayload { - BootRequest(BootRequest), - BootResponse(BootResponse), + BootRequest(PluginBootRequest), + BootResponse(PluginBootResponse), ImportRequest(ImportRequest), ImportResponse(ImportResponse), @@ -71,14 +68,14 @@ pub struct EmptyResponse {} #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export)] -pub struct BootRequest { +pub struct PluginBootRequest { pub dir: String, } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export)] -pub struct BootResponse { +pub struct PluginBootResponse { pub name: String, pub version: String, pub capabilities: Vec, @@ -401,4 +398,5 @@ pub enum Model { Workspace(Workspace), CookieJar(CookieJar), Settings(Settings), + Plugin(Plugin), } diff --git a/src-tauri/yaak_plugin_runtime/src/manager.rs b/src-tauri/yaak_plugin_runtime/src/manager.rs index 7d7cff11..2a0eb586 100644 --- a/src-tauri/yaak_plugin_runtime/src/manager.rs +++ b/src-tauri/yaak_plugin_runtime/src/manager.rs @@ -1,9 +1,9 @@ use crate::error::Result; use crate::events::{ - CallHttpRequestActionRequest, CallTemplateFunctionArgs, RenderPurpose, + PluginBootResponse, CallHttpRequestActionRequest, CallTemplateFunctionArgs, CallTemplateFunctionRequest, CallTemplateFunctionResponse, FilterRequest, FilterResponse, GetHttpRequestActionsRequest, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, - ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, + ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose, }; use std::collections::HashMap; @@ -64,6 +64,10 @@ impl PluginManager { .await } + pub async fn get_plugin_info(&self, dir: &str) -> Option { + self.server.plugin_by_dir(dir).await.ok()?.info().await + } + pub async fn get_http_request_actions(&self) -> Result> { let reply_events = self .server @@ -155,7 +159,9 @@ impl PluginManager { }); match result { - None => Err(PluginErr("No importers found for file contents".to_string())), + None => Err(PluginErr( + "No importers found for file contents".to_string(), + )), Some((resp, ref_id)) => { let plugin = self.server.plugin_by_ref_id(ref_id.as_str()).await?; let plugin_name = plugin.name().await; diff --git a/src-tauri/yaak_plugin_runtime/src/plugin.rs b/src-tauri/yaak_plugin_runtime/src/plugin.rs index 64d962a8..ead60212 100644 --- a/src-tauri/yaak_plugin_runtime/src/plugin.rs +++ b/src-tauri/yaak_plugin_runtime/src/plugin.rs @@ -15,21 +15,30 @@ use tokio::fs::read_dir; use tokio::net::TcpListener; use tonic::codegen::tokio_stream; use tonic::transport::Server; +use yaak_models::queries::list_plugins; pub fn init() -> TauriPlugin { Builder::new("yaak_plugin_runtime") - .setup(|app, _| { - let plugins_dir = app + .setup(|app_handle, _| { + let plugins_dir = app_handle .path() .resolve("plugins", BaseDirectory::Resource) .expect("failed to resolve plugin directory resource"); tauri::async_runtime::block_on(async move { - let plugin_dirs = read_plugins_dir(&plugins_dir) + let bundled_plugin_dirs = read_plugins_dir(&plugins_dir) .await .expect(format!("Failed to read plugins dir: {:?}", plugins_dir).as_str()); - let manager = PluginManager::new(&app, plugin_dirs).await; - app.manage(manager); + + let plugins = list_plugins(app_handle).await.unwrap_or_default(); + let installed_plugin_dirs = plugins + .iter() + .map(|p| p.directory.to_owned()) + .collect::>(); + + let plugin_dirs = [installed_plugin_dirs, bundled_plugin_dirs].concat(); + let manager = PluginManager::new(&app_handle, plugin_dirs).await; + app_handle.manage(manager); Ok(()) }) }) diff --git a/src-tauri/yaak_plugin_runtime/src/server.rs b/src-tauri/yaak_plugin_runtime/src/server.rs index 58e89d84..bdd0c4db 100644 --- a/src-tauri/yaak_plugin_runtime/src/server.rs +++ b/src-tauri/yaak_plugin_runtime/src/server.rs @@ -1,4 +1,3 @@ -use log::info; use rand::distributions::{Alphanumeric, DistString}; use std::collections::HashMap; use std::pin::Pin; @@ -11,7 +10,7 @@ use tonic::{Request, Response, Status, Streaming}; use crate::error::Error::PluginNotFoundErr; use crate::error::Result; -use crate::events::{BootRequest, BootResponse, InternalEvent, InternalEventPayload}; +use crate::events::{PluginBootRequest, PluginBootResponse, InternalEvent, InternalEventPayload}; use crate::server::plugin_runtime::plugin_runtime_server::PluginRuntime; use plugin_runtime::EventStreamEvent; use yaak_models::queries::generate_id; @@ -28,7 +27,7 @@ pub struct PluginHandle { dir: String, to_plugin_tx: Arc>>>, ref_id: String, - boot_resp: Arc>>, + boot_resp: Arc>>, } impl PluginHandle { @@ -38,6 +37,11 @@ impl PluginHandle { Some(r) => r.name.to_owned(), } } + + pub async fn info(&self) -> Option { + let resp = &*self.boot_resp.lock().await; + resp.clone() + } pub fn build_event_to_send( &self, @@ -53,11 +57,11 @@ impl PluginHandle { } pub async fn send(&self, event: &InternalEvent) -> Result<()> { - info!( - "Sending event to plugin {} {:?}", - event.id, - self.name().await - ); + // info!( + // "Sending event to plugin {} {:?}", + // event.id, + // self.name().await + // ); self.to_plugin_tx .lock() .await @@ -68,7 +72,7 @@ impl PluginHandle { Ok(()) } - pub async fn boot(&self, resp: &BootResponse) { + pub async fn boot(&self, resp: &PluginBootResponse) { let mut boot_resp = self.boot_resp.lock().await; *boot_resp = Some(resp.clone()); } @@ -120,7 +124,7 @@ impl PluginRuntimeGrpcServer { }; } - pub async fn boot_plugin(&self, id: &str, resp: &BootResponse) { + pub async fn boot_plugin(&self, id: &str, resp: &PluginBootResponse) { match self.plugin_ref_to_plugin.lock().await.get(id) { None => { println!("Tried booting non-existing plugin {}", id); @@ -151,61 +155,27 @@ impl PluginRuntimeGrpcServer { plugin_handle } - // pub async fn callback( - // &self, - // source_event: InternalEvent, - // payload: InternalEventPayload, - // ) -> Result { - // let reply_id = match source_event.clone().reply_id { - // None => { - // let msg = format!("Source event missing reply Id {:?}", source_event.clone()); - // return Err(MissingCallbackIdErr(msg)); - // } - // Some(id) => id, - // }; - // - // let callbacks = self.callbacks.lock().await; - // let plugin_name = match callbacks.get(reply_id.as_str()) { - // None => { - // let msg = format!("Callback not found {:?}", source_event); - // return Err(MissingCallbackErr(msg)); - // } - // Some(n) => n, - // }; - // - // let plugins = self.plugins.lock().await; - // let plugin = match plugins.get(plugin_name) { - // None => { - // let msg = format!( - // "Plugin not found {plugin_name}. Choices were {:?}", - // plugins.keys() - // ); - // return Err(UnknownPluginErr(msg)); - // } - // Some(n) => n, - // }; - // - // plugin.send(&payload, Some(reply_id)).await - // } - pub async fn plugin_by_ref_id(&self, ref_id: &str) -> Result { let plugins = self.plugin_ref_to_plugin.lock().await; - if plugins.is_empty() { - return Err(PluginNotFoundErr(ref_id.into())); - } - match plugins.get(ref_id) { None => Err(PluginNotFoundErr(ref_id.into())), Some(p) => Ok(p.to_owned()), } } + + pub async fn plugin_by_dir(&self, dir: &str) -> Result { + let plugins = self.plugin_ref_to_plugin.lock().await; + for p in plugins.values() { + if p.dir == dir { + return Ok(p.to_owned()); + } + } + + Err(PluginNotFoundErr(dir.into())) + } pub async fn plugin_by_name(&self, plugin_name: &str) -> Result { let plugins = self.plugin_ref_to_plugin.lock().await; - if plugins.is_empty() { - return Err(PluginNotFoundErr(plugin_name.into())); - } - for p in plugins.values() { if p.name().await == plugin_name { return Ok(p.to_owned()); @@ -341,7 +311,7 @@ impl PluginRuntimeGrpcServer { plugin_ids.push(plugin.clone().ref_id); let event = plugin.build_event_to_send( - &InternalEventPayload::BootRequest(BootRequest { + &InternalEventPayload::BootRequest(PluginBootRequest { dir: dir.to_string(), }), None, diff --git a/src-web/components/GlobalHooks.tsx b/src-web/components/GlobalHooks.tsx index 50bca895..292f6e03 100644 --- a/src-web/components/GlobalHooks.tsx +++ b/src-web/components/GlobalHooks.tsx @@ -18,6 +18,7 @@ import { httpResponsesQueryKey } from '../hooks/useHttpResponses'; import { keyValueQueryKey } from '../hooks/useKeyValue'; import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { useNotificationToast } from '../hooks/useNotificationToast'; +import { pluginsAtom } from '../hooks/usePlugins'; import { useRecentCookieJars } from '../hooks/useRecentCookieJars'; import { useRecentEnvironments } from '../hooks/useRecentEnvironments'; import { useRecentRequests } from '../hooks/useRecentRequests'; @@ -67,6 +68,7 @@ export function GlobalHooks() { const setSettings = useSetAtom(settingsAtom); const setWorkspaces = useSetAtom(workspacesAtom); + const setPlugins = useSetAtom(pluginsAtom); const setHttpRequests = useSetAtom(httpRequestsAtom); const setGrpcRequests = useSetAtom(grpcRequestsAtom); const setEnvironments = useSetAtom(environmentsAtom); @@ -100,6 +102,8 @@ export function GlobalHooks() { if (model.model === 'workspace') { setWorkspaces(updateModelList(model, pushToFront)); + } else if (model.model === 'plugin') { + setPlugins(updateModelList(model, pushToFront)); } else if (model.model === 'http_request') { setHttpRequests(updateModelList(model, pushToFront)); } else if (model.model === 'grpc_request') { @@ -129,6 +133,8 @@ export function GlobalHooks() { if (model.model === 'workspace') { setWorkspaces(removeById(model)); + } else if (model.model === 'plugin') { + setPlugins(removeById(model)); } else if (model.model === 'http_request') { setHttpRequests(removeById(model)); } else if (model.model === 'http_response') { diff --git a/src-web/components/SelectFile.tsx b/src-web/components/SelectFile.tsx index afa2d392..43132e4b 100644 --- a/src-web/components/SelectFile.tsx +++ b/src-web/components/SelectFile.tsx @@ -9,20 +9,31 @@ import { HStack } from './core/Stacks'; type Props = ButtonProps & { onChange: (value: { filePath: string | null; contentType: string | null }) => void; filePath: string | null; + directory?: boolean; inline?: boolean; + noun?: string; }; // Special character to insert ltr text in rtl element const rtlEscapeChar = <>‎; -export function SelectFile({ onChange, filePath, inline, className }: Props) { +export function SelectFile({ + onChange, + filePath, + inline, + className, + directory, + noun, + size = 'sm', + ...props +}: Props) { const handleClick = async () => { const filePath = await open({ title: 'Select File', multiple: false, + directory, }); if (filePath == null) return; - const contentType = filePath ? mime.getType(filePath) : null; onChange({ filePath, contentType }); }; @@ -31,31 +42,41 @@ export function SelectFile({ onChange, filePath, inline, className }: Props) { onChange({ filePath: null, contentType: null }); }; + const itemLabel = noun ?? (directory ? 'Folder' : 'File'); + const selectOrChange = (filePath ? 'Change ' : 'Select ') + itemLabel; + return ( - + {!inline && ( <> {filePath && ( )} -
+
{rtlEscapeChar} - {filePath ?? 'No file selected'} + {filePath ?? `No ${itemLabel.toLowerCase()} selected`}
)} diff --git a/src-web/components/Settings/Settings.tsx b/src-web/components/Settings/Settings.tsx index ad8dce04..68b45dc6 100644 --- a/src-web/components/Settings/Settings.tsx +++ b/src-web/components/Settings/Settings.tsx @@ -10,13 +10,15 @@ import { HeaderSize } from '../HeaderSize'; import { WindowControls } from '../WindowControls'; import { SettingsAppearance } from './SettingsAppearance'; import { SettingsGeneral } from './SettingsGeneral'; +import { SettingsPlugins } from './SettingsPlugins'; enum Tab { General = 'general', Appearance = 'appearance', + Plugins = 'plugins', } -const tabs = [Tab.General, Tab.Appearance]; +const tabs = [Tab.General, Tab.Appearance, Tab.Plugins]; export const Settings = () => { const osInfo = useOsInfo(); @@ -58,6 +60,9 @@ export const Settings = () => { + + +
); diff --git a/src-web/components/Settings/SettingsPlugins.tsx b/src-web/components/Settings/SettingsPlugins.tsx new file mode 100644 index 00000000..dc16a7b7 --- /dev/null +++ b/src-web/components/Settings/SettingsPlugins.tsx @@ -0,0 +1,95 @@ +import type { Plugin } from '@yaakapp/api'; +import React from 'react'; +import { useCreatePlugin } from '../../hooks/useCreatePlugin'; +import { usePluginInfo } from '../../hooks/usePluginInfo'; +import { usePlugins, useRefreshPlugins } from '../../hooks/usePlugins'; +import { Button } from '../core/Button'; +import { Checkbox } from '../core/Checkbox'; +import { IconButton } from '../core/IconButton'; +import { InlineCode } from '../core/InlineCode'; +import { HStack } from '../core/Stacks'; +import { SelectFile } from '../SelectFile'; + +export function SettingsPlugins() { + const [directory, setDirectory] = React.useState(null); + const plugins = usePlugins(); + const createPlugin = useCreatePlugin(); + const refreshPlugins = useRefreshPlugins(); + return ( +
+ + + + + + + + + + + {plugins.map((p) => ( + + ))} + +
PluginVersion
+
{ + e.preventDefault(); + if (directory == null) return; + createPlugin.mutate(directory); + setDirectory(null); + }} + > +
+ setDirectory(filePath)} + filePath={directory} + /> + + {directory && ( + + )} + refreshPlugins.mutate()} + /> + +
+
+
+ ); +} + +function PluginInfo({ plugin }: { plugin: Plugin }) { + const pluginInfo = usePluginInfo(plugin.id); + if (pluginInfo.data == null) return null; + return ( + + + null} /> + + + {pluginInfo.data?.name} + + + {pluginInfo.data?.version} + + + + + + ); +} diff --git a/src-web/components/core/Button.tsx b/src-web/components/core/Button.tsx index b784e468..dda1c24f 100644 --- a/src-web/components/core/Button.tsx +++ b/src-web/components/core/Button.tsx @@ -73,7 +73,7 @@ export const Button = forwardRef(function Button size === 'md' && 'h-md px-3 rounded-md', size === 'sm' && 'h-sm px-2.5 rounded-md', size === 'xs' && 'h-xs px-2 text-sm rounded-md', - size === '2xs' && 'h-5 px-1 text-xs rounded', + size === '2xs' && 'h-2xs px-1 text-xs rounded', // Solids variant === 'solid' && 'border-transparent', diff --git a/src-web/components/core/IconButton.tsx b/src-web/components/core/IconButton.tsx index 6731439b..9c81c525 100644 --- a/src-web/components/core/IconButton.tsx +++ b/src-web/components/core/IconButton.tsx @@ -58,9 +58,9 @@ export const IconButton = forwardRef(function IconButt '!px-0', color === 'custom' && 'text-text-subtle', color === 'default' && 'text-text-subtle', - size === 'md' && 'w-9', - size === 'sm' && 'w-8', - size === 'xs' && 'w-6', + size === 'md' && 'w-md', + size === 'sm' && 'w-sm', + size === 'xs' && 'w-xs', size === '2xs' && 'w-5', )} {...props} diff --git a/src-web/components/core/InlineCode.tsx b/src-web/components/core/InlineCode.tsx index f96efea6..6eb62196 100644 --- a/src-web/components/core/InlineCode.tsx +++ b/src-web/components/core/InlineCode.tsx @@ -6,6 +6,7 @@ export function InlineCode({ className, ...props }: HTMLAttributes({ + mutationKey: ['create_plugin'], + mutationFn: async (directory: string) => { + await invokeCmd('cmd_create_plugin', { directory }); + }, + onSettled: () => trackEvent('plugin', 'create'), + }); +} diff --git a/src-web/hooks/usePluginInfo.ts b/src-web/hooks/usePluginInfo.ts new file mode 100644 index 00000000..081e592b --- /dev/null +++ b/src-web/hooks/usePluginInfo.ts @@ -0,0 +1,13 @@ +import { useQuery } from '@tanstack/react-query'; +import type { BootResponse } from '@yaakapp/api'; +import { invokeCmd } from '../lib/tauri'; + +export function usePluginInfo(id: string) { + return useQuery({ + queryKey: ['plugin_info', id], + queryFn: async () => { + const info = (await invokeCmd('cmd_plugin_info', { id })) as BootResponse; + return info; + }, + }); +} diff --git a/src-web/hooks/usePlugins.ts b/src-web/hooks/usePlugins.ts new file mode 100644 index 00000000..a8196e22 --- /dev/null +++ b/src-web/hooks/usePlugins.ts @@ -0,0 +1,23 @@ +import { useMutation } from '@tanstack/react-query'; +import type { Plugin } from '@yaakapp/api'; +import { atom, useAtomValue, useSetAtom } from 'jotai'; +import { minPromiseMillis } from '../lib/minPromiseMillis'; +import { listPlugins } from '../lib/store'; + +const plugins = await listPlugins(); +export const pluginsAtom = atom(plugins); + +export function usePlugins() { + return useAtomValue(pluginsAtom); +} + +export function useRefreshPlugins() { + const setPlugins = useSetAtom(pluginsAtom); + return useMutation({ + mutationKey: ['refresh_plugins'], + mutationFn: async () => { + const plugins = await minPromiseMillis(listPlugins()); + setPlugins(plugins); + }, + }); +} diff --git a/src-web/lib/analytics.ts b/src-web/lib/analytics.ts index e8e831cd..f3c1b029 100644 --- a/src-web/lib/analytics.ts +++ b/src-web/lib/analytics.ts @@ -13,6 +13,7 @@ export type TrackResource = | 'http_request' | 'http_response' | 'key_value' + | 'plugin' | 'setting' | 'sidebar' | 'theme' diff --git a/src-web/lib/minPromiseMillis.ts b/src-web/lib/minPromiseMillis.ts index 08e4f06b..7728c5ee 100644 --- a/src-web/lib/minPromiseMillis.ts +++ b/src-web/lib/minPromiseMillis.ts @@ -1,7 +1,7 @@ import { sleep } from './sleep'; /** Ensures a promise takes at least a certain number of milliseconds to resolve */ -export async function minPromiseMillis(promise: Promise, millis: number) { +export async function minPromiseMillis(promise: Promise, millis = 300) { const start = Date.now(); let result; let err; diff --git a/src-web/lib/store.ts b/src-web/lib/store.ts index 45f86962..c11b0ca3 100644 --- a/src-web/lib/store.ts +++ b/src-web/lib/store.ts @@ -4,6 +4,7 @@ import type { Folder, GrpcRequest, HttpRequest, + Plugin, Settings, Workspace, } from '@yaakapp/api'; @@ -63,6 +64,11 @@ export async function listWorkspaces(): Promise { return workspaces; } +export async function listPlugins(): Promise { + const plugins: Plugin[] = (await invokeCmd('cmd_list_plugins')) ?? []; + return plugins; +} + export async function getCookieJar(id: string | null): Promise { if (id === null) return null; const cookieJar: CookieJar = (await invokeCmd('cmd_get_cookie_jar', { id })) ?? null; diff --git a/src-web/lib/tauri.ts b/src-web/lib/tauri.ts index 20731935..ca20cf77 100644 --- a/src-web/lib/tauri.ts +++ b/src-web/lib/tauri.ts @@ -6,6 +6,7 @@ type TauriCmd = | 'cmd_check_for_updates' | 'cmd_create_cookie_jar' | 'cmd_create_environment' + | 'cmd_create_plugin' | 'cmd_template_tokens_to_string' | 'cmd_create_folder' | 'cmd_create_grpc_request' @@ -47,11 +48,13 @@ type TauriCmd = | 'cmd_list_grpc_requests' | 'cmd_list_http_requests' | 'cmd_list_http_responses' + | 'cmd_list_plugins' | 'cmd_list_workspaces' | 'cmd_metadata' | 'cmd_new_nested_window' | 'cmd_new_window' | 'cmd_parse_template' + | 'cmd_plugin_info' | 'cmd_render_template' | 'cmd_save_response' | 'cmd_send_ephemeral_request' diff --git a/tailwind.config.cjs b/tailwind.config.cjs index c2911d6b..27dceee9 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -1,6 +1,7 @@ const plugin = require('tailwindcss/plugin'); -const height = { +const sizes = { + '2xs': '1.25rem', xs: '1.8rem', sm: '2.0rem', md: '2.5rem', @@ -18,8 +19,10 @@ module.exports = { fontSize: { xs: '0.8rem', }, - height, - minHeight: height, + height: sizes, + width: sizes, + minHeight: sizes, + minWidth: sizes, lineHeight: { // HACK: Minus 2 to account for borders inside inputs xs: 'calc(1.75rem - 2px)',