From da9defbaa7fd9d283d674ab7207772c3052fc687 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 31 Aug 2023 16:32:42 +0200
Subject: [PATCH 001/209] Add new build workflow
---
.github/workflows/build.yml | 122 ++++++++++++++++++++++--------------
package.json | 15 ++---
2 files changed, 81 insertions(+), 56 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d41b3270..d9197b07 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,59 +1,89 @@
-name: Build Codebase
+name: Build/release
-on:
- push:
- branches: [ master, dev ]
- pull_request:
- branches: [ master, dev ]
- types: [opened, synchronize, reopened, ready_for_review]
-permissions:
- contents: read
+on: push
jobs:
- start:
- name: Start State 🚀🚀🚀
- runs-on: ubuntu-latest
- steps:
- - name: Starting
- id: init
- run: |
- echo "Starting building of ${{ github.repository }}"
-
- build_project:
- name: Build on all platforms
+ release:
runs-on: ${{ matrix.os }}
+
strategy:
matrix:
- os: [ubuntu-latest, windows-latest, macOS-latest]
- needs: start
+ os: [macos-latest, ubuntu-latest, windows-latest]
+
steps:
- - name: Checkout for ${{ runner.os }}
+ - name: Check out Git repository
uses: actions/checkout@v3
- - name: Set up Node 18
+
+ - name: Install Node.js and NPM
uses: actions/setup-node@v3
with:
node-version: 18
- - name: Cache dependencies
- uses: actions/cache@v2
+ - name: Build/release Electron app
+ uses: samuelmeuli/action-electron-builder@v1
with:
- path: ~/.npm
- key: npm-${{ hashFiles('package-lock.json') }}
- restore-keys: npm-
-
- - name: Install dependencies
- run: npm ci
- - name: Run tests and collect coverage
- run: npm run test
- - name: Build Project for ${{ runner.os }}
- run: npm run build
-
- end:
- name: End State ✅✅✅
- runs-on: ubuntu-latest
- needs: build_project
- steps:
- - name: Ending
- id: init
- run: |
- echo "Ending building of ${{ github.repository }}"
\ No newline at end of file
+ github_token: ${{ secrets.github_token }}
+
+ # If the commit is tagged with a version (e.g. "v1.0.0"),
+ # release the app after building
+ release: ${{ startsWith(github.ref, 'refs/tags/v') }}
+
+# name: Build Codebase
+
+# on:
+# push:
+# branches: [ master, dev ]
+# pull_request:
+# branches: [ master, dev ]
+# types: [opened, synchronize, reopened, ready_for_review]
+# permissions:
+# contents: read
+
+# jobs:
+# start:
+# name: Start State 🚀🚀🚀
+# runs-on: ubuntu-latest
+# steps:
+# - name: Starting
+# id: init
+# run: |
+# echo "Starting building of ${{ github.repository }}"
+
+# build_project:
+# name: Build on all platforms
+# runs-on: ${{ matrix.os }}
+# strategy:
+# matrix:
+# os: [ubuntu-latest, windows-latest, macOS-latest]
+# needs: start
+# steps:
+# - name: Checkout for ${{ runner.os }}
+# uses: actions/checkout@v3
+# - name: Set up Node 18
+# uses: actions/setup-node@v3
+# with:
+# node-version: 18
+
+# - name: Cache dependencies
+# uses: actions/cache@v2
+# with:
+# path: ~/.npm
+# key: npm-${{ hashFiles('package-lock.json') }}
+# restore-keys: npm-
+
+# - name: Install dependencies
+# run: npm ci
+# - name: Run tests and collect coverage
+# run: npm run test
+# - name: Build Project for ${{ runner.os }}
+# run: npm run build
+
+# end:
+# name: End State ✅✅✅
+# runs-on: ubuntu-latest
+# needs: build_project
+# steps:
+# - name: Ending
+# id: init
+# run: |
+# echo "Ending building of ${{ github.repository }}"
\ No newline at end of file
diff --git a/package.json b/package.json
index 11ed5a03..e840eac2 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.0",
+ "version": "1.1.0",
"repository": {
"type": "git",
"url": "https://github.com/COS301-SE-2023/AI-Photo-Editor"
@@ -162,18 +162,13 @@
]
},
"mac": {
- "target": [
- "dmg"
- ],
+ "target": ["dmg"],
"category": "productivity",
"type": "distribution",
"hardenedRuntime": "true"
},
"linux": {
- "target": [
- "AppImage",
- "snap"
- ],
+ "target": ["AppImage"],
"category": "productivity"
},
"files": [
@@ -195,8 +190,8 @@
"publish": [
{
"provider": "github",
- "owner": "COS301-SE-2023",
- "repo": "AI-Photo-Editor"
+ "owner": "ArmandKrynauw",
+ "repo": "Blix"
}
]
}
From 4df428a2ca9de6f904616be5a0579d9e1d7775bc Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 31 Aug 2023 16:37:26 +0200
Subject: [PATCH 002/209] Add test to package.json
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index e840eac2..fe7fda7d 100644
--- a/package.json
+++ b/package.json
@@ -155,7 +155,7 @@
"build": {
"productName": "Blix",
"appId": "com.the-spanish-inquisition.blix",
- "copyright": "Copyright © 2023 The Spanish Inquisition",
+ "copyright": "Copyright © 2023 Blix",
"win": {
"target": [
"nsis"
From 3b9bdffb4014aec9707fd84d0c15e45ca1bad3e1 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 31 Aug 2023 16:45:33 +0200
Subject: [PATCH 003/209] v1.1.0
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index fe7fda7d..d47483e3 100644
--- a/package.json
+++ b/package.json
@@ -155,7 +155,7 @@
"build": {
"productName": "Blix",
"appId": "com.the-spanish-inquisition.blix",
- "copyright": "Copyright © 2023 Blix",
+ "copyright": "Copyright© 2023 Blix",
"win": {
"target": [
"nsis"
From 4a8b71d0f33dc5f90d317da3d8d619342623b55e Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 31 Aug 2023 18:40:41 +0200
Subject: [PATCH 004/209] Change config
---
.github/workflows/build.yml | 3 ++-
package.json | 13 +++++--------
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d9197b07..2e8d2910 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -26,7 +26,8 @@ jobs:
# If the commit is tagged with a version (e.g. "v1.0.0"),
# release the app after building
- release: ${{ startsWith(github.ref, 'refs/tags/v') }}
+ # release: ${{ startsWith(github.ref, 'refs/tags/v') }}
+ release: true
# name: Build Codebase
diff --git a/package.json b/package.json
index d47483e3..7885854a 100644
--- a/package.json
+++ b/package.json
@@ -157,18 +157,17 @@
"appId": "com.the-spanish-inquisition.blix",
"copyright": "Copyright© 2023 Blix",
"win": {
- "target": [
- "nsis"
- ]
+ "target": "nsis"
+
},
"mac": {
- "target": ["dmg"],
+ "target": "dmg",
"category": "productivity",
"type": "distribution",
"hardenedRuntime": "true"
},
"linux": {
- "target": ["AppImage"],
+ "target": "AppImage",
"category": "productivity"
},
"files": [
@@ -187,12 +186,10 @@
]
}
],
- "publish": [
- {
+ "publish": {
"provider": "github",
"owner": "ArmandKrynauw",
"repo": "Blix"
}
- ]
}
}
From f46ec3a09acc47ab4992ded7f664a0b92907e2d4 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Wed, 6 Sep 2023 14:13:34 +0200
Subject: [PATCH 005/209] Fix BlypescriptInterpreter breaking startup on
invalid UI input definition
---
blix-plugins/base-plugin/package.json | 2 +-
blix-plugins/hello-plugin/package.json | 2 +-
blix-plugins/input-plugin/package.json | 19 +++----------------
blix-plugins/input-plugin/src/main.js | 9 ++++-----
blix-plugins/logic-plugin/package.json | 4 ++--
blix-plugins/math-plugin/package.json | 2 +-
blix-plugins/sharp-plugin/package.json | 18 ++++++++++++++++++
blix-plugins/sharp-plugin/src/main.js | 7 ++++---
package.json | 2 +-
src/electron/lib/ai/AiLang.ts | 15 +++++++++------
src/electron/lib/plugins/Plugin.ts | 12 ++++++------
.../lib/registries/TypeclassRegistry.ts | 4 ++--
12 files changed, 52 insertions(+), 44 deletions(-)
create mode 100644 blix-plugins/sharp-plugin/package.json
diff --git a/blix-plugins/base-plugin/package.json b/blix-plugins/base-plugin/package.json
index 617faa23..76f03f92 100644
--- a/blix-plugins/base-plugin/package.json
+++ b/blix-plugins/base-plugin/package.json
@@ -18,7 +18,7 @@
},
"main": "src/main.js",
- "renderer": "src/renderer.js",
+ "renderers": {},
"devDependencies": {
"@types/node": "^12.0.0",
diff --git a/blix-plugins/hello-plugin/package.json b/blix-plugins/hello-plugin/package.json
index 997ce571..7f12325a 100644
--- a/blix-plugins/hello-plugin/package.json
+++ b/blix-plugins/hello-plugin/package.json
@@ -18,7 +18,7 @@
},
"main": "dist/main.js",
- "renderer": "src/renderer.js",
+ "renderers": {},
"devDependencies": {
"@types/node": "^12.0.0",
diff --git a/blix-plugins/input-plugin/package.json b/blix-plugins/input-plugin/package.json
index 6a1ab3cc..19e8b45f 100644
--- a/blix-plugins/input-plugin/package.json
+++ b/blix-plugins/input-plugin/package.json
@@ -1,5 +1,5 @@
{
- "name": "Input-plugin",
+ "name": "input-plugin",
"displayName": "Input Plugin",
"description": "Provides basic nodes for providing input to a graph.",
"version": "0.0.1",
@@ -9,24 +9,11 @@
"contributes": {
"commands": [],
- "nodes": [
- {
- "id": "input",
- "name": "Input",
- "interface" : {
- "inputs": {
- "numA": "int"
- },
- "outputs": {
- "numOut": "int"
- }
- }
- }
- ]
+ "nodes": []
},
"main": "src/main.js",
- "renderer": "src/renderer.js",
+ "renderers": {},
"devDependencies": {
"@types/node": "^12.0.0",
diff --git a/blix-plugins/input-plugin/src/main.js b/blix-plugins/input-plugin/src/main.js
index 465bbd90..9f10a0f7 100644
--- a/blix-plugins/input-plugin/src/main.js
+++ b/blix-plugins/input-plugin/src/main.js
@@ -72,19 +72,19 @@ const nodes = {
nodeBuilder.setDescription("Provides a radio box to select a single true/false value");
nodeBuilder.define((input, uiInput, from) => {
- return { "val": uiInput["radio"]};
+ return { "val": uiInput["radio"] === "true" };
});
const ui = nodeBuilder.createUIBuilder();
ui.addRadio({
componentId: "radio",
label: "Boolean value",
- defaultValue: false,
+ defaultValue: "false",
triggerUpdate: true,
}, {
options: {
- "False": false,
- "True": true,
+ "False": "false",
+ "True": "true",
}
});
nodeBuilder.setUI(ui);
@@ -96,7 +96,6 @@ const nodes = {
const commands = {}
-
const tiles = {}
module.exports = {
diff --git a/blix-plugins/logic-plugin/package.json b/blix-plugins/logic-plugin/package.json
index faf63f13..3805ef1f 100644
--- a/blix-plugins/logic-plugin/package.json
+++ b/blix-plugins/logic-plugin/package.json
@@ -1,5 +1,5 @@
{
- "name": "Logic-plugin",
+ "name": "logic-plugin",
"displayName": "Logic Plugin",
"description": "Performs basic logical operations through n-ary nodes.",
"version": "0.0.1",
@@ -13,7 +13,7 @@
},
"main": "src/main.js",
- "renderer": "src/renderer.js",
+ "renderers": {},
"devDependencies": {
"@types/node": "^12.0.0",
diff --git a/blix-plugins/math-plugin/package.json b/blix-plugins/math-plugin/package.json
index 856eda9c..ba99b7e0 100644
--- a/blix-plugins/math-plugin/package.json
+++ b/blix-plugins/math-plugin/package.json
@@ -26,7 +26,7 @@
},
"main": "src/main.js",
- "renderer": "src/renderer.js",
+ "renderers": {},
"devDependencies": {
"@types/node": "^12.0.0",
diff --git a/blix-plugins/sharp-plugin/package.json b/blix-plugins/sharp-plugin/package.json
new file mode 100644
index 00000000..2268db1b
--- /dev/null
+++ b/blix-plugins/sharp-plugin/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "sharp-plugin",
+ "displayName": "Sharp Plugin",
+ "description": "Performs basic image operations using the Sharp library for Node.js",
+ "version": "0.0.1",
+ "author": "Rec1dite",
+ "repository": "",
+ "type": "commonjs",
+ "scripts": {},
+ "contributes": {
+ "commands": [],
+ "nodes": []
+ },
+ "main": "src/main.js",
+ "renderers": {},
+ "devDependencies": { "@types/node": "^12.0.0" },
+ "comments": []
+ }
diff --git a/blix-plugins/sharp-plugin/src/main.js b/blix-plugins/sharp-plugin/src/main.js
index 4cce8718..0dedddff 100644
--- a/blix-plugins/sharp-plugin/src/main.js
+++ b/blix-plugins/sharp-plugin/src/main.js
@@ -31,7 +31,7 @@ const nodes = {
nodeBuilder.setUI(ui);
nodeBuilder.addInput("Sharp", "img", "Img");
- nodeBuilder.addInput("Number", "value", "Value");
+ nodeBuilder.addInput("number", "value", "Value");
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"saturation": (context) => {
@@ -125,6 +125,7 @@ const nodes = {
const nodeBuilder = context.instantiate("sharp-plugin", "sharpen");
nodeBuilder.setTitle("Sharpen");
nodeBuilder.setDescription("Sharpens an image taking one image as input and returning one image as output");
+ const ui = nodeBuilder.createUIBuilder();
ui
.addSlider(
{
@@ -185,7 +186,7 @@ const nodes = {
});
nodeBuilder.addInput("Sharp", "img", "Img");
- nodeBuilder.addOutput("Image", "res", "Result");
+ nodeBuilder.addOutput("image", "res", "Result");
},
"toSharp": (context) => {
const nodeBuilder = context.instantiate("sharp-plugin", "toSharp");
@@ -197,7 +198,7 @@ const nodes = {
return { "res": await sharp(input["img"]) };
});
- nodeBuilder.addInput("Image", "img", "Img");
+ nodeBuilder.addInput("image", "img", "Img");
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"inputSharpImage": (context) => {
diff --git a/package.json b/package.json
index 31eb91d7..e9c55434 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"start:electron:run": "electron .",
"start:electron:nodemon": "nodemon --verbose",
"start:electron:dev": "npm-run-all -s build:electron:dev start:electron:nodemon",
- "start:electron": "sleep 10 && npm-run-all -p build:electron:dev:watch start:electron:dev",
+ "start:electron": "sleep 2 && npm-run-all -p build:electron:dev:watch start:electron:dev",
"test": "jest --config jest.config.json",
"preplaywrite": "npm run build",
"playwright": "playwright test",
diff --git a/src/electron/lib/ai/AiLang.ts b/src/electron/lib/ai/AiLang.ts
index fd8e9256..0837137c 100644
--- a/src/electron/lib/ai/AiLang.ts
+++ b/src/electron/lib/ai/AiLang.ts
@@ -187,11 +187,14 @@ export class BlypescriptInterpreter {
private readonly graphManager: CoreGraphManager
) {
const result = BlypescriptToolbox.fromToolbox(this.toolbox);
- if (!result.success) {
- throw result.error;
- }
- this.blypescriptToolbox = result.data;
+ if (result.success) {
+ this.blypescriptToolbox = result.data;
+ } else {
+ // throw result.error;
+ this.blypescriptToolbox = new BlypescriptToolbox([]);
+ logger.error("Failed to initialize BlypescriptInterpreter with error:", result.error);
+ }
}
public run(
@@ -632,11 +635,11 @@ export class BlypescriptPlugin {
if (componentType === "Button") {
nodeParam.aiCanUse = false;
nodeParam.types.push("string");
- } else if (componentType === "Slider") {
+ } else if (componentType === "Slider" || componentType === "NumberInput") {
nodeParam.types.push("number");
} else if (componentType === "Knob") {
nodeParam.types.push("number");
- } else if (componentType === "Dropdown") {
+ } else if (componentType === "Dropdown" || componentType === "Radio") {
const objectSchema = z.record(z.string(), z.string());
const options = objectSchema.safeParse(props.options);
diff --git a/src/electron/lib/plugins/Plugin.ts b/src/electron/lib/plugins/Plugin.ts
index c745d085..ac240434 100644
--- a/src/electron/lib/plugins/Plugin.ts
+++ b/src/electron/lib/plugins/Plugin.ts
@@ -103,12 +103,12 @@ export class Plugin {
const ctx = new TilePluginContext(this.name);
- try {
- pluginModule.tiles[tile](ctx); // Execute tile builder
- blix.tileRegistry.addInstance(ctx.tileBuilder.build); // Add to registry
- } catch (err) {
- logger.warn(err);
- }
+ // try {
+ // pluginModule.tiles[tile](ctx); // Execute tile builder
+ // blix.tileRegistry.addInstance(ctx.tileBuilder.build); // Add to registry
+ // } catch (err) {
+ // logger.warn(err);
+ // }
}
}
diff --git a/src/electron/lib/registries/TypeclassRegistry.ts b/src/electron/lib/registries/TypeclassRegistry.ts
index d54569c1..3935a10e 100644
--- a/src/electron/lib/registries/TypeclassRegistry.ts
+++ b/src/electron/lib/registries/TypeclassRegistry.ts
@@ -191,7 +191,7 @@ const baseTypes: Typeclass[] = [
}),
},
{
- id: "bool",
+ id: "boolean",
description: "A true/false value",
subtypes: [],
mediaDisplayConfig: (data: number) => ({
@@ -249,7 +249,7 @@ export type ConverterTriple = [TypeclassId, TypeclassId, TypeConverter];
const baseConverters: ConverterTriple[] = [
["number", "string", (value: number) => value.toString()],
["string", "number", (value: string) => parseFloat(value)],
- ["bool", "string", (value: boolean) => (value ? "true" : "false")],
+ ["boolean", "string", (value: boolean) => (value ? "true" : "false")],
["string", "boolean", (value: string) => value.toLowerCase() === "true"],
["number", "boolean", (value: number) => value !== 0],
];
From 9c4c0a4d14badd12b66e7da52ebc7ed9ad9296d4 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 6 Sep 2023 14:34:29 +0200
Subject: [PATCH 006/209] Add smarter Blypescript parsing logic
Parser will attempt to fix nested functions calls or primitives as node inputs
---
src/electron/lib/ai/AiLang.ts | 351 ++++++++++++++++++--
src/electron/lib/ai/AiManagerv2.ts | 7 +-
src/frontend/ui/base/App.svelte | 8 +-
src/frontend/ui/base/palette/Palette.svelte | 21 +-
4 files changed, 350 insertions(+), 37 deletions(-)
diff --git a/src/electron/lib/ai/AiLang.ts b/src/electron/lib/ai/AiLang.ts
index fd8e9256..c846f5a3 100644
--- a/src/electron/lib/ai/AiLang.ts
+++ b/src/electron/lib/ai/AiLang.ts
@@ -44,22 +44,32 @@ export class BlypescriptProgram implements AiLangProgram {
constructor(
public readonly statements: BlypescriptStatement[],
public nodeNameIdMap: Map
- ) {}
+ ) {
+ this.repairLineNumbers();
+ }
- public static fromString(program: string): Result {
+ /**
+ * Parses a Blypescript program string. If a BlypescriptToolbox is provided
+ * then additional syntax and semantic analysis will be done in order to try
+ * to validate and fix parts of the program.
+ *
+ * @param program Blypescript program to parse
+ * @param toolbox Blypescript toolbox
+ * @returns Blypescript program
+ */
+ public static fromString(
+ program: string,
+ toolbox?: BlypescriptToolbox
+ ): Result {
const nodeNameIdMap = new Map();
- // const match = program.match(/^.*(?:function)?\s*graph\(\)\s*{([\s\S]*)}.*$/s);
const match = program.match(/^.*\s*graph\(\)\s*{([\s\S]*)}.*$/s);
- // Does not conform to syntax
- if (!match)
- return {
- success: false,
- error: "Program does not conform to syntax",
- message: "Incorrect syntax provided for Blypescript program",
- };
+ if (!match) {
+ return error("invalid_syntax", "Function should be in the form `graph() { // statements }`");
+ }
const extractedProgram = match[1];
+
const statements = extractedProgram
.split("\n")
.map((s) => s.trim())
@@ -93,6 +103,11 @@ export class BlypescriptProgram implements AiLangProgram {
const prog = new BlypescriptProgram(resStatements, nodeNameIdMap);
+ if (toolbox) {
+ const result = prog.repair(toolbox);
+ if (!result.success) return result;
+ }
+
return { success: true, data: prog };
}
@@ -132,19 +147,275 @@ export class BlypescriptProgram implements AiLangProgram {
}
});
}
+
+ /**
+ * Makes a second pass over the program to check the semantics of the
+ * statements to 1. Attempt to fix nested function calls or primitive types
+ * substituted for edge types 2. Raise an error if a syntax/semantic error
+ * can't be fixed.
+ *
+ * This function does NOT do semantic analysis on all parameters. If there are
+ * errors then it needs to be detected by the interpreter.
+ *
+ * @param toolbox
+ */
+ public repair(toolbox: BlypescriptToolbox) {
+ for (const statement of this.statements) {
+ const node = toolbox.getNode(statement.nodeSignature);
+
+ if (!node) {
+ return error(
+ "node_not_valid",
+ `Invalid function \`${statement.nodeSignature}\` used at line: ${statement.toString()}`
+ );
+ }
+
+ if (statement.nodeInputs.length > node.getNodeParameters().length) {
+ return error(
+ "too_many_parameters",
+ `Received ${statement.nodeInputs.length} parameters, but received ${
+ node.getNodeParameters().length
+ } at line: ${statement.toString()}`
+ );
+ }
+
+ let result = this.repairNestedFunctionCalls(statement, node);
+
+ if (!result.success) return result;
+
+ result = this.repairPrimitiveTypes(statement, node, toolbox);
+
+ if (!result.success) return result;
+ }
+
+ return {
+ success: true,
+ data: true as const,
+ } satisfies Result;
+ }
+
+ /**
+ * Adds a statement to the Blypescript program.
+ *
+ * @param statement Blypescript statement
+ * @param append Whether to append or prepend to program if no line number
+ * @param line Indexing starts at 1
+ * @returns Whether statement was added or not
+ */
+ public addStatement(statement: BlypescriptStatement, append = true, line?: number): boolean {
+ let flag = true;
+ line = line ? line - 1 : line;
+
+ if (!line) {
+ if (append) {
+ this.statements.push(statement);
+ } else {
+ this.statements.unshift(statement);
+ }
+ } else if (line >= 0 && line < this.statements.length) {
+ this.statements.splice(line, 0, statement);
+ } else {
+ flag = false;
+ }
+
+ this.repairLineNumbers();
+ return flag;
+ }
+
+ private repairLineNumbers() {
+ this.statements.forEach((s, index) => (s.lineNumber = index + 1));
+ }
+
+ /**
+ * Fixes nested function call in Blypescript statements.
+ *
+ * ```javascript
+ * const num1 = input-plugin.inputNumber(5);
+ * const binary1 = math-plugin. binary(num1['res'], input-plugin.inputNumber(8)['res'], 'add');
+ * ```
+ * is converted to
+ * ```javascript
+ * const num1 = input-plugin.inputNumber(5);
+ * const num2 = input-plugin.inputNumber(8);
+ * const binary1 = math-plugin. binary(num1['res'], num2['res'], 'add');
+ * ```
+ *
+ * @param statement
+ * @param node
+ */
+ private repairNestedFunctionCalls(statement: BlypescriptStatement, node: BlypescriptNode) {
+ const statementNodeInputs = statement.nodeInputs.slice(0, node.nodeInputs.length);
+
+ for (let i = 0; i < statementNodeInputs.length; i++) {
+ const statementNodeInput = statementNodeInputs[i];
+ // TODO: Use to check types perhaps
+ const nodeInput = node.nodeInputs[i];
+
+ if (statementNodeInput.match(BLYPESCRIPT_FUNCTION_CALL_REGEX)) {
+ const match = statementNodeInput.match(BLYPESCRIPT_NESTED_FUNCTION_CALL_REGEX);
+
+ if (!match) {
+ return error(
+ "invalid_nested_function_call",
+ `Nested function call \`${statementNodeInput}\` syntax is invalid on line: ${statement.toString()}`
+ );
+ }
+
+ const [_, nodeSignature, params, subscript] = match;
+
+ const newStatement = new BlypescriptStatement(
+ this.generateUniqueVarName(nodeSignature),
+ nodeSignature as `${string}.${string}`,
+ params.split(",").map((p) => p.trim())
+ );
+
+ this.addStatement(newStatement, false, statement.lineNumber);
+
+ statement.nodeInputs.splice(
+ statement.nodeInputs.indexOf(statementNodeInput),
+ 1,
+ `${newStatement.name}${subscript}`
+ );
+ }
+ }
+
+ return {
+ success: true,
+ data: true as const,
+ } satisfies Result;
+ }
+
+ /**
+ * Fixes primitives passed as edges types in Blypescript statements.
+ *
+ * ```javascript
+ * const num1 = input-plugin.inputNumber(5);
+ * const binary1 = math-plugin. binary(num1['res'], 8, 'add');
+ * ```
+ * is converted to
+ * ```javascript
+ * const num1 = input-plugin.inputNumber(5);
+ * const num2 = input-plugin.inputNumber(8);
+ * const binary1 = math-plugin. binary(num1['res'], num2['res'], 'add');
+ * ```
+ *
+ * @param statement
+ * @param node
+ */
+ private repairPrimitiveTypes(
+ statement: BlypescriptStatement,
+ node: BlypescriptNode,
+ toolbox: BlypescriptToolbox
+ ) {
+ const statementNodeInputs = statement.nodeInputs.slice(0, node.nodeInputs.length);
+
+ for (let i = 0; i < statementNodeInputs.length; i++) {
+ const statementNodeInput = statementNodeInputs[i];
+ // TODO: Use to check types perhaps
+ const nodeInput = node.nodeInputs[i];
+
+ // Assumes parameter valid when of form: nodeName['res']
+ // Does not do comprehensive typechecking here
+ if (statementNodeInput.match(/^\s*(.*)\s*(\['([\w.-]+)'\])\s*$/)) {
+ continue;
+ }
+
+ let newStatement: BlypescriptStatement | null = null;
+ let inputNode: BlypescriptNode | null = null;
+
+ if (!isNaN(Number(statementNodeInput))) {
+ inputNode = toolbox.getNode("input-plugin.inputNumber");
+
+ if (!inputNode) {
+ return error(
+ "node_not_found",
+ "The `input-plugin.inputNumber` plugin node can not be found"
+ );
+ }
+
+ newStatement = new BlypescriptStatement(
+ this.generateUniqueVarName(inputNode.signature),
+ inputNode.signature as `${string}.${string}`,
+ [statementNodeInput]
+ );
+ } else if (statementNodeInput === "true" || statementNodeInput === "false") {
+ // TODO: Add add a inputBoolean statement
+ } else {
+ // TODO: Add add a inputString statement
+ }
+
+ // TODO: Should be able to remove this if all cases of if statements are implemented
+ if (!newStatement || !inputNode) {
+ return error(
+ "invalid_blypescript_statement",
+ "Blypescript statement was not created, check if string or boolean input nodes are available"
+ );
+ }
+
+ if (inputNode.nodeOutputs.length !== 1) {
+ return error(
+ "invalid_blypescript_input_node",
+ `Blypescript statement was not created due to ambiguity. The ${inputNode.signature} node does not have exactly one output`
+ );
+ }
+
+ this.addStatement(newStatement, false, statement.lineNumber);
+
+ statement.nodeInputs.splice(
+ statement.nodeInputs.indexOf(statementNodeInput),
+ 1,
+ `${newStatement.name}['${inputNode.nodeOutputs[0].name}']`
+ );
+ }
+
+ return {
+ success: true,
+ data: true as const,
+ } satisfies Result;
+ }
+
+ private generateUniqueVarName(nodeSignature?: string) {
+ const varPrefix = nodeSignature ? nodeSignature.split(".").slice(1).join(".") : "var";
+ let count = 1;
+ let varName = `${varPrefix}${count}`;
+
+ while (this.statements.some((s) => s.name === varName)) {
+ varName = `${varPrefix}${++count}`;
+ }
+
+ return varName;
+ }
+}
+
+function error(error: string, message: string, data?: T) {
+ return {
+ success: false,
+ error,
+ message,
+ data,
+ } satisfies Result;
}
// const 139dfjslaf = hello-plugin.gloria(10, "The quick brown fox jumps over the lazy dog");
// const 12u394238x = hello-plugin.hello(139dfjslaf["output1"]);
// const afhuoewnc2 = math-plugin.add(139dfjslaf["output2"], 12u394238x["out"]);
-const BlypescriptStatementRegex = /\s*const \s*(\w+)\s*=\s*([\w-]+)\s*\.\s*([\w-]+)\s*\((.*)\)\s*;/;
-// A single line in a BlypescriptProgram
+/**
+ * const num1 = input-plugin.inputNumber(69);
+ * const binary1 = math-plugin.input(num1['res'], num1['res'], 'multiply');
+ */
+const BLYPESCRIPT_STATEMENT_TEMPLATE = "const {{name}} = {{signature}}.({{params}});";
+const BLYPESCRIPT_STATEMENT_REGEX =
+ /\s*const \s*(\w+)\s*=\s*([\w-]+)\s*\.\s*([\w-]+)\s*\((.*)\)\s*;/;
+const BLYPESCRIPT_FUNCTION_CALL_REGEX = /^([\w-]+)\s*\.\s*([\w-]+)\s*\((.*)\).*$/;
+const BLYPESCRIPT_NESTED_FUNCTION_CALL_REGEX = /^([\w\.-]+)\s*\((.*)\)\s*(\['([\w]+)'\]).*$/;
+
+/** Represents a single line in a Blypescript. */
export class BlypescriptStatement extends AiLangStatement {
public lineNumber?: number;
public static fromString(statement: string): Result {
- const match = statement.match(BlypescriptStatementRegex);
+ const match = statement.match(BLYPESCRIPT_STATEMENT_REGEX);
if (!match) {
return {
@@ -154,22 +425,12 @@ export class BlypescriptStatement extends AiLangStatement {
};
}
- const parameters = match[4].split(",").map((s) => s.trim());
-
- const regex = /^([\w-]+)\s*\.\s*([\w-]+)\s*\((.*)\).*$/;
- if (parameters.some((param) => param.match(regex))) {
- return {
- success: false,
- error: "syntax_error",
- message: `Invalid syntax for statement: ${statement}\nNested function calls not allowed!`,
- };
- }
+ const [_, name, pluginName, nodeName, params] = match;
const blypescriptStatement = new BlypescriptStatement(
- match[1],
- `${match[2]}.${match[3]}`,
- // TODO: Look into being a bit smarter about checking/storing input 'arguments'
- parameters
+ name,
+ `${pluginName}.${nodeName}`,
+ params.split(",").map((p) => p.trim())
);
return { success: true, data: blypescriptStatement };
@@ -177,6 +438,11 @@ export class BlypescriptStatement extends AiLangStatement {
public toString(): string {
return `const ${this.name} = ${this.nodeSignature}(${this.nodeInputs.join(", ")});`;
+ return fillTemplate(BLYPESCRIPT_STATEMENT_TEMPLATE, {
+ name: this.name,
+ signature: this.nodeSignature,
+ params: this.nodeInputs.join(", "),
+ });
}
}
export class BlypescriptInterpreter {
@@ -452,6 +718,7 @@ export class BlypescriptInterpreter {
export type BlypescriptNodeParam = {
name: string;
+ type: "input" | "output" | "ui";
aiCanUse: boolean;
types: string[];
};
@@ -504,6 +771,19 @@ export class BlypescriptNode {
return str;
}
+
+ /**
+ *
+ * Returns only the parameters that the AI can use if flag is true else
+ * returns all the parameters.
+ *
+ * @param aiCanUse
+ * @returns List of parameters
+ */
+ public getNodeParameters(aiCanUse = true) {
+ const params = [...this.nodeInputs, ...this.uiInputs];
+ return aiCanUse ? params.filter((param) => param.aiCanUse) : params;
+ }
}
export class BlypescriptPlugin {
@@ -579,6 +859,7 @@ export class BlypescriptPlugin {
return node.inputs.map((input) => {
return {
name: input.id,
+ type: "input",
aiCanUse: true,
types: [input.type ? input.type : "any"],
};
@@ -625,6 +906,7 @@ export class BlypescriptPlugin {
const nodeParam: BlypescriptNodeParam = {
name: ui.label,
+ type: "ui",
aiCanUse: true,
types: [],
};
@@ -678,6 +960,7 @@ export class BlypescriptPlugin {
return node.outputs.map((output) => {
return {
name: output.id,
+ type: "output",
aiCanUse: true,
types: [output.type ? output.type : "any"],
};
@@ -748,6 +1031,20 @@ export class BlypescriptToolbox {
// HELPERS
// ==================================================================
+/**
+ * Formats a template string by replacing placeholders with values.
+ *
+ * @param template - The string template to fill.
+ * @param replacements - The replacements to fill the template with.
+ * @returns The formatted template.
+ *
+ * @example
+ * fillTemplate("Life is a {{description}}", { description: "dream" })
+ */
+function fillTemplate(template: string, replacements: Record) {
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => replacements[key] || match);
+}
+
export function colorString(str: string, color: Colors) {
return `${COLORS[color]}${str}${COLORS.RESET}`;
}
diff --git a/src/electron/lib/ai/AiManagerv2.ts b/src/electron/lib/ai/AiManagerv2.ts
index 9e57b1df..147cbf6c 100644
--- a/src/electron/lib/ai/AiManagerv2.ts
+++ b/src/electron/lib/ai/AiManagerv2.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
import { type Message, Chat } from "./Chat";
-import { type ChatModel, Model } from "./Model";
+import { type ChatModel, Model, type ModelResponse } from "./Model";
import { NodeInstance, ToolboxRegistry } from "../registries/ToolboxRegistry";
import { CoreGraphManager } from "../core-graph/CoreGraphManager";
@@ -102,7 +102,10 @@ export class AiManager {
chat.addMessage({ role: "assistant", content: response.data.content });
- const result = BlypescriptProgram.fromString(response.data.content);
+ const result = BlypescriptProgram.fromString(
+ response.data.content,
+ blypescriptToolboxResult.data.toolbox
+ );
if (!result.success) {
console.log(result.message);
diff --git a/src/frontend/ui/base/App.svelte b/src/frontend/ui/base/App.svelte
index e43672de..d972f920 100644
--- a/src/frontend/ui/base/App.svelte
+++ b/src/frontend/ui/base/App.svelte
@@ -12,7 +12,7 @@
import Settings from "./Settings.svelte";
import Shortcuts from "../../ui/utils/Shortcuts.svelte";
import { settingsStore } from "../../lib/stores/SettingsStore";
- import { confetti } from '@neoconfetti/svelte';
+ import { confetti } from "@neoconfetti/svelte";
const testing = false;
let showSettings = false;
@@ -36,7 +36,6 @@
});
-
{#if $blixStore.blixReady && testing}
@@ -79,9 +78,6 @@
-
-
-
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index fd30c69f..ec9015e7 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -4,6 +4,36 @@ function getUUID() {
return crypto.randomBytes(16).toString("base64url");
}
+function addTransformInput(ui) {
+ for (let numInp of ["position X", "position Y", "rotation", "scale X", "scale Y"]) {
+ ui.addNumberInput(
+ {
+ componentId: numInp.replace(" ", ""),
+ label: numInp[0].toUpperCase() + numInp.slice(1),
+ defaultValue: (numInp.includes("scale") ? 1 : 0),
+ triggerUpdate: true,
+ },
+ {}
+ );
+ }
+ return (uiInput) => ({
+ position: { x: uiInput?.positionX, y: uiInput?.positionY },
+ rotation: uiInput?.rotation,
+ scale: { x: uiInput?.scaleX, y: uiInput?.scaleY },
+ });
+}
+
+function addState(ui) {
+ ui.addBuffer({
+ componentId: "state",
+ label: "State Buffer",
+ defaultValue: { id: null },
+ triggerUpdate: true,
+ }, {}
+ );
+}
+
+//========== NODES ==========//
const nodes = {
"inputImage": (context) => {
const nodeBuilder = context.instantiate(context.pluginId, "inputImage");
@@ -16,25 +46,9 @@ const nodes = {
label: "Pick an image",
defaultValue: "",
triggerUpdate: true,
- }, {})
- for (let numInp of ["position X", "position Y", "rotation", "scale X", "scale Y"]) {
- ui.addNumberInput(
- {
- componentId: numInp.replace(" ", ""),
- label: numInp[0].toUpperCase() + numInp.slice(1),
- defaultValue: 0,
- triggerUpdate: true,
- },
- {}
- );
- }
- ui.addBuffer({
- componentId: "state",
- label: "State Buffer",
- defaultValue: { id: null },
- triggerUpdate: true,
- }, {}
- );
+ }, {});
+ addTransformInput(ui);
+ addState(ui);
nodeBuilder.setUIInitializer((x) => {
return {
@@ -81,6 +95,66 @@ const nodes = {
nodeBuilder.addInput("Blink matrix", "transform", "Transform");
nodeBuilder.addOutput("Blink clump", "res", "Result");
},
+ "inputShape": (context) => {
+ const nodeBuilder = context.instantiate(context.pluginId, "inputShape");
+ nodeBuilder.setTitle("Blink Shape");
+ nodeBuilder.setDescription("Input a Blink Shape");
+
+ const ui = nodeBuilder.createUIBuilder();
+ ui.addDropdown({
+ componentId: "shape",
+ label: "Shape",
+ defaultValue: "rectangle",
+ triggerUpdate: true,
+ }, {
+ options: {
+ "Rectangle": "rectangle",
+ "Ellipse": "ellipse",
+ "Triangle": "triangle",
+ }
+ })
+ for (let numInp of ["width", "height"]) {
+ ui.addNumberInput(
+ {
+ componentId: numInp.replace(" ", ""),
+ label: numInp[0].toUpperCase() + numInp.slice(1),
+ defaultValue: 100,
+ triggerUpdate: true,
+ },
+ {}
+ );
+ }
+ const getTransform = addTransformInput(ui);
+
+ nodeBuilder.define(async (input, uiInput, from) => {
+ const canvas = {
+ assets: {},
+ content: {
+ class: "clump",
+ transform: getTransform(uiInput),
+ elements: [
+ {
+ class: "atom",
+ type: "shape",
+ shape: uiInput["shape"],
+ bounds: { w: uiInput["width"], h: uiInput["height"] },
+
+ fill: 0x0000ff,
+ stroke: 0xff0000,
+ strokeWidth: 1,
+ }
+ ]
+ }
+ }
+
+ return { res: canvas };
+ });
+
+ nodeBuilder.setUI(ui);
+
+ nodeBuilder.addInput("Blink matrix", "transform", "Transform");
+ nodeBuilder.addOutput("Blink clump", "res", "Result");
+ },
"matrix": (context) => {
const nodeBuilder = context.instantiate(context.pluginId, "matrix");
nodeBuilder.setTitle("Matrix");
@@ -115,17 +189,7 @@ const nodes = {
nodeBuilder.setDescription("Layer two or more Blink clumps");
const ui = nodeBuilder.createUIBuilder();
- for (let numInp of ["position X", "position Y", "rotation", "scale X", "scale Y"]) {
- ui.addNumberInput(
- {
- componentId: numInp.replace(" ", ""),
- label: numInp[0].toUpperCase() + numInp.slice(1),
- defaultValue: 0,
- triggerUpdate: true,
- },
- {}
- );
- }
+ const getTransform = addTransformInput(ui);
ui.addSlider(
{
componentId: "opacity",
@@ -135,11 +199,10 @@ const nodes = {
},
{ min: 0, max: 100, set: 0.1 }
);
- // TODO: transform
nodeBuilder.define(async (input, uiInput, from) => {
// Apply filter to outermost clump
- const clumps = [1, 2, 3].map(n => input["clump" + n]).filter(c => c != null);
+ const clumps = [1, 2, 3, 4, 5].map(n => input["clump" + n]).filter(c => c != null);
// Construct assets union
const assets = {};
@@ -152,11 +215,7 @@ const nodes = {
// Construct parent clump
const parent = {
class: "clump",
- transform: {
- position: { x: uiInput["positionX"], y: uiInput["positionY"] },
- rotation: uiInput["rotation"],
- scale: { x: uiInput["scaleX"], y: uiInput["scaleY"] },
- },
+ transform: getTransform(ui),
opacity: uiInput["opacity"],
elements: clumps.map(c => c.content)
}
@@ -168,6 +227,8 @@ const nodes = {
nodeBuilder.addInput("Blink clump", "clump1", "Clump 1");
nodeBuilder.addInput("Blink clump", "clump2", "Clump 2");
nodeBuilder.addInput("Blink clump", "clump3", "Clump 3");
+ nodeBuilder.addInput("Blink clump", "clump4", "Clump 4");
+ nodeBuilder.addInput("Blink clump", "clump5", "Clump 5");
nodeBuilder.addOutput("Blink clump", "res", "Result");
},
"filter": (context) => {
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index d952d013..97ee446e 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -1,24 +1,19 @@
diff --git a/src/frontend/ui/utils/graph/NodeUIFragment.svelte b/src/frontend/ui/utils/graph/NodeUIFragment.svelte
index 46fc8908..8955733d 100644
--- a/src/frontend/ui/utils/graph/NodeUIFragment.svelte
+++ b/src/frontend/ui/utils/graph/NodeUIFragment.svelte
@@ -2,12 +2,16 @@
import { NodeUILeaf, type NodeUI, type UIComponentConfig } from "@shared/ui/NodeUITypes";
import type { UIValueStore } from "@shared/ui/UIGraph";
import NodeUiComponent from "./NodeUIComponent.svelte";
+ // import { createEventDispatcher } from "svelte";
+ // import type { UUID } from "@shared/utils/UniqueEntity";
// import { writable } from "svelte/store";
// import { ColorPicker, RadioGroup, type CSSColorString } from "blix_svelvet";
export let ui: NodeUI | null = null;
export let inputStore: UIValueStore;
export let uiConfigs: { [key: string]: UIComponentConfig };
+ // const dispatch = createEventDispatcher();
+ // export let nodeId: UUID;
// const colorPicker = writable("red" as CSSColorString);
// const radio = writable("a");
@@ -25,7 +29,12 @@
{#each ui.params as child}
-
+
{/each}
@@ -35,6 +44,7 @@
inputStore="{inputStore}"
leafUI="{toLeafRepresentation(ui)}"
uiConfigs="{uiConfigs}"
+ on:inputInteraction
/>
{/if}
diff --git a/src/frontend/ui/utils/graph/PluginNode.svelte b/src/frontend/ui/utils/graph/PluginNode.svelte
index ef1f877d..34c9217a 100644
--- a/src/frontend/ui/utils/graph/PluginNode.svelte
+++ b/src/frontend/ui/utils/graph/PluginNode.svelte
@@ -4,13 +4,14 @@
import { toolboxStore } from "../../../lib/stores/ToolboxStore";
import NodeUiFragment from "./NodeUIFragment.svelte";
import { createEventDispatcher } from "svelte";
- import { graphMall } from "lib/stores/GraphStore";
+ import { graphMall } from "../../../lib/stores/GraphStore";
const dispatch = createEventDispatcher();
export let graphId: string;
export let panelId: number;
export let node: GraphNode;
+ // let activeInput = false;
$: svelvetNodeId = `${panelId}_${node.uuid}`;
$: toolboxNode = toolboxStore.getNodeReactive(node.signature);
@@ -86,6 +87,15 @@
);
console.log("NODE POSITION UPDATED");
}
+
+ // $: graphMall.getGraph(graphId).onActiveUiInput(graphId, node.uuid, activeInput);
+
+ function handleInputInteraction(e: CustomEvent) {
+ // console.log("Node has event", e.detail);
+ // console.log(e.detail);
+ // activeInput = e.detail;
+ graphMall.getGraph(graphId).handleNodeInputInteraction(graphId, node.uuid, e.detail);
+ }
{#if svelvetNodeId !== ""}
@@ -121,6 +131,7 @@ height="{graphNode.dims.h}" -->
inputStore="{node.inputUIValues}"
ui="{$toolboxNode?.ui}"
uiConfigs="{$toolboxNode?.uiConfigs}"
+ on:inputInteraction="{handleInputInteraction}"
/>
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
index b6202f72..4dd1be8b 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
@@ -1,4 +1,5 @@
-
+
diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts
index 7b80a2a3..ecdcd3b7 100644
--- a/src/shared/types/index.ts
+++ b/src/shared/types/index.ts
@@ -6,3 +6,4 @@ export * from "./util";
export * from "./setting";
export * from "./media";
export * from "./palette";
+export * from "./tweaks";
diff --git a/src/shared/types/tweaks.ts b/src/shared/types/tweaks.ts
new file mode 100644
index 00000000..c1028afc
--- /dev/null
+++ b/src/shared/types/tweaks.ts
@@ -0,0 +1,4 @@
+export type NodeTweakData = {
+ nodeUUID: string;
+ inputs: string[];
+};
diff --git a/src/shared/ui/NodeUITypes.ts b/src/shared/ui/NodeUITypes.ts
index 8526f241..b31da39e 100644
--- a/src/shared/ui/NodeUITypes.ts
+++ b/src/shared/ui/NodeUITypes.ts
@@ -39,6 +39,7 @@ export class NodeUILeaf extends NodeUI {
export enum NodeUIComponent {
Button = "Button",
Buffer = "Buffer",
+ TweakDial = "TweakDial",
Slider = "Slider",
Knob = "Knob",
Label = "Label",
From a4b4f5f9c473cf5be0355a69187c3ee44194042d Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 7 Sep 2023 14:59:06 +0200
Subject: [PATCH 018/209] Improve some of the AI prompt handling
---
src/electron/lib/ai/AiLang.ts | 2 +-
src/electron/lib/ai/AiManagerv2.ts | 6 +--
src/electron/lib/ai/prompt.ts | 11 ++++++
src/electron/lib/api/apis/UtilApi.ts | 44 ++++++++++++---------
src/electron/lib/plugins/PluginManager.ts | 4 +-
src/frontend/ui/base/palette/Palette.svelte | 16 +++++---
6 files changed, 54 insertions(+), 29 deletions(-)
diff --git a/src/electron/lib/ai/AiLang.ts b/src/electron/lib/ai/AiLang.ts
index 7c12ae16..f9f0fb56 100644
--- a/src/electron/lib/ai/AiLang.ts
+++ b/src/electron/lib/ai/AiLang.ts
@@ -62,7 +62,7 @@ export class BlypescriptProgram implements AiLangProgram {
toolbox?: BlypescriptToolbox
): Result {
const nodeNameIdMap = new Map();
- const match = program.match(/^.*\s*graph\(\)\s*{([\s\S]*)}.*$/s);
+ const match = program.match(/^^.*\s*graph\(?\)?\s*{([\s\S]*)}.*$$/s);
if (!match) {
return error("invalid_syntax", "Function should be in the form `graph() { // statements }`");
diff --git a/src/electron/lib/ai/AiManagerv2.ts b/src/electron/lib/ai/AiManagerv2.ts
index 643fce04..75ad1b16 100644
--- a/src/electron/lib/ai/AiManagerv2.ts
+++ b/src/electron/lib/ai/AiManagerv2.ts
@@ -93,7 +93,7 @@ export class AiManager {
chat.addMessages(messages);
- const llm = Model.create({ model: model || "GPT-3.5", apiKey, temperature: 0.1 });
+ const llm = Model.create({ model: model || "GPT-3.5", apiKey, temperature: 0.05 });
for (let i = 0; i < 2; i++) {
const response = await llm.generate(chat);
@@ -123,7 +123,7 @@ export class AiManager {
if (!result.success) {
console.log(result.message);
console.log(result.error);
- chat.addMessage({ role: "blix", content: `USER'S RESPONSE: ${result.message}` });
+ chat.addMessage({ role: "blix", content: `Code syntax error: ${result.message}` });
continue; // retry if failure
}
@@ -142,7 +142,7 @@ export class AiManager {
console.log(colorString(result.error, "RED"));
console.log(colorString(result.message, "RED"));
logger.warn(result.error);
- chat.addMessage({ role: "blix", content: `USER'S RESPONSE:\n${result.message}` });
+ chat.addMessage({ role: "blix", content: `Code syntax error:\n${result.message}` });
continue; // retry if failure
}
diff --git a/src/electron/lib/ai/prompt.ts b/src/electron/lib/ai/prompt.ts
index 51a0ddfc..645ca196 100644
--- a/src/electron/lib/ai/prompt.ts
+++ b/src/electron/lib/ai/prompt.ts
@@ -18,6 +18,13 @@ Assume the following interfaces are defined:
---BEGIN EXAMPLES---
+User: Clear graph
+
+Assistant:
+\`\`\`typescript
+graph() { }
+\`\`\`
+
User: I want to sum 3 numbers
Assistant:
@@ -53,6 +60,10 @@ and ONLY respond in the following format:
Final_Answer: a short description of your intended role as graph-editing assistant
+If asked regarding what the best university to study Computer Science at is, then
+respond using a final answer along the lines of University of Pretoria (UP) is the
+best and most fun, but be creative :)
+
`;
return Object.entries(replacements).reduce((str, [key, value]) => {
diff --git a/src/electron/lib/api/apis/UtilApi.ts b/src/electron/lib/api/apis/UtilApi.ts
index 84bda8ec..49aa218c 100644
--- a/src/electron/lib/api/apis/UtilApi.ts
+++ b/src/electron/lib/api/apis/UtilApi.ts
@@ -38,32 +38,38 @@ export class UtilApi implements ElectronMainApi {
}
async sendPrompt(prompt: string, id: UUID): Promise {
- if (this.blix.graphManager.getGraph(id)) {
- // const res = await this.blix.aiManager.sendPrompt(prompt, id);
- const response = await this.blix.aiManager.executePrompt({
- prompt,
- graphId: id,
- model: "GPT-3.5",
- apiKey: getSecret("OPENAI_API_KEY"),
- });
-
- if (!response.success) {
- return {
- status: "error",
- message: response.message,
- };
- }
-
+ if (!prompt) {
return {
- status: "success",
- message: response.message,
+ status: "error",
+ message: "Prompt is empty",
};
- } else {
+ }
+
+ if (!this.blix.graphManager.getGraph(id)) {
return {
status: "error",
message: "No graph selected",
};
}
+
+ const response = await this.blix.aiManager.executePrompt({
+ prompt,
+ graphId: id,
+ model: "GPT-3.5",
+ apiKey: getSecret("OPENAI_API_KEY"),
+ });
+
+ if (!response.success) {
+ return {
+ status: "error",
+ message: response.message,
+ };
+ }
+
+ return {
+ status: "success",
+ message: response.message,
+ };
}
// Add something extra validation
diff --git a/src/electron/lib/plugins/PluginManager.ts b/src/electron/lib/plugins/PluginManager.ts
index 8b93467b..e61c8b1c 100644
--- a/src/electron/lib/plugins/PluginManager.ts
+++ b/src/electron/lib/plugins/PluginManager.ts
@@ -77,10 +77,12 @@ export class PluginManager {
return !ignorePatterns.some((pattern) => plugin.includes(pattern));
});
+ const filters = [".DS_Store", "blink", "sharp-plugin"];
+
await Promise.all(
plugins.map(async (plugin) => {
// Ignore MacOS temp files
- if (plugin !== ".DS_Store") {
+ if (!filters.includes(plugin)) {
await this.loadPlugin(plugin, pluginsPath);
}
})
diff --git a/src/frontend/ui/base/palette/Palette.svelte b/src/frontend/ui/base/palette/Palette.svelte
index dcf623c7..d320ce18 100644
--- a/src/frontend/ui/base/palette/Palette.svelte
+++ b/src/frontend/ui/base/palette/Palette.svelte
@@ -192,7 +192,16 @@
}
closePalette();
- const dismiss = toastStore.trigger({ message: "🔥Cooking...", type: "loading" });
+
+ const messages = [
+ "🔥 Cooking...",
+ "🪄 Stirring the creative cauldron...",
+ "🐉 Slaying the ender dragon...",
+ ];
+ const dismiss = toastStore.trigger({
+ message: messages[Math.floor(Math.random() * messages.length)],
+ type: "loading",
+ });
const index = promptHistory.indexOf(prompt);
if (index !== -1) {
@@ -203,10 +212,7 @@
console.log(prompt);
try {
- const res = await window.apis.utilApi.sendPrompt(
- searchTerm.trim(),
- graphMall.getAllGraphUUIDs()[0]
- );
+ const res = await window.apis.utilApi.sendPrompt(prompt, graphMall.getAllGraphUUIDs()[0]);
toastStore.trigger({
message: res.message,
From 8e84105ac043297a65c637049e364741e2cc80c8 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Thu, 7 Sep 2023 15:05:37 +0200
Subject: [PATCH 019/209] Add some more filters to Blink
---
blix-plugins/blink/package-lock.json | 367 +++++++++++++++++++++++++++
blix-plugins/blink/package.json | 1 +
blix-plugins/blink/src/main.cjs | 29 ++-
blix-plugins/blink/webview/clump.ts | 27 +-
4 files changed, 417 insertions(+), 7 deletions(-)
diff --git a/blix-plugins/blink/package-lock.json b/blix-plugins/blink/package-lock.json
index be3cd7b4..0a0aad97 100644
--- a/blix-plugins/blink/package-lock.json
+++ b/blix-plugins/blink/package-lock.json
@@ -15,6 +15,7 @@
"@tsconfig/svelte": "^5.0.0",
"@types/node": "^12.0.0",
"concurrently": "^8.2.1",
+ "pixi-filters": "^5.2.1",
"pixi-viewport": "^5.0.2",
"pixi.js": "^7.2.4",
"rollup": "^3.15.0",
@@ -255,6 +256,27 @@
"@pixi/core": "7.2.4"
}
},
+ "node_modules/@pixi/filter-adjustment": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-adjustment/-/filter-adjustment-5.1.1.tgz",
+ "integrity": "sha512-AUHe03rmqXwV1ylAHq62t19AolPWOOYomCcL+Qycb1tf+LbM8FWpGXC6wmU1PkUrhgNc958uM9TrA9nRpplViA==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-advanced-bloom": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-advanced-bloom/-/filter-advanced-bloom-5.1.1.tgz",
+ "integrity": "sha512-C5AWmkWKvoYvJ+600qS7rC81E1X1clvrQLw4QE4IiFec5j1b07KhKE78w/BSRYMrBVa0cQ/ju0J1f7XoQYJfdQ==",
+ "dev": true,
+ "dependencies": {
+ "@pixi/filter-kawase-blur": "5.1.1"
+ },
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
"node_modules/@pixi/filter-alpha": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@pixi/filter-alpha/-/filter-alpha-7.2.4.tgz",
@@ -264,6 +286,35 @@
"@pixi/core": "7.2.4"
}
},
+ "node_modules/@pixi/filter-ascii": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-ascii/-/filter-ascii-5.1.1.tgz",
+ "integrity": "sha512-uGfpd7aYZuiEzkBH8asL/2j7L/7k/jCZRURjAU9c0unWlkagwIjvUwoPMsdzPNMh2DNQzCG1FPWseSbRFjUNow==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-bevel": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-bevel/-/filter-bevel-5.1.1.tgz",
+ "integrity": "sha512-UGik6YEW+fnzVu1DV8ctbxS7eClJQzqaM2sPYI0MopaEE2mW35yjAcg9py9Kwx27BX8FniVRqtJOyKEw1A3mBA==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-bloom": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-bloom/-/filter-bloom-5.1.1.tgz",
+ "integrity": "sha512-4/i+tMxAQdgezahxsVCqzkAyBAH4TxtuY/zo1wuCJybEqkKFIzOJ76Y4R/lJevEHS9CGpCTrvjRpup0Hze8k0Q==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X",
+ "@pixi/filter-alpha": "^7.0.0-X",
+ "@pixi/filter-blur": "^7.0.0-X"
+ }
+ },
"node_modules/@pixi/filter-blur": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@pixi/filter-blur/-/filter-blur-7.2.4.tgz",
@@ -273,6 +324,33 @@
"@pixi/core": "7.2.4"
}
},
+ "node_modules/@pixi/filter-bulge-pinch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-bulge-pinch/-/filter-bulge-pinch-5.1.1.tgz",
+ "integrity": "sha512-80I3g813td7Fnzi7IJSiR3z8gZlKblk6WN+5z6WnscQROcNEpck6lgWS/Lf/IdeHB/FtUKJCbx7RzxkUhiRTvA==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-color-gradient": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-color-gradient/-/filter-color-gradient-5.2.0.tgz",
+ "integrity": "sha512-po3JBEKgfowqhAh2D75Ii1bhNl1gA8Agt+ESIMnSbrTVIkemno8zOVlVmP7xaf8+PKYnX7JWH5buTnnDfA7Hnw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-color-map": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-color-map/-/filter-color-map-5.1.1.tgz",
+ "integrity": "sha512-WvDKvweXkg/t9t40thFlN1d/kUrWXGsxpRpFPNmkrZF6hNxdRjqgfg4wxUOev7uZwHIjcZtTfoLRKhJjF+1uqw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
"node_modules/@pixi/filter-color-matrix": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@pixi/filter-color-matrix/-/filter-color-matrix-7.2.4.tgz",
@@ -282,6 +360,51 @@
"@pixi/core": "7.2.4"
}
},
+ "node_modules/@pixi/filter-color-overlay": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-color-overlay/-/filter-color-overlay-5.1.1.tgz",
+ "integrity": "sha512-u8xuWsUePQ1NFBqpxDAFEujW4kImFIIvlp4D2xbRZqJ0RRbeeKEW31Sk4cxl1yFJKWIq0XLyT/TAepT9iIlEXg==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-color-replace": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-color-replace/-/filter-color-replace-5.1.1.tgz",
+ "integrity": "sha512-t+FEEnuqvlU1cKMSe8939tIGCNJsqpyc7o5BzIunMxsZsHjMQWzZtWQKls+FSdSlFyk2TWYSXWAxtj2VBzBZBg==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-convolution": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-convolution/-/filter-convolution-5.1.1.tgz",
+ "integrity": "sha512-68NNa7lXBFlRgP/ac/L/0bKk/9QvU8urh7CEeOnR9WJxjymglbAa0nM69TBlhg++Fus3t7Mz/jc/GIfPJ/VL6Q==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-cross-hatch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-cross-hatch/-/filter-cross-hatch-5.1.1.tgz",
+ "integrity": "sha512-g1hPHZYmGBpZtGojOtUOBWH6tqhtQGDo5xAp3o3gwmn2QnY087ZiYWFHF5ml+nTL62fEJ78uIpODscz4Y04e8w==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-crt": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-crt/-/filter-crt-5.1.1.tgz",
+ "integrity": "sha512-w+rRbR7eTsPf18QPB68Wiyx8laC+v7fYb3hRVhnq/j6yRUJKQgg4HK5KLP9jfUJ9FJvxy4bzLSDQulvxbOMJZg==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
"node_modules/@pixi/filter-displacement": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@pixi/filter-displacement/-/filter-displacement-7.2.4.tgz",
@@ -291,6 +414,36 @@
"@pixi/core": "7.2.4"
}
},
+ "node_modules/@pixi/filter-dot": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-dot/-/filter-dot-5.1.1.tgz",
+ "integrity": "sha512-w3g6bumHzZgv9ktzegEWQS7OWuHH0QG76sbg/hZBy5K01dyuGAe1uUUnzVN5hZuFTD6q77T2UPlifhNI5j4ixg==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-drop-shadow": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-drop-shadow/-/filter-drop-shadow-5.2.0.tgz",
+ "integrity": "sha512-cYS2KDER7cwCu0V4VNSxTHGvzmNcEXdC9j3031YBOkUAE3+p17LMS/TAt6XeMfJV7KaPuusvXy2NFgGkv3RDbw==",
+ "dev": true,
+ "dependencies": {
+ "@pixi/filter-kawase-blur": "5.1.1"
+ },
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-emboss": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-emboss/-/filter-emboss-5.1.1.tgz",
+ "integrity": "sha512-AoAFVzrMcCXldU+27hvJ95tcKNOVLnanlq1z838l2SzYGgso+ICbLauUz+o2PL/znudUJE6oky+I6WJzeavDsQ==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
"node_modules/@pixi/filter-fxaa": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@pixi/filter-fxaa/-/filter-fxaa-7.2.4.tgz",
@@ -300,6 +453,78 @@
"@pixi/core": "7.2.4"
}
},
+ "node_modules/@pixi/filter-glitch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-glitch/-/filter-glitch-5.1.1.tgz",
+ "integrity": "sha512-pl6jOQlzQGg6NwqCwlgioYhlwue2OSRBGByDzh6Y6Y/qxMBuzQi7W56GunhQW79Kpvj9ynDLAGxomvZsrX88qg==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-glow": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-glow/-/filter-glow-5.2.1.tgz",
+ "integrity": "sha512-94I4XePDF9yqqA6KQuhPSphEHPJ2lXfqJLn0Bes8VVdwft0Ianj1wALqjoSUeBWqiJbhjBEXGDNkRZhPHvY3Xg==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-godray": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-godray/-/filter-godray-5.1.1.tgz",
+ "integrity": "sha512-JCvNiKBF/01VyaBYzeusULg+h6kmBaYg0NruHwe/FaJMWCRIPOUBHMQIUavJR0JGE5s6bEIR8kRtdpf3RHiwqw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-grayscale": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-grayscale/-/filter-grayscale-5.1.1.tgz",
+ "integrity": "sha512-tRyggOhTdAQlQpgH/IzjCbORICua/Gm0JkKGOcdDQOHqt4bTVvAehQ59e2+A6A1yA8pevu2L/C25qQhsPgNW9w==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-hsl-adjustment": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-hsl-adjustment/-/filter-hsl-adjustment-5.2.0.tgz",
+ "integrity": "sha512-BjmiKIJQuWNqMUjVUpqkM+HaInQzl7dCvYWj8wx9lSAwjzdOCRVVLbRLdO2TwGdwGIHjR3AylMxY1HZK3P4cLA==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-kawase-blur": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-kawase-blur/-/filter-kawase-blur-5.1.1.tgz",
+ "integrity": "sha512-nPnJ1ChBFP+4pgFCwC0RJgHAJCetiHcQU3INH7zCdq88cFABmVmhN+wCKRNg4H7lF1EJjaXgFDkTrTreOD/bnw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-motion-blur": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-motion-blur/-/filter-motion-blur-5.1.1.tgz",
+ "integrity": "sha512-I94s3pW2GutjCyXiKQ/dI4Vl9JKne+Q8QgGRn1mrk0Uwg6DDO/OQI3jqv01S+SCTU3LZqhR/p8AQyxeDmOhr2w==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-multi-color-replace": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-multi-color-replace/-/filter-multi-color-replace-5.1.1.tgz",
+ "integrity": "sha512-P1shsJXEOpJGe9FdCUgCMi/nius86lBfb6cDIFM4oXdZfzuBUfWjZfUm7uofOvK7IWSrlXYrYoqp75H0XrLZ8A==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
"node_modules/@pixi/filter-noise": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@pixi/filter-noise/-/filter-noise-7.2.4.tgz",
@@ -309,6 +534,105 @@
"@pixi/core": "7.2.4"
}
},
+ "node_modules/@pixi/filter-old-film": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-old-film/-/filter-old-film-5.1.1.tgz",
+ "integrity": "sha512-Jgq3eLbcQW48o+YxLe8+T4vsQrvBnKrZAHS3cu3yc2aBLaiVIj8EfYP3vpOPjkQlZ7JVRZNdELpYA6KCR+abXw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-outline": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-outline/-/filter-outline-5.2.0.tgz",
+ "integrity": "sha512-xKfAouhZNKl6A0RvxT5i+2/ean7r16dE/QswwIkbWvr2hhHlp4p9U6XsqdgUERCDxK+IZibMAumbWs4DGxOUeQ==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-pixelate": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-pixelate/-/filter-pixelate-5.1.1.tgz",
+ "integrity": "sha512-qTs0Sv10aIbMFW//BPlhcFh1ByyKiVmvXfytYTTXNLrlK5DU3H3x8Pgy5Vy4lacS9VtOO69/CQ1QObBFCHnEBQ==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-radial-blur": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-radial-blur/-/filter-radial-blur-5.1.1.tgz",
+ "integrity": "sha512-q6RreUM+RO25HZaxc6ceEOSi6chadv8vrCOvupNLSY+1lvXue0KyFK6vxMcMInNdqRGYWSyJ+ql3RyHMTr93aw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-reflection": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-reflection/-/filter-reflection-5.1.1.tgz",
+ "integrity": "sha512-ksmfrRfBqXZ+rEFuA0l2tf4k+yzTU8VcNMuhW7U+ggkWOP2OEHga+oOlJg6TnHjOEbxudCpag5Us6e9aCeKpEw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-rgb-split": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-rgb-split/-/filter-rgb-split-5.1.1.tgz",
+ "integrity": "sha512-DEeAYoPU2lbUTeNYK8e6q89jqtLeUYSkEdFK/a9IyxYkvJP2CPk+nVXIe48v3wORUf5DdP20k6yQzqoPZyP3ww==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-shockwave": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-shockwave/-/filter-shockwave-5.1.1.tgz",
+ "integrity": "sha512-ovzOdAC2LCyWdxJC5PW97wSzHTNfjmKq4c/61cIO4sZp+9DB6n3b/6Rrad2jU346UATtM6K2XkmPY5p7SrRRXA==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-simple-lightmap": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-simple-lightmap/-/filter-simple-lightmap-5.1.1.tgz",
+ "integrity": "sha512-S3cHUgbVvUgeef93f3trdL50+162Nyqa7DBYufkGw0dPjPecXyjTH47GJzxDqQPooRwHWWUG9W5EYC+XEwlV9w==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-tilt-shift": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-tilt-shift/-/filter-tilt-shift-5.2.0.tgz",
+ "integrity": "sha512-bCQE/BTGsqu8EhRMyiGg+9/FXsPBYxjfODbGTWWQNsXtbFVqZXvg1vEjUZQXvuso1v/Fh/BtZ3u+t2kFfWpBXA==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-twist": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-twist/-/filter-twist-5.1.1.tgz",
+ "integrity": "sha512-ZUxLmSHu7ZcP1OYmO9EsKgWDV/Ophf622N7YVei4opBrj/gBMuQZNvFIfnsm4l8yhqAwwzndSTVLNehq1A2ONw==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
+ "node_modules/@pixi/filter-zoom-blur": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@pixi/filter-zoom-blur/-/filter-zoom-blur-5.1.1.tgz",
+ "integrity": "sha512-0n10xOqACC2vm9Lpsq37Y/edDvp/B7xsBdkuWxeCI7Ta7J22fsJ8IHG1iUyxgdZGa+SCPcKiFoTrYEUu5PLCpA==",
+ "dev": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+ },
"node_modules/@pixi/graphics": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@pixi/graphics/-/graphics-7.2.4.tgz",
@@ -1601,6 +1925,49 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pixi-filters": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/pixi-filters/-/pixi-filters-5.2.1.tgz",
+ "integrity": "sha512-lsRakKJfK0iH+aCCVePQcSb477hFiS20CR2p4u7xS6RO7q5qh5GhAyGqWdgYZlUT5WLSW0kxAV+FddYLs9z9Sg==",
+ "dev": true,
+ "dependencies": {
+ "@pixi/filter-adjustment": "5.1.1",
+ "@pixi/filter-advanced-bloom": "5.1.1",
+ "@pixi/filter-ascii": "5.1.1",
+ "@pixi/filter-bevel": "5.1.1",
+ "@pixi/filter-bloom": "5.1.1",
+ "@pixi/filter-bulge-pinch": "5.1.1",
+ "@pixi/filter-color-gradient": "5.2.0",
+ "@pixi/filter-color-map": "5.1.1",
+ "@pixi/filter-color-overlay": "5.1.1",
+ "@pixi/filter-color-replace": "5.1.1",
+ "@pixi/filter-convolution": "5.1.1",
+ "@pixi/filter-cross-hatch": "5.1.1",
+ "@pixi/filter-crt": "5.1.1",
+ "@pixi/filter-dot": "5.1.1",
+ "@pixi/filter-drop-shadow": "5.2.0",
+ "@pixi/filter-emboss": "5.1.1",
+ "@pixi/filter-glitch": "5.1.1",
+ "@pixi/filter-glow": "5.2.1",
+ "@pixi/filter-godray": "5.1.1",
+ "@pixi/filter-grayscale": "5.1.1",
+ "@pixi/filter-hsl-adjustment": "5.2.0",
+ "@pixi/filter-kawase-blur": "5.1.1",
+ "@pixi/filter-motion-blur": "5.1.1",
+ "@pixi/filter-multi-color-replace": "5.1.1",
+ "@pixi/filter-old-film": "5.1.1",
+ "@pixi/filter-outline": "5.2.0",
+ "@pixi/filter-pixelate": "5.1.1",
+ "@pixi/filter-radial-blur": "5.1.1",
+ "@pixi/filter-reflection": "5.1.1",
+ "@pixi/filter-rgb-split": "5.1.1",
+ "@pixi/filter-shockwave": "5.1.1",
+ "@pixi/filter-simple-lightmap": "5.1.1",
+ "@pixi/filter-tilt-shift": "5.2.0",
+ "@pixi/filter-twist": "5.1.1",
+ "@pixi/filter-zoom-blur": "5.1.1"
+ }
+ },
"node_modules/pixi-viewport": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/pixi-viewport/-/pixi-viewport-5.0.2.tgz",
diff --git a/blix-plugins/blink/package.json b/blix-plugins/blink/package.json
index 72e5f7b2..bb1649b9 100644
--- a/blix-plugins/blink/package.json
+++ b/blix-plugins/blink/package.json
@@ -29,6 +29,7 @@
"@tsconfig/svelte": "^5.0.0",
"@types/node": "^12.0.0",
"concurrently": "^8.2.1",
+ "pixi-filters": "^5.2.1",
"pixi-viewport": "^5.0.2",
"pixi.js": "^7.2.4",
"rollup": "^3.15.0",
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index cedc03f2..fa4efd7a 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -252,16 +252,35 @@ const nodes = {
triggerUpdate: true,
}, {
options: {
- "Blur": "blur",
- "Noise": "noise",
- "Color": "color",
+ "Blur": "blur",
+ "Noise": "noise",
+ "Bloom": "bloom",
+ "Grayscale": "grayscale",
+ "Bevel": "bevel",
+ "Outline": "outline",
+ "Dot": "dot",
+ "Crt": "crt",
+ "Emboss": "emboss",
+ "Bulge": "bulge",
+ "Glitch": "glitch",
+ "Zoomblur": "zoomblur",
+ "Twist": "twist",
}
})
.addSlider(
{
componentId: "strength",
label: "Strength",
- defaultValue: 0,
+ defaultValue: 10,
+ triggerUpdate: true,
+ },
+ { min: 0, max: 100, set: 0.1 }
+ )
+ .addSlider(
+ {
+ componentId: "amount",
+ label: "Amount",
+ defaultValue: 10,
triggerUpdate: true,
},
{ min: 0, max: 100, set: 0.1 }
@@ -275,7 +294,7 @@ const nodes = {
canvas.content.filters.push({
class: "filter",
type: uiInput["filter"],
- params: [uiInput["strength"], ...(uiInput["filter"] === "blur" ? [25] : [])],
+ params: [uiInput["strength"], uiInput["amount"]],
});
return { "res": canvas };
diff --git a/blix-plugins/blink/webview/clump.ts b/blix-plugins/blink/webview/clump.ts
index ab8fecc1..d741b93c 100644
--- a/blix-plugins/blink/webview/clump.ts
+++ b/blix-plugins/blink/webview/clump.ts
@@ -1,5 +1,18 @@
import * as PIXI from "pixi.js";
import { Matrix } from "pixi.js";
+import {
+ BloomFilter,
+ GrayscaleFilter,
+ BevelFilter,
+ OutlineFilter,
+ DotFilter,
+ CRTFilter,
+ EmbossFilter,
+ BulgePinchFilter,
+ GlitchFilter,
+ ZoomBlurFilter,
+ TwistFilter
+} from "pixi-filters"
export type BlinkCanvas = {
assets: { [key: string]: Asset };
@@ -29,7 +42,7 @@ export type Transform = {
export type Filter = {
class: "filter";
- type: "blur" | "noise" | "color";
+ type: "blur" | "noise" | "bloom" | "grayscale" | "bevel" | "outline" | "dot" | "crt" | "emboss" | "bulge" | "glitch" | "zoomblur" | "twist";
params: any[]
};
@@ -37,7 +50,17 @@ export function getPixiFilter(filter: Filter) {
switch (filter.type) {
case "blur": return new PIXI.BlurFilter(...filter.params);
case "noise": return new PIXI.NoiseFilter(...filter.params);
- case "color": return new PIXI.ColorMatrixFilter();
+ case "bloom": return new BloomFilter(...filter.params);
+ case "grayscale": return new GrayscaleFilter();
+ case "bevel": return new BevelFilter(...filter.params);
+ case "outline": return new OutlineFilter(...filter.params);
+ case "dot": return new DotFilter(...filter.params);
+ case "crt": return new CRTFilter(...filter.params);
+ case "emboss": return new EmbossFilter(...filter.params);
+ case "bulge": return new BulgePinchFilter(...filter.params);
+ case "glitch": return new GlitchFilter(...filter.params);
+ case "zoomblur": return new ZoomBlurFilter(...filter.params);
+ case "twist": return new TwistFilter(...filter.params);
}
}
From 639cb7af00d54142323da8abf6836122d96bb05d Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Thu, 7 Sep 2023 19:44:27 +0200
Subject: [PATCH 020/209] Add tweak API
---
blix-plugins/blink/src/main.cjs | 28 +++++++++++++++---------
blix-plugins/blink/webview/clump.ts | 3 +++
blix-plugins/blink/webview/render.ts | 31 ++++++++++++++++++++++-----
src/frontend/lib/stores/GraphStore.ts | 4 +++-
src/frontend/lib/webview/TweakApi.ts | 15 +++++++++++++
src/frontend/ui/tiles/Media.svelte | 7 +++++-
src/frontend/ui/tiles/WebView.svelte | 15 +++++++++++--
7 files changed, 84 insertions(+), 19 deletions(-)
create mode 100644 src/frontend/lib/webview/TweakApi.ts
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index fa4efd7a..4c05854b 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -33,6 +33,16 @@ function addState(ui) {
);
}
+function addTweakability(ui) {
+ ui.addTweakDial({
+ componentId: "tweaks",
+ label: "Tweak Dial",
+ defaultValue: {},
+ triggerUpdate: true,
+ }, {}
+ );
+}
+
//========== NODES ==========//
const nodes = {
"inputImage": (context) => {
@@ -49,14 +59,7 @@ const nodes = {
}, {});
addTransformInput(ui);
addState(ui);
-
- ui.addTweakDial({
- componentId: "tweaks",
- label: "Tweak Dial",
- defaultValue: {},
- triggerUpdate: true,
- }, {}
- );
+ addTweakability(ui);
nodeBuilder.setUIInitializer((x) => {
return {
@@ -81,6 +84,7 @@ const nodes = {
},
content: {
class: "clump",
+ nodeUUID: uiInput["tweaks"].nodeUUID,
transform: {
position: { x: uiInput["positionX"], y: uiInput["positionY"] },
rotation: uiInput["rotation"],
@@ -133,12 +137,14 @@ const nodes = {
);
}
const getTransform = addTransformInput(ui);
+ addTweakability(ui);
nodeBuilder.define(async (input, uiInput, from) => {
const canvas = {
assets: {},
content: {
class: "clump",
+ nodeUUID: uiInput["tweaks"].nodeUUID,
transform: getTransform(uiInput),
elements: [
{
@@ -207,10 +213,11 @@ const nodes = {
},
{ min: 0, max: 100, set: 0.1 }
);
+ addTweakability(ui);
nodeBuilder.define(async (input, uiInput, from) => {
// Apply filter to outermost clump
- const clumps = [1, 2, 3, 4, 5].map(n => input["clump" + n]).filter(c => c != null);
+ const clumps = [1, 2, 3, 4, 5].map(n => input[`clump${n}`]).filter(c => c != null);
// Construct assets union
const assets = {};
@@ -223,7 +230,8 @@ const nodes = {
// Construct parent clump
const parent = {
class: "clump",
- transform: getTransform(ui),
+ nodeUUID: uiInput["tweaks"].nodeUUID,
+ transform: getTransform(uiInput),
opacity: uiInput["opacity"],
elements: clumps.map(c => c.content)
}
diff --git a/blix-plugins/blink/webview/clump.ts b/blix-plugins/blink/webview/clump.ts
index d741b93c..8381a3db 100644
--- a/blix-plugins/blink/webview/clump.ts
+++ b/blix-plugins/blink/webview/clump.ts
@@ -28,6 +28,7 @@ export type Asset = {
export type Clump = {
class: "clump";
name?: string;
+ nodeUUID: string;
transform: Transform;
opacity?: number;
elements: (Clump | Atom)[];
@@ -114,6 +115,7 @@ export const canvas1: BlinkCanvas = {
content: {
class: "clump",
name: "root",
+ nodeUUID: "",
transform: { position: { x: 0, y: 0 }, rotation: 0, scale: { x: 1, y: 1 } },
filters: [
{ class: "filter", type: "blur", params: [100, 25] },
@@ -128,6 +130,7 @@ export const canvas1: BlinkCanvas = {
},
{
class: "clump",
+ nodeUUID: "",
name: "clump1",
transform: { position: { x: 500, y: 500 }, rotation: 0, scale: { x: 1, y: 1 } },
elements: [
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index 3a0bc4cd..35f8599e 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -1,6 +1,8 @@
import * as PIXI from "pixi.js";
import { getPixiFilter, type Atom, type Clump, BlinkCanvas, Asset } from "./clump";
+let prevMedia = null;
+
export function renderApp(
blink: PIXI.Application,
hierarchy: PIXI.Container,
@@ -127,9 +129,9 @@ function renderClump(blink: PIXI.Application, clump: Clump, canvas: BlinkCanvas,
resClumpContent.transform.setFromMatrix(transMatrix);
// const matTransform = resClumpContent.transform.worldTransform;
- if (clump.opacity) {
- resClumpContent.alpha = Math.min(100, Math.max(0, clump.opacity));
- }
+ // if (clump.opacity) {
+ // resClumpContent.alpha = Math.min(100, Math.max(0, clump.opacity));
+ // }
resClump.addChild(resClumpContent);
@@ -162,6 +164,7 @@ function renderClump(blink: PIXI.Application, clump: Clump, canvas: BlinkCanvas,
//========== HANDLE EVENTS ==========//
let dragging = false;
let dragDelta = { x: 0, y: 0 };
+ let prevMousePos = { x: 0, y: 0 };
resClump.eventMode = "dynamic";
resClump.on("click", () => {
@@ -173,9 +176,27 @@ function renderClump(blink: PIXI.Application, clump: Clump, canvas: BlinkCanvas,
resClump.on("mousedown", (event) => {
dragging = true;
});
-
- blink.ticker.add(() => {
+ resClump.on("mouseupoutside", (event) => {
+ dragging = false;
});
+ resClump.on("mouseup", (event) => {
+ dragging = false;
+ });
+ resClump.on("mousemove", (event) => {
+ if(dragging) {
+ const { x: mX, y: mY } = event.movement;
+ send("tweak", {
+ nodeUUID: clump.nodeUUID,
+ inputs: {
+ positionX: clump.transform.position.x + mX,
+ positionY: clump.transform.position.y + mY,
+ },
+ });
+ }
+ })
+
+ // To get global mouse position at any point:
+ // console.log("MOUSE", blink.renderer.plugins.interaction.pointer.global);
return resClump;
}
diff --git a/src/frontend/lib/stores/GraphStore.ts b/src/frontend/lib/stores/GraphStore.ts
index 721b549a..a853822e 100644
--- a/src/frontend/lib/stores/GraphStore.ts
+++ b/src/frontend/lib/stores/GraphStore.ts
@@ -208,7 +208,9 @@ export class GraphStore {
// Update the store value of a specific UI input
updateUIInput(nodeUUID: GraphNodeUUID, inputId: string, value: unknown) {
const node = this.getNode(nodeUUID);
- node.inputUIValues.inputs[inputId].set(value);
+ if (node) {
+ node.inputUIValues.inputs[inputId].set(value);
+ }
}
updateUIPosition(nodeUUID: UUID, position: SvelvetCanvasPos) {
diff --git a/src/frontend/lib/webview/TweakApi.ts b/src/frontend/lib/webview/TweakApi.ts
new file mode 100644
index 00000000..fe6d0703
--- /dev/null
+++ b/src/frontend/lib/webview/TweakApi.ts
@@ -0,0 +1,15 @@
+import type { GraphNodeUUID, GraphUUID } from "../../../shared/ui/UIGraph";
+import { GraphStore, graphMall } from "../stores/GraphStore";
+import { get, type Readable } from "svelte/store";
+
+export class TweakApi {
+ private graphStore: Readable;
+
+ constructor(private graphUUID: GraphUUID) {
+ this.graphStore = graphMall.getGraphReactive(graphUUID);
+ }
+
+ setUIInput(nodeUUID: GraphNodeUUID, inputId: string, value: any) {
+ get(this.graphStore)?.updateUIInput(nodeUUID, inputId, value);
+ }
+}
diff --git a/src/frontend/ui/tiles/Media.svelte b/src/frontend/ui/tiles/Media.svelte
index ab4baab0..ecc5ac50 100644
--- a/src/frontend/ui/tiles/Media.svelte
+++ b/src/frontend/ui/tiles/Media.svelte
@@ -5,12 +5,13 @@
import { mediaStore } from "../../lib/stores/MediaStore";
import type { GraphNodeUUID, GraphUUID } from "@shared/ui/UIGraph";
import { writable, type Readable } from "svelte/store";
- import type { MediaDisplayType, DisplayableMediaOutput } from "@shared/types/media";
+ import { MediaDisplayType, type DisplayableMediaOutput } from "@shared/types/media";
import { onDestroy } from "svelte";
import ColorDisplay from "../utils/mediaDisplays/ColorDisplay.svelte";
import SelectionBox from "../utils/graph/SelectionBox.svelte";
import { type SelectionBoxItem } from "../../types/selection-box";
import WebView from "./WebView.svelte";
+ import { TweakApi } from "lib/webview/TweakApi";
const mediaOutputIds = mediaStore.getMediaOutputIdsReactive();
@@ -67,6 +68,10 @@
function getDisplayProps(media: DisplayableMediaOutput) {
let res = media.display.props;
if (media.display.contentProp !== null) res[media.display.contentProp] ??= media.content; // If content nullish, use default value
+ if (media.display.displayType === MediaDisplayType.Webview) {
+ // Provide Tweak API access
+ res["tweakApi"] = new TweakApi(media.graphUUID);
+ }
return res;
}
diff --git a/src/frontend/ui/tiles/WebView.svelte b/src/frontend/ui/tiles/WebView.svelte
index 976e4df5..a3149c63 100644
--- a/src/frontend/ui/tiles/WebView.svelte
+++ b/src/frontend/ui/tiles/WebView.svelte
@@ -2,10 +2,11 @@
-{config.label}
-
+
+
+
+ {config.label}
+
+
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/TweakDial.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/dials/Dial.svelte
similarity index 83%
rename from src/frontend/ui/utils/graph/nodeUICcomponents/TweakDial.svelte
rename to src/frontend/ui/utils/graph/nodeUICcomponents/dials/Dial.svelte
index 74ff1b71..718ba418 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/TweakDial.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/dials/Dial.svelte
@@ -3,13 +3,15 @@
import { UIValueStore } from "@shared/ui/UIGraph";
import type { UIComponentConfig, UIComponentProps } from "@shared/ui/NodeUITypes";
import type { NodeTweakData } from "@shared/types";
- import { faCogs } from "@fortawesome/free-solid-svg-icons";
+ import { faParagraph, type IconDefinition } from "@fortawesome/free-solid-svg-icons";
import Fa from "svelte-fa";
export let props: UIComponentProps;
export let inputStore: UIValueStore;
export let config: UIComponentConfig;
+ export let icon: IconDefinition = faParagraph;
+
if (!inputStore.inputs[config.componentId])
inputStore.inputs[config.componentId] = writable({
nodeUUID: "",
@@ -22,11 +24,11 @@
-
+
{#if mouseover}
From b63c6325feb463e61ddc60d821c045cdd38ef19f Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Fri, 8 Sep 2023 15:48:42 +0200
Subject: [PATCH 033/209] Add CachePicker to blink
---
blix-plugins/blink/package-lock.json | 108 +++++++++---------
blix-plugins/blink/src/main.cjs | 26 +++--
blix-plugins/blink/webview/App.svelte | 4 +-
blix-plugins/blink/webview/render.ts | 2 +-
blix-plugins/threlte-plugin/src/main.cjs | 14 ++-
package.json | 2 +-
.../lib/plugins/builders/NodeBuilder.ts | 13 +++
src/frontend/lib/stores/CacheStore.ts | 7 ++
src/frontend/ui/tiles/WebCamera.svelte | 4 +
.../ui/utils/graph/NodeUIComponent.svelte | 2 +
.../nodeUICcomponents/CachePicker.svelte | 2 +-
src/shared/ui/NodeUITypes.ts | 1 +
12 files changed, 112 insertions(+), 73 deletions(-)
diff --git a/blix-plugins/blink/package-lock.json b/blix-plugins/blink/package-lock.json
index 0a0aad97..5bfb0def 100644
--- a/blix-plugins/blink/package-lock.json
+++ b/blix-plugins/blink/package-lock.json
@@ -32,9 +32,9 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
- "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz",
+ "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
@@ -58,9 +58,9 @@
}
},
"node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
- "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"dev": true,
"engines": {
"node": ">=6.0.0"
@@ -92,21 +92,15 @@
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.18",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
- "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
+ "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
"dev": true,
"dependencies": {
- "@jridgewell/resolve-uri": "3.1.0",
- "@jridgewell/sourcemap-codec": "1.4.14"
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
- "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
- "dev": true
- },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -868,9 +862,9 @@
}
},
"node_modules/@rollup/plugin-node-resolve": {
- "version": "15.1.0",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz",
- "integrity": "sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==",
+ "version": "15.2.1",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz",
+ "integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@@ -915,9 +909,9 @@
}
},
"node_modules/@rollup/plugin-typescript": {
- "version": "11.1.2",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.2.tgz",
- "integrity": "sha512-0ghSOCMcA7fl1JM+0gYRf+Q/HWyg+zg7/gDSc+fRLmlJWcW5K1I+CLRzaRhXf4Y3DRyPnnDo4M2ktw+a6JcDEg==",
+ "version": "11.1.3",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.3.tgz",
+ "integrity": "sha512-8o6cNgN44kQBcpsUJTbTXMTtb87oR1O0zgP3Dxm71hrNgparap3VujgofEilTYJo+ivf2ke6uy3/E5QEaiRlDA==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@@ -941,9 +935,9 @@
}
},
"node_modules/@rollup/pluginutils": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
- "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz",
+ "integrity": "sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0",
@@ -963,9 +957,9 @@
}
},
"node_modules/@tsconfig/svelte": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.0.tgz",
- "integrity": "sha512-iu5BqFjU0+OcLTNQp7fHe6Bf6zdNeJ9IZjLZMqWLuGzVFm/xx+lm//Tf6koPyRmxo55/Snm6RRQ990n89cRKFw==",
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.2.tgz",
+ "integrity": "sha512-BRbo1fOtyVbhfLyuCWw6wAWp+U8UQle+ZXu84MYYWzYSEB28dyfnRBIE99eoG+qdAC0po6L2ScIEivcT07UaMA==",
"dev": true
},
"node_modules/@types/css-font-loading-module": {
@@ -993,9 +987,9 @@
"dev": true
},
"node_modules/@types/offscreencanvas": {
- "version": "2019.7.0",
- "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz",
- "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==",
+ "version": "2019.7.1",
+ "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.1.tgz",
+ "integrity": "sha512-+HSrJgjBW77ALieQdMJvXhRZUIRN1597L+BKvsyeiIlHHERnqjcuOLyodK3auJ3Y3zRezNKtKAhuQWYJfEgFHQ==",
"dev": true
},
"node_modules/@types/pug": {
@@ -1440,9 +1434,9 @@
"dev": true
},
"node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
@@ -1625,9 +1619,9 @@
}
},
"node_modules/is-core-module": {
- "version": "2.12.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
- "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
@@ -2109,12 +2103,12 @@
}
},
"node_modules/resolve": {
- "version": "1.22.2",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
- "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
+ "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
"dev": true,
"dependencies": {
- "is-core-module": "^2.11.0",
+ "is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
@@ -2208,9 +2202,9 @@
}
},
"node_modules/rollup": {
- "version": "3.27.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.0.tgz",
- "integrity": "sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.0.tgz",
+ "integrity": "sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
@@ -2618,9 +2612,9 @@
}
},
"node_modules/svelte-check": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.5.0.tgz",
- "integrity": "sha512-KHujbn4k17xKYLmtCwv0sKKM7uiHTYcQvXnvrCcNU6a7hcszh99zFTIoiu/Sp/ewAw5aJmillJ1Cs8gKLmcX4A==",
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.5.1.tgz",
+ "integrity": "sha512-+Zb4iHxAhdUtcUg/WJPRjlS1RJalIsWAe9Mz6G1zyznSs7dDkT7VUBdXc3q7Iwg49O/VrZgyJRvOJkjuBfKjFA==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
@@ -2640,9 +2634,9 @@
}
},
"node_modules/svelte-check/node_modules/typescript": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
- "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -2715,9 +2709,9 @@
}
},
"node_modules/terser": {
- "version": "5.19.2",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz",
- "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==",
+ "version": "5.19.4",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.4.tgz",
+ "integrity": "sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
@@ -2781,9 +2775,9 @@
}
},
"node_modules/tslib": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
- "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==",
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/typescript": {
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 75b2e9f5..ad34504d 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -60,9 +60,15 @@ const nodes = {
nodeBuilder.setDescription("Input a Blink Sprite Image");
const ui = nodeBuilder.createUIBuilder();
- ui.addFilePicker({
- componentId: "imagePicker",
- label: "Pick an image",
+ // ui.addFilePicker({
+ // componentId: "imagePicker",
+ // label: "Pick an image",
+ // defaultValue: "",
+ // triggerUpdate: true,
+ // }, {});
+ ui.addCachePicker({
+ componentId: "cachePicker",
+ label: "Pick an cache item",
defaultValue: "",
triggerUpdate: true,
}, {});
@@ -79,7 +85,7 @@ const nodes = {
});
nodeBuilder.define(async (input, uiInput, from) => {
- let src = uiInput["imagePicker"].split("/");
+ let src = uiInput["cachePicker"].split("/");
src = src.splice(-2);
src = src.join("/");
@@ -87,8 +93,8 @@ const nodes = {
assets: {
[uiInput["state"]["id"]]: {
class: "asset",
- type: "image",
- data: src
+ type: "blob",
+ data: await window.cache.get(src)
}
},
content: {
@@ -150,7 +156,13 @@ const nodes = {
nodeBuilder.define(async (input, uiInput, from) => {
const canvas = {
- assets: {},
+ assets: {
+ "1": {
+ class: "asset",
+ type: "image",
+ data: "media/bird.png",
+ },
+ },
content: {
class: "clump",
nodeUUID: uiInput["tweaks"].nodeUUID,
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index 8a6c1d2b..fe15731b 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -2,9 +2,9 @@
import * as PIXI from "pixi.js";
import { Viewport } from "pixi-viewport";
import { onDestroy, onMount, tick } from "svelte";
- import { Writable } from "svelte/store";
+ import { type Writable } from "svelte/store";
import { renderApp } from "./render";
- import { BlinkCanvas, canvas1 } from "./clump";
+ import { type BlinkCanvas, canvas1 } from "./clump";
export let media: Writable;
export let send: (msg: string, data: any) => void;
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index fbd52551..cc438548 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -1,5 +1,5 @@
import * as PIXI from "pixi.js";
-import { getPixiFilter, type Atom, type Clump, BlinkCanvas, Asset } from "./clump";
+import { getPixiFilter, type Atom, type Clump, type BlinkCanvas, type Asset } from "./clump";
// let prevMedia = null; // TODO: Replace with DiffDial
diff --git a/blix-plugins/threlte-plugin/src/main.cjs b/blix-plugins/threlte-plugin/src/main.cjs
index 747cab72..16884ca2 100644
--- a/blix-plugins/threlte-plugin/src/main.cjs
+++ b/blix-plugins/threlte-plugin/src/main.cjs
@@ -109,16 +109,22 @@ const nodes = {
nodeBuilder.setDescription("Provides an image input and returns a single image output");
nodeBuilder.define(async (input, uiInput, from) => {
- return { "res": { src: uiInput["imagePicker"] } };
+ return { "res": { src: uiInput["cacheid"] } };
});
const ui = nodeBuilder.createUIBuilder();
- ui.addFilePicker({
- componentId: "imagePicker",
+ // ui.addFilePicker({
+ // componentId: "imagePicker",
+ // label: "Pick an image",
+ // defaultValue: "",
+ // triggerUpdate: true,
+ // }, {});
+ ui.addCachePicker({
+ componentId: "cacheid",
label: "Pick an image",
defaultValue: "",
triggerUpdate: true,
- }, {});
+ }, {})
nodeBuilder.setUI(ui);
diff --git a/package.json b/package.json
index e9c55434..5bac9c2f 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"start:electron:run": "electron .",
"start:electron:nodemon": "nodemon --verbose",
"start:electron:dev": "npm-run-all -s build:electron:dev start:electron:nodemon",
- "start:electron": "sleep 2 && npm-run-all -p build:electron:dev:watch start:electron:dev",
+ "start:electron": "sleep 3 && npm-run-all -p build:electron:dev:watch start:electron:dev",
"test": "jest --config jest.config.json",
"preplaywrite": "npm run build",
"playwright": "playwright test",
diff --git a/src/electron/lib/plugins/builders/NodeBuilder.ts b/src/electron/lib/plugins/builders/NodeBuilder.ts
index e78bb890..8d89b950 100644
--- a/src/electron/lib/plugins/builders/NodeBuilder.ts
+++ b/src/electron/lib/plugins/builders/NodeBuilder.ts
@@ -174,6 +174,19 @@ export class NodeUIBuilder {
return this;
}
+ public addCachePicker(config: UIComponentConfig, props: UIComponentProps): NodeUIBuilder {
+ const componentId = config.componentId ?? getRandomComponentId(NodeUIComponent.CachePicker);
+ this.node.params.push(
+ new NodeUILeaf(this.node, NodeUIComponent.CachePicker, componentId, [props])
+ );
+ this.uiConfigs[componentId] = {
+ componentId,
+ label: config.label,
+ defaultValue: config.defaultValue ?? 0,
+ triggerUpdate: config.triggerUpdate ?? true,
+ };
+ return this;
+ }
// This dial enables plugins to access the current node's UUID, as well as a list of uiInputs id's.
// This can then be used in coordination with the webview Tweaks API to modify node UI inputs.
diff --git a/src/frontend/lib/stores/CacheStore.ts b/src/frontend/lib/stores/CacheStore.ts
index 8dc5d45b..70cb09e3 100644
--- a/src/frontend/lib/stores/CacheStore.ts
+++ b/src/frontend/lib/stores/CacheStore.ts
@@ -37,6 +37,13 @@ class CacheStore {
};
}
+ // public refreshStore(cacheId: CacheUUID) {
+ // this.cacheStore.update((cache) => {
+ // cache.push(cacheId)
+ // return cache;
+ // });
+ // }
+
public get subscribe() {
return this.cacheStore.subscribe;
}
diff --git a/src/frontend/ui/tiles/WebCamera.svelte b/src/frontend/ui/tiles/WebCamera.svelte
index 941f0e52..cd8d3a90 100644
--- a/src/frontend/ui/tiles/WebCamera.svelte
+++ b/src/frontend/ui/tiles/WebCamera.svelte
@@ -1,10 +1,12 @@
From c56c0b0260f1c48e3841bef9a2055dccd0bd7fe5 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Fri, 8 Sep 2023 18:23:40 +0200
Subject: [PATCH 035/209] Fix active graph ID for AI
---
src/frontend/ui/base/palette/Palette.svelte | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/frontend/ui/base/palette/Palette.svelte b/src/frontend/ui/base/palette/Palette.svelte
index d320ce18..2d495d0c 100644
--- a/src/frontend/ui/base/palette/Palette.svelte
+++ b/src/frontend/ui/base/palette/Palette.svelte
@@ -4,8 +4,9 @@
import type { ICommand } from "../../../../shared/types/index";
import { onDestroy, onMount } from "svelte";
import Shortcuts from "../../utils/Shortcuts.svelte";
- import { graphMall } from "../../../lib/stores/GraphStore";
+ import { focusedGraphStore, graphMall } from "../../../lib/stores/GraphStore";
import { toastStore } from "lib/stores/ToastStore";
+ import { get } from "svelte/store";
let showPalette = false;
let expanded = true;
let inputElement: HTMLInputElement;
@@ -212,7 +213,7 @@
console.log(prompt);
try {
- const res = await window.apis.utilApi.sendPrompt(prompt, graphMall.getAllGraphUUIDs()[0]);
+ const res = await window.apis.utilApi.sendPrompt(prompt, get(focusedGraphStore).graphUUID);
toastStore.trigger({
message: res.message,
From 85c233210ed0aec065d6836cc1d872af575bd994 Mon Sep 17 00:00:00 2001
From: Jake Mileham
Date: Fri, 8 Sep 2023 18:45:54 +0200
Subject: [PATCH 036/209] Fix Ai undo/redo events and gravity node position
saving
---
src/electron/lib/ai/AiLang.ts | 7 ++++++-
.../lib/core-graph/CoreGraphManager.ts | 19 ++++++++++++-------
src/frontend/lib/stores/GraphStore.ts | 12 ++++++++----
3 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/src/electron/lib/ai/AiLang.ts b/src/electron/lib/ai/AiLang.ts
index 5681e919..6e2103dd 100644
--- a/src/electron/lib/ai/AiLang.ts
+++ b/src/electron/lib/ai/AiLang.ts
@@ -719,7 +719,12 @@ export class BlypescriptInterpreter {
newNodeUiInputs,
CoreGraphUpdateParticipant.ai
);
- // graph.updateUIInputs(node.uuid, newNodeUiInputs);
+ for (const input of Object.keys(newNodeUiInputs.inputs)) {
+ this.graphManager.handleNodeInputInteraction(graph.uuid, node.uuid, {
+ id: input,
+ value: newNodeUiInputs.inputs[input],
+ });
+ }
return { success: true, data: null };
}
diff --git a/src/electron/lib/core-graph/CoreGraphManager.ts b/src/electron/lib/core-graph/CoreGraphManager.ts
index 12111560..df3654c6 100644
--- a/src/electron/lib/core-graph/CoreGraphManager.ts
+++ b/src/electron/lib/core-graph/CoreGraphManager.ts
@@ -210,13 +210,15 @@ export class CoreGraphManager {
});
old.changes = [input.id];
}
- // Add Event
- this._events[graphUUID].addEvent({
- element: "UiInput",
- operation: "Change",
- execute: { graphUUID, nodeUUId: nodeUUID, nodeUIInputs },
- revert: { graphUUID, nodeUUId: nodeUUID, nodeUIInputs: old },
- });
+
+ // Add Event only if new value is not the same as old value
+ if (old.inputs[input.id] !== input.value)
+ this._events[graphUUID].addEvent({
+ element: "UiInput",
+ operation: "Change",
+ execute: { graphUUID, nodeUUId: nodeUUID, nodeUIInputs },
+ revert: { graphUUID, nodeUUId: nodeUUID, nodeUIInputs: old },
+ });
// console.log("OLD: ", old);
// console.log("NEW: ", nodeUIInputs);
@@ -273,6 +275,9 @@ export class CoreGraphManager {
updateUIPositions(graphUUID: UUID, positions: { [key: UUID]: SvelvetCanvasPos }) {
if (this._graphs[graphUUID]) {
this._graphs[graphUUID].UIPositions = positions;
+ for (const node of Object.keys(positions)) {
+ this._graphs[graphUUID].setNodePos(node, positions[node]);
+ }
}
}
diff --git a/src/frontend/lib/stores/GraphStore.ts b/src/frontend/lib/stores/GraphStore.ts
index 04d9d179..508ae223 100644
--- a/src/frontend/lib/stores/GraphStore.ts
+++ b/src/frontend/lib/stores/GraphStore.ts
@@ -238,7 +238,12 @@ export class GraphStore {
// await window.apis.graphApi.updateUIPosition(get(this.graphStore).uuid, nodeUUID, get(get(this.graphStore).uiPositions[nodeUUID]));
}
+ /**
+ * BE CAREFUL OF THE UNAWAITED PROMISE
+ * AWAIT REMOVED FOR UP DAY AS A TEMP FIX
+ */
async updateUIPositions() {
+ // eslint-disable-next-line
await window.apis.graphApi.updateUIPositions(
get(this.graphStore).uuid,
get(this.graphStore).uiPositions
@@ -312,8 +317,6 @@ export class GraphStore {
const nodePos = this.getNode(node)?.styling?.pos;
if (!nodePos) return;
nodePos.update((pos) => {
- // console.log("New X: ", pos.x + NUDGE_DISTANCE * 2 * (Math.random() - 0.5))
- // console.log("New Y: ", pos.y + NUDGE_DISTANCE * 2 * (Math.random() - 0.5))
return {
x: pos.x + NUDGE_DISTANCE * 2 * (Math.random() - 0.5),
y: pos.y + NUDGE_DISTANCE * 2 * (Math.random() - 0.5),
@@ -322,7 +325,8 @@ export class GraphStore {
});
// Notify the system of the new node positions
- const registerPositionUpdate = () => {
+ const registerPositionUpdate = async () => {
+ await this.updateUIPositions();
return; // TODO
};
@@ -330,7 +334,7 @@ export class GraphStore {
const now = performance.now();
if (now - start > duration * 1000) {
// Animation is done
- registerPositionUpdate();
+ void registerPositionUpdate().then().catch();
return;
}
From 4132ce4d9cdca07912d2fb572bb0b258bb339cf4 Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Fri, 8 Sep 2023 19:38:19 +0200
Subject: [PATCH 037/209] Update glfx and pixi to work with camera
---
blix-plugins/blink/src/main.cjs | 10 +++----
blix-plugins/blink/webview/clump.ts | 11 ++++++--
blix-plugins/glfx-plugin/webview/App.svelte | 8 +++---
blix-plugins/threlte-plugin/src/main.cjs | 27 +++++++++++++++++++
src/frontend/lib/stores/CacheStore.ts | 12 ++++-----
src/frontend/ui/tiles/WebCamera.svelte | 10 +++----
.../nodeUICcomponents/CachePicker.svelte | 2 ++
7 files changed, 58 insertions(+), 22 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index ad34504d..dd307f9d 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -85,16 +85,16 @@ const nodes = {
});
nodeBuilder.define(async (input, uiInput, from) => {
- let src = uiInput["cachePicker"].split("/");
- src = src.splice(-2);
- src = src.join("/");
+ // let src = uiInput["cachePicker"].split("/");
+ // src = src.splice(-2);
+ // src = src.join("/");
const canvas = {
assets: {
[uiInput["state"]["id"]]: {
class: "asset",
- type: "blob",
- data: await window.cache.get(src)
+ type: "image",
+ data: uiInput["cachePicker"],
}
},
content: {
diff --git a/blix-plugins/blink/webview/clump.ts b/blix-plugins/blink/webview/clump.ts
index 8381a3db..1ddedc2a 100644
--- a/blix-plugins/blink/webview/clump.ts
+++ b/blix-plugins/blink/webview/clump.ts
@@ -13,6 +13,7 @@ import {
ZoomBlurFilter,
TwistFilter
} from "pixi-filters"
+import { type } from "os";
export type BlinkCanvas = {
assets: { [key: string]: Asset };
@@ -21,7 +22,7 @@ export type BlinkCanvas = {
export type Asset = {
class: "asset";
- type: "image" | "text";
+ type: "image" | "text" | "blob";
data: any;
};
@@ -66,11 +67,17 @@ export function getPixiFilter(filter: Filter) {
}
// A single indivisible unit of a clump (E.g. image, shape, text etc.)
-export type Atom = { class: "atom" } & (ImageAtom | ShapeAtom | TextAtom | PaintAtom);
+export type Atom = { class: "atom" } & (ImageAtom | ShapeAtom | TextAtom | PaintAtom | BlobAtom);
type ImageAtom = {
type: "image";
assetId: string;
};
+
+type BlobAtom = {
+ type: "blob";
+ assetId: string;
+}
+
type ShapeAtom = {
type: "shape";
shape: "rectangle" | "ellipse" | "triangle";
diff --git a/blix-plugins/glfx-plugin/webview/App.svelte b/blix-plugins/glfx-plugin/webview/App.svelte
index bbc9f345..88cdf00b 100644
--- a/blix-plugins/glfx-plugin/webview/App.svelte
+++ b/blix-plugins/glfx-plugin/webview/App.svelte
@@ -89,10 +89,10 @@
bind:clientWidth="{canvasWidth}"
/>
-
- Rendering at: {canvasWidth} x {canvasHeight}
- Media: {JSON.stringify($media)}
-
+
+
+
+
From 9db968ec755b3d2bac46fedec775c597f8b852d6 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Fri, 8 Sep 2023 22:03:07 +0200
Subject: [PATCH 038/209] Fix linux build
---
src/frontend/ui/base/palette/Palette.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/frontend/ui/base/palette/Palette.svelte b/src/frontend/ui/base/palette/Palette.svelte
index 2d495d0c..82c98ae8 100644
--- a/src/frontend/ui/base/palette/Palette.svelte
+++ b/src/frontend/ui/base/palette/Palette.svelte
@@ -5,7 +5,7 @@
import { onDestroy, onMount } from "svelte";
import Shortcuts from "../../utils/Shortcuts.svelte";
import { focusedGraphStore, graphMall } from "../../../lib/stores/GraphStore";
- import { toastStore } from "lib/stores/ToastStore";
+ import { toastStore } from "../../../lib/stores/ToastStore";
import { get } from "svelte/store";
let showPalette = false;
let expanded = true;
From 41045f58e1d166eb4dbcb8353b7e02303e53ef4c Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Fri, 8 Sep 2023 22:30:34 +0200
Subject: [PATCH 039/209] Add checks to parser to validate numeric UI inputs
---
src/electron/lib/ai/AiLang.ts | 47 ++++++++++++++++++---
src/electron/lib/ai/AiManagerv2.ts | 6 +--
src/electron/lib/ai/prompt.ts | 4 +-
src/frontend/ui/base/ProjectBar.svelte | 2 +-
src/frontend/ui/base/palette/Palette.svelte | 6 +--
5 files changed, 49 insertions(+), 16 deletions(-)
diff --git a/src/electron/lib/ai/AiLang.ts b/src/electron/lib/ai/AiLang.ts
index 6e2103dd..a704450b 100644
--- a/src/electron/lib/ai/AiLang.ts
+++ b/src/electron/lib/ai/AiLang.ts
@@ -179,13 +179,17 @@ export class BlypescriptProgram implements AiLangProgram {
);
}
- let result = this.repairNestedFunctionCalls(statement, node);
+ const repairFunctions = [
+ this.repairNestedFunctionCalls,
+ this.repairPrimitiveTypes,
+ this.repairUiInputTypes,
+ ];
- if (!result.success) return result;
-
- result = this.repairPrimitiveTypes(statement, node, toolbox);
+ for (const repairFunction of repairFunctions) {
+ const result = repairFunction(statement, node, toolbox);
- if (!result.success) return result;
+ if (!result.success) return result;
+ }
}
return {
@@ -377,6 +381,34 @@ export class BlypescriptProgram implements AiLangProgram {
} satisfies Result;
}
+ private repairUiInputTypes(statement: BlypescriptStatement, node: BlypescriptNode) {
+ const statementUiInputs = statement.nodeInputs.slice(node.nodeInputs.length);
+
+ for (let i = 0; i < statementUiInputs.length; i++) {
+ const statementUiInput = statementUiInputs[i];
+ const nodeUiInput = node.uiInputs[i];
+
+ if (nodeUiInput.types.length === 1 && nodeUiInput.types[0].toLowerCase() === "number") {
+ if (statementUiInput === "null") {
+ statement.nodeInputs[node.nodeInputs.length + i] = "0";
+ } else if (isNaN(Number(statementUiInput))) {
+ return {
+ success: false,
+ error: "ui_input_type_error",
+ message: `Type error: '${statementUiInput}' is not of type \`number\` on the following line: ${statement.toString()}`,
+ } satisfies Result;
+ }
+ }
+
+ // TODO: Add other type checks here
+ }
+
+ return {
+ success: true,
+ data: true as const,
+ } satisfies Result;
+ }
+
private generateUniqueVarName(nodeSignature?: string) {
const varPrefix = nodeSignature ? nodeSignature.split(".").slice(1).join(".") : "var";
let count = 1;
@@ -407,7 +439,7 @@ function error(error: string, message: string, data?: T) {
* const num1 = input-plugin.inputNumber(69);
* const binary1 = math-plugin.input(num1['res'], num1['res'], 'multiply');
*/
-const BLYPESCRIPT_STATEMENT_TEMPLATE = "const {{name}} = {{signature}}.({{params}});";
+const BLYPESCRIPT_STATEMENT_TEMPLATE = "const {{name}} = {{signature}}({{params}});";
const BLYPESCRIPT_STATEMENT_REGEX =
/\s*const \s*(\w+)\s*=\s*([\w-]+)\s*\.\s*([\w-]+)\s*\((.*)\)\s*;/;
const BLYPESCRIPT_FUNCTION_CALL_REGEX = /^([\w-]+)\s*\.\s*([\w-]+)\s*\((.*)\).*$/;
@@ -446,7 +478,7 @@ export class BlypescriptStatement extends AiLangStatement {
return fillTemplate(BLYPESCRIPT_STATEMENT_TEMPLATE, {
name: this.name,
signature: this.nodeSignature,
- params: this.nodeInputs.join(", "),
+ params: this.nodeInputs.join(", ") || "",
});
}
}
@@ -719,6 +751,7 @@ export class BlypescriptInterpreter {
newNodeUiInputs,
CoreGraphUpdateParticipant.ai
);
+
for (const input of Object.keys(newNodeUiInputs.inputs)) {
this.graphManager.handleNodeInputInteraction(graph.uuid, node.uuid, {
id: input,
diff --git a/src/electron/lib/ai/AiManagerv2.ts b/src/electron/lib/ai/AiManagerv2.ts
index 5ac9f45c..b1d7037e 100644
--- a/src/electron/lib/ai/AiManagerv2.ts
+++ b/src/electron/lib/ai/AiManagerv2.ts
@@ -39,7 +39,7 @@ type PromptOptions = {
verbose?: boolean;
};
-export const genericErrorResponse = "Oops, that wasn't supposed to happen🫠. Try again.";
+export const genericErrorResponse = "Oops, that wasn't supposed to happen😅";
export class AiManager {
private readonly graphExporter: CoreGraphExporter;
@@ -123,7 +123,7 @@ export class AiManager {
);
if (!result.success) {
- chat.addMessage({ role: "blix", content: `USER'S RESPONSE: ${result.message}` });
+ chat.addMessage({ role: "blix", content: `Error: ${result.message}` });
continue; // retry if failure
}
@@ -140,7 +140,7 @@ export class AiManager {
if (!result.success) {
logger.warn(result.error);
- chat.addMessage({ role: "blix", content: `USER'S RESPONSE:\n${result.message}` });
+ chat.addMessage({ role: "blix", content: `Error: ${result.message}` });
continue; // retry if failure
}
diff --git a/src/electron/lib/ai/prompt.ts b/src/electron/lib/ai/prompt.ts
index a6d90fe8..21d4d4d3 100644
--- a/src/electron/lib/ai/prompt.ts
+++ b/src/electron/lib/ai/prompt.ts
@@ -31,8 +31,8 @@ User: Add some nodes and connect them
Assistant:
\`\`\`typescript
graph {
- const inputNumber1 = input-plugin.inputNumber.(5);
- const inputNumber2 = input-plugin.inputNumber.(10);
+ const inputNumber1 = input-plugin.inputNumber(5);
+ const inputNumber2 = input-plugin.inputNumber(10);
const binary1 = math-plugin.binary.(inputNumber1['res'], inputNumber2['res'], 'add');
const output1 = blix.output.(binary1['res'], 'output1');
}
diff --git a/src/frontend/ui/base/ProjectBar.svelte b/src/frontend/ui/base/ProjectBar.svelte
index 9901debc..dcc02663 100644
--- a/src/frontend/ui/base/ProjectBar.svelte
+++ b/src/frontend/ui/base/ProjectBar.svelte
@@ -46,7 +46,7 @@
{#if !project.saved}
{/if}
diff --git a/src/frontend/ui/base/palette/Palette.svelte b/src/frontend/ui/base/palette/Palette.svelte
index 82c98ae8..ddaef874 100644
--- a/src/frontend/ui/base/palette/Palette.svelte
+++ b/src/frontend/ui/base/palette/Palette.svelte
@@ -195,7 +195,7 @@
closePalette();
const messages = [
- "🔥 Cooking...",
+ "👨🏼🍳 Cooking...",
"🪄 Stirring the creative cauldron...",
"🐉 Slaying the ender dragon...",
];
@@ -211,7 +211,6 @@
promptHistory.unshift(prompt);
await window.apis.utilApi.saveState("prompts", promptHistory);
- console.log(prompt);
try {
const res = await window.apis.utilApi.sendPrompt(prompt, get(focusedGraphStore).graphUUID);
@@ -318,8 +317,9 @@
Ask AI to assist with a task
- "What is the result of 16 subtract 42, squared?"
"I want to edit the brightness, hue and noise of an image."
+ "What is the result of 16 subtract 42, squared?"
+ "I want my image to sparkly like summer's day"
"Make my image more medieval"
From 90201afb81f72aef1777f8c9d520be129349ccdb Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Fri, 8 Sep 2023 23:18:00 +0200
Subject: [PATCH 040/209] Remove sharp plugin and save AI chats
---
blix-plugins/glfx-plugin/webview/App.svelte | 4 +-
blix-plugins/sharp-plugin/package.json | 18 --
blix-plugins/sharp-plugin/src/main.js | 239 ------------------
package-lock.json | 240 +------------------
package.json | 1 -
src/electron/lib/Blix.ts | 18 +-
src/electron/lib/ai/AiLang.ts | 13 +-
src/electron/lib/ai/AiManagerv2.ts | 52 +++-
src/electron/lib/projects/ProjectCommands.ts | 99 ++++----
src/frontend/ui/base/palette/Palette.svelte | 2 +-
src/frontend/ui/tiles/WebView.svelte | 4 +-
11 files changed, 120 insertions(+), 570 deletions(-)
delete mode 100644 blix-plugins/sharp-plugin/package.json
delete mode 100644 blix-plugins/sharp-plugin/src/main.js
diff --git a/blix-plugins/glfx-plugin/webview/App.svelte b/blix-plugins/glfx-plugin/webview/App.svelte
index bbc9f345..10f94c6d 100644
--- a/blix-plugins/glfx-plugin/webview/App.svelte
+++ b/blix-plugins/glfx-plugin/webview/App.svelte
@@ -89,10 +89,10 @@
bind:clientWidth="{canvasWidth}"
/>
-
+
diff --git a/src/shared/types/dials.ts b/src/shared/types/dials.ts
index 0fcf7d63..172b8f6a 100644
--- a/src/shared/types/dials.ts
+++ b/src/shared/types/dials.ts
@@ -5,5 +5,5 @@ export type NodeTweakData = {
export type NodeDiffData = {
uiInputs: string[];
- anchors: { anchorId: string; change: "connection" | "value" }[];
+ anchors: { [key: string]: "connection" | "value" }; // anchorId -> changeType
};
From 049899ce38a9f008e1df642d44a55b3d4b631ab5 Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Tue, 12 Sep 2023 17:39:34 +0200
Subject: [PATCH 045/209] Add edit nodes to blink
---
blix-plugins/blink/src/main.cjs | 133 ++++++++++++++++++++++++++++
blix-plugins/blink/webview/clump.ts | 26 +++++-
2 files changed, 157 insertions(+), 2 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index dd307f9d..26c6f3cb 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -4,6 +4,17 @@ function getUUID() {
return crypto.randomBytes(16).toString("base64url");
}
+function chooseInput(input, uiInput, inputKey) {
+ if (input[inputKey] ?? false) {
+ return input[inputKey];
+ }
+ return uiInput[inputKey];
+}
+
+function toTitleCase(str) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+
function addTransformInput(ui) {
for (let numInp of ["position X", "position Y", "rotation", "scale X", "scale Y"]) {
ui.addNumberInput(
@@ -52,8 +63,130 @@ function addTweakability(ui) {
);
}
+function createBlinkNode(type, title, desc, params) {
+ return (context) => {
+ const nodeBuilder = context.instantiate(context.pluginId, type);
+ nodeBuilder.setTitle(title);
+ nodeBuilder.setDescription(desc);
+
+ const ui = nodeBuilder.createUIBuilder();
+ for (let param of params) {
+ if(param.id.includes("color") || param.id.includes("Color")) {
+ ui.addColorPicker({
+ componentId: param.id,
+ label: "Multitudinous seas incarnadine",
+ defaultValue: 0,
+ triggerUpdate: true,
+ }, {})
+
+ }
+ else{
+ ui.addSlider(
+ {
+ componentId: param.id,
+ label: toTitleCase(param.id),
+ defaultValue: 0,
+ triggerUpdate: true,
+ },
+ { min: param.min ?? -1, max: param.max ?? 1, step: param.step ?? 0.05 }
+ );
+ }
+ }
+ nodeBuilder.define(async (input, uiInput, from) => {
+
+ const canvas = input["clump"];
+
+ if (!canvas.content.filters) canvas.content.filters = [];
+ canvas.content.filters.push({
+ class: "filter",
+ type: type,
+ params: params.map((param) => chooseInput(input, uiInput, param.id)),
+ });
+
+ return { "res": canvas };
+ });
+
+ nodeBuilder.setUI(ui);
+ nodeBuilder.addInput("Blink clump", "clump", "Clump");
+ // for (let param of params) {
+ // nodeBuilder.addInput("number", type, toTitleCase(param.id));
+ // }
+ nodeBuilder.addOutput("Blink clump", "res", "Result");
+ };
+}
+
+const blinkNodes = {
+ "blur": [
+ "Blur",
+ "Applies a blur to the image",
+ [{ id: "blur", min: 0, max: 100, step: 0.1 }, { id: "quality", min: 1, max: 10, step: 0.01 }]
+ ],
+ "noise": [
+ "Noise",
+ "Applies a noise filter to the image",
+ [{ id: "noise", min: 0, max: 1, step: 0.01 }, { id: "seed", min: 0.01, max: 0.99, step: 0.01 }]
+ ],
+ "bloom": [
+ "Bloom",
+ "Applies a Guassian blur to the image",
+ [{ id: "strength", min: 0, max: 20, step: 0.1 }]
+ ],
+ "grayscale": [
+ "Gray Scale",
+ "Applies a grayscale filter to the image",
+ []
+ ],
+ "bevel": [
+ "Bevel",
+ "Bevel Filter",
+ [
+ { id: "rotation", min: 0, max: 360, step: 1.0 },
+ { id: "thickness", min: 0, max: 10, step: 0.01 },
+ { id: "lightColor", min: 0, max: 360, step: 1.0 },
+ { id: "lightAlpha", min: 0, max: 1, step: 0.01 },
+ { id: "shadowColor", min: 0, max: 360, step: 1.0 },
+ { id: "shadowAlpha", min: 0, max: 1, step: 0.01 },
+ ]
+ ],
+ "outline": [
+ "Outline",
+ "Applies an outline filter to the image",
+ [
+ { id: "thickness", min: 0, max: 10, step: 0.1 },
+ { id: "color", min: 0, max: 5, step: 0.05 },
+ { id: "alpha", min: 0, max: 1, step: 0.01 },
+ ]
+ ],
+ "dot": [
+ "Dot",
+ "This filter applies a dotscreen effect making display objects appear to be made out of halftone dots like an old printer",
+ [{ id: "scale", min: 0.3, max: 1, step: 0.01 }, { id: "angle", min: 0, max: 5, step: 0.01 }]
+ ],
+ "crt": [
+ "CRT",
+ "Applies a CRT effect to the image",
+ [
+ { id: "curvature", min: 0, max: 10, step: 0.01 },
+ { id: "lineWidth", min: 0, max: 5, step: 0.01 },
+ { id: "lineContrast", min: 0, max: 1, step: 0.01 },
+ { id: "noise", min: 0, max: 1, step: 0.01 },
+ { id: "noiseSize", min: 1, max: 10, step: 0.01 },
+ { id: "vignetting", min: 0, max: 1, step: 0.01 },
+ { id: "vignettingAlpha", min: 0, max: 1, step: 0.01 },
+ { id: "vignettingBlur", min: 0, max: 1, step: 0.01 },
+ { id: "seed", min: 0, max: 1, step: 0.01 },
+ ]
+ ],
+};
+
+Object.keys(blinkNodes).forEach((key) => {
+ blinkNodes[key] = createBlinkNode(key, ...blinkNodes[key]);
+});
+
//========== NODES ==========//
const nodes = {
+ ...blinkNodes,
+
"inputImage": (context) => {
const nodeBuilder = context.instantiate(context.pluginId, "inputImage");
nodeBuilder.setTitle("Blink Image");
diff --git a/blix-plugins/blink/webview/clump.ts b/blix-plugins/blink/webview/clump.ts
index 1ddedc2a..0a47ea30 100644
--- a/blix-plugins/blink/webview/clump.ts
+++ b/blix-plugins/blink/webview/clump.ts
@@ -1,6 +1,7 @@
import * as PIXI from "pixi.js";
import { Matrix } from "pixi.js";
import {
+ KawaseBlurFilter,
BloomFilter,
GrayscaleFilter,
BevelFilter,
@@ -54,10 +55,31 @@ export function getPixiFilter(filter: Filter) {
case "noise": return new PIXI.NoiseFilter(...filter.params);
case "bloom": return new BloomFilter(...filter.params);
case "grayscale": return new GrayscaleFilter();
- case "bevel": return new BevelFilter(...filter.params);
+ case "bevel": return new BevelFilter(
+ {
+ rotation: filter.params[0],
+ thickness: filter.params[1],
+ lightColor: filter.params[2],
+ lightAlpha: filter.params[3],
+ shadowColor: filter.params[4],
+ shadowAlpha: filter.params[5],
+ }
+ );
case "outline": return new OutlineFilter(...filter.params);
case "dot": return new DotFilter(...filter.params);
- case "crt": return new CRTFilter(...filter.params);
+ case "crt": return new CRTFilter(
+ {
+ curvature: filter.params[0],
+ lineWidth: filter.params[1],
+ lineContrast: filter.params[2],
+ noise: filter.params[3],
+ noiseSize: filter.params[4],
+ vignetting: filter.params[5],
+ vignettingAlpha: filter.params[6],
+ vignettingBlur: filter.params[7],
+ seed: filter.params[8],
+ }
+ );
case "emboss": return new EmbossFilter(...filter.params);
case "bulge": return new BulgePinchFilter(...filter.params);
case "glitch": return new GlitchFilter(...filter.params);
From 5cfadf660b7f6ce0a8ab9c23bc34e0c0d87e7b54 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Wed, 13 Sep 2023 20:50:33 +0200
Subject: [PATCH 046/209] Add new ColorPicker
---
blix-plugins/hello-plugin/src/main.ts | 2 +-
blix-plugins/input-plugin/src/main.js | 2 +-
package-lock.json | 17 ++
package.json | 2 +
.../lib/plugins/builders/NodeBuilder.ts | 4 +-
src/frontend/ui/utils/graph/PluginNode.svelte | 40 ++--
.../nodeUICcomponents/ColorPicker.svelte | 61 +++++-
.../ColorPicker/ColorPickerTextInput.svelte | 192 ++++++++++++++++++
.../ColorPicker/ColorPickerWrapper.svelte | 47 +++++
.../utils/mediaDisplays/ColorDisplay.svelte | 71 ++++---
.../lib/plugins/builder/NodeBuilder.spec.ts | 2 +-
11 files changed, 380 insertions(+), 60 deletions(-)
create mode 100644 src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerTextInput.svelte
create mode 100644 src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerWrapper.svelte
diff --git a/blix-plugins/hello-plugin/src/main.ts b/blix-plugins/hello-plugin/src/main.ts
index aaa0668b..6b654b8e 100644
--- a/blix-plugins/hello-plugin/src/main.ts
+++ b/blix-plugins/hello-plugin/src/main.ts
@@ -61,7 +61,7 @@ const nodes = {
.addColorPicker({
componentId: "incarnadine",
label: "Multitudinous seas incarnadine",
- defaultValue: 0,
+ defaultValue: "#ff000088",
triggerUpdate: true,
}, {})
diff --git a/blix-plugins/input-plugin/src/main.js b/blix-plugins/input-plugin/src/main.js
index 9f10a0f7..3f1dcb72 100644
--- a/blix-plugins/input-plugin/src/main.js
+++ b/blix-plugins/input-plugin/src/main.js
@@ -58,7 +58,7 @@ const nodes = {
ui.addColorPicker({
componentId: "colorPicker",
label: "Pick a color",
- defaultValue: "red",
+ defaultValue: "#ff0000ff",
triggerUpdate: true,
}, {})
nodeBuilder.setUI(ui);
diff --git a/package-lock.json b/package-lock.json
index 4c0e2c4f..ac0ac8e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -62,6 +62,7 @@
"babel-jest": "^29.5.0",
"chalk": "^4.1.2",
"clean-css": "^5.3.2",
+ "colord": "^2.9.3",
"commander": "^11.0.0",
"cross-env": "^7.0.3",
"electron": "^24.2.0",
@@ -88,6 +89,7 @@
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^2.0.2",
"rollup-plugin-svelte": "^7.1.4",
+ "svelte-awesome-color-picker": "^2.4.7",
"svelte-check": "^3.3.1",
"svelte-fa": "^3.0.4",
"svelte-jester": "^2.3.2",
@@ -6309,6 +6311,12 @@
"color-support": "bin.js"
}
},
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+ "dev": true
+ },
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
@@ -15465,6 +15473,15 @@
"node": ">= 8"
}
},
+ "node_modules/svelte-awesome-color-picker": {
+ "version": "2.4.7",
+ "resolved": "https://registry.npmjs.org/svelte-awesome-color-picker/-/svelte-awesome-color-picker-2.4.7.tgz",
+ "integrity": "sha512-LW9n16pR3V9vxFXdq0rmtBYJXv57zBZmLV/KNPtFqTo0kYwu7NsQhZZxFv6qoB/5PUXyPMfe3VCllyauFEY3SQ==",
+ "dev": true,
+ "dependencies": {
+ "colord": "^2.9.3"
+ }
+ },
"node_modules/svelte-check": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.4.4.tgz",
diff --git a/package.json b/package.json
index 5bac9c2f..7e620111 100644
--- a/package.json
+++ b/package.json
@@ -122,6 +122,7 @@
"babel-jest": "^29.5.0",
"chalk": "^4.1.2",
"clean-css": "^5.3.2",
+ "colord": "^2.9.3",
"commander": "^11.0.0",
"cross-env": "^7.0.3",
"electron": "^24.2.0",
@@ -148,6 +149,7 @@
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^2.0.2",
"rollup-plugin-svelte": "^7.1.4",
+ "svelte-awesome-color-picker": "^2.4.7",
"svelte-check": "^3.3.1",
"svelte-fa": "^3.0.4",
"svelte-jester": "^2.3.2",
diff --git a/src/electron/lib/plugins/builders/NodeBuilder.ts b/src/electron/lib/plugins/builders/NodeBuilder.ts
index 2527e602..c8c11741 100644
--- a/src/electron/lib/plugins/builders/NodeBuilder.ts
+++ b/src/electron/lib/plugins/builders/NodeBuilder.ts
@@ -182,7 +182,7 @@ export class NodeUIBuilder {
this.uiConfigs[componentId] = {
componentId,
label: config.label,
- defaultValue: config.defaultValue ?? 0,
+ defaultValue: config.defaultValue ?? "",
triggerUpdate: config.triggerUpdate ?? true,
};
return this;
@@ -396,7 +396,7 @@ export class NodeUIBuilder {
this.uiConfigs[componentId] = {
componentId,
label: config.label,
- defaultValue: config.defaultValue ?? "#000000",
+ defaultValue: config.defaultValue ?? "#000000ff",
triggerUpdate: config.triggerUpdate ?? true,
};
return this;
diff --git a/src/frontend/ui/utils/graph/PluginNode.svelte b/src/frontend/ui/utils/graph/PluginNode.svelte
index ef1f877d..8a7a99fd 100644
--- a/src/frontend/ui/utils/graph/PluginNode.svelte
+++ b/src/frontend/ui/utils/graph/PluginNode.svelte
@@ -5,6 +5,7 @@
import NodeUiFragment from "./NodeUIFragment.svelte";
import { createEventDispatcher } from "svelte";
import { graphMall } from "lib/stores/GraphStore";
+ import { colord } from "colord";
const dispatch = createEventDispatcher();
@@ -44,28 +45,29 @@
return colour as CSSColorString;
}
- function changeBrightness(color: CSSColorString, percent: number): CSSColorString {
- var R = parseInt(color.substring(1, 3), 16);
- var G = parseInt(color.substring(3, 5), 16);
- var B = parseInt(color.substring(5, 7), 16);
+ function changeBrightness(color: CSSColorString, percent: number) {
+ return colord(color).lighten(percent).toRgbString();
+ // var R = parseInt(color.substring(1, 3), 16);
+ // var G = parseInt(color.substring(3, 5), 16);
+ // var B = parseInt(color.substring(5, 7), 16);
- R = parseInt(`${(R * (100 + percent)) / 100}`);
- G = parseInt(`${(G * (100 + percent)) / 100}`);
- B = parseInt(`${(B * (100 + percent)) / 100}`);
+ // R = parseInt(`${(R * (100 + percent)) / 100}`);
+ // G = parseInt(`${(G * (100 + percent)) / 100}`);
+ // B = parseInt(`${(B * (100 + percent)) / 100}`);
- R = R < 255 ? R : 255;
- G = G < 255 ? G : 255;
- B = B < 255 ? B : 255;
+ // R = R < 255 ? R : 255;
+ // G = G < 255 ? G : 255;
+ // B = B < 255 ? B : 255;
- R = Math.round(R);
- G = Math.round(G);
- B = Math.round(B);
+ // R = Math.round(R);
+ // G = Math.round(G);
+ // B = Math.round(B);
- var RR = R.toString(16).length == 1 ? "0" + R.toString(16) : R.toString(16);
- var GG = G.toString(16).length == 1 ? "0" + G.toString(16) : G.toString(16);
- var BB = B.toString(16).length == 1 ? "0" + B.toString(16) : B.toString(16);
+ // var RR = R.toString(16).length == 1 ? "0" + R.toString(16) : R.toString(16);
+ // var GG = G.toString(16).length == 1 ? "0" + G.toString(16) : G.toString(16);
+ // var BB = B.toString(16).length == 1 ? "0" + B.toString(16) : B.toString(16);
- return ("#" + RR + GG + BB) as CSSColorString;
+ // return ("#" + RR + GG + BB) as CSSColorString;
}
async function nodeClicked(e: CustomEvent) {
@@ -144,7 +146,7 @@ height="{graphNode.dims.h}" -->
{#if input.displayName}
{input.displayName}
{/if}
- <{input.type || "any"} {input.type || "any"}>
{/if}
@@ -180,7 +182,7 @@ height="{graphNode.dims.h}" -->
{output.displayName}
{/if}
- <{output.type || "any"} >
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
index b6202f72..6a337e6b 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
@@ -1,25 +1,76 @@
-
+
+
+
+ {#if typeof $valStore === "string"}
+
+ {:else}
+ ERR: Invaid colour: {JSON.stringify($valStore)}
+ {/if}
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerTextInput.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerTextInput.svelte
new file mode 100644
index 00000000..41832451
--- /dev/null
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerTextInput.svelte
@@ -0,0 +1,192 @@
+
+
+
+
+
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerWrapper.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerWrapper.svelte
new file mode 100644
index 00000000..f74e2cb8
--- /dev/null
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker/ColorPickerWrapper.svelte
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/frontend/ui/utils/mediaDisplays/ColorDisplay.svelte b/src/frontend/ui/utils/mediaDisplays/ColorDisplay.svelte
index 17519344..09502e6a 100644
--- a/src/frontend/ui/utils/mediaDisplays/ColorDisplay.svelte
+++ b/src/frontend/ui/utils/mediaDisplays/ColorDisplay.svelte
@@ -1,32 +1,34 @@
-
diff --git a/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts b/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts
index 68bc76bd..fa012704 100644
--- a/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts
+++ b/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts
@@ -157,7 +157,7 @@ describe("Test NodeUIBuilder", () => {
const uiComponentConfig : UIComponentConfig = {
label: "slider",
componentId: "shrek",
- defaultValue: 50,
+ defaultValue: "#beef69",
triggerUpdate: true
}
nodeUIBuilder.addColorPicker(uiComponentConfig,{set : "ayo"});
From b5fb82f744105c344ac5943e50ca4fca7575a4ab Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Fri, 15 Sep 2023 18:09:37 +0200
Subject: [PATCH 047/209] Add Blink diffing system; Working 1 layer deep
---
blix-plugins/blink/src/main.cjs | 68 +--
blix-plugins/blink/webview/App.svelte | 5 +-
blix-plugins/blink/webview/app.ts | 2 +-
blix-plugins/blink/webview/atom.ts | 75 ++++
blix-plugins/blink/webview/diff.ts | 134 ++++++
blix-plugins/blink/webview/filter.ts | 31 ++
blix-plugins/blink/webview/render.ts | 391 ++++++++++--------
.../blink/webview/{clump.ts => types.ts} | 12 +-
src/frontend/ui/base/palette/Palette.svelte | 8 +
src/frontend/ui/tiles/Assets.svelte | 9 +
src/frontend/ui/utils/graph/PluginNode.svelte | 46 +--
src/index.ts | 9 +
12 files changed, 560 insertions(+), 230 deletions(-)
create mode 100644 blix-plugins/blink/webview/atom.ts
create mode 100644 blix-plugins/blink/webview/diff.ts
create mode 100644 blix-plugins/blink/webview/filter.ts
rename blix-plugins/blink/webview/{clump.ts => types.ts} (96%)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index de4e43a5..558f64ec 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -4,19 +4,29 @@ function getUUID() {
return crypto.randomBytes(16).toString("base64url");
}
+function colorHexToNumber(str) {
+ return parseInt(str.slice(0, 7).replace("#", "0x"));
+}
+
function chooseInput(input, uiInput, inputKey) {
- if (input[inputKey] ?? false) {
+ if (input[inputKey] != null) {
return input[inputKey];
}
- return uiInput[inputKey];
+ return inputKey.includes("color") ? colorHexToNumber(uiInput[inputKey]) : uiInput[inputKey];
}
function toTitleCase(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
-function addTransformInput(ui) {
- for (let numInp of ["position X", "position Y", "rotation", "scale X", "scale Y"]) {
+function addTransformInput(ui, props = ["position", "rotation", "scale"]) {
+ const propInputs = [
+ ...(props.includes("position") ? ["position X", "position Y"] : []),
+ ...(props.includes("rotation") ? ["rotation"] : []),
+ ...(props.includes("scale") ? ["scale X", "scale Y"] : [])
+ ];
+ for (let numInp of propInputs) {
+ // for (let numInp of ["position X", "position Y", "rotation", "scale X", "scale Y"]) {
ui.addNumberInput(
{
componentId: numInp.replace(" ", ""),
@@ -30,9 +40,9 @@ function addTransformInput(ui) {
);
}
return (uiInput) => ({
- position: { x: uiInput?.positionX, y: uiInput?.positionY },
- rotation: uiInput?.rotation,
- scale: { x: uiInput?.scaleX, y: uiInput?.scaleY },
+ ...(props.includes("position") ? { position: { x: uiInput?.positionX, y: uiInput?.positionY } } : {}),
+ ...(props.includes("rotation") ? { rotation: uiInput?.rotation } : {}),
+ ...(props.includes("scale") ? { scale: { x: uiInput?.scaleX, y: uiInput?.scaleY }, } : {})
});
}
@@ -62,8 +72,8 @@ function createBlinkNode(type, title, desc, params) {
if(param.id.includes("color") || param.id.includes("Color")) {
ui.addColorPicker({
componentId: param.id,
- label: "Multitudinous seas incarnadine",
- defaultValue: 0,
+ label: toTitleCase(param.id),
+ defaultValue: "#000000",
triggerUpdate: true,
}, {})
@@ -129,10 +139,10 @@ const blinkNodes = {
"Bevel Filter",
[
{ id: "rotation", min: 0, max: 360, step: 1.0 },
- { id: "thickness", min: 0, max: 10, step: 0.01 },
- { id: "lightColor", min: 0, max: 360, step: 1.0 },
+ { id: "thickness", min: 0, max: 100, step: 0.1 },
+ { id: "lightColor" },
{ id: "lightAlpha", min: 0, max: 1, step: 0.01 },
- { id: "shadowColor", min: 0, max: 360, step: 1.0 },
+ { id: "shadowColor" },
{ id: "shadowAlpha", min: 0, max: 1, step: 0.01 },
]
],
@@ -140,8 +150,8 @@ const blinkNodes = {
"Outline",
"Applies an outline filter to the image",
[
- { id: "thickness", min: 0, max: 10, step: 0.1 },
- { id: "color", min: 0, max: 5, step: 0.05 },
+ { id: "thickness", min: 0, max: 100, step: 0.1 },
+ { id: "color" },
{ id: "alpha", min: 0, max: 1, step: 0.01 },
]
],
@@ -181,18 +191,18 @@ const nodes = {
nodeBuilder.setDescription("Input a Blink Sprite Image");
const ui = nodeBuilder.createUIBuilder();
- // ui.addFilePicker({
- // componentId: "imagePicker",
- // label: "Pick an image",
- // defaultValue: "",
- // triggerUpdate: true,
- // }, {});
- ui.addCachePicker({
- componentId: "cachePicker",
- label: "Pick an cache item",
+ ui.addFilePicker({
+ componentId: "imagePicker",
+ label: "Pick an image",
defaultValue: "",
triggerUpdate: true,
}, {});
+ // ui.addCachePicker({
+ // componentId: "cachePicker",
+ // label: "Pick an cache item",
+ // defaultValue: "",
+ // triggerUpdate: true,
+ // }, {});
addTransformInput(ui);
addState(ui);
addTweakability(ui);
@@ -206,16 +216,17 @@ const nodes = {
});
nodeBuilder.define(async (input, uiInput, from) => {
- // let src = uiInput["cachePicker"].split("/");
- // src = src.splice(-2);
- // src = src.join("/");
+ let src = uiInput["imagePicker"].split("/");
+ src = src.splice(-2);
+ src = src.join("/");
const canvas = {
assets: {
[uiInput["state"]["id"]]: {
class: "asset",
type: "image",
- data: uiInput["cachePicker"],
+ data: uiInput["imagePicker"].split("/").splice(-2).join("/"),
+ // data: uiInput["cachePicker"],
}
},
content: {
@@ -273,7 +284,7 @@ const nodes = {
{}
);
}
- const getTransform = addTransformInput(ui);
+ const getTransform = addTransformInput(ui, ["position", "rotation"]);
addTweakability(ui);
nodeBuilder.define(async (input, uiInput, from) => {
@@ -288,6 +299,7 @@ const nodes = {
content: {
class: "clump",
nodeUUID: uiInput["tweaks"].nodeUUID,
+ changes: uiInput["diffs"]?.uiInputs ?? [],
transform: getTransform(uiInput),
elements: [
{
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index 6804e166..b770cee4 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -4,7 +4,7 @@
import { onDestroy, onMount, tick } from "svelte";
import { type Writable } from "svelte/store";
import { renderApp } from "./render";
- import { type BlinkCanvas, canvas1 } from "./clump";
+ import { type BlinkCanvas } from "./types";
export let media: Writable;
export let send: (msg: string, data: any) => void;
@@ -29,6 +29,8 @@
// resizeTo: window,
});
+ globalThis.__PIXI_APP__ = blink;
+
window.addEventListener("resize", () => {
blink.renderer.resize(window.innerWidth, window.innerHeight);
blink.render();
@@ -80,6 +82,7 @@
const imgCanvasBlockH = 1080;
let imgCanvas = new PIXI.Container();
+ imgCanvas.name = "imgCanvas";
let imgCanvasBlock = new PIXI.Graphics();
imgCanvasBlock.beginFill(0xffffff, 0.9);
diff --git a/blix-plugins/blink/webview/app.ts b/blix-plugins/blink/webview/app.ts
index 1e84613c..2d235077 100644
--- a/blix-plugins/blink/webview/app.ts
+++ b/blix-plugins/blink/webview/app.ts
@@ -1,6 +1,6 @@
import { writable } from 'svelte/store';
import App from './App.svelte';
-import { BlinkCanvas } from './clump';
+import { BlinkCanvas } from './types';
const media = writable({
assets: {},
diff --git a/blix-plugins/blink/webview/atom.ts b/blix-plugins/blink/webview/atom.ts
new file mode 100644
index 00000000..5b844283
--- /dev/null
+++ b/blix-plugins/blink/webview/atom.ts
@@ -0,0 +1,75 @@
+import * as PIXI from 'pixi.js';
+import { Asset, Atom, ImageAtom, PaintAtom, ShapeAtom, TextAtom } from "./types";
+import { diffAtom, diffImageAtom, diffPaintAtom, diffShapeAtom, diffTextAtom } from './diff';
+import { HierarchyAtom } from './render';
+
+export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key: string]: Asset } | undefined, atom: Atom, prevAtom: HierarchyAtom | undefined): {
+ pixiAtom: PIXI.Container,
+ changed: boolean // Whether the pixiClump is a different PIXI object than before
+} {
+ if (!atom) return null;
+
+ const atomsDiffer = diffAtom(atom, prevAtom);
+
+ switch (atom.type) {
+ case "image":
+ const imageDiff = atomsDiffer || prevAssets == null || diffImageAtom(atom, prevAtom as ImageAtom, assets, prevAssets);
+ if (!imageDiff) {
+ return { pixiAtom: prevAtom.container, changed: false };
+ }
+
+ if (assets[atom.assetId] && assets[atom.assetId].type === "image") {
+ const sprite = PIXI.Sprite.from(assets[atom.assetId].data);
+
+ sprite.anchor.x = 0.5;
+ sprite.anchor.y = 0.5;
+
+ sprite.name = "ImageSprite";
+ prevAtom?.container?.destroy();
+ return { pixiAtom: sprite, changed: true };
+ }
+
+ return { pixiAtom: null, changed: true };
+
+ case "shape":
+ const shapeDiff = atomsDiffer || diffShapeAtom(atom, prevAtom as ShapeAtom);
+ if (!shapeDiff) return { pixiAtom: prevAtom.container, changed: false };
+
+ const shapeContainer = new PIXI.Container();
+ const shape = new PIXI.Graphics();
+ // shape.lineStyle(1, 0xf43e5c, 0.5);
+ shape.beginFill(atom.fill, 1);
+ shape.lineStyle(atom.strokeWidth, atom.stroke, 1);
+ const halfBounds = { w: atom.bounds.w / 2, h: atom.bounds.h / 2 };
+
+ switch (atom.shape) {
+ case "rectangle":
+ shape.drawRect(-halfBounds.w, -halfBounds.h, atom.bounds.w, atom.bounds.h);
+ break;
+ case "ellipse":
+ shape.drawEllipse(-halfBounds.w, -halfBounds.h, atom.bounds.w, atom.bounds.h);
+ break;
+ case "triangle":
+ shape.drawPolygon([0, 0, atom.bounds.w, 0, atom.bounds.w / 2, atom.bounds.h]);
+ break;
+ }
+ shape.endFill();
+
+ shapeContainer.addChild(shape);
+
+ shapeContainer.name = "ShapeContainer";
+ return { pixiAtom: shapeContainer, changed: true };
+
+ case "text":
+ const textDiff = atomsDiffer || diffTextAtom(atom, prevAtom as TextAtom);
+ if (!textDiff) return { pixiAtom: prevAtom.container, changed: false };
+
+ return { pixiAtom: null, changed: true };
+
+ case "paint":
+ const paintDiff = atomsDiffer || diffPaintAtom(atom, prevAtom as PaintAtom);
+ if (!paintDiff) return { pixiAtom: prevAtom.container, changed: false };
+
+ return { pixiAtom: null, changed: true };
+ }
+}
\ No newline at end of file
diff --git a/blix-plugins/blink/webview/diff.ts b/blix-plugins/blink/webview/diff.ts
new file mode 100644
index 00000000..0bef98e9
--- /dev/null
+++ b/blix-plugins/blink/webview/diff.ts
@@ -0,0 +1,134 @@
+import { HierarchyAtom, HierarchyClump } from "./render";
+import {
+ Asset,
+ Atom,
+ BlobAtom,
+ Clump,
+ Filter,
+ ImageAtom,
+ PaintAtom,
+ ShapeAtom,
+ TextAtom,
+ Transform,
+} from "./types";
+
+export type ClumpDiff = "name" | "transform" | "opacity" | "filters";
+
+export function diffClump(h1: Clump, h2: HierarchyClump) {
+ const diffs = new Set();
+ if (h1 == null && h2 == null) return new Set([]); // Vacuous case
+ if (h1 == null || h2 == null)
+ return new Set(["name", "transform", "opacity", "filters"]);
+
+ // Diff name
+ if (h1.name !== h2.name) {
+ diffs.add("name");
+ }
+
+ // Diff transform
+ if (h1.transform == null && h2.transform == null) {
+ diffs.add("transform");
+ } else if (diffTransform(h1.transform, h2.transform).length > 0) {
+ diffs.add("transform");
+ }
+
+ // Diff opacity
+ if (h1.opacity !== h2.opacity) {
+ diffs.add("opacity");
+ }
+
+ // Diff filters
+ if (diffFilters(h1.filters, h2.filters)) {
+ diffs.add("filters");
+ }
+
+ return diffs;
+}
+
+function diffTransform(t1: Transform, t2: Transform) {
+ const diffs = [];
+ if (t1?.position?.x !== t2?.position?.x || t1?.position?.y !== t2?.position?.y)
+ diffs.push("position");
+ if (t1?.rotation !== t2?.rotation) diffs?.push("rotation");
+ if (t1?.scale?.x !== t2?.scale?.x || t1?.scale?.y !== t2?.scale?.y) diffs?.push("scale");
+ return diffs;
+}
+
+// Returns true if the filters differ in some way
+function diffFilters(f1s: Filter[], f2s: Filter[]) {
+ if (f1s == null && f2s == null) return false;
+ if (f1s == null || f2s == null) return true;
+
+ const maxLen = Math.max(f1s.length, f2s.length);
+
+ for (let f = 0; f < maxLen; f++) {
+ // Diff filter
+ if (f1s[f] && f2s[f]) {
+ // Diff types
+ if (f1s[f].type !== f2s[f].type) return true;
+
+ // Diff params
+ const maxParamsLen = Math.max(f1s[f].params.length, f2s[f].params.length);
+ for (let p = 0; p < maxParamsLen; p++) {
+ if (f1s[f].params[p] !== f2s[f].params[p]) {
+ return true;
+ }
+ }
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Returns true if the atoms differ in some way
+export function diffAtom(a1: Atom, a2: HierarchyAtom) {
+ if (a1 == null && a2 == null) return false;
+ if (a1 == null || a2 == null) return true;
+
+ if (a1.type !== a2.type) return true;
+ return false;
+}
+
+// TODO: Pass in `assets` later on, so that we can diff on asset.data instead of assetId
+// TODO: For now this just returns a boolean, but later on we'll return a more fine-grained diff
+export function diffImageAtom(a1: ImageAtom, a2: ImageAtom, assets1: { [key: string]: Asset }, assets2: { [key: string]: Asset } ) {
+ if (a1.assetId !== a2.assetId) return true;
+ if (assets1[a1.assetId].type !== assets2[a2.assetId].type) return true;
+ if (assets1[a1.assetId].data !== assets2[a2.assetId].data) return true;
+ return false;
+}
+
+export function diffBlobAtom(a1: BlobAtom, a2: BlobAtom) {
+ if (a1.assetId !== a2.assetId) return true;
+ return false;
+}
+
+export function diffShapeAtom(a1: ShapeAtom, a2: ShapeAtom) {
+ if (a1.shape !== a2.shape) return true;
+ if (a1.bounds?.w !== a2.bounds?.w || a1.bounds?.h !== a2.bounds?.h) return true;
+ if (a1.fill !== a2.fill) return true;
+ if (a1.stroke !== a2.stroke) return true;
+ if (a1.strokeWidth !== a2.strokeWidth) return true;
+ return false;
+}
+
+export function diffTextAtom(a1: TextAtom, a2: TextAtom) {
+ if (a1.text !== a2.text) return true;
+ if (a1.fill !== a2.fill) return true;
+ if (a1.stroke !== a2.stroke) return true;
+ if (a1.strokeWidth !== a2.strokeWidth) return true;
+ if (a1.fontSize !== a2.fontSize) return true;
+ if (a1.fontFamily !== a2.fontFamily) return true;
+ if (a1.fontStyle !== a2.fontStyle) return true;
+ if (a1.fontWeight !== a2.fontWeight) return true;
+ if (a1.textAlign !== a2.textAlign) return true;
+ if (a1.textBaseline !== a2.textBaseline) return true;
+ return false;
+}
+
+export function diffPaintAtom(a1: PaintAtom, a2: PaintAtom) {
+ if (a1.uuid !== a2.uuid) return true;
+ return false;
+}
diff --git a/blix-plugins/blink/webview/filter.ts b/blix-plugins/blink/webview/filter.ts
new file mode 100644
index 00000000..b1b836d6
--- /dev/null
+++ b/blix-plugins/blink/webview/filter.ts
@@ -0,0 +1,31 @@
+import * as PIXI from 'pixi.js';
+import { Filter, getPixiFilter } from './types';
+
+// Apply a series of filters and flatten the result to a sprite.
+// This is done so that the filters are applied evenly regardless of scaling.
+export function applyFilters(blink: PIXI.Application, content: PIXI.Container, filters: Filter[]) {
+ // `renderPadding` is necessary for filters like
+ // blur that spread beyond the bounds of the sprite
+ const renderPadding = 100;
+
+ // Apply filters
+ content.filters = filters.map(getPixiFilter);
+
+ // Normalize offset to fit within renderTexture
+ const { x: bx, y: by, width: bw, height: bh } = content.getBounds();
+ content.transform.position.x = -bx + renderPadding;
+ content.transform.position.y = -by + renderPadding;
+
+ // Render to texture
+ const renderTexture = PIXI.RenderTexture.create({
+ width: bw + 2 * renderPadding,
+ height: bh + 2 * renderPadding,
+ });
+ blink.renderer.render(content, { renderTexture: renderTexture });
+
+ // Create flattened sprite
+ const renderSprite = new PIXI.Sprite(renderTexture);
+ renderSprite.setTransform(bx - renderPadding, by - renderPadding);
+
+ return renderSprite;
+}
\ No newline at end of file
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index aa0cce31..d7c5a79a 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -1,22 +1,42 @@
import * as PIXI from "pixi.js";
-import { getPixiFilter, type Atom, type Clump, type BlinkCanvas, type Asset } from "./clump";
+import {
+ getPixiFilter,
+ type Atom,
+ type Clump,
+ type BlinkCanvas,
+ type Asset,
+ Filter,
+ Transform,
+} from "./types";
import { Viewport } from "pixi-viewport";
import { createBoundingBox } from "./select";
+import { renderAtom } from "./atom";
+import { applyFilters } from "./filter";
+import { diffAtom, diffClump } from "./diff";
// let prevMedia = null; // TODO: Replace with DiffDial
-type Scene = { [key: string]: PIXI.Container };
+// Utility type to override properties of T with properties of R
+type Override = { [P in Exclude]: T[P] } & R;
+
+type HierarchyData = { container: PIXI.Container; containerIndex: number };
+
+export type HierarchyCanvas = Override;
+export type HierarchyClump = Override &
+ HierarchyData;
+export type HierarchyAtom = Atom & HierarchyData;
+// type Clumps = { [key: string]: PIXI.Container };
let selected: string = ""; // NodeUUID of selected clump
let oldSceneStructure = "";
let sceneStructure = "_";
-let oldScene: Scene = {};
-let clumps: Scene = {};
-let boundingBox: PIXI.Container;
+let boundingBox: PIXI.Container = null;
let scene: PIXI.Container;
+let hierarchy: HierarchyCanvas | undefined = undefined;
+
export function renderApp(
blink: PIXI.Application,
canvas: BlinkCanvas,
@@ -24,21 +44,25 @@ export function renderApp(
send: (message: string, data: any) => void
): boolean {
if (!canvas || !canvas.content) return false;
- if (!scene) scene = viewport.addChild(new PIXI.Container());
-
- oldSceneStructure = sceneStructure;
- sceneStructure = getSceneStructure(canvas.content);
-
- if (oldSceneStructure === sceneStructure) return;
+ if (!scene) {
+ scene = new PIXI.Container();
+ scene.name = "Blink Scene";
+ viewport.addChild(scene);
+ }
// Destroy previous viewport contents
- scene.removeChildren();
- oldScene = clumps;
- clumps = {};
+ // scene.removeChildren();
+ // if (boundingBox) {
+ // boundingBox.removeChildren();
+ // }
//===== CREATE BOUNDING BOX =====//
- boundingBox = new PIXI.Container();
- blink.stage.addChild(boundingBox);
+ // TODO
+ if (boundingBox == null) {
+ boundingBox = new PIXI.Container();
+ boundingBox.name = "boundingBox";
+ blink.stage.addChild(boundingBox);
+ }
//===== PRELOAD IMAGE ASSETS =====//
const imgPromises = [];
@@ -53,10 +77,28 @@ export function renderApp(
// Construct clump hierarchy
const loaded = Promise.all(imgPromises);
loaded.then(() => {
-
- const child = renderClump(blink, canvas.content, canvas, viewport, send);
- if (child != null) {
- scene.addChild(child);
+ const { pixiClump, changed } = renderClump(
+ blink,
+ canvas.content,
+ hierarchy?.content,
+ canvas,
+ hierarchy,
+ viewport,
+ send
+ );
+
+ // Update hierarchy
+ hierarchy = {
+ assets: canvas.assets,
+ content: {
+ ...canvas.content,
+ container: pixiClump,
+ containerIndex: 0,
+ } as HierarchyClump,
+ };
+
+ if (hierarchy.content.container != null && changed) {
+ scene.addChild(hierarchy.content.container);
}
//===============// DELETE DEAD CLUMPS //==============//
@@ -67,121 +109,161 @@ export function renderApp(
// oldScene[nodeUUID].destroy(); // PIXI.js cleanup
// }
// }
- console.log("CLUMPS", clumps);
- console.log("SCENE", scene);
+ // console.log("CLUMPS", clumps);
+ // console.log("SCENE", scene);
+ console.log("====================================");
});
return true;
}
-function getSceneStructure(clump: Clump) {
- if (!clump) return "";
- let res = clump.nodeUUID;
-
- if (clump.elements) {
- let counter = clump.elements.length;
- for (let child of clump.elements) {
- if (child.class === "clump") {
- res += ">" + getSceneStructure(child);
- } else if (child.class === "atom") {
- // res += ">" + child.nodeUUID;
- }
- }
- }
-
- return res;
-}
-
export function renderCanvas(blink: PIXI.Application, root: Clump) {}
function renderClump(
blink: PIXI.Application,
clump: Clump,
+ prevClump: HierarchyClump | undefined,
canvas: BlinkCanvas,
+ prevCanvas: BlinkCanvas,
viewport: Viewport,
send: (message: string, data: any) => void
-) {
- if (!clump) return null;
+): {
+ pixiClump: PIXI.Container;
+ changed: boolean; // Whether the pixiClump is a different PIXI object than before
+} {
+ if (!clump) return { pixiClump: null, changed: prevClump != null };
- // //===============// RESURRECT OLD CLUMPS //==============//
- // if (clump.nodeUUID in oldScene) {
- // // Clump already exists, resurrect and update it
- // scene[clump.nodeUUID] = oldScene[clump.nodeUUID];
- // }
- // //===============// ADD NEW CLUMPS //==============//
- // else {
- // }
-
- //========== CREATE CONTAINER ==========//
- const content = new PIXI.Container();
- content.sortableChildren = true;
+ //========== DIFF CLUMP VS HIERARCHY ==========//
+ const diffs = diffClump(clump, prevClump);
+ // console.log("DIFFS", diffs);
//========== CREATE CHILD ELEMENTS ==========//
+ let childChanged = false;
+ const children: PIXI.Container[] = [];
+
if (clump.elements) {
- let counter = clump.elements.length;
- for (let child of clump.elements) {
- if (child.class === "clump") {
- const pixiClump = renderClump(blink, child, canvas, viewport, send);
+ for (let i = 0; i < clump.elements.length; i++) {
+ const child = clump.elements[i];
+ const prevChild = prevClump?.elements != null && prevClump.elements[i];
- if (pixiClump != null) {
- pixiClump.zIndex = counter--;
- content.addChild(pixiClump);
+ if (child.class === "clump") {
+ //===== CHILD CLUMP =====//
+ const { pixiClump, changed } = renderClump(
+ blink,
+ child as HierarchyClump,
+ (prevChild?.class === "clump" ? prevChild : undefined) as HierarchyClump,
+ canvas,
+ prevCanvas,
+ viewport,
+ send
+ );
+
+ if (changed) {
+ childChanged = true;
+ // console.log("CREATE CLUMP", pixiClump);
+ if (pixiClump != null) {
+ children.push(pixiClump);
+ // content.addChild(pixiClump);
+ }
+
+ // Destroy previous contents
+ // console.log("DESTROY CLUMP", prevChild?.container);
+ prevChild?.container?.destroy();
}
} else if (child.class === "atom") {
- const pixiAtom = renderAtom(canvas.assets, child);
+ //===== CHILD ATOM =====//
+ const { pixiAtom, changed } = renderAtom(
+ canvas.assets,
+ prevCanvas?.assets,
+ child,
+ (prevChild?.class === "atom" ? prevChild : undefined) as HierarchyAtom
+ );
+
+ // Construct hierarchy atom
+ // const hierarchyAtom = child as HierarchyAtom;
+ // hierarchyAtom.container = pixiAtom;
+ // hierarchyAtom.containerIndex = i;
+ // hierarchy.elements.push(hierarchyAtom);
+
+ // console.log("PIXI ATOM", hierarchyAtom);
+
+ if (changed) {
+ childChanged = true;
+ // console.log("CREATE ATOM", pixiAtom);
+ // content.removeChild(pixiAtom);
+ if (pixiAtom != null) {
+ children.push(pixiAtom);
+ // content.addChild(pixiAtom);
+ }
+
+ // Destroy previous contents
+ // console.log("DESTROY ATOM", prevChild?.container);
+ prevChild?.container?.destroy();
+ }
+ }
+ }
- if (pixiAtom != null) {
- pixiAtom.zIndex = counter--;
- content.addChild(pixiAtom);
+ // Remove previous surplus children
+ if (prevClump?.elements) {
+ for (let i = clump.elements.length + 1; i < prevClump.elements.length; i++) {
+ if (prevClump.elements[i].class === "clump") {
+ const pClump = prevClump.elements[i] as HierarchyClump;
+ // console.log("REMOVE CLUMP CHILD");
+ prevClump.container.removeChild(pClump.container);
+ } else if (prevClump.elements[i].class === "atom") {
+ const pAtom = prevClump.elements[i] as HierarchyAtom;
+ // console.log("REMOVE ATOM CHILD");
+ prevClump.container.removeChild(pAtom.container);
}
}
}
}
- //========== CREATE INTERACTION BOX ==========//
- // Create interaction box once sprite has loaded
- content.sortChildren();
-
- const resClump = new PIXI.Container();
- const resClumpContent = new PIXI.Container();
- resClump.sortableChildren = true;
-
- if (clump.filters && clump.filters.length > 0) {
- //===== FLATTEN CLUMP =====//
- // Clump uses filters, we must flatten it to a texture
- // so that the filters are applied evenly regardless of scaling
+ //========== OBTAIN CLUMP CONTAINER ==========//
+ let resClump: PIXI.Container;
+ if (prevClump?.container) {
+ resClump = prevClump.container;
+ // TODO: Destroy previous contents
+ } else {
+ resClump = new PIXI.Container();
+ resClump.name = "Clump";
+ resClump.sortableChildren = true;
- // `renderPadding` is necessary for filters like
- // blur that spread beyond the bounds of the sprite
- const renderPadding = 100;
+ addInteractivity(resClump, clump, viewport, send);
+ }
- // Apply filters
- content.filters = clump.filters.map(getPixiFilter);
+ if (childChanged || diffs.has("filters")) {
+ //========== RECREATE CONTENT ==========//
+ let content = new PIXI.Container();
+ content = new PIXI.Container();
+ content.name = "content";
+ content.sortableChildren = true;
- // Normalize offset to fit within renderTexture
- const { x: bx, y: by, width: bw, height: bh } = content.getBounds();
- content.transform.position.x = -bx + renderPadding;
- content.transform.position.y = -by + renderPadding;
+ // TODO: Only add children that have changed
+ // Also remove redundant children
- // Render to texture
- const renderTexture = PIXI.RenderTexture.create({
- width: bw + 2 * renderPadding,
- height: bh + 2 * renderPadding,
- });
- blink.renderer.render(content, { renderTexture: renderTexture });
+ //Add children to content
+ for (let i = 0; i < children.length; i++) {
+ children[i].zIndex = children.length - i;
+ content.addChild(children[i]);
+ }
- // Create flattened sprite and add to clump
- const renderSprite = new PIXI.Sprite(renderTexture);
- renderSprite.setTransform(bx - renderPadding, by - renderPadding);
+ content.sortChildren();
- resClumpContent.addChild(renderSprite);
- } else {
- //===== ADD CONTENT WITHOUT FLATTENING =====//
- resClumpContent.addChild(content);
+ //========== APPLY FILTERS ==========//
+ if (clump.filters && clump.filters.length > 0) {
+ resClump.addChild(applyFilters(blink, content, clump.filters));
+ } else {
+ //===== ADD CONTENT WITHOUT FLATTENING =====//
+ if (childChanged) {
+ // resClump.removeChildren();
+ resClump.addChild(content);
+ }
+ }
}
// Get bounds before applying transform
- const clumpBounds = resClumpContent.getBounds();
+ const clumpBounds = resClump.getBounds();
//========== APPLY CLUMP PROPERTIES ==========//
let transMatrix = PIXI.Matrix.IDENTITY;
@@ -197,106 +279,73 @@ function renderClump(
// const matTransform = resClumpContent.transform.worldTransform;
if (clump.opacity) {
- resClumpContent.alpha = Math.min(100, Math.max(0, clump.opacity));
+ resClump.alpha = Math.min(1, Math.max(0, clump.opacity / 100.0));
}
- resClump.addChild(resClumpContent);
+ resClump.sortChildren();
- const box = createBoundingBox(transMatrix, clumpBounds, viewport);
+ // boundingBox.addChild(createBoundingBox(transMatrix, resClump.getBounds(), viewport));
- boundingBox.removeChildren();
- boundingBox.addChild(box);
+ // hierarchy.container = resClump;
+ return {
+ pixiClump: resClump,
+ changed: true,
+ };
+}
- resClump.sortChildren();
- //========== HANDLE EVENTS ==========//
+function addInteractivity(container: PIXI.Container, clump: Clump, viewport: Viewport, send: (message: string, data: any) => void) {
let dragging = false;
var prevMousePos = new PIXI.Point();
- resClump.eventMode = "dynamic";
+ container.eventMode = "dynamic";
// resClump.on("click", () => {
// });
- resClump.on("mousedown", (event) => {
+ container.on("mousedown", (event) => {
event.stopPropagation();
selected = clump.nodeUUID;
dragging = true;
prevMousePos = viewport.toWorld(event.global);
});
viewport.on("mouseup", (event) => {
+ if (selected === clump.nodeUUID && dragging) {
+ send("tweak", {
+ nodeUUID: clump.nodeUUID,
+ inputs: {
+ positionX: container.transform.position.x,
+ positionY: container.transform.position.y
+ },
+ });
+ }
+
dragging = false;
});
viewport.on("mousemove", (event) => {
if (selected === clump.nodeUUID && dragging) {
+ // boundingBox.removeChildren();
+ // boundingBox.addChild(createBoundingBox(container.transform.worldTransform, container.getBounds(), viewport));
+
const pos = viewport.toWorld(event.global);
const shift = new PIXI.Point(pos.x - prevMousePos.x, pos.y - prevMousePos.y);
prevMousePos = pos;
- resClump.transform.position.x += shift.x;
- resClump.transform.position.y += shift.y;
-
- send("tweak", {
- nodeUUID: clump.nodeUUID,
- inputs: {
- positionX: resClump.transform.position.x,
- positionY: resClump.transform.position.y,
- },
- });
+ container.transform.position.x += shift.x;
+ container.transform.position.y += shift.y;
+
+ // send("tweak", {
+ // nodeUUID: clump.nodeUUID,
+ // inputs: {
+ // // positionX: container.transform.position.x + shift.x,
+ // // positionY: container.transform.position.y + shift.y
+ // positionX: container.transform.position.x,
+ // positionY: container.transform.position.y
+ // },
+ // });
}
});
// To get global mouse position at any point:
// console.log("MOUSE", blink.renderer.plugins.interaction.pointer.global);
-
- clumps[clump.nodeUUID] = resClump;
- return resClump;
-}
-
-
-
-function renderAtom(assets: { [key: string]: Asset }, atom: Atom) {
- switch (atom.type) {
- case "image":
- if (assets[atom.assetId] && assets[atom.assetId].type === "image") {
- const sprite = PIXI.Sprite.from(assets[atom.assetId].data);
-
- sprite.anchor.x = 0.5;
- sprite.anchor.y = 0.5;
-
- return sprite;
- }
-
- return null;
-
- case "shape":
- const shapeContainer = new PIXI.Container();
- const shape = new PIXI.Graphics();
- // shape.lineStyle(1, 0xf43e5c, 0.5);
- shape.beginFill(atom.fill, 1);
- shape.lineStyle(atom.strokeWidth, atom.stroke, 1);
-
- switch (atom.shape) {
- case "rectangle":
- shape.drawRect(0, 0, atom.bounds.w, atom.bounds.h);
- break;
- case "ellipse":
- shape.drawEllipse(0, 0, atom.bounds.w, atom.bounds.h);
- break;
- case "triangle":
- shape.drawPolygon([0, 0, atom.bounds.w, 0, atom.bounds.w / 2, atom.bounds.h]);
- break;
- }
- shape.endFill();
-
- shapeContainer.addChild(shape);
-
- return shapeContainer;
-
- case "text":
- break;
-
- case "paint":
- break;
- }
-}
+}
\ No newline at end of file
diff --git a/blix-plugins/blink/webview/clump.ts b/blix-plugins/blink/webview/types.ts
similarity index 96%
rename from blix-plugins/blink/webview/clump.ts
rename to blix-plugins/blink/webview/types.ts
index 18786268..fd23cf9c 100644
--- a/blix-plugins/blink/webview/clump.ts
+++ b/blix-plugins/blink/webview/types.ts
@@ -14,7 +14,6 @@ import {
ZoomBlurFilter,
TwistFilter
} from "pixi-filters"
-import { type } from "os";
export type BlinkCanvas = {
assets: { [key: string]: Asset };
@@ -90,17 +89,18 @@ export function getPixiFilter(filter: Filter) {
// A single indivisible unit of a clump (E.g. image, shape, text etc.)
export type Atom = { class: "atom", nodeUUID: string } & (ImageAtom | ShapeAtom | TextAtom | PaintAtom | BlobAtom);
-type ImageAtom = {
+export type ImageAtom = {
type: "image";
assetId: string;
};
-type BlobAtom = {
+export type BlobAtom = {
type: "blob";
+ blob: "image"; //TODO: Add more options
assetId: string;
}
-type ShapeAtom = {
+export type ShapeAtom = {
type: "shape";
shape: "rectangle" | "ellipse" | "triangle";
@@ -109,7 +109,7 @@ type ShapeAtom = {
stroke: number;
strokeWidth: number;
};
-type TextAtom = {
+export type TextAtom = {
type: "text";
text: string;
@@ -123,7 +123,7 @@ type TextAtom = {
textAlign: "left" | "center" | "right";
textBaseline: "top" | "hanging" | "middle" | "alphabetic" | "ideographic" | "bottom";
};
-type PaintAtom = {
+export type PaintAtom = {
type: "paint";
uuid: string;
};
diff --git a/src/frontend/ui/base/palette/Palette.svelte b/src/frontend/ui/base/palette/Palette.svelte
index adc56693..da5a3752 100644
--- a/src/frontend/ui/base/palette/Palette.svelte
+++ b/src/frontend/ui/base/palette/Palette.svelte
@@ -156,10 +156,14 @@
closePalette();
},
"blix.palette.scrollDown": () => {
+ if (!showPalette) return;
+
selectedItem++;
repairItemIndex();
},
"blix.palette.scrollUp": () => {
+ if (!showPalette) return;
+
if (!searchTerm && promptHistory.length) {
// inputElement.focus();
// inputElement.value = prompts[0];
@@ -172,6 +176,8 @@
repairItemIndex();
},
"blix.palette.selectItem": () => {
+ if (!showPalette) return;
+
// Default enter
const item = categories[selectedCategory]?.items[selectedItem];
if (item) {
@@ -179,6 +185,8 @@
}
},
"blix.palette.prompt": async () => {
+ if (!showPalette) return;
+
const prompt = searchTerm.trim();
if (!prompt) {
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index 68990632..5ad41ec2 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -1,5 +1,7 @@
@@ -11,6 +13,13 @@
{/each}
+ Add asset
diff --git a/src/frontend/ui/utils/graph/PluginNode.svelte b/src/frontend/ui/utils/graph/PluginNode.svelte
index 8a7a99fd..351f4842 100644
--- a/src/frontend/ui/utils/graph/PluginNode.svelte
+++ b/src/frontend/ui/utils/graph/PluginNode.svelte
@@ -5,7 +5,10 @@
import NodeUiFragment from "./NodeUIFragment.svelte";
import { createEventDispatcher } from "svelte";
import { graphMall } from "lib/stores/GraphStore";
- import { colord } from "colord";
+ import { colord, extend } from "colord";
+ import a11yPlugin from "colord/plugins/a11y";
+
+ extend([a11yPlugin]);
const dispatch = createEventDispatcher();
@@ -47,27 +50,11 @@
function changeBrightness(color: CSSColorString, percent: number) {
return colord(color).lighten(percent).toRgbString();
- // var R = parseInt(color.substring(1, 3), 16);
- // var G = parseInt(color.substring(3, 5), 16);
- // var B = parseInt(color.substring(5, 7), 16);
-
- // R = parseInt(`${(R * (100 + percent)) / 100}`);
- // G = parseInt(`${(G * (100 + percent)) / 100}`);
- // B = parseInt(`${(B * (100 + percent)) / 100}`);
-
- // R = R < 255 ? R : 255;
- // G = G < 255 ? G : 255;
- // B = B < 255 ? B : 255;
-
- // R = Math.round(R);
- // G = Math.round(G);
- // B = Math.round(B);
-
- // var RR = R.toString(16).length == 1 ? "0" + R.toString(16) : R.toString(16);
- // var GG = G.toString(16).length == 1 ? "0" + G.toString(16) : G.toString(16);
- // var BB = B.toString(16).length == 1 ? "0" + B.toString(16) : B.toString(16);
+ }
- // return ("#" + RR + GG + BB) as CSSColorString;
+ function checkShowTextOutline(color: string) {
+ const readable = colord(color).isReadable();
+ return readable;
}
async function nodeClicked(e: CustomEvent) {
@@ -142,11 +129,15 @@ height="{graphNode.dims.h}" -->
let:hovering
>
{#if hovering}
+ {@const typeCol = changeBrightness(color, 0.3)}
{#if input.displayName}
{input.displayName}
{/if}
- <{input.type || "any"} {input.type || "any"}>
{/if}
@@ -177,12 +168,15 @@ height="{graphNode.dims.h}" -->
let:hovering
>
{#if hovering}
+ {@const typeCol = changeBrightness(color, 0.3)}
{#if output.displayName}
{output.displayName}
{/if}
- <{output.type || "any"} >
@@ -259,6 +253,12 @@ height="{graphNode.dims.h}" -->
padding: 0.2em;
top: -3.5em;
}
+ .outlineText {
+ background-color: lightgrey;
+ border-radius: 0.25em;
+ padding: 0.1em;
+ }
+
.header {
display: flex;
justify-content: space-between;
diff --git a/src/index.ts b/src/index.ts
index 360bf031..a98c4bfb 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,6 +7,7 @@ import {
MenuItem,
dialog,
globalShortcut,
+ session,
} from "electron";
import { join } from "path";
import { parse } from "url";
@@ -65,12 +66,20 @@ let blix: Blix | null = null;
* process will bind to the window IPC APIs 5. The Blix state is instantiated
* and the various managers are initialized.
*/
+// TODO: Investigate app.whenReady().then(...)
+// This may be more stable, as it guarantees the callback always fires regardless of startup time
+// See: [https://www.reddit.com/r/electronjs/comments/t151k8/what_is_the_difference_between_apponready_and/hydu5vc]
app.on("ready", async () => {
protocol.registerFileProtocol("blix-image", (request, callback) => {
const url = request.url.slice("blix-image://".length);
callback({ path: join(__dirname, "..", "..", url) });
});
+ // TODO: Remove
+ await session.defaultSession.loadExtension(
+ "/home/rec1dite/.config/google-chrome/Default/Extensions/aamddddknhcagpehecnhphigffljadon/2.6.1_0"
+ );
+
// const coreGraphInterpreter = new CoreGraphInterpreter(new ToolboxRegistry);
// coreGraphInterpreter.run();
From 3d4e4cfdf199eeb2b14799e03abb432822520383 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 12:12:43 +0200
Subject: [PATCH 048/209] Try something
---
.github/workflows/build.yml | 2 +-
electron-builder.yml | 4 ++++
package-lock.json | 4 ++--
package.json | 26 ++++++++++++++++++--------
4 files changed, 25 insertions(+), 11 deletions(-)
create mode 100644 electron-builder.yml
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2e8d2910..71ceb2e4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
- os: [macos-latest, ubuntu-latest, windows-latest]
+ os: [buntu-latest, windows-latest]
steps:
- name: Check out Git repository
diff --git a/electron-builder.yml b/electron-builder.yml
new file mode 100644
index 00000000..b5c3ed33
--- /dev/null
+++ b/electron-builder.yml
@@ -0,0 +1,4 @@
+appId: com.the-spanish-inquisition.blix
+publish:
+ provider: github
+ token: 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v
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index a05bf73f..9504cdfe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "blix",
- "version": "1.0.0",
+ "version": "1.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "blix",
- "version": "1.0.0",
+ "version": "1.1.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index df15320c..c18a3090 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.1.0",
+ "version": "1.2.0",
"repository": {
"type": "git",
"url": "https://github.com/COS301-SE-2023/AI-Photo-Editor"
@@ -62,7 +62,10 @@
"preversion": "npm-run-all -s lint format",
"prepare": "husky install",
"llm": "cross-env NODE_ENV=development npm run build:electron:dev && node build/electron/lib/ai/ai-profiler-v2.js",
- "cli": "cross-env NODE_ENV=development npm run build:electron:dev && node build/electron/lib/ai/cli.js"
+ "cli": "cross-env NODE_ENV=development npm run build:electron:dev && node build/electron/lib/ai/cli.js",
+ "predeploy": "npm run build",
+ "deploy": "cross-env CSC_IDENTITY_AUTO_DISCOVERY=false GH_TOKEN=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 electron-builder --win --publish always",
+ "postdeploy": "node scripts/cleanBuilds.js"
},
"lint-staged": {
"src/electron/**/*.{js,ts}": "eslint -c eslint.electron.json",
@@ -165,19 +168,24 @@
"build": {
"productName": "Blix",
"appId": "com.the-spanish-inquisition.blix",
- "copyright": "Copyright© 2023 Blix",
+ "copyright": "Copyright © 2023 The Spanish Inquisition",
"win": {
- "target": "nsis"
-
+ "target": [
+ "nsis"
+ ]
},
"mac": {
- "target": "dmg",
+ "target": [
+ "dmg"
+ ],
"category": "productivity",
"type": "distribution",
"hardenedRuntime": "true"
},
"linux": {
- "target": "AppImage",
+ "target": [
+ "AppImage"
+ ],
"category": "productivity"
},
"files": [
@@ -196,10 +204,12 @@
]
}
],
- "publish": {
+ "publish": [
+ {
"provider": "github",
"owner": "ArmandKrynauw",
"repo": "Blix"
}
+ ]
}
}
From 59da2b4a4386d6626b792695226d493bc3f5124e Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 12:14:10 +0200
Subject: [PATCH 049/209] Oops, made a mistake last commit
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 71ceb2e4..9cfcc3ba 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
- os: [buntu-latest, windows-latest]
+ os: [ubuntu-latest, windows-latest]
steps:
- name: Check out Git repository
From ffafba0315a3dba67e6812d4c35c02cdb3e1d3d7 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 13:28:30 +0200
Subject: [PATCH 050/209] Try again
---
.github/workflows/build.yml | 19 +++++++++++--------
package.json | 2 +-
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9cfcc3ba..6e52a807 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -19,15 +19,18 @@ jobs:
with:
node-version: 18
- - name: Build/release Electron app
- uses: samuelmeuli/action-electron-builder@v1
- with:
- github_token: ${{ secrets.github_token }}
+ - name: Build & Release Blix
+ run: npm run deploy
+
+ # - name: Build/release Electron app
+ # uses: samuelmeuli/action-electron-builder@v1
+ # with:
+ # github_token: ${{ secrets.github_token }}
- # If the commit is tagged with a version (e.g. "v1.0.0"),
- # release the app after building
- # release: ${{ startsWith(github.ref, 'refs/tags/v') }}
- release: true
+ # # If the commit is tagged with a version (e.g. "v1.0.0"),
+ # # release the app after building
+ # # release: ${{ startsWith(github.ref, 'refs/tags/v') }}
+ # release: true
# name: Build Codebase
diff --git a/package.json b/package.json
index c18a3090..58a3bc5f 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,7 @@
"llm": "cross-env NODE_ENV=development npm run build:electron:dev && node build/electron/lib/ai/ai-profiler-v2.js",
"cli": "cross-env NODE_ENV=development npm run build:electron:dev && node build/electron/lib/ai/cli.js",
"predeploy": "npm run build",
- "deploy": "cross-env CSC_IDENTITY_AUTO_DISCOVERY=false GH_TOKEN=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 electron-builder --win --publish always",
+ "deploy": "cross-env CSC_IDENTITY_AUTO_DISCOVERY=false on-builder --publish always",
"postdeploy": "node scripts/cleanBuilds.js"
},
"lint-staged": {
From 408f525e0c2246f93213b09d84e847e6b4a85b05 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 13:32:31 +0200
Subject: [PATCH 051/209] Add extra step
---
.github/workflows/build.yml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6e52a807..ad978fb6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -14,11 +14,14 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v3
- - name: Install Node.js and NPM
+ - name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
+ - name: Install dependencies
+ run: npm ci
+
- name: Build & Release Blix
run: npm run deploy
From f9457c59aa7c9558e15e3ac720d4a8e06af6bea7 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 13:34:45 +0200
Subject: [PATCH 052/209] Fix package.json
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 58a3bc5f..959bf536 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,7 @@
"llm": "cross-env NODE_ENV=development npm run build:electron:dev && node build/electron/lib/ai/ai-profiler-v2.js",
"cli": "cross-env NODE_ENV=development npm run build:electron:dev && node build/electron/lib/ai/cli.js",
"predeploy": "npm run build",
- "deploy": "cross-env CSC_IDENTITY_AUTO_DISCOVERY=false on-builder --publish always",
+ "deploy": "cross-env CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --publish always",
"postdeploy": "node scripts/cleanBuilds.js"
},
"lint-staged": {
From 1ad093d9b36b7c1bdc5aa34a3f9230b22053f707 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 13:45:31 +0200
Subject: [PATCH 053/209] Add set env step
---
.github/workflows/build.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ad978fb6..4a5b14d8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -24,6 +24,8 @@ jobs:
- name: Build & Release Blix
run: npm run deploy
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: Build/release Electron app
# uses: samuelmeuli/action-electron-builder@v1
From 777c267434cdba6ac28a93732a8aeaa3080cdd5f Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 13:52:50 +0200
Subject: [PATCH 054/209] Make some changes
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4a5b14d8..f6ac7559 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -23,7 +23,7 @@ jobs:
run: npm ci
- name: Build & Release Blix
- run: npm run deploy
+ run: npm run dist
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From e0e5083fadee03d456c81abae380d05fe6ddbce4 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 14:02:20 +0200
Subject: [PATCH 055/209] Update version
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 959bf536..08d3ee6b 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.2.0",
+ "version": "1.0.1",
"repository": {
"type": "git",
"url": "https://github.com/COS301-SE-2023/AI-Photo-Editor"
From bf0ff1085bda8eb30e6dc9b27156f6a8137015ee Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Mon, 18 Sep 2023 15:38:04 +0200
Subject: [PATCH 056/209] Fix Blink nested children destroyed on graph
transform update
---
blix-plugins/blink/webview/atom.ts | 8 +-
blix-plugins/blink/webview/render.ts | 213 +++++++++++++++------------
2 files changed, 125 insertions(+), 96 deletions(-)
diff --git a/blix-plugins/blink/webview/atom.ts b/blix-plugins/blink/webview/atom.ts
index 5b844283..da261294 100644
--- a/blix-plugins/blink/webview/atom.ts
+++ b/blix-plugins/blink/webview/atom.ts
@@ -1,12 +1,13 @@
import * as PIXI from 'pixi.js';
import { Asset, Atom, ImageAtom, PaintAtom, ShapeAtom, TextAtom } from "./types";
import { diffAtom, diffImageAtom, diffPaintAtom, diffShapeAtom, diffTextAtom } from './diff';
-import { HierarchyAtom } from './render';
+import { HierarchyAtom, randomId } from './render';
export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key: string]: Asset } | undefined, atom: Atom, prevAtom: HierarchyAtom | undefined): {
pixiAtom: PIXI.Container,
changed: boolean // Whether the pixiClump is a different PIXI object than before
} {
+ console.log(">>>---------------------------------");
if (!atom) return null;
const atomsDiffer = diffAtom(atom, prevAtom);
@@ -24,7 +25,8 @@ export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key:
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
- sprite.name = "ImageSprite";
+ sprite.name = `ImageSprite(${randomId()})`;
+ // console.log("DESTROY PREV ATOM");
prevAtom?.container?.destroy();
return { pixiAtom: sprite, changed: true };
}
@@ -57,7 +59,7 @@ export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key:
shapeContainer.addChild(shape);
- shapeContainer.name = "ShapeContainer";
+ shapeContainer.name = `ShapeContainer(${randomId()})`;
return { pixiAtom: shapeContainer, changed: true };
case "text":
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index d7c5a79a..3062462a 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -14,12 +14,16 @@ import { renderAtom } from "./atom";
import { applyFilters } from "./filter";
import { diffAtom, diffClump } from "./diff";
+export function randomId() {
+ return Math.random().toString(36).slice(2, 6);
+}
+
// let prevMedia = null; // TODO: Replace with DiffDial
// Utility type to override properties of T with properties of R
type Override = { [P in Exclude]: T[P] } & R;
-type HierarchyData = { container: PIXI.Container; containerIndex: number };
+type HierarchyData = { container: PIXI.Container };
export type HierarchyCanvas = Override;
export type HierarchyClump = Override &
@@ -43,6 +47,8 @@ export function renderApp(
viewport: Viewport,
send: (message: string, data: any) => void
): boolean {
+ console.log("====================================");
+
if (!canvas || !canvas.content) return false;
if (!scene) {
scene = new PIXI.Container();
@@ -93,7 +99,6 @@ export function renderApp(
content: {
...canvas.content,
container: pixiClump,
- containerIndex: 0,
} as HierarchyClump,
};
@@ -111,7 +116,6 @@ export function renderApp(
// }
// console.log("CLUMPS", clumps);
// console.log("SCENE", scene);
- console.log("====================================");
});
return true;
@@ -131,15 +135,19 @@ function renderClump(
pixiClump: PIXI.Container;
changed: boolean; // Whether the pixiClump is a different PIXI object than before
} {
- if (!clump) return { pixiClump: null, changed: prevClump != null };
+ if (!clump) {
+ console.log("------------------------------------");
+ return { pixiClump: null, changed: prevClump != null };
+ }
+ console.log("PREVCLUMP EXISTS", prevClump?.container != null)
//========== DIFF CLUMP VS HIERARCHY ==========//
const diffs = diffClump(clump, prevClump);
- // console.log("DIFFS", diffs);
//========== CREATE CHILD ELEMENTS ==========//
let childChanged = false;
- const children: PIXI.Container[] = [];
+ const children: { changed: boolean, child: PIXI.Container }[] = [];
+ const hierarchyElements = [];
if (clump.elements) {
for (let i = 0; i < clump.elements.length; i++) {
@@ -158,18 +166,18 @@ function renderClump(
send
);
- if (changed) {
- childChanged = true;
- // console.log("CREATE CLUMP", pixiClump);
- if (pixiClump != null) {
- children.push(pixiClump);
- // content.addChild(pixiClump);
- }
+ // Construct hierarchy clump
+ const hierarchyClump = child as HierarchyClump;
+ hierarchyClump.container = pixiClump;
+ hierarchyElements.push(hierarchyClump);
- // Destroy previous contents
- // console.log("DESTROY CLUMP", prevChild?.container);
- prevChild?.container?.destroy();
+ childChanged ||= changed;
+
+ if (pixiClump != null) {
+ children.push({ changed, child: pixiClump });
+ // content.addChild(pixiClump);
}
+
} else if (child.class === "atom") {
//===== CHILD ATOM =====//
const { pixiAtom, changed } = renderAtom(
@@ -180,84 +188,102 @@ function renderClump(
);
// Construct hierarchy atom
- // const hierarchyAtom = child as HierarchyAtom;
- // hierarchyAtom.container = pixiAtom;
- // hierarchyAtom.containerIndex = i;
- // hierarchy.elements.push(hierarchyAtom);
-
- // console.log("PIXI ATOM", hierarchyAtom);
-
- if (changed) {
- childChanged = true;
- // console.log("CREATE ATOM", pixiAtom);
- // content.removeChild(pixiAtom);
- if (pixiAtom != null) {
- children.push(pixiAtom);
- // content.addChild(pixiAtom);
- }
+ const hierarchyAtom = child as HierarchyAtom;
+ hierarchyAtom.container = pixiAtom;
+ hierarchyElements.push(hierarchyAtom);
- // Destroy previous contents
- // console.log("DESTROY ATOM", prevChild?.container);
- prevChild?.container?.destroy();
- }
- }
- }
+ childChanged ||= changed;
- // Remove previous surplus children
- if (prevClump?.elements) {
- for (let i = clump.elements.length + 1; i < prevClump.elements.length; i++) {
- if (prevClump.elements[i].class === "clump") {
- const pClump = prevClump.elements[i] as HierarchyClump;
- // console.log("REMOVE CLUMP CHILD");
- prevClump.container.removeChild(pClump.container);
- } else if (prevClump.elements[i].class === "atom") {
- const pAtom = prevClump.elements[i] as HierarchyAtom;
- // console.log("REMOVE ATOM CHILD");
- prevClump.container.removeChild(pAtom.container);
+ if (pixiAtom != null) {
+ children.push({ changed, child: pixiAtom });
+ // content.addChild(pixiAtom);
}
}
}
+ console.log("-> CHILDREN", children);
}
//========== OBTAIN CLUMP CONTAINER ==========//
let resClump: PIXI.Container;
- if (prevClump?.container) {
- resClump = prevClump.container;
- // TODO: Destroy previous contents
- } else {
- resClump = new PIXI.Container();
- resClump.name = "Clump";
- resClump.sortableChildren = true;
+ let content: PIXI.Container;
+ const newContainer = prevClump?.container == null;
+ // Create resClump
+ if (newContainer) {
+ resClump = new PIXI.Container();
+ resClump.name = `Clump(${randomId()})`;
+ // resClump.sortableChildren = true;
addInteractivity(resClump, clump, viewport, send);
- }
- if (childChanged || diffs.has("filters")) {
- //========== RECREATE CONTENT ==========//
- let content = new PIXI.Container();
+ } else { resClump = prevClump.container; }
+
+ // Add content
+ if (resClump.children.length === 0) {
content = new PIXI.Container();
- content.name = "content";
+ content.name = `content(${randomId()})`;
content.sortableChildren = true;
- // TODO: Only add children that have changed
- // Also remove redundant children
+ resClump.addChild(content);
+ } else { content = resClump.getChildAt(0) as PIXI.Container; }
+
+ console.log(`==> ${newContainer ? "" : "NO "}NEW RESCLUMP (${resClump.name.split("(")[1].slice(0, 4)})`);
+
+ if (childChanged || diffs.has("filters")) {
+ //========== BUILD CONTENT ==========//
//Add children to content
- for (let i = 0; i < children.length; i++) {
- children[i].zIndex = children.length - i;
- content.addChild(children[i]);
+ if (newContainer) {
+ for (let i = 0; i < children.length; i++) {
+ children[i].child.zIndex = children.length - i;
+ console.log("=====> ADD CHILD", i, children[i].child.name);
+ content.addChild(children[i].child);
+ }
+ } else {
+ for (let i = 0; i < children.length; i++) {
+ children[i].child.zIndex = children.length - i;
+ // Only update if child changed
+ if (children[i].changed) {
+ console.log("=====> UPDATE CHILD", i, children[i].child.name);
+ // const prevChild = content.removeChildAt(i) as PIXI.Container;
+ // console.log("OLD CHILD", prevChild.name, "NEW CHILD", children[i].child.name)
+ // content.addChildAt(children[i].child, i);
+ // children[i].child.addChild(prevChild.getChildAt(prevChild.children.length - 1));
+ }
+ }
+ }
+
+ if (false && !newContainer) {
+ // Remove redundant surplus children
+ if (prevClump?.elements) {
+ for (let i = clump.elements.length + 1; i < prevClump.elements.length; i++) {
+ if (prevClump.elements[i].class === "clump") {
+ const pClump = prevClump.elements[i] as HierarchyClump;
+ console.log("REMOVE CLUMP CHILD");
+ prevClump.container.removeChild(pClump.container);
+ } else if (prevClump.elements[i].class === "atom") {
+ const pAtom = prevClump.elements[i] as HierarchyAtom;
+ console.log("REMOVE ATOM CHILD");
+ prevClump.container.removeChild(pAtom.container);
+ }
+ }
+ }
}
content.sortChildren();
- //========== APPLY FILTERS ==========//
- if (clump.filters && clump.filters.length > 0) {
+ if (clump.filters && clump.filters.length > 0)
+ {
+ //===== FILTERS =====//
resClump.addChild(applyFilters(blink, content, clump.filters));
- } else {
+ }
+ else
+ {
//===== ADD CONTENT WITHOUT FLATTENING =====//
if (childChanged) {
- // resClump.removeChildren();
- resClump.addChild(content);
+ console.log("RESCLUMP CHILDREN", resClump.children);
+ // if (newContainer) {
+ // resClump.addChild(content);
+ // }
}
}
}
@@ -266,30 +292,40 @@ function renderClump(
const clumpBounds = resClump.getBounds();
//========== APPLY CLUMP PROPERTIES ==========//
- let transMatrix = PIXI.Matrix.IDENTITY;
- if (clump.transform) {
- const { position: pos, rotation: rot, scale: scl } = clump.transform;
+ if (diffs.has("transform")) {
+ console.log("UPDATE TRANSFORM", resClump.name.split("(")[1].slice(0, 4));
+ let transMatrix = PIXI.Matrix.IDENTITY;
+ if (clump.transform) {
+ const { position: pos, rotation: rot, scale: scl } = clump.transform;
+
+ if (scl) transMatrix.scale(scl.x, scl.y);
+ if (rot) transMatrix.rotate((rot * Math.PI) / 180);
+ if (pos) transMatrix.translate(pos.x, pos.y);
+ }
- if (scl) transMatrix.scale(scl.x, scl.y);
- if (rot) transMatrix.rotate((rot * Math.PI) / 180);
- if (pos) transMatrix.translate(pos.x, pos.y);
+ resClump.transform.setFromMatrix(transMatrix);
+ // const matTransform = resClumpContent.transform.worldTransform;
}
- resClump.transform.setFromMatrix(transMatrix);
- // const matTransform = resClumpContent.transform.worldTransform;
-
- if (clump.opacity) {
- resClump.alpha = Math.min(1, Math.max(0, clump.opacity / 100.0));
+ if (diffs.has("opacity")) {
+ if (clump.opacity) {
+ resClump.alpha = Math.min(1, Math.max(0, clump.opacity / 100.0));
+ }
}
resClump.sortChildren();
// boundingBox.addChild(createBoundingBox(transMatrix, resClump.getBounds(), viewport));
- // hierarchy.container = resClump;
+ if (prevClump != null) {
+ prevClump.container = resClump;
+ }
+ // console.log("--> RESULT", resClump, "\n\n--> CHILD CHANGED", childChanged, "\n\n--> DIFFS", diffs);
+
+ console.log("------------------------------------");
return {
pixiClump: resClump,
- changed: true,
+ changed: childChanged || diffs.size > 0,
};
}
@@ -328,21 +364,12 @@ function addInteractivity(container: PIXI.Container, clump: Clump, viewport: Vie
// boundingBox.addChild(createBoundingBox(container.transform.worldTransform, container.getBounds(), viewport));
const pos = viewport.toWorld(event.global);
+ // const pos = viewport.worldTransform.apply(event.global);
const shift = new PIXI.Point(pos.x - prevMousePos.x, pos.y - prevMousePos.y);
prevMousePos = pos;
container.transform.position.x += shift.x;
container.transform.position.y += shift.y;
-
- // send("tweak", {
- // nodeUUID: clump.nodeUUID,
- // inputs: {
- // // positionX: container.transform.position.x + shift.x,
- // // positionY: container.transform.position.y + shift.y
- // positionX: container.transform.position.x,
- // positionY: container.transform.position.y
- // },
- // });
}
});
From a627aed76040a29637c9f1956c583e3889f05877 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 16:05:55 +0200
Subject: [PATCH 057/209] Fix
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f6ac7559..07ac0c3e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,7 +25,7 @@ jobs:
- name: Build & Release Blix
run: npm run dist
env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_TOKEN: ${{ secrets.TEST_TOKEN }}
# - name: Build/release Electron app
# uses: samuelmeuli/action-electron-builder@v1
From 58a31a91ce8b821b314f7ce6be8ac8e2144b0f21 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 16:13:30 +0200
Subject: [PATCH 058/209] Update version
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 08d3ee6b..cba21191 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.1",
+ "version": "1.0.2",
"repository": {
"type": "git",
"url": "https://github.com/COS301-SE-2023/AI-Photo-Editor"
From 37ba60cc5e057ad953aa7224aeef5999a090c809 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Mon, 18 Sep 2023 16:52:14 +0200
Subject: [PATCH 059/209] Fix Blink filter bounds broken with dynamic viewport
on re-render
---
blix-plugins/blink/webview/filter.ts | 15 +++++++++++----
blix-plugins/blink/webview/render.ts | 24 ++++++++++++++++++------
2 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/blix-plugins/blink/webview/filter.ts b/blix-plugins/blink/webview/filter.ts
index b1b836d6..5e7aa3fb 100644
--- a/blix-plugins/blink/webview/filter.ts
+++ b/blix-plugins/blink/webview/filter.ts
@@ -1,5 +1,6 @@
import * as PIXI from 'pixi.js';
import { Filter, getPixiFilter } from './types';
+import { randomId } from './render';
// Apply a series of filters and flatten the result to a sprite.
// This is done so that the filters are applied evenly regardless of scaling.
@@ -8,14 +9,14 @@ export function applyFilters(blink: PIXI.Application, content: PIXI.Container, f
// blur that spread beyond the bounds of the sprite
const renderPadding = 100;
- // Apply filters
- content.filters = filters.map(getPixiFilter);
-
// Normalize offset to fit within renderTexture
- const { x: bx, y: by, width: bw, height: bh } = content.getBounds();
+ const { x: bx, y: by, width: bw, height: bh } = content.getLocalBounds();
content.transform.position.x = -bx + renderPadding;
content.transform.position.y = -by + renderPadding;
+ // Apply filters
+ content.filters = filters.map(getPixiFilter);
+
// Render to texture
const renderTexture = PIXI.RenderTexture.create({
width: bw + 2 * renderPadding,
@@ -23,8 +24,14 @@ export function applyFilters(blink: PIXI.Application, content: PIXI.Container, f
});
blink.renderer.render(content, { renderTexture: renderTexture });
+ content.filters = null;
+ content.transform.setFromMatrix(new PIXI.Matrix());
+
// Create flattened sprite
const renderSprite = new PIXI.Sprite(renderTexture);
+ renderSprite.name = `FilterSprite(${randomId()})`
+ // renderSprite.anchor.x = 0.5;
+ // renderSprite.anchor.y = 0.5;
renderSprite.setTransform(bx - renderPadding, by - renderPadding);
return renderSprite;
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index 3062462a..7486eb6e 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -225,10 +225,11 @@ function renderClump(
resClump.addChild(content);
} else { content = resClump.getChildAt(0) as PIXI.Container; }
+ content.visible = true;
console.log(`==> ${newContainer ? "" : "NO "}NEW RESCLUMP (${resClump.name.split("(")[1].slice(0, 4)})`);
- if (childChanged || diffs.has("filters")) {
+ if (childChanged || newContainer || diffs.has("filters")) {
//========== BUILD CONTENT ==========//
//Add children to content
@@ -271,25 +272,36 @@ function renderClump(
content.sortChildren();
- if (clump.filters && clump.filters.length > 0)
+ if ((diffs.has("filters") || childChanged) && clump.filters && clump.filters.length > 0)
{
//===== FILTERS =====//
- resClump.addChild(applyFilters(blink, content, clump.filters));
+ console.log("APPLY FILTER", content.name);
+ const filterSprite = applyFilters(blink, content, clump.filters);
+ // resClump.addChildAt(content, 0);
+ if (resClump.children.length > 1) {
+ resClump.removeChildAt(1);
+ }
+ resClump.addChildAt(filterSprite, 1);
}
else
{
//===== ADD CONTENT WITHOUT FLATTENING =====//
if (childChanged) {
console.log("RESCLUMP CHILDREN", resClump.children);
- // if (newContainer) {
- // resClump.addChild(content);
- // }
}
}
}
+ if (resClump.children.length > 1) {
+ // If we've applied a filter, hide the content
+ content.visible = false;
+ }
+
// Get bounds before applying transform
const clumpBounds = resClump.getBounds();
+ // TODO: Optimize with resClump._bounds.getRectangle() on children,
+ // to avoid recalculating all the way down the hierarchy
+ // Also: Look into .getLocalBounds() instead
//========== APPLY CLUMP PROPERTIES ==========//
if (diffs.has("transform")) {
From 42594df6a5e192aa092f9af3b630096c72bcfe41 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 18 Sep 2023 17:19:05 +0200
Subject: [PATCH 060/209] Update workflow
---
.github/workflows/build.yml | 77 +++----------------------------------
package.json | 2 +-
2 files changed, 6 insertions(+), 73 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 07ac0c3e..a33fd069 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,77 +22,10 @@ jobs:
- name: Install dependencies
run: npm ci
- - name: Build & Release Blix
+ - name: Build Blix Plugins
+ run: cd ./blix-plugins/glfx-plugin && npm ci && npm run build
+
+ - name: Build/Release Blix
run: npm run dist
env:
- GH_TOKEN: ${{ secrets.TEST_TOKEN }}
-
- # - name: Build/release Electron app
- # uses: samuelmeuli/action-electron-builder@v1
- # with:
- # github_token: ${{ secrets.github_token }}
-
- # # If the commit is tagged with a version (e.g. "v1.0.0"),
- # # release the app after building
- # # release: ${{ startsWith(github.ref, 'refs/tags/v') }}
- # release: true
-
-# name: Build Codebase
-
-# on:
-# push:
-# branches: [ master, dev ]
-# pull_request:
-# branches: [ master, dev ]
-# types: [opened, synchronize, reopened, ready_for_review]
-# permissions:
-# contents: read
-
-# jobs:
-# start:
-# name: Start State 🚀🚀🚀
-# runs-on: ubuntu-latest
-# steps:
-# - name: Starting
-# id: init
-# run: |
-# echo "Starting building of ${{ github.repository }}"
-
-# build_project:
-# name: Build on all platforms
-# runs-on: ${{ matrix.os }}
-# strategy:
-# matrix:
-# os: [ubuntu-latest, windows-latest, macOS-latest]
-# needs: start
-# steps:
-# - name: Checkout for ${{ runner.os }}
-# uses: actions/checkout@v3
-# - name: Set up Node 18
-# uses: actions/setup-node@v3
-# with:
-# node-version: 18
-
-# - name: Cache dependencies
-# uses: actions/cache@v2
-# with:
-# path: ~/.npm
-# key: npm-${{ hashFiles('package-lock.json') }}
-# restore-keys: npm-
-
-# - name: Install dependencies
-# run: npm ci
-# - name: Run tests and collect coverage
-# run: npm run test
-# - name: Build Project for ${{ runner.os }}
-# run: npm run build
-
-# end:
-# name: End State ✅✅✅
-# runs-on: ubuntu-latest
-# needs: build_project
-# steps:
-# - name: Ending
-# id: init
-# run: |
-# echo "Ending building of ${{ github.repository }}"
\ No newline at end of file
+ GH_TOKEN: ${{ secrets.TEST_TOKEN }}
\ No newline at end of file
diff --git a/package.json b/package.json
index cba21191..0e0067a7 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.2",
+ "version": "1.0.3",
"repository": {
"type": "git",
"url": "https://github.com/COS301-SE-2023/AI-Photo-Editor"
From b5583c6fde0c6bfecc23dc99ad03811d20a5fa0d Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Mon, 18 Sep 2023 17:39:49 +0200
Subject: [PATCH 061/209] Fix Blink shapes and add color pickers for
fill/stroke
---
blix-plugins/blink/src/main.cjs | 24 ++++++++++++--
blix-plugins/blink/webview/atom.ts | 12 ++++---
blix-plugins/blink/webview/diff.ts | 2 ++
blix-plugins/blink/webview/render.ts | 48 +++++++++-------------------
blix-plugins/blink/webview/types.ts | 12 +++++++
5 files changed, 58 insertions(+), 40 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 558f64ec..0e43d32b 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -4,6 +4,10 @@ function getUUID() {
return crypto.randomBytes(16).toString("base64url");
}
+function colorHexToAlpha(str) {
+ if (str.length <= 7) return 1.0;
+ return parseInt("0x" + str.slice(7, 9))/255.0;
+}
function colorHexToNumber(str) {
return parseInt(str.slice(0, 7).replace("#", "0x"));
}
@@ -285,6 +289,20 @@ const nodes = {
);
}
const getTransform = addTransformInput(ui, ["position", "rotation"]);
+
+ ui.addColorPicker({
+ componentId: "fill",
+ label: "Fill",
+ defaultValue: "#000000",
+ triggerUpdate: true,
+ }, {})
+ ui.addColorPicker({
+ componentId: "stroke",
+ label: "Stroke",
+ defaultValue: "#00000000",
+ triggerUpdate: true,
+ }, {})
+
addTweakability(ui);
nodeBuilder.define(async (input, uiInput, from) => {
@@ -308,8 +326,10 @@ const nodes = {
shape: uiInput["shape"],
bounds: { w: uiInput["width"], h: uiInput["height"] },
- fill: 0x0000ff,
- stroke: 0xff0000,
+ fill: colorHexToNumber(uiInput["fill"]),
+ fillAlpha: colorHexToAlpha(uiInput["fill"]),
+ stroke: colorHexToNumber(uiInput["stroke"]),
+ strokeAlpha: colorHexToAlpha(uiInput["stroke"]),
strokeWidth: 1,
}
]
diff --git a/blix-plugins/blink/webview/atom.ts b/blix-plugins/blink/webview/atom.ts
index da261294..5e25811c 100644
--- a/blix-plugins/blink/webview/atom.ts
+++ b/blix-plugins/blink/webview/atom.ts
@@ -35,13 +35,15 @@ export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key:
case "shape":
const shapeDiff = atomsDiffer || diffShapeAtom(atom, prevAtom as ShapeAtom);
- if (!shapeDiff) return { pixiAtom: prevAtom.container, changed: false };
+ if (!shapeDiff) {
+ return { pixiAtom: prevAtom.container, changed: false };
+ }
const shapeContainer = new PIXI.Container();
const shape = new PIXI.Graphics();
// shape.lineStyle(1, 0xf43e5c, 0.5);
- shape.beginFill(atom.fill, 1);
- shape.lineStyle(atom.strokeWidth, atom.stroke, 1);
+ shape.beginFill(atom.fill, atom.fillAlpha);
+ shape.lineStyle(atom.strokeWidth, atom.stroke, atom.strokeAlpha);
const halfBounds = { w: atom.bounds.w / 2, h: atom.bounds.h / 2 };
switch (atom.shape) {
@@ -49,10 +51,10 @@ export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key:
shape.drawRect(-halfBounds.w, -halfBounds.h, atom.bounds.w, atom.bounds.h);
break;
case "ellipse":
- shape.drawEllipse(-halfBounds.w, -halfBounds.h, atom.bounds.w, atom.bounds.h);
+ shape.drawEllipse(0, 0, halfBounds.w, halfBounds.h);
break;
case "triangle":
- shape.drawPolygon([0, 0, atom.bounds.w, 0, atom.bounds.w / 2, atom.bounds.h]);
+ shape.drawPolygon([-halfBounds.w, halfBounds.h, halfBounds.w, halfBounds.h, 0, -halfBounds.h]);
break;
}
shape.endFill();
diff --git a/blix-plugins/blink/webview/diff.ts b/blix-plugins/blink/webview/diff.ts
index 0bef98e9..49dd403d 100644
--- a/blix-plugins/blink/webview/diff.ts
+++ b/blix-plugins/blink/webview/diff.ts
@@ -109,7 +109,9 @@ export function diffShapeAtom(a1: ShapeAtom, a2: ShapeAtom) {
if (a1.shape !== a2.shape) return true;
if (a1.bounds?.w !== a2.bounds?.w || a1.bounds?.h !== a2.bounds?.h) return true;
if (a1.fill !== a2.fill) return true;
+ if (a1.fillAlpha !== a2.fillAlpha) return true;
if (a1.stroke !== a2.stroke) return true;
+ if (a1.strokeAlpha !== a2.strokeAlpha) return true;
if (a1.strokeWidth !== a2.strokeWidth) return true;
return false;
}
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index 7486eb6e..0382213c 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -12,7 +12,7 @@ import { Viewport } from "pixi-viewport";
import { createBoundingBox } from "./select";
import { renderAtom } from "./atom";
import { applyFilters } from "./filter";
-import { diffAtom, diffClump } from "./diff";
+import { diffClump } from "./diff";
export function randomId() {
return Math.random().toString(36).slice(2, 6);
@@ -105,17 +105,6 @@ export function renderApp(
if (hierarchy.content.container != null && changed) {
scene.addChild(hierarchy.content.container);
}
-
- //===============// DELETE DEAD CLUMPS //==============//
- // const newClumps = new Set(Object.keys(scene));
-
- // for (let nodeUUID in oldScene) {
- // if (!newClumps.has(nodeUUID)) {
- // oldScene[nodeUUID].destroy(); // PIXI.js cleanup
- // }
- // }
- // console.log("CLUMPS", clumps);
- // console.log("SCENE", scene);
});
return true;
@@ -233,41 +222,35 @@ function renderClump(
//========== BUILD CONTENT ==========//
//Add children to content
- if (newContainer) {
+ if (true || newContainer) {
+ content.removeChildren();
for (let i = 0; i < children.length; i++) {
children[i].child.zIndex = children.length - i;
console.log("=====> ADD CHILD", i, children[i].child.name);
content.addChild(children[i].child);
}
} else {
+ // TODO: Fix this so it only replaces changed children / removes excess
+ // Above is a temp fix that removes all children and adds everything back
+
for (let i = 0; i < children.length; i++) {
children[i].child.zIndex = children.length - i;
// Only update if child changed
if (children[i].changed) {
console.log("=====> UPDATE CHILD", i, children[i].child.name);
- // const prevChild = content.removeChildAt(i) as PIXI.Container;
- // console.log("OLD CHILD", prevChild.name, "NEW CHILD", children[i].child.name)
- // content.addChildAt(children[i].child, i);
- // children[i].child.addChild(prevChild.getChildAt(prevChild.children.length - 1));
+ // content.children[i] = children[i].child;
+ content.removeChildAt(i);
+ content.addChildAt(children[i].child, i);
+ // content.addChild(children[i].child);
}
}
- }
- if (false && !newContainer) {
// Remove redundant surplus children
- if (prevClump?.elements) {
- for (let i = clump.elements.length + 1; i < prevClump.elements.length; i++) {
- if (prevClump.elements[i].class === "clump") {
- const pClump = prevClump.elements[i] as HierarchyClump;
- console.log("REMOVE CLUMP CHILD");
- prevClump.container.removeChild(pClump.container);
- } else if (prevClump.elements[i].class === "atom") {
- const pAtom = prevClump.elements[i] as HierarchyAtom;
- console.log("REMOVE ATOM CHILD");
- prevClump.container.removeChild(pAtom.container);
- }
- }
- }
+ // content.children.splice(children.length, content.children.length - children.length);
+ // for (let i = content.children.length-1; i >= children.length; i--) {
+ // console.log("=====> REMOVE CHILD", i, content.children[i].name);
+ // content.removeChildAt(i);
+ // }
}
content.sortChildren();
@@ -332,7 +315,6 @@ function renderClump(
if (prevClump != null) {
prevClump.container = resClump;
}
- // console.log("--> RESULT", resClump, "\n\n--> CHILD CHANGED", childChanged, "\n\n--> DIFFS", diffs);
console.log("------------------------------------");
return {
diff --git a/blix-plugins/blink/webview/types.ts b/blix-plugins/blink/webview/types.ts
index fd23cf9c..dfe2d4b9 100644
--- a/blix-plugins/blink/webview/types.ts
+++ b/blix-plugins/blink/webview/types.ts
@@ -49,6 +49,7 @@ export type Filter = {
};
export function getPixiFilter(filter: Filter) {
+ try {
switch (filter.type) {
case "blur": return new PIXI.BlurFilter(...filter.params);
case "noise": return new PIXI.NoiseFilter(...filter.params);
@@ -85,6 +86,9 @@ export function getPixiFilter(filter: Filter) {
case "zoomblur": return new ZoomBlurFilter(...filter.params);
case "twist": return new TwistFilter(...filter.params);
}
+ } catch {
+ return new PIXI.Filter();
+ }
}
// A single indivisible unit of a clump (E.g. image, shape, text etc.)
@@ -106,7 +110,9 @@ export type ShapeAtom = {
bounds: { w: number, h: number };
fill: number;
+ fillAlpha: number;
stroke: number;
+ strokeAlpha: number;
strokeWidth: number;
};
export type TextAtom = {
@@ -114,7 +120,9 @@ export type TextAtom = {
text: string;
fill: number;
+ fillAlpha: number;
stroke: number;
+ strokeAlpha: number;
strokeWidth: number;
fontSize: number;
fontFamily: string;
@@ -172,7 +180,9 @@ export const canvas1: BlinkCanvas = {
bounds: { w: 100, h: 100 },
fill: 0xff0000,
+ fillAlpha: 1,
stroke: 0x00ff00,
+ strokeAlpha: 1,
strokeWidth: 5,
},
{
@@ -182,7 +192,9 @@ export const canvas1: BlinkCanvas = {
nodeUUID: "d",
fill: 0x0000ff,
+ fillAlpha: 1,
stroke: 0x00ff00,
+ strokeAlpha: 1,
strokeWidth: 5,
fontSize: 20,
fontFamily: "Arial",
From 1bcb6e2af809919ccf6cc0e523ed1528de47a6ac Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Mon, 18 Sep 2023 18:37:51 +0200
Subject: [PATCH 062/209] Add inputText node to Blink
---
blix-plugins/blink/src/main.cjs | 179 +++++++++++++++++++++++++++-
blix-plugins/blink/webview/atom.ts | 32 ++++-
blix-plugins/blink/webview/types.ts | 8 +-
3 files changed, 207 insertions(+), 12 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 0e43d32b..e4159aaa 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -295,13 +295,19 @@ const nodes = {
label: "Fill",
defaultValue: "#000000",
triggerUpdate: true,
- }, {})
+ }, {});
ui.addColorPicker({
componentId: "stroke",
label: "Stroke",
defaultValue: "#00000000",
triggerUpdate: true,
- }, {})
+ }, {});
+ ui.addSlider({
+ componentId: "strokeWidth",
+ label: "Stroke Width",
+ defaultValue: 0,
+ triggerUpdate: true,
+ }, { min: 0, max: 100, set: 0.1 });
addTweakability(ui);
@@ -330,7 +336,174 @@ const nodes = {
fillAlpha: colorHexToAlpha(uiInput["fill"]),
stroke: colorHexToNumber(uiInput["stroke"]),
strokeAlpha: colorHexToAlpha(uiInput["stroke"]),
- strokeWidth: 1,
+ strokeWidth: uiInput["strokeWidth"],
+ }
+ ]
+ }
+ }
+
+ return { res: canvas };
+ });
+
+ nodeBuilder.setUI(ui);
+
+ nodeBuilder.addInput("Blink matrix", "transform", "Transform");
+ nodeBuilder.addOutput("Blink clump", "res", "Result");
+ },
+ "inputText": (context) => {
+ const nodeBuilder = context.instantiate(context.pluginId, "inputText");
+ nodeBuilder.setTitle("Blink Text");
+ nodeBuilder.setDescription("Input a Blink Text element");
+
+ const ui = nodeBuilder.createUIBuilder();
+ ui.addTextInput({
+ componentId: "text",
+ label: "Text",
+ defaultValue: "input text",
+ triggerUpdate: true,
+ }, {});
+
+ ui.addDropdown({
+ componentId: "fontFamily",
+ label: "Family",
+ defaultValue: "Arial",
+ triggerUpdate: true,
+ }, {
+ options: {
+ "Arial": "Arial",
+ "Consolas": "Consolas",
+ "Courier New": "Courier New",
+ "Georgia": "Georgia",
+ "Helvetica": "Helvetica",
+ "Impact": "Impact",
+ "Times New Roman": "Times New Roman",
+ "Trebuchet MS": "Trebuchet MS",
+ "Verdana": "Verdana",
+ }
+ });
+
+ ui.addNumberInput({
+ componentId: "fontSize",
+ label: "Size",
+ defaultValue: 24,
+ triggerUpdate: true,
+ }, { step: 0.2, min: 0 });
+
+ ui.addDropdown({
+ componentId: "fontStyle",
+ label: "Style",
+ defaultValue: "normal",
+ triggerUpdate: true,
+ }, {
+ options: {
+ "Normal": "normal",
+ "Italic": "italic",
+ }
+ });
+ ui.addDropdown({
+ componentId: "fontWeight",
+ label: "Weight",
+ defaultValue: "normal",
+ triggerUpdate: true,
+ }, {
+ options: {
+ "Normal": "normal",
+ "Bold": "bold",
+ }
+ });
+ ui.addDropdown({
+ componentId: "textAlign",
+ label: "Align",
+ defaultValue: "center",
+ triggerUpdate: true,
+ }, {
+ options: {
+ "Left": "left",
+ "Center": "center",
+ "Right": "right"
+ }
+ });
+ ui.addDropdown({
+ componentId: "textBaseline",
+ label: "Baseline",
+ defaultValue: "top",
+ triggerUpdate: true,
+ }, {
+ options: {
+ "Top": "top",
+ "Hanging": "hanging",
+ "Middle": "middle",
+ "Alphabetic": "alphabetic",
+ "Ideographic": "ideographic",
+ "Bottom": "bottom",
+ }
+ });
+ for (let numInp of ["width", "height"]) {
+ ui.addNumberInput(
+ {
+ componentId: numInp.replace(" ", ""),
+ label: numInp[0].toUpperCase() + numInp.slice(1),
+ defaultValue: 100,
+ triggerUpdate: true,
+ },
+ {}
+ );
+ }
+ const getTransform = addTransformInput(ui, ["position", "rotation"]);
+
+ ui.addColorPicker({
+ componentId: "fill",
+ label: "Fill",
+ defaultValue: "#000000",
+ triggerUpdate: true,
+ }, {});
+ ui.addColorPicker({
+ componentId: "stroke",
+ label: "Stroke",
+ defaultValue: "#00000000",
+ triggerUpdate: true,
+ }, {});
+ ui.addSlider({
+ componentId: "strokeWidth",
+ label: "Stroke Width",
+ defaultValue: 0,
+ triggerUpdate: true,
+ }, { min: 0, max: 100, set: 0.1 });
+
+ addTweakability(ui);
+
+ nodeBuilder.define(async (input, uiInput, from) => {
+ const canvas = {
+ assets: {
+ "1": {
+ class: "asset",
+ type: "image",
+ data: "media/bird.png",
+ },
+ },
+ content: {
+ class: "clump",
+ nodeUUID: uiInput["tweaks"].nodeUUID,
+ changes: uiInput["diffs"]?.uiInputs ?? [],
+ transform: getTransform(uiInput),
+ elements: [
+ {
+ class: "atom",
+ type: "text",
+
+ text: uiInput["text"],
+
+ fill: colorHexToNumber(uiInput["fill"]),
+ stroke: colorHexToNumber(uiInput["stroke"]),
+ strokeWidth: uiInput["strokeWidth"],
+ alpha: colorHexToAlpha(uiInput["fill"]),
+
+ fontSize: uiInput["fontSize"],
+ fontFamily: uiInput["fontFamily"],
+ fontStyle: uiInput["fontStyle"],
+ fontWeight: uiInput["fontWeight"],
+ textAlign: uiInput["textAlign"],
+ textBaseline: uiInput["textBaseline"],
}
]
}
diff --git a/blix-plugins/blink/webview/atom.ts b/blix-plugins/blink/webview/atom.ts
index 5e25811c..f0d51cd2 100644
--- a/blix-plugins/blink/webview/atom.ts
+++ b/blix-plugins/blink/webview/atom.ts
@@ -60,19 +60,43 @@ export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key:
shape.endFill();
shapeContainer.addChild(shape);
-
shapeContainer.name = `ShapeContainer(${randomId()})`;
+
return { pixiAtom: shapeContainer, changed: true };
case "text":
const textDiff = atomsDiffer || diffTextAtom(atom, prevAtom as TextAtom);
- if (!textDiff) return { pixiAtom: prevAtom.container, changed: false };
+ if (!textDiff) {
+ return { pixiAtom: prevAtom.container, changed: false };
+ }
- return { pixiAtom: null, changed: true };
+ const textContainer = new PIXI.Container();
+ const text = new PIXI.Text(atom.text, {
+ fill: atom.fill,
+ stroke: atom.stroke,
+ strokeThickness: atom.strokeWidth,
+
+ fontFamily: atom.fontFamily,
+ fontSize: atom.fontSize,
+ fontStyle: atom.fontStyle,
+ fontWeight: atom.fontWeight,
+
+ align: atom.textAlign,
+ textBaseline: atom.textBaseline,
+ });
+
+ textContainer.addChild(text);
+ textContainer.name = `TextContainer(${randomId()})`;
+
+ textContainer.alpha = atom.alpha;
+
+ return { pixiAtom: textContainer, changed: true };
case "paint":
const paintDiff = atomsDiffer || diffPaintAtom(atom, prevAtom as PaintAtom);
- if (!paintDiff) return { pixiAtom: prevAtom.container, changed: false };
+ if (!paintDiff) {
+ return { pixiAtom: prevAtom.container, changed: false };
+ }
return { pixiAtom: null, changed: true };
}
diff --git a/blix-plugins/blink/webview/types.ts b/blix-plugins/blink/webview/types.ts
index dfe2d4b9..581b123b 100644
--- a/blix-plugins/blink/webview/types.ts
+++ b/blix-plugins/blink/webview/types.ts
@@ -22,7 +22,7 @@ export type BlinkCanvas = {
export type Asset = {
class: "asset";
- type: "image" | "text" | "blob";
+ type: "image" | "blob";
data: any;
};
@@ -120,9 +120,8 @@ export type TextAtom = {
text: string;
fill: number;
- fillAlpha: number;
stroke: number;
- strokeAlpha: number;
+ alpha: number;
strokeWidth: number;
fontSize: number;
fontFamily: string;
@@ -192,9 +191,8 @@ export const canvas1: BlinkCanvas = {
nodeUUID: "d",
fill: 0x0000ff,
- fillAlpha: 1,
stroke: 0x00ff00,
- strokeAlpha: 1,
+ alpha: 1,
strokeWidth: 5,
fontSize: 20,
fontFamily: "Arial",
From f3c076a0e1c0f802379c4ecc539dc4d0573a31ce Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Mon, 18 Sep 2023 21:55:22 +0200
Subject: [PATCH 063/209] Add asset upload
---
src/electron/lib/cache/CacheManager.ts | 2 +-
src/frontend/lib/stores/CacheStore.ts | 4 ++
src/frontend/ui/tiles/Assets.svelte | 52 ++++++++++++++++++++++----
3 files changed, 49 insertions(+), 9 deletions(-)
diff --git a/src/electron/lib/cache/CacheManager.ts b/src/electron/lib/cache/CacheManager.ts
index 28c5b930..93b5d02c 100644
--- a/src/electron/lib/cache/CacheManager.ts
+++ b/src/electron/lib/cache/CacheManager.ts
@@ -13,6 +13,7 @@ import WebSocket from "ws";
import { randomBytes } from "crypto";
import logger from "../../utils/logger";
import { ipcMain } from "electron";
+import { showOpenDialog } from "../../utils/dialog";
// The main interface which this manager must expose is:
// - get(cacheUUID: CacheUUID): CacheObject
@@ -75,7 +76,6 @@ export class CacheManager {
JSON.stringify({ type: "cache-update", cache: Object.keys(this.cache) })
);
}
-
break;
case "cache-subscribe":
this.listeners.add(socket);
diff --git a/src/frontend/lib/stores/CacheStore.ts b/src/frontend/lib/stores/CacheStore.ts
index e564b660..798b2150 100644
--- a/src/frontend/lib/stores/CacheStore.ts
+++ b/src/frontend/lib/stores/CacheStore.ts
@@ -44,6 +44,10 @@ class CacheStore {
});
}
+ public addCacheObject(Blob: Blob) {
+ this.ws.send(Blob);
+ }
+
public get subscribe() {
return this.cacheStore.subscribe;
}
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index 68990632..363a5cb2 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -1,16 +1,52 @@
-
-
- {#each $cacheStore as id}
-
- {id}
-
- {/each}
-
+
+
+
+ {#each $cacheStore as id}
+
+ {id}
+
+ {/each}
+
+
+
+
From b67bc9a9f5fd6037018afecff5428aaed2efb5a8 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 07:13:17 +0200
Subject: [PATCH 064/209] Attempt to fix jest type complaints
---
jest.config.json | 16 +++++++++++-----
.../integration-tests/media/mediaManager.spec.ts | 5 ++++-
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/jest.config.json b/jest.config.json
index f780db5a..24c01e15 100644
--- a/jest.config.json
+++ b/jest.config.json
@@ -1,5 +1,5 @@
{
- "collectCoverage" : true,
+ "collectCoverage": true,
"transform": {
"^.+\\.svelte$": [
"svelte-jester",
@@ -7,12 +7,18 @@
"preprocess": true
}
],
- "^.+\\.ts$": "ts-jest",
+ "^.+\\.ts$": [
+ "ts-jest",
+ {
+ "diagnostics": false
+ }
+ ],
"^.+\\.js$": "babel-jest"
},
- "coveragePathIgnorePatterns" : [ "blix-plugins" ],
+ "coveragePathIgnorePatterns": ["blix-plugins"],
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node", "svelte"],
"coverageDirectory": "coverage",
- "testPathIgnorePatterns": ["\bbuild\b","e2e"]
-}
+ "testPathIgnorePatterns": ["\bbuild\b", "e2e"],
+ "preset": "ts-jest/presets/js-with-babel"
+}
\ No newline at end of file
diff --git a/tests/integration-tests/media/mediaManager.spec.ts b/tests/integration-tests/media/mediaManager.spec.ts
index 87cbab23..ce87807a 100644
--- a/tests/integration-tests/media/mediaManager.spec.ts
+++ b/tests/integration-tests/media/mediaManager.spec.ts
@@ -13,6 +13,7 @@ import type { MediaOutput } from "../../../src/shared/types/media";
import { CoreGraphUpdateParticipant } from "../../../src/electron/lib/core-graph/CoreGraphInteractors";
import { MediaSubscriber } from "../../../src/electron/lib/media/MediaSubscribers";
import { measureMemory } from "vm";
+import { TypeclassRegistry } from "../../../src/electron/lib/registries/TypeclassRegistry";
jest.mock('@electron/remote', () => ({ exec: jest.fn() }));
@@ -92,11 +93,13 @@ describe("Test graph importer", () => {
let mediaManager: MediaManager;
let blix: Blix;
+ let typeRegistry: TypeclassRegistry
beforeEach(async() => {
blix = new Blix();
await blix.init(mainWindow);
- mediaManager = new MediaManager(mainWindow, blix.graphInterpreter, blix.graphManager);
+ typeRegistry = new TypeclassRegistry(blix);
+ mediaManager = new MediaManager(typeRegistry, blix.graphInterpreter, blix.graphManager);
});
test("Media manager should be defined", () => {
From fb908adad6d49ee19930ee92370186970ce2ea6f Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 10:49:34 +0200
Subject: [PATCH 065/209] Add check for updates command
---
package.json | 2 +-
src/electron/lib/BlixCommands.ts | 27 ++++++++++++++++++++++++++-
src/index.ts | 3 ++-
3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/package.json b/package.json
index 0e0067a7..d8ccb003 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.3",
+ "version": "1.0.4",
"repository": {
"type": "git",
"url": "https://github.com/COS301-SE-2023/AI-Photo-Editor"
diff --git a/src/electron/lib/BlixCommands.ts b/src/electron/lib/BlixCommands.ts
index bc1d3cbb..e6252872 100644
--- a/src/electron/lib/BlixCommands.ts
+++ b/src/electron/lib/BlixCommands.ts
@@ -1,9 +1,34 @@
import { projectCommands } from "./projects/ProjectCommands";
import { coreGraphCommands } from "./core-graph/CoreGraphCommands";
-import { type Command } from "./registries/CommandRegistry";
+import { type CommandContext, type Command } from "./registries/CommandRegistry";
import { pluginCommands } from "./plugins/pluginCommands";
+import { autoUpdater } from "electron-updater";
+import logger from "../utils/logger";
+
+const checkForUpdatesCommand: Command = {
+ id: "blix.checkForUpdates",
+ description: {
+ name: "Check for updates...",
+ description: "Checks for updates to Blix",
+ },
+ handler: async (ctx: CommandContext) => {
+ try {
+ const response = await autoUpdater.checkForUpdatesAndNotify();
+ if (response) {
+ return { status: "success" };
+ } else {
+ ctx.sendInformationMessage("You are up to date!");
+ return { status: "success", message: "No updates available" };
+ }
+ } catch (error) {
+ logger.error(JSON.stringify(error));
+ return { status: "error", message: "Error checking for updates" };
+ }
+ },
+};
export const blixCommands: Command[] = [
+ checkForUpdatesCommand,
...projectCommands,
...coreGraphCommands,
...pluginCommands,
diff --git a/src/index.ts b/src/index.ts
index 360bf031..ab082bfd 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -260,10 +260,11 @@ app.on("web-contents-created", (e, contents) => {
// ========== AUTO UPDATER ==========//
-if (isProd)
+if (isProd) {
autoUpdater.checkForUpdates().catch((err) => {
logger.error(JSON.stringify(err));
});
+}
autoUpdater.logger = logger;
From fbad78d343837790de96af8ceda0acb63a258c96 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 11:33:21 +0200
Subject: [PATCH 066/209] Add some auto update fixes
---
package.json | 4 ++--
src/index.ts | 16 +++-------------
2 files changed, 5 insertions(+), 15 deletions(-)
diff --git a/package.json b/package.json
index d8ccb003..e66f55d8 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,10 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.4",
+ "version": "1.0.5",
"repository": {
"type": "git",
- "url": "https://github.com/COS301-SE-2023/AI-Photo-Editor"
+ "url": "https://github.com/ArmandKrynauw/Blix"
},
"author": {
"name": "The Spanish Inquisition",
diff --git a/src/index.ts b/src/index.ts
index ab082bfd..beb51094 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -284,13 +284,7 @@ autoUpdater.on("update-available", () => {
});
autoUpdater.on("update-not-available", () => {
- notification = new Notification({
- title: "Blix",
- body: "Your software is up to date.",
- silent: true,
- // icon: nativeImage.createFromPath(join(__dirname, "..", "assets", "icon.png"),
- });
- notification.show();
+ blix?.sendInformationMessage("You are up to date!");
});
autoUpdater.on("update-downloaded", () => {
@@ -307,12 +301,8 @@ autoUpdater.on("update-downloaded", () => {
});
autoUpdater.on("error", (err) => {
- notification = new Notification({
- title: "Blix",
- body: JSON.stringify(err),
- // icon: nativeImage.createFromPath(join(__dirname, "..", "assets", "icon.png"),
- });
- notification.show();
+ blix?.sendErrorMessage("Error checking for updates.");
+ logger.error(JSON.stringify(err));
});
// Menu
From 5583292e423ba02c049ad327f3f35166f1206af8 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 14:38:27 +0200
Subject: [PATCH 067/209] Update workflow
---
.github/workflows/build.yml | 11 +++++++++--
package.json | 4 ++--
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a33fd069..eca5b4d9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
- os: [ubuntu-latest, windows-latest]
+ os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Check out Git repository
@@ -26,6 +26,13 @@ jobs:
run: cd ./blix-plugins/glfx-plugin && npm ci && npm run build
- name: Build/Release Blix
- run: npm run dist
+ run: |
+ if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
+ npm run dist -- --linux deb
+ elif [ "${{ matrix.os }}" = "windows-latest" ]; then
+ npm run dist -- --windows nsis
+ elif [ "${{ matrix.os }}" = "macos-latest' ]; then
+ echo "macOS build not supported yet"
+ fi
env:
GH_TOKEN: ${{ secrets.TEST_TOKEN }}
\ No newline at end of file
diff --git a/package.json b/package.json
index e66f55d8..0eeeb495 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,10 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.5",
+ "version": "1.0.6",
"repository": {
"type": "git",
- "url": "https://github.com/ArmandKrynauw/Blix"
+ "url": "https://github.com/ArmandKrynauw/Blix.git"
},
"author": {
"name": "The Spanish Inquisition",
From c15e5c6f0ee288b46888dafca2d615b6e79d3fc8 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 14:44:38 +0200
Subject: [PATCH 068/209] Update workflow
---
.github/workflows/build.yml | 2 +-
package.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index eca5b4d9..1471a25c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,7 +31,7 @@ jobs:
npm run dist -- --linux deb
elif [ "${{ matrix.os }}" = "windows-latest" ]; then
npm run dist -- --windows nsis
- elif [ "${{ matrix.os }}" = "macos-latest' ]; then
+ elif [ "${{ matrix.os }}" = "macos-latest" ]; then
echo "macOS build not supported yet"
fi
env:
diff --git a/package.json b/package.json
index 0eeeb495..c709eafd 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.6",
+ "version": "1.0.7",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
From 21ae588278b56400f9e08eac2a8aebab532c3773 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 15:00:12 +0200
Subject: [PATCH 069/209] Update workflow
---
.github/workflows/build.yml | 3 ++-
package.json | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1471a25c..28ae4a1b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -35,4 +35,5 @@ jobs:
echo "macOS build not supported yet"
fi
env:
- GH_TOKEN: ${{ secrets.TEST_TOKEN }}
\ No newline at end of file
+ GH_TOKEN: ${{ secrets.TEST_TOKEN }}
+ shell: bash
\ No newline at end of file
diff --git a/package.json b/package.json
index c709eafd..bcbe6ad2 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.7",
+ "version": "1.0.8",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
From 0e4cf5a6525b1ad90a7717f5cf114dbfc31387f9 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 15:10:31 +0200
Subject: [PATCH 070/209] Update version
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index bcbe6ad2..48c3aff0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.8",
+ "version": "1.0.9",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
From b06ca9be752848e32f3cfd6cdb82b98daa4f9c2c Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 15:18:18 +0200
Subject: [PATCH 071/209] Update workflow
---
.github/workflows/build.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 28ae4a1b..1471a25c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -35,5 +35,4 @@ jobs:
echo "macOS build not supported yet"
fi
env:
- GH_TOKEN: ${{ secrets.TEST_TOKEN }}
- shell: bash
\ No newline at end of file
+ GH_TOKEN: ${{ secrets.TEST_TOKEN }}
\ No newline at end of file
From 9b774c5ee5ec7fb04ec802d413c0bfec21ab22df Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 15:58:41 +0200
Subject: [PATCH 072/209] Update workflow
---
.github/workflows/build.yml | 6 +++---
package.json | 15 ++++-----------
2 files changed, 7 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1471a25c..42344529 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,7 +3,7 @@ name: Build/release
on: push
jobs:
- release:
+ Build:
runs-on: ${{ matrix.os }}
strategy:
@@ -28,11 +28,11 @@ jobs:
- name: Build/Release Blix
run: |
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
- npm run dist -- --linux deb
+ npm run dist -- --linux deb AppImage --x64 --arm64
elif [ "${{ matrix.os }}" = "windows-latest" ]; then
npm run dist -- --windows nsis
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
- echo "macOS build not supported yet"
+ npm run dist -- --mac dmg --x64 --arm64
fi
env:
GH_TOKEN: ${{ secrets.TEST_TOKEN }}
\ No newline at end of file
diff --git a/package.json b/package.json
index 48c3aff0..b524f9de 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
- "description": "Blix - A cross-platform AI-assisted graph photo editor",
- "version": "1.0.9",
+ "description": "Blix - An AI-assisted graph photo editor",
+ "version": "1.0.10",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
@@ -168,7 +168,7 @@
"build": {
"productName": "Blix",
"appId": "com.the-spanish-inquisition.blix",
- "copyright": "Copyright © 2023 The Spanish Inquisition",
+ "copyright": "Copyright © 2023 Blix",
"win": {
"target": [
"nsis"
@@ -195,14 +195,7 @@
],
"extraResources": [
"assets/**",
- "blix-plugins/**",
- {
- "from": "src/electron/lib/ai/python",
- "to": "python",
- "filter": [
- "**/*.py"
- ]
- }
+ "blix-plugins/**"
],
"publish": [
{
From fbf3dd22bfd441d41ac685e2911fa59a4c84ddfe Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Tue, 19 Sep 2023 15:59:41 +0200
Subject: [PATCH 073/209] Fix FilePicker loses state on tile split; Add neater
debug output for Blink
---
blix-plugins/blink/src/main.cjs | 44 +------------
blix-plugins/blink/webview/App.svelte | 22 ++++++-
blix-plugins/blink/webview/Debug.svelte | 65 +++++++++++++++++++
src/electron/lib/projects/ProjectManager.ts | 1 +
src/frontend/ui/tiles/WebView.svelte | 32 +++++----
src/frontend/ui/utils/graph/PluginNode.svelte | 2 +-
.../nodeUICcomponents/ColorPicker.svelte | 13 +---
.../graph/nodeUICcomponents/FilePicker.svelte | 35 ++++++++--
.../graph/nodeUICcomponents/TextInput.svelte | 11 +++-
.../graph/nodeUICcomponents/dials/Dial.svelte | 6 +-
10 files changed, 150 insertions(+), 81 deletions(-)
create mode 100644 blix-plugins/blink/webview/Debug.svelte
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index e4159aaa..4a3a8082 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -313,13 +313,7 @@ const nodes = {
nodeBuilder.define(async (input, uiInput, from) => {
const canvas = {
- assets: {
- "1": {
- class: "asset",
- type: "image",
- data: "media/bird.png",
- },
- },
+ assets: {},
content: {
class: "clump",
nodeUUID: uiInput["tweaks"].nodeUUID,
@@ -423,32 +417,6 @@ const nodes = {
"Right": "right"
}
});
- ui.addDropdown({
- componentId: "textBaseline",
- label: "Baseline",
- defaultValue: "top",
- triggerUpdate: true,
- }, {
- options: {
- "Top": "top",
- "Hanging": "hanging",
- "Middle": "middle",
- "Alphabetic": "alphabetic",
- "Ideographic": "ideographic",
- "Bottom": "bottom",
- }
- });
- for (let numInp of ["width", "height"]) {
- ui.addNumberInput(
- {
- componentId: numInp.replace(" ", ""),
- label: numInp[0].toUpperCase() + numInp.slice(1),
- defaultValue: 100,
- triggerUpdate: true,
- },
- {}
- );
- }
const getTransform = addTransformInput(ui, ["position", "rotation"]);
ui.addColorPicker({
@@ -474,13 +442,7 @@ const nodes = {
nodeBuilder.define(async (input, uiInput, from) => {
const canvas = {
- assets: {
- "1": {
- class: "asset",
- type: "image",
- data: "media/bird.png",
- },
- },
+ assets: {},
content: {
class: "clump",
nodeUUID: uiInput["tweaks"].nodeUUID,
@@ -503,7 +465,7 @@ const nodes = {
fontStyle: uiInput["fontStyle"],
fontWeight: uiInput["fontWeight"],
textAlign: uiInput["textAlign"],
- textBaseline: uiInput["textBaseline"],
+ textBaseline: "alphabetic"
}
]
}
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index b770cee4..e33382bb 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -5,6 +5,7 @@
import { type Writable } from "svelte/store";
import { renderApp } from "./render";
import { type BlinkCanvas } from "./types";
+ import Debug from "./Debug.svelte";
export let media: Writable;
export let send: (msg: string, data: any) => void;
@@ -84,10 +85,24 @@
let imgCanvas = new PIXI.Container();
imgCanvas.name = "imgCanvas";
+ // canvas
let imgCanvasBlock = new PIXI.Graphics();
imgCanvasBlock.beginFill(0xffffff, 0.9);
imgCanvasBlock.drawRect(0, 0, imgCanvasBlockW, imgCanvasBlockH);
+ // x-axis
+ imgCanvasBlock.lineStyle();
+ imgCanvasBlock.moveTo(0, imgCanvasBlockH/2);
+ imgCanvasBlock.lineStyle(1, 0xff0000);
+ imgCanvasBlock.lineTo(imgCanvasBlockW, imgCanvasBlockH/2);
+
+ // y-axis
+ imgCanvasBlock.lineStyle();
+ imgCanvasBlock.moveTo(imgCanvasBlockW/2, 0);
+ imgCanvasBlock.lineStyle(1, 0x00ff00);
+ imgCanvasBlock.lineTo(imgCanvasBlockW/2, imgCanvasBlockH);
+
+ imgCanvasBlock.position.set(-imgCanvasBlockW/2, -imgCanvasBlockH/2);
imgCanvas.addChild(imgCanvasBlock);
viewport.addChild(imgCanvas);
@@ -110,7 +125,7 @@
const viewportFitX = imgCanvasBlockW + 2 * imgCanvasInitialPadding;
const viewportFitY = imgCanvasBlockH + 2 * imgCanvasInitialPadding;
viewport.fit(true, viewportFitX, viewportFitY);
- viewport.moveCenter(imgCanvasBlockW/2, imgCanvasBlockH/2);
+ viewport.moveCenter(0, 0);
hasCentered = true;
}
@@ -147,7 +162,8 @@
- {JSON.stringify($media, null, 2)}
+
+
diff --git a/src/electron/lib/projects/ProjectManager.ts b/src/electron/lib/projects/ProjectManager.ts
index 0fa058d6..03db8d6a 100644
--- a/src/electron/lib/projects/ProjectManager.ts
+++ b/src/electron/lib/projects/ProjectManager.ts
@@ -59,6 +59,7 @@ export class ProjectManager {
public async removeProject(blix: Blix, uuid: UUID, forceRemove = false) {
const project = this._projects[uuid];
+ if (!project) forceRemove = true; // Project does not exist, just obliterate the tab
let remove = true;
let output = -1;
let res;
diff --git a/src/frontend/ui/tiles/WebView.svelte b/src/frontend/ui/tiles/WebView.svelte
index a3149c63..cfa34197 100644
--- a/src/frontend/ui/tiles/WebView.svelte
+++ b/src/frontend/ui/tiles/WebView.svelte
@@ -13,13 +13,14 @@
$: webviewUpdated(webview);
+ let webviewReady = false;
+
// Called when the webview is created/recreated
function webviewUpdated(webview: Electron.WebviewTag | null) {
if (!webview) return;
// To receive a message from the webview, add an event listener for the ipc-message event:
webview?.addEventListener("ipc-message", (event) => {
- // console.log("Message received from webview:", event.channel, event.args);
switch (event.channel) {
case "tweak":
const data = event.args[0];
@@ -38,6 +39,7 @@
$: updateMedia(media);
function updateMedia(media: unknown) {
+ if (!webviewReady) return;
// To manually execute a javascript function within the webview:
// const res = await webview?.executeJavaScript("app.dispatchMessage('hi there!')");
@@ -49,19 +51,21 @@
$: initWebviewMedia(webview);
function initWebviewMedia(webview: Electron.WebviewTag | null) {
- if (webview) {
- console.log("WEBVIEW");
- webview.addEventListener("dom-ready", () => {
- updateMedia(media);
- });
-
- // Force focus the webview when the mouse is over it.
- // This is necessary to prevent the user having to
- // click into it every time before it can receive input
- webview.addEventListener("mouseover", () => {
- webview.focus();
- });
- }
+ webviewReady = false;
+ if (!webview) return;
+
+ // console.log("WEBVIEW");
+ webview.addEventListener("dom-ready", () => {
+ webviewReady = true;
+ updateMedia(media);
+ });
+
+ // Force focus the webview when the mouse is over it.
+ // This is necessary to prevent the user having to
+ // click into it every time before it can receive input
+ webview.addEventListener("mouseover", () => {
+ webview.focus();
+ });
}
function reload() {
diff --git a/src/frontend/ui/utils/graph/PluginNode.svelte b/src/frontend/ui/utils/graph/PluginNode.svelte
index 351f4842..6d39d392 100644
--- a/src/frontend/ui/utils/graph/PluginNode.svelte
+++ b/src/frontend/ui/utils/graph/PluginNode.svelte
@@ -206,7 +206,7 @@ height="{graphNode.dims.h}" -->
.node {
box-sizing: border-box;
- width: fit-content;
+ min-width: max-content;
border-radius: 8px;
height: fit-content;
position: relative;
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
index 6a337e6b..314b40b1 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/ColorPicker.svelte
@@ -13,17 +13,7 @@
if (!inputStore.inputs[config.componentId])
inputStore.inputs[config.componentId] = writable("#f43e5cff");
- console.log("CONFIG", config);
- console.log("INPUTS", inputStore.inputs);
-
$: valStore = inputStore.inputs[config.componentId];
-
- setTimeout(() => {
- // valStore = inputStore.inputs[config.componentId];
- console.log("VALSTORE", valStore, $valStore);
- }, 200);
-
- console.log("VALSTORE", valStore, $valStore);
@@ -31,9 +21,10 @@
{#if typeof $valStore === "string"}
+
-
+
+
+ {$valStore}
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
index 3b3bec0a..eaf583dc 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
@@ -15,13 +15,22 @@
$: valStore = inputStore.inputs[config.componentId];
-
+{config.label}
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/dials/Dial.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/dials/Dial.svelte
index 0fd7e8e8..becce0b8 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/dials/Dial.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/dials/Dial.svelte
@@ -22,9 +22,9 @@
$: valStore = inputStore.inputs[config.componentId];
onMount(() => {
- valStore.subscribe((v) => {
- console.log("DIAL", v);
- });
+ // valStore.subscribe((v) => {
+ // console.log("DIAL", v);
+ // });
});
let mouseover = false;
From c168758912984f0cfc72b898bde1a887967fbf65 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 16:03:15 +0200
Subject: [PATCH 074/209] Update workflow
---
.github/workflows/build.yml | 3 ++-
package.json | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 42344529..6589ec2e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -35,4 +35,5 @@ jobs:
npm run dist -- --mac dmg --x64 --arm64
fi
env:
- GH_TOKEN: ${{ secrets.TEST_TOKEN }}
\ No newline at end of file
+ GH_TOKEN: ${{ secrets.TEST_TOKEN }}
+ shell: bash
\ No newline at end of file
diff --git a/package.json b/package.json
index b524f9de..131f7b2c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - An AI-assisted graph photo editor",
- "version": "1.0.10",
+ "version": "1.0.11",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
From 2c4f63b37501d77c048716e2f255db60c69883d5 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 19 Sep 2023 19:07:09 +0200
Subject: [PATCH 075/209] Update check for updates message
---
package.json | 2 +-
src/electron/lib/BlixCommands.ts | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/package.json b/package.json
index 131f7b2c..2eff7961 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - An AI-assisted graph photo editor",
- "version": "1.0.11",
+ "version": "1.0.12",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
diff --git a/src/electron/lib/BlixCommands.ts b/src/electron/lib/BlixCommands.ts
index e6252872..354f5be7 100644
--- a/src/electron/lib/BlixCommands.ts
+++ b/src/electron/lib/BlixCommands.ts
@@ -17,8 +17,9 @@ const checkForUpdatesCommand: Command = {
if (response) {
return { status: "success" };
} else {
- ctx.sendInformationMessage("You are up to date!");
- return { status: "success", message: "No updates available" };
+ const version = JSON.stringify(autoUpdater.currentVersion);
+ ctx.sendInformationMessage(`You are up to date! Blix ${version} is the latest version.`);
+ return { status: "success" };
}
} catch (error) {
logger.error(JSON.stringify(error));
From 22127f27447fe121d277082e66d1b59cdf29b770 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Wed, 20 Sep 2023 12:00:59 +0200
Subject: [PATCH 076/209] Replace plugin signature in node.instantiate() with
ContextMenu folder path
---
blix-plugins/blink/src/main.cjs | 14 ++---
blix-plugins/glfx-plugin/src/main.cjs | 4 +-
blix-plugins/hello-plugin/src/main.ts | 4 +-
blix-plugins/input-plugin/src/main.js | 8 +--
blix-plugins/logic-plugin/src/main.js | 8 +--
blix-plugins/math-plugin/src/main.js | 4 +-
blix-plugins/sharp-plugin/src/main.js | 18 +++----
blix-plugins/threlte-plugin/src/main.cjs | 51 +++----------------
src/electron/lib/Blix.ts | 4 +-
src/electron/lib/ai/ai-profiler-v2.ts | 2 +-
src/electron/lib/plugins/Plugin.ts | 8 +--
.../lib/plugins/builders/NodeBuilder.ts | 5 +-
.../lib/registries/ToolboxRegistry.ts | 2 +
.../lib/stores/GraphContextMenuStore.ts | 51 +++++++++++++++----
src/shared/ui/ToolboxTypes.ts | 1 +
15 files changed, 92 insertions(+), 92 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 4a3a8082..87574b1a 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -67,7 +67,7 @@ function addTweakability(ui) {
function createBlinkNode(type, title, desc, params) {
return (context) => {
- const nodeBuilder = context.instantiate(context.pluginId, type);
+ const nodeBuilder = context.instantiate("Blink/Filters", type);
nodeBuilder.setTitle(title);
nodeBuilder.setDescription(desc);
@@ -190,7 +190,7 @@ const nodes = {
...blinkNodes,
"inputImage": (context) => {
- const nodeBuilder = context.instantiate(context.pluginId, "inputImage");
+ const nodeBuilder = context.instantiate(String.raw`Blink/Input`, "inputImage");
nodeBuilder.setTitle("Blink Image");
nodeBuilder.setDescription("Input a Blink Sprite Image");
@@ -260,7 +260,7 @@ const nodes = {
nodeBuilder.addOutput("Blink clump", "res", "Result");
},
"inputShape": (context) => {
- const nodeBuilder = context.instantiate(context.pluginId, "inputShape");
+ const nodeBuilder = context.instantiate("Blink/Input", "inputShape");
nodeBuilder.setTitle("Blink Shape");
nodeBuilder.setDescription("Input a Blink Shape");
@@ -345,7 +345,7 @@ const nodes = {
nodeBuilder.addOutput("Blink clump", "res", "Result");
},
"inputText": (context) => {
- const nodeBuilder = context.instantiate(context.pluginId, "inputText");
+ const nodeBuilder = context.instantiate("Blink/Input", "inputText");
nodeBuilder.setTitle("Blink Text");
nodeBuilder.setDescription("Input a Blink Text element");
@@ -480,7 +480,7 @@ const nodes = {
nodeBuilder.addOutput("Blink clump", "res", "Result");
},
"matrix": (context) => {
- const nodeBuilder = context.instantiate(context.pluginId, "matrix");
+ const nodeBuilder = context.instantiate("Blink/Input", "matrix");
nodeBuilder.setTitle("Matrix");
nodeBuilder.setDescription("Construct a Blink matrix");
@@ -508,7 +508,7 @@ const nodes = {
nodeBuilder.addOutput("Blink matrix", "res", "Result");
},
"layer": (context) => {
- const nodeBuilder = context.instantiate(context.pluginId, "layer");
+ const nodeBuilder = context.instantiate("Blink/Utils", "layer");
nodeBuilder.setTitle("Layer");
nodeBuilder.setDescription("Layer two or more Blink clumps");
@@ -559,7 +559,7 @@ const nodes = {
nodeBuilder.addOutput("Blink clump", "res", "Result");
},
"filter": (context) => {
- const nodeBuilder = context.instantiate(context.pluginId, "filter");
+ const nodeBuilder = context.instantiate("Blink/Utils", "filter");
nodeBuilder.setTitle("Filter");
nodeBuilder.setDescription("Construct a Blink matrix");
diff --git a/blix-plugins/glfx-plugin/src/main.cjs b/blix-plugins/glfx-plugin/src/main.cjs
index 27085c1e..983d1ccb 100644
--- a/blix-plugins/glfx-plugin/src/main.cjs
+++ b/blix-plugins/glfx-plugin/src/main.cjs
@@ -12,7 +12,7 @@ function toTitleCase(str) {
function createGLFXNode(type, title, desc, params) {
return (context) => {
- const nodeBuilder = context.instantiate("glfx-plugin", type);
+ const nodeBuilder = context.instantiate("GLFX", type);
nodeBuilder.setTitle(title);
nodeBuilder.setDescription(desc);
@@ -103,7 +103,7 @@ const nodes = {
...glfxNodes,
"inputGLFXImage": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputGLFXImage");
+ const nodeBuilder = context.instantiate("Input", "inputGLFXImage");
nodeBuilder.setTitle("Input GLFX image");
nodeBuilder.setDescription("Provides an image input and returns a single image output");
diff --git a/blix-plugins/hello-plugin/src/main.ts b/blix-plugins/hello-plugin/src/main.ts
index 6b654b8e..878ef377 100644
--- a/blix-plugins/hello-plugin/src/main.ts
+++ b/blix-plugins/hello-plugin/src/main.ts
@@ -13,7 +13,7 @@ import { NodeBuilder } from "electron/lib/plugins/builders/NodeBuilder";
const nodes = {
"hello": (context: NodePluginContext) => {
// Use context.nodeBuilder to construct the node UI
- const nodeBuilder: NodeBuilder = context.instantiate("hello-plugin", "hello");
+ const nodeBuilder: NodeBuilder = context.instantiate("Dev", "hello");
nodeBuilder.setTitle("Gloria");
nodeBuilder.setDescription("Provides a test slider and button and label for testing purposes, taking two string inputs and returning one string output");
@@ -128,7 +128,7 @@ const nodes = {
nodeBuilder.addOutput("string", "outFilePicker", "");
}
, "Jake": (context: any) => {
- const nodeBuilder = context.instantiate("hello-plugin", "Jake");
+ const nodeBuilder = context.instantiate("Dev", "Jake");
nodeBuilder.setTitle("Jake");
nodeBuilder.setDescription("This is currently a useless node that does nothing.");
diff --git a/blix-plugins/input-plugin/src/main.js b/blix-plugins/input-plugin/src/main.js
index 3f1dcb72..15fe2788 100644
--- a/blix-plugins/input-plugin/src/main.js
+++ b/blix-plugins/input-plugin/src/main.js
@@ -1,6 +1,6 @@
const nodes = {
"inputNumber": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputNumber");
+ const nodeBuilder = context.instantiate("Input", "inputNumber");
nodeBuilder.setTitle("Input number");
nodeBuilder.setDescription("Provides a number input and returns a single number output");
@@ -24,7 +24,7 @@ const nodes = {
nodeBuilder.addOutput("number", "res", "Result");
},
"inputImage": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputImage");
+ const nodeBuilder = context.instantiate("Input", "inputImage");
nodeBuilder.setTitle("Input image");
nodeBuilder.setDescription("Provides an image input and returns a single image output");
@@ -46,7 +46,7 @@ const nodes = {
},
// Will we define a color type? or just a vector4/string
"inputColor": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputColor");
+ const nodeBuilder = context.instantiate("Input", "inputColor");
nodeBuilder.setTitle("Input color");
nodeBuilder.setDescription("Provides a color input and returns a single color output");
@@ -67,7 +67,7 @@ const nodes = {
},
// Will we define a color type? or just a vector4/string
"inputBoolean": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputBoolean");
+ const nodeBuilder = context.instantiate("Input", "inputBoolean");
nodeBuilder.setTitle("Input Boolean");
nodeBuilder.setDescription("Provides a radio box to select a single true/false value");
diff --git a/blix-plugins/logic-plugin/src/main.js b/blix-plugins/logic-plugin/src/main.js
index 0fc983cf..6cc6e07c 100644
--- a/blix-plugins/logic-plugin/src/main.js
+++ b/blix-plugins/logic-plugin/src/main.js
@@ -1,6 +1,6 @@
const nodes ={
"not": (context) => {
- nodeBuilder = context.instantiate("logic-plugin","not");
+ nodeBuilder = context.instantiate("Logic","not");
nodeBuilder.setTitle("Not");
nodeBuilder.setDescription("Performs Boolean NOT operation on a single input and returns a single output");
@@ -14,7 +14,7 @@ const nodes ={
nodeBuilder.addOutput("boolean", "res","Result");
},
"logic": (context) => {
- const nodeBuilder = context.instantiate("logic-plugin", "logic");
+ const nodeBuilder = context.instantiate("Logic", "logic");
nodeBuilder.setTitle("Logic");
nodeBuilder.setDescription("Performs a logical operation (AND/OR/XOR) on two inputs and returns a single output");
@@ -50,7 +50,7 @@ const nodes ={
nodeBuilder.addOutput("boolean", "res", "Result");
},
"compare": (context) => {
- const nodeBuilder = context.instantiate("logic-plugin", "compare");
+ const nodeBuilder = context.instantiate("Logic", "compare");
nodeBuilder.setTitle("Compare");
nodeBuilder.setDescription("Performs a comparison operation (==/!=/>/>=/<=) on two inputs and returns a single boolean output");
@@ -91,7 +91,7 @@ const nodes ={
nodeBuilder.addOutput("boolean", "res", "Result");
},
"ternary": (context) => {
- const nodeBuilder = context.instantiate("logic-plugin","ternary");
+ const nodeBuilder = context.instantiate("Logic","ternary");
nodeBuilder.setTitle("Ternary Comp.");
nodeBuilder.define((anchorInputs, uiInputs, requiredOutputs) => {
diff --git a/blix-plugins/math-plugin/src/main.js b/blix-plugins/math-plugin/src/main.js
index bcf67597..13f5adcf 100644
--- a/blix-plugins/math-plugin/src/main.js
+++ b/blix-plugins/math-plugin/src/main.js
@@ -1,6 +1,6 @@
const nodes = {
"unary": (context) => {
- const nodeBuilder = context.instantiate("math-plugin","unary");
+ const nodeBuilder = context.instantiate("Math","unary");
nodeBuilder.setTitle("Unary");
nodeBuilder.setDescription("Performs Unary math operations taking one number input and returning one number output,such as root, negate or square");
@@ -44,7 +44,7 @@ const nodes = {
nodeBuilder.addOutput("number", "res","Result");
},
"binary": (context) => {
- const nodeBuilder = context.instantiate("math-plugin", "binary");
+ const nodeBuilder = context.instantiate("Math", "binary");
nodeBuilder.setTitle("Binary");
nodeBuilder.setDescription("Performs Binary math operations taking two number inputs and returning one number output");
diff --git a/blix-plugins/sharp-plugin/src/main.js b/blix-plugins/sharp-plugin/src/main.js
index 0dedddff..3de8d874 100644
--- a/blix-plugins/sharp-plugin/src/main.js
+++ b/blix-plugins/sharp-plugin/src/main.js
@@ -3,7 +3,7 @@ const sharp = require(isProd ? "../../../app.asar/node_modules/sharp" : "sharp")
const nodes = {
"brightness": (context) => {
- nodeBuilder = context.instantiate("sharp-plugin", "brightness");
+ nodeBuilder = context.instantiate("Sharp", "brightness");
nodeBuilder.setTitle("Brightness");
nodeBuilder.setDescription("Adjusts the brighness of an image taking one image as input and returning one image as output");
@@ -35,7 +35,7 @@ const nodes = {
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"saturation": (context) => {
- const nodeBuilder = context.instantiate("sharp-plugin", "saturation");
+ const nodeBuilder = context.instantiate("Sharp", "saturation");
nodeBuilder.setTitle("Saturation");
nodeBuilder.setDescription("Adjusts the saturation of an image taking one image as input and returning one image as output");
@@ -66,7 +66,7 @@ const nodes = {
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"hue": (context) => {
- const nodeBuilder = context.instantiate("sharp-plugin", "hue");
+ const nodeBuilder = context.instantiate("Sharp", "hue");
nodeBuilder.setTitle("Hue");
nodeBuilder.setDescription("Adjusts the hue of an image taking one image as input and returning one image as output");
const ui = nodeBuilder.createUIBuilder();
@@ -95,7 +95,7 @@ const nodes = {
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"rotate": (context) => {
- const nodeBuilder = context.instantiate("sharp-plugin", "rotate");
+ const nodeBuilder = context.instantiate("Sharp", "rotate");
nodeBuilder.setTitle("Rotate");
nodeBuilder.setDescription("Rotates an image by an explicit angle taking one image as input and returning one image as output");
const ui = nodeBuilder.createUIBuilder();
@@ -122,7 +122,7 @@ const nodes = {
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"sharpen": (context) => {
- const nodeBuilder = context.instantiate("sharp-plugin", "sharpen");
+ const nodeBuilder = context.instantiate("Sharp", "sharpen");
nodeBuilder.setTitle("Sharpen");
nodeBuilder.setDescription("Sharpens an image taking one image as input and returning one image as output");
const ui = nodeBuilder.createUIBuilder();
@@ -160,7 +160,7 @@ const nodes = {
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"normalise": (context) => {
- const nodeBuilder = context.instantiate("sharp-plugin", "normalise");
+ const nodeBuilder = context.instantiate("Sharp", "normalise");
nodeBuilder.setTitle("Normalise");
nodeBuilder.setDescription("Enhance image contrast by stretching its luminance to cover a full dynamic range taking one image as input and returning one image as output");
@@ -175,7 +175,7 @@ const nodes = {
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"toImage": (context) => {
- const nodeBuilder = context.instantiate("sharp-plugin", "toImage");
+ const nodeBuilder = context.instantiate("Sharp", "toImage");
nodeBuilder.setTitle("To Image");
nodeBuilder.setDescription("Converts the sharp object to an image");
@@ -189,7 +189,7 @@ const nodes = {
nodeBuilder.addOutput("image", "res", "Result");
},
"toSharp": (context) => {
- const nodeBuilder = context.instantiate("sharp-plugin", "toSharp");
+ const nodeBuilder = context.instantiate("Sharp", "toSharp");
nodeBuilder.setTitle("To Sharp");
nodeBuilder.setDescription("Converts an image path to a sharp object");
@@ -202,7 +202,7 @@ const nodes = {
nodeBuilder.addOutput("Sharp", "res", "Result");
},
"inputSharpImage": (context) => {
- nodeBuilder = context.instantiate("input-plugin", "inputImage");
+ nodeBuilder = context.instantiate("Input", "inputImage");
nodeBuilder.setTitle("Input image");
nodeBuilder.setDescription("Provides an image input and returns a single image output");
diff --git a/blix-plugins/threlte-plugin/src/main.cjs b/blix-plugins/threlte-plugin/src/main.cjs
index 3bfaee3f..38f2e944 100644
--- a/blix-plugins/threlte-plugin/src/main.cjs
+++ b/blix-plugins/threlte-plugin/src/main.cjs
@@ -10,9 +10,9 @@ function toTitleCase(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
-function createGLFXNode(type, title, desc, params) {
+function createThrelteNode(type, title, desc, params) {
return (context) => {
- const nodeBuilder = context.instantiate("glfx-plugin", type);
+ const nodeBuilder = context.instantiate("Threlte", type);
nodeBuilder.setTitle(title);
nodeBuilder.setDescription(desc);
@@ -54,45 +54,10 @@ function createGLFXNode(type, title, desc, params) {
}
const glfxNodes = {
- "brightnessContrast": [
- "Brightness/Contrast",
- "Adjust the brightness and contrast of the image",
- [{ id: "brightness" }, { id: "contrast" }]
- ],
- "hueSaturation": [
- "Hue / Saturation",
- "Adjust the hue and saturation of the image",
- [{ id: "hue" }, { id: "saturation" }]
- ],
- "noise": [
- "Noise",
- "Add black and white noise to the image",
- [{ id: "amount", min: 0, max: 1, step: 0.01 }]
- ],
- "denoise": [
- "Denoise",
- "Smooth over grainy noise in dark images",
- [{ id: "exponent", min: 0, max: 50, step: 0.1 }]
- ],
- "sepia": [
- "Sepia",
- "Add a reddish-brown monochrome tint to the image",
- [{ id: "amount", min: 0, max: 1, step: 0.01 }]
- ],
- "unsharpMask": [
- "Unsharp Mask",
- "Image sharpening that amplifies high-frequency detail in the image",
- [{ id: "radius", min: 0, max: 200, step: 1 }, { id: "strength", min: 0, max: 5, step: 0.05 }]
- ],
- "vibrance": [
- "Vibrance",
- "Adjust saturation of desaturated colors, leaving saturated colors unmodified",
- [{ id: "amount" }]
- ],
- "vignette": [
- "Vignette",
- "Add a vignette effect to the image",
- [{ id: "size", min: 0, max: 1, step: 0.01 }, { id: "amount", min: 0, max: 1, step: 0.01 }]
+ "addPrimitive": [
+ "Add Primitive",
+ "Add a 3D primitive",
+ []
],
};
@@ -104,7 +69,7 @@ const nodes = {
...glfxNodes,
"inputGLFXImage": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputGLFXImage");
+ const nodeBuilder = context.instantiate("Input/Other", "inputGLFXImage");
nodeBuilder.setTitle("Input GLFX image");
nodeBuilder.setDescription("Provides an image input and returns a single image output");
@@ -131,7 +96,7 @@ const nodes = {
nodeBuilder.addOutput("GLFX image", "res", "Result");
},
"inputGLFXCache": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputGLFXCache");
+ const nodeBuilder = context.instantiate("Input/Other", "inputGLFXCache");
nodeBuilder.setTitle("Input GLFX cache");
nodeBuilder.setDescription("Provides an cache input and returns a single image output");
diff --git a/src/electron/lib/Blix.ts b/src/electron/lib/Blix.ts
index d72b5361..e6cd626c 100644
--- a/src/electron/lib/Blix.ts
+++ b/src/electron/lib/Blix.ts
@@ -75,7 +75,7 @@ export class Blix {
this._graphInterpreter = new CoreGraphInterpreter(this._toolboxRegistry);
// Create Output node
- const outputNodeBuilder = new NodeBuilder("blix", "output");
+ const outputNodeBuilder = new NodeBuilder("blix", "Blix", "output");
const outputUIBuilder = outputNodeBuilder.createUIBuilder();
outputUIBuilder.addButton(
{
@@ -189,7 +189,7 @@ export class Blix {
const ipcGravityAISubsriber = new IPCGraphSubscriber();
ipcGravityAISubsriber.setListenEvents([CoreGraphUpdateEvent.graphUpdated]);
ipcGravityAISubsriber.setListenParticipants([
- CoreGraphUpdateParticipant.system,
+ // CoreGraphUpdateParticipant.system,
CoreGraphUpdateParticipant.ai,
]);
diff --git a/src/electron/lib/ai/ai-profiler-v2.ts b/src/electron/lib/ai/ai-profiler-v2.ts
index c5806893..0fad4743 100644
--- a/src/electron/lib/ai/ai-profiler-v2.ts
+++ b/src/electron/lib/ai/ai-profiler-v2.ts
@@ -176,7 +176,7 @@ export class Profiler {
}
private static generateBlixOutputNode(): NodeInstance {
- const outputNodeBuilder = new NodeBuilder("blix", "output");
+ const outputNodeBuilder = new NodeBuilder("blix", "Blix", "output");
const outputUIBuilder = outputNodeBuilder.createUIBuilder();
outputUIBuilder.addButton(
{
diff --git a/src/electron/lib/plugins/Plugin.ts b/src/electron/lib/plugins/Plugin.ts
index b85a05cf..8eb22ad4 100644
--- a/src/electron/lib/plugins/Plugin.ts
+++ b/src/electron/lib/plugins/Plugin.ts
@@ -156,12 +156,8 @@ export class NodePluginContext extends PluginContext {
return this._nodeBuilder;
}
- // nodeBuilder = context.instantiate("hello-plugin","hello");
- // TODO: Change this: it should not be done in the plugin,
- // but when the plugin loads. The plugin already defines each node name as the key
- // in the dictionary, and we already know the plugin name in the package.json
- public instantiate(plugin: string, name: string): NodeBuilder {
- this._nodeBuilder = new NodeBuilder(plugin, name);
+ public instantiate(folder: string, name: string): NodeBuilder {
+ this._nodeBuilder = new NodeBuilder(this.pluginId, folder, name);
return this.nodeBuilder;
}
}
diff --git a/src/electron/lib/plugins/builders/NodeBuilder.ts b/src/electron/lib/plugins/builders/NodeBuilder.ts
index c8c11741..cbce221d 100644
--- a/src/electron/lib/plugins/builders/NodeBuilder.ts
+++ b/src/electron/lib/plugins/builders/NodeBuilder.ts
@@ -17,6 +17,7 @@ import { type NodeTweakData } from "../../../../shared/types";
type PartialNode = {
name: string;
plugin: string;
+ folder: string;
displayName: string;
description: string;
icon: string;
@@ -34,10 +35,11 @@ type PartialNode = {
export class NodeBuilder implements PluginContextBuilder {
private partialNode: PartialNode;
- constructor(plugin: string, name: string) {
+ constructor(plugin: string, folder: string, name: string) {
this.partialNode = {
name,
plugin,
+ folder,
displayName: name,
description: "",
icon: "",
@@ -54,6 +56,7 @@ export class NodeBuilder implements PluginContextBuilder {
return new NodeInstance(
this.partialNode.name,
this.partialNode.plugin,
+ this.partialNode.folder,
this.partialNode.displayName,
this.partialNode.description,
this.partialNode.icon,
diff --git a/src/electron/lib/registries/ToolboxRegistry.ts b/src/electron/lib/registries/ToolboxRegistry.ts
index fa5b1485..73767e41 100644
--- a/src/electron/lib/registries/ToolboxRegistry.ts
+++ b/src/electron/lib/registries/ToolboxRegistry.ts
@@ -46,6 +46,7 @@ export class ToolboxRegistry implements Registry {
const nodeObject = new INode(
nodeInstance.signature,
nodeInstance.displayName,
+ nodeInstance.folder,
nodeInstance.description,
nodeInstance.icon,
inputAnchors,
@@ -81,6 +82,7 @@ export class NodeInstance implements RegistryInstance {
constructor(
public readonly name: string, // Unique identifier for the node within the plugin
public readonly plugin: string,
+ public readonly folder: string,
public readonly displayName: string,
public readonly description: string,
public readonly icon: string,
diff --git a/src/frontend/lib/stores/GraphContextMenuStore.ts b/src/frontend/lib/stores/GraphContextMenuStore.ts
index ff5cc65f..6aa59f7b 100644
--- a/src/frontend/lib/stores/GraphContextMenuStore.ts
+++ b/src/frontend/lib/stores/GraphContextMenuStore.ts
@@ -26,6 +26,10 @@ export type Item = {
action: Action;
};
+function isItemGroup(item: ItemGroup | Item): item is ItemGroup {
+ return "items" in item;
+}
+
export type Action = { type: "addNode"; signature: string };
class ContextMenuStore {
@@ -123,21 +127,50 @@ class ContextMenuStore {
});
}
+ // Construct item group tree from nodes
private groupBy(nodes: INode[]): ItemGroup[] {
- const groups: Record = {};
+ const res: ItemGroup[] = [];
for (const node of nodes) {
- const parts = node.signature.split(".");
- const plugin = parts[0];
-
- if (!groups[plugin]) {
- groups[plugin] = {
- label: plugin,
+ // Split by non-escaped forward slashes
+ const folderParts = node.folder
+ .replace(/\\/g, "\0") // Eliminate escaped backslashes
+ .split(/(?
+ part
+ .replace(/\\\//g, "/") // Unescape remaining forward slashes
+ .replace(/\0/g, "\\") // Unescape backslashes
+ .trim()
+ );
+
+ // Construct folder hierarchy
+ let elems: (ItemGroup | Item)[] = res;
+ construct: for (const folder of folderParts) {
+ if (folder === "") continue;
+
+ // Check if folder is already in the hierarchy
+ for (const elem of elems) {
+ if (!isItemGroup(elem)) continue; // Skip non-groups
+
+ if (elem.label === folder) {
+ // Folder already exists
+ elems = elem.items;
+ continue construct;
+ }
+ }
+
+ // Folder does not exist, construct it
+ const newGroup: ItemGroup = {
+ label: folder,
items: [],
};
+ elems.push(newGroup);
+
+ elems = newGroup.items as ItemGroup[];
}
- groups[plugin].items.push({
+ // Add node to folder (item to group)
+ elems.push({
label: node.title,
icon: node.icon,
action: {
@@ -147,7 +180,7 @@ class ContextMenuStore {
});
}
- return Object.values(groups);
+ return res;
}
}
diff --git a/src/shared/ui/ToolboxTypes.ts b/src/shared/ui/ToolboxTypes.ts
index 8a25b5dd..107fa49f 100644
--- a/src/shared/ui/ToolboxTypes.ts
+++ b/src/shared/ui/ToolboxTypes.ts
@@ -17,6 +17,7 @@ export class INode {
constructor(
readonly signature: NodeSignature,
readonly title: string,
+ readonly folder: string,
readonly description: string,
readonly icon: string,
readonly inputs: IAnchor[],
From febc8b8ca15f58b8e1bec8ee49839ae3da34b5c9 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 12:06:13 +0200
Subject: [PATCH 077/209] Refactor settings and keybindings
---
src/electron/lib/api/apis/UtilApi.ts | 32 ++-
src/electron/utils/settings.ts | 4 +-
src/frontend/lib/stores/BlixStore.ts | 8 +-
src/frontend/lib/stores/ShortcutStore.ts | 246 ++++++++++++++----
src/frontend/ui/base/App.svelte | 2 +-
src/frontend/ui/base/layout/Panel.svelte | 2 +-
src/frontend/ui/base/settings/About.svelte | 81 ++++++
.../ui/base/settings/AiSettings.svelte | 81 ++++++
src/frontend/ui/base/settings/Hotkeys.svelte | 148 +++++++++++
.../ui/base/{ => settings}/Settings.svelte | 120 +++++++--
.../settings}/utils/SecureInput.svelte | 0
src/frontend/ui/tiles/ShortcutSettings.svelte | 101 -------
src/frontend/ui/utils/Shortcuts.svelte | 2 +-
src/shared/types/setting.ts | 48 +++-
14 files changed, 680 insertions(+), 195 deletions(-)
create mode 100644 src/frontend/ui/base/settings/About.svelte
create mode 100644 src/frontend/ui/base/settings/AiSettings.svelte
create mode 100644 src/frontend/ui/base/settings/Hotkeys.svelte
rename src/frontend/ui/base/{ => settings}/Settings.svelte (59%)
rename src/frontend/ui/{ => base/settings}/utils/SecureInput.svelte (100%)
delete mode 100644 src/frontend/ui/tiles/ShortcutSettings.svelte
diff --git a/src/electron/lib/api/apis/UtilApi.ts b/src/electron/lib/api/apis/UtilApi.ts
index 2672e16a..bbe10380 100644
--- a/src/electron/lib/api/apis/UtilApi.ts
+++ b/src/electron/lib/api/apis/UtilApi.ts
@@ -12,7 +12,7 @@ import {
getSecret,
type Settings,
} from "../../../utils/settings";
-import type { Setting, UserSettingsCategory, QueryResponse } from "../../../../shared/types";
+import type { Setting, QueryResponse } from "../../../../shared/types";
import { type ChatModel } from "../../../lib/ai/Model";
// import dotenv from "dotenv";
// dotenv.config();
@@ -77,6 +77,11 @@ export class UtilApi implements ElectronMainApi {
}
// Add something extra validation
+
+ async saveUserSetting(setting: Setting) {
+ return await this.saveUserSettings([setting]);
+ }
+
async saveUserSettings(newSettings: Setting[]): Promise {
for (const setting of newSettings) {
if (setting.secret) {
@@ -91,7 +96,7 @@ export class UtilApi implements ElectronMainApi {
async getUserSettings() {
// const secrets = getSecrets();
- const userSettings: UserSettingsCategory[] = [
+ const userSettings = [
{
id: "ai_settings",
title: "AI Settings",
@@ -132,6 +137,29 @@ export class UtilApi implements ElectronMainApi {
return { status: "success", data: userSettings } satisfies QueryResponse;
}
+ /** Retrieve a user setting from the ElectronStore. */
+ async getUserSetting(key: string) {
+ if (settings.has(key)) {
+ let data: unknown;
+
+ if (key.startsWith("secrets.")) {
+ data = getSecret(key);
+ } else {
+ data = settings.get(key);
+ }
+
+ return {
+ status: "success",
+ data,
+ } satisfies QueryResponse;
+ } else {
+ return {
+ status: "error",
+ message: "Setting not found",
+ } satisfies QueryResponse;
+ }
+ }
+
/**
* This function takes in a key and then will save the key for the specified model to local storage.
* Electron's safeStorage is used to encrypt/decrypt these keys.
diff --git a/src/electron/utils/settings.ts b/src/electron/utils/settings.ts
index 01ea99c3..b0558de5 100644
--- a/src/electron/utils/settings.ts
+++ b/src/electron/utils/settings.ts
@@ -1,7 +1,7 @@
import ElectronStore from "electron-store";
import { safeStorage } from "electron";
import logger from "./logger";
-import type { Preferences } from "../../shared/types";
+import type { KeyboardShortcut } from "../../shared/types";
import type { recentProject } from "../../shared/types/index";
export interface Settings {
@@ -12,6 +12,7 @@ export interface Settings {
recentProjects: recentProject[];
prompts: string[];
model: string;
+ keyboardShortcuts: KeyboardShortcut[];
}
type DotUnionKeys = T extends object
@@ -40,6 +41,7 @@ export const settings = new ElectronStore({
recentProjects: [],
prompts: [],
model: "GPT-3.5",
+ keyboardShortcuts: [],
},
});
diff --git a/src/frontend/lib/stores/BlixStore.ts b/src/frontend/lib/stores/BlixStore.ts
index 9e9f66e1..eca07ae1 100644
--- a/src/frontend/lib/stores/BlixStore.ts
+++ b/src/frontend/lib/stores/BlixStore.ts
@@ -3,6 +3,7 @@ import { commandStore } from "./CommandStore";
import { toolboxStore } from "./ToolboxStore";
import { tileStore } from "./TileStore";
import { shortcutsRegistry } from "./ShortcutStore";
+import type { KeyboardShortcut } from "@shared/types";
interface BlixStore {
blixReady: boolean;
@@ -43,8 +44,11 @@ export async function setInitialStores() {
const tile = await window.apis.tileApi.getTiles();
tileStore.refreshStore(tile);
- const shortcuts = await window.apis.utilApi.getUserSettings();
- if (shortcuts.status === "success") shortcutsRegistry.refreshStore(shortcuts.data);
+ const shortcuts = await window.apis.utilApi.getUserSetting("keyboardShortcuts");
+ if (shortcuts.status === "success") {
+ // TODO: Add some sort of schema check
+ shortcutsRegistry.refreshStore(shortcuts.data as KeyboardShortcut[]);
+ }
// Graph store
// const allGraphIds = await window.apis.graphApi.getAllGraphUUIDs();
diff --git a/src/frontend/lib/stores/ShortcutStore.ts b/src/frontend/lib/stores/ShortcutStore.ts
index d40f8301..07ae75bf 100644
--- a/src/frontend/lib/stores/ShortcutStore.ts
+++ b/src/frontend/lib/stores/ShortcutStore.ts
@@ -1,10 +1,87 @@
-import { derived, writable, get } from "svelte/store";
+import type { KeyboardShortcut, KeyboardShortcuts } from "../../../shared/types";
+import { derived, writable, get, type Readable } from "svelte/store";
+
+const defaultShortcuts: Omit[] = [
+ {
+ id: "blix.palette.toggle",
+ title: "Toggle Command Palette",
+ value: ["ctrl+[KeyP]", "meta+[KeyP]"],
+ },
+ {
+ id: "blix.palette.hide",
+ title: "Hide Command Palette",
+ value: ["[Escape]"],
+ },
+ {
+ id: "blix.palette.scrollDown",
+ title: "Scroll Down Command Palette",
+ value: ["[ArrowDown]", "ctrl+[KeyJ]"],
+ },
+ {
+ id: "blix.palette.scrollUp",
+ title: "Scroll Up Command Palette",
+ value: ["[ArrowUp]", "ctrl+[KeyK]"],
+ },
+ {
+ id: "blix.palette.selectItem",
+ title: "Select Command Palette Item",
+ value: ["[Enter]"],
+ },
+ {
+ id: "blix.palette.prompt",
+ title: "Submit Prompt Command Palette",
+ value: ["[Tab]"],
+ },
+ {
+ id: "blix.contextMenu.hide",
+ title: "Hide Context Menu",
+ value: ["[Escape]"],
+ },
+ {
+ id: "blix.contextMenu.scrollDown",
+ title: "Scroll Down Context Menu",
+ value: ["ctrl+[KeyJ]"],
+ },
+ {
+ id: "blix.contextMenu.scrollUp",
+ title: "Scroll Up Context Menu",
+ value: ["ctrl+[KeyK]"],
+ },
+ {
+ id: "blix.contextMenu.selectItem",
+ title: "Select Context Menu Item",
+ value: ["[Enter]"],
+ },
+ {
+ id: "blix.projects.newProject",
+ title: "Create New Project",
+ value: ["meta+[KeyN]", "ctrl+[KeyN]"],
+ },
+ {
+ id: "blix.settings.toggle",
+ title: "Toggle Settings",
+ value: ["meta+[Comma]", "ctrl+[Comma]"],
+ },
+ {
+ id: "blix.settings.hide",
+ title: "Hide Settings",
+ value: ["[Escape]"],
+ },
+ {
+ id: "blix.splash.hide",
+ title: "Hide Splash Screen",
+ value: ["[Escape]"],
+ },
+ {
+ id: "blix.projects.save",
+ title: "Save Project",
+ value: ["ctrl+[KeyS]", "meta+[KeyS]"],
+ },
+];
export type ShortcutAction = `${string}.${string}`; // Actions must be nested at least one layer deep
export type ShortcutString = string;
-import type { UserSettingsCategory } from "../../../shared/types";
-
export class ShortcutCombo {
keyCode: string;
@@ -84,87 +161,139 @@ export class ShortcutCombo {
}
}
-type ShortcutsDict = { [key: ShortcutAction]: ShortcutString[] };
-
-// Maps actions to their respective shortcuts
-let categories: UserSettingsCategory[] = [];
-let selectedCategory: UserSettingsCategory | undefined;
class ShortcutStore {
- public refreshStore(data: UserSettingsCategory[]) {
- if (data) {
- categories = data;
- selectedCategory = categories.find((c) => c.title === "Keybindings");
-
- if (selectedCategory) {
- const short = selectedCategory.settings[0].value as { [key: string]: string[] };
- if (short !== undefined) this.shortcuts.set(short);
- }
+ // Gets filled with default shortcuts
+ shortcuts = writable>();
+
+ constructor() {
+ this.refreshStore(
+ defaultShortcuts.map((shortcut) => ({ ...shortcut, type: "keyboardShortcut" }))
+ );
+ }
+
+ public refreshStore(keyboardShortcuts: KeyboardShortcut[]) {
+ if (keyboardShortcuts.length === 0) {
+ return;
}
+
+ this.shortcuts.set(new Map(keyboardShortcuts.map((shortcut) => [shortcut.id, shortcut])));
+ }
+
+ public async persistShortcuts() {
+ const $shortcuts = get(this.shortcuts);
+ const shortcutsList = Array.from($shortcuts.values());
+ const keyboardShortcuts: KeyboardShortcuts = {
+ id: "keyboardShortcuts",
+ type: "keyboardShortcuts",
+ title: "Keyboard Shortcuts",
+ value: shortcutsList,
+ };
+
+ return await window.apis.utilApi.saveUserSetting(keyboardShortcuts);
}
- // Fill default shortcuts here
- shortcuts = writable({
- "blix.palette.toggle": ["ctrl+[KeyP]", "meta+[KeyP]"],
- "blix.palette.show": [],
- "blix.palette.hide": ["[Escape]"],
- "blix.palette.scrollDown": ["[ArrowDown]", "ctrl+[KeyJ]"],
- "blix.palette.scrollUp": ["[ArrowUp]", "ctrl+[KeyK]"],
- "blix.palette.selectItem": ["[Enter]"],
- "blix.palette.prompt": ["[Tab]"],
- "blix.contextMenu.hide": ["[Escape]"],
- "blix.contextMenu.show": [],
- "blix.contextMenu.scrollDown": ["ctrl+[KeyJ]"],
- "blix.contextMenu.scrollUp": ["ctrl+[KeyK]"],
- "blix.contextMenu.selectItem": ["[Enter]"],
- "blix.projects.newProject": ["meta+[KeyN]", "ctrl+[KeyN]"],
- "blix.settings.toggle": ["meta+[Comma]", "ctrl+[Comma]"],
- "blix.settings.hide": ["[Escape]"],
- "blix.splash.hide": ["[Escape]"],
- "blix.projects.save": ["ctrl+[KeyS]", "meta+[KeyS]"],
- });
public addActionShortcut(action: ShortcutAction, combo: ShortcutCombo) {
this.shortcuts.update((shortcuts) => {
- if (shortcuts[action] === undefined) {
- shortcuts[action] = [];
+ const shortcut = shortcuts.get(action);
+
+ if (!shortcut) {
+ shortcuts.set(action, { id: action, title: "", value: [], type: "keyboardShortcut" });
+ } else {
+ shortcuts.set(action, { ...shortcut, value: [...shortcut.value, combo.getString] });
}
- shortcuts[action].push(combo.getString);
+
return shortcuts;
});
}
- public updateActionShortcut(action: ShortcutAction, index: number, combo: ShortcutCombo) {
+ public removeShortcutHotkey(action: ShortcutAction, index: number) {
this.shortcuts.update((shortcuts) => {
- if (shortcuts[action] === undefined) {
- shortcuts[action] = [];
- }
- if (shortcuts[action].length <= index) {
- shortcuts[action].push(combo.getString);
+ const shortcut = shortcuts.get(action);
+
+ if (!shortcut) {
return shortcuts;
}
- shortcuts[action][index] = combo.getString;
+
+ const keys = [...shortcut.value];
+ keys.splice(index, 1);
+ shortcuts.set(action, { ...shortcut, value: keys });
+
return shortcuts;
});
}
- public get subscribe() {
- return this.shortcuts.subscribe;
+ public updateActionShortcut(action: ShortcutAction, index: number, combo: ShortcutCombo) {
+ this.shortcuts.update((shortcuts) => {
+ const shortcut = shortcuts.get(action);
+
+ if (!shortcut) {
+ shortcuts.set(action, { id: action, title: "", value: [], type: "keyboardShortcut" });
+ return shortcuts;
+ }
+
+ if (shortcut.value.length <= index) {
+ shortcuts.set(action, { ...shortcut, value: [...shortcut.value, combo.getString] });
+ } else {
+ const keys = [...shortcut.value];
+ keys[index] = combo.getString;
+ shortcuts.set(action, { ...shortcut, value: keys });
+ }
+
+ return shortcuts;
+ });
}
public getShortcutsForAction(action: ShortcutAction): ShortcutString[] {
- const sc = get(this.shortcuts);
- if (sc[action] === undefined) {
+ const $shortcuts = get(this.shortcuts);
+ const shortcut = $shortcuts.get(action);
+
+ if (!shortcut) {
return [];
}
- return sc[action];
+
+ return shortcut.value;
}
- // Returns a derived store that only listens for the specified action
- public getShortcutsForActionReactive(action: ShortcutAction) {
+ /** Returns a derived store that only listens for a specified action */
+ public getShortcutsForActionReactive(action: ShortcutAction): Readable {
return derived(this.shortcuts, ($shortcuts) => {
- if ($shortcuts[action] === undefined) {
+ const shortcut = $shortcuts.get(action);
+
+ if (!shortcut) {
return [];
}
- return $shortcuts[action];
+
+ return shortcut.value;
+ });
+ }
+
+ public getShortcutsReactive(): Readable {
+ return derived(this.shortcuts, ($shortcuts) => {
+ return Array.from($shortcuts.values());
+ });
+ }
+
+ public getFormattedShortcutsReactive(): Readable {
+ return derived(this.shortcuts, ($shortcuts) => {
+ const shortcuts = Array.from($shortcuts.values()).map((shortcut) => ({ ...shortcut }));
+
+ shortcuts.forEach((shortcut) => {
+ shortcut.value = shortcut.value.map((combo) => {
+ combo = combo.replace("Comma", ",");
+ combo = combo.replace("meta", "⌘");
+ combo = combo.replace("ctrl", "⌃");
+ combo = combo.replace("shift", "⇧");
+ combo = combo.replace("Key", "");
+ combo = combo.replace("[", "");
+ combo = combo.replace("]", "");
+ combo = combo.replace("+", " ");
+ return combo;
+ });
+
+ return shortcut;
+ });
+
+ return shortcuts;
});
}
@@ -173,7 +302,10 @@ class ShortcutStore {
return this.getShortcutsForAction(action).includes(combo.getString);
}
+
+ public get subscribe() {
+ return this.shortcuts.subscribe;
+ }
}
-// export const shortcutsRegistry = writable(new ShortcutStore());
export const shortcutsRegistry = new ShortcutStore();
diff --git a/src/frontend/ui/base/App.svelte b/src/frontend/ui/base/App.svelte
index 99355c0d..2e504efa 100644
--- a/src/frontend/ui/base/App.svelte
+++ b/src/frontend/ui/base/App.svelte
@@ -9,7 +9,7 @@
import { initAPIs } from "../../lib/api/apiInitializer";
import ContextMenu from "../../ui/utils/ContextMenu.svelte";
import Test from "./Test.svelte";
- import Settings from "./Settings.svelte";
+ import Settings from "./settings/Settings.svelte";
import Shortcuts from "../../ui/utils/Shortcuts.svelte";
import { settingsStore } from "../../lib/stores/SettingsStore";
import { confetti } from "@neoconfetti/svelte";
diff --git a/src/frontend/ui/base/layout/Panel.svelte b/src/frontend/ui/base/layout/Panel.svelte
index 5bbb53e4..8d20b32b 100644
--- a/src/frontend/ui/base/layout/Panel.svelte
+++ b/src/frontend/ui/base/layout/Panel.svelte
@@ -21,7 +21,7 @@
import Browser from "../../tiles/Browser.svelte";
import Assets from "../../tiles/Assets.svelte";
import WebCamera from "../../tiles/WebCamera.svelte";
- import ShortcutSettings from "../../tiles/ShortcutSettings.svelte";
+ import ShortcutSettings from "../settings/Hotkeys.svelte";
import { PanelGroup, PanelLeaf, type PanelNode } from "@frontend/lib/PanelNode";
import type { PanelType } from "@shared/types";
import { focusedPanelStore } from "../../../lib/PanelNode";
diff --git a/src/frontend/ui/base/settings/About.svelte b/src/frontend/ui/base/settings/About.svelte
new file mode 100644
index 00000000..3311cf88
--- /dev/null
+++ b/src/frontend/ui/base/settings/About.svelte
@@ -0,0 +1,81 @@
+
+
+API Keys
+
+ Rest assured that none of your API keys get stored remotely. Your information is encrypted and
+ maintained securely and solely within the confines of your own device.
+
+
+{#each settings as item (item.id)}
+
+
+ {item.title}
+ {#if item.subtitle}
+ {item.subtitle}
+ {/if}
+
+
+ {#if item.type === "text" && item.secret}
+
+ {:else if item.type === "dropdown"}
+
+ {#each item.options as option}
+ {#if item.value === option}
+ {option}
+ {:else}
+ {option}
+ {/if}
+ {/each}
+
+ {/if}
+
+{/each}
+
+
+ Save
+
diff --git a/src/frontend/ui/base/settings/AiSettings.svelte b/src/frontend/ui/base/settings/AiSettings.svelte
new file mode 100644
index 00000000..3311cf88
--- /dev/null
+++ b/src/frontend/ui/base/settings/AiSettings.svelte
@@ -0,0 +1,81 @@
+
+
+API Keys
+
+ Rest assured that none of your API keys get stored remotely. Your information is encrypted and
+ maintained securely and solely within the confines of your own device.
+
+
+{#each settings as item (item.id)}
+
+
+ {item.title}
+ {#if item.subtitle}
+ {item.subtitle}
+ {/if}
+
+
+ {#if item.type === "text" && item.secret}
+
+ {:else if item.type === "dropdown"}
+
+ {#each item.options as option}
+ {#if item.value === option}
+ {option}
+ {:else}
+ {option}
+ {/if}
+ {/each}
+
+ {/if}
+
+{/each}
+
+
+ Save
+
diff --git a/src/frontend/ui/base/settings/Hotkeys.svelte b/src/frontend/ui/base/settings/Hotkeys.svelte
new file mode 100644
index 00000000..bc976e8f
--- /dev/null
+++ b/src/frontend/ui/base/settings/Hotkeys.svelte
@@ -0,0 +1,148 @@
+
+
+
+
+ {#each $shortcuts as { id, title, value } (id)}
+
+
{title}
+
+ {#each value as hotkey, i (hotkey)}
+
+ {hotkey}
+
+
+
+
+
+
+ {:else}
+
Blank
+ {/each}
+
+
+
+ {/each}
+
+
+
+
+
diff --git a/src/frontend/ui/base/Settings.svelte b/src/frontend/ui/base/settings/Settings.svelte
similarity index 59%
rename from src/frontend/ui/base/Settings.svelte
rename to src/frontend/ui/base/settings/Settings.svelte
index c5e5d369..ffebedf0 100644
--- a/src/frontend/ui/base/Settings.svelte
+++ b/src/frontend/ui/base/settings/Settings.svelte
@@ -1,28 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+ {#each userSettingSections as section (section.id)}
+
+ {section.title}
+
+ {#each section.categories as category (category.id)}
+ {category.title}
+ {/each}
+
+ {/each}
+
+
+
+
+ {#if selectedCategoryId in userSettingsComponentMap}
+ {#key selectedCategoryId}
+
+ {/key}
+ {:else}
+
+ {/if}
+
+
+
+
+
diff --git a/src/frontend/ui/utils/SecureInput.svelte b/src/frontend/ui/base/settings/utils/SecureInput.svelte
similarity index 100%
rename from src/frontend/ui/utils/SecureInput.svelte
rename to src/frontend/ui/base/settings/utils/SecureInput.svelte
diff --git a/src/frontend/ui/tiles/ShortcutSettings.svelte b/src/frontend/ui/tiles/ShortcutSettings.svelte
deleted file mode 100644
index 6be456b1..00000000
--- a/src/frontend/ui/tiles/ShortcutSettings.svelte
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
Keyboard Shortcuts Settings
-
Configure your shortcut preferences here
-
- {#each Object.entries($shortcutsRegistry) as [action, shortcuts]}
-
-
- {action}
-
- {#each shortcuts as shortcut, index}
-
-
- updateShortcut(action, index, event)}"
- >
- {shortcut}
-
-
- {/each}
-
- addShortcut(action, event)}"
- >
- +
-
-
-
- {/each}
-
-
-
-
diff --git a/src/frontend/ui/utils/Shortcuts.svelte b/src/frontend/ui/utils/Shortcuts.svelte
index 70a197aa..11a19803 100644
--- a/src/frontend/ui/utils/Shortcuts.svelte
+++ b/src/frontend/ui/utils/Shortcuts.svelte
@@ -15,7 +15,7 @@
// Check each action
for (const action of Object.keys(shortcuts)) {
- if (shortcutsRegistry.checkShortcut(action as ShortcutAction, combo)) {
+ if (combo && shortcutsRegistry.checkShortcut(action as ShortcutAction, combo)) {
shortcuts[action as ShortcutAction](event);
}
}
diff --git a/src/shared/types/setting.ts b/src/shared/types/setting.ts
index e3c5655c..83ef56be 100644
--- a/src/shared/types/setting.ts
+++ b/src/shared/types/setting.ts
@@ -1,16 +1,37 @@
-export interface UserSettingsCategory {
+export const userSettingSections = [
+ {
+ id: "general",
+ title: "General",
+ categories: [
+ { id: "about", title: "About" },
+ { id: "ai", title: "AI Settings" },
+ { id: "hotkeys", title: "Hotkeys" },
+ ],
+ },
+] as const satisfies readonly UserSettingsSection[];
+
+export type UserSettingsCategoryId =
+ (typeof userSettingSections)[number]["categories"][number]["id"];
+export type UserSettingsCategoryTitle =
+ (typeof userSettingSections)[number]["categories"][number]["title"];
+
+export type UserSettingsSection = {
+ id: string;
+ title: string;
+ categories: ReadonlyArray;
+};
+
+export type UserSettingCategory = {
id: string;
title: string;
subtitle?: string;
- settings: Setting[];
-}
+};
export interface UserSetting {
id: string;
title: string;
subtitle?: string;
secret?: boolean;
- type: "dropdown" | "password" | "text" | "toggle" | "preferences";
}
export interface InputSetting extends UserSetting {
@@ -24,15 +45,24 @@ export interface DropdownSetting extends UserSetting {
options: string[];
value: string;
}
-
export interface ToggleSetting extends UserSetting {
type: "toggle";
value: boolean;
}
-export interface Preferences extends UserSetting {
- type: "preferences";
- value: { [key: string]: string[] };
+export interface KeyboardShortcuts extends UserSetting {
+ id: "keyboardShortcuts";
+ type: "keyboardShortcuts";
+ value: KeyboardShortcut[];
+}
+export interface KeyboardShortcut extends UserSetting {
+ id: `${string}.${string}`;
+ type: "keyboardShortcut";
+ value: string[];
}
-export type Setting = DropdownSetting | InputSetting | ToggleSetting | Preferences;
+export type Setting = DropdownSetting | InputSetting | ToggleSetting | KeyboardShortcuts;
+
+type Prettify = T extends object ? { [K in keyof T]: T[K] } : never;
+
+type t = Prettify;
From fb79997411c28a68799fe7e3b8f056ddc2151176 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 13:09:20 +0200
Subject: [PATCH 078/209] Add some extra key replacements for keybinds
---
src/frontend/lib/stores/ShortcutStore.ts | 36 +++++++++++++++-----
src/frontend/ui/base/settings/Hotkeys.svelte | 17 +++++----
2 files changed, 39 insertions(+), 14 deletions(-)
diff --git a/src/frontend/lib/stores/ShortcutStore.ts b/src/frontend/lib/stores/ShortcutStore.ts
index 07ae75bf..8043f789 100644
--- a/src/frontend/lib/stores/ShortcutStore.ts
+++ b/src/frontend/lib/stores/ShortcutStore.ts
@@ -279,14 +279,34 @@ class ShortcutStore {
shortcuts.forEach((shortcut) => {
shortcut.value = shortcut.value.map((combo) => {
- combo = combo.replace("Comma", ",");
- combo = combo.replace("meta", "⌘");
- combo = combo.replace("ctrl", "⌃");
- combo = combo.replace("shift", "⇧");
- combo = combo.replace("Key", "");
- combo = combo.replace("[", "");
- combo = combo.replace("]", "");
- combo = combo.replace("+", " ");
+ const map = new Map([
+ ["Comma", ","],
+ ["Equal", "="],
+ ["Minus", "-"],
+ ["Period", "."],
+ ["Backslash", "\\"],
+ ["Backspace", "⌫"],
+ ["meta", "⌘"],
+ ["alt", "⌥"],
+ ["ctrl", "⌃"],
+ ["shift", "⇧"],
+ ["ArrowDown", "↓"],
+ ["ArrowUp", "↑"],
+ ["ArrowLeft", "←"],
+ ["ArrowRight", "→"],
+ ["BracketRight", "]"],
+ ["BracketLeft", "["],
+ ["Digit", ""],
+ ["Key", ""],
+ ["[", ""],
+ ["]", ""],
+ [/\+/g, " "],
+ ]);
+
+ for (const [key, value] of map.entries()) {
+ combo = combo.replace(key, value);
+ }
+
return combo;
});
diff --git a/src/frontend/ui/base/settings/Hotkeys.svelte b/src/frontend/ui/base/settings/Hotkeys.svelte
index bc976e8f..5bc069e5 100644
--- a/src/frontend/ui/base/settings/Hotkeys.svelte
+++ b/src/frontend/ui/base/settings/Hotkeys.svelte
@@ -8,7 +8,6 @@
let shortcuts = shortcutsRegistry.getFormattedShortcutsReactive();
export function updateShortcut(action: string, index: number, event: KeyboardEvent) {
- console.log(action, event);
const combo: ShortcutCombo | null = ShortcutCombo.fromEvent(event);
if (!combo) return;
@@ -25,6 +24,11 @@
(event.target as HTMLButtonElement).blur();
shortcutsRegistry.persistShortcuts();
}
+
+ function removeShortcutHotkey(action: ShortcutAction, index: number) {
+ shortcutsRegistry.removeShortcutHotkey(action, index);
+ shortcutsRegistry.persistShortcuts();
+ }
@@ -33,7 +37,7 @@
{title}
- {#each value as hotkey, i (hotkey)}
+ {#each value as hotkey, index (hotkey)}
@@ -41,7 +45,7 @@
Blank
{/each}
- addShortcut(id, event)}"
>
-
+
{/each}
From 05986ede4b783d3926c6f91060390ce8467b33da Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 13:19:46 +0200
Subject: [PATCH 079/209] Add scrollbar on vertical overflow to hotkeys page
---
src/frontend/lib/stores/ShortcutStore.ts | 2 +
src/frontend/ui/base/settings/Hotkeys.svelte | 74 ++++---------------
src/frontend/ui/base/settings/Settings.svelte | 8 +-
3 files changed, 20 insertions(+), 64 deletions(-)
diff --git a/src/frontend/lib/stores/ShortcutStore.ts b/src/frontend/lib/stores/ShortcutStore.ts
index 8043f789..d193b239 100644
--- a/src/frontend/lib/stores/ShortcutStore.ts
+++ b/src/frontend/lib/stores/ShortcutStore.ts
@@ -179,6 +179,7 @@ class ShortcutStore {
this.shortcuts.set(new Map(keyboardShortcuts.map((shortcut) => [shortcut.id, shortcut])));
}
+ /** Saves user shortcuts to ElectronStore. */
public async persistShortcuts() {
const $shortcuts = get(this.shortcuts);
const shortcutsList = Array.from($shortcuts.values());
@@ -206,6 +207,7 @@ class ShortcutStore {
});
}
+ /** Removes a specific keybind from an action/command. */
public removeShortcutHotkey(action: ShortcutAction, index: number) {
this.shortcuts.update((shortcuts) => {
const shortcut = shortcuts.get(action);
diff --git a/src/frontend/ui/base/settings/Hotkeys.svelte b/src/frontend/ui/base/settings/Hotkeys.svelte
index 5bc069e5..e5ea4d67 100644
--- a/src/frontend/ui/base/settings/Hotkeys.svelte
+++ b/src/frontend/ui/base/settings/Hotkeys.svelte
@@ -32,10 +32,11 @@
-
+
{#each $shortcuts as { id, title, value } (id)}
{title}
+
{#each value as hotkey, index (hotkey)}
Blank
{/each}
+
-
-
-
diff --git a/src/frontend/ui/base/settings/Settings.svelte b/src/frontend/ui/base/settings/Settings.svelte
index ffebedf0..17f00368 100644
--- a/src/frontend/ui/base/settings/Settings.svelte
+++ b/src/frontend/ui/base/settings/Settings.svelte
@@ -65,8 +65,8 @@
-->
-
+
{#if selectedCategoryId in userSettingsComponentMap}
{#key selectedCategoryId}
From 81e871e624f3e7935b7cfe0f444522f83070695f Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 14:15:07 +0200
Subject: [PATCH 080/209] Fix retrieving user settings
---
src/electron/lib/api/apis/UtilApi.ts | 12 ++-
src/frontend/ui/base/App.svelte | 4 +-
src/frontend/ui/base/settings/About.svelte | 73 --------------
.../ui/base/settings/AiSettings.svelte | 99 ++++++++++---------
src/frontend/ui/base/settings/Settings.svelte | 10 +-
5 files changed, 70 insertions(+), 128 deletions(-)
diff --git a/src/electron/lib/api/apis/UtilApi.ts b/src/electron/lib/api/apis/UtilApi.ts
index bbe10380..1f1bef42 100644
--- a/src/electron/lib/api/apis/UtilApi.ts
+++ b/src/electron/lib/api/apis/UtilApi.ts
@@ -138,12 +138,20 @@ export class UtilApi implements ElectronMainApi {
}
/** Retrieve a user setting from the ElectronStore. */
- async getUserSetting(key: string) {
+ async getUserSetting(setting: Setting | string) {
+ let key = "";
+
+ if (typeof setting === "string") {
+ key = setting;
+ } else {
+ key = setting.secret ? `secrets.${setting.id}` : setting.id;
+ }
+
if (settings.has(key)) {
let data: unknown;
if (key.startsWith("secrets.")) {
- data = getSecret(key);
+ data = getSecret(key.replace("secrets.", ""));
} else {
data = settings.get(key);
}
diff --git a/src/frontend/ui/base/App.svelte b/src/frontend/ui/base/App.svelte
index 2e504efa..9653573a 100644
--- a/src/frontend/ui/base/App.svelte
+++ b/src/frontend/ui/base/App.svelte
@@ -70,9 +70,9 @@
{/if}
-{#if $settingsStore.showing}
+
-{/if}
+
diff --git a/src/frontend/ui/base/settings/About.svelte b/src/frontend/ui/base/settings/About.svelte
index 3311cf88..bbbecf88 100644
--- a/src/frontend/ui/base/settings/About.svelte
+++ b/src/frontend/ui/base/settings/About.svelte
@@ -5,77 +5,4 @@
import SecureInput from "./utils/SecureInput.svelte";
const { getSetting, saveSettings } = getContext("settings");
-
- let settings: Setting[] = [
- {
- id: "OPENAI_API_KEY",
- title: "Open AI Key",
- subtitle: "Required to use Open AI models such as GPT-3.5",
- type: "text",
- secret: true,
- value: "",
- },
- {
- id: "model",
- title: "Open AI Model",
- type: "dropdown",
- value: "",
- options: ["GPT-4", "GPT-3.5"],
- },
- ];
-
- onMount(async () => {
- for (const setting of settings) {
- const res = await getSetting(setting.id);
-
- if (res.status === "success" && res.data) {
- // Need to figure how to type this
- setting.value = res.data as any;
- }
- }
- });
-
-API Keys
-
- Rest assured that none of your API keys get stored remotely. Your information is encrypted and
- maintained securely and solely within the confines of your own device.
-
-
-{#each settings as item (item.id)}
-
-
- {item.title}
- {#if item.subtitle}
- {item.subtitle}
- {/if}
-
-
- {#if item.type === "text" && item.secret}
-
- {:else if item.type === "dropdown"}
-
- {#each item.options as option}
- {#if item.value === option}
- {option}
- {:else}
- {option}
- {/if}
- {/each}
-
- {/if}
-
-{/each}
-
-
- Save
-
diff --git a/src/frontend/ui/base/settings/AiSettings.svelte b/src/frontend/ui/base/settings/AiSettings.svelte
index 3311cf88..6e807c67 100644
--- a/src/frontend/ui/base/settings/AiSettings.svelte
+++ b/src/frontend/ui/base/settings/AiSettings.svelte
@@ -20,62 +20,69 @@
title: "Open AI Model",
type: "dropdown",
value: "",
- options: ["GPT-4", "GPT-3.5"],
+ options: ["GPT-3.5", "GPT-4"],
},
];
onMount(async () => {
- for (const setting of settings) {
- const res = await getSetting(setting.id);
+ const updatedSettings = await Promise.all(
+ settings.map(async (setting) => {
+ const res = await getSetting(setting);
- if (res.status === "success" && res.data) {
- // Need to figure how to type this
- setting.value = res.data as any;
- }
- }
+ if (res.status === "success" && res.data) {
+ return { ...setting, value: res.data as any };
+ }
+
+ return setting;
+ })
+ );
+
+ settings = updatedSettings;
});
-API Keys
-
- Rest assured that none of your API keys get stored remotely. Your information is encrypted and
- maintained securely and solely within the confines of your own device.
-
+
+
API Keys
+
+ Rest assured that none of your API keys get stored remotely. Your information is encrypted and
+ maintained securely and solely within the confines of your own device.
+
+
+ {#each settings as item (item.id)}
+
+
+ {item.title}
+ {#if item.subtitle}
+ {item.subtitle}
+ {/if}
+
-{#each settings as item (item.id)}
-
-
- {item.title}
- {#if item.subtitle}
- {item.subtitle}
+ {#if item.type === "text" && item.secret}
+
+ {:else if item.type === "dropdown"}
+
+ {#each item.options as option}
+ {#if item.value === option}
+ {option}
+ {:else}
+ {option}
+ {/if}
+ {/each}
+
{/if}
-
+
+ {/each}
- {#if item.type === "text" && item.secret}
-
- {:else if item.type === "dropdown"}
-
- {#each item.options as option}
- {#if item.value === option}
- {option}
- {:else}
- {option}
- {/if}
- {/each}
-
- {/if}
+
+ Save
-{/each}
-
-
- Save
diff --git a/src/frontend/ui/base/settings/Settings.svelte b/src/frontend/ui/base/settings/Settings.svelte
index 17f00368..9b4ba52d 100644
--- a/src/frontend/ui/base/settings/Settings.svelte
+++ b/src/frontend/ui/base/settings/Settings.svelte
@@ -1,7 +1,7 @@
@@ -12,8 +12,8 @@
import { settingsStore } from "../../../lib/stores/SettingsStore";
import { userSettingSections, type UserSettingsCategoryId } from "../../../../shared/types";
- import About from "./AiSettings.svelte";
- import AiSettings from "./About.svelte";
+ import About from "./About.svelte";
+ import AiSettings from "./AiSettings.svelte";
import Hotkeys from "./Hotkeys.svelte";
let selectedCategoryId: UserSettingsCategoryId = "about";
@@ -39,8 +39,8 @@
}
}
- async function getSetting(key: string) {
- return await window.apis.utilApi.getUserSetting(key);
+ async function getSetting(setting: Setting) {
+ return await window.apis.utilApi.getUserSetting(setting);
}
const userSettingsComponentMap: Record<
From e2a333df16892d77f094f47c9a324f3438b392a9 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 14:16:15 +0200
Subject: [PATCH 081/209] Remove old settings page code
---
src/frontend/ui/base/settings/Settings.svelte | 120 ------------------
1 file changed, 120 deletions(-)
diff --git a/src/frontend/ui/base/settings/Settings.svelte b/src/frontend/ui/base/settings/Settings.svelte
index 9b4ba52d..aef7aed5 100644
--- a/src/frontend/ui/base/settings/Settings.svelte
+++ b/src/frontend/ui/base/settings/Settings.svelte
@@ -118,123 +118,3 @@
-
-
From 024c0c1b27cd10150e7f940ce7b58d1641ced721 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 14:18:45 +0200
Subject: [PATCH 082/209] Remove old get settings from UtilApi
---
src/electron/lib/api/apis/UtilApi.ts | 45 ----------------------------
1 file changed, 45 deletions(-)
diff --git a/src/electron/lib/api/apis/UtilApi.ts b/src/electron/lib/api/apis/UtilApi.ts
index 1f1bef42..d5d5e566 100644
--- a/src/electron/lib/api/apis/UtilApi.ts
+++ b/src/electron/lib/api/apis/UtilApi.ts
@@ -92,51 +92,6 @@ export class UtilApi implements ElectronMainApi {
return { status: "success" };
}
- // This will have to be cleaned up later. Kinda a temp implementation rn
- async getUserSettings() {
- // const secrets = getSecrets();
-
- const userSettings = [
- {
- id: "ai_settings",
- title: "AI Settings",
- settings: [
- {
- id: "OPENAI_API_KEY",
- title: "Open AI Key",
- subtitle: "Required to use Open AI models such as GPT-3.5",
- type: "password",
- secret: true,
- value: getSecret("OPENAI_API_KEY"),
- },
- {
- id: "model",
- title: "Open AI Model",
- type: "dropdown",
- value: settings.get("model"),
- options: ["GPT-4", "GPT-3.5"],
- },
- ],
- },
- {
- id: "keybind_settings",
- title: "Keybindings",
- settings: [
- {
- id: "Keybindings",
- title: "Keybindings",
- subtitle: "Customize your keybindings",
- type: "preferences",
- secret: false,
- value: settings.get("Keybindings"),
- },
- ],
- },
- ];
-
- return { status: "success", data: userSettings } satisfies QueryResponse;
- }
-
/** Retrieve a user setting from the ElectronStore. */
async getUserSetting(setting: Setting | string) {
let key = "";
From 7f096768fcfe556dd786485053e986aab0306103 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 14:33:54 +0200
Subject: [PATCH 083/209] Add chip to show when adding hotkey
---
src/frontend/lib/stores/ShortcutStore.ts | 4 +++-
src/frontend/ui/base/settings/Hotkeys.svelte | 17 ++++++++++++++++-
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/src/frontend/lib/stores/ShortcutStore.ts b/src/frontend/lib/stores/ShortcutStore.ts
index d193b239..4a3404e7 100644
--- a/src/frontend/lib/stores/ShortcutStore.ts
+++ b/src/frontend/lib/stores/ShortcutStore.ts
@@ -200,7 +200,9 @@ class ShortcutStore {
if (!shortcut) {
shortcuts.set(action, { id: action, title: "", value: [], type: "keyboardShortcut" });
} else {
- shortcuts.set(action, { ...shortcut, value: [...shortcut.value, combo.getString] });
+ if (!shortcut.value.includes(combo.getString)) {
+ shortcuts.set(action, { ...shortcut, value: [...shortcut.value, combo.getString] });
+ }
}
return shortcuts;
diff --git a/src/frontend/ui/base/settings/Hotkeys.svelte b/src/frontend/ui/base/settings/Hotkeys.svelte
index e5ea4d67..24aa862e 100644
--- a/src/frontend/ui/base/settings/Hotkeys.svelte
+++ b/src/frontend/ui/base/settings/Hotkeys.svelte
@@ -6,6 +6,7 @@
} from "@frontend/lib/stores/ShortcutStore";
let shortcuts = shortcutsRegistry.getFormattedShortcutsReactive();
+ let focusedShortcutId = "";
export function updateShortcut(action: string, index: number, event: KeyboardEvent) {
const combo: ShortcutCombo | null = ShortcutCombo.fromEvent(event);
@@ -62,8 +63,20 @@
+ {#if focusedShortcutId === id}
+ Press hotkey...
+ {/if}
{:else}
- Blank
+ {#if focusedShortcutId === id}
+ Press hotkey...
+ {:else}
+ Blank
+ {/if}
{/each}
addShortcut(id, event)}"
+ on:focusin="{() => (focusedShortcutId = id)}"
+ on:focusout="{() => (focusedShortcutId = '')}"
>
Date: Wed, 20 Sep 2023 19:09:37 +0200
Subject: [PATCH 084/209] Implement undo/redo system to work with text inputs
---
src/frontend/lib/stores/GraphStore.ts | 4 +---
src/frontend/lib/stores/MediaStore.ts | 21 ++++++++++++++++++-
.../graph/nodeUICcomponents/TextInput.svelte | 14 +++++++++++--
3 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/src/frontend/lib/stores/GraphStore.ts b/src/frontend/lib/stores/GraphStore.ts
index 508ae223..aa2909d6 100644
--- a/src/frontend/lib/stores/GraphStore.ts
+++ b/src/frontend/lib/stores/GraphStore.ts
@@ -119,9 +119,7 @@ export class GraphStore {
// console.log("SUB TO", node, "-->>", input)
let first = true;
this.uiInputUnsubscribers[node].push(
- inputs[input].subscribe(() => {
- // console.log("UPDATE UI INPUTS", node, "->", input);
- // console.log("Sg", false, node)
+ inputs[input].subscribe((state) => {
// Ensure the subscribe does not run when first created
if (first) {
first = false;
diff --git a/src/frontend/lib/stores/MediaStore.ts b/src/frontend/lib/stores/MediaStore.ts
index 692cd9ff..d1009900 100644
--- a/src/frontend/lib/stores/MediaStore.ts
+++ b/src/frontend/lib/stores/MediaStore.ts
@@ -19,7 +19,15 @@ class MediaStore {
}
public updateOutputIds(ids: Set) {
- this.outputIds.set(ids);
+ const oldIds = get(this.outputIds);
+ if (!oldIds) this.outputIds.set(ids);
+
+ // Wont set store and notfiy subscribers if the value allOutputIds have remained the same.
+ // This is the case where the undo/redo system willchange something and then the stores see a change and do their
+ // own update but their update wont matter.
+ if (oldIds && ids && !equivSets(oldIds, ids)) {
+ this.outputIds.set(ids);
+ }
}
// Stop listening for graph changes
@@ -72,4 +80,15 @@ class MediaStore {
}
}
+function equivSets(s1: Set, s2: Set): boolean {
+ for (const element of s1) {
+ if (!s2.has(element)) {
+ return false;
+ } else {
+ s2.delete(element);
+ }
+ }
+ return true;
+}
+
export const mediaStore = new MediaStore();
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
index 3b3bec0a..45685a92 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
@@ -2,17 +2,27 @@
import { writable } from "svelte/store";
import { UIValueStore } from "@shared/ui/UIGraph";
import type { UIComponentConfig, UIComponentProps } from "@shared/ui/NodeUITypes";
-
+ import { createEventDispatcher } from "svelte";
// export let label: string;
// export let inputStore: UIValueStore;
export let props: UIComponentProps;
export let inputStore: UIValueStore;
export let config: UIComponentConfig;
+ const dispatch = createEventDispatcher();
if (!inputStore.inputs[config.componentId]) inputStore.inputs[config.componentId] = writable("");
+ let valStore;
+ let first = true;
- $: valStore = inputStore.inputs[config.componentId];
+ $: {
+ valStore = inputStore.inputs[config.componentId];
+ if (!first) {
+ dispatch("inputInteraction", { id: config.componentId, value: $valStore });
+ } else {
+ first = false;
+ }
+ }
From b6c83ffe2f833622d9ad4bcd9c9f8b01dd5124e9 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 19:55:06 +0200
Subject: [PATCH 085/209] Fix everything with media outputs
---
src/frontend/ui/utils/graph/SelectionBox.svelte | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/frontend/ui/utils/graph/SelectionBox.svelte b/src/frontend/ui/utils/graph/SelectionBox.svelte
index 307cc663..895826e4 100644
--- a/src/frontend/ui/utils/graph/SelectionBox.svelte
+++ b/src/frontend/ui/utils/graph/SelectionBox.svelte
@@ -96,10 +96,11 @@
$: selectedItemTitle = getSelectedItemTitle(selectedItemId, items);
$: filteredItems = filterItems(searchTerm, items);
$: if (showItems) searchContainer?.focus();
- $: if (!selectedItemId && items.length > 0) selectedItemId = items[0].id;
// Make sure selected item id is reset when item list is empty
$: if (items.length === 0) selectedItemId = "";
+
$: if (!items.some((i) => i.id === selectedItemId)) selectedItemId = "";
+ $: if (!selectedItemId && items.length > 0) selectedItemId = items[0].id;
From 43362e16a4130ceee6f9cd52218b134a53bbe5bb Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 20 Sep 2023 21:17:24 +0200
Subject: [PATCH 086/209] Add initial About settings page content
---
src/electron/lib/api/apis/UtilApi.ts | 25 ++++---
src/frontend/lib/stores/BlixStore.ts | 74 ++++++++-----------
src/frontend/ui/base/App.svelte | 2 +-
src/frontend/ui/base/settings/About.svelte | 52 ++++++++++++-
.../ui/base/settings/AiSettings.svelte | 14 +++-
src/frontend/ui/base/settings/Hotkeys.svelte | 17 ++---
.../ui/base/settings/utils/Button.svelte | 13 ++++
7 files changed, 129 insertions(+), 68 deletions(-)
create mode 100644 src/frontend/ui/base/settings/utils/Button.svelte
diff --git a/src/electron/lib/api/apis/UtilApi.ts b/src/electron/lib/api/apis/UtilApi.ts
index d5d5e566..f54f5aed 100644
--- a/src/electron/lib/api/apis/UtilApi.ts
+++ b/src/electron/lib/api/apis/UtilApi.ts
@@ -14,6 +14,7 @@ import {
} from "../../../utils/settings";
import type { Setting, QueryResponse } from "../../../../shared/types";
import { type ChatModel } from "../../../lib/ai/Model";
+import { autoUpdater } from "electron-updater";
// import dotenv from "dotenv";
// dotenv.config();
@@ -25,20 +26,24 @@ export class UtilApi implements ElectronMainApi {
this.blix = blix;
}
- async getSystemInfo() {
- const nodeVersion = process.version;
- const systemPlatform = platform().toString();
- const systemType = type();
- const systemVersion = release();
-
+ async getInfo() {
return {
- nodeVersion,
- systemPlatform,
- systemType,
- systemVersion,
+ system: {
+ nodeVersion: process.version,
+ platform: platform().toString(),
+ type: type(),
+ version: release(),
+ },
+ blix: {
+ version: autoUpdater.currentVersion.version,
+ },
};
}
+ async checkForUpdates() {
+ return await autoUpdater.checkForUpdates();
+ }
+
async sendPrompt(prompt: string, id: UUID): Promise {
if (!prompt) {
return {
diff --git a/src/frontend/lib/stores/BlixStore.ts b/src/frontend/lib/stores/BlixStore.ts
index eca07ae1..8bfc5a2c 100644
--- a/src/frontend/lib/stores/BlixStore.ts
+++ b/src/frontend/lib/stores/BlixStore.ts
@@ -5,32 +5,43 @@ import { tileStore } from "./TileStore";
import { shortcutsRegistry } from "./ShortcutStore";
import type { KeyboardShortcut } from "@shared/types";
-interface BlixStore {
- blixReady: boolean;
- systemInfo: {
- nodeVersion: string;
- systemPlatform: string;
- systemType: string;
- systemVersion: string;
- };
-}
-
-// Currently used to store some startup config. Can potentially be used to store
-// some other global state in the future.
-export const blixStore = writable({
+const blixStoreDefaults = {
blixReady: false,
- systemInfo: {
+ system: {
nodeVersion: "",
- systemPlatform: "",
- systemType: "",
- systemVersion: "",
+ platform: "",
+ type: "",
+ version: "",
+ },
+ blix: {
+ version: "",
},
-});
+};
+
+type BlixStoreState = typeof blixStoreDefaults;
+
+export class BlixStore {
+ store = writable(blixStoreDefaults);
+
+ public async checkForUpdates() {
+ return await window.apis.utilApi.checkForUpdates();
+ }
+
+ public get subscribe() {
+ return this.store.subscribe;
+ }
+
+ public update(fn: (state: BlixStoreState) => BlixStoreState) {
+ this.store.update(fn);
+ }
+}
+
+export const blixStore = new BlixStore();
export async function setInitialStores() {
// BLix store
- const res = await window.apis.utilApi.getSystemInfo();
- blixStore.update((state) => ({ ...state, systemInfo: res }));
+ const res = await window.apis.utilApi.getInfo();
+ blixStore.update((state) => ({ ...state, ...res }));
// Command store
const command = await window.apis.commandApi.getCommands();
@@ -49,27 +60,4 @@ export async function setInitialStores() {
// TODO: Add some sort of schema check
shortcutsRegistry.refreshStore(shortcuts.data as KeyboardShortcut[]);
}
-
- // Graph store
- // const allGraphIds = await window.apis.graphApi.getAllGraphUUIDs();
- // console.log("ALL GRAPHS", allGraphIds);
-
- // for (const graphId of allGraphIds) {
- // const graph = await window.apis.graphApi.getGraph(graphId);
- // console.log("BACKEND GRAPH", graph.getNodes);
-
- // TODO: REMOVE; This is just for testing
- // const uiGraph = new UIGraph(graphId);
- // const node1 = new GraphNode("1");
- // const node2 = new GraphNode("2");
- // const node3 = new GraphNode("a");
- // uiGraph.nodes[node1.uuid] = node1;
- // uiGraph.nodes[node2.uuid] = node2;
- // uiGraph.nodes[node3.uuid] = node3;
- // node1.pos.x = 100;
- // node1.pos.y = 100;
-
- // graphMall.refreshGraph(uiGraph.uuid, uiGraph);
- // }
- // exportLayout()
}
diff --git a/src/frontend/ui/base/App.svelte b/src/frontend/ui/base/App.svelte
index 9653573a..f5b0840c 100644
--- a/src/frontend/ui/base/App.svelte
+++ b/src/frontend/ui/base/App.svelte
@@ -55,7 +55,7 @@
/>
{/if}
-
+
+
diff --git a/src/frontend/ui/base/settings/Hotkeys.svelte b/src/frontend/ui/base/settings/Hotkeys.svelte
index 24aa862e..00ea551a 100644
--- a/src/frontend/ui/base/settings/Hotkeys.svelte
+++ b/src/frontend/ui/base/settings/Hotkeys.svelte
@@ -63,22 +63,19 @@
- {#if focusedShortcutId === id}
- Press hotkey...
- {/if}
{:else}
- {#if focusedShortcutId === id}
- Press hotkey...
- {:else}
+ {#if focusedShortcutId !== id}
Blank
{/if}
{/each}
+ {#if focusedShortcutId === id}
+ Press hotkey...
+ {/if}
+
+ export let title: string = "Title";
+ export let onClick: () => void;
+
+
+
+ {title}
+
From 9ce05cb1083f7933f561f04065f0e8c8a4698206 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Thu, 21 Sep 2023 02:48:25 +0200
Subject: [PATCH 087/209] Add cache metadata and image thumbnails to Assets
tile
---
blix-plugins/glfx-plugin/src/main.cjs | 27 ++++++++
blix-plugins/threlte-plugin/src/main.cjs | 27 --------
public/index.html | 2 +-
src/electron/lib/cache/CacheManager.ts | 38 ++++++++----
src/electron/lib/webviews/preload.ts | 17 ++++--
src/frontend/lib/stores/CacheStore.ts | 60 ++++++++++++------
src/frontend/ui/tiles/Assets.svelte | 61 ++++++++++++++++---
.../nodeUICcomponents/CachePicker.svelte | 8 ++-
src/shared/types/cache.ts | 28 +++++----
9 files changed, 185 insertions(+), 83 deletions(-)
diff --git a/blix-plugins/glfx-plugin/src/main.cjs b/blix-plugins/glfx-plugin/src/main.cjs
index 27085c1e..67ee1018 100644
--- a/blix-plugins/glfx-plugin/src/main.cjs
+++ b/blix-plugins/glfx-plugin/src/main.cjs
@@ -121,6 +121,33 @@ const nodes = {
nodeBuilder.setUI(ui);
+ nodeBuilder.addOutput("GLFX image", "res", "Result");
+ },
+ "inputGLFXCache": (context) => {
+ const nodeBuilder = context.instantiate("input-plugin", "inputGLFXCache");
+ nodeBuilder.setTitle("Input GLFX cache");
+ nodeBuilder.setDescription("Provides an cache input and returns a single image output");
+
+ nodeBuilder.define(async (input, uiInput, from) => {
+ return { "res": { src: uiInput["cacheid"] } };
+ });
+
+ const ui = nodeBuilder.createUIBuilder();
+ // ui.addFilePicker({
+ // componentId: "imagePicker",
+ // label: "Pick an image",
+ // defaultValue: "",
+ // triggerUpdate: true,
+ // }, {});
+ ui.addCachePicker({
+ componentId: "cacheid",
+ label: "Pick an image",
+ defaultValue: "",
+ triggerUpdate: true,
+ }, {})
+
+ nodeBuilder.setUI(ui);
+
nodeBuilder.addOutput("GLFX image", "res", "Result");
},
}
diff --git a/blix-plugins/threlte-plugin/src/main.cjs b/blix-plugins/threlte-plugin/src/main.cjs
index 3bfaee3f..9886f1c0 100644
--- a/blix-plugins/threlte-plugin/src/main.cjs
+++ b/blix-plugins/threlte-plugin/src/main.cjs
@@ -128,33 +128,6 @@ const nodes = {
nodeBuilder.setUI(ui);
- nodeBuilder.addOutput("GLFX image", "res", "Result");
- },
- "inputGLFXCache": (context) => {
- const nodeBuilder = context.instantiate("input-plugin", "inputGLFXCache");
- nodeBuilder.setTitle("Input GLFX cache");
- nodeBuilder.setDescription("Provides an cache input and returns a single image output");
-
- nodeBuilder.define(async (input, uiInput, from) => {
- return { "res": { src: uiInput["cacheid"] } };
- });
-
- const ui = nodeBuilder.createUIBuilder();
- // ui.addFilePicker({
- // componentId: "imagePicker",
- // label: "Pick an image",
- // defaultValue: "",
- // triggerUpdate: true,
- // }, {});
- ui.addCachePicker({
- componentId: "cacheid",
- label: "Pick an image",
- defaultValue: "",
- triggerUpdate: true,
- }, {})
-
- nodeBuilder.setUI(ui);
-
nodeBuilder.addOutput("GLFX image", "res", "Result");
},
}
diff --git a/public/index.html b/public/index.html
index 8361f8dd..7921ef54 100644
--- a/public/index.html
+++ b/public/index.html
@@ -8,7 +8,7 @@
See: [https://stackoverflow.com/a/54484389]
and: [https://stackoverflow.com/a/46259847]
-->
-
+
AI Photo Editor
diff --git a/src/electron/lib/cache/CacheManager.ts b/src/electron/lib/cache/CacheManager.ts
index 93b5d02c..6f237bf5 100644
--- a/src/electron/lib/cache/CacheManager.ts
+++ b/src/electron/lib/cache/CacheManager.ts
@@ -5,7 +5,8 @@ import type {
CacheSubsidiary,
CacheObject,
CacheRequest,
- CacheResponse,
+ CacheUpdateNotification,
+ CacheWriteResponse,
} from "../../../shared/types/cache";
import WebSocket from "ws";
@@ -61,6 +62,7 @@ export class CacheManager {
switch (data.type) {
case "cache-write-metadata":
this.writeMetadata(data.id, data.metadata);
+ this.notifyListeners();
break;
case "cache-get":
// TODO: check if exist
@@ -71,11 +73,7 @@ export class CacheManager {
// TODO: check if exist
socket.send(JSON.stringify({ success: true }));
- for (const listener of this.listeners) {
- listener.send(
- JSON.stringify({ type: "cache-update", cache: Object.keys(this.cache) })
- );
- }
+ this.notifyListeners();
break;
case "cache-subscribe":
this.listeners.add(socket);
@@ -85,11 +83,10 @@ export class CacheManager {
}
} else if (event.data instanceof Buffer) {
const id = this.writeContent(event.data);
- socket.send(JSON.stringify({ success: true, id }));
+ socket.send(JSON.stringify({ success: true, id } as CacheWriteResponse));
+
// Send cache update to all listeners
- for (const listener of this.listeners) {
- listener.send(JSON.stringify({ type: "cache-update", cache: Object.keys(this.cache) }));
- }
+ this.notifyListeners();
} else {
logger.info("unknown", event.data);
}
@@ -101,6 +98,17 @@ export class CacheManager {
});
}
+ private notifyListeners() {
+ const notification: CacheUpdateNotification = {
+ type: "cache-update",
+ cache: Object.keys(this.cache).map((uuid) => ({ uuid, metadata: this.cache[uuid].metadata })),
+ };
+
+ for (const listener of this.listeners) {
+ listener.send(JSON.stringify(notification));
+ }
+ }
+
// TODO
connect() {
return;
@@ -113,11 +121,19 @@ export class CacheManager {
writeContent(content: Buffer): CacheUUID {
const cacheUUID = randomBytes(32).toString("base64url");
- this.cache[cacheUUID] = { uuid: cacheUUID, data: content, metadata: {} };
+ this.cache[cacheUUID] = {
+ uuid: cacheUUID,
+ data: content,
+ metadata: { contentType: "unknown" },
+ }; // TODO: Add content type
return cacheUUID;
}
writeMetadata(cacheUUID: CacheUUID, metadata: any) {
+ if (this.cache[cacheUUID] == null) {
+ logger.warn("Tried writing metadata to non-existent cache object: ", cacheUUID);
+ return;
+ }
this.cache[cacheUUID].metadata = metadata;
}
diff --git a/src/electron/lib/webviews/preload.ts b/src/electron/lib/webviews/preload.ts
index 4d163217..630950ab 100644
--- a/src/electron/lib/webviews/preload.ts
+++ b/src/electron/lib/webviews/preload.ts
@@ -1,16 +1,21 @@
// This is the preload script for the webviews.
+
+import { CacheMetadata, CacheWriteResponse } from "@shared/types/cache";
+
// It exposes some IPC functions so the webview can communicate with its parent renderer.
const { ipcRenderer, contextBridge } = require("electron");
const ws = new WebSocket("ws://localhost:60606");
ws.binaryType = "blob";
-function sendAndRecieveData(data: string | Blob): Promise {
+function sendAndRecieveData(payload: string | Blob): Promise {
return new Promise((resolve, reject) => {
- ws.send(data);
- ws.addEventListener("message", (event) => {
+ ws.send(payload);
+ const recv = (event: MessageEvent) => {
+ ws.removeEventListener("message", recv); // Remove this listener
resolve(event.data);
- });
+ };
+ ws.addEventListener("message", recv);
});
}
@@ -24,9 +29,9 @@ contextBridge.exposeInMainWorld("api", {
});
contextBridge.exposeInMainWorld("cache", {
- write: async (content: Blob, metadata: any) => {
+ write: async (content: Blob, metadata: CacheMetadata) => {
const response: string = await sendAndRecieveData(new Blob([content]));
- const data = JSON.parse(response);
+ const data = JSON.parse(response) as CacheWriteResponse;
ws.send(JSON.stringify({ type: "cache-write-metadata", id: data.id, metadata }));
return data;
},
diff --git a/src/frontend/lib/stores/CacheStore.ts b/src/frontend/lib/stores/CacheStore.ts
index 798b2150..5182a066 100644
--- a/src/frontend/lib/stores/CacheStore.ts
+++ b/src/frontend/lib/stores/CacheStore.ts
@@ -1,19 +1,23 @@
-import type { SubsidiaryUUID, CacheUUID, CacheObject } from "@shared/types/cache";
+import type {
+ SubsidiaryUUID,
+ CacheUUID,
+ CacheObject,
+ CacheUpdateNotification,
+ CacheMetadata,
+ CacheRequest,
+ CacheWriteResponse,
+} from "@shared/types/cache";
import { derived, get, writable } from "svelte/store";
// type CacheObjects = {
// [key: CacheUUID]: CacheObject;
// };
-type CacheObjects = CacheUUID[];
-
-type CacheResponse = {
- type: string;
- cache: CacheUUID[];
-};
+type CacheObjects = { [key: CacheUUID]: CacheMetadata };
+// type CacheObjects = CacheUUID[];
class CacheStore {
- private cacheStore = writable([]);
+ private cacheStore = writable({});
// private globalCache: { [key: CacheUUID]: SubsidiaryUUID } = {};
private ws: WebSocket;
@@ -25,11 +29,17 @@ class CacheStore {
this.ws.onmessage = (event) => {
if (typeof event.data === "string") {
- const data: CacheResponse = JSON.parse(event.data) as CacheResponse;
+ const data: CacheUpdateNotification = JSON.parse(event.data) as CacheUpdateNotification;
switch (data.type) {
case "cache-update":
- this.cacheStore.set(data.cache);
+ this.cacheStore.update((store) => {
+ for (const obj of data.cache) {
+ store[obj.uuid] = obj.metadata;
+ }
+
+ return store;
+ });
break;
}
}
@@ -37,15 +47,31 @@ class CacheStore {
};
}
- public refreshStore(cacheId: CacheUUID) {
- this.cacheStore.update((cache) => {
- cache.push(cacheId);
- return cache;
- });
+ public addCacheObject(blob: Blob, metadata?: CacheMetadata) {
+ if (metadata != null) {
+ const recv = (event: MessageEvent) => {
+ this.ws.removeEventListener("message", recv);
+ if (typeof event.data === "string") {
+ const data = JSON.parse(event.data) as CacheWriteResponse;
+ this.ws.send(JSON.stringify({ type: "cache-write-metadata", id: data.id, metadata }));
+ }
+ };
+ this.ws.addEventListener("message", recv);
+ }
+ this.ws.send(blob);
}
- public addCacheObject(Blob: Blob) {
- this.ws.send(Blob);
+ public async get(uuid: CacheUUID): Promise {
+ const payload = JSON.stringify({ type: "cache-get", id: uuid });
+
+ return new Promise((resolve, reject) => {
+ this.ws.send(payload);
+ const recv = (event: MessageEvent) => {
+ this.ws.removeEventListener("message", recv); // Remove this listener
+ resolve(event.data as Buffer);
+ };
+ this.ws.addEventListener("message", recv);
+ });
}
public get subscribe() {
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index 363a5cb2..c34e9647 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -1,27 +1,61 @@
-
- {#each $cacheStore as id}
-
- {id}
-
+
+
+ Cache Id
+ Name
+ Type
+ Content
+
+ {#each Object.keys($cacheStore) as uuid}
+
+ {uuid.slice(0, 8)}
+ {$cacheStore[uuid].name ?? "-"}
+ {$cacheStore[uuid].contentType}
+ {#if ["image/png", "image/jpeg"].includes($cacheStore[uuid].contentType)}
+ {#await getBlobURL(uuid, $cacheStore[uuid].contentType) then src}
+
+
+
+ {:catch error}
+ error: {error.message}
+ {/await}
+ {/if}
+
{/each}
-
+
@@ -49,4 +83,17 @@
color: black;
z-index: 100;
}
+
+ table {
+ width: calc(100% - 8em);
+ margin: 4em auto;
+ }
+
+ table,
+ td,
+ th {
+ border: 1px solid white;
+ border-collapse: collapse;
+ padding: 0.4em;
+ }
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/CachePicker.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/CachePicker.svelte
index a55db90c..464dd072 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/CachePicker.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/CachePicker.svelte
@@ -20,12 +20,14 @@
$: valStore = inputStore.inputs[config.componentId];
-{#if items.length > 0}
+{#if Object.keys(items).length > 0}
{#key inputStore.inputs[config.componentId]}
- {#each items as itemKey}
- {itemKey}
+ {#each Object.keys(items) as itemKey}
+ {items[itemKey].name} [{itemKey}] ({items[itemKey].contentType})
{/each}
{/key}
diff --git a/src/shared/types/cache.ts b/src/shared/types/cache.ts
index 06aa7693..6ac6c3fd 100644
--- a/src/shared/types/cache.ts
+++ b/src/shared/types/cache.ts
@@ -20,21 +20,27 @@ export type CacheSubsidiary = {
export type CacheObject = {
uuid: CacheUUID;
data: Buffer;
- metadata: any;
+ metadata: CacheMetadata;
};
export type CacheRequest = {
type: string;
id: string;
- metadata?: any;
+ metadata?: CacheMetadata;
};
-export type CacheResponse =
- | {
- success: true;
- data?: string;
- }
- | {
- success: false;
- message?: string;
- };
+export type CacheWriteResponse = {
+ success: boolean;
+ id: CacheUUID;
+};
+
+export type CacheMetadata = {
+ contentType: string;
+ name?: string;
+ other?: any;
+};
+
+export type CacheUpdateNotification = {
+ type: string;
+ cache: { uuid: string; metadata: CacheMetadata }[];
+};
From e877d4b0a4f7f89aa1768066d18b60f49912bfea Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 21 Sep 2023 09:04:07 +0200
Subject: [PATCH 088/209] Split settings into components
---
src/electron/lib/api/apis/UtilApi.ts | 8 ++-
src/frontend/ui/base/settings/About.svelte | 63 +++++++++----------
.../ui/base/settings/AiSettings.svelte | 56 +++--------------
src/frontend/ui/base/settings/Settings.svelte | 12 ++--
.../ui/base/settings/utils/Button.svelte | 15 ++---
.../ui/base/settings/utils/Dropdown.svelte | 30 +++++++++
.../base/settings/utils/SettingsItem.svelte | 29 +++++++++
src/shared/types/setting.ts | 13 +++-
8 files changed, 124 insertions(+), 102 deletions(-)
create mode 100644 src/frontend/ui/base/settings/utils/Dropdown.svelte
create mode 100644 src/frontend/ui/base/settings/utils/SettingsItem.svelte
diff --git a/src/electron/lib/api/apis/UtilApi.ts b/src/electron/lib/api/apis/UtilApi.ts
index f54f5aed..8282432a 100644
--- a/src/electron/lib/api/apis/UtilApi.ts
+++ b/src/electron/lib/api/apis/UtilApi.ts
@@ -12,7 +12,7 @@ import {
getSecret,
type Settings,
} from "../../../utils/settings";
-import type { Setting, QueryResponse } from "../../../../shared/types";
+import type { Setting, QueryResponse, ButtonSetting } from "../../../../shared/types";
import { type ChatModel } from "../../../lib/ai/Model";
import { autoUpdater } from "electron-updater";
// import dotenv from "dotenv";
@@ -83,7 +83,7 @@ export class UtilApi implements ElectronMainApi {
// Add something extra validation
- async saveUserSetting(setting: Setting) {
+ async saveUserSetting(setting: Exclude) {
return await this.saveUserSettings([setting]);
}
@@ -91,7 +91,9 @@ export class UtilApi implements ElectronMainApi {
for (const setting of newSettings) {
if (setting.secret) {
setSecret(setting.id, setting.value.toString());
- } else settings.set(setting.id, setting.value);
+ } else {
+ settings.set(setting.id, setting.value);
+ }
}
return { status: "success" };
diff --git a/src/frontend/ui/base/settings/About.svelte b/src/frontend/ui/base/settings/About.svelte
index 26cab8a3..88aa8f2a 100644
--- a/src/frontend/ui/base/settings/About.svelte
+++ b/src/frontend/ui/base/settings/About.svelte
@@ -1,11 +1,9 @@
-
-
-
-
- Current Version: {$blixStore.blix.version}
-
-
Blix is up-to-date!
- {#if checkingForUpdates}
-
Checking for updates...
- {/if}
-
-
-
-
-
-
-
-
-
-
-
Get help
-
Get help on using Blix.
-
-
-
-
-
-
+
+ {#if checkingForUpdates}
+ Checking for updates...
+ {/if}
+
+
+
diff --git a/src/frontend/ui/base/settings/AiSettings.svelte b/src/frontend/ui/base/settings/AiSettings.svelte
index 3d949c8d..a2a06c99 100644
--- a/src/frontend/ui/base/settings/AiSettings.svelte
+++ b/src/frontend/ui/base/settings/AiSettings.svelte
@@ -2,7 +2,7 @@
import { getContext, onMount } from "svelte";
import type { Setting } from "../../../../shared/types";
import type { SettingsContext } from "./Settings.svelte";
- import SecureInput from "./utils/SecureInput.svelte";
+ import SettingsItem from "./utils/SettingsItem.svelte";
const { getSetting, saveSettings } = getContext("settings");
@@ -10,7 +10,7 @@
{
id: "OPENAI_API_KEY",
title: "Open AI Key",
- subtitle: "Required to use Open AI models such as GPT-3.5",
+ subtitle: "Required to use Open AI models",
type: "text",
secret: true,
value: "",
@@ -23,6 +23,7 @@
options: ["GPT-3.5", "GPT-4"],
},
];
+ let initializedSettings = false;
onMount(async () => {
const updatedSettings = await Promise.all(
@@ -38,7 +39,10 @@
);
settings = updatedSettings;
+ initializedSettings = true;
});
+
+ $: if (initializedSettings) saveSettings(settings);
@@ -49,52 +53,6 @@
{#each settings as item (item.id)}
-
-
- {item.title}
- {#if item.subtitle}
- {item.subtitle}
- {/if}
-
-
- {#if item.type === "text" && item.secret}
-
- {:else if item.type === "dropdown"}
-
- {#each item.options as option}
- {#if item.value === option}
- {option}
- {:else}
- {option}
- {/if}
- {/each}
-
- {/if}
-
+
{/each}
-
-
- Save
-
-
-
diff --git a/src/frontend/ui/base/settings/Settings.svelte b/src/frontend/ui/base/settings/Settings.svelte
index aef7aed5..ba5a5d87 100644
--- a/src/frontend/ui/base/settings/Settings.svelte
+++ b/src/frontend/ui/base/settings/Settings.svelte
@@ -23,15 +23,13 @@
getSetting,
});
+ /** State of buttons don't get saved */
async function saveSettings(settings: Setting[]) {
- const res = await window.apis.utilApi.saveUserSettings(settings);
+ const filteredSettings = settings.filter((setting) => setting.type !== "button");
- if (res.status === "success") {
- toastStore.trigger({
- message: "Your preferences have been updated successfully",
- type: "success",
- });
- } else {
+ const res = await window.apis.utilApi.saveUserSettings(filteredSettings);
+
+ if (res.status === "error") {
toastStore.trigger({
message: "Something went wrong while updating your preferences",
type: "error",
diff --git a/src/frontend/ui/base/settings/utils/Button.svelte b/src/frontend/ui/base/settings/utils/Button.svelte
index 1274a874..e7274c74 100644
--- a/src/frontend/ui/base/settings/utils/Button.svelte
+++ b/src/frontend/ui/base/settings/utils/Button.svelte
@@ -1,13 +1,14 @@
- {title}
+ {item.value}
diff --git a/src/frontend/ui/base/settings/utils/Dropdown.svelte b/src/frontend/ui/base/settings/utils/Dropdown.svelte
new file mode 100644
index 00000000..2cbe5fab
--- /dev/null
+++ b/src/frontend/ui/base/settings/utils/Dropdown.svelte
@@ -0,0 +1,30 @@
+
+
+
+ {#each item.options as option}
+ {#if item.value === option}
+ {option}
+ {:else}
+ {option}
+ {/if}
+ {/each}
+
+
+
diff --git a/src/frontend/ui/base/settings/utils/SettingsItem.svelte b/src/frontend/ui/base/settings/utils/SettingsItem.svelte
new file mode 100644
index 00000000..8f8811a1
--- /dev/null
+++ b/src/frontend/ui/base/settings/utils/SettingsItem.svelte
@@ -0,0 +1,29 @@
+
+
+
+
+
+
{item.title}
+ {#if item.subtitle}
+
{item.subtitle}
+ {/if}
+
+
+
+
+ {#if item.type === "text" && item.secret}
+
+ {:else if item.type === "dropdown"}
+
+ {:else if item.type === "button"}
+
+ {/if}
+
+
diff --git a/src/shared/types/setting.ts b/src/shared/types/setting.ts
index 83ef56be..3a41faf8 100644
--- a/src/shared/types/setting.ts
+++ b/src/shared/types/setting.ts
@@ -12,6 +12,7 @@ export const userSettingSections = [
export type UserSettingsCategoryId =
(typeof userSettingSections)[number]["categories"][number]["id"];
+
export type UserSettingsCategoryTitle =
(typeof userSettingSections)[number]["categories"][number]["title"];
@@ -49,6 +50,11 @@ export interface ToggleSetting extends UserSetting {
type: "toggle";
value: boolean;
}
+export interface ButtonSetting extends UserSetting {
+ type: "button";
+ value: string;
+ onClick: (item: Setting) => void;
+}
export interface KeyboardShortcuts extends UserSetting {
id: "keyboardShortcuts";
@@ -61,7 +67,12 @@ export interface KeyboardShortcut extends UserSetting {
value: string[];
}
-export type Setting = DropdownSetting | InputSetting | ToggleSetting | KeyboardShortcuts;
+export type Setting =
+ | DropdownSetting
+ | InputSetting
+ | ToggleSetting
+ | KeyboardShortcuts
+ | ButtonSetting;
type Prettify
= T extends object ? { [K in keyof T]: T[K] } : never;
From 2aef87a8beb31385964a3cb95a600512ec4bf21a Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 21 Sep 2023 10:12:32 +0200
Subject: [PATCH 089/209] Add some auto update stuff
---
package.json | 2 +-
src/frontend/lib/api/apis/UtilClientApi.ts | 6 ++-
src/frontend/lib/stores/BlixStore.ts | 13 ++++++-
src/frontend/ui/base/settings/About.svelte | 8 ++--
src/index.ts | 37 ++++++++++--------
test.json | 44 ++++++++++++++++++++++
6 files changed, 87 insertions(+), 23 deletions(-)
create mode 100644 test.json
diff --git a/package.json b/package.json
index 2eff7961..acf7b8ad 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - An AI-assisted graph photo editor",
- "version": "1.0.12",
+ "version": "1.0.13",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
diff --git a/src/frontend/lib/api/apis/UtilClientApi.ts b/src/frontend/lib/api/apis/UtilClientApi.ts
index 53fac517..3f362372 100644
--- a/src/frontend/lib/api/apis/UtilClientApi.ts
+++ b/src/frontend/lib/api/apis/UtilClientApi.ts
@@ -1,6 +1,6 @@
import type { ElectronWindowApi } from "electron-affinity/window";
import { toastStore, type ToastOptions } from "../../stores/ToastStore";
-import { blixStore, setInitialStores } from "../../../lib/stores/BlixStore";
+import { blixStore, setInitialStores, type BlixStoreState } from "../../../lib/stores/BlixStore";
import { bindMainApis } from "../apiInitializer";
export class UtilClientApi implements ElectronWindowApi {
@@ -18,4 +18,8 @@ export class UtilClientApi implements ElectronWindowApi {
await setInitialStores();
blixStore.update((state) => ({ ...state, blixReady: true }));
}
+
+ refreshBlixStore(data: Partial) {
+ blixStore.refreshStore(data);
+ }
}
diff --git a/src/frontend/lib/stores/BlixStore.ts b/src/frontend/lib/stores/BlixStore.ts
index 8bfc5a2c..d2d6e7dc 100644
--- a/src/frontend/lib/stores/BlixStore.ts
+++ b/src/frontend/lib/stores/BlixStore.ts
@@ -16,9 +16,14 @@ const blixStoreDefaults = {
blix: {
version: "",
},
+ update: {
+ isAvailable: false,
+ isDownloaded: false,
+ version: "",
+ },
};
-type BlixStoreState = typeof blixStoreDefaults;
+export type BlixStoreState = typeof blixStoreDefaults;
export class BlixStore {
store = writable(blixStoreDefaults);
@@ -27,6 +32,10 @@ export class BlixStore {
return await window.apis.utilApi.checkForUpdates();
}
+ public refreshStore(state: Partial) {
+ this.store.update((s) => ({ ...s, ...state }));
+ }
+
public get subscribe() {
return this.store.subscribe;
}
@@ -41,7 +50,7 @@ export const blixStore = new BlixStore();
export async function setInitialStores() {
// BLix store
const res = await window.apis.utilApi.getInfo();
- blixStore.update((state) => ({ ...state, ...res }));
+ blixStore.refreshStore(res);
// Command store
const command = await window.apis.commandApi.getCommands();
diff --git a/src/frontend/ui/base/settings/About.svelte b/src/frontend/ui/base/settings/About.svelte
index 88aa8f2a..7ac749b2 100644
--- a/src/frontend/ui/base/settings/About.svelte
+++ b/src/frontend/ui/base/settings/About.svelte
@@ -8,7 +8,7 @@
async function checkForUpdates() {
checkingForUpdates = true;
- await blixStore.checkForUpdates();
+ console.log(JSON.stringify(await blixStore.checkForUpdates()));
await sleep(400);
checkingForUpdates = false;
}
@@ -23,10 +23,12 @@
item="{{
id: 'version',
title: `Current Version: ${$blixStore.blix.version}`,
- subtitle: 'Blix is up-to-date!',
+ subtitle: $blixStore.update.isAvailable
+ ? `Update is available: ${$blixStore.update.version}`
+ : 'Blix is up to date',
value: 'Check for updates',
type: 'button',
- onClick: () => checkForUpdates(),
+ onClick: checkForUpdates,
}}"
>
{#if checkingForUpdates}
diff --git a/src/index.ts b/src/index.ts
index beb51094..495382f8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -258,7 +258,9 @@ app.on("web-contents-created", (e, contents) => {
});
});
-// ========== AUTO UPDATER ==========//
+// ==================================================================
+// AUTO UPDATER
+// ==================================================================
if (isProd) {
autoUpdater.checkForUpdates().catch((err) => {
@@ -268,26 +270,29 @@ if (isProd) {
autoUpdater.logger = logger;
-autoUpdater.on("update-available", () => {
- notification = new Notification({
- title: "BLix",
- body: "Updates are available. Click to download.",
- silent: true,
- // icon: nativeImage.createFromPath(join(__dirname, "..", "assets", "icon.png"),
- });
- notification.show();
- notification.on("click", () => {
- autoUpdater.downloadUpdate().catch((err) => {
- logger.error(JSON.stringify(err));
- });
+autoUpdater.on("update-available", (updateInfo) => {
+ mainWindow?.apis.utilClientApi.refreshBlixStore({
+ update: { isAvailable: true, isDownloaded: false, version: updateInfo.version },
});
+
+ // notification.on("click", () => {
+ // autoUpdater.downloadUpdate().catch((err) => {
+ // logger.error(JSON.stringify(err));
+ // });
+ // });
});
-autoUpdater.on("update-not-available", () => {
- blix?.sendInformationMessage("You are up to date!");
+autoUpdater.on("update-not-available", (updateInfo) => {
+ mainWindow?.apis.utilClientApi.refreshBlixStore({
+ update: { isAvailable: false, isDownloaded: false, version: updateInfo.version },
+ });
});
-autoUpdater.on("update-downloaded", () => {
+autoUpdater.on("update-downloaded", (updateInfo) => {
+ mainWindow?.apis.utilClientApi.refreshBlixStore({
+ update: { isAvailable: true, isDownloaded: true, version: updateInfo.version },
+ });
+
notification = new Notification({
title: "Blix",
body: "The updates are ready. Click to quit and install.",
diff --git a/test.json b/test.json
new file mode 100644
index 00000000..bf7f9bb5
--- /dev/null
+++ b/test.json
@@ -0,0 +1,44 @@
+{
+ "versionInfo": {
+ "tag": "v1.0.12",
+ "version": "1.0.12",
+ "files": [
+ {
+ "url": "Blix-1.0.12.dmg",
+ "sha512": "74kfMvWO2D521hYQbsugN+U47eHRFs1T7S42jVEx2rQZZvH38iuRBWzezW5eiRqkoc3sSxXUbNNw8foFtpv+jw==",
+ "size": 140012174
+ },
+ {
+ "url": "Blix-1.0.12-arm64.dmg",
+ "sha512": "+loDMnjkcZyn/e8TullQ6rZRGcoIgJCGQQsSwreFSc0/fHA3niE+NfplzLliIdboi6KcJFatbxX+a0sdEv+HMw==",
+ "size": 136332672
+ }
+ ],
+ "path": "Blix-1.0.12.dmg",
+ "sha512": "74kfMvWO2D521hYQbsugN+U47eHRFs1T7S42jVEx2rQZZvH38iuRBWzezW5eiRqkoc3sSxXUbNNw8foFtpv+jw==",
+ "releaseDate": "2023-09-19T17:18:01.602Z",
+ "releaseName": "Blix v1.0.12",
+ "releaseNotes": ""
+ },
+ "updateInfo": {
+ "tag": "v1.0.12",
+ "version": "1.0.12",
+ "files": [
+ {
+ "url": "Blix-1.0.12.dmg",
+ "sha512": "74kfMvWO2D521hYQbsugN+U47eHRFs1T7S42jVEx2rQZZvH38iuRBWzezW5eiRqkoc3sSxXUbNNw8foFtpv+jw==",
+ "size": 140012174
+ },
+ {
+ "url": "Blix-1.0.12-arm64.dmg",
+ "sha512": "+loDMnjkcZyn/e8TullQ6rZRGcoIgJCGQQsSwreFSc0/fHA3niE+NfplzLliIdboi6KcJFatbxX+a0sdEv+HMw==",
+ "size": 136332672
+ }
+ ],
+ "path": "Blix-1.0.12.dmg",
+ "sha512": "74kfMvWO2D521hYQbsugN+U47eHRFs1T7S42jVEx2rQZZvH38iuRBWzezW5eiRqkoc3sSxXUbNNw8foFtpv+jw==",
+ "releaseDate": "2023-09-19T17:18:01.602Z",
+ "releaseName": "Blix v1.0.12",
+ "releaseNotes": ""
+ }
+}
\ No newline at end of file
From 1e587556dcb82027d433fb6a3dcd616ee12ae8b8 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 21 Sep 2023 10:47:12 +0200
Subject: [PATCH 090/209] New version test
---
.github/workflows/build.yml | 6 ++++--
package.json | 2 +-
src/frontend/ui/base/settings/About.svelte | 2 ++
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6589ec2e..05b39dc3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -28,11 +28,13 @@ jobs:
- name: Build/Release Blix
run: |
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
- npm run dist -- --linux deb AppImage --x64 --arm64
+ # npm run dist -- --linux deb AppImage --x64 --arm64
+ npm run dist -- --linux deb AppImage --x86
elif [ "${{ matrix.os }}" = "windows-latest" ]; then
npm run dist -- --windows nsis
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
- npm run dist -- --mac dmg --x64 --arm64
+ # npm run dist -- --mac dmg --x64 --arm64
+ npm run dist -- --mac dmg --arm64
fi
env:
GH_TOKEN: ${{ secrets.TEST_TOKEN }}
diff --git a/package.json b/package.json
index acf7b8ad..f81c446c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - An AI-assisted graph photo editor",
- "version": "1.0.13",
+ "version": "1.0.14",
"repository": {
"type": "git",
"url": "https://github.com/ArmandKrynauw/Blix.git"
diff --git a/src/frontend/ui/base/settings/About.svelte b/src/frontend/ui/base/settings/About.svelte
index 7ac749b2..3da8a022 100644
--- a/src/frontend/ui/base/settings/About.svelte
+++ b/src/frontend/ui/base/settings/About.svelte
@@ -46,4 +46,6 @@
onClick: () => ({}),
}}"
/>
+
+ Hello World
From 3d6206480d805221f221e03666d559c47c5fc868 Mon Sep 17 00:00:00 2001
From: Jake Mileham
Date: Thu, 21 Sep 2023 10:51:27 +0200
Subject: [PATCH 091/209] Fix media tile loading after using AI
---
src/frontend/lib/stores/GraphStore.ts | 8 ++++++--
src/frontend/lib/stores/MediaStore.ts | 10 ++++++----
.../graph/nodeUICcomponents/TextInput.svelte | 15 ++++++---------
3 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/src/frontend/lib/stores/GraphStore.ts b/src/frontend/lib/stores/GraphStore.ts
index aa2909d6..417d64f5 100644
--- a/src/frontend/lib/stores/GraphStore.ts
+++ b/src/frontend/lib/stores/GraphStore.ts
@@ -116,17 +116,21 @@ export class GraphStore {
for (const input in inputs) {
if (!inputs.hasOwnProperty(input)) continue;
- // console.log("SUB TO", node, "-->>", input)
let first = true;
+ let oldState: unknown;
this.uiInputUnsubscribers[node].push(
inputs[input].subscribe((state) => {
// Ensure the subscribe does not run when first created
if (first) {
first = false;
- } else {
+ oldState = state;
+ } else if (state !== oldState) {
+ // Prevent unnecessary updates when the value is the exact same as the prior
+ // Only would then update if maybe the set function is used but old state === new state
this.globalizeUIInputs(node, input).catch((err) => {
return;
});
+ oldState = state;
}
})
);
diff --git a/src/frontend/lib/stores/MediaStore.ts b/src/frontend/lib/stores/MediaStore.ts
index d1009900..0333e944 100644
--- a/src/frontend/lib/stores/MediaStore.ts
+++ b/src/frontend/lib/stores/MediaStore.ts
@@ -25,6 +25,7 @@ class MediaStore {
// Wont set store and notfiy subscribers if the value allOutputIds have remained the same.
// This is the case where the undo/redo system willchange something and then the stores see a change and do their
// own update but their update wont matter.
+
if (oldIds && ids && !equivSets(oldIds, ids)) {
this.outputIds.set(ids);
}
@@ -39,10 +40,11 @@ class MediaStore {
}
public async getMediaReactive(mediaId: MediaOutputId) {
- await window.apis.mediaApi.subscribeToMedia(mediaId).catch((err) => {
- return;
- });
-
+ if (mediaId) {
+ await window.apis.mediaApi.subscribeToMedia(mediaId).catch((err) => {
+ return;
+ });
+ }
// If we do not have a frontend copy of the media, fetch it
if (!get(this.store)[mediaId]) {
const media = await window.apis.mediaApi.getDisplayableMedia(mediaId);
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
index 45685a92..710f14df 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
@@ -13,19 +13,16 @@
if (!inputStore.inputs[config.componentId]) inputStore.inputs[config.componentId] = writable("");
let valStore;
- let first = true;
- $: {
- valStore = inputStore.inputs[config.componentId];
- if (!first) {
- dispatch("inputInteraction", { id: config.componentId, value: $valStore });
- } else {
- first = false;
- }
+ $: valStore = inputStore.inputs[config.componentId];
+
+ // Only add event when user changes text, if pasted by ai, do nothing
+ function handleInteraction() {
+ dispatch("inputInteraction", { id: config.componentId, value: $valStore });
}
-
+
diff --git a/src/shared/types/cache.ts b/src/shared/types/cache.ts
index 6ac6c3fd..674e620b 100644
--- a/src/shared/types/cache.ts
+++ b/src/shared/types/cache.ts
@@ -26,6 +26,7 @@ export type CacheObject = {
export type CacheRequest = {
type: string;
id: string;
+ messageId?: string;
metadata?: CacheMetadata;
};
From 062e33aae18dab2c8c0049f39b7e7ecc275a8d6d Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 21 Sep 2023 22:48:39 +0200
Subject: [PATCH 120/209] Remove token
---
electron-builder.yml | 4 ----
1 file changed, 4 deletions(-)
delete mode 100644 electron-builder.yml
diff --git a/electron-builder.yml b/electron-builder.yml
deleted file mode 100644
index b5c3ed33..00000000
--- a/electron-builder.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-appId: com.the-spanish-inquisition.blix
-publish:
- provider: github
- token: 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v
\ No newline at end of file
From 62dd3353496cf8c1190f8c25324756f65bdf29f0 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Thu, 21 Sep 2023 23:12:26 +0200
Subject: [PATCH 121/209] Disable webview dev buttons
---
src/frontend/ui/tiles/WebView.svelte | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/frontend/ui/tiles/WebView.svelte b/src/frontend/ui/tiles/WebView.svelte
index 976e4df5..b9cd3c36 100644
--- a/src/frontend/ui/tiles/WebView.svelte
+++ b/src/frontend/ui/tiles/WebView.svelte
@@ -64,10 +64,10 @@
{#await asyncSrc then src}
{#if src !== null}
-
+
From d2d378d97458939286837568b9e7b45002dafe92 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Fri, 22 Sep 2023 00:20:07 +0200
Subject: [PATCH 122/209] Fix cache system race condition for write() and in
webview preload
---
src/electron/lib/cache/CacheManager.ts | 52 ++++++------
src/electron/lib/webviews/preload.ts | 92 +++++++++++++++++----
src/frontend/lib/stores/CacheStore.ts | 108 ++++++++++++++++---------
src/shared/types/cache.ts | 16 ++--
4 files changed, 185 insertions(+), 83 deletions(-)
diff --git a/src/electron/lib/cache/CacheManager.ts b/src/electron/lib/cache/CacheManager.ts
index c2e1f1aa..1a83763c 100644
--- a/src/electron/lib/cache/CacheManager.ts
+++ b/src/electron/lib/cache/CacheManager.ts
@@ -1,12 +1,14 @@
// Blix caching system primarily for large binary objects
-import type {
- SubsidiaryUUID,
- CacheUUID,
- CacheSubsidiary,
- CacheObject,
- CacheRequest,
- CacheUpdateNotification,
- CacheWriteResponse,
+import {
+ type SubsidiaryUUID,
+ type CacheUUID,
+ type CacheSubsidiary,
+ type CacheObject,
+ type CacheRequest,
+ type CacheUpdateNotification,
+ type CacheWriteResponse,
+ type CacheResponse,
+ CACHE_MESSAGE_ID_SIZE,
} from "../../../shared/types/cache";
import WebSocket from "ws";
@@ -63,27 +65,26 @@ export class CacheManager {
case "cache-write-metadata":
this.writeMetadata(data.id, data.metadata);
this.notifyListeners();
+
+ socket.send(
+ JSON.stringify({ success: true, messageId: data.messageId } as CacheResponse)
+ );
break;
case "cache-get":
- // TODO: check if exist
const messageId = data.messageId;
- if (messageId != null) {
- // Send payload on this message id
- socket.send(
- Buffer.concat([
- Buffer.from(messageId),
- this.cache[data.id]?.data ?? Buffer.from([]),
- ])
- );
- } else {
- // Send anonymous payload
- socket.send(this.cache[data.id]?.data ?? null);
- }
+ socket.send(
+ Buffer.concat([
+ Buffer.from(messageId),
+ this.cache[data.id]?.data ?? Buffer.from([]),
+ ])
+ );
break;
case "cache-delete":
this.delete(data.id);
// TODO: check if exist
- socket.send(JSON.stringify({ success: true }));
+ socket.send(
+ JSON.stringify({ success: true, messageId: data.messageId } as CacheResponse)
+ );
this.notifyListeners();
break;
@@ -95,8 +96,11 @@ export class CacheManager {
logger.info("Unknown cache message type", data.type);
}
} else if (event.data instanceof Buffer) {
- const id = this.writeContent(event.data);
- socket.send(JSON.stringify({ success: true, id } as CacheWriteResponse));
+ const messageId = event.data.subarray(0, CACHE_MESSAGE_ID_SIZE).toString("ascii");
+ const data = event.data.subarray(CACHE_MESSAGE_ID_SIZE);
+
+ const id = this.writeContent(data);
+ socket.send(JSON.stringify({ success: true, id, messageId } as CacheWriteResponse));
// Send cache update to all listeners
this.notifyListeners();
diff --git a/src/electron/lib/webviews/preload.ts b/src/electron/lib/webviews/preload.ts
index 630950ab..9a5da9a2 100644
--- a/src/electron/lib/webviews/preload.ts
+++ b/src/electron/lib/webviews/preload.ts
@@ -1,21 +1,60 @@
// This is the preload script for the webviews.
-import { CacheMetadata, CacheWriteResponse } from "@shared/types/cache";
+import {
+ CacheMetadata,
+ CacheRequest,
+ CacheResponse,
+ CacheWriteResponse,
+} from "@shared/types/cache";
// It exposes some IPC functions so the webview can communicate with its parent renderer.
const { ipcRenderer, contextBridge } = require("electron");
+const MESSAGE_ID_SIZE = 32; // bytes
+// String of MESSAGE_ID_SIZE random bytes in hex
+const randomMessageId = () => {
+ return Array.from({ length: MESSAGE_ID_SIZE / 2 }, () =>
+ Math.floor(Math.random() * 256)
+ .toString(16)
+ .padStart(2, "0")
+ ).join("");
+};
+
const ws = new WebSocket("ws://localhost:60606");
ws.binaryType = "blob";
-function sendAndRecieveData(payload: string | Blob): Promise {
+const lobby: { [key: string]: (value: Blob | any) => void } = {};
+
+ws.onmessage = (event) => {
+ if (typeof event.data === "string") {
+ const payload = JSON.parse(event.data) as CacheResponse;
+ const messageId = payload.messageId;
+
+ if (lobby[messageId] != null) {
+ lobby[messageId](payload);
+ delete lobby[messageId];
+ }
+ } else if (event.data instanceof Blob) {
+ // Check if there's a listener in the lobby
+ event.data
+ .slice(0, MESSAGE_ID_SIZE)
+ .text()
+ .then((messageId) => {
+ if (lobby[messageId] != null) {
+ // Listener found, resolve promise
+ const data = event.data.slice(MESSAGE_ID_SIZE);
+ lobby[messageId](data);
+ delete lobby[messageId];
+ }
+ });
+ }
+};
+
+// Low-level RPC wrapper for sending/receiving data with CacheManager
+async function sendCachePayload(messageId: string, payload: Blob | string) {
return new Promise((resolve, reject) => {
+ lobby[messageId] = resolve;
ws.send(payload);
- const recv = (event: MessageEvent) => {
- ws.removeEventListener("message", recv); // Remove this listener
- resolve(event.data);
- };
- ws.addEventListener("message", recv);
});
}
@@ -29,18 +68,39 @@ contextBridge.exposeInMainWorld("api", {
});
contextBridge.exposeInMainWorld("cache", {
- write: async (content: Blob, metadata: CacheMetadata) => {
- const response: string = await sendAndRecieveData(new Blob([content]));
- const data = JSON.parse(response) as CacheWriteResponse;
- ws.send(JSON.stringify({ type: "cache-write-metadata", id: data.id, metadata }));
- return data;
+ write: async (content: Blob, metadata?: CacheMetadata) => {
+ // Write cache object
+ const messageId = randomMessageId();
+ const payload = new Blob([messageId, content]);
+ const writeResp = (await sendCachePayload(messageId, payload)) as CacheWriteResponse;
+
+ if (!writeResp.success) return null;
+
+ if (metadata != null) {
+ // Write cache metadata
+ const metadataMessageId = randomMessageId();
+ const metadataPayload = JSON.stringify({
+ type: "cache-write-metadata",
+ id: writeResp.id,
+ messageId,
+ metadata,
+ } as CacheRequest);
+ const metadataResp = (await sendCachePayload(
+ metadataMessageId,
+ metadataPayload
+ )) as CacheResponse;
+ }
+
+ return writeResp.id;
},
get: async (id: string) => {
- const results = await sendAndRecieveData(JSON.stringify({ type: "cache-get", id }));
- return results;
+ const messageId = randomMessageId();
+ const payload = JSON.stringify({ type: "cache-get", id, messageId } as CacheRequest);
+ return sendCachePayload(messageId, payload);
},
delete: async (id: string) => {
- const results: string = await sendAndRecieveData(JSON.stringify({ type: "cache-delete", id }));
- return JSON.parse(results);
+ const messageId = randomMessageId();
+ const payload = JSON.stringify({ type: "cache-delete", id, messageId } as CacheRequest);
+ return sendCachePayload(messageId, payload);
},
});
diff --git a/src/frontend/lib/stores/CacheStore.ts b/src/frontend/lib/stores/CacheStore.ts
index fae1333a..9c2aa56d 100644
--- a/src/frontend/lib/stores/CacheStore.ts
+++ b/src/frontend/lib/stores/CacheStore.ts
@@ -1,13 +1,15 @@
-import type {
- SubsidiaryUUID,
- CacheUUID,
- CacheObject,
- CacheUpdateNotification,
- CacheMetadata,
- CacheRequest,
- CacheWriteResponse,
+import {
+ type SubsidiaryUUID,
+ type CacheUUID,
+ type CacheObject,
+ type CacheUpdateNotification,
+ type CacheMetadata,
+ type CacheRequest,
+ type CacheWriteResponse,
+ CACHE_MESSAGE_ID_SIZE,
+ type CacheResponse,
} from "@shared/types/cache";
-import { derived, get, writable } from "svelte/store";
+import { derived, get, writable, type Writable } from "svelte/store";
// type CacheObjects = {
// [key: CacheUUID]: CacheObject;
@@ -16,22 +18,24 @@ import { derived, get, writable } from "svelte/store";
type CacheObjects = { [key: CacheUUID]: CacheMetadata };
// type CacheObjects = CacheUUID[];
-const MESSAGE_ID_SIZE = 32; // bytes
-
// String of MESSAGE_ID_SIZE random bytes in hex
const randomMessageId = () => {
- return Array.from({ length: MESSAGE_ID_SIZE / 2 }, () =>
+ return Array.from({ length: CACHE_MESSAGE_ID_SIZE / 2 }, () =>
Math.floor(Math.random() * 256)
.toString(16)
.padStart(2, "0")
).join("");
};
+function isUpdateNotification(payload: any): payload is CacheUpdateNotification {
+ return (payload as { type: string }).type === "cache-update";
+}
+
class CacheStore {
private cacheStore = writable({});
// private globalCache: { [key: CacheUUID]: SubsidiaryUUID } = {};
private ws: WebSocket;
- private lobby: { [key: string]: (value: Blob) => void };
+ private lobby: { [key: string]: (value: Blob | any) => void };
constructor() {
this.ws = new WebSocket("ws://localhost:60606");
@@ -42,28 +46,34 @@ class CacheStore {
this.ws.onmessage = (event) => {
if (typeof event.data === "string") {
- const data: CacheUpdateNotification = JSON.parse(event.data) as CacheUpdateNotification;
-
- switch (data.type) {
- case "cache-update":
- this.cacheStore.update((store) => {
- for (const obj of data.cache) {
- store[obj.uuid] = obj.metadata;
- }
-
- return store;
- });
- break;
+ const payload = JSON.parse(event.data) as CacheResponse;
+
+ if (isUpdateNotification(payload)) {
+ // Handle notification
+ this.cacheStore.update((store) => {
+ for (const obj of payload.cache) {
+ store[obj.uuid] = obj.metadata;
+ }
+ return store;
+ });
+ } else {
+ // Handle response
+ const messageId = payload.messageId;
+
+ if (this.lobby[messageId] != null) {
+ this.lobby[messageId](payload);
+ delete this.lobby[messageId];
+ }
}
} else if (event.data instanceof Blob) {
// Check if there's a listener in the lobby
event.data
- .slice(0, MESSAGE_ID_SIZE)
+ .slice(0, CACHE_MESSAGE_ID_SIZE)
.text()
.then((messageId) => {
if (this.lobby[messageId] != null) {
// Listener found, resolve promise
- const payload = (event.data as Blob).slice(MESSAGE_ID_SIZE);
+ const payload = (event.data as Blob).slice(CACHE_MESSAGE_ID_SIZE);
this.lobby[messageId](payload);
delete this.lobby[messageId];
}
@@ -74,23 +84,45 @@ class CacheStore {
};
}
- public addCacheObject(blob: Blob, metadata?: CacheMetadata) {
+ // Low-level RPC wrapper for sending/receiving data with CacheManager
+ async sendCachePayload(messageId: string, payload: Blob | string) {
+ return new Promise((resolve, reject) => {
+ this.lobby[messageId] = resolve;
+ this.ws.send(payload);
+ });
+ }
+
+ public async addCacheObject(blob: Blob, metadata?: CacheMetadata) {
+ // this.ws.send(blob);
+ // if (metadata != null) {
+ // const data = JSON.parse(event.data) as CacheWriteResponse;
+ // this.ws.send(JSON.stringify({ type: "cache-write-metadata", id: data.id, metadata }) as CacheRequest);
+ // }
+
+ // Write cache object
+ const messageId = randomMessageId();
+ const payload = new Blob([messageId, blob]);
+ const writeResp = (await this.sendCachePayload(messageId, payload)) as CacheWriteResponse;
+
+ if (!writeResp.success) return null;
+
if (metadata != null) {
- const recv = (event: MessageEvent) => {
- this.ws.removeEventListener("message", recv);
- if (typeof event.data === "string") {
- const data = JSON.parse(event.data) as CacheWriteResponse;
- this.ws.send(JSON.stringify({ type: "cache-write-metadata", id: data.id, metadata }));
- }
- };
- this.ws.addEventListener("message", recv);
+ // Write cache metadata
+ const metadataMessageId = randomMessageId();
+ const metadataPayload = JSON.stringify({
+ type: "cache-write-metadata",
+ id: writeResp.id,
+ messageId,
+ metadata,
+ } as CacheRequest);
+ (await this.sendCachePayload(metadataMessageId, metadataPayload)) as CacheResponse;
}
- this.ws.send(blob);
+
+ return writeResp.id;
}
public async get(uuid: CacheUUID): Promise {
const messageId = randomMessageId();
- const messageIdBlob = new Blob([messageId]);
const payload = JSON.stringify({ type: "cache-get", id: uuid, messageId } as CacheRequest);
diff --git a/src/shared/types/cache.ts b/src/shared/types/cache.ts
index 674e620b..13d58e74 100644
--- a/src/shared/types/cache.ts
+++ b/src/shared/types/cache.ts
@@ -1,5 +1,7 @@
import { type UUID } from "../../shared/utils/UniqueEntity";
+export const CACHE_MESSAGE_ID_SIZE = 32; // bytes
+
export enum CacheSubsidiaryType {
Manager,
Frontend,
@@ -23,18 +25,22 @@ export type CacheObject = {
metadata: CacheMetadata;
};
+type CacheRequestType = "cache-subscribe" | "cache-delete" | "cache-get" | "cache-write-metadata";
+
export type CacheRequest = {
- type: string;
+ type: CacheRequestType;
id: string;
- messageId?: string;
+ messageId: string;
metadata?: CacheMetadata;
};
-export type CacheWriteResponse = {
+export type CacheResponse = {
success: boolean;
- id: CacheUUID;
+ messageId: string;
};
+export type CacheWriteResponse = CacheResponse & { id: CacheUUID };
+
export type CacheMetadata = {
contentType: string;
name?: string;
@@ -42,6 +48,6 @@ export type CacheMetadata = {
};
export type CacheUpdateNotification = {
- type: string;
+ type: "cache-update";
cache: { uuid: string; metadata: CacheMetadata }[];
};
From 947a1a00ee44f3c398010c35356a578075e28c85 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Fri, 22 Sep 2023 01:08:28 +0200
Subject: [PATCH 123/209] Skeleton PluginBrowser UI + add markdown rendering
---
package-lock.json | 13 ++
package.json | 1 +
src/electron/lib/api/apis/UtilApi.ts | 5 +
src/frontend/lib/stores/ShortcutStore.ts | 1 +
src/frontend/ui/base/Markdown.svelte | 75 +++++++++++
src/frontend/ui/base/PluginBrowser.svelte | 123 ++++++++++++++++++
src/frontend/ui/base/Settings.svelte | 12 +-
.../graph/nodeUICcomponents/TextInput.svelte | 4 +-
8 files changed, 229 insertions(+), 5 deletions(-)
create mode 100644 src/frontend/ui/base/Markdown.svelte
create mode 100644 src/frontend/ui/base/PluginBrowser.svelte
diff --git a/package-lock.json b/package-lock.json
index ac0ac8e8..2a41a3e6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -76,6 +76,7 @@
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"lint-staged": "^13.2.2",
+ "marked": "^9.0.3",
"nodemon": "^2.0.22",
"npm-run-all": "^4.1.5",
"playwright": "^1.36.0",
@@ -11753,6 +11754,18 @@
"tmpl": "1.0.5"
}
},
+ "node_modules/marked": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-9.0.3.tgz",
+ "integrity": "sha512-pI/k4nzBG1PEq1J3XFEHxVvjicfjl8rgaMaqclouGSMPhk7Q3Ejb2ZRxx/ZQOcQ1909HzVoWCFYq6oLgtL4BpQ==",
+ "dev": true,
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 16"
+ }
+ },
"node_modules/matcher": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
diff --git a/package.json b/package.json
index 7e620111..9bf044d3 100644
--- a/package.json
+++ b/package.json
@@ -136,6 +136,7 @@
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"lint-staged": "^13.2.2",
+ "marked": "^9.0.3",
"nodemon": "^2.0.22",
"npm-run-all": "^4.1.5",
"playwright": "^1.36.0",
diff --git a/src/electron/lib/api/apis/UtilApi.ts b/src/electron/lib/api/apis/UtilApi.ts
index 39313be2..d1035374 100644
--- a/src/electron/lib/api/apis/UtilApi.ts
+++ b/src/electron/lib/api/apis/UtilApi.ts
@@ -75,6 +75,11 @@ export class UtilApi implements ElectronMainApi {
// const secrets = getSecrets();
const userSettings: UserSettingsCategory[] = [
+ {
+ id: "plugin_browser",
+ title: "Plugin Browser",
+ settings: [],
+ },
{
id: "ai_settings",
title: "AI Settings",
diff --git a/src/frontend/lib/stores/ShortcutStore.ts b/src/frontend/lib/stores/ShortcutStore.ts
index d40f8301..2d491ec1 100644
--- a/src/frontend/lib/stores/ShortcutStore.ts
+++ b/src/frontend/lib/stores/ShortcutStore.ts
@@ -120,6 +120,7 @@ class ShortcutStore {
"blix.settings.hide": ["[Escape]"],
"blix.splash.hide": ["[Escape]"],
"blix.projects.save": ["ctrl+[KeyS]", "meta+[KeyS]"],
+ "blix.pluginBrowser.deselectPlugin": ["[Escape]"],
});
public addActionShortcut(action: ShortcutAction, combo: ShortcutCombo) {
diff --git a/src/frontend/ui/base/Markdown.svelte b/src/frontend/ui/base/Markdown.svelte
new file mode 100644
index 00000000..bc2c53dd
--- /dev/null
+++ b/src/frontend/ui/base/Markdown.svelte
@@ -0,0 +1,75 @@
+
+
+{@html marked(markdown)}
+
+
diff --git a/src/frontend/ui/base/PluginBrowser.svelte b/src/frontend/ui/base/PluginBrowser.svelte
new file mode 100644
index 00000000..9165a7bc
--- /dev/null
+++ b/src/frontend/ui/base/PluginBrowser.svelte
@@ -0,0 +1,123 @@
+
+
+
+
+
+ {#if selectedPlugin != null}
+ {@const plugin = plugins[selectedPlugin]}
+
+
{plugin.name}
+
{selectedPlugin}
+
+
+
+
+ {/if}
+
+
+
+
+
+
diff --git a/src/frontend/ui/base/Settings.svelte b/src/frontend/ui/base/Settings.svelte
index 7ec26532..4a0e2807 100644
--- a/src/frontend/ui/base/Settings.svelte
+++ b/src/frontend/ui/base/Settings.svelte
@@ -5,6 +5,7 @@
import { toastStore } from "./../../lib/stores/ToastStore";
import { settingsStore } from "../../lib/stores/SettingsStore";
import ShortcutSettings from "../../ui/tiles/ShortcutSettings.svelte";
+ import PluginBrowser from "./PluginBrowser.svelte";
let selectedCategoryId = "";
let selectedCategory: UserSettingsCategory | undefined;
@@ -80,11 +81,15 @@
- {#if selectedCategory?.id === "ai_settings"}
+ {#if selectedCategory?.id === "plugin_browser"}
+
+
+ {:else if selectedCategory?.id === "ai_settings"}
+
API Keys
- Rest assured that none of your API keys get stored remotely. Your information is encrypted
- and maintained securely and solely within the confines of your own device.
+ None of your API keys get stored remotely. Your information is encrypted and maintained
+ securely and solely within the confines of your own device.
{#each selectedCategory.settings as item (item.id)}
@@ -111,6 +116,7 @@
Save
{:else if selectedCategory?.id === "keybind_settings"}
+
{#each selectedCategory.settings as item (item.id)}
diff --git a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
index eaf583dc..05f214c1 100644
--- a/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
+++ b/src/frontend/ui/utils/graph/nodeUICcomponents/TextInput.svelte
@@ -30,7 +30,7 @@
background-color: #1f1f28;
border: none;
padding: 0.1em;
-
- float: right;
+ padding-right: 0.8em;
+ margin: 0.2em;
}
From d87beaa38d8a7832edb94e62c25f38fe3d778eb2 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Fri, 22 Sep 2023 11:14:29 +0200
Subject: [PATCH 124/209] Add PluginApi; Add install/enable buttons to
PluginBrowser
---
src/electron/lib/api/MainApi.ts | 4 +-
src/electron/lib/api/apis/PluginApi.ts | 33 +++++++
src/frontend/lib/api/apiInitializer.ts | 2 +
src/frontend/lib/stores/CommandStore.ts | 2 +-
src/frontend/ui/base/PluginBrowser.svelte | 106 +++++++++++++++++++---
5 files changed, 134 insertions(+), 13 deletions(-)
create mode 100644 src/electron/lib/api/apis/PluginApi.ts
diff --git a/src/electron/lib/api/MainApi.ts b/src/electron/lib/api/MainApi.ts
index 5711ec15..43e51920 100644
--- a/src/electron/lib/api/MainApi.ts
+++ b/src/electron/lib/api/MainApi.ts
@@ -9,6 +9,7 @@ import { ToolboxApi } from "./apis/ToolboxApi";
import { MediaApi } from "./apis/MediaApi";
import { TileApi } from "./apis/TileApi";
import { TypeclassApi } from "./apis/TypeclassApi";
+import { PluginApi } from "./apis/PluginApi";
/**
* Expose all main process APIs to the renderer. This method will be called on
@@ -23,7 +24,8 @@ export function exposeMainApis(blix: Blix) {
const apis = {
utilApi: new UtilApi(blix),
projectApi: new ProjectApi(blix),
- pluginApi: new CommandApi(blix),
+ commandApi: new CommandApi(blix),
+ pluginApi: new PluginApi(blix),
graphApi: new GraphApi(blix),
typeclassApi: new TypeclassApi(blix),
mediaApi: new MediaApi(blix),
diff --git a/src/electron/lib/api/apis/PluginApi.ts b/src/electron/lib/api/apis/PluginApi.ts
new file mode 100644
index 00000000..7ec10eac
--- /dev/null
+++ b/src/electron/lib/api/apis/PluginApi.ts
@@ -0,0 +1,33 @@
+import type { ElectronMainApi } from "electron-affinity/main";
+import type { Blix } from "../../Blix";
+import type { CommandResponse } from "../../../../shared/types/index";
+import { URL } from "url";
+
+type PluginSignature = string;
+
+export class PluginApi implements ElectronMainApi {
+ private readonly blix: Blix;
+
+ constructor(blix: Blix) {
+ this.blix = blix;
+ }
+
+ async enablePlugin(plugin: PluginSignature) {
+ // this.blix.commandRegistry.addInstance(instance);
+ }
+ async disablePlugin(plugin: PluginSignature) {
+ // this.blix.commandRegistry.addInstance(instance);
+ }
+
+ async installPlugin(url: URL) {
+ // return await this.blix.commandRegistry.runCommand(id, params);
+ }
+
+ async uninstallPlugin(url: URL) {
+ // return await this.blix.commandRegistry.runCommand(id, params);
+ }
+
+ async getInstalledPlugins() {
+ return this.blix.pluginManager.pluginPaths;
+ }
+}
diff --git a/src/frontend/lib/api/apiInitializer.ts b/src/frontend/lib/api/apiInitializer.ts
index e9e786e1..a639bf55 100644
--- a/src/frontend/lib/api/apiInitializer.ts
+++ b/src/frontend/lib/api/apiInitializer.ts
@@ -5,6 +5,7 @@ import type { AwaitedType } from "electron-affinity/window";
import type { UtilApi } from "@electron/lib/api/apis/UtilApi";
import type { ProjectApi } from "@electron/lib/api/apis/ProjectApi";
import type { CommandApi } from "@electron/lib/api/apis/CommandApi";
+import type { PluginApi } from "@electron/lib/api/apis/PluginApi";
import type { GraphApi } from "@electron/lib/api/apis/GraphApi";
import type { TypeclassApi } from "@electron/lib/api/apis/TypeclassApi";
import type { ToolboxApi } from "@electron/lib/api/apis/ToolboxApi";
@@ -36,6 +37,7 @@ export async function bindMainApis() {
utilApi: await bindMainApi("UtilApi"),
projectApi: await bindMainApi("ProjectApi"),
commandApi: await bindMainApi("CommandApi"),
+ pluginApi: await bindMainApi("PluginApi"),
graphApi: await bindMainApi("GraphApi"),
typeclassApi: await bindMainApi("TypeclassApi"),
toolboxApi: await bindMainApi("ToolboxApi"),
diff --git a/src/frontend/lib/stores/CommandStore.ts b/src/frontend/lib/stores/CommandStore.ts
index 27a27b65..bb213665 100644
--- a/src/frontend/lib/stores/CommandStore.ts
+++ b/src/frontend/lib/stores/CommandStore.ts
@@ -18,7 +18,7 @@ function createCommandStore() {
}
async function addCommands(cmds: any[]) {
- // window.apis.pluginApi.addCommand(cmds);
+ // window.apis.commandApi.addCommand(cmds);
}
async function runCommand(id: string, args?: Record) {
diff --git a/src/frontend/ui/base/PluginBrowser.svelte b/src/frontend/ui/base/PluginBrowser.svelte
index 9165a7bc..366a59f9 100644
--- a/src/frontend/ui/base/PluginBrowser.svelte
+++ b/src/frontend/ui/base/PluginBrowser.svelte
@@ -7,20 +7,46 @@
type PluginData = {
name: string;
md: string;
+ installed: boolean;
+ enabled: boolean;
};
+ function installPlugin(id: string) {
+ plugins[id].installed = true;
+ plugins[id].enabled = true;
+ }
+
+ function uninstallPlugin(id: string) {
+ plugins[id].installed = false;
+ plugins[id].enabled = false;
+ }
+
+ function enablePlugin(id: string) {
+ plugins[id].enabled = true;
+ }
+
+ function disablePlugin(id: string) {
+ plugins[id].enabled = false;
+ }
+
const plugins: { [key: string]: PluginData } = {
"hello-plugin": {
name: "Hello Plugin",
md: "# Hello plugin\n\n> This is a plugin with basic dev testing nodes",
+ installed: true,
+ enabled: false,
},
"glfx-plugin": {
name: "GLFX Plugin",
md: "# GLFX Plugin\n\nThis is a plugin that uses the GLFX library to apply effects to images",
+ installed: false,
+ enabled: false,
},
blink: {
name: "Blink",
md: "# Blink\n\n> A feature-rich plugin for non-destructive image editing",
+ installed: true,
+ enabled: true,
},
};
@@ -43,19 +69,40 @@
on:click|stopPropagation="{() => (selectedPlugin = pluginId)}"
on:keypress
>
- {plugin.name}
- {pluginId}
+
+
+
{plugin.name}
+
+ {pluginId}
+ {plugin.installed ? (plugin.enabled ? "Installed" : "Disabled") : ""}
+
+
-
+
{/each}
{#if selectedPlugin != null}
- {@const plugin = plugins[selectedPlugin]}
+ {@const id = selectedPlugin}
+ {@const plugin = plugins[id]}
{plugin.name}
{selectedPlugin}
+
+ {#if plugin.installed}
+ Uninstall
+ {#if plugin.enabled}
+ Disable
+ {:else}
+ Enable
+ {/if}
+ {:else}
+ Install
+ {/if}
+
@@ -92,18 +139,31 @@
.pluginItem {
display: grid;
- grid-template-rows: 1.4em 0.8em;
+ grid-template-columns: 3em auto;
color: white;
padding: 0.4em;
+ height: 4em;
- .title {
- font-size: 1em;
- font-weight: bold;
+ .icon {
+ height: 100%;
+ width: 100%;
+ border: 1px solid magenta;
}
- .id {
- font-size: 0.6em;
- padding-left: 0.4em;
+ .details {
+ display: grid;
+ grid-template-rows: 1.4em 0.8em;
+ padding: 0.4em;
+
+ .title {
+ font-size: 1em;
+ font-weight: bold;
+ }
+
+ .info {
+ font-size: 0.6em;
+ padding-left: 0.4em;
+ }
}
}
@@ -115,6 +175,30 @@
.banner {
border: 1px solid green;
+ padding: 1em;
+ padding-top: 1.6em;
+
+ .title {
+ font-size: 1.6em;
+ font-weight: bold;
+ line-height: 0.9em;
+ }
+
+ .id {
+ font-size: 0.8em;
+ margin-left: 1em;
+ font-style: italic;
+ }
+
+ .buttons {
+ margin-top: 0.8em;
+ font-size: 0.8em;
+ // margin-left: 1em;
+
+ button {
+ padding: 0px 0.2em;
+ }
+ }
}
.readmeBox {
From 1a052c82861ea643a603605bbbbb17651ef64a18 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Fri, 22 Sep 2023 20:34:23 +0200
Subject: [PATCH 125/209] Minor PanelNode coverage fix
---
src/frontend/lib/PanelNode.ts | 33 ++++++++++++++-
tests/unit-tests/frontend/PanelNode.spec.ts | 47 +++++++++++++++++++++
2 files changed, 79 insertions(+), 1 deletion(-)
create mode 100644 tests/unit-tests/frontend/PanelNode.spec.ts
diff --git a/src/frontend/lib/PanelNode.ts b/src/frontend/lib/PanelNode.ts
index 16d93463..1aeb283b 100644
--- a/src/frontend/lib/PanelNode.ts
+++ b/src/frontend/lib/PanelNode.ts
@@ -2,7 +2,7 @@
import type { LayoutPanel, PanelType } from "../../shared/types/index";
import { writable } from "svelte/store";
-
+/* eslint-disable no-console */
export abstract class PanelNode {
static panelCounter = 0;
@@ -43,8 +43,15 @@ export class PanelGroup extends PanelNode {
name: string;
panels: PanelNode[] = [];
+ /**
+ * Removes a panel to from the panelgroup at index i. Existing panelGroup will be pruned if only one panel exists.
+ * @param i The index to remove the panel from
+ * @returns void
+ * */
+
// Nukes the current panelgroup and replaces it with its first child if possible
removePanel(i: number) {
+ if (i > this.panels.length) console.warn("PanelGroup.removePanel: Index out of bounds : ", i);
this.panels.splice(i, 1);
// If length 1, dissolve and replace with child
@@ -61,21 +68,43 @@ export class PanelGroup extends PanelNode {
}
this.updateParent(this);
}
+
+ /**
+ * Replaces a panel at the designated index in the panelgroup.
+ * @param panel The panel to be used to replace the existing panel
+ * @param i The index of the panel to be replaced
+ * @returns void
+ * */
+
setPanel(panel: PanelNode, i: number) {
+ if (i > this.panels.length) {
+ console.warn("PanelGroup.setPanel: Index out of bounds : ", i);
+ return;
+ }
const tempId = this.panels[i].id;
this.panels[i] = panel;
panel.parent = this;
panel.index = i;
panel.id = tempId;
}
+
+ /**
+ * Inserts a panel to the panelgroup at index i
+ * @param content The type of the panel to be added
+ * @param i The index to place the panel at
+ * @returns void
+ * */
+
addPanel(content: PanelType, i: number) {
const newLeaf = new PanelLeaf(content);
+ if (i > this.panels.length) console.warn("PanelGroup.addPanel: Index out of bounds : ", i);
this.panels.splice(i, 0, newLeaf);
newLeaf.parent = this;
newLeaf.index = i;
this.updateParent(this);
}
+
addPanelGroup(panelGroup: PanelGroup, i: number) {
this.panels.splice(i, 0, panelGroup);
panelGroup.parent = this;
@@ -165,3 +194,5 @@ class FocusedPanelStore {
}
export const focusedPanelStore = new FocusedPanelStore();
+
+/* eslint-enable no-console */
diff --git a/tests/unit-tests/frontend/PanelNode.spec.ts b/tests/unit-tests/frontend/PanelNode.spec.ts
new file mode 100644
index 00000000..b9eca015
--- /dev/null
+++ b/tests/unit-tests/frontend/PanelNode.spec.ts
@@ -0,0 +1,47 @@
+import exp from "constants";
+import { PanelGroup, PanelNode } from "../../../src/frontend/lib/PanelNode";
+import { Pane } from "svelte-splitpanes";
+import { exportMedia } from "../../../src/electron/lib/projects/ProjectCommands";
+
+
+
+
+ describe("Test PanelGroup", () => {
+ let panelNode : PanelGroup;
+
+ beforeEach(() => {
+ PanelGroup.groupCounter = 0;
+ PanelNode.panelCounter = 0;
+ panelNode = new PanelGroup("Unique id");
+
+ });
+
+ // This file only consists of constructors, so we only test those
+
+ test("Test constructor", () => {
+ expect(panelNode.id).toBe(0);
+ expect(panelNode.panels).toEqual([]);
+ expect(panelNode.parent).toBe(null);
+ expect(panelNode.index).toBe(-1);
+
+ panelNode = new PanelGroup("Hello",3);
+ expect(panelNode.id).toBe(3);
+
+
+ panelNode = new PanelGroup();
+ expect(panelNode.name).toBe("pg_2")
+ })
+
+ test("Adding panels", () => {
+ panelNode.addPanel("media",0);
+
+
+ expect(panelNode.panels.length).toBe(1);
+ expect(panelNode.panels[0].parent).toBe(panelNode);
+ expect(panelNode.panels[0].index).toBe(0);
+ expect(panelNode.panels[0].id).toBe(1);
+
+
+ })
+
+ });
\ No newline at end of file
From 1702b6567647d194dd8fb9226a9c418bca67b8a1 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Fri, 22 Sep 2023 22:17:27 +0200
Subject: [PATCH 126/209] Restyle 'no content' message in media tile
---
src/electron/lib/plugins/Plugin.ts | 2 +-
src/frontend/ui/tiles/Media.svelte | 99 +++++++++++--------
.../ui/utils/graph/NodeUIComponent.svelte | 26 ++---
.../Buffer.svelte | 0
.../Button.svelte | 0
.../CachePicker.svelte | 0
.../ColorPicker.svelte | 0
.../ColorPicker/ColorPickerTextInput.svelte | 0
.../ColorPicker/ColorPickerWrapper.svelte | 0
.../Dropdown.svelte | 0
.../FilePicker.svelte | 0
.../Knob.svelte | 0
.../NumberInput.svelte | 0
.../Radio.svelte | 0
.../Slider.svelte | 0
.../TextInput.svelte | 0
.../dials/Dial.svelte | 0
.../dials/DiffDial.svelte | 0
.../dials/TweakDial.svelte | 0
19 files changed, 74 insertions(+), 53 deletions(-)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/Buffer.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/Button.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/CachePicker.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/ColorPicker.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/ColorPicker/ColorPickerTextInput.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/ColorPicker/ColorPickerWrapper.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/Dropdown.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/FilePicker.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/Knob.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/NumberInput.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/Radio.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/Slider.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/TextInput.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/dials/Dial.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/dials/DiffDial.svelte (100%)
rename src/frontend/ui/utils/graph/{nodeUICcomponents => nodeUIComponents}/dials/TweakDial.svelte (100%)
diff --git a/src/electron/lib/plugins/Plugin.ts b/src/electron/lib/plugins/Plugin.ts
index 8eb22ad4..1fcc041a 100644
--- a/src/electron/lib/plugins/Plugin.ts
+++ b/src/electron/lib/plugins/Plugin.ts
@@ -51,7 +51,7 @@ export class Plugin {
requireSelf(blix: Blix, force = false): void {
try {
// This uses Node.js require() to load the plugin as a module
- // TODO: ISOLATION + LIMITED API
+ // TODO: ISOLATION
// @ts-ignore: no-var-requires
// We need to clear the local node cache so that the plugin can be reloaded
diff --git a/src/frontend/ui/tiles/Media.svelte b/src/frontend/ui/tiles/Media.svelte
index c3bf0b8f..8384ea76 100644
--- a/src/frontend/ui/tiles/Media.svelte
+++ b/src/frontend/ui/tiles/Media.svelte
@@ -12,6 +12,36 @@
import { type SelectionBoxItem } from "../../types/selection-box";
import WebView from "./WebView.svelte";
import { TweakApi } from "lib/webview/TweakApi";
+ import {
+ faBacon,
+ faBowlRice,
+ faBurger,
+ faCandyCane,
+ faCarrot,
+ faCoffee,
+ faCookieBite,
+ faFish,
+ faHotdog,
+ faIceCream,
+ faLemon,
+ faPizzaSlice,
+ } from "@fortawesome/free-solid-svg-icons";
+ import Fa from "svelte-fa";
+
+ const noContentIcons = [
+ faBacon,
+ faBowlRice,
+ faBurger,
+ faCandyCane,
+ faCarrot,
+ faCoffee,
+ faCookieBite,
+ faFish,
+ faHotdog,
+ faIceCream,
+ faLemon,
+ faPizzaSlice,
+ ];
const mediaOutputIds = mediaStore.getMediaOutputIdsReactive();
@@ -50,15 +80,6 @@
unsubMedia();
});
- // function handleSelect(e: Event) {
- // return;
- // const value = (e.target as HTMLSelectElement).value;
- // if (!value) return;
-
- // const [graphUUID, nodeUUID] = value.split("/");
- // selectedNode = { graphUUID, outNode: nodeUUID };
- // }
-
async function exportMedia(e: Event) {
if ($media?.dataType && $media?.content) {
await mediaStore.exportMedia($media);
@@ -75,26 +96,9 @@
return res;
}
- // async function getDisplay(id: TypeclassId) {
- // const value = null;
- // return await window.apis.typeclassApi.getMediaDisplay(id, value);
- // }
-
- // type MediaDisplay = {
- // component: any;
- // props: (data: any) => { [key: string]: any };
- // };
- // const dataTypeToMediaDisplay: { [key: string]: MediaDisplay } = {
- // [""]: { component: TextBox, props: (_data: any) => ({ content: "NO INPUT", status: "warning" }), },
- // Image: { component: Image, props: (data: string) => ({ src: data }), },
- // Number: { component: TextBox, props: (data: number) => ({ content: data?.toString() || "NULL", status: data == null ? "warning" : "normal", fontSize: "large", }),
- // }, boolean: { component: TextBox, props: (data: number) => ({ content: data?.toString() || "NULL", status: data == null ? "warning" : "normal", fontSize: "large", }), },
- // string: { component: TextBox, props: (data: string) => ({ content: data }), },
- // color: { component: ColorDisplay, props: (data: string) => ({ color: data }), },
- // Error: { component: TextBox, props: (data: string) => ({ content: data, status: "error" }), },
- // ["GLFX image"]: { component: WebView, props: (data: string) => ({ media: data }), },
- // ["Pixi image"]: { component: WebView, props: (data: string) => ({ media: data }), },
- // };
+ function getNoContentIcon() {
+ return noContentIcons[Math.floor(Math.random() * noContentIcons.length)];
+ }
const displayIdToSvelteConstructor: { [key in MediaDisplayType]: any } = {
image: Image,
@@ -151,13 +155,17 @@
/> -->
{:else}
- NO CONTENT
+
+
+
No content!
+
Add an Output node to the graph to create a media output
+
{/if}
-
diff --git a/src/frontend/ui/utils/graph/NodeUIComponent.svelte b/src/frontend/ui/utils/graph/NodeUIComponent.svelte
index 3007b245..5c2cec41 100644
--- a/src/frontend/ui/utils/graph/NodeUIComponent.svelte
+++ b/src/frontend/ui/utils/graph/NodeUIComponent.svelte
@@ -1,19 +1,19 @@
-
+ Export
diff --git a/blix-plugins/blink/webview/app.ts b/blix-plugins/blink/webview/app.ts
index 2d235077..b0ac35b1 100644
--- a/blix-plugins/blink/webview/app.ts
+++ b/blix-plugins/blink/webview/app.ts
@@ -1,6 +1,6 @@
import { writable } from 'svelte/store';
import App from './App.svelte';
-import { BlinkCanvas } from './types';
+import { type BlinkCanvas } from './types';
const media = writable({
assets: {},
diff --git a/blix-plugins/blink/webview/atom.ts b/blix-plugins/blink/webview/atom.ts
index f0d51cd2..b296f83d 100644
--- a/blix-plugins/blink/webview/atom.ts
+++ b/blix-plugins/blink/webview/atom.ts
@@ -1,7 +1,7 @@
import * as PIXI from 'pixi.js';
-import { Asset, Atom, ImageAtom, PaintAtom, ShapeAtom, TextAtom } from "./types";
+import type { Asset, Atom, ImageAtom, PaintAtom, ShapeAtom, TextAtom } from "./types";
import { diffAtom, diffImageAtom, diffPaintAtom, diffShapeAtom, diffTextAtom } from './diff';
-import { HierarchyAtom, randomId } from './render';
+import { type HierarchyAtom, randomId } from './render';
export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key: string]: Asset } | undefined, atom: Atom, prevAtom: HierarchyAtom | undefined): {
pixiAtom: PIXI.Container,
diff --git a/blix-plugins/blink/webview/diff.ts b/blix-plugins/blink/webview/diff.ts
index 49dd403d..5728d2f0 100644
--- a/blix-plugins/blink/webview/diff.ts
+++ b/blix-plugins/blink/webview/diff.ts
@@ -1,5 +1,5 @@
-import { HierarchyAtom, HierarchyClump } from "./render";
-import {
+import type { HierarchyAtom, HierarchyClump } from "./render";
+import type {
Asset,
Atom,
BlobAtom,
diff --git a/blix-plugins/blink/webview/filter.ts b/blix-plugins/blink/webview/filter.ts
index 5e7aa3fb..a98ba57f 100644
--- a/blix-plugins/blink/webview/filter.ts
+++ b/blix-plugins/blink/webview/filter.ts
@@ -1,5 +1,5 @@
import * as PIXI from 'pixi.js';
-import { Filter, getPixiFilter } from './types';
+import { type Filter, getPixiFilter } from './types';
import { randomId } from './render';
// Apply a series of filters and flatten the result to a sprite.
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index 0382213c..5eab534f 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -4,9 +4,6 @@ import {
type Atom,
type Clump,
type BlinkCanvas,
- type Asset,
- Filter,
- Transform,
} from "./types";
import { Viewport } from "pixi-viewport";
import { createBoundingBox } from "./select";
@@ -46,10 +43,10 @@ export function renderApp(
canvas: BlinkCanvas,
viewport: Viewport,
send: (message: string, data: any) => void
-): boolean {
+): { success: boolean; scene: PIXI.Container } {
console.log("====================================");
- if (!canvas || !canvas.content) return false;
+ if (!canvas || !canvas.content) return { success: false, scene: null };
if (!scene) {
scene = new PIXI.Container();
scene.name = "Blink Scene";
@@ -107,7 +104,7 @@ export function renderApp(
}
});
- return true;
+ return { success: true, scene };
}
export function renderCanvas(blink: PIXI.Application, root: Clump) {}
diff --git a/blix-plugins/blink/webview/select.ts b/blix-plugins/blink/webview/select.ts
index cf16b39a..858560c9 100644
--- a/blix-plugins/blink/webview/select.ts
+++ b/blix-plugins/blink/webview/select.ts
@@ -1,4 +1,4 @@
-import { Viewport } from "pixi-viewport";
+import { type Viewport } from "pixi-viewport";
import * as PIXI from "pixi.js";
const BOX_CORNER_DOT_SIZE = 5;
diff --git a/blix-plugins/blink/webview/types.ts b/blix-plugins/blink/webview/types.ts
index 581b123b..6570fd9d 100644
--- a/blix-plugins/blink/webview/types.ts
+++ b/blix-plugins/blink/webview/types.ts
@@ -1,5 +1,4 @@
import * as PIXI from "pixi.js";
-import { Matrix } from "pixi.js";
import {
KawaseBlurFilter,
BloomFilter,
diff --git a/src/electron/lib/api/apis/PluginApi.ts b/src/electron/lib/api/apis/PluginApi.ts
index 7ec10eac..986acd28 100644
--- a/src/electron/lib/api/apis/PluginApi.ts
+++ b/src/electron/lib/api/apis/PluginApi.ts
@@ -12,13 +12,6 @@ export class PluginApi implements ElectronMainApi {
this.blix = blix;
}
- async enablePlugin(plugin: PluginSignature) {
- // this.blix.commandRegistry.addInstance(instance);
- }
- async disablePlugin(plugin: PluginSignature) {
- // this.blix.commandRegistry.addInstance(instance);
- }
-
async installPlugin(url: URL) {
// return await this.blix.commandRegistry.runCommand(id, params);
}
@@ -27,6 +20,13 @@ export class PluginApi implements ElectronMainApi {
// return await this.blix.commandRegistry.runCommand(id, params);
}
+ async enablePlugin(plugin: PluginSignature) {
+ // this.blix.commandRegistry.addInstance(instance);
+ }
+ async disablePlugin(plugin: PluginSignature) {
+ // this.blix.commandRegistry.addInstance(instance);
+ }
+
async getInstalledPlugins() {
return this.blix.pluginManager.pluginPaths;
}
From 1cda9f7acede00d51fb0b0d3621b578e9f755841 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Sat, 23 Sep 2023 00:11:53 +0200
Subject: [PATCH 129/209] Fix Blink cache image export function typo
---
blix-plugins/blink/webview/App.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index 1dd90c5f..a4c24993 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -164,7 +164,7 @@
async function exportImage() {
if (!currScene || !canvasBlock) return;
- const exportCanvas = blink.renderer.extract.canvas(currScene, canvasBlock.getBounds());
+ const exportCanvas = blink.renderer.extract.canvas(currScene, canvasBlock.getLocalBounds());
exportCanvas.toBlob((blob) => {
const metadata = {
contentType: "image/png",
From e557a2bab82d1512607f3798f24882a0ea35a640 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Sat, 23 Sep 2023 02:04:19 +0200
Subject: [PATCH 130/209] Migrate Blink inputImage to cache system; Fix
incorrect bounding rectangle on Blink image export
---
blix-plugins/blink/src/main.cjs | 8 +++-----
blix-plugins/blink/webview/App.svelte | 23 ++++++++++++++++-------
blix-plugins/blink/webview/atom.ts | 4 ++--
blix-plugins/blink/webview/render.ts | 23 ++++++++++++++++++++---
src/frontend/lib/Project.ts | 9 ++++++++-
5 files changed, 49 insertions(+), 18 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 87574b1a..03e02fb0 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -195,7 +195,7 @@ const nodes = {
nodeBuilder.setDescription("Input a Blink Sprite Image");
const ui = nodeBuilder.createUIBuilder();
- ui.addFilePicker({
+ ui.addCachePicker({
componentId: "imagePicker",
label: "Pick an image",
defaultValue: "",
@@ -220,16 +220,14 @@ const nodes = {
});
nodeBuilder.define(async (input, uiInput, from) => {
- let src = uiInput["imagePicker"].split("/");
- src = src.splice(-2);
- src = src.join("/");
+ let src = uiInput["imagePicker"];
const canvas = {
assets: {
[uiInput["state"]["id"]]: {
class: "asset",
type: "image",
- data: uiInput["imagePicker"].split("/").splice(-2).join("/"),
+ data: uiInput["imagePicker"]
// data: uiInput["cachePicker"],
}
},
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index a4c24993..d0fb5007 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -3,19 +3,24 @@
import { Viewport } from "pixi-viewport";
import { onDestroy, onMount, tick } from "svelte";
import { type Writable } from "svelte/store";
- import { renderApp } from "./render";
+ import { renderScene } from "./render";
import { type BlinkCanvas } from "./types";
import Debug from "./Debug.svelte";
export let media: Writable;
export let send: (msg: string, data: any) => void;
+ let imgCanvasInitialPadding = 100;
+ let imgCanvasBlockW = 1920;
+ let imgCanvasBlockH = 1080;
+
let blink: PIXI.Application;
let pixiCanvas: HTMLCanvasElement;
let mouseCursor = "cursorDefault";
let canvasBlock: PIXI.Graphics;
let currScene: PIXI.Container;
+ let viewport: Viewport;
onMount(async () => {
// window.addEventListener("DOMContentLoaded", async () => {
@@ -46,7 +51,7 @@
}
//====== CREATE VIEWPORT ======//
- const viewport = new Viewport({
+ viewport = new Viewport({
screenWidth: pixiCanvas.width,
screenHeight: pixiCanvas.height,
worldWidth: 100,
@@ -81,9 +86,9 @@
});
//===== CREATE BASE LAYOUT =====//
- const imgCanvasInitialPadding = 100;
- const imgCanvasBlockW = 1920;
- const imgCanvasBlockH = 1080;
+ imgCanvasInitialPadding = 100;
+ imgCanvasBlockW = 1920;
+ imgCanvasBlockH = 1080;
let imgCanvas = new PIXI.Container();
imgCanvas.name = "imgCanvas";
@@ -120,7 +125,7 @@
//===== RENDER Blink =====//
let hasCentered = false;
media.subscribe(async (media) => {
- const { success, scene } = renderApp(blink, media, viewport, send);
+ const { success, scene } = renderScene(blink, media, viewport, send);
currScene = scene;
// Necessary to fix an occasional race condition with PIXI failing to load
@@ -164,7 +169,10 @@
async function exportImage() {
if (!currScene || !canvasBlock) return;
- const exportCanvas = blink.renderer.extract.canvas(currScene, canvasBlock.getLocalBounds());
+ const bounds = currScene.getLocalBounds();
+ const frame = new PIXI.Rectangle(-bounds.x-imgCanvasBlockW/2, -bounds.y-imgCanvasBlockH/2, imgCanvasBlockW, imgCanvasBlockH);
+
+ const exportCanvas = blink.renderer.extract.canvas(currScene, frame);
exportCanvas.toBlob((blob) => {
const metadata = {
contentType: "image/png",
@@ -172,6 +180,7 @@
};
window.cache.write(blob, metadata);
}, "image/png");
+ // REMOVED: Exporting straight to local file
// const link = document.createElement("a");
// link.download = "export.png";
// link.href = exportCanvas.toDataURL("image/png", 1.0);
diff --git a/blix-plugins/blink/webview/atom.ts b/blix-plugins/blink/webview/atom.ts
index b296f83d..4ab29ab8 100644
--- a/blix-plugins/blink/webview/atom.ts
+++ b/blix-plugins/blink/webview/atom.ts
@@ -3,7 +3,7 @@ import type { Asset, Atom, ImageAtom, PaintAtom, ShapeAtom, TextAtom } from "./t
import { diffAtom, diffImageAtom, diffPaintAtom, diffShapeAtom, diffTextAtom } from './diff';
import { type HierarchyAtom, randomId } from './render';
-export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key: string]: Asset } | undefined, atom: Atom, prevAtom: HierarchyAtom | undefined): {
+export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key: string]: Asset } | undefined, atom: Atom, prevAtom: HierarchyAtom | undefined, textures: { [key: string]: PIXI.Texture }): {
pixiAtom: PIXI.Container,
changed: boolean // Whether the pixiClump is a different PIXI object than before
} {
@@ -20,7 +20,7 @@ export function renderAtom(assets: { [key: string]: Asset }, prevAssets: { [key:
}
if (assets[atom.assetId] && assets[atom.assetId].type === "image") {
- const sprite = PIXI.Sprite.from(assets[atom.assetId].data);
+ const sprite = PIXI.Sprite.from(textures[assets[atom.assetId].data]);
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index 5eab534f..d4859ff8 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -38,7 +38,9 @@ let scene: PIXI.Container;
let hierarchy: HierarchyCanvas | undefined = undefined;
-export function renderApp(
+const textures: { [key: string]: PIXI.Texture } = {}
+
+export function renderScene(
blink: PIXI.Application,
canvas: BlinkCanvas,
viewport: Viewport,
@@ -71,7 +73,21 @@ export function renderApp(
const imgPromises = [];
for (let assetId in canvas.assets) {
if (canvas.assets[assetId].type === "image") {
- imgPromises.push(PIXI.Assets.load(canvas.assets[assetId].data));
+ const cacheId = canvas.assets[assetId].data;
+ if (textures[cacheId] != null) continue;
+
+ // Get cache object
+ imgPromises.push(new Promise(async (resolve, reject) => {
+ const blob: Blob = await window.cache.get(cacheId);
+ console.log("BLOB", blob);
+ const url = window.URL.createObjectURL(blob);
+ console.log("URL", url);
+ textures[cacheId] = PIXI.Texture.from(url);
+ // const asset = await PIXI.Assets.load(url);
+ // console.log("ASSET", asset);
+ resolve(null);
+ // resolve(asset);
+ }))
// TODO: Use bundles instead (e.g. PIXI.Assets.addBundle())
// See: [https://pixijs.io/guides/basics/assets.html]
}
@@ -170,7 +186,8 @@ function renderClump(
canvas.assets,
prevCanvas?.assets,
child,
- (prevChild?.class === "atom" ? prevChild : undefined) as HierarchyAtom
+ (prevChild?.class === "atom" ? prevChild : undefined) as HierarchyAtom,
+ textures
);
// Construct hierarchy atom
diff --git a/src/frontend/lib/Project.ts b/src/frontend/lib/Project.ts
index 3c9cfcf3..6a3cd05c 100644
--- a/src/frontend/lib/Project.ts
+++ b/src/frontend/lib/Project.ts
@@ -32,7 +32,14 @@ export function constructLayout(layout: LayoutPanel): PanelGroup {
export const layoutTemplate: LayoutPanel = {
panels: [
{
- content: "assets",
+ panels: [
+ {
+ content: "media",
+ },
+ {
+ content: "assets",
+ },
+ ],
},
{
content: "graph",
From 709fa32850b5b2dd13dfd04b5b35aeecd4756fa7 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Sat, 23 Sep 2023 09:46:07 +0200
Subject: [PATCH 131/209] Fix window control buttons on Windows
---
src/index.ts | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/index.ts b/src/index.ts
index 405c6ef2..96ab2d69 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -19,7 +19,7 @@ import { Blix } from "./electron/lib/Blix";
import { CoreGraphInterpreter } from "./electron/lib/core-graph/CoreGraphInterpreter";
import { exposeMainApis } from "./electron/lib/api/MainApi";
import { MainWindow, bindMainWindowApis } from "./electron/lib/api/apis/WindowApi";
-import { platform } from "os";
+import { type } from "os";
const isProd = process.env.NODE_ENV === "production" || app.isPackaged;
// const isProd = true;
@@ -105,8 +105,9 @@ async function createMainWindow() {
},
// Set icon for Windows and Linux
icon: isProd ? join(__dirname, "icon.png") : "public/images/icon.png",
- titleBarStyle: "hidden",
+ titleBarStyle: type() === "Windows_NT" ? "default" : "hidden",
trafficLightPosition: { x: 10, y: 10 },
+ title: "Blix",
// show: false,
}) as MainWindow;
From 5f92651c65a73d957b33cf23e1afba738383b80c Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Sat, 23 Sep 2023 12:34:55 +0200
Subject: [PATCH 132/209] PanelNode tests finished
---
src/frontend/lib/PanelNode.ts | 20 +++++
tests/unit-tests/frontend/PanelNode.spec.ts | 92 ++++++++++++++++++++-
2 files changed, 110 insertions(+), 2 deletions(-)
diff --git a/src/frontend/lib/PanelNode.ts b/src/frontend/lib/PanelNode.ts
index 4802e853..d0798c87 100644
--- a/src/frontend/lib/PanelNode.ts
+++ b/src/frontend/lib/PanelNode.ts
@@ -141,6 +141,12 @@ export class PanelGroup extends PanelNode {
current = current;
}
+ /**
+ * Prints the panelgroup and its children
+ * @param indent The indentation level to be used
+ * @returns string
+ */
+
// Print tree for debug
public print(indent = 0): string {
let res;
@@ -164,6 +170,11 @@ export class PanelGroup extends PanelNode {
return res;
}
+ /**
+ * Recursively sets the parent of the panelgroup and its children
+ * @returns void
+ * */
+
recurseParent() {
for (let p = 0; p < this.panels.length; p++) {
const panel = this.panels[p];
@@ -176,6 +187,11 @@ export class PanelGroup extends PanelNode {
}
}
+ /**
+ * Saves the layout of the panelgroup and its children
+ * @returns LayoutPanel
+ * */
+
public saveLayout(): LayoutPanel {
const p: LayoutPanel = {
panels: [],
@@ -191,6 +207,10 @@ export class PanelGroup extends PanelNode {
}
}
+/**
+ * A leaf node that contains the type of the panel
+ * @extends PanelNode
+ */
export class PanelLeaf extends PanelNode {
constructor(public content: PanelType) {
super();
diff --git a/tests/unit-tests/frontend/PanelNode.spec.ts b/tests/unit-tests/frontend/PanelNode.spec.ts
index fc5490ed..bf95764c 100644
--- a/tests/unit-tests/frontend/PanelNode.spec.ts
+++ b/tests/unit-tests/frontend/PanelNode.spec.ts
@@ -1,7 +1,8 @@
import exp from "constants";
-import { PanelGroup, PanelLeaf, PanelNode } from "../../../src/frontend/lib/PanelNode";
+import { PanelGroup, PanelLeaf, PanelNode, focusedPanelStore } from "../../../src/frontend/lib/PanelNode";
import { Pane } from "svelte-splitpanes";
import { exportMedia } from "../../../src/electron/lib/projects/ProjectCommands";
+import { LayoutPanel } from "../../../src/shared/types";
@@ -40,6 +41,9 @@ import { exportMedia } from "../../../src/electron/lib/projects/ProjectCommands"
expect(panelNode.panels[0].parent).toBe(panelNode);
expect(panelNode.panels[0].index).toBe(0);
expect(panelNode.panels[0].id).toBe(1);
+
+ panelNode.addPanel("media",69);
+ expect(panelNode.panels.length).toBe(2);
})
test("Removing panels", () => {
@@ -66,6 +70,11 @@ import { exportMedia } from "../../../src/electron/lib/projects/ProjectCommands"
panelNode2.addPanelGroup(panelNode,0);
panelNode.removePanel(0);
expect(panelNode2.panels.length).toBe(1);
+
+ const len = panelNode.panels.length;
+ panelNode.removePanel(69);
+ expect(panelNode.panels.length).toBe(len);
+
}
)
@@ -77,12 +86,91 @@ import { exportMedia } from "../../../src/electron/lib/projects/ProjectCommands"
const node = new PanelLeaf("graph");
+
+ panelNode.setPanel(node,69);
+
+ expect(panelNode.panels.length).toBe(1);
+ expect(panelNode.panels[0]).toBe(curr);
panelNode.setPanel(node,0);
expect(panelNode.panels.length).toBe(1);
expect(panelNode.panels[0]).toBe(node);
expect(panelNode.panels[0].id).toBe(curr.id);
+ })
+ test("Printing a panel group", () => {
+ panelNode.addPanel("media",0);
+ panelNode.addPanel("media",1);
+ panelNode.addPanel("media",2);
+
+ expect(panelNode.print()).toBe("Unique id[NULL](-1)\n" + "+ media[Unique id](0)\n" + "+ media[Unique id](1)\n" + "+ media[Unique id](2)\n")
+ const panelGroup1 = new PanelGroup("Unique id");
+ panelGroup1.addPanelGroup(panelNode,0);
+
+
+ expect(panelGroup1.print()).toBe("Unique id[NULL](-1)\n" + "- Unique id[Unique id](0)\n" + " + media[Unique id](0)\n" + " + media[Unique id](1)\n" + " + media[Unique id](2)\n")
+ panelNode.panels[1].parent = null;
+ expect(panelNode.print()).toBe("Unique id[Unique id](0)\n" + "+ media[Unique id](0)\n" + "+ media[NULL](1)\n" + "+ media[Unique id](2)\n")
})
- });
\ No newline at end of file
+ test("Testing Recurse Parent", () => {
+ panelNode.addPanel("media",0);
+ panelNode.addPanel("media",1);
+ panelNode.addPanel("media",2);
+
+ const panelGroup = new PanelGroup("Unique id");
+ panelGroup.addPanel("media",0);
+
+ panelNode.addPanelGroup(panelGroup,3);
+
+ panelNode.panels[1].parent=null;
+ panelNode.recurseParent();
+
+ expect(panelNode.panels[1].parent).toBe(panelNode);
+
+ })
+
+
+ test("Testing save layout", () => {
+ panelNode.addPanel("media",0);
+ panelNode.addPanel("media",1);
+ panelNode.addPanel("media",2);
+
+ const panelGroup = new PanelGroup("Unique id");
+ panelGroup.addPanel("media",0);
+
+ panelNode.addPanelGroup(panelGroup,3);
+
+ const layout : LayoutPanel = {
+ panels: [
+ {
+ content: "media",
+ },
+ {
+ content: "media",
+ },
+ {
+ content: "media",
+ },
+ {
+ panels: [
+ {
+ content: "media",
+ },
+ ]
+ }
+ ]
+ }
+ expect(panelNode.saveLayout()).toEqual(layout);
+ })
+
+ test("Testing focusedPanelStore", () => {
+ focusedPanelStore.focusOnPanel(1);
+ focusedPanelStore.focusOnPanel(2);
+ focusedPanelStore.focusOnPanel(3);
+
+ expect(focusedPanelStore.subscribe).toReturn;
+
+ })
+
+ });
\ No newline at end of file
From 80e587550f5a6b1d46a412cc9631f5349dd0be95 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Sat, 23 Sep 2023 12:50:50 +0200
Subject: [PATCH 133/209] Add project.ts tests and docs
---
src/frontend/lib/Project.ts | 9 ++++
tests/unit-tests/frontend/PanelNode.spec.ts | 47 ++++++++++++++++++++-
2 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/src/frontend/lib/Project.ts b/src/frontend/lib/Project.ts
index 57085cb6..4ea42926 100644
--- a/src/frontend/lib/Project.ts
+++ b/src/frontend/lib/Project.ts
@@ -12,6 +12,12 @@ export interface UIProject {
let groupTest = 0;
+/**
+ * Constructs a PanelGroup from a LayoutPanel
+ * @param layout The layout to construct from
+ * @returns The constructed PanelGroup
+ */
+
export function constructLayout(layout: LayoutPanel): PanelGroup {
const group = new PanelGroup((groupTest++).toString());
if (layout.panels) {
@@ -28,6 +34,9 @@ export function constructLayout(layout: LayoutPanel): PanelGroup {
}
return group;
}
+/**
+ * A layout template for the UI
+ */
export const layoutTemplate: LayoutPanel = {
panels: [
diff --git a/tests/unit-tests/frontend/PanelNode.spec.ts b/tests/unit-tests/frontend/PanelNode.spec.ts
index bf95764c..8aa1c5fe 100644
--- a/tests/unit-tests/frontend/PanelNode.spec.ts
+++ b/tests/unit-tests/frontend/PanelNode.spec.ts
@@ -3,6 +3,7 @@ import { PanelGroup, PanelLeaf, PanelNode, focusedPanelStore } from "../../../sr
import { Pane } from "svelte-splitpanes";
import { exportMedia } from "../../../src/electron/lib/projects/ProjectCommands";
import { LayoutPanel } from "../../../src/shared/types";
+import { constructLayout } from "../../../src/frontend/lib/Project";
@@ -173,4 +174,48 @@ import { LayoutPanel } from "../../../src/shared/types";
})
- });
\ No newline at end of file
+});
+
+describe("Test project.ts", () => {
+
+ let panelNode : PanelGroup;
+ beforeEach(() => {
+ PanelGroup.groupCounter = 0;
+ PanelNode.panelCounter = 0;
+ panelNode = new PanelGroup("Unique id");
+ });
+ test ("Testing constructLayout", () => {
+ panelNode.addPanel("media",0);
+ panelNode.addPanel("media",1);
+ panelNode.addPanel("media",2);
+
+ const panelGroup = new PanelGroup("Unique id");
+ panelGroup.addPanel("media",0);
+
+ panelNode.addPanelGroup(panelGroup,3);
+
+ const layout : LayoutPanel = {
+ panels: [
+ {
+ content: "media",
+ },
+ {
+ content: "media",
+ },
+ {
+ content: "media",
+ },
+ {
+ panels: [
+ {
+ content: "media",
+ },
+ ]
+ }
+ ]
+ }
+
+ const group = constructLayout(layout);
+ expect(group.panels.length).toEqual(panelNode.panels.length);
+ })
+})
\ No newline at end of file
From 64a94552f88310757e9e37c064c225245fdfff08 Mon Sep 17 00:00:00 2001
From: Jake Mileham
Date: Sat, 23 Sep 2023 16:22:07 +0200
Subject: [PATCH 134/209] Refactor CoreGraph node adding
---
src/electron/lib/core-graph/CoreGraph.ts | 19 +++++--------------
1 file changed, 5 insertions(+), 14 deletions(-)
diff --git a/src/electron/lib/core-graph/CoreGraph.ts b/src/electron/lib/core-graph/CoreGraph.ts
index 60ab16f3..81ebda9a 100644
--- a/src/electron/lib/core-graph/CoreGraph.ts
+++ b/src/electron/lib/core-graph/CoreGraph.ts
@@ -199,37 +199,30 @@ export class CoreGraph extends UniqueEntity {
let inputValues: Record = {};
// Create or restore UI inputs depending on brand new node or restored node
-
Object.values(nodeInstance.uiConfigs).forEach((config) => {
inputValues[config.componentId] = uiValues
? uiValues[config.componentId]
: config.defaultValue;
});
- this.uiInputs[node.uuid] = new CoreNodeUIInputs(
- { inputs: inputValues, changes: uiValues ? Object.keys(uiValues) : [] },
- {}
- );
+ const changes = Object.keys(inputValues);
// Handle special readonly UI component types (e.g. TweakDial)
const { dials: dialInputs, filledInputs: filledDialInputs } = populateDials(nodeInstance.ui, {
nodeUUID: node.uuid,
- uiInputs: Object.keys(inputValues),
- uiInputChanges: Object.keys(inputValues), // When node is created, all inputs have 'changed'
+ uiInputs: changes,
+ uiInputChanges: changes, // When node is created, all inputs have 'changed'
});
inputValues = { ...inputValues, ...filledDialInputs };
// Handle the UI input initializer
const initializedInputs = nodeInstance.uiInitializer(inputValues);
- let uiInputsInitialized = false;
const uiChanges = Object.keys(initializedInputs);
- // console.log("Initialized Inputs", initializedInputs);
-
+ let uiInputsInitialized = false;
if (typeof initializedInputs === "object" && uiChanges.length > 0) {
inputValues = { ...inputValues, ...initializedInputs };
-
// Update the graph's UI inputs
const uiInputsPayload: INodeUIInputs = {
inputs: inputValues,
@@ -237,11 +230,9 @@ export class CoreGraph extends UniqueEntity {
};
this.uiInputs[node.uuid] = new CoreNodeUIInputs(uiInputsPayload, dialInputs);
-
uiInputsInitialized = true;
}
-
- // console.log(QueryResponseStatus.success)
+ // console.log(uiInputsInitialized)
const anchors: AiAnchors = node.returnAnchors();
// Add position of node to graph
this.uiPositions[node.uuid] = pos;
From ded43477d6efbf33f98a6228c8256e7c2cfd76da Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Sat, 23 Sep 2023 17:41:18 +0200
Subject: [PATCH 135/209] Fix tests
---
__mocks__/hello-plugin/src/main.js | 2 +-
src/electron/lib/core-graph/CoreGraph.ts | 4 ++
.../lib/core-graph/CoreGraphExporter.ts | 2 +-
.../core-graph/graphImporter.spec.ts | 2 +-
.../core-graph/graphInterpreter.spec.ts | 6 +++
.../media/mediaManager.spec.ts | 5 ++-
.../integration-tests/plugins/plugin.spec.ts | 4 +-
.../projects/projectCommands.spec.ts | 2 +-
.../electron/lib/core-graph/CoreGraph.spec.ts | 42 +++++++++----------
.../lib/core-graph/CoreGraphExporter.spec.ts | 4 +-
.../lib/core-graph/CoreGraphManager.spec.ts | 16 +++----
.../electron/lib/core-graph/Toolbox.spec.ts | 6 +--
.../lib/plugins/builder/NodeBuilder.spec.ts | 22 +++++-----
13 files changed, 64 insertions(+), 53 deletions(-)
diff --git a/__mocks__/hello-plugin/src/main.js b/__mocks__/hello-plugin/src/main.js
index 4887262b..19b805e9 100644
--- a/__mocks__/hello-plugin/src/main.js
+++ b/__mocks__/hello-plugin/src/main.js
@@ -3,7 +3,7 @@ const nodes = {
"hello": (context) => {
// Use context.nodeBuilder to construct the node UI
- console.log("Bogus nodes");
+ // console.log("Bogus nodes");
}
}
diff --git a/src/electron/lib/core-graph/CoreGraph.ts b/src/electron/lib/core-graph/CoreGraph.ts
index 81ebda9a..60733147 100644
--- a/src/electron/lib/core-graph/CoreGraph.ts
+++ b/src/electron/lib/core-graph/CoreGraph.ts
@@ -221,6 +221,9 @@ export class CoreGraph extends UniqueEntity {
const uiChanges = Object.keys(initializedInputs);
let uiInputsInitialized = false;
+
+ this.uiInputs[node.uuid] = new CoreNodeUIInputs({ inputs: inputValues, changes }, dialInputs);
+
if (typeof initializedInputs === "object" && uiChanges.length > 0) {
inputValues = { ...inputValues, ...initializedInputs };
// Update the graph's UI inputs
@@ -232,6 +235,7 @@ export class CoreGraph extends UniqueEntity {
this.uiInputs[node.uuid] = new CoreNodeUIInputs(uiInputsPayload, dialInputs);
uiInputsInitialized = true;
}
+
// console.log(uiInputsInitialized)
const anchors: AiAnchors = node.returnAnchors();
// Add position of node to graph
diff --git a/src/electron/lib/core-graph/CoreGraphExporter.ts b/src/electron/lib/core-graph/CoreGraphExporter.ts
index 1c20e8d5..05f794e4 100644
--- a/src/electron/lib/core-graph/CoreGraphExporter.ts
+++ b/src/electron/lib/core-graph/CoreGraphExporter.ts
@@ -43,7 +43,7 @@ export class GraphFileExportStrategy implements ExportStrategy {
json.push({
signature: `${graph.getNodes[node].getPlugin}.${graph.getNodes[node].getName}`,
position: graph.getUIPositions()[node],
- inputs: inputs[node].getInputs,
+ inputs: inputs[node] ? inputs[node].getInputs : {},
});
}
diff --git a/tests/integration-tests/core-graph/graphImporter.spec.ts b/tests/integration-tests/core-graph/graphImporter.spec.ts
index 2b6f7fc5..3000645b 100644
--- a/tests/integration-tests/core-graph/graphImporter.spec.ts
+++ b/tests/integration-tests/core-graph/graphImporter.spec.ts
@@ -93,7 +93,7 @@ describe("Test graph importer", () => {
blix = new Blix();
blix.init(mainWindow);
importer = new CoreGraphImporter(blix.toolbox);
- blix.toolbox.addInstance(new NodeInstance("testNode", "blix", "testNode", "This is a test node", "", [{ type: "Number", "displayName": "blix.testNode.0", "identifier": "In0" }], [{ type: "Number", "displayName": "blix.testNode.1", "identifier": "Out0" }]))
+ blix.toolbox.addInstance(new NodeInstance("testNode", "blix", "folder", "testNode", "This is a test node", "", [{ type: "Number", "displayName": "blix.testNode.0", "identifier": "In0" }], [{ type: "Number", "displayName": "blix.testNode.1", "identifier": "Out0" }]))
});
test("Test import of valid json graph", () => {
diff --git a/tests/integration-tests/core-graph/graphInterpreter.spec.ts b/tests/integration-tests/core-graph/graphInterpreter.spec.ts
index 20a27394..86a9ff3a 100644
--- a/tests/integration-tests/core-graph/graphInterpreter.spec.ts
+++ b/tests/integration-tests/core-graph/graphInterpreter.spec.ts
@@ -98,6 +98,7 @@ describe("Test graph interpreter", () => {
new NodeInstance(
`hello-plugin.hello`,
`input`,
+ `folder`,
`hello-plugin`,
`title`,
`description`,
@@ -119,6 +120,7 @@ describe("Test graph interpreter", () => {
new NodeInstance(
`hello-plugin.hello`,
`flip`,
+ `folder`,
`hello-plugin`,
`title`,
`description`,
@@ -146,6 +148,7 @@ describe("Test graph interpreter", () => {
new NodeInstance(
`output`,
`blix`,
+ `folder`,
`hello-plugin`,
`title`,
`description`,
@@ -207,6 +210,7 @@ describe("Test graph interpreter", () => {
new NodeInstance(
`hello-plugin.hello`,
`input`,
+ `folder`,
`hello-plugin`,
`title`,
`description`,
@@ -228,6 +232,7 @@ describe("Test graph interpreter", () => {
new NodeInstance(
`hello-plugin.hello`,
`flip`,
+ `folder`,
`hello-plugin`,
`title`,
`description`,
@@ -255,6 +260,7 @@ describe("Test graph interpreter", () => {
new NodeInstance(
`output`,
`blix`,
+ `folder`,
`hello-plugin`,
`title`,
`description`,
diff --git a/tests/integration-tests/media/mediaManager.spec.ts b/tests/integration-tests/media/mediaManager.spec.ts
index 3238f774..84cd141d 100644
--- a/tests/integration-tests/media/mediaManager.spec.ts
+++ b/tests/integration-tests/media/mediaManager.spec.ts
@@ -35,7 +35,8 @@ const mainWindow: MainWindow = {
},
graphClientApi: {
graphChanged: jest.fn(),
- graphRemoved: jest.fn()
+ graphRemoved: jest.fn(),
+ uiInputsChanged: jest.fn(),
},
utilClientApi: {
showToast: jest.fn()
@@ -158,7 +159,7 @@ describe("Test graph importer", () => {
//mediaManager.onGraphUpdated(graph.uuid);
- expect(blix.graphInterpreter.run).toBeCalledTimes(1);
+ expect(blix.graphInterpreter.run).toBeCalledTimes(2);
});
diff --git a/tests/integration-tests/plugins/plugin.spec.ts b/tests/integration-tests/plugins/plugin.spec.ts
index 12fab996..9fc67510 100644
--- a/tests/integration-tests/plugins/plugin.spec.ts
+++ b/tests/integration-tests/plugins/plugin.spec.ts
@@ -101,10 +101,10 @@ describe("Test builder propagations", () => {
beforeEach(() => {
jest.clearAllMocks();
- const node = new NodeInstance("Jake.Shark", "Shark", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
+ const node = new NodeInstance("Jake.Shark", "Shark", "folder", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
const nodeUI = new NodeUIParent("Jake.Shark", null);
- nodeBuilder = new NodeBuilder("testing-plugin", "My cool node");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "My cool node");
nodeUIBuilder = new NodeUIBuilder();
});
diff --git a/tests/integration-tests/projects/projectCommands.spec.ts b/tests/integration-tests/projects/projectCommands.spec.ts
index 94624ef1..ab7e02e6 100644
--- a/tests/integration-tests/projects/projectCommands.spec.ts
+++ b/tests/integration-tests/projects/projectCommands.spec.ts
@@ -31,7 +31,7 @@ const mainWindow: MainWindow = {
},
utilClientApi: {
showToast: jest.fn((message) => {
- console.log(message);
+ // console.log(message);
}),
}
diff --git a/tests/unit-tests/electron/lib/core-graph/CoreGraph.spec.ts b/tests/unit-tests/electron/lib/core-graph/CoreGraph.spec.ts
index f62eee3d..714293ab 100644
--- a/tests/unit-tests/electron/lib/core-graph/CoreGraph.spec.ts
+++ b/tests/unit-tests/electron/lib/core-graph/CoreGraph.spec.ts
@@ -19,7 +19,7 @@ describe("Test backend graph", () => {
graph = new CoreGraph();
for(let i = 0; i < 10; i++){
- const node = new NodeInstance(`Node-${i}`, "Test-plugin", `Node-${i}`, `This is node ${i}`, `fa-duotone fa-bell`, inputs, outputs);
+ const node = new NodeInstance(`Node-${i}`, "Test-plugin", "folder", `Node-${i}`, `This is node ${i}`, `fa-duotone fa-bell`, inputs, outputs);
nodes.push(node);
graph.addNode(node, { x: 0, y: 0 });
}
@@ -39,7 +39,7 @@ describe("Test backend graph", () => {
for(let i = 0; i < 10; i++){
const input: MinAnchor = { type: "string", displayName: `Test-plugin.Node-${i}.0`, identifier: `in${i}` };
const output: MinAnchor = { type: "string", displayName: `Test-plugin.Node-${i}.1`, identifier: `out${i}` };
- const node = new NodeInstance(`Node-${i}`, `Test-plugin`, `Node-${i}`, `This is node ${i}`, `fa-duotone fa-bell`, [input], [output]);
+ const node = new NodeInstance(`Node-${i}`, `Test-plugin`, "folder", `Node-${i}`, `This is node ${i}`, `fa-duotone fa-bell`, [input], [output]);
graph.addNode(node, { x: 0, y: 0 });
names.push(input.displayName, output.displayName);
}
@@ -64,7 +64,7 @@ describe("Test backend graph", () => {
})
test("Setting a node's position", () => {
- const node = new NodeInstance("Test-plugin",`Node-1`, `Node-1`, `This is node 1`, `fa-duotone fa-bell`, inputs, outputs);
+ const node = new NodeInstance("Test-plugin",`Node-1`, "folder", `Node-1`, `This is node 1`, `fa-duotone fa-bell`, inputs, outputs);
const response: QueryResponse<{ nodeId: UUID }> = graph.addNode(node, { x: 0, y: 0});
const uuid = (response.data! as { nodeId: UUID }).nodeId;
const response2: QueryResponse = graph.setNodePos(uuid, { x: 6, y: 9});
@@ -114,7 +114,7 @@ describe("Test CoreGraph Class", () => {
});
test("Adding a node to a graph", () => {
- const node = new NodeInstance("Node-1",`Test-Plugin`, `Node-1`, `This is node 1`, `fa-duotone fa-bell`, inputs, outputs);
+ const node = new NodeInstance("Node-1",`Test-Plugin`, "folder", `Node-1`, `This is node 1`, `fa-duotone fa-bell`, inputs, outputs);
const response: QueryResponse<{ nodeId: UUID }> = graph.addNode(node, { x: 0, y: 0 });
const uuid = (response.data! as { nodeId: UUID }).nodeId;
expect(uuid).toBe(graph.getNodes[uuid].uuid);
@@ -126,7 +126,7 @@ test("Adding a node to a graph", () => {
test("Removing a node from a graph", () => {
- const node = new NodeInstance("Test-plugin",`Node-1`, `Node-1`, `This is node 1`, `fa-duotone fa-bell`, inputs, outputs);
+ const node = new NodeInstance("Test-plugin",`Node-1`, "folder", `Node-1`, `This is node 1`, `fa-duotone fa-bell`, inputs, outputs);
const response: QueryResponse<{ nodeId: UUID }> = graph.addNode(node, { x: 0, y: 0 });
const uuid = (response.data! as { nodeId: UUID }).nodeId;
graph.removeNode(uuid);
@@ -162,8 +162,8 @@ test("Adding a node to a graph", () => {
{ type: "string", displayName: `Test-plugin.Node-2.3`, identifier: `out1` },
{ type: "number", displayName: `Test-plugin.Node-2.4`, identifier: `out2` });
- const node1Instance = new NodeInstance(`${bestNode}-${1}`, `${plugin}`, `${bestNode}-1`, description, icon, inputs1, outputs1);
- const node2Instance = new NodeInstance(`${bestNode}-${2}`, `${plugin}`, `${bestNode}-2`, description, icon, inputs2, outputs2);
+ const node1Instance = new NodeInstance(`${bestNode}-${1}`, `${plugin}`, "folder", `${bestNode}-1`, description, icon, inputs1, outputs1);
+ const node2Instance = new NodeInstance(`${bestNode}-${2}`, `${plugin}`, "folder", `${bestNode}-2`, description, icon, inputs2, outputs2);
const pos = { x: 0, y: 0 };
graph.addNode(node1Instance, pos);
@@ -260,8 +260,8 @@ test("Adding a node to a graph", () => {
{ type: "string", displayName: `Test-plugin.Node-2.3`, identifier: `out1` },
{ type: "number", displayName: `Test-plugin.Node-2.4`, identifier: `out2` });
- const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, `${node}-1`, description, icon, inputs1, outputs1);
- const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, `${node}-2`, description, icon, inputs2, outputs2);
+ const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, "folder", `${node}-1`, description, icon, inputs1, outputs1);
+ const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, "folder", `${node}-2`, description, icon, inputs2, outputs2);
const pos = { x: 0, y: 0 };
graph.addNode(node1Instance, pos);
@@ -331,10 +331,10 @@ test("Adding a node to a graph", () => {
{ type: "string", displayName: `Test-plugin.Node-4.3`, identifier: `out1` },
{ type: "number", displayName: `Test-plugin.Node-4.4`, identifier: `out2` });
- const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, `${node}-1`, description, icon, inputs1, outputs1);
- const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, `${node}-2`, description, icon, inputs2, outputs2);
- const node3Instance = new NodeInstance(`${node}-${3}`, `${plugin}`, `${node}-3`, description, icon, inputs3, outputs3);
- const node4Instance = new NodeInstance(`${node}-${4}`, `${plugin}`, `${node}-4`, description, icon, inputs4, outputs4);
+ const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, "folder", `${node}-1`, description, icon, inputs1, outputs1);
+ const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, "folder", `${node}-2`, description, icon, inputs2, outputs2);
+ const node3Instance = new NodeInstance(`${node}-${3}`, `${plugin}`, "folder", `${node}-3`, description, icon, inputs3, outputs3);
+ const node4Instance = new NodeInstance(`${node}-${4}`, `${plugin}`, "folder", `${node}-4`, description, icon, inputs4, outputs4);
const pos = { x: 0, y: 0 };
graph.addNode(node1Instance, pos);
@@ -396,8 +396,8 @@ test("Adding a node to a graph", () => {
{ type: "string", displayName: `Test-plugin.Node-2.3`, identifier: `out1` },
{ type: "number", displayName: `Test-plugin.Node-2.4`, identifier: `out2` });
- const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, `${node}-1`, description, icon, inputs1, outputs1);
- const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, `${node}-2`, description, icon, inputs2, outputs2);
+ const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, "folder", `${node}-1`, description, icon, inputs1, outputs1);
+ const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, "folder", `${node}-2`, description, icon, inputs2, outputs2);
const pos = { x: 0, y: 0 };
graph.addNode(node1Instance, pos);
@@ -468,10 +468,10 @@ test("Adding a node to a graph", () => {
{ type: "string", displayName: `Test-plugin.Node-4.3`, identifier: `out1` },
{ type: "number", displayName: `Test-plugin.Node-4.4`, identifier: `out2` });
- const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, `${node}-1`, description, icon, inputs1, outputs1);
- const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, `${node}-2`, description, icon, inputs2, outputs2);
- const node3Instance = new NodeInstance(`${node}-${3}`, `${plugin}`, `${node}-3`, description, icon, inputs3, outputs3);
- const node4Instance = new NodeInstance(`${node}-${4}`, `${plugin}`, `${node}-4`, description, icon, inputs4, outputs4);
+ const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, "folder", `${node}-1`, description, icon, inputs1, outputs1);
+ const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, "folder", `${node}-2`, description, icon, inputs2, outputs2);
+ const node3Instance = new NodeInstance(`${node}-${3}`, `${plugin}`, "folder", `${node}-3`, description, icon, inputs3, outputs3);
+ const node4Instance = new NodeInstance(`${node}-${4}`, `${plugin}`, "folder", `${node}-4`, description, icon, inputs4, outputs4);
const pos = { x: 0, y: 0 };
graph.addNode(node1Instance, pos);
@@ -722,8 +722,8 @@ describe("Test NodesAndEdgesGraph Class ", () => {
{ type: "string", displayName: `Test-plugin.Node-2.3`, identifier: `out1` },
{ type: "number", displayName: `Test-plugin.Node-2.4`, identifier: `out2` });
- const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, `${node}-1`, description, icon, inputs1, outputs1);
- const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, `${node}-2`, description, icon, inputs2, outputs2);
+ const node1Instance = new NodeInstance(`${node}-${1}`, `${plugin}`, "folder", `${node}-1`, description, icon, inputs1, outputs1);
+ const node2Instance = new NodeInstance(`${node}-${2}`, `${plugin}`, "folder", `${node}-2`, description, icon, inputs2, outputs2);
const pos = { x: 0, y: 0 };
g.addNode(node1Instance, pos);
diff --git a/tests/unit-tests/electron/lib/core-graph/CoreGraphExporter.spec.ts b/tests/unit-tests/electron/lib/core-graph/CoreGraphExporter.spec.ts
index 30bb89d2..d7092a28 100644
--- a/tests/unit-tests/electron/lib/core-graph/CoreGraphExporter.spec.ts
+++ b/tests/unit-tests/electron/lib/core-graph/CoreGraphExporter.spec.ts
@@ -55,8 +55,8 @@ jest.mock("fs", () => ({
{ type: "string", displayName: `Test-plugin.Node-2.3`, identifier: `out1` },
{ type: "number", displayName: `Test-plugin.Node-2.4`, identifier: `out2` });
- node1 = new NodeInstance("Node-1", "Test-Plugin","Node 1","This is node 1","fa-bell", inputs1, outputs1);
- node2 = new NodeInstance("Node-2", "Test-Plugin","node 2","This is node 2","fa-bell", inputs2, outputs2);
+ node1 = new NodeInstance("Node-1", "Test-Plugin", "folder", "Node 1", "This is node 1","fa-bell", inputs1, outputs1);
+ node2 = new NodeInstance("Node-2", "Test-Plugin", "folder", "node 2", "This is node 2","fa-bell", inputs2, outputs2);
let response: QueryResponse<{ nodeId: UUID }> = graph.addNode(node1, { x: 0, y: 0 });
const uuid1 = (response.data! as { nodeId: UUID }).nodeId;
diff --git a/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts b/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
index fba47738..7acfd71e 100644
--- a/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
+++ b/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
@@ -122,7 +122,7 @@ jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
test("Test addNode", () => {
//Add Node to graph
- const node = new NodeInstance("Jake", "Shark", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
+ const node = new NodeInstance("Jake", "Shark", "folder", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
graphManager.addGraph(graph);
graphManager.addNode(graph.uuid,node, { x: 0, y: 0 }, CoreGraphUpdateParticipant.system);
const nodes = graphManager.getGraph(graph.uuid).getNodes;
@@ -131,8 +131,8 @@ jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
test("Test addEdge", () => {
//Add Edge to graph
- const node = new NodeInstance("Jake", "Shark", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
- const node2 = new NodeInstance("Finn", "Human", "Human.Finn", "This is the Finn plugin :)", "1150", inputs, outputs);
+ const node = new NodeInstance("Jake", "Shark", "folder", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
+ const node2 = new NodeInstance("Finn", "Human", "folder", "Human.Finn", "This is the Finn plugin :)", "1150", inputs, outputs);
node.inputs.push(new InputAnchorInstance("number","aaaa","aaaa.number"));
node2.outputs.push(new OutputAnchorInstance("number","bbbb","bbbb.number"));
@@ -160,7 +160,7 @@ jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
})
test("Test removeNode", () => {
- const node = new NodeInstance("Jake", "Shark", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
+ const node = new NodeInstance("Jake", "Shark", "folder", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
graphManager.addGraph(graph);
graphManager.addNode(graph.uuid, node, { x: 0, y: 0 }, CoreGraphUpdateParticipant.system);
@@ -175,8 +175,8 @@ jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
test("Test removeEdge", () => {
//Add Edge to graph
- const node = new NodeInstance("Jake", "Shark", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
- const node2 = new NodeInstance("Finn", "Human", "Human.Finn", "This is the Finn plugin :)", "1150", inputs, outputs);
+ const node = new NodeInstance("Jake", "Shark", "folder", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
+ const node2 = new NodeInstance("Finn", "Human", "folder", "Human.Finn", "This is the Finn plugin :)", "1150", inputs, outputs);
node.inputs.push(new InputAnchorInstance("number","aaaa","aaaa.number"));
node2.outputs.push(new OutputAnchorInstance("number","bbbb","bbbb.number"));
@@ -204,7 +204,7 @@ jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
test("Test setPos", () => {
- const node = new NodeInstance("Jake", "Shark", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
+ const node = new NodeInstance("Jake", "Shark", "folder", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
graphManager.addGraph(graph);
graphManager.addNode(graph.uuid, node, { x: 0, y: 0 }, CoreGraphUpdateParticipant.system);
@@ -248,7 +248,7 @@ jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
const result = graphManager.addEdge("","","",CoreGraphUpdateParticipant.system);
expect(result.status).toBe("error");
- const node = new NodeInstance("Jake", "Shark", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
+ const node = new NodeInstance("Jake", "Shark", "folder", "Shark.Jake", "This is the Jake plugin :)", "1149", inputs, outputs);
const result2 = graphManager.addNode("", node, { x: 0, y: 0 }, CoreGraphUpdateParticipant.system);
expect(result2.status).toBe("error");
diff --git a/tests/unit-tests/electron/lib/core-graph/Toolbox.spec.ts b/tests/unit-tests/electron/lib/core-graph/Toolbox.spec.ts
index 28bb5ed4..55a776ed 100644
--- a/tests/unit-tests/electron/lib/core-graph/Toolbox.spec.ts
+++ b/tests/unit-tests/electron/lib/core-graph/Toolbox.spec.ts
@@ -16,7 +16,7 @@ describe("Test toolbox", () => {
beforeEach(() => {
jest.clearAllMocks();
- node = new NodeInstance("Shark", "Jake", "Shark.Jake", "The Jake plugin", "1149", inputs, outputs);
+ node = new NodeInstance("Shark", "Jake", "folder", "Shark.Jake", "The Jake plugin", "1149", inputs, outputs);
});
test("getId should be defined", () => {
@@ -52,7 +52,7 @@ describe("Test toolbox", () => {
});
test("getUI should return ui", () => {
- const nod = new NodeInstance("Jake.Shark", "Shark", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
+ const nod = new NodeInstance("Jake.Shark", "Shark", "folder", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
expect(nod.ui).toEqual(null);
});
@@ -233,7 +233,7 @@ describe("Test Toolbox registry", () => {
});
test("ToolboxRegistry should add and return registrys properly", () => {
- const node = new NodeInstance("Jake.Shark", "Shark", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
+ const node = new NodeInstance("Jake.Shark", "Shark", "folder", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
box.addInstance(node);
expect(box.getRegistry()[node.signature]).toEqual(node);
});
diff --git a/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts b/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts
index fa012704..aa671313 100644
--- a/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts
+++ b/tests/unit-tests/electron/lib/plugins/builder/NodeBuilder.spec.ts
@@ -20,10 +20,10 @@ describe("Test NodeBuilder", () => {
jest.clearAllMocks();
- const node = new NodeInstance("Jake.Shark", "Shark", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
+ const node = new NodeInstance("Jake.Shark", "Shark", "folder", "Jake", "The Jake plugin", "This is the Jake plugin", inputs, outputs);
const nodeUI = new NodeUIParent("", null);
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 1");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 1");
nodeUIBuilder = new NodeUIBuilder();
});
@@ -35,7 +35,7 @@ describe("Test NodeBuilder", () => {
});
test("getBuild should return NodeInstance", () => {
- const node = new NodeInstance("cool node 1", "testing-plugin", "Jake", "The Jake plugin","", inputs, outputs);
+ const node = new NodeInstance("cool node 1", "testing-plugin", "folder", "Jake", "The Jake plugin","", inputs, outputs);
nodeBuilder.setTitle("Jake");
nodeBuilder.setDescription("The Jake plugin");
nodeBuilder.define(() => {return {"res" : "Shrek"}});
@@ -43,18 +43,18 @@ describe("Test NodeBuilder", () => {
});
test("setTitle should set the title", () => {
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 3");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 3");
nodeBuilder.setTitle("Jake");
expect(nodeBuilder["partialNode"].displayName).toEqual("Jake"); });
test("setDescription should set the description", () => {
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 3");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 3");
nodeBuilder.setDescription("The Jake plugin");
expect(nodeBuilder["partialNode"].description).toEqual("The Jake plugin");
});
test("define should set the function", () => {
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 4");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 4");
nodeBuilder.define(() => {return {"res" : "Shrek"}});
expect(nodeBuilder["partialNode"].func({},{},[]).res).toEqual("Shrek");
});
@@ -73,24 +73,24 @@ describe("Test NodeBuilder", () => {
beforeEach(() => {
jest.clearAllMocks();
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 5");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 5");
nodeUIBuilder = new NodeUIBuilder();
});
test("addIcon should add an icon", () => {
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 6");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 6");
nodeBuilder.addIcon("Shrek");
expect(nodeBuilder["partialNode"].icon).toEqual("Shrek");
});
test("addInput should add an input", () => {
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 7");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 7");
nodeBuilder.addInput("string", "shrek", "Shrek");
expect(nodeBuilder["partialNode"].inputs[0].displayName).toEqual("Shrek");
});
test("addOutput should add an output", () => {
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 8");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 8");
nodeBuilder.addOutput("string", "shrek2", "Shrek");
expect(nodeBuilder["partialNode"].outputs[0].displayName).toEqual("Shrek");
});
@@ -104,7 +104,7 @@ describe("Test NodeUIBuilder", () => {
beforeEach(() => {
jest.clearAllMocks();
- nodeBuilder = new NodeBuilder("testing-plugin", "cool node 9");
+ nodeBuilder = new NodeBuilder("testing-plugin", "folder", "cool node 9");
nodeUIBuilder = new NodeUIBuilder();
});
From 1412610ee22866c2de1fbc7b2f9cbc50766d26ac Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Sat, 23 Sep 2023 18:45:23 +0200
Subject: [PATCH 136/209] Initiated TileBuilder testing
---
.../lib/plugins/builders/TileBuilder.ts | 79 ++++++++++++++++++-
.../lib/plugins/builder/TileBuilder.spec.ts | 53 +++++++++++++
2 files changed, 131 insertions(+), 1 deletion(-)
create mode 100644 tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts
diff --git a/src/electron/lib/plugins/builders/TileBuilder.ts b/src/electron/lib/plugins/builders/TileBuilder.ts
index 1763090b..973a4775 100644
--- a/src/electron/lib/plugins/builders/TileBuilder.ts
+++ b/src/electron/lib/plugins/builders/TileBuilder.ts
@@ -20,9 +20,19 @@ type PartialTile = {
uiConfigs: { [key: string]: UIComponentConfig };
};
+/**
+ * TileBuilder is a builder class for creating TileInstance objects.
+ * It is used to create a TileInstance object that can be registered with the TileRegistry.
+ */
+
export class TileBuilder implements PluginContextBuilder {
private partialTile: PartialTile;
+ /**
+ * @param plugin string
+ * @param name string
+ */
+
constructor(plugin: string, name: string) {
this.partialTile = {
name,
@@ -35,6 +45,11 @@ export class TileBuilder implements PluginContextBuilder {
};
}
+ /**
+ * Returns the TileInstance object that has been built.
+ * @returns TileInstance
+ */
+
get build(): TileInstance {
return new TileInstance(
this.partialTile.name,
@@ -46,40 +61,102 @@ export class TileBuilder implements PluginContextBuilder {
this.partialTile.uiConfigs
);
}
+
+ /**
+ * Resets the tile builder's tile instance to its initial state
+ * @returns void
+ */
+
public reset(): void {
+ this.partialTile = {
+ name: this.partialTile.name,
+ plugin: this.partialTile.plugin,
+ displayName: this.partialTile.displayName,
+ description: "",
+ icon: "",
+ ui: {},
+ uiConfigs: {},
+ };
+
return;
}
// TODO: Implement all of these
+
+ /**
+ * Sets the title of the tile
+ * @param title string
+ * @returns void
+ */
public setTitle(title: string): void {
this.partialTile.displayName = title;
}
+
+ /**
+ * Sets the description of the tile
+ * @param description string
+ * @returns void
+ */
public setDescription(description: string): void {
this.partialTile.description = description;
}
+
+ /**
+ * Sets the icon of the tile
+ * @param icon string
+ * @returns void
+ */
public addIcon(icon: string): void {
this.partialTile.icon = icon;
}
+
+ /**
+ * Sets the UI of the tile
+ * @param ui ITileUI
+ * @returns void
+ */
public setUI(ui: TileUIBuilder): void {
this.partialTile.ui = ui.getUI();
this.partialTile.uiConfigs = ui.getUIConfigs();
}
+ /**
+ * Creates a new TileUIBuilder object
+ * @returns TileUIBuilder
+ */
+
public createUIBuilder(): TileUIBuilder {
const builder = new TileUIBuilder();
return builder;
}
+ /**
+ * adds a new UI element to the tile
+ * @todo implement this function
+ * @returns void
+ */
+
public addUIElement(): void {
return;
}
}
-function getRandomComponentId(type: TileUIComponent) {
+/**
+ * Returns a random component id for a given TileUIComponent
+ * @param type TileUIComponent
+ * @returns string
+ */
+
+function getRandomComponentId(type: TileUIComponent): string {
return `${type.toString()}-${Math.floor(Math.random() * 16 ** 6).toString(16)}`;
}
+export function forTesting() {
+ // to allow testing of getRandomComponentId
+ return getRandomComponentId;
+}
+
export class TileUIBuilder {
private _main: TileUIParent;
private _sidebar: tileUIComponentBuilder | null;
diff --git a/tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts b/tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts
new file mode 100644
index 00000000..763e53b0
--- /dev/null
+++ b/tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts
@@ -0,0 +1,53 @@
+
+import {TileBuilder, TileUIBuilder, forTesting} from "../../../../../../src/electron/lib/plugins/builders/TileBuilder"
+import { TileInstance } from "../../../../../../src/electron/lib/registries/TileRegistry";
+
+describe("Test TileBuilder", () => {
+ let tileBuilder : TileBuilder;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ tileBuilder = new TileBuilder("testing-plugin", "cool tile 6");
+ });
+
+ test("Initial build", () => {
+ const build : TileInstance = tileBuilder.build;
+
+ const expectedBuild : TileInstance = new TileInstance(
+ "cool tile 6",
+ "testing-plugin",
+ "cool tile 6",
+ "",
+ "",
+ {},
+ {},
+ );
+
+ expect(build).toEqual(expectedBuild);
+
+ tileBuilder.reset();
+ expect(tileBuilder.build).toEqual(expectedBuild);
+
+ });
+
+ test("Getters and setters", () => {
+ tileBuilder.setDescription("Hello world");
+
+ tileBuilder.addIcon("Iconicus");
+ tileBuilder.setTitle("This is a title");
+
+ const tileUIBuilder = new TileUIBuilder();
+ tileBuilder.setUI(tileUIBuilder);
+
+ expect(tileBuilder.build.description).toEqual("Hello world");
+ expect(tileBuilder.build.icon).toEqual("Iconicus");
+ expect(tileBuilder.build.displayName).toEqual("This is a title");
+ expect(tileBuilder.build.ui).toEqual(tileUIBuilder.getUI());
+ expect(tileBuilder.build.uiConfigs).toEqual(tileUIBuilder.getUIConfigs());
+
+ expect(tileBuilder.createUIBuilder()).toBeDefined();
+ expect(tileBuilder.addUIElement()).toReturn;
+ });
+
+
+});
\ No newline at end of file
From 8eb16fabb7fc1856b34e31e3cfff9edea5289fd0 Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Sun, 24 Sep 2023 14:25:01 +0200
Subject: [PATCH 137/209] Add emboss, bulge and zoomblur filters to blink
---
blix-plugins/blink/src/main.cjs | 26 +++++++++++++++++++
blix-plugins/blink/webview/types.ts | 16 ++++++++++--
.../lib/core-graph/CoreGraphInterpreter.ts | 1 +
3 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 03e02fb0..57d97d1a 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -179,6 +179,32 @@ const blinkNodes = {
{ id: "seed", min: 0, max: 1, step: 0.01 },
]
],
+ "emboss": [
+ "Emboss",
+ "An RGB Split Filter.",
+ [{ id: "strength", min: 0, max: 20, step: 0.1 }]
+ ],
+ "bulge": [
+ "Bulge / Pinch",
+ "Bulges or pinches the image in a circle.",
+ [
+ { id: "radius", min: 0, max: 1000, step: 1.0 },
+ { id: "strenght", min: -1, max: 1, step: 0.01 },
+ { id: "center.x", min: 0, max: 1, step: 0.01 },
+ { id: "center.y", min: 0, max: 1, step: 0.01 },
+ ]
+ ],
+ "zoomblur": [
+ "ZoomBlur",
+ "The ZoomFilter applies a Zoom blur to an object.",
+ [
+ { id: "strenght", min: 0, max: 0.5, step: 0.01 },
+ { id: "innerRadius", min: 0, max: 1000, step: 1.0 },
+ { id: "center.x", min: 0, max: 2000, step: 1.0 },
+ { id: "center.y", min: 0, max: 2000, step: 1.0 },
+ ]
+ ],
+
};
Object.keys(blinkNodes).forEach((key) => {
diff --git a/blix-plugins/blink/webview/types.ts b/blix-plugins/blink/webview/types.ts
index 6570fd9d..ffcfddee 100644
--- a/blix-plugins/blink/webview/types.ts
+++ b/blix-plugins/blink/webview/types.ts
@@ -80,9 +80,21 @@ export function getPixiFilter(filter: Filter) {
}
);
case "emboss": return new EmbossFilter(...filter.params);
- case "bulge": return new BulgePinchFilter(...filter.params);
+ case "bulge": return new BulgePinchFilter(
+ {
+ radius: filter.params[0],
+ strength: filter.params[1],
+ center: [filter.params[2], filter.params[3]],
+ }
+ );
case "glitch": return new GlitchFilter(...filter.params);
- case "zoomblur": return new ZoomBlurFilter(...filter.params);
+ case "zoomblur": return new ZoomBlurFilter(
+ {
+ strength: filter.params[0],
+ innerRadius: filter.params[1],
+ center: [filter.params[2], filter.params[3]],
+ }
+ );
case "twist": return new TwistFilter(...filter.params);
}
} catch {
diff --git a/src/electron/lib/core-graph/CoreGraphInterpreter.ts b/src/electron/lib/core-graph/CoreGraphInterpreter.ts
index 98e21c41..910b682e 100644
--- a/src/electron/lib/core-graph/CoreGraphInterpreter.ts
+++ b/src/electron/lib/core-graph/CoreGraphInterpreter.ts
@@ -147,6 +147,7 @@ export class CoreGraphInterpreter {
}
// Resolve all input values (functions)
+
const inputs: { [key: string]: T }[] = await Promise.all(inputPromises).catch((err) => {
throw err;
});
From ccb1627221efb1b2b1258fcb66da33ea74421be7 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Sun, 24 Sep 2023 16:12:29 +0200
Subject: [PATCH 138/209] TileBuilder tests improved
---
.../lib/plugins/builders/TileBuilder.ts | 77 +++++++++
.../lib/plugins/builder/TileBuilder.spec.ts | 147 ++++++++++++++++++
2 files changed, 224 insertions(+)
diff --git a/src/electron/lib/plugins/builders/TileBuilder.ts b/src/electron/lib/plugins/builders/TileBuilder.ts
index 973a4775..0a995f29 100644
--- a/src/electron/lib/plugins/builders/TileBuilder.ts
+++ b/src/electron/lib/plugins/builders/TileBuilder.ts
@@ -170,31 +170,67 @@ export class TileUIBuilder {
this.uiConfigs = {};
}
+ /**
+ * Returns the TileUI object that has been built.
+ * @returns any
+ * @todo implement this function
+ */
get build(): any {
return null;
}
+ /**
+ * Returns the main TileUIParent object
+ * @returns TileUIParent
+ */
+
get main(): TileUIParent {
return this._main;
}
+ /**
+ * Returns the sidebar TileUIParent object
+ * @returns tileUIComponentBuilder | null
+ */
get sidebar(): tileUIComponentBuilder | null {
return this._sidebar;
}
+ /**
+ * Returns the statusbar TileUIParent object
+ * @returns tileUIComponentBuilder | null
+ */
+
get statusbar(): tileUIComponentBuilder | null {
return this._statusbar;
}
+ /**
+ * Adds a layout to this builders main childUIs
+ * @param builder
+ * @returns void
+ */
+
public addLayout(builder: TileUIBuilder): void {
this.main.childUis = { ui: builder.getUI(), uiConfigs: builder.getUIConfigs() };
}
+ /**
+ * Adds a sidebar to this builders sidebar
+ * @param location string
+ * @returns
+ */
+
public addSidebar(location: string): TileUIBuilder {
this._sidebar = new tileUIComponentBuilder("", location);
return this;
}
+ /**
+ * Adds a statusbar to this builders statusbar
+ * @param location string
+ * @returns
+ */
public addStatusbar(location: string): TileUIBuilder {
this._statusbar = new tileUIComponentBuilder("", location);
return this;
@@ -208,6 +244,11 @@ export class TileUIBuilder {
// return;
// }
+ /**
+ * Gets the UI object that has been built.
+ * @returns { [key: string]: TileUIParent | null }
+ */
+
public getUI(): { [key: string]: TileUIParent | null } {
return {
main: this.main,
@@ -216,11 +257,19 @@ export class TileUIBuilder {
};
}
+ /**
+ * Gets the UI configs that have been built.
+ * @returns { [key: string]: UIComponentConfig }
+ */
+
public getUIConfigs(): { [key: string]: UIComponentConfig } {
return Object.assign({}, this.uiConfigs, this.sidebar?.uiConfigs, this.statusbar?.uiConfigs);
}
}
+/**
+ * TileUIComponentBuilder is a builder class for creating TileUIParent objects.
+ */
class tileUIComponentBuilder {
private _component: TileUIParent;
private _uiConfigs: { [key: string]: UIComponentConfig };
@@ -238,6 +287,13 @@ class tileUIComponentBuilder {
this._uiConfigs = {};
}
+ /**
+ * Adds a button to this builders component
+ * @param config UIComponentConfig
+ * @param props UIComponentProps
+ * @returns tileUIComponentBuilder
+ */
+
public addButton(config: UIComponentConfig, props: UIComponentProps): tileUIComponentBuilder {
const componentId = config.componentId ?? getRandomComponentId(TileUIComponent.Button);
this._component?.params.push(
@@ -280,6 +336,13 @@ class tileUIComponentBuilder {
// return;
// }
+ /**
+ * Adds a slider to this builders component
+ * @param config
+ * @param props
+ * @returns tileUIComponentBuilder
+ */
+
public addSlider(config: UIComponentConfig, props: UIComponentProps): tileUIComponentBuilder {
const componentId = config.componentId ?? getRandomComponentId(TileUIComponent.Slider);
this.component.params.push(
@@ -326,6 +389,13 @@ class tileUIComponentBuilder {
// return this;
// }
+ /**
+ * Adds a dropdown to this builders component
+ * @param config
+ * @param props
+ * @returns tileUIComponentBuilder
+ */
+
public addDropdown(config: UIComponentConfig, props: UIComponentProps): tileUIComponentBuilder {
const componentId = config.componentId ?? getRandomComponentId(TileUIComponent.Dropdown);
this.component.params.push(
@@ -354,6 +424,13 @@ class tileUIComponentBuilder {
// return this;
// }
+ /**
+ * Adds a text input to this builders component
+ * @param config
+ * @param props
+ * @returns tileUIComponentBuilder
+ */
+
public addTextInput(config: UIComponentConfig, props: UIComponentProps): tileUIComponentBuilder {
const componentId = config.componentId ?? getRandomComponentId(TileUIComponent.TextInput);
this.component.params.push(
diff --git a/tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts b/tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts
index 763e53b0..4d427aff 100644
--- a/tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts
+++ b/tests/unit-tests/electron/lib/plugins/builder/TileBuilder.spec.ts
@@ -1,6 +1,8 @@
+import { Slider } from "blix_svelvet";
import {TileBuilder, TileUIBuilder, forTesting} from "../../../../../../src/electron/lib/plugins/builders/TileBuilder"
import { TileInstance } from "../../../../../../src/electron/lib/registries/TileRegistry";
+import { TileUI, TileUIParent, UIComponentConfig, UIComponentProps,TileUIComponent, TileUILeaf } from "../../../../../../src/shared/ui/TileUITypes";
describe("Test TileBuilder", () => {
let tileBuilder : TileBuilder;
@@ -48,6 +50,151 @@ describe("Test TileBuilder", () => {
expect(tileBuilder.createUIBuilder()).toBeDefined();
expect(tileBuilder.addUIElement()).toReturn;
});
+});
+describe("Test TileUIBuilder", () => {
+ let tileUIBuilder : TileUIBuilder;
+ beforeEach(() => {
+ jest.clearAllMocks();
+ tileUIBuilder = new TileUIBuilder();
+ })
+
+ test("Initial build", () => {
+ expect(tileUIBuilder.main).toBeDefined();
+ expect(tileUIBuilder.sidebar).toBe(null);
+ expect(tileUIBuilder.statusbar).toBe(null);
+ expect(tileUIBuilder.getUIConfigs()).toEqual({});
+ });
+
+ test("Getters and setters", () => {
+ expect(tileUIBuilder.build).toBe(null);
+ expect(tileUIBuilder.main).toBeInstanceOf(TileUIParent);
+ expect(tileUIBuilder.main).toBeDefined();
+ expect(tileUIBuilder.sidebar).toBe(null);
+ expect(tileUIBuilder.statusbar).toBe(null);
+ });
+
+ test("Add UI element", () => {
+ const tileUIBuilder2 = new TileUIBuilder();
+
+ const UI = tileUIBuilder2.getUI();
+ const UIConfigs = tileUIBuilder2.getUIConfigs();
+
+
+ tileUIBuilder.addLayout(tileUIBuilder2);
+
+ expect(tileUIBuilder.main).toBeDefined();
+
+ expect(tileUIBuilder.main.childUis?.ui).toEqual(UI);
+ expect(tileUIBuilder.main.childUis?.uiConfigs).toEqual(UIConfigs);
+
+
+ expect(tileUIBuilder.addSidebar("below")).toBe(tileUIBuilder)
+ expect(tileUIBuilder.sidebar).toBeDefined();
+ expect(tileUIBuilder.sidebar?.component.location).toBe("below");
+
+ expect(tileUIBuilder.addStatusbar("below")).toBe(tileUIBuilder)
+ expect(tileUIBuilder.sidebar).toBeDefined();
+ expect(tileUIBuilder.sidebar?.component.location).toBe("below");
+
+ expect(tileUIBuilder.sidebar?.uiConfigs).toEqual({});
+ })
+
+ test("Add UI element with configs", () => {
+ tileUIBuilder.addSidebar("below");
+
+ const uiCompontentConfig : UIComponentConfig = {
+ label: "Hello",
+ componentId: "world",
+ defaultValue: "!",
+ updatesBackend: true,
+ }
+
+ const uiComponentProps : UIComponentProps = {
+ "Hello": "world",
+ }
+
+ const leaf = new TileUILeaf(tileUIBuilder.sidebar?.component!, TileUIComponent.Button, "world", [uiComponentProps])
+
+ tileUIBuilder.sidebar?.addButton(uiCompontentConfig, uiComponentProps);
+ expect(tileUIBuilder.sidebar?.component.params).toHaveLength(1);
+ expect(tileUIBuilder.sidebar?.component.params[0]).toEqual(leaf);
+ expect(tileUIBuilder.sidebar?.uiConfigs["world"]).toEqual(uiCompontentConfig);
+ })
+
+ test("Add slider UI element with configs", () => {
+ tileUIBuilder.addSidebar("below");
+
+ const uiCompontentConfig : UIComponentConfig = {
+ label: "Hello",
+ componentId: "world",
+ defaultValue: "!",
+ updatesBackend: true,
+ }
+
+ const uiComponentProps : UIComponentProps = {
+ "Hello": "world",
+ }
+
+ const leaf = new TileUILeaf(tileUIBuilder.sidebar?.component!, TileUIComponent.Slider, "world", [uiComponentProps])
+
+ tileUIBuilder.sidebar?.addSlider(uiCompontentConfig, uiComponentProps);
+ expect(tileUIBuilder.sidebar?.component.params).toHaveLength(1);
+ expect(tileUIBuilder.sidebar?.component.params[0]).toEqual(leaf);
+ expect(tileUIBuilder.sidebar?.uiConfigs["world"]).toEqual(uiCompontentConfig);
+ })
+
+ test("Add Dropdown UI element with configs", () => {
+ tileUIBuilder.addSidebar("below");
+
+ const uiCompontentConfig : UIComponentConfig = {
+ label: "Hello",
+ componentId: "world",
+ defaultValue: "!",
+ updatesBackend: true,
+ }
+
+ const uiComponentProps : UIComponentProps = {
+ "Hello": "world",
+ }
+
+ const leaf = new TileUILeaf(tileUIBuilder.sidebar?.component!, TileUIComponent.Dropdown, "world", [uiComponentProps])
+
+ tileUIBuilder.sidebar?.addDropdown(uiCompontentConfig, uiComponentProps);
+ expect(tileUIBuilder.sidebar?.component.params).toHaveLength(1);
+ expect(tileUIBuilder.sidebar?.component.params[0]).toEqual(leaf);
+ expect(tileUIBuilder.sidebar?.uiConfigs["world"]).toEqual(uiCompontentConfig);
+ })
+
+ test("Add TextInput UI element with configs", () => {
+ tileUIBuilder.addSidebar("below");
+
+ const uiCompontentConfig : UIComponentConfig = {
+ label: "Hello",
+ componentId: "world",
+ defaultValue: "!",
+ updatesBackend: true,
+ }
+
+ const uiComponentProps : UIComponentProps = {
+ "Hello": "world",
+ }
+
+ const leaf = new TileUILeaf(tileUIBuilder.sidebar?.component!, TileUIComponent.TextInput, "world", [uiComponentProps])
+
+ tileUIBuilder.sidebar?.addTextInput(uiCompontentConfig, uiComponentProps);
+ expect(tileUIBuilder.sidebar?.component.params).toHaveLength(1);
+ expect(tileUIBuilder.sidebar?.component.params[0]).toEqual(leaf);
+ expect(tileUIBuilder.sidebar?.uiConfigs["world"]).toEqual(uiCompontentConfig);
+ })
+
+ test("For testing", () => {
+ const tileUIBuilder2 = new TileUIBuilder();
+
+ const result = forTesting();
+
+ expect(result(TileUIComponent.Button).substring(0,7)).toBe(`${TileUIComponent.Button.toString()}-${Math.floor(Math.random() * 16 ** 6).toString(16)}`.substring(0,7))
+
+ })
});
\ No newline at end of file
From 6860d88b80cf5688aee9d9888fbdc5fe178d2093 Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Mon, 25 Sep 2023 00:53:44 +0200
Subject: [PATCH 139/209] Add frontend implementation of export system
---
blix-plugins/blink/webview/App.svelte | 20 ++++++++++++++-----
src/electron/lib/webviews/preload.ts | 11 +++++++----
src/frontend/ui/tiles/Media.svelte | 28 ++++++++++++++++++++++-----
src/frontend/ui/tiles/WebView.svelte | 12 ++++++++++--
4 files changed, 55 insertions(+), 16 deletions(-)
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index d333b138..27ee0bb9 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -24,6 +24,15 @@
onMount(async () => {
// window.addEventListener("DOMContentLoaded", async () => {
+
+ // Add export listener
+
+ window.api.on("export", async () => {
+ exportImage();
+ // send("exportResponse", "exported");
+ })
+
+
//====== INITIALIZE PIXI ======//
blink = new PIXI.Application({
view: pixiCanvas,
@@ -173,12 +182,13 @@
const frame = new PIXI.Rectangle(-bounds.x-imgCanvasBlockW/2, -bounds.y-imgCanvasBlockH/2, imgCanvasBlockW, imgCanvasBlockH);
const exportCanvas = blink.renderer.extract.canvas(currScene, frame);
- exportCanvas.toBlob((blob) => {
+ exportCanvas.toBlob(async (blob) => {
const metadata = {
contentType: "image/png",
name: `Blink Export ${Math.floor(100000 * Math.random())}`
};
- window.cache.write(blob, metadata);
+
+ send("exportResponse", {cacheUUID: await window.cache.write(blob, metadata)});
}, "image/png");
// REMOVED: Exporting straight to local file
// const link = document.createElement("a");
@@ -191,7 +201,7 @@
- Export
+
@@ -207,14 +217,14 @@
padding: 0px;
}
- button {
+ /* button {
position: absolute;
z-index: 10;
top: 10px;
right: 0px;
width: 60px;
height: 30px;
- }
+ } */
.cursorPointer {
cursor: pointer;
diff --git a/src/electron/lib/webviews/preload.ts b/src/electron/lib/webviews/preload.ts
index 9a5da9a2..638b079f 100644
--- a/src/electron/lib/webviews/preload.ts
+++ b/src/electron/lib/webviews/preload.ts
@@ -85,10 +85,13 @@ contextBridge.exposeInMainWorld("cache", {
messageId,
metadata,
} as CacheRequest);
- const metadataResp = (await sendCachePayload(
- metadataMessageId,
- metadataPayload
- )) as CacheResponse;
+ // TODO fix this
+ // const metadataResp = ( await sendCachePayload(
+ // metadataMessageId,
+ // metadataPayload
+ // )) as CacheResponse;
+
+ const metadataResp = sendCachePayload(metadataMessageId, metadataPayload);
}
return writeResp.id;
diff --git a/src/frontend/ui/tiles/Media.svelte b/src/frontend/ui/tiles/Media.svelte
index 95b00062..b9e16c76 100644
--- a/src/frontend/ui/tiles/Media.svelte
+++ b/src/frontend/ui/tiles/Media.svelte
@@ -55,6 +55,7 @@
let mediaId = writable("");
let oldMediaId: string | null = null;
+ let webview: WebView;
const unsubMedia = mediaId.subscribe((newMediaId) => {
// console.log("SUBSCRIBE MEDIA ID", oldMediaId, newMediaId);
@@ -82,10 +83,18 @@
async function exportMedia(e: Event) {
if ($media?.dataType && $media?.content) {
- await mediaStore.exportMedia($media);
+ if ($media.display.displayType === "webview") {
+ webview.exportMedia(e);
+ } else {
+ await mediaStore.exportMedia($media);
+ }
}
}
+ async function exportCache(e: CustomEvent) {
+ console.log("export cache", e.detail);
+ }
+
function getDisplayProps(media: DisplayableMediaOutput) {
let res = media.display.props;
if (media.display.contentProp !== null) res[media.display.contentProp] ??= media.content; // If content nullish, use default value
@@ -145,10 +154,19 @@
{:else}
{uuid.slice(0, 8)}
{$cacheStore[uuid].name ?? "-"}
{$cacheStore[uuid].contentType}
+
+
Save As
{/if}
{/each}
@@ -126,6 +146,14 @@
z-index: 100;
}
+ .exportButton {
+ font-size: 0.6em;
+ height: 20px;
+ width: 50%;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
.itemsBox {
display: flex;
flex-wrap: wrap;
@@ -139,9 +167,9 @@
.item {
display: grid;
- grid-template-rows: 70% 15% 15%;
- width: 100px;
- height: 100px;
+ grid-template-rows: 55% 15% 15% 15%;
+ width: 120px;
+ height: 130px;
min-width: 100px;
min-height: 100px;
border: 1px solid #2a2a3f;
diff --git a/src/frontend/ui/tiles/Media.svelte b/src/frontend/ui/tiles/Media.svelte
index dac6382a..a3f597a2 100644
--- a/src/frontend/ui/tiles/Media.svelte
+++ b/src/frontend/ui/tiles/Media.svelte
@@ -151,7 +151,7 @@
on:keydown="{null}"
class="flex h-7 select-none items-center justify-center rounded-md border border-zinc-600 bg-zinc-800/80 p-2 text-zinc-400 hover:bg-zinc-700 active:bg-zinc-800/50"
>
- Export
+ Export To Cache
-
+
{#each Object.keys(items) as itemKey}
{items[itemKey].name} [{itemKey}] ({items[itemKey].contentType}) {
+ const now = performance.now();
+ if (!first) {
+ if (now - prev >= 1000) {
+ dispatch("inputInteraction", { id: config.componentId, value });
+ }
+ } else {
+ first = false;
+ }
+ prev = now;
+ });
+ }
diff --git a/src/frontend/ui/utils/graph/nodeUIComponents/Dropdown.svelte b/src/frontend/ui/utils/graph/nodeUIComponents/Dropdown.svelte
index 97587a54..aa24fd6c 100644
--- a/src/frontend/ui/utils/graph/nodeUIComponents/Dropdown.svelte
+++ b/src/frontend/ui/utils/graph/nodeUIComponents/Dropdown.svelte
@@ -26,7 +26,6 @@
{#if Object.keys(items).length > 0}
{#key inputStore.inputs[config.componentId]}
-
{#each Object.keys(items) as itemKey}
{itemKey}
diff --git a/src/frontend/ui/utils/graph/nodeUIComponents/NumberInput.svelte b/src/frontend/ui/utils/graph/nodeUIComponents/NumberInput.svelte
index cc710630..6db843a5 100644
--- a/src/frontend/ui/utils/graph/nodeUIComponents/NumberInput.svelte
+++ b/src/frontend/ui/utils/graph/nodeUIComponents/NumberInput.svelte
@@ -2,12 +2,14 @@
import { get, writable } from "svelte/store";
import { UIValueStore } from "@shared/ui/UIGraph";
import type { UIComponentConfig, UIComponentProps } from "@shared/ui/NodeUITypes";
+ import { createEventDispatcher } from "svelte";
const randomId = Math.random().toString(32);
export let props: UIComponentProps;
export let inputStore: UIValueStore;
export let config: UIComponentConfig;
+ const dispatch = createEventDispatcher();
if (!inputStore.inputs[config.componentId]) inputStore.inputs[config.componentId] = writable(0);
@@ -36,6 +38,7 @@
function handlePointerUp() {
isDragging = false;
+ if (initialValue !== (get(valStore) as number)) handleInteraction();
}
function handlePointerMove(e: PointerEvent) {
@@ -48,6 +51,10 @@
}
$: valStore = inputStore.inputs[config.componentId];
+
+ function handleInteraction() {
+ dispatch("inputInteraction", { id: config.componentId, value: $valStore });
+ }
Date: Mon, 25 Sep 2023 20:22:06 +0200
Subject: [PATCH 148/209] Cleanup cache picker
---
.../utils/graph/nodeUIComponents/CachePicker.svelte | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/frontend/ui/utils/graph/nodeUIComponents/CachePicker.svelte b/src/frontend/ui/utils/graph/nodeUIComponents/CachePicker.svelte
index 19d656c2..3ef3fd70 100644
--- a/src/frontend/ui/utils/graph/nodeUIComponents/CachePicker.svelte
+++ b/src/frontend/ui/utils/graph/nodeUIComponents/CachePicker.svelte
@@ -2,6 +2,7 @@
import { writable } from "svelte/store";
import { UIValueStore } from "@shared/ui/UIGraph";
import { cacheStore } from "../../../../lib/stores/CacheStore";
+ import { blixStore } from "../../../../lib/stores/BlixStore";
import type { UIComponentConfig, UIComponentProps } from "@shared/ui/NodeUITypes";
import { createEventDispatcher } from "svelte";
@@ -29,16 +30,20 @@
{#key inputStore.inputs[config.componentId]}
{#each Object.keys(items) as itemKey}
- {items[itemKey].name} [{itemKey}] ({items[itemKey].contentType})
+ {#if !$blixStore.production}
+ {items[itemKey].name} [{itemKey}] ({items[itemKey].contentType})
+ {:else}
+ {items[itemKey].name}
+ {/if}
{/each}
{/key}
{:else}
- -
+ Add an asset
{/if}
From 25cf59228aafc7fe34d4ad178f11ba8ea3d5a4e7 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Mon, 25 Sep 2023 20:29:06 +0200
Subject: [PATCH 149/209] Settings.ts fully tested
---
.../electron/utils/settings.spec.ts | 35 ++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/tests/unit-tests/electron/utils/settings.spec.ts b/tests/unit-tests/electron/utils/settings.spec.ts
index 0ed1563c..5a91af51 100644
--- a/tests/unit-tests/electron/utils/settings.spec.ts
+++ b/tests/unit-tests/electron/utils/settings.spec.ts
@@ -1,5 +1,6 @@
-import {setSecret,settings,getSecret,getSecrets} from "../../../../src/electron/utils/settings";
+import {setSecret,settings,getSecret,getSecrets, clearSecret, setRecentProjects, getRecentProjects, decryptWithSafeStorage} from "../../../../src/electron/utils/settings";
import { safeStorage } from "electron";
+import { recentProject } from "../../../../src/shared/types";
let flag = true; // Used to flip isEncryptionAvailable() return value
@@ -63,5 +64,37 @@ describe("Testing settings.ts", () => {
expect(settings["cookedKey"]).toBe("cookedValue");
})
+ it("Test remaining functions", () => {
+ const invalidTypeInput = 42 as unknown as string;
+ expect(decryptWithSafeStorage(invalidTypeInput)).toBe("");
+
+ expect(getSecret(invalidTypeInput)).toBe("");
+
+
+ setSecret("key", "value");
+ clearSecret("key");
+ expect(settings.get("secrets.key")).toBe("");
+
+ const projects : recentProject[] = [
+ {
+ path: "path",
+ lastEdited: "lastEdited"
+ }
+ ];
+ setRecentProjects(projects);
+ expect(settings.get("recentProjects")).toEqual(projects);
+
+ expect(getRecentProjects()).toEqual(projects);
+
+ settings.set("secrets","ay");
+ expect(getSecrets()).toEqual({});
+
+
+ const nextInvalid = undefined as unknown as string;
+ expect(decryptWithSafeStorage(nextInvalid)).toBe("");
+ flag = false;
+ expect(decryptWithSafeStorage(nextInvalid)).toBe("");
+
+ })
});
\ No newline at end of file
From 4430ed93bc5b4042ee93728f82a660e241a6d318 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Mon, 25 Sep 2023 21:03:05 +0200
Subject: [PATCH 150/209] Initial TypeClassRegistry tests
---
.../lib/registries/TypeclassRegistry.ts | 32 ++++++++
.../lib/registries/TypeClassRegistry.spec.ts | 81 +++++++++++++++++++
2 files changed, 113 insertions(+)
create mode 100644 tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts
diff --git a/src/electron/lib/registries/TypeclassRegistry.ts b/src/electron/lib/registries/TypeclassRegistry.ts
index b4902fb4..15d35b01 100644
--- a/src/electron/lib/registries/TypeclassRegistry.ts
+++ b/src/electron/lib/registries/TypeclassRegistry.ts
@@ -46,6 +46,16 @@ export class TypeclassRegistry implements Registry {
});
}
+ /**
+ * Adds a typeclass instance to the registry.
+ * The instance will not be added if it already exists, unless `force` is true.
+ * The instance must be defined.
+ * This function will also notify the main window of the change.
+ * @param instance
+ * @param force default : false
+ * @returns
+ */
+
addInstance(instance: Typeclass, force = false): void {
if (!instance) {
logger.warn("Invalid Typeclass");
@@ -59,10 +69,20 @@ export class TypeclassRegistry implements Registry {
this.blix.mainWindow?.apis.commandClientApi.registryChanged(this.getTypeclasses());
}
+ /**
+ * @returns All typeclasses in the registry.
+ */
getRegistry() {
return { ...this.typeclasses };
}
+ // ADD DOC
+ /**
+ * Inserts a new converter into the registry's converter array
+ * @param from input type
+ * @param to output type
+ * @param converter converter to be inserted
+ */
addConverter(from: TypeclassId, to: TypeclassId, converter: TypeConverter): void {
if (!this.converters[from]) {
this.converters[from] = {};
@@ -71,6 +91,13 @@ export class TypeclassRegistry implements Registry {
}
// Returns a composite converter from `from` to `to`
+ /**
+ * Searches for a composite converter from `from` to `to`. If none is found, returns null.
+ * @param from input type
+ * @param to output type
+ * @param depth Specifies how deep the search should go. Default is 2.
+ * @returns Returns a composite converter from `from` to `to`
+ */
resolveConversion(from: TypeclassId, to: TypeclassId, depth?: number): TypeConverter | null {
depth = depth ?? MAX_SEARCH_DEPTH;
if (depth <= 0) return null;
@@ -97,6 +124,11 @@ export class TypeclassRegistry implements Registry {
}
// Returns true if the `from` type is compatible with the `to` type
+ /**
+ * Returns true if the `from` type is compatible with the `to` type
+ * @param from
+ * @param to
+ */
checkTypesCompatible(from: TypeclassId, to: TypeclassId): boolean {
// Handle base cases
if (from === to || from === "" || to === "") return true;
diff --git a/tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts b/tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts
new file mode 100644
index 00000000..babfbdbc
--- /dev/null
+++ b/tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts
@@ -0,0 +1,81 @@
+
+import { Typeclass, TypeclassRegistry } from "../../../../../src/electron/lib/registries/TypeclassRegistry";
+import { Blix } from "../../../../../src/electron/lib/Blix";
+import { MediaDisplayType } from "../../../../../src/shared/types";
+
+jest.mock("electron", () => ({
+ app: {
+ getPath: jest.fn((path) => {
+ return "test/electron";
+ }),
+ getName: jest.fn(() => {
+ return "TestElectron";
+ }),
+ getVersion: jest.fn(() => {
+ return "v1.1.1";
+ }),
+ getAppPath: jest.fn(() => {
+ return "test/electron";
+ })
+ },
+ ipcMain: {
+ on: jest.fn()
+ }
+}));
+
+jest.mock('ws', () => {
+ return {
+ WebSocketServer: jest.fn().mockImplementation(() => {
+ return {
+ on: jest.fn()
+ }
+ }
+ )
+ }
+});
+
+jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
+
+describe("Test TypeClassRegistry", () => {
+
+ let typeclassRegistry : TypeclassRegistry;
+ let blix : Blix;
+
+ beforeEach(() => {
+ blix = new Blix();
+ typeclassRegistry = new TypeclassRegistry(blix);
+ });
+
+
+ test("Test constructor", () => {
+ expect(typeclassRegistry).toBeDefined();
+ });
+
+ test("Test addInstance and getRegistry", () => {
+ const invalidType = undefined as unknown as Typeclass;
+ expect(typeclassRegistry.addInstance(invalidType)).toReturn;
+ expect(typeclassRegistry.getTypeclasses().length).toBe(0);
+
+ expect(typeclassRegistry.addInstance({id: "number", description: "test",subtypes: [], mediaDisplayConfig: (data: string) => ({
+ displayType: MediaDisplayType.TextBox,
+ props: {
+ content: data,
+ },
+ contentProp: "content",
+ }),
+ })).toReturn;
+ expect(typeclassRegistry.getTypeclasses().length).toBe(0);
+
+
+ expect(typeclassRegistry.getRegistry()).toEqual(typeclassRegistry["typeclasses"])
+ });
+
+ test("Test converters functionality", () => {
+ typeclassRegistry.addConverter("number", "string", (data: number) => {
+ return data.toString();
+ });
+
+ expect(typeclassRegistry.resolveConversion("number", "string")).toBeDefined(); // else returns null
+ });
+
+});
\ No newline at end of file
From a4299b9cbd2fae55be8e0c2dcfca6127648a317a Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 25 Sep 2023 22:06:56 +0200
Subject: [PATCH 151/209] Clean up exporting of assets
---
blix-plugins/glfx-plugin/src/main.cjs | 4 +-
blix-plugins/threlte-plugin/src/main.cjs | 54 +++++++++---------
src/electron/lib/cache/CacheManager.ts | 33 ++++++++++-
src/frontend/lib/stores/CacheStore.ts | 11 ++++
src/frontend/ui/tiles/Assets.svelte | 72 +++++++++++++++++++-----
src/frontend/ui/tiles/Media.svelte | 8 ++-
src/shared/types/cache.ts | 7 ++-
7 files changed, 141 insertions(+), 48 deletions(-)
diff --git a/blix-plugins/glfx-plugin/src/main.cjs b/blix-plugins/glfx-plugin/src/main.cjs
index f9c1d030..0cd01021 100644
--- a/blix-plugins/glfx-plugin/src/main.cjs
+++ b/blix-plugins/glfx-plugin/src/main.cjs
@@ -165,8 +165,8 @@ const nodes = {
"inputGLFXCache": (context) => {
const nodeBuilder = context.instantiate("Input", "inputGLFXImage");
- nodeBuilder.setTitle("Input GLFX image");
- nodeBuilder.setDescription("Provides an cache input and returns a single image output");
+ nodeBuilder.setTitle("GLFX Image");
+ nodeBuilder.setDescription("Takes a cache object as input and outputs a GLFX image");
nodeBuilder.define(async (input, uiInput, from) => {
return { "res": { src: uiInput["cacheid"] } };
diff --git a/blix-plugins/threlte-plugin/src/main.cjs b/blix-plugins/threlte-plugin/src/main.cjs
index 3f9d1b6e..55f291b3 100644
--- a/blix-plugins/threlte-plugin/src/main.cjs
+++ b/blix-plugins/threlte-plugin/src/main.cjs
@@ -68,33 +68,33 @@ const glfxNodes = {
const nodes = {
// ...glfxNodes,
- "inputGLFXImage": (context) => {
- const nodeBuilder = context.instantiate("Input/Other", "inputGLFXImage");
- nodeBuilder.setTitle("Input GLFX image");
- nodeBuilder.setDescription("Provides an image input and returns a single image output");
-
- nodeBuilder.define(async (input, uiInput, from) => {
- return { "res": { src: uiInput["imagePicker"] } };
- });
-
- const ui = nodeBuilder.createUIBuilder();
- ui.addFilePicker({
- componentId: "imagePicker",
- label: "Pick an image",
- defaultValue: "",
- triggerUpdate: true,
- }, {});
- // ui.addCachePicker({
- // componentId: "cacheid",
- // label: "Pick an image",
- // defaultValue: "",
- // triggerUpdate: true,
- // }, {})
-
- nodeBuilder.setUI(ui);
-
- nodeBuilder.addOutput("GLFX image", "res", "Result");
- },
+ // "inputGLFXImage": (context) => {
+ // const nodeBuilder = context.instantiate("Input/Other", "inputGLFXImage");
+ // nodeBuilder.setTitle("Input GLFX image");
+ // nodeBuilder.setDescription("Provides an image input and returns a single image output");
+
+ // nodeBuilder.define(async (input, uiInput, from) => {
+ // return { "res": { src: uiInput["imagePicker"] } };
+ // });
+
+ // const ui = nodeBuilder.createUIBuilder();
+ // ui.addFilePicker({
+ // componentId: "imagePicker",
+ // label: "Pick an image",
+ // defaultValue: "",
+ // triggerUpdate: true,
+ // }, {});
+ // // ui.addCachePicker({
+ // // componentId: "cacheid",
+ // // label: "Pick an image",
+ // // defaultValue: "",
+ // // triggerUpdate: true,
+ // // }, {})
+
+ // nodeBuilder.setUI(ui);
+
+ // nodeBuilder.addOutput("GLFX image", "res", "Result");
+ // },
}
const commands = {}
diff --git a/src/electron/lib/cache/CacheManager.ts b/src/electron/lib/cache/CacheManager.ts
index dc6cf097..92b962f3 100644
--- a/src/electron/lib/cache/CacheManager.ts
+++ b/src/electron/lib/cache/CacheManager.ts
@@ -15,8 +15,10 @@ import WebSocket, { WebSocketServer } from "ws";
// import { Server } from "socket.io";
import { randomBytes } from "crypto";
import logger from "../../utils/logger";
-import { ipcMain } from "electron";
-import { showOpenDialog } from "../../utils/dialog";
+import { app, ipcMain } from "electron";
+import { showSaveDialog } from "../../utils/dialog";
+import { join } from "path";
+import { writeFile } from "fs/promises";
// The main interface which this manager must expose is:
// - get(cacheUUID: CacheUUID): CacheObject
@@ -92,6 +94,11 @@ export class CacheManager {
this.listeners.add(socket);
socket.send(JSON.stringify(this.cacheUpdateNotification)); // Send initial cache update
break;
+ case "export-cache":
+ if ("ids" in data && Array.isArray(data.ids)) {
+ this.export(data.ids as CacheUUID[]);
+ }
+ break;
default:
logger.info("Unknown cache message type", data.type);
}
@@ -170,4 +177,26 @@ export class CacheManager {
delete(cacheUUID: CacheUUID) {
delete this.cache[cacheUUID];
}
+
+ async export(ids: CacheUUID[]) {
+ for (const id of ids) {
+ const cacheObject = this.cache[id];
+
+ if (!cacheObject) {
+ continue;
+ }
+
+ const path = await showSaveDialog({
+ title: "Save Asset",
+ defaultPath: join(app.getPath("downloads"), cacheObject.metadata.name || "export.png"),
+ properties: ["createDirectory"],
+ });
+
+ if (!path) {
+ continue;
+ }
+
+ await writeFile(path, cacheObject.data);
+ }
+ }
}
diff --git a/src/frontend/lib/stores/CacheStore.ts b/src/frontend/lib/stores/CacheStore.ts
index 9c2aa56d..551e0a1f 100644
--- a/src/frontend/lib/stores/CacheStore.ts
+++ b/src/frontend/lib/stores/CacheStore.ts
@@ -132,6 +132,17 @@ class CacheStore {
});
}
+ public async exportCache(ids: CacheUUID[]): Promise {
+ const messageId = randomMessageId();
+
+ const payload = JSON.stringify({ type: "export-cache", ids, messageId });
+
+ return new Promise((resolve, reject) => {
+ this.lobby[messageId] = resolve;
+ this.ws.send(payload);
+ });
+ }
+
public get subscribe() {
return this.cacheStore.subscribe;
}
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index 463cc134..d9e72769 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -16,6 +16,11 @@
faPizzaSlice,
} from "@fortawesome/free-solid-svg-icons";
import Fa from "svelte-fa";
+ import { toastStore } from "lib/stores/ToastStore";
+
+ let selectedCacheItems: CacheUUID[] = [];
+ // Not used at the moment
+ let controlKeyDown = false;
const noContentIcons = [
faBacon,
@@ -36,6 +41,26 @@
return noContentIcons[Math.floor(Math.random() * noContentIcons.length)];
}
+ function handleKeyDown(event: KeyboardEvent) {
+ if (event.ctrlKey || event.metaKey) {
+ controlKeyDown = true;
+ }
+ }
+
+ function handleItemOnClick(id: CacheUUID) {
+ if (selectedCacheItems.includes(id)) {
+ selectedCacheItems = selectedCacheItems.filter((item) => item !== id);
+ } else {
+ selectedCacheItems = [...selectedCacheItems, id];
+ }
+ }
+
+ function handleKeyUp(event: KeyboardEvent) {
+ if (!event.ctrlKey || !event.metaKey) {
+ controlKeyDown = false;
+ }
+ }
+
async function requestFileAccess() {
try {
const handle = await window.showOpenFilePicker();
@@ -53,15 +78,13 @@
const blobs: { [key: CacheUUID]: { blob: Blob; url: string } } = {};
- async function exportCache(UUID: CacheUUID) {
- const blob = new Blob([await cacheStore.get(UUID)], {
- type: $cacheStore[UUID].contentType,
- });
- const link = document.createElement("a");
- link.download = $cacheStore[UUID].name ?? "export.png";
- link.href = URL.createObjectURL(blob);
- link.click();
- link.remove();
+ async function exportAsset() {
+ if (selectedCacheItems.length === 0) {
+ toastStore.trigger({ message: "No asset selected.", type: "warn" });
+ return;
+ }
+
+ await cacheStore.exportCache(selectedCacheItems);
}
// let barrier = 0;
@@ -83,7 +106,11 @@
{#each Object.keys($cacheStore) as uuid}
{#if ["image/png", "image/jpeg"].includes($cacheStore[uuid].contentType)}
-
+
handleItemOnClick(uuid)}"
+ on:keydown="{null}"
+ >
{#await getBlobURL(uuid, $cacheStore[uuid].contentType) then src}
@@ -93,10 +120,6 @@
{/await}
{$cacheStore[uuid].name ?? "-"}
{$cacheStore[uuid].contentType}
-
Save As
{:else}
@@ -128,9 +151,28 @@
>
Add Asset
+
+ {#if selectedCacheItems.length > 1}
+ Export Assets
+ {:else}
+ Export Asset
+ {/if}
+
+
+
diff --git a/src/frontend/ui/tiles/WebView.svelte b/src/frontend/ui/tiles/WebView.svelte
index 9ce994ba..9d7cca51 100644
--- a/src/frontend/ui/tiles/WebView.svelte
+++ b/src/frontend/ui/tiles/WebView.svelte
@@ -4,6 +4,7 @@
import TextBox from "../../ui/utils/mediaDisplays/TextBox.svelte";
import { type RendererId } from "../../../shared/types/typeclass";
import type { TweakApi } from "../../lib/webview/TweakApi";
+ import { blixStore } from "../../lib/stores/BlixStore";
let webview: Electron.WebviewTag | null = null;
@@ -87,10 +88,12 @@
{#await asyncSrc then src}
{#if src !== null}
-
- Reload
- DevTools
-
+ {#if !$blixStore.production}
+
+ {/if}
From 99f31ff2a70545781ce1556dcbe6a712ac0de51d Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Mon, 25 Sep 2023 23:07:09 +0200
Subject: [PATCH 155/209] Fix glfx load bug
---
blix-plugins/glfx-plugin/webview/App.svelte | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/blix-plugins/glfx-plugin/webview/App.svelte b/blix-plugins/glfx-plugin/webview/App.svelte
index 6359d371..d9105edb 100644
--- a/blix-plugins/glfx-plugin/webview/App.svelte
+++ b/blix-plugins/glfx-plugin/webview/App.svelte
@@ -49,16 +49,22 @@
$: canvasUpdate(canvasWidth);
async function redraw(media) {
+ if(!media.src){
+ // image = null;
+ // texture = null;
+ // canvas.draw(texture).update();
+ var gl = canvas.getContext("webgl");
+
+ // Clear the canvas to a specified color
+ // gl.clearColor(0.0, 0.0, 0.0, 1.0); // This sets the clear color to black
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ return;
+ }
if (media?.ops && canvas && texture) {
if (media.src != lastMediaSrc) {
- if (media.src === "") {
- console.log("here");
- image = null;
- texture = null;
- canvas.draw(texture).update();
- return;
- }
await updateImage(media.src);
+ lastMediaSrc = media.src;
reloadTexture();
}
@@ -79,6 +85,7 @@
else{
if(media.src){
await updateImage(media.src);
+ lastMediaSrc = media.src;
reloadTexture();
const dimRatio = image.height / image.width;
let buffer = canvas.draw(texture, canvasWidth, canvasWidth*dimRatio);
From ec04c8bd6295d285b11edbee693c80dfadc79eef Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 25 Sep 2023 23:19:11 +0200
Subject: [PATCH 156/209] Fix some UI button widths
---
src/frontend/ui/tiles/Assets.svelte | 2 +-
src/frontend/ui/tiles/Media.svelte | 15 +--------------
2 files changed, 2 insertions(+), 15 deletions(-)
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index e6d5d21a..59d8469d 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -110,7 +110,7 @@
}
-
+
{#if Object.keys($cacheStore).length > 0}
{#each Object.keys($cacheStore) as uuid}
diff --git a/src/frontend/ui/tiles/Media.svelte b/src/frontend/ui/tiles/Media.svelte
index a40e7fe6..9bb89ea9 100644
--- a/src/frontend/ui/tiles/Media.svelte
+++ b/src/frontend/ui/tiles/Media.svelte
@@ -98,19 +98,6 @@
}
}
- async function exportCache(e: CustomEvent) {
- const blob = new Blob([await cacheStore.get(e.detail[0].cacheUUID)], {
- type: $cacheStore[e.detail[0].cacheUUID].contentType,
- });
- const link = document.createElement("a");
- link.download = $cacheStore[e.detail[0].cacheUUID].name ?? "export.png";
- link.href = URL.createObjectURL(blob);
- link.click();
- link.remove();
-
- // console.log(await cacheStore.get(e.detail.cacheUUID));
- }
-
function getDisplayProps(media: DisplayableMediaOutput) {
let res = media.display.props;
if (media.display.contentProp !== null) res[media.display.contentProp] ??= media.content; // If content nullish, use default value
@@ -155,7 +142,7 @@
Save Asset
From a3cb5fffcc864ece3dec94f394973af7b3acb585 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Mon, 25 Sep 2023 23:35:55 +0200
Subject: [PATCH 157/209] Fix test
---
blix-plugins/glfx-plugin/src/main.cjs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/blix-plugins/glfx-plugin/src/main.cjs b/blix-plugins/glfx-plugin/src/main.cjs
index bc344cd9..9cbdbba9 100644
--- a/blix-plugins/glfx-plugin/src/main.cjs
+++ b/blix-plugins/glfx-plugin/src/main.cjs
@@ -162,8 +162,8 @@ Object.keys(glfxNodes).forEach((key) => {
const nodes = {
...glfxNodes,
- "inputGLFXCache": (context) => {
- const nodeBuilder = context.instantiate("Input", "inputGLFXImage");
+ "GLFXImage": (context) => {
+ const nodeBuilder = context.instantiate("Input", "GLFXImage");
nodeBuilder.setTitle("GLFX Image");
nodeBuilder.setDescription("Takes a cache object as input and outputs a GLFX image");
From 35f16684cde4c03dbee2a7fac79333a22cb32b02 Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Mon, 25 Sep 2023 23:44:50 +0200
Subject: [PATCH 158/209] Fix Blink deletion of child clump not updating scene
---
blix-plugins/blink/webview/render.ts | 37 +++++++++++++++++++++++-----
1 file changed, 31 insertions(+), 6 deletions(-)
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index 32109121..9c4f349b 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -37,6 +37,8 @@ type SelectionState = {
prevMousePos: PIXI.Point
};
+const MAX_CHILDREN_IN_CLUMP = 5;
+
let selection: SelectionState = null;
let oldSceneStructure = "";
@@ -138,6 +140,7 @@ export async function renderScene(
const { pixiClump, changed } = renderClump(
blink,
+ 0,
canvas.content,
hierarchy?.content,
canvas,
@@ -166,6 +169,7 @@ export function renderCanvas(blink: PIXI.Application, root: Clump) {}
function renderClump(
blink: PIXI.Application,
+ index: number,
clump: Clump,
prevClump: HierarchyClump | undefined,
canvas: BlinkCanvas,
@@ -191,14 +195,23 @@ function renderClump(
const hierarchyElements = [];
if (clump.elements) {
- for (let i = 0; i < clump.elements.length; i++) {
+ for (let i = 0; i < MAX_CHILDREN_IN_CLUMP; i++) {
const child = clump.elements[i];
const prevChild = prevClump?.elements != null && prevClump.elements[i];
- if (child.class === "clump") {
+ if (child == null) {
+ //===== REMOVED CHILD =====//
+ if (prevChild) {
+ console.log("----> DELETED CHILD", i);
+ childChanged = true;
+ children.push({ changed: true, child: null });
+ }
+ }
+ else if (child.class === "clump") {
//===== CHILD CLUMP =====//
const { pixiClump, changed } = renderClump(
blink,
+ i,
child as HierarchyClump,
(prevChild?.class === "clump" ? prevChild : undefined) as HierarchyClump,
canvas,
@@ -253,20 +266,21 @@ function renderClump(
// Create resClump
if (newContainer) {
resClump = new PIXI.Container();
- resClump.name = `Clump(${randomId()})`;
+ resClump.name = `Clump[${index}](${randomId()})`;
// resClump.sortableChildren = true;
addInteractivity(resClump, clump, viewport, send);
} else { resClump = prevClump.container; }
// Add content
- if (resClump.children.length === 0) {
+ const prevContent = findChild(resClump, "content");
+ if (prevContent == null) {
content = new PIXI.Container();
content.name = `content(${randomId()})`;
content.sortableChildren = true;
resClump.addChild(content);
- } else { content = resClump.getChildAt(0) as PIXI.Container; }
+ } else { content = prevContent as PIXI.Container; }
content.visible = true;
console.log(`==> ${newContainer ? "" : "NO "}NEW RESCLUMP (${resClump.name.split("(")[1].slice(0, 4)})`);
@@ -278,6 +292,7 @@ function renderClump(
if (true || newContainer) {
content.removeChildren();
for (let i = 0; i < children.length; i++) {
+ if (children[i].child == null) continue;
children[i].child.zIndex = children.length - i;
console.log("=====> ADD CHILD", i, children[i].child.name);
content.addChild(children[i].child);
@@ -335,7 +350,7 @@ function renderClump(
break;
}
}
- if (appliedFilter) {
+ if (findChild(resClump, "FilterSprite") != null) {
// If we've applied a filter (potentially in a previous step), hide the content
content.visible = false;
}
@@ -371,6 +386,7 @@ function renderClump(
if (diffs.has("mask")) {
const mask = clump.mask != null ? renderClump(
blink,
+ 0,
clump.mask,
undefined,
canvas,
@@ -474,4 +490,13 @@ function addInteractivity(container: PIXI.Container, clump: Clump, viewport: Vie
// To get global mouse position at any point:
// console.log("MOUSE", blink.renderer.plugins.interaction.pointer.global);
+}
+
+function findChild(container: PIXI.Container, namePrefix: string): PIXI.DisplayObject | null {
+ for (let i = 0; i < container.children.length; i++) {
+ if (container.children[i].name.includes(namePrefix)) {
+ return container.children[i];
+ }
+ }
+ return null;
}
\ No newline at end of file
From 69498e20465a5c07d5a31741e41016e0ced7815e Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Tue, 26 Sep 2023 00:35:47 +0200
Subject: [PATCH 159/209] Add canvas configuration to Blink
---
blix-plugins/blink/src/main.cjs | 83 ++++++++++++++++++++++----
blix-plugins/blink/webview/App.svelte | 86 ++++++++++++++++++---------
blix-plugins/blink/webview/diff.ts | 25 ++++++++
blix-plugins/blink/webview/render.ts | 1 +
blix-plugins/blink/webview/types.ts | 9 +++
5 files changed, 165 insertions(+), 39 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 16b1fb77..75ff8ad7 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -763,6 +763,64 @@ const nodes = {
nodeBuilder.addInput("Blink clump", "clump", "Clump");
nodeBuilder.addInput("Blink clump", "mask", "Mask");
nodeBuilder.addOutput("Blink clump", "res", "Result");
+ },
+ "canvasConfig": (context) => {
+ const nodeBuilder = context.instantiate("Blink/Utils", "canvasConfig");
+ nodeBuilder.setTitle("Canvas Config");
+ nodeBuilder.setDescription("Configure the Blink canvas");
+
+ const ui = nodeBuilder.createUIBuilder();
+ ui.addTextInput({
+ componentId: "exportName",
+ label: "Canvas Export Name",
+ defaultValue: "Blink Export",
+ triggerUpdate: true,
+ }, {});
+
+ ui.addNumberInput(
+ {
+ componentId: "canvasW",
+ label: "Canvas Width",
+ defaultValue: 1920,
+ triggerUpdate: true,
+ },
+ { step: 1 }
+ );
+ ui.addNumberInput(
+ {
+ componentId: "canvasH",
+ label: "Canvas Height",
+ defaultValue: 1080,
+ triggerUpdate: true,
+ },
+ { step: 1 }
+ );
+ ui.addColorPicker({
+ componentId: "canvasColor",
+ label: "Canvas Background",
+ defaultValue: "#ffffffff",
+ triggerUpdate: true,
+ }, {})
+
+ nodeBuilder.define(async (input, uiInput, from) => {
+ // Apply mask to outermost clump
+ const canvas = {
+ ...input["clump"],
+ config: {
+ canvasDims: { w: uiInput["canvasW"], h: uiInput["canvasH"] },
+ canvasColor: colorHexToNumber(uiInput["canvasColor"]),
+ canvasAlpha: colorHexToAlpha(uiInput["canvasColor"]),
+
+ exportName: uiInput["exportName"]
+ }
+ }
+
+ return { "canvas": canvas };
+ });
+
+ nodeBuilder.setUI(ui);
+ nodeBuilder.addInput("Blink clump", "clump", "Clump");
+ nodeBuilder.addOutput("Blink canvas", "canvas", "Canvas");
}
};
const commands = {};
@@ -770,15 +828,7 @@ const tiles = {};
function init(context) {
- const glfxTypeBuilder = context.createTypeclassBuilder("Blink clump");
- // glfxTypeBuilder.setToConverters({
- // "image": (value) => ({})
- // });
- // glfxTypeBuilder.setFromConverters({
- // "image": (value) => ({})
- // });
-
- glfxTypeBuilder.setDisplayConfigurator((data) => {
+ const configurator = (data) => {
return {
displayType: "webview",
props: {
@@ -787,7 +837,20 @@ function init(context) {
},
contentProp: "media"
};
- });
+ }
+
+ const clumpTypeBuilder = context.createTypeclassBuilder("Blink clump");
+ clumpTypeBuilder.setDisplayConfigurator(configurator);
+
+ // clumpTypeBuilder.setToConverters({
+ // "image": (value) => ({})
+ // });
+ // clumpTypeBuilder.setFromConverters({
+ // "image": (value) => ({})
+ // });
+
+ const canvasTypeBuilder = context.createTypeclassBuilder("Blink canvas");
+ canvasTypeBuilder.setDisplayConfigurator(configurator);
}
module.exports = {
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index 9d55072c..41161665 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -4,15 +4,21 @@
import { onDestroy, onMount, tick } from "svelte";
import { type Writable } from "svelte/store";
import { renderScene } from "./render";
- import { WindowWithApis, type BlinkCanvas } from "./types";
+ import { WindowWithApis, type BlinkCanvas, BlinkCanvasConfig } from "./types";
import Debug from "./Debug.svelte";
+ import { diffCanvasConfig } from "./diff";
export let media: Writable;
export let send: (msg: string, data: any) => void;
let imgCanvasInitialPadding = 100;
- let imgCanvasBlockW = 1920;
- let imgCanvasBlockH = 1080;
+ let canvasConfig: BlinkCanvasConfig = {
+ canvasDims: { w: 1920, h: 1080 },
+ canvasColor: 0xffffff,
+ canvasAlpha: 1,
+
+ exportName: "Blink Export"
+ }
let blink: PIXI.Application;
let pixiCanvas: HTMLCanvasElement;
@@ -86,45 +92,65 @@
});
//===== CREATE BASE LAYOUT =====//
- imgCanvasInitialPadding = 100;
- imgCanvasBlockW = 1920;
- imgCanvasBlockH = 1080;
-
let imgCanvas = new PIXI.Container();
imgCanvas.name = "imgCanvas";
- // canvas
- let imgCanvasBlock = new PIXI.Graphics();
- imgCanvasBlock.beginFill(0xffffff, 0.9);
- imgCanvasBlock.drawRect(0, 0, imgCanvasBlockW, imgCanvasBlockH);
+ function createImageCanvasBlock() {
+ const { w: imgCanvasBlockW, h: imgCanvasBlockH } = canvasConfig.canvasDims;
- // x-axis
- imgCanvasBlock.lineStyle();
- imgCanvasBlock.moveTo(0, imgCanvasBlockH/2);
- imgCanvasBlock.lineStyle(1, 0xff0000);
- imgCanvasBlock.lineTo(imgCanvasBlockW, imgCanvasBlockH/2);
+ // canvas
+ let imgCanvasBlock = new PIXI.Graphics();
+ imgCanvasBlock.beginFill(canvasConfig.canvasColor, canvasConfig.canvasAlpha);
+ imgCanvasBlock.drawRect(0, 0, imgCanvasBlockW, imgCanvasBlockH);
- // y-axis
- imgCanvasBlock.lineStyle();
- imgCanvasBlock.moveTo(imgCanvasBlockW/2, 0);
- imgCanvasBlock.lineStyle(1, 0x00ff00);
- imgCanvasBlock.lineTo(imgCanvasBlockW/2, imgCanvasBlockH);
+ // x-axis
+ imgCanvasBlock.lineStyle();
+ imgCanvasBlock.moveTo(0, imgCanvasBlockH/2);
+ imgCanvasBlock.lineStyle(1, 0xff0000);
+ imgCanvasBlock.lineTo(imgCanvasBlockW, imgCanvasBlockH/2);
- imgCanvasBlock.position.set(-imgCanvasBlockW/2, -imgCanvasBlockH/2);
- imgCanvas.addChild(imgCanvasBlock);
+ // y-axis
+ imgCanvasBlock.lineStyle();
+ imgCanvasBlock.moveTo(imgCanvasBlockW/2, 0);
+ imgCanvasBlock.lineStyle(1, 0x00ff00);
+ imgCanvasBlock.lineTo(imgCanvasBlockW/2, imgCanvasBlockH);
+
+ imgCanvasBlock.position.set(-imgCanvasBlockW/2, -imgCanvasBlockH/2);
+ return imgCanvasBlock;
+ }
+
+ const imgCanvasBlock = createImageCanvasBlock();
+ imgCanvas.addChild(imgCanvasBlock);
canvasBlock = imgCanvasBlock;
viewport.addChild(imgCanvas);
- const viewportFitX = imgCanvasBlockW + 2 * imgCanvasInitialPadding;
- const viewportFitY = imgCanvasBlockH + 2 * imgCanvasInitialPadding;
+ const viewportFitX = canvasConfig.canvasDims.w + 2 * imgCanvasInitialPadding;
+ const viewportFitY = canvasConfig.canvasDims.h + 2 * imgCanvasInitialPadding;
viewport.fit(true, viewportFitX, viewportFitY);
- viewport.moveCenter(imgCanvasBlockW/2, imgCanvasBlockH/2);
+ viewport.moveCenter(canvasConfig.canvasDims.w/2, canvasConfig.canvasDims.h/2);
//===== RENDER Blink =====//
let hasCentered = false;
media.subscribe(async (media) => {
+ //===== UPDATE CANVAS CONFIG =====//
+ if (media?.config != null) {
+ const configDiffs = diffCanvasConfig(canvasConfig, media.config);
+
+ if (configDiffs.size > 0) {
+ canvasConfig = media.config;
+
+ if (configDiffs.has("canvasBlock")) {
+ imgCanvas.removeChildren();
+ const newImgCanvasBlock = createImageCanvasBlock();
+ imgCanvas.addChild(newImgCanvasBlock);
+ canvasBlock = newImgCanvasBlock;
+ }
+ }
+ }
+
+ //===== RENDER SCENE =====//
const { success, scene } = await renderScene(blink, media, viewport, send);
currScene = scene;
@@ -133,8 +159,8 @@
window.dispatchEvent(new Event("resize"));
if (success && !hasCentered) {
- const viewportFitX = imgCanvasBlockW + 2 * imgCanvasInitialPadding;
- const viewportFitY = imgCanvasBlockH + 2 * imgCanvasInitialPadding;
+ const viewportFitX = canvasConfig.canvasDims.w + 2 * imgCanvasInitialPadding;
+ const viewportFitY = canvasConfig.canvasDims.h + 2 * imgCanvasInitialPadding;
viewport.fit(true, viewportFitX, viewportFitY);
viewport.moveCenter(0, 0);
@@ -169,6 +195,8 @@
async function exportImage() {
if (!currScene || !canvasBlock) return;
+ const { w: imgCanvasBlockW, h: imgCanvasBlockH } = canvasConfig.canvasDims;
+
const bounds = currScene.getLocalBounds();
const frame = new PIXI.Rectangle(-bounds.x-imgCanvasBlockW/2, -bounds.y-imgCanvasBlockH/2, imgCanvasBlockW, imgCanvasBlockH);
@@ -176,7 +204,7 @@
exportCanvas.toBlob((blob) => {
const metadata = {
contentType: "image/png",
- name: `Blink Export ${Math.floor(100000 * Math.random())}`
+ name: `${canvasConfig.exportName} ${Math.floor(100000 * Math.random())}`
};
(window as WindowWithApis).cache.write(blob, metadata);
}, "image/png");
diff --git a/blix-plugins/blink/webview/diff.ts b/blix-plugins/blink/webview/diff.ts
index 9f55bcd8..75a504e5 100644
--- a/blix-plugins/blink/webview/diff.ts
+++ b/blix-plugins/blink/webview/diff.ts
@@ -2,6 +2,7 @@ import type { HierarchyAtom, HierarchyClump } from "./render";
import type {
Asset,
Atom,
+ BlinkCanvasConfig,
Clump,
CurveAtom,
Filter,
@@ -147,3 +148,27 @@ export function diffPaintAtom(a1: PaintAtom, a2: PaintAtom) {
if (a1.uuid !== a2.uuid) return true;
return false;
}
+
+export type CanvasConfigDiff = "canvasBlock" | "exportName";
+
+export function diffCanvasConfig(c1: BlinkCanvasConfig, c2: BlinkCanvasConfig) {
+ const diffs = new Set();
+ if (c1 == null && c2 == null) return new Set([]); // Vacuous case
+ if (c1 == null || c2 == null)
+ return new Set(["canvasBlock", "exportName"]);
+
+ if (
+ c1.canvasDims.w !== c2.canvasDims.w ||
+ c1.canvasDims.h !== c2.canvasDims.h ||
+ c1.canvasColor !== c2.canvasColor ||
+ c1.canvasAlpha !== c2.canvasAlpha
+ ) {
+ diffs.add("canvasBlock");
+ }
+
+ if (c1.exportName !== c2.exportName) {
+ diffs.add("exportName");
+ }
+
+ return diffs;
+}
\ No newline at end of file
diff --git a/blix-plugins/blink/webview/render.ts b/blix-plugins/blink/webview/render.ts
index 9c4f349b..547644ca 100644
--- a/blix-plugins/blink/webview/render.ts
+++ b/blix-plugins/blink/webview/render.ts
@@ -152,6 +152,7 @@ export async function renderScene(
// Update hierarchy
hierarchy = {
assets: canvas.assets,
+ config: undefined,
content: {
...canvas.content,
container: pixiClump,
diff --git a/blix-plugins/blink/webview/types.ts b/blix-plugins/blink/webview/types.ts
index 3bda5658..065217e8 100644
--- a/blix-plugins/blink/webview/types.ts
+++ b/blix-plugins/blink/webview/types.ts
@@ -30,9 +30,18 @@ export type WindowWithApis = Window & typeof globalThis & {
export type BlinkCanvas = {
assets: { [key: string]: Asset };
+ config?: BlinkCanvasConfig;
content: Clump | null;
}
+export type BlinkCanvasConfig = {
+ canvasDims: { w: number, h: number };
+ canvasColor: number;
+ canvasAlpha: number;
+
+ exportName: string;
+};
+
export type Asset = { class: "asset" } & (ImageAsset | CurveAsset);
export type ImageAsset = {
From fb8687357701d37fb05851db2397bbaf6cb11dea Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Tue, 26 Sep 2023 10:27:23 +0200
Subject: [PATCH 160/209] TypeclassRegistry tests done
---
.../lib/registries/TypeclassRegistry.ts | 4 +
.../lib/registries/TypeClassRegistry.spec.ts | 102 +++++++++++++++++-
2 files changed, 104 insertions(+), 2 deletions(-)
diff --git a/src/electron/lib/registries/TypeclassRegistry.ts b/src/electron/lib/registries/TypeclassRegistry.ts
index 15d35b01..3c799581 100644
--- a/src/electron/lib/registries/TypeclassRegistry.ts
+++ b/src/electron/lib/registries/TypeclassRegistry.ts
@@ -138,6 +138,10 @@ export class TypeclassRegistry implements Registry {
return res;
}
+ /**
+ * This function currently returns an empty array : TODO
+ * @returns All typeclasses in the registry. Currently empty
+ */
getTypeclasses(): ICommand[] {
const commands: ICommand[] = [];
// for (const key in this.registry) {
diff --git a/tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts b/tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts
index babfbdbc..dd9fb3b2 100644
--- a/tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts
+++ b/tests/unit-tests/electron/lib/registries/TypeClassRegistry.spec.ts
@@ -1,7 +1,9 @@
import { Typeclass, TypeclassRegistry } from "../../../../../src/electron/lib/registries/TypeclassRegistry";
import { Blix } from "../../../../../src/electron/lib/Blix";
-import { MediaDisplayType } from "../../../../../src/shared/types";
+import { MediaDisplayType,MediaOutput,DisplayableMediaOutput } from "../../../../../src/shared/types";
+import { type } from "os";
+import exp from "constants";
jest.mock("electron", () => ({
app: {
@@ -71,11 +73,107 @@ describe("Test TypeClassRegistry", () => {
});
test("Test converters functionality", () => {
+ typeclassRegistry["converters"] = {};
+
typeclassRegistry.addConverter("number", "string", (data: number) => {
return data.toString();
});
expect(typeclassRegistry.resolveConversion("number", "string")).toBeDefined(); // else returns null
+
+ typeclassRegistry.addConverter("boolean", "number", (data: boolean) => {
+ return data ? 1 : 0;
+ });
+
+ console.log(typeclassRegistry.resolveConversion("boolean", "string")!(true))
+ expect(typeclassRegistry.resolveConversion("boolean", "string")).toBeDefined(); // else returns null
+
+
+ expect(typeclassRegistry.resolveConversion("number", "a",1)).toBe(null);
+
+ expect(typeclassRegistry.checkTypesCompatible("number", "string")).toBe(true);
+
+ expect(typeclassRegistry.checkTypesCompatible("number", "number")).toBe(true);
+
+ expect(typeclassRegistry.checkTypesCompatible("number", "a")).toBe(false);
+
+ expect(typeclassRegistry.getTypeclasses()).toEqual([]);
+ });
+
+ it("Test displayable Media Output", () => {
+
+ console.log(typeclassRegistry["typeclasses"]["string"]);
+ const output : MediaOutput = {
+ outputId: "00000",
+ outputNodeUUID: "12345",
+ graphUUID: "67890",
+ content: "test",
+ dataType: "string"
+ }
+
+ const value = typeclassRegistry["typeclasses"]["string"];
+
+ const expected = value.mediaDisplayConfig(output.content);
+
+ const displayableMediaOutput : DisplayableMediaOutput = {
+ ...output,
+ display: expected,
+ }
+
+ expect(typeclassRegistry.getDisplayableMedia(output)).toBeDefined();
+ expect(typeof typeclassRegistry.getDisplayableMedia(output)).toBe('object');
+ expect(typeclassRegistry.getDisplayableMedia(output)).toEqual(displayableMediaOutput);
+
+ output.dataType = "unregisteredType";
+
+ const newDisplayableMediaOutput : DisplayableMediaOutput = {
+ ...output,
+ display: {
+ displayType: MediaDisplayType.TextBox,
+ props: {
+ content: `INVALID TYPE: ${output.dataType}\nCONTENT: ${JSON.stringify(output.content)}`,
+ status: "error",
+ },
+ contentProp: null,
+ },
+ }
+
+ expect(typeclassRegistry.getDisplayableMedia(output)).toBeDefined();
+ expect(typeof typeclassRegistry.getDisplayableMedia(output)).toBe('object');
+ expect(typeclassRegistry.getDisplayableMedia(output)).toEqual(newDisplayableMediaOutput);
});
-});
\ No newline at end of file
+
+ it("Test renderer", () => {
+ // console.log(typeclassRegistry["renderers"])
+ expect(typeclassRegistry["renderers"]).toEqual({});
+
+ typeclassRegistry.addRenderer(`${'Hello'}/${'Bruh'}`, "test");
+ expect(typeclassRegistry["renderers"]).toEqual({[`${'Hello'}/${'Bruh'}`]: "test"});
+
+
+ typeclassRegistry.addRenderer(`${'Ano'}/${'Second'}`, "test2");
+ expect(typeclassRegistry.getRendererSrc(`${'Ano'}/${'Second'}`)).toEqual("test2");
+ });
+
+
+ it("Test typeclassses ",() => { // they all return the same object with different prop
+ expect(typeof typeclassRegistry["typeclasses"][""].mediaDisplayConfig("")).toBe('object');
+ expect(typeof typeclassRegistry["typeclasses"]["number"].mediaDisplayConfig("")).toBe('object');
+ expect(typeof typeclassRegistry["typeclasses"]["string"].mediaDisplayConfig("")).toBe('object');
+ expect(typeof typeclassRegistry["typeclasses"]["boolean"].mediaDisplayConfig("")).toBe('object');
+ expect(typeof typeclassRegistry["typeclasses"]["color"].mediaDisplayConfig("")).toBe('object');
+ expect(typeof typeclassRegistry["typeclasses"]["image"].mediaDisplayConfig("")).toBe('object');
+ expect(typeof typeclassRegistry["typeclasses"]["error"].mediaDisplayConfig("")).toBe('object');
+ })
+
+ it("Test base converters", () => {
+ expect(typeclassRegistry["converters"].number.string(1)).toBe("1")
+ expect(typeclassRegistry["converters"].string.number(1)).toBe(1)
+ expect(typeclassRegistry["converters"].boolean.string(true)).toBe("true")
+ expect(typeclassRegistry["converters"].string.boolean("true")).toBe(true)
+ expect(typeclassRegistry["converters"].number.boolean(1)).toBe(true)
+
+ })
+
+})
\ No newline at end of file
From 21fc4594abe7c95ab145b3cec95f84962dca7649 Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Tue, 26 Sep 2023 10:59:19 +0200
Subject: [PATCH 161/209] Fix Camera Tile
---
src/frontend/ui/tiles/WebCamera.svelte | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/frontend/ui/tiles/WebCamera.svelte b/src/frontend/ui/tiles/WebCamera.svelte
index 186e76a6..08359e06 100644
--- a/src/frontend/ui/tiles/WebCamera.svelte
+++ b/src/frontend/ui/tiles/WebCamera.svelte
@@ -35,11 +35,29 @@
function takePicture() {
capture
- .takePhoto()
- .then((blob: Blob) => {
+ .grabFrame()
+ .then((imageBitmap: ImageBitmap) => {
// ws.send(blob);
- pictureUrl = URL.createObjectURL(blob);
- cacheStore.refreshStore(pictureUrl);
+ console.log(imageBitmap);
+ // cacheStore.addCacheObject(blob, {contentType: "image/png", name: `Webcam Capture ${Math.floor(100000 * Math.random())}`})
+ // pictureUrl = URL.createObjectURL(blob);
+ // cacheStore.refreshStore(pictureUrl);
+ const canvas = document.createElement("canvas");
+ const context = canvas.getContext("2d");
+
+ canvas.width = imageBitmap.width;
+ canvas.height = imageBitmap.height;
+
+ context?.drawImage(imageBitmap, 0, 0);
+
+ canvas.toBlob((blob) => {
+ // const blobUrl = URL.createObjectURL(blob);
+ if (blob)
+ cacheStore.addCacheObject(blob, {
+ contentType: "image/png",
+ name: `Webcam Capture ${Math.floor(100000 * Math.random())}`,
+ });
+ }, "image/png"); // You can specify the MIME type here
})
.catch((err: string) => console.log("Error while taking photo ", err));
}
From 62c1c7c85f37cc336ab08af4618a5e6a81194394 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Tue, 26 Sep 2023 11:03:27 +0200
Subject: [PATCH 162/209] Initial TypeClassBuilder tests
---
.../lib/plugins/builders/TypeclassBuilder.ts | 29 ++++++++++++++++
.../plugins/builder/TypeClassBuilder.spec.ts | 34 +++++++++++++++++++
2 files changed, 63 insertions(+)
create mode 100644 tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts
diff --git a/src/electron/lib/plugins/builders/TypeclassBuilder.ts b/src/electron/lib/plugins/builders/TypeclassBuilder.ts
index 3ca6d958..7022e432 100644
--- a/src/electron/lib/plugins/builders/TypeclassBuilder.ts
+++ b/src/electron/lib/plugins/builders/TypeclassBuilder.ts
@@ -18,6 +18,9 @@ type PartialTypeclass = {
toConverters: [TypeclassId, TypeConverter][];
};
+/**
+ * Builder class for creating Typeclasses through Builder pattern
+ */
export class TypeclassBuilder implements PluginContextBuilder {
private partial: PartialTypeclass;
@@ -37,26 +40,45 @@ export class TypeclassBuilder implements PluginContextBuilder {
};
}
+ /**
+ * Sets the description of the partial typeclass
+ * @param description
+ */
setDescription(description: string) {
this.partial.description = description;
}
+ /**
+ * Sets the From converters of the partial typeclass
+ * @param converters
+ */
setFromConverters(converters: { [key: string]: (fromType: string) => any }) {
Object.keys(converters).forEach((fromTypeId) => {
this.partial.fromConverters.push([fromTypeId, converters[fromTypeId]]);
});
}
+ /**
+ * Set the To converters of the partial typeclass
+ * @param converters
+ */
setToConverters(converters: { [key: string]: (toType: string) => any }) {
Object.keys(converters).forEach((toTypeId) => {
this.partial.toConverters.push([toTypeId, converters[toTypeId]]);
});
}
+ /**
+ * Set the mediaDisplayConfigurator of the partial typeclass
+ * @param configurator
+ */
setDisplayConfigurator(configurator: (data: string) => MediaDisplayConfig) {
this.partial.mediaDisplayConfigurator = configurator;
}
+ /**
+ * Returns the current build of the partial typeclass
+ */
private get buildTypeclass(): Typeclass {
return {
id: this.partial.id,
@@ -66,6 +88,10 @@ export class TypeclassBuilder implements PluginContextBuilder {
};
}
+ /**
+ * Returns the from and to converters of the current partial typeclass
+ * @returns List of converter Triples
+ */
private get buildConverters(): ConverterTriple[] {
return [
...this.partial.fromConverters.map(
@@ -77,6 +103,9 @@ export class TypeclassBuilder implements PluginContextBuilder {
];
}
+ /**
+ * @returns the current build of the partial typeclass and its converters
+ */
get build(): [Typeclass, ConverterTriple[]] {
return [this.buildTypeclass, this.buildConverters];
}
diff --git a/tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts b/tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts
new file mode 100644
index 00000000..de989b1c
--- /dev/null
+++ b/tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts
@@ -0,0 +1,34 @@
+import { type } from "os";
+import {TypeclassBuilder} from "../../../../../../src/electron/lib/plugins/builders/TypeclassBuilder"
+
+
+describe("TypeClassBuilder", () => {
+ let typeclassBuilder : TypeclassBuilder;
+
+
+ beforeEach(() => {
+ typeclassBuilder = new TypeclassBuilder("test", "number");
+ });
+
+
+ it("Test constructor and getters and setters", () => {
+ expect(typeclassBuilder).not.toBeNull();
+ expect(typeclassBuilder["partial"].id).toBe("number");
+ expect(typeof typeclassBuilder["partial"].mediaDisplayConfigurator("a")).toBe("object");
+
+ typeclassBuilder.setDescription("test description");
+ expect(typeclassBuilder["partial"].description).toBe("test description");
+
+ const fn = (fromType: string) => {return "test";}
+ typeclassBuilder.setFromConverters({"test1": fn});
+ expect(typeclassBuilder["partial"].fromConverters).toEqual([["test1", fn]]);
+
+ const fn1 = (toType: string) => {return "test";}
+ typeclassBuilder.setToConverters({"test2": fn1});
+ expect(typeclassBuilder["partial"].toConverters).toEqual([["test2", fn1]]);
+
+ })
+
+
+
+});
\ No newline at end of file
From fa659f3b00df759e895786ef5ecf61bcb0ea6551 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Tue, 26 Sep 2023 11:19:10 +0200
Subject: [PATCH 163/209] TypeClassBuilder finished
---
.../plugins/builder/TypeClassBuilder.spec.ts | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts b/tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts
index de989b1c..473ec699 100644
--- a/tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts
+++ b/tests/unit-tests/electron/lib/plugins/builder/TypeClassBuilder.spec.ts
@@ -1,5 +1,8 @@
import { type } from "os";
import {TypeclassBuilder} from "../../../../../../src/electron/lib/plugins/builders/TypeclassBuilder"
+import { MediaDisplayConfig, MediaDisplayType } from "../../../../../../src/shared/types";
+import { Typeclass } from "../../../../../../src/electron/lib/registries/TypeclassRegistry";
+import type { ConverterTriple } from "../../../../../../src/electron/lib/registries/TypeclassRegistry";
describe("TypeClassBuilder", () => {
@@ -27,6 +30,31 @@ describe("TypeClassBuilder", () => {
typeclassBuilder.setToConverters({"test2": fn1});
expect(typeclassBuilder["partial"].toConverters).toEqual([["test2", fn1]]);
+
+ const configurator: (data: string) => MediaDisplayConfig = (data: string) => {
+ return {
+ displayType: MediaDisplayType.TextBox,
+ props: { content: "Invalid typeclass: Media display not specified" },
+ contentProp: null,
+ } as MediaDisplayConfig;
+ }
+
+ typeclassBuilder.setDisplayConfigurator(configurator);
+ expect(typeclassBuilder["partial"].mediaDisplayConfigurator("a")).toEqual(configurator("a"));
+
+ const build : Typeclass = {
+ id: "number",
+ description: "test description",
+ subtypes: [],
+ mediaDisplayConfig: configurator,
+ }
+
+ expect(typeclassBuilder["buildTypeclass"]).toEqual(build);
+
+ const converters : ConverterTriple[] = [["test1",typeclassBuilder["partial"].id, fn], [typeclassBuilder["partial"].id,"test2", fn1]];
+ expect(typeclassBuilder["buildConverters"]).toEqual(converters);
+
+ expect(typeclassBuilder["build"]).toEqual([build,converters]);
})
From 6f2afdbd6119e8917473c29217e53c365b66ee97 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 12:49:57 +0200
Subject: [PATCH 164/209] Clean up camera UI
---
src/electron/lib/cache/CacheManager.ts | 9 +++-
.../ui/base/layout/TileSelector.svelte | 10 +++-
src/frontend/ui/tiles/WebCamera.svelte | 53 ++++++++++++-------
3 files changed, 50 insertions(+), 22 deletions(-)
diff --git a/src/electron/lib/cache/CacheManager.ts b/src/electron/lib/cache/CacheManager.ts
index 7e98748a..6c025bb8 100644
--- a/src/electron/lib/cache/CacheManager.ts
+++ b/src/electron/lib/cache/CacheManager.ts
@@ -192,9 +192,16 @@ export class CacheManager {
continue;
}
+ const fileType = cacheObject.metadata.contentType.split("/")[1];
+ let fileName = cacheObject.metadata.name || "export";
+
+ if (!fileName.endsWith(`.${fileType}`)) {
+ fileName = `${fileName}.${fileType}`;
+ }
+
const path = await showSaveDialog({
title: "Save Asset",
- defaultPath: join(app.getPath("downloads"), cacheObject.metadata.name || "export.png"),
+ defaultPath: join(app.getPath("downloads"), fileName),
properties: ["createDirectory"],
});
diff --git a/src/frontend/ui/base/layout/TileSelector.svelte b/src/frontend/ui/base/layout/TileSelector.svelte
index e57a4b15..da565f28 100644
--- a/src/frontend/ui/base/layout/TileSelector.svelte
+++ b/src/frontend/ui/base/layout/TileSelector.svelte
@@ -56,6 +56,12 @@
};
console.log("Icon", tiles[tile].icon);
}
+
+ function toTitleCase(str: string) {
+ return str.replace(/\w\S*/g, (txt) => {
+ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
+ });
+ }
@@ -74,7 +80,9 @@
on:keydown="{null}"
>
- {tileDict[to].displayName}
+ {toTitleCase(
+ tileDict[to].displayName
+ )}
{/each}
diff --git a/src/frontend/ui/tiles/WebCamera.svelte b/src/frontend/ui/tiles/WebCamera.svelte
index 08359e06..f9615609 100644
--- a/src/frontend/ui/tiles/WebCamera.svelte
+++ b/src/frontend/ui/tiles/WebCamera.svelte
@@ -1,11 +1,12 @@
-
+
+
+
{#if loading}
-
LOADING
+
Loading...
+ {:else}
+
{/if}
-
-
-
CAPTURE
-
-
From 1c52e133337e4317c9b36fe42c25a97134a6761c Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Tue, 26 Sep 2023 13:21:24 +0200
Subject: [PATCH 165/209] Add blink nodes
---
blix-plugins/blink/src/main.cjs | 27 +++++++++++++++++++++-
blix-plugins/blink/webview/types.ts | 35 +++++++++++++++++++++++++----
2 files changed, 57 insertions(+), 5 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 57d97d1a..7bdf1147 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -204,7 +204,32 @@ const blinkNodes = {
{ id: "center.y", min: 0, max: 2000, step: 1.0 },
]
],
-
+ "brightnessContrast": [
+ "Brightness / Contrast",
+ "Adjusts the brightness and contrast of the image.",
+ [
+ { id: "brightness", min: 0, max: 10, step: 0.1 },
+ { id: "contrast", min: 0, max: 10, step: 0.1 },
+ ]
+ ],
+ "saturationGamma": [
+ "Saturation / Gamma",
+ "Adjusts the saturation and gamma of the image.",
+ [
+ { id: "saturation", min: 0, max: 10, step: 0.1 },
+ { id: "gamma", min: 0, max: 10, step: 0.1 },
+ ]
+ ],
+ "colorChannel": [
+ "Color-channel",
+ "Shifts the color channel and alpha of the image.",
+ [
+ { id: "red", min: 0, max: 10, step: 0.1 },
+ { id: "green", min: 0, max: 10, step: 0.1 },
+ { id: "blue", min: 0, max: 10, step: 0.1 },
+ { id: "alpha", min: 0, max: 10, step: 0.1 },
+ ]
+ ],
};
Object.keys(blinkNodes).forEach((key) => {
diff --git a/blix-plugins/blink/webview/types.ts b/blix-plugins/blink/webview/types.ts
index ffcfddee..450bbcd7 100644
--- a/blix-plugins/blink/webview/types.ts
+++ b/blix-plugins/blink/webview/types.ts
@@ -11,7 +11,8 @@ import {
BulgePinchFilter,
GlitchFilter,
ZoomBlurFilter,
- TwistFilter
+ TwistFilter,
+ AdjustmentFilter
} from "pixi-filters"
export type BlinkCanvas = {
@@ -43,7 +44,7 @@ export type Transform = {
export type Filter = {
class: "filter";
- type: "blur" | "noise" | "bloom" | "grayscale" | "bevel" | "outline" | "dot" | "crt" | "emboss" | "bulge" | "glitch" | "zoomblur" | "twist";
+ type: "blur" | "noise" | "bloom" | "grayscale" | "bevel" | "outline" | "dot" | "crt" | "emboss" | "bulge" | "glitch" | "zoomblur" | "twist" | "brightnessContrast" | "saturationGamma" | "colorChannel";
params: any[]
};
@@ -84,7 +85,7 @@ export function getPixiFilter(filter: Filter) {
{
radius: filter.params[0],
strength: filter.params[1],
- center: [filter.params[2], filter.params[3]],
+ center: new PIXI.Point(filter.params[2], filter.params[3]),
}
);
case "glitch": return new GlitchFilter(...filter.params);
@@ -95,7 +96,33 @@ export function getPixiFilter(filter: Filter) {
center: [filter.params[2], filter.params[3]],
}
);
- case "twist": return new TwistFilter(...filter.params);
+ case "twist": return new TwistFilter(
+ {
+ angle: filter.params[0],
+ radius: filter.params[1],
+ offset: new PIXI.Point(filter.params[2], filter.params[3]),
+ }
+ );
+ case "brightnessContrast": return new AdjustmentFilter(
+ {
+ brightness: filter.params[0],
+ contrast: filter.params[1],
+ }
+ );
+ case "saturationGamma": return new AdjustmentFilter(
+ {
+ saturation: filter.params[0],
+ gamma: filter.params[1],
+ }
+ );
+ case "colorChannel": return new AdjustmentFilter(
+ {
+ red: filter.params[0],
+ green: filter.params[1],
+ blue: filter.params[2],
+ alpha: filter.params[3],
+ }
+ );
}
} catch {
return new PIXI.Filter();
From f831756cd6a871b221f23b0e06c99e356d4c10ae Mon Sep 17 00:00:00 2001
From: Rec1dite
Date: Tue, 26 Sep 2023 14:03:58 +0200
Subject: [PATCH 166/209] Add label to ColorPicker, specialize filter padding
---
blix-plugins/blink/src/main.cjs | 2 +-
blix-plugins/blink/webview/App.svelte | 6 ----
blix-plugins/blink/webview/filter.ts | 4 ++-
.../graph/nodeUIComponents/ColorPicker.svelte | 28 +++++++++++++------
4 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 75ff8ad7..42473366 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -772,7 +772,7 @@ const nodes = {
const ui = nodeBuilder.createUIBuilder();
ui.addTextInput({
componentId: "exportName",
- label: "Canvas Export Name",
+ label: "Export Name",
defaultValue: "Blink Export",
triggerUpdate: true,
}, {});
diff --git a/blix-plugins/blink/webview/App.svelte b/blix-plugins/blink/webview/App.svelte
index 41161665..2c9940e2 100644
--- a/blix-plugins/blink/webview/App.svelte
+++ b/blix-plugins/blink/webview/App.svelte
@@ -36,7 +36,6 @@
width: 500,
height: 500,
backgroundColor: "#181925",
- // transparent: true,
antialias: true,
resolution: 1,
autoDensity: true,
@@ -126,11 +125,6 @@
viewport.addChild(imgCanvas);
- const viewportFitX = canvasConfig.canvasDims.w + 2 * imgCanvasInitialPadding;
- const viewportFitY = canvasConfig.canvasDims.h + 2 * imgCanvasInitialPadding;
- viewport.fit(true, viewportFitX, viewportFitY);
- viewport.moveCenter(canvasConfig.canvasDims.w/2, canvasConfig.canvasDims.h/2);
-
//===== RENDER Blink =====//
let hasCentered = false;
media.subscribe(async (media) => {
diff --git a/blix-plugins/blink/webview/filter.ts b/blix-plugins/blink/webview/filter.ts
index dd41b70f..e8fbe7e1 100644
--- a/blix-plugins/blink/webview/filter.ts
+++ b/blix-plugins/blink/webview/filter.ts
@@ -2,12 +2,14 @@ import * as PIXI from 'pixi.js';
import { type Filter, getPixiFilter } from './types';
import { randomId } from './render';
+const paddedFilters = new Set(["blur", "outline", "twist"]);
+
// Apply a series of filters and flatten the result to a sprite.
// This is done so that the filters are applied evenly regardless of scaling.
export function applyFilters(blink: PIXI.Application, content: PIXI.Container, filters: Filter[]) {
// `renderPadding` is necessary for filters like
// blur that spread beyond the bounds of the sprite
- const renderPadding = 100;
+ const renderPadding = filters.some(v => paddedFilters.has(v.type)) ? 100 : 0;
// Normalize offset to fit within renderTexture
const { x: bx, y: by, width: bw, height: bh } = content.getLocalBounds();
diff --git a/src/frontend/ui/utils/graph/nodeUIComponents/ColorPicker.svelte b/src/frontend/ui/utils/graph/nodeUIComponents/ColorPicker.svelte
index a23bb7f6..c252eca8 100644
--- a/src/frontend/ui/utils/graph/nodeUIComponents/ColorPicker.svelte
+++ b/src/frontend/ui/utils/graph/nodeUIComponents/ColorPicker.svelte
@@ -28,14 +28,21 @@
{#if typeof $valStore === "string"}
-
+
+
+ {config.label}
+
+
+
+
+
{:else}
ERR: Invaid colour: {JSON.stringify($valStore)}
{/if}
@@ -48,6 +55,11 @@
height: 100%;
}
+ .row {
+ display: grid;
+ grid-template-columns: auto 6em;
+ }
+
/* .picker :global(label .color) {
width: 45px !important;
height: 24px !important;
From b85355e7ec7a465304d515a1b2ce527136004208 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 20:10:15 +0200
Subject: [PATCH 167/209] Urgent AiManger toolbox bug fix
---
src/electron/lib/ai/AiManagerv2.ts | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/electron/lib/ai/AiManagerv2.ts b/src/electron/lib/ai/AiManagerv2.ts
index 44ed928d..6aa418ee 100644
--- a/src/electron/lib/ai/AiManagerv2.ts
+++ b/src/electron/lib/ai/AiManagerv2.ts
@@ -42,7 +42,7 @@ type PromptOptions = {
export const genericErrorResponse = "Oops, that wasn't supposed to happen😅";
export class AiManager {
- private readonly graphExporter: CoreGraphExporter;
+ private graphExporter: CoreGraphExporter;
private readonly chats: Chat[] = [];
private blypescriptInterpreter: BlypescriptInterpreter;
private blypescriptToolbox: BlypescriptToolbox;
@@ -61,8 +61,12 @@ export class AiManager {
}
async executePrompt({ prompt, graphId, model, apiKey, chatId, verbose }: PromptOptions) {
+ this.blypescriptToolbox = BlypescriptToolbox.fromToolbox(this.toolbox);
+ this.graphExporter = new CoreGraphExporter(
+ new BlypescriptExportStrategyV2(this.blypescriptToolbox)
+ );
+ // console.log(this.blypescriptToolbox.toString());
const blypescriptProgram = this.graphExporter.exportGraph(this.graphManager.getGraph(graphId));
- const blypescriptToolbox = BlypescriptToolbox.fromToolbox(this.toolbox);
this.blypescriptInterpreter = new BlypescriptInterpreter(this.toolbox, this.graphManager);
let chat = this.chats.find((chat) => chat.id === chatId);
@@ -76,7 +80,7 @@ export class AiManager {
{
role: "system",
content: generateGuidePrompt({
- interfaces: blypescriptToolbox.toString(),
+ interfaces: this.blypescriptToolbox.toString(),
}),
},
{
@@ -119,7 +123,7 @@ export class AiManager {
} satisfies Result;
}
- const result = BlypescriptProgram.fromString(response.data.content, blypescriptToolbox);
+ const result = BlypescriptProgram.fromString(response.data.content, this.blypescriptToolbox);
if (!result.success) {
logger.error(result.error, result.message);
From 9c8e6113aacac18dc42bcac16555e1679787b591 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 20:21:12 +0200
Subject: [PATCH 168/209] Add initial plugin build script
---
.github/workflows/build.yml | 2 +-
scripts/plugin.js | 74 +++++++++++++++++++++++++++++++++++++
2 files changed, 75 insertions(+), 1 deletion(-)
create mode 100644 scripts/plugin.js
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bb5723f2..4bf72de3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,7 +30,7 @@ jobs:
run: npm ci
- name: Build Blix Plugins
- run: cd ./blix-plugins/glfx-plugin && npm ci && npm run build
+ run: node scripts/plugin.js
- name: Build/Release Blix
run: |
diff --git a/scripts/plugin.js b/scripts/plugin.js
new file mode 100644
index 00000000..9720352d
--- /dev/null
+++ b/scripts/plugin.js
@@ -0,0 +1,74 @@
+const { readFileSync, writeFileSync, existsSync } = require("fs");
+const { exec } = require("child_process");
+const { join, resolve } = require("path");
+
+function buildGLFX() {
+ const directory = resolve(join(__dirname, "..", "blix-plugins/glfx-plugin"));
+ const options = { cwd: directory };
+ const command = "npm ci && npm run build";
+
+ console.log("Building glfx...");
+
+ exec(command, options, (err, stdout) => {
+ if (err) {
+ console.log(err);
+ return;
+ }
+
+ console.log(stdout);
+ console.log("Done building glfx.");
+ });
+}
+
+function buildBlink() {
+ const tsconfigPath = join(
+ __dirname,
+ "..",
+ "blix-plugins/blink/node_modules/@tsconfig/svelte/tsconfig.json"
+ );
+
+ const directory = resolve(join(__dirname, "..", "blix-plugins/blink"));
+ const options = { cwd: directory };
+ let command = "npm ci";
+
+ console.log("Building blink ...");
+
+ exec(command, options, (err, stdout) => {
+ if (err) {
+ console.log(err);
+ return;
+ }
+
+ console.log(stdout);
+
+ if (existsSync(tsconfigPath)) {
+ const tsconfigRaw = readFileSync(tsconfigPath, "utf8");
+ const withoutSingleLineComments = tsconfigRaw.replace(
+ /^(?!.*https?:\/\/)(.*)\/\/(.*)$/gm,
+ "$1"
+ );
+ const withoutComments = withoutSingleLineComments.replace(/\/\*([\s\S]*?)\*\//g, "");
+ const tsconfigJSON = JSON.parse(withoutComments);
+ tsconfigJSON.compilerOptions.moduleResolution = "node";
+ delete tsconfigJSON.compilerOptions.verbatimModuleSyntax;
+ writeFileSync(tsconfigPath, JSON.stringify(tsconfigJSON, null, 2));
+ } else {
+ console.log("TSConfig not found. Building...");
+ }
+
+ command = "npm run build";
+
+ exec(command, options, (err, stdout) => {
+ if (err) {
+ console.log(err);
+ return;
+ }
+
+ console.log(stdout);
+ console.log("Done building blink.");
+ });
+ });
+}
+
+// buildGLFX();
+buildBlink();
From d514a7a78ed9a73a20ff096a071bc7f37f5eea07 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 22:22:00 +0200
Subject: [PATCH 169/209] Add plugins build shell script
---
.github/workflows/build.yml | 2 +-
scripts/plugin.js | 79 ++++++++++++++++++++++---------------
scripts/plugins.sh | 43 ++++++++++++++++++++
3 files changed, 91 insertions(+), 33 deletions(-)
create mode 100755 scripts/plugins.sh
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4bf72de3..56199c2a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,7 +30,7 @@ jobs:
run: npm ci
- name: Build Blix Plugins
- run: node scripts/plugin.js
+ run: ./scripts/plugins.sh
- name: Build/Release Blix
run: |
diff --git a/scripts/plugin.js b/scripts/plugin.js
index 9720352d..cddb5bef 100644
--- a/scripts/plugin.js
+++ b/scripts/plugin.js
@@ -1,5 +1,5 @@
const { readFileSync, writeFileSync, existsSync } = require("fs");
-const { exec } = require("child_process");
+const { execSync } = require("child_process");
const { join, resolve } = require("path");
function buildGLFX() {
@@ -31,44 +31,59 @@ function buildBlink() {
const options = { cwd: directory };
let command = "npm ci";
- console.log("Building blink ...");
+ console.log("Installing blink node_modules...");
- exec(command, options, (err, stdout) => {
- if (err) {
- console.log(err);
- return;
- }
+ try {
+ const output = execSync(command, options);
+ console.log(output.toString());
+ } catch (error) {
+ console.log(error.toString());
+ }
- console.log(stdout);
+ if (existsSync(tsconfigPath)) {
+ const tsconfigRaw = readFileSync(tsconfigPath, "utf8");
+ const withoutSingleLineComments = tsconfigRaw.replace(
+ /^(?!.*https?:\/\/)(.*)\/\/(.*)$/gm,
+ "$1"
+ );
+ const withoutComments = withoutSingleLineComments.replace(/\/\*([\s\S]*?)\*\//g, "");
+ const tsconfigJSON = JSON.parse(withoutComments);
+ tsconfigJSON.compilerOptions.moduleResolution = "node";
+ delete tsconfigJSON.compilerOptions.verbatimModuleSyntax;
+ writeFileSync(tsconfigPath, JSON.stringify(tsconfigJSON, null, 2));
+ } else {
+ console.log("TSConfig not found. Building...");
+ }
- if (existsSync(tsconfigPath)) {
- const tsconfigRaw = readFileSync(tsconfigPath, "utf8");
- const withoutSingleLineComments = tsconfigRaw.replace(
- /^(?!.*https?:\/\/)(.*)\/\/(.*)$/gm,
- "$1"
- );
- const withoutComments = withoutSingleLineComments.replace(/\/\*([\s\S]*?)\*\//g, "");
- const tsconfigJSON = JSON.parse(withoutComments);
- tsconfigJSON.compilerOptions.moduleResolution = "node";
- delete tsconfigJSON.compilerOptions.verbatimModuleSyntax;
- writeFileSync(tsconfigPath, JSON.stringify(tsconfigJSON, null, 2));
- } else {
- console.log("TSConfig not found. Building...");
- }
+ command = "npm run build";
- command = "npm run build";
+ // console.log("Building blink...");
- exec(command, options, (err, stdout) => {
- if (err) {
- console.log(err);
- return;
- }
+ // try {
+ // const output = execSync(command, options);
+ // console.log(output.toString());
+ // } catch (error) {
+ // console.log(error.toString());
+ // }
- console.log(stdout);
- console.log("Done building blink.");
- });
- });
+ // console.log("Building blink completed");
+}
+
+function helper() {
+ const directory = resolve(join(__dirname, "..", "blix-plugins/blink"));
+ const options = { cwd: directory };
+ const command = "npm run build";
+
+ console.log("Starting build...");
+ try {
+ const output = execSync(command, options);
+ console.log(output.toString());
+ } catch (error) {
+ console.log(error.toString());
+ }
+ console.log("Done building blink");
}
// buildGLFX();
buildBlink();
+helper();
diff --git a/scripts/plugins.sh b/scripts/plugins.sh
new file mode 100755
index 00000000..eb570f43
--- /dev/null
+++ b/scripts/plugins.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+blixPluginDirectory=$(realpath "$(dirname "$0")/../blix-plugins/")
+
+# ===================================================================
+# GLFX
+# ===================================================================
+
+echo "Installing glfx-plugin node_modules..."
+cd "$blixPluginDirectory/glfx-plugin"
+output=$(npm ci 2>&1) && echo "$output" || echo "$output"
+
+echo "Building glfx-plugin..."
+output=$(npm run build 2>&1) && echo "$output" || echo "$output"
+
+echo "Building glfx-plugin completed"
+
+# ===================================================================
+# BLINK
+# ===================================================================
+
+tsconfigPath=$(realpath "$blixPluginDirectory/blink/node_modules/@tsconfig/svelte/tsconfig.json")
+blinkDirectory=$(realpath "$blixPluginDirectory/blink")
+
+echo "Installing blink node_modules..."
+cd "$blinkDirectory"
+output=$(npm ci 2>&1) && echo "$output" || echo "$output"
+
+echo "Attempting to fix tsconfig..."
+if test -f "$tsconfigPath"; then
+ tsconfigRaw=$(cat "$tsconfigPath")
+ withoutComments=$(echo "$tsconfigRaw" | sed '/\/\*/,/\*\//d')
+ tsconfigJSON="$(echo "$withoutComments" | sed -E 's/"moduleResolution"[^,}]*/"moduleResolution": "node"/' | sed -E '/"verbatimModuleSyntax"/d')"
+ printf "%s\n" "$tsconfigJSON" > "$tsconfigPath"
+ echo "TSConfig fixed"
+else
+ echo "TSConfig not found. Building..."
+fi
+
+echo "Building blink..."
+output=$(npm run build 2>&1) && echo "$output" || echo "$output"
+
+echo "Building blink completed"
From 8fee2db76ae830287b9e0f5d894caed5420d609d Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 22:25:57 +0200
Subject: [PATCH 170/209] Fix script variable instantiation
---
scripts/plugins.sh | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/scripts/plugins.sh b/scripts/plugins.sh
index eb570f43..9735dc9c 100755
--- a/scripts/plugins.sh
+++ b/scripts/plugins.sh
@@ -19,13 +19,14 @@ echo "Building glfx-plugin completed"
# BLINK
# ===================================================================
-tsconfigPath=$(realpath "$blixPluginDirectory/blink/node_modules/@tsconfig/svelte/tsconfig.json")
blinkDirectory=$(realpath "$blixPluginDirectory/blink")
echo "Installing blink node_modules..."
cd "$blinkDirectory"
output=$(npm ci 2>&1) && echo "$output" || echo "$output"
+tsconfigPath=$(realpath "$blixPluginDirectory/blink/node_modules/@tsconfig/svelte/tsconfig.json")
+
echo "Attempting to fix tsconfig..."
if test -f "$tsconfigPath"; then
tsconfigRaw=$(cat "$tsconfigPath")
From e704314b73906aaac66d48dcd7e7adb5b5febdca Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 22:36:38 +0200
Subject: [PATCH 171/209] Change back to JS build script
---
.github/workflows/build.yml | 2 +-
scripts/{plugin.js => plugins.js} | 26 ++++++--------------------
2 files changed, 7 insertions(+), 21 deletions(-)
rename scripts/{plugin.js => plugins.js} (78%)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 56199c2a..375f3b0b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,7 +30,7 @@ jobs:
run: npm ci
- name: Build Blix Plugins
- run: ./scripts/plugins.sh
+ run: node ./scripts/plugins.js
- name: Build/Release Blix
run: |
diff --git a/scripts/plugin.js b/scripts/plugins.js
similarity index 78%
rename from scripts/plugin.js
rename to scripts/plugins.js
index cddb5bef..6cd97c8e 100644
--- a/scripts/plugin.js
+++ b/scripts/plugins.js
@@ -40,6 +40,7 @@ function buildBlink() {
console.log(error.toString());
}
+ console.log("Attempting to fix tsconfig...");
if (existsSync(tsconfigPath)) {
const tsconfigRaw = readFileSync(tsconfigPath, "utf8");
const withoutSingleLineComments = tsconfigRaw.replace(
@@ -51,39 +52,24 @@ function buildBlink() {
tsconfigJSON.compilerOptions.moduleResolution = "node";
delete tsconfigJSON.compilerOptions.verbatimModuleSyntax;
writeFileSync(tsconfigPath, JSON.stringify(tsconfigJSON, null, 2));
+ console.log("TSConfig fixed");
} else {
console.log("TSConfig not found. Building...");
}
command = "npm run build";
- // console.log("Building blink...");
+ console.log("Building blink...");
- // try {
- // const output = execSync(command, options);
- // console.log(output.toString());
- // } catch (error) {
- // console.log(error.toString());
- // }
-
- // console.log("Building blink completed");
-}
-
-function helper() {
- const directory = resolve(join(__dirname, "..", "blix-plugins/blink"));
- const options = { cwd: directory };
- const command = "npm run build";
-
- console.log("Starting build...");
try {
const output = execSync(command, options);
console.log(output.toString());
} catch (error) {
console.log(error.toString());
}
- console.log("Done building blink");
+
+ console.log("Building blink completed");
}
-// buildGLFX();
+buildGLFX();
buildBlink();
-helper();
From 9a4bac530d524283c6c7346dd1ba14bd184231b1 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 22:39:55 +0200
Subject: [PATCH 172/209] Fix plugin build script
---
scripts/plugins.js | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/scripts/plugins.js b/scripts/plugins.js
index 6cd97c8e..79ceac7c 100644
--- a/scripts/plugins.js
+++ b/scripts/plugins.js
@@ -9,15 +9,14 @@ function buildGLFX() {
console.log("Building glfx...");
- exec(command, options, (err, stdout) => {
- if (err) {
- console.log(err);
- return;
- }
+ try {
+ const output = execSync(command, options);
+ console.log(output.toString());
+ } catch (error) {
+ console.log(error.toString());
+ }
- console.log(stdout);
- console.log("Done building glfx.");
- });
+ console.log("Building glfx completed");
}
function buildBlink() {
From c0391d608d59295a863336763b331219b1a8b85e Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Tue, 26 Sep 2023 22:53:35 +0200
Subject: [PATCH 173/209] Fix paths and add build timeout
---
.github/workflows/build.yml | 4 +++-
src/frontend/ui/tiles/Assets.svelte | 2 +-
src/frontend/ui/tiles/Media.svelte | 2 +-
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 375f3b0b..519aee55 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -43,4 +43,6 @@ jobs:
fi
env:
GH_TOKEN: ${{ secrets.TEST_TOKEN }}
- shell: bash
\ No newline at end of file
+ shell: bash
+
+ timeout-minutes: 30
\ No newline at end of file
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index f84f20dd..fca51b40 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -16,7 +16,7 @@
faPizzaSlice,
} from "@fortawesome/free-solid-svg-icons";
import Fa from "svelte-fa";
- import { toastStore } from "lib/stores/ToastStore";
+ import { toastStore } from "../../lib/stores/ToastStore";
let selectedCacheItems: CacheUUID[] = [];
// Not used at the moment
diff --git a/src/frontend/ui/tiles/Media.svelte b/src/frontend/ui/tiles/Media.svelte
index 9bb89ea9..4b2cd4ae 100644
--- a/src/frontend/ui/tiles/Media.svelte
+++ b/src/frontend/ui/tiles/Media.svelte
@@ -28,7 +28,7 @@
faPizzaSlice,
} from "@fortawesome/free-solid-svg-icons";
import Fa from "svelte-fa";
- import { toastStore } from "lib/stores/ToastStore";
+ import { toastStore } from "../../lib/stores/ToastStore";
const noContentIcons = [
faBacon,
From d153845143924ecd04e4621060183258a73e7caa Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Tue, 26 Sep 2023 22:55:20 +0200
Subject: [PATCH 174/209] Fix spelling mistakes
---
blix-plugins/blink/src/main.cjs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/blix-plugins/blink/src/main.cjs b/blix-plugins/blink/src/main.cjs
index 7bdf1147..22b4a1e3 100644
--- a/blix-plugins/blink/src/main.cjs
+++ b/blix-plugins/blink/src/main.cjs
@@ -189,7 +189,7 @@ const blinkNodes = {
"Bulges or pinches the image in a circle.",
[
{ id: "radius", min: 0, max: 1000, step: 1.0 },
- { id: "strenght", min: -1, max: 1, step: 0.01 },
+ { id: "strength", min: -1, max: 1, step: 0.01 },
{ id: "center.x", min: 0, max: 1, step: 0.01 },
{ id: "center.y", min: 0, max: 1, step: 0.01 },
]
@@ -198,7 +198,7 @@ const blinkNodes = {
"ZoomBlur",
"The ZoomFilter applies a Zoom blur to an object.",
[
- { id: "strenght", min: 0, max: 0.5, step: 0.01 },
+ { id: "strength", min: 0, max: 0.5, step: 0.01 },
{ id: "innerRadius", min: 0, max: 1000, step: 1.0 },
{ id: "center.x", min: 0, max: 2000, step: 1.0 },
{ id: "center.y", min: 0, max: 2000, step: 1.0 },
From a37244fbdb58c1178696ce496255bf0ef41abdf0 Mon Sep 17 00:00:00 2001
From: Jake Mileham
Date: Tue, 26 Sep 2023 23:45:38 +0200
Subject: [PATCH 175/209] Add CoreGraph manager tests
---
.../core-graph/CoreGraphEventManager.spec.ts | 664 ++++++++++++++++++
.../lib/core-graph/CoreGraphManager.spec.ts | 7 +
2 files changed, 671 insertions(+)
create mode 100644 tests/unit-tests/electron/lib/core-graph/CoreGraphEventManager.spec.ts
diff --git a/tests/unit-tests/electron/lib/core-graph/CoreGraphEventManager.spec.ts b/tests/unit-tests/electron/lib/core-graph/CoreGraphEventManager.spec.ts
new file mode 100644
index 00000000..f1f39c47
--- /dev/null
+++ b/tests/unit-tests/electron/lib/core-graph/CoreGraphEventManager.spec.ts
@@ -0,0 +1,664 @@
+import expect from "expect";
+import {
+ CoreGraphEvent,
+ CoreGraphEventManager,
+} from "../../../../../src/electron/lib/core-graph/CoreGraphEventManger";
+import { CoreGraphManager } from "../../../../../src/electron/lib/core-graph/CoreGraphManager";
+import { Blix } from "../../../../../src/electron/lib/Blix";
+import { MainWindow } from "../../../../../src/electron/lib/api/apis/WindowApi";
+import {
+ MinAnchor,
+ NodeInstance,
+} from "../../../../../src/electron/lib/registries/ToolboxRegistry";
+import { CoreGraph } from "../../../../../src/electron/lib/core-graph/CoreGraph";
+import { NodeUIParent } from "../../../../../src/shared/ui/NodeUITypes";
+import exp from "constants";
+
+const mainWindow: MainWindow = {
+ apis: {
+ commandRegistryApi: jest.fn(),
+ clientGraphApi: jest.fn(),
+ clientProjectApi: jest.fn(),
+ toolboxClientApi: {
+ registryChanged: jest.fn(),
+ },
+ commandClientApi: {
+ registryChanged: jest.fn(),
+ },
+ graphClientApi: {
+ graphRemoved: jest.fn(),
+ },
+ },
+} as any;
+
+jest.mock("chokidar", () => ({
+ default: {
+ watch: jest.fn(() => {
+ return {
+ on: jest.fn(),
+ };
+ }),
+ },
+}));
+
+jest.mock("electron", () => ({
+ app: {
+ getPath: jest.fn((path) => {
+ return "test/electron";
+ }),
+ getName: jest.fn(() => {
+ return "TestElectron";
+ }),
+ getVersion: jest.fn(() => {
+ return "v1.1.1";
+ }),
+ getAppPath: jest.fn(() => {
+ return "test/electron";
+ }),
+ },
+ ipcMain: {
+ on: jest.fn(),
+ },
+}));
+
+jest.mock("fs", () => ({
+ readFileSync: jest.fn().mockReturnValue("mocked_base64_string"),
+ readFile: jest.fn((filePath, callback) => callback(null, "mocked_file_data")),
+ readdirSync: jest.fn(() => ["hello-plugin"]),
+ existsSync: jest.fn(),
+ mkdirSync: jest.fn(),
+ writeFileSync: jest.fn(),
+}));
+
+jest.mock("electron-store", () => ({
+ default: jest.fn().mockImplementation(() => {
+ return {};
+ }),
+}));
+
+jest.mock("ws", () => {
+ return {
+ WebSocketServer: jest.fn().mockImplementation(() => {
+ return {
+ on: jest.fn(),
+ };
+ }),
+ };
+});
+
+jest.mock("../../../../../src/electron/lib/plugins/PluginManager");
+describe("Test Core Graph Event Manager", () => {
+ let blix: Blix;
+ let graph_manager: CoreGraphManager;
+ let graph: CoreGraph;
+ let nodeA: NodeInstance;
+ let nodeB: NodeInstance;
+ // const errorOccurred = (test: string, error?: string) => {
+ // console.log(`\u001b[31mAn error occured!!!\n\u001b[0mTest: ${test}\nError: ${error}\n`);
+ // };
+ beforeEach(() => {
+ // INIT
+ blix = new Blix();
+ blix.init(mainWindow);
+ graph_manager = new CoreGraphManager(blix.toolbox, mainWindow);
+ // SETUP GRAPH
+ graph = new CoreGraph();
+ graph_manager.addGraph(graph);
+ const inputA: MinAnchor = {
+ type: "string",
+ displayName: `Test-plugin.Node-A.0`,
+ identifier: `inA`,
+ };
+ const outputA: MinAnchor = {
+ type: "string",
+ displayName: `Test-plugin.Node-A.1`,
+ identifier: `outA`,
+ };
+ nodeA = new NodeInstance(
+ `Node-A`,
+ `Test-plugin`,
+ "folder",
+ `Node-A`,
+ `This is node A`,
+ `fa-duotone fa-bell`,
+ [inputA],
+ [outputA],
+ undefined,
+ null,
+ { "numberA": {
+ label: "numberA",
+ defaultValue: "0",
+ componentId: "numberA",
+ triggerUpdate: true
+ }},
+ undefined
+ );
+
+ const inputB: MinAnchor = {
+ type: "string",
+ displayName: `Test-plugin.Node-B.0`,
+ identifier: `inB`,
+ };
+ const outputB: MinAnchor = {
+ type: "string",
+ displayName: `Test-plugin.Node-B.1`,
+ identifier: `outB`,
+ };
+ nodeB = new NodeInstance(
+ `Node-B`,
+ `Test-plugin`,
+ "folder",
+ `Node-B`,
+ `This is node B`,
+ `fa-duotone fa-bell`,
+ [inputB],
+ [outputB],
+ undefined,
+ new NodeUIParent("label",null),
+ { "numberB": {
+ label: "numberB",
+ defaultValue: "0",
+ componentId: "numberB",
+ triggerUpdate: true
+ }},
+ undefined
+ );
+
+ // SETUP TOOLBOX
+ blix.toolbox.addInstance(
+ new NodeInstance(
+ `Node-A`,
+ `Test-plugin`,
+ "folder",
+ `Node-A`,
+ `This is node A`,
+ `fa-duotone fa-bell`,
+ [inputA],
+ [outputA],
+ undefined,
+ null,
+ { "numberA": {
+ label: "numberA",
+ defaultValue: "0",
+ componentId: "numberA",
+ triggerUpdate: true
+ }},
+ undefined
+ )
+ );
+ blix.toolbox.addInstance(
+ new NodeInstance(
+ `Node-B`,
+ `Test-plugin`,
+ "folder",
+ `Node-B`,
+ `This is node B`,
+ `fa-duotone fa-bell`,
+ [inputB],
+ [outputB],
+ undefined,
+ new NodeUIParent("label",null),
+ { "numberB": {
+ label: "numberB",
+ defaultValue: "0",
+ componentId: "numberB",
+ triggerUpdate: true
+ }},
+ undefined
+ )
+ );
+ });
+
+ test("Roll forwards limit", () => {
+ const res = graph_manager.redoEvent(graph.uuid);
+ expect(res.status).toBe("error");
+ });
+
+ test("Roll backwards limit", () => {
+ const res = graph_manager.undoEvent(graph.uuid);
+ expect(res.status).toBe("error");
+ })
+
+ test("Resting event manager", () => {
+ expect(() => {
+ const event_manager = new CoreGraphEventManager();
+ event_manager.reset();
+ }).not.toThrow("some error");
+ })
+
+ test("Adding a Node", () => {
+ const addRes = graph_manager.addNode(graph.uuid, nodeA, { x: 0, y: 0 }, 1);
+ expect(addRes.status).toBe("success");
+ if (addRes.status === "success") {
+ // Undo - Remove the node
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ // Check node is removed
+ if (addRes.data) expect(graph.getNodes[addRes.data.nodeId]).toBeUndefined();
+ // Redo - Add the node
+ const redoRes = graph_manager.redoEvent(graph.uuid);
+ expect(redoRes.status).toBe("success");
+ // Node was added back
+ expect(Object.values(graph.getNodes)[0]).toBeDefined();
+ }
+ });
+
+ test("Removing a Node", () => {
+ const addRes = graph_manager.addNode(graph.uuid, nodeA, { x: 0, y: 0 }, 1);
+ expect(addRes.status).toBe("success");
+ expect(addRes.data).toBeDefined();
+ if (addRes.status === "success" && addRes.data) {
+ // Node exists
+ expect(graph.getNodes[addRes.data.nodeId]).toBeDefined();
+ // Remove Node
+ const removeRes = graph_manager.removeNode(graph.uuid, addRes.data.nodeId, 1);
+ expect(removeRes.status).toBe("success");
+ if (removeRes.status === "success") {
+ // Node Removed
+ expect(graph.getNodes[addRes.data.nodeId]).toBeUndefined();
+ // Undo - Add the node
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ // Check node is added back
+ expect(Object.values(graph.getNodes)).toBeDefined();
+ // Redo - Remove the node
+ const redoRes = graph_manager.redoEvent(graph.uuid);
+ expect(redoRes.status).toBe("success");
+ // Check node is removed again
+ expect(Object.values(graph.getNodes)[0]).toBeUndefined();
+ }
+ }
+ });
+
+ test("Adding an edge", () => {
+ const pos = { x: 0, y: 0 };
+ const addNodeARes = graph_manager.addNode(graph.uuid, nodeA, pos, 1);
+ const addNodeBRes = graph_manager.addNode(graph.uuid, nodeB, pos, 1);
+
+ expect(addNodeARes.status).toBe("success");
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.status).toBe("success");
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.status === "success" && addNodeBRes.status === "success") {
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.data && addNodeBRes.data) {
+ const nodes = graph.getNodes;
+ const anchorARes = nodes[addNodeARes.data.nodeId].findAnchorUUID("outA");
+ const anchorBRes = nodes[addNodeBRes.data.nodeId].findAnchorUUID("inB");
+ expect(anchorARes.status).toBe("success");
+ expect(anchorBRes.status).toBe("success");
+ if (anchorARes.status === "success" && anchorBRes.status === "success") {
+ expect(anchorARes.data).toBeDefined();
+ expect(anchorBRes.data).toBeDefined();
+ if (anchorARes.data && anchorBRes.data) {
+ // Add edge
+ const addEdgeRes = graph_manager.addEdge(
+ graph.uuid,
+ anchorARes.data.anchorUUID,
+ anchorBRes.data.anchorUUID,
+ 1
+ );
+ expect(addEdgeRes.status).toBe("success");
+ if (addEdgeRes.status === "success" && addEdgeRes.data) {
+ // Edge should be added
+ const edge = graph.getEdgeDest[anchorBRes.data.anchorUUID];
+ expect(edge).toBeDefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID].uuid).toBe(edge.uuid);
+ expect(edge.uuid).toBe(addEdgeRes.data.edgeId);
+ expect(edge.getAnchorFrom).toBe(anchorARes.data.anchorUUID);
+ expect(edge.getAnchorTo).toBe(anchorBRes.data.anchorUUID);
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toContain(edge.getAnchorTo);
+ // Undo - Remove the edge
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ // Edge should be removed
+ expect(graph.getEdgeDest[addEdgeRes.data.edgeId]).toBeUndefined();
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toBeUndefined();
+ // Redo - Add the edge
+ const redoRes = graph_manager.redoEvent(graph.uuid);
+ expect(redoRes.status).toBe("success");
+ expect(redoRes.data).toBeDefined();
+ // Edge should be added again
+ if (redoRes.status === "success" && redoRes.data) {
+ const edge = Object.values(graph.getEdgeDest)[0];
+ expect(edge).toBeDefined();
+ expect(anchorARes.data.anchorUUID).toBe(edge.getAnchorFrom);
+ expect(anchorBRes.data.anchorUUID).toBe(edge.getAnchorTo);
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toBeDefined();
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toContain(edge.getAnchorTo);
+ expect(graph.getEdgeDest[edge.getAnchorTo]).toBeDefined();
+ expect(graph.getEdgeDest[edge.getAnchorTo].uuid).toBe(edge.uuid);
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ test("Changing UiInput of node", () => {
+ const addRes = graph_manager.addNode(graph.uuid, nodeA, { x: 0, y: 0 }, 1);
+ expect(addRes.status).toBe("success");
+ if (addRes.status === "success") {
+ expect(addRes.data).toBeDefined();
+ if (addRes.data) {
+ // Update uiInput of node and create event
+ const oldinputs = graph.getAllUIInputs[addRes.data.nodeId].getInputs;
+ expect(oldinputs["numberA"]).toBeDefined();
+ oldinputs["numberA"] = 1;
+ const updateRes = graph_manager.updateUIInputs(graph.uuid, addRes.data.nodeId, { inputs: oldinputs, changes: ["numberA"] }, 1);
+ expect(updateRes.status).toBe("success");
+ const interRes = graph_manager.handleNodeInputInteraction(graph.uuid, addRes.data.nodeId, { id: "numberA", value: 1 });
+ expect(interRes.status).toBe("success");
+
+ // Undo
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ // Redo
+ const redoRes = graph_manager.redoEvent(graph.uuid);
+ expect(redoRes.status).toBe("success");
+ // Remove and add node so trigger update of ui change event
+ const removeRes = graph_manager.removeNode(graph.uuid, addRes.data.nodeId, 1);
+ expect(removeRes.status).toBe("success");
+ if (removeRes.status === "success") {
+ // Node Removed
+ expect(graph.getNodes[addRes.data.nodeId]).toBeUndefined();
+ // Undo - Add the node
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ // Check node is added back
+ expect(Object.values(graph.getNodes)).toBeDefined();
+
+ const newNode = Object.values(graph.getNodes)[0];
+ // Update uiInput of node and create event
+ const oldinputs = graph.getAllUIInputs[newNode.uuid].getInputs;
+ expect(oldinputs["numberA"]).toBeDefined();
+ oldinputs["numberA"] = 2;
+ const updateRes = graph_manager.updateUIInputs(graph.uuid, newNode.uuid, { inputs: oldinputs, changes: ["numberA"] }, 1);
+ expect(updateRes.status).toBe("success");
+ const interRes = graph_manager.handleNodeInputInteraction(graph.uuid, newNode.uuid, { id: "numberA", value: 2 });
+ expect(interRes.status).toBe("success");
+ }
+ }
+ }
+ });
+
+ test("Removing an edge", () => {
+ const pos = { x: 0, y: 0 };
+ const addNodeARes = graph_manager.addNode(graph.uuid, nodeA, pos, 1);
+ const addNodeBRes = graph_manager.addNode(graph.uuid, nodeB, pos, 1);
+
+ expect(addNodeARes.status).toBe("success");
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.status).toBe("success");
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.status === "success" && addNodeBRes.status === "success") {
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.data && addNodeBRes.data) {
+ const nodes = graph.getNodes;
+ const anchorARes = nodes[addNodeARes.data.nodeId].findAnchorUUID("outA");
+ const anchorBRes = nodes[addNodeBRes.data.nodeId].findAnchorUUID("inB");
+ expect(anchorARes.status).toBe("success");
+ expect(anchorBRes.status).toBe("success");
+ if (anchorARes.status === "success" && anchorBRes.status === "success") {
+ expect(anchorARes.data).toBeDefined();
+ expect(anchorBRes.data).toBeDefined();
+ if (anchorARes.data && anchorBRes.data) {
+ // Add edge
+ const addEdgeRes = graph_manager.addEdge(
+ graph.uuid,
+ anchorARes.data.anchorUUID,
+ anchorBRes.data.anchorUUID,
+ 1
+ );
+ expect(addEdgeRes.status).toBe("success");
+ if (addEdgeRes.status === "success" && addEdgeRes.data) {
+ // Edge should be added
+ const edge = graph.getEdgeDest[anchorBRes.data.anchorUUID];
+ expect(edge).toBeDefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID].uuid).toBe(edge.uuid);
+ expect(edge.uuid).toBe(addEdgeRes.data.edgeId);
+ expect(edge.getAnchorFrom).toBe(anchorARes.data.anchorUUID);
+ expect(edge.getAnchorTo).toBe(anchorBRes.data.anchorUUID);
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toContain(edge.getAnchorTo);
+ // Remove edge
+ const removeEdgeRes = graph_manager.removeEdge(
+ graph.uuid,
+ anchorBRes.data.anchorUUID,
+ 1
+ );
+ expect(removeEdgeRes.status).toBe("success");
+ if (removeEdgeRes.status === "success") {
+ expect(graph.getEdgeSrc[anchorARes.data.anchorUUID]).toBeUndefined();
+ expect(graph.getEdgeSrc[anchorBRes.data.anchorUUID]).toBeUndefined();
+ // Undo - Add Edge Back
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ // Edge should be added
+ const edge = graph.getEdgeDest[anchorBRes.data.anchorUUID];
+ expect(edge).toBeDefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID].uuid).toBe(edge.uuid);
+ expect(edge.getAnchorFrom).toBe(anchorARes.data.anchorUUID);
+ expect(edge.getAnchorTo).toBe(anchorBRes.data.anchorUUID);
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toContain(edge.getAnchorTo);
+ // Redo - Remove Edge
+ const redoRes = graph_manager.redoEvent(graph.uuid);
+ expect(redoRes.status).toBe("success");
+ if (redoRes.status === "success") {
+ expect(graph.getEdgeSrc[anchorARes.data.anchorUUID]).toBeUndefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID]).toBeUndefined();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ test("Remove nodeA with edges", () => {
+ const pos = { x: 0, y: 0 };
+ const addNodeARes = graph_manager.addNode(graph.uuid, nodeA, pos, 1);
+ const addNodeBRes = graph_manager.addNode(graph.uuid, nodeB, pos, 1);
+
+ expect(addNodeARes.status).toBe("success");
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.status).toBe("success");
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.status === "success" && addNodeBRes.status === "success") {
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.data && addNodeBRes.data) {
+ const nodes = graph.getNodes;
+ const anchorARes = nodes[addNodeARes.data.nodeId].findAnchorUUID("outA");
+ const anchorBRes = nodes[addNodeBRes.data.nodeId].findAnchorUUID("inB");
+ expect(anchorARes.status).toBe("success");
+ expect(anchorBRes.status).toBe("success");
+ if (anchorARes.status === "success" && anchorBRes.status === "success") {
+ expect(anchorARes.data).toBeDefined();
+ expect(anchorBRes.data).toBeDefined();
+ if (anchorARes.data && anchorBRes.data) {
+ // Add edge
+ const addEdgeRes = graph_manager.addEdge(
+ graph.uuid,
+ anchorARes.data.anchorUUID,
+ anchorBRes.data.anchorUUID,
+ 1
+ );
+ expect(addEdgeRes.status).toBe("success");
+ if (addEdgeRes.status === "success" && addEdgeRes.data) {
+ // Edge should be added
+ const edge = graph.getEdgeDest[anchorBRes.data.anchorUUID];
+ expect(edge).toBeDefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID].uuid).toBe(edge.uuid);
+ expect(edge.uuid).toBe(addEdgeRes.data.edgeId);
+ expect(edge.getAnchorFrom).toBe(anchorARes.data.anchorUUID);
+ expect(edge.getAnchorTo).toBe(anchorBRes.data.anchorUUID);
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toContain(edge.getAnchorTo);
+ // Remove Node
+ const removeNodeRes = graph_manager.removeNode(
+ graph.uuid,
+ addNodeARes.data.nodeId,
+ 1
+ );
+ expect(removeNodeRes.status).toBe("success");
+ if (removeNodeRes.status === "success") {
+ // Node Should be removed
+ expect(graph.getNodes[addNodeARes.data.nodeId]).toBeUndefined();
+ expect(Object.keys(graph.getNodes).length).toBe(1);
+ // Edge should be removed
+ expect(graph.getEdgeSrc[anchorARes.data.anchorUUID]).toBeUndefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID]).toBeUndefined();
+ // Undo - Add node and its edges
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ if (undoRes.status === "success") {
+ // Check for node
+ const newNode =
+ Object.keys(graph.getNodes)[0] === addNodeBRes.data.nodeId
+ ? Object.values(graph.getNodes)[1]
+ : Object.values(graph.getNodes)[0];
+ expect(newNode).toBeDefined();
+ expect(newNode.getName).toBe("Node-A");
+ const newAnchorA = newNode.findAnchorUUID("outA");
+ expect(newAnchorA.status).toBe("success");
+ if (newAnchorA.status === "success") {
+ expect(newAnchorA.data).toBeDefined();
+ if (newAnchorA.data) {
+ // Check for edge
+ expect(graph.getEdgeSrc[newAnchorA.data.anchorUUID]).toBeDefined();
+ expect(graph.getEdgeSrc[newAnchorA.data.anchorUUID]).toContain(
+ anchorBRes.data.anchorUUID
+ );
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID]).toBeDefined();
+ const edge = graph.getEdgeDest[anchorBRes.data.anchorUUID];
+ expect(edge).toBeDefined();
+ expect(edge.getAnchorFrom).toBe(newAnchorA.data.anchorUUID);
+ expect(edge.getAnchorTo).toBe(anchorBRes.data.anchorUUID);
+ // Redo - Remove Node and its edges
+ const redoRes = graph_manager.redoEvent(graph.uuid);
+ expect(redoRes.status).toBe("success");
+ if (redoRes.status === "success") {
+ // Node should be removed
+ expect(graph.getNodes[newNode.uuid]).toBeUndefined();
+ expect(Object.keys(graph.getNodes).length).toBe(1);
+ // Edge should be removed
+ expect(graph.getEdgeSrc[newAnchorA.data.anchorUUID]).toBeUndefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID]).toBeUndefined();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ test("Remove nodeB with edges", () => {
+ const pos = { x: 0, y: 0 };
+ const addNodeARes = graph_manager.addNode(graph.uuid, nodeA, pos, 1);
+ const addNodeBRes = graph_manager.addNode(graph.uuid, nodeB, pos, 1);
+
+ expect(addNodeARes.status).toBe("success");
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.status).toBe("success");
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.status === "success" && addNodeBRes.status === "success") {
+ expect(addNodeARes.data).toBeDefined();
+ expect(addNodeBRes.data).toBeDefined();
+ if (addNodeARes.data && addNodeBRes.data) {
+ const nodes = graph.getNodes;
+ const anchorARes = nodes[addNodeARes.data.nodeId].findAnchorUUID("outA");
+ const anchorBRes = nodes[addNodeBRes.data.nodeId].findAnchorUUID("inB");
+ expect(anchorARes.status).toBe("success");
+ expect(anchorBRes.status).toBe("success");
+ if (anchorARes.status === "success" && anchorBRes.status === "success") {
+ expect(anchorARes.data).toBeDefined();
+ expect(anchorBRes.data).toBeDefined();
+ if (anchorARes.data && anchorBRes.data) {
+ // Add edge
+ const addEdgeRes = graph_manager.addEdge(
+ graph.uuid,
+ anchorARes.data.anchorUUID,
+ anchorBRes.data.anchorUUID,
+ 1
+ );
+ expect(addEdgeRes.status).toBe("success");
+ if (addEdgeRes.status === "success" && addEdgeRes.data) {
+ // Edge should be added
+ const edge = graph.getEdgeDest[anchorBRes.data.anchorUUID];
+ expect(edge).toBeDefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID].uuid).toBe(edge.uuid);
+ expect(edge.uuid).toBe(addEdgeRes.data.edgeId);
+ expect(edge.getAnchorFrom).toBe(anchorARes.data.anchorUUID);
+ expect(edge.getAnchorTo).toBe(anchorBRes.data.anchorUUID);
+ expect(graph.getEdgeSrc[edge.getAnchorFrom]).toContain(edge.getAnchorTo);
+ // Remove Node
+ const removeNodeRes = graph_manager.removeNode(
+ graph.uuid,
+ addNodeBRes.data.nodeId,
+ 1
+ );
+ expect(removeNodeRes.status).toBe("success");
+ if (removeNodeRes.status === "success") {
+ // Node Should be removed
+ expect(graph.getNodes[addNodeBRes.data.nodeId]).toBeUndefined();
+ expect(Object.keys(graph.getNodes).length).toBe(1);
+ // Edge should be removed
+ expect(graph.getEdgeSrc[anchorARes.data.anchorUUID]).toBeUndefined();
+ expect(graph.getEdgeDest[anchorBRes.data.anchorUUID]).toBeUndefined();
+ // Undo - Add node and its edges
+ const undoRes = graph_manager.undoEvent(graph.uuid);
+ expect(undoRes.status).toBe("success");
+ if (undoRes.status === "success") {
+ // Check for node
+ const newNode =
+ Object.keys(graph.getNodes)[0] === addNodeARes.data.nodeId
+ ? Object.values(graph.getNodes)[1]
+ : Object.values(graph.getNodes)[0];
+ expect(newNode).toBeDefined();
+ expect(newNode.getName).toBe("Node-B");
+ const newAnchorB = newNode.findAnchorUUID("inB");
+ expect(newAnchorB.status).toBe("success");
+ if (newAnchorB.status === "success") {
+ expect(newAnchorB.data).toBeDefined();
+ if (newAnchorB.data) {
+ // Check for edge
+ expect(graph.getEdgeSrc[anchorARes.data.anchorUUID]).toBeDefined();
+ expect(graph.getEdgeSrc[anchorARes.data.anchorUUID]).toContain(
+ newAnchorB.data.anchorUUID
+ );
+ expect(graph.getEdgeDest[newAnchorB.data.anchorUUID]).toBeDefined();
+ const edge = graph.getEdgeDest[newAnchorB.data.anchorUUID];
+ expect(edge).toBeDefined();
+ expect(edge.getAnchorFrom).toBe(anchorARes.data.anchorUUID);
+ expect(edge.getAnchorTo).toBe(newAnchorB.data.anchorUUID);
+ // Redo - Remove Node and its edges
+ const redoRes = graph_manager.redoEvent(graph.uuid);
+ expect(redoRes.status).toBe("success");
+ if (redoRes.status === "success") {
+ // Node should be removed
+ expect(graph.getNodes[newNode.uuid]).toBeUndefined();
+ expect(Object.keys(graph.getNodes).length).toBe(1);
+ // Edge should be removed
+ expect(graph.getEdgeSrc[anchorARes.data.anchorUUID]).toBeUndefined();
+ expect(graph.getEdgeDest[newAnchorB.data.anchorUUID]).toBeUndefined();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+
+});
diff --git a/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts b/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
index 7acfd71e..704ea6fd 100644
--- a/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
+++ b/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
@@ -273,4 +273,11 @@ jest.mock('../../../../../src/electron/lib/plugins/PluginManager')
graphManager.onGraphUpdated(graph.uuid,GRAPH_UPDATED_EVENT,CoreGraphUpdateParticipant.system);
expect(subscriber.onGraphChanged).toBeCalled()
})
+
+ test("Update Graph metadata", () => {
+ expect(() => { const res = graphManager.addGraph(graph) }).not.toThrow("some error");
+
+ const res = graphManager.updateGraphMetadata(graph.uuid, {}, 1);
+ expect(res.status).toBe("success");
+ })
});
\ No newline at end of file
From 3dff9cfad7d04c829963b51d1cc3063776c28f37 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 27 Sep 2023 06:46:49 +0200
Subject: [PATCH 176/209] Bump Blix to v1.2.0
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index fb0b0b67..5f2255df 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "blix",
"description": "Blix - An AI-assisted graph photo editor",
- "version": "1.1.0",
+ "version": "1.2.0",
"repository": {
"type": "git",
"url": "https://github.com/COS301-SE-2023/AI-Photo-Editor.git"
From 2311f915cb8a5ea36b83168c1cc3ced2b00eb887 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Wed, 27 Sep 2023 08:24:05 +0200
Subject: [PATCH 177/209] pluginCommand tests done
---
.../electron/lib/plugins/plugin.spec.ts | 5 +
.../lib/plugins/pluginCommands.spec.ts | 110 ++++++++++++++++++
2 files changed, 115 insertions(+)
create mode 100644 tests/unit-tests/electron/lib/plugins/pluginCommands.spec.ts
diff --git a/tests/unit-tests/electron/lib/plugins/plugin.spec.ts b/tests/unit-tests/electron/lib/plugins/plugin.spec.ts
index 2cd01f30..0d6dafa0 100644
--- a/tests/unit-tests/electron/lib/plugins/plugin.spec.ts
+++ b/tests/unit-tests/electron/lib/plugins/plugin.spec.ts
@@ -38,6 +38,11 @@ jest.mock("electron", () => ({
},
ipcMain: {
on: jest.fn()
+ },
+ session: {
+ defaultSession: {
+ clearCache: jest.fn()
+ }
}
}));
diff --git a/tests/unit-tests/electron/lib/plugins/pluginCommands.spec.ts b/tests/unit-tests/electron/lib/plugins/pluginCommands.spec.ts
new file mode 100644
index 00000000..beda3a4d
--- /dev/null
+++ b/tests/unit-tests/electron/lib/plugins/pluginCommands.spec.ts
@@ -0,0 +1,110 @@
+import {refreshPluginsCommand} from "../../../../../src/electron/lib/plugins/pluginCommands";
+import { Blix } from "../../../../../src/electron/lib/Blix";
+import { MainWindow } from "../../../../../src/electron/lib/api/apis/WindowApi";
+import { boolean } from "zod";
+import { CommandContext } from "../../../../../src/electron/lib/registries/CommandRegistry";
+
+jest.mock('../../../../../src/electron/lib/registries/CommandRegistry', () => {
+ const originalModule = jest.requireActual('../../../../../src/electron/lib/registries/CommandRegistry'); // Replace with the correct path
+ return {
+ ...originalModule,
+ CommandContext: {
+ ...originalModule.CommandContext,
+ pluginManager: {
+ loadBasePlugins: jest.fn(),
+ },
+ },
+ };
+});
+
+
+// ====================================================
+const mainWindow: MainWindow = {
+ apis: {
+ commandRegistryApi: jest.fn(),
+ clientGraphApi: jest.fn(),
+ clientProjectApi: jest.fn(),
+ toolboxClientApi: {
+ registryChanged: jest.fn(),
+ },
+ commandClientApi: {
+ registryChanged: jest.fn(),
+ },
+ projectClientApi: {
+ onProjectCreated: jest.fn(),
+ onProjectChanged: jest.fn()
+ },
+ graphClientApi: {
+ graphChanged: jest.fn(),
+ graphRemoved: jest.fn(),
+ uiInputsChanged: jest.fn()
+ },
+ utilClientApi: {
+ showToast: jest.fn((message) => {
+ // console.log(message);
+ }),
+ }
+
+ }
+ } as any;
+
+// jest.mock("../../../src/electron/lib/projects/ProjectManager");
+
+
+jest.mock("chokidar", () => ({
+ default: {
+ watch: jest.fn(() => {
+ return {
+ on: jest.fn()
+ }
+ }),
+ }
+}));
+
+jest.mock("electron", () => ({
+ app: {
+ getPath: jest.fn((path) => {
+ return "test/electron";
+ }),
+ getName: jest.fn(() => {
+ return "TestElectron";
+ }),
+ getVersion: jest.fn(() => {
+ return "v1.1.1";
+ }),
+ getAppPath: jest.fn(() => {
+ return "test/electron";
+ })
+ },
+ dialog: {
+ showSaveDialog: jest.fn(() => {
+ return { filePath: "path.blix" };
+ }),
+ showOpenDialog: jest.fn(() => {
+ return { filePaths: ["path1.blix", "path2.blix"] };
+ })
+ },
+ ipcMain: {
+ on: jest.fn()
+ },
+ session: {
+ defaultSession: {
+ clearCache: jest.fn()
+ }
+ }
+}));
+
+
+it("Test refreshPluginsCommand", async () => {
+
+ const blix = new Blix();
+ const ctx = {
+ pluginManager: {
+ loadBasePlugins: jest.fn(),
+ }
+ } as unknown as CommandContext;
+
+
+ const a = await refreshPluginsCommand.handler(ctx);
+ expect(a.status).toBe("success");
+ })
\ No newline at end of file
From 57972dfdaf35ddbdc7d2715a48000010675f6e49 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Wed, 27 Sep 2023 08:28:47 +0200
Subject: [PATCH 178/209] small fix
---
jest.config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jest.config.json b/jest.config.json
index 24c01e15..181f591f 100644
--- a/jest.config.json
+++ b/jest.config.json
@@ -15,7 +15,7 @@
],
"^.+\\.js$": "babel-jest"
},
- "coveragePathIgnorePatterns": ["blix-plugins"],
+ "coveragePathIgnorePatterns": ["blix-plugins","node_modules","src/electron/lib/plugins/PluginManager"],
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node", "svelte"],
"coverageDirectory": "coverage",
From 49eb2bc25cc028ab2e1d0a0f14692624c2bbe3d2 Mon Sep 17 00:00:00 2001
From: CenturionLC
Date: Wed, 27 Sep 2023 09:02:17 +0200
Subject: [PATCH 179/209] update package.json
---
package-lock.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index e4a766b6..a2b6f1df 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "blix",
- "version": "1.1.0",
+ "version": "1.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "blix",
- "version": "1.1.0",
+ "version": "1.2.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
From c84c027f5c8a33764ac2635f4b04a9c5445ee087 Mon Sep 17 00:00:00 2001
From: Klairgo
Date: Wed, 27 Sep 2023 09:49:58 +0200
Subject: [PATCH 180/209] Fix e2e test
---
tests/e2e/blix.spec.ts | 38 ++++++++++++++++++++------------------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/tests/e2e/blix.spec.ts b/tests/e2e/blix.spec.ts
index 9b637ca5..bf5dcab4 100644
--- a/tests/e2e/blix.spec.ts
+++ b/tests/e2e/blix.spec.ts
@@ -4,9 +4,9 @@ import { join } from "path";
import { waitForDebugger } from "inspector";
import exp from "constants";
-test('E2E testing blix', async () => {
- const electronApp = await electron.launch({ args: ['.']})
+test('E2E testing blix', async () => {
+const electronApp = await electron.launch({ args: ['.']})
const isPackaged = await electronApp.evaluate(async ({ app }) => {
// This runs in Electron's main process, parameter here is always
// the result of the require('electron') in the main app script.
@@ -32,45 +32,47 @@ test('E2E testing blix', async () => {
// and return its Page object
const window = await electronApp.firstWindow();
- await window.locator('svg').first().click();
+ await window.keyboard.press('Escape', {delay: 4000});
+ // await window.locator('svg').first().click();
- expect((await window.getByTitle('Untitled').allInnerTexts()).at(0)).toBe("Untitled");
+ expect((await window.getByTitle('Untitled').allInnerTexts()).at(0)).toBe("Untitled-1");
const graph = window.locator('section').first();
await graph.click({button: 'right'});
// Check plugin menu
const plugin = window.getByText('blix');
- expect((await plugin.allInnerTexts()).at(0)).toBe("blix");
- expect((await window.getByText('hello-plugin').allInnerTexts()).at(0)).toBe("hello-plugin");
- expect((await window.getByText('math-plugin').allInnerTexts()).at(0)).toBe("math-plugin");
- expect((await window.getByText('input-plugin').allInnerTexts()).at(0)).toBe("input-plugin");
- expect((await window.getByText('sharp-plugin').allInnerTexts()).at(0)).toBe("sharp-plugin");
+ expect(((await plugin.allInnerTexts()).at(2))).toBe("Blix");
+ expect((await window.getByText('Blink').allInnerTexts()).at(0)).toBe("Blink");
+ expect((await window.getByText('GLFX').allInnerTexts()).at(0)).toBe("GLFX");
+ expect((await window.getByText('Input').allInnerTexts()).at(0)).toBe("Input");
+ expect((await window.getByText('Logic').allInnerTexts()).at(0)).toBe("Logic");
+ expect((await window.getByText('Math').allInnerTexts()).at(0)).toBe("Math");
// Check add node to graph
- await plugin.click();
- await window.getByText('Output').click();
+ await (await plugin.all()).at(2)?.click();
+ await (await window.getByText('Output').all()).at(2)?.click();
await window.getByText('Output').first().click({button: 'right'});
expect((await window.locator('Output').allInnerTexts()).at(0)).toBe(undefined);
// Check add graph
await window.getByText('Graph').click();
- expect((await window.getByText('Graph', { exact: true }).allInnerTexts()).length).toBe(4);
+ expect((await window.getByText('Graph', { exact: true }).allInnerTexts()).length).toBe(2);
await window.getByTitle('Add Graph').getByRole('img').click();
- expect((await window.getByText('Graph', { exact: true }).allInnerTexts()).length).toBe(4);
+ expect((await window.getByText('Graph', { exact: true }).allInnerTexts()).length).toBe(2);
// Connect edges
- await graph.click({button: 'right'});
- await plugin.click();
+ await graph.click({button: 'right', position: {x: 100, y: 100}});
+ await (await plugin.all()).at(2)?.click();
await window.getByText('Output').click();
- await graph.click({button: 'right'});
- await window.getByText('input-plugin').first().click();
+ await graph.click({button: 'right', position: {x: 300, y: 600}});
+ await (await window.getByText('Input').all()).at(1)?.click({delay: 100});
await window.getByText('Input number').click();
await window.locator('css=div.svelvet-anchor').nth(1).dragTo(window.locator('css=div.svelvet-anchor').first());
- expect((await window.locator('css=div.output.normal').allInnerTexts()).at(0)).toBe("3");
+ expect((await window.locator('css=div.output.normal').allInnerTexts()).at(0)).toBe("0");
// close app
await electronApp.close()
From cfc4b605466da75ec230f9d095e6dcdad8dd94b1 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 27 Sep 2023 10:01:16 +0200
Subject: [PATCH 181/209] Rewrite media output id logic
---
src/electron/lib/api/apis/GraphApi.ts | 4 ++
.../lib/core-graph/CoreGraphManager.ts | 37 +++++++++++------
src/frontend/lib/api/apis/MediaClientApi.ts | 8 +++-
src/frontend/lib/stores/GraphStore.ts | 31 +++-----------
src/frontend/lib/stores/MediaStore.ts | 40 +++++++------------
src/frontend/ui/tiles/Assets.svelte | 4 +-
src/frontend/ui/tiles/Media.svelte | 38 +++++++++---------
src/frontend/ui/utils/graph/PluginNode.svelte | 6 +--
.../nodeUIComponents/dials/DiffDial.svelte | 5 ++-
.../nodeUIComponents/dials/TweakDial.svelte | 5 ++-
10 files changed, 87 insertions(+), 91 deletions(-)
diff --git a/src/electron/lib/api/apis/GraphApi.ts b/src/electron/lib/api/apis/GraphApi.ts
index ff2d72e9..1047a20c 100644
--- a/src/electron/lib/api/apis/GraphApi.ts
+++ b/src/electron/lib/api/apis/GraphApi.ts
@@ -95,4 +95,8 @@ export class GraphApi implements ElectronMainApi {
async updateUIPositions(graphUUID: UUID, positions: { [key: UUID]: SvelvetCanvasPos }) {
this._blix.graphManager.updateUIPositions(graphUUID, positions);
}
+
+ async getMediaOutputs(graphIds: UUID[]) {
+ return this._blix.graphManager.getMediaOutputs(graphIds);
+ }
}
diff --git a/src/electron/lib/core-graph/CoreGraphManager.ts b/src/electron/lib/core-graph/CoreGraphManager.ts
index d6b2a7d2..3e8341d9 100644
--- a/src/electron/lib/core-graph/CoreGraphManager.ts
+++ b/src/electron/lib/core-graph/CoreGraphManager.ts
@@ -72,13 +72,6 @@ export class CoreGraphManager {
execute: { graphUUID, node, pos },
revert: { graphUUID, nodeUUId: res.data.nodeId },
});
-
- if (node.signature === "blix.output") {
- this._outputIds[res.data.nodeId] = "default";
- this._mainWindow?.apis.mediaClientApi.onMediaOutputIdsChanged(
- new Set(Object.values(this._outputIds))
- );
- }
} else if (eventArgs) {
const { event } = eventArgs;
if (event?.element === "Node" && event.operation === "Add") {
@@ -123,6 +116,7 @@ export class CoreGraphManager {
}
delete this._outputIds[nodeUUID];
+ this._mainWindow?.apis.mediaClientApi.outputNodesChanged();
}
return res;
@@ -240,7 +234,9 @@ export class CoreGraphManager {
participant: CoreGraphUpdateParticipant,
eventArgs?: EventArgs
): QueryResponse {
- if (!this._graphs[graphUUID]) {
+ const graph = this._graphs[graphUUID];
+
+ if (!graph) {
return { status: "error", message: "Graph does not exist" };
}
@@ -257,11 +253,9 @@ export class CoreGraphManager {
const uiConfigs = this._toolbox.getNodeInstance(signature).uiConfigs;
const changes = nodeUIInputs.changes;
- if (signature === "blix.output") {
+ if (signature === "blix.output" && changes.includes("outputId")) {
this._outputIds[nodeUUID] = nodeUIInputs.inputs.outputId as string;
- this._mainWindow?.apis.mediaClientApi.onMediaOutputIdsChanged(
- new Set(Object.values(this._outputIds))
- );
+ this._mainWindow?.apis.mediaClientApi.outputNodesChanged();
}
let shouldUpdate = false;
@@ -422,6 +416,25 @@ export class CoreGraphManager {
return;
}
+ getMediaOutputs(graphIds: UUID[]) {
+ const outputs: { nodeId: string; mediaId: string }[] = [];
+
+ graphIds.forEach((graphId) => {
+ const graph = this._graphs[graphId];
+ if (graph) {
+ const outputNodes = graph.getOutputNodes;
+ Object.keys(outputNodes).forEach((outputNodeId) => {
+ const mediaOutputId = this._outputIds[outputNodeId];
+ if (mediaOutputId) {
+ outputs.push({ nodeId: outputNodeId, mediaId: mediaOutputId });
+ }
+ });
+ }
+ });
+
+ return outputs;
+ }
+
// ===============================================
// Graph Events
// ===============================================
diff --git a/src/frontend/lib/api/apis/MediaClientApi.ts b/src/frontend/lib/api/apis/MediaClientApi.ts
index 6a165678..c08f5e68 100644
--- a/src/frontend/lib/api/apis/MediaClientApi.ts
+++ b/src/frontend/lib/api/apis/MediaClientApi.ts
@@ -7,7 +7,11 @@ export class MediaClientApi implements ElectronWindowApi {
mediaStore.refreshStore(mediaOutput);
}
- public onMediaOutputIdsChanged(outputIds: Set) {
- mediaStore.updateOutputIds(outputIds);
+ // public onMediaOutputIdsChanged(outputIds: Set) {
+ // mediaStore.updateOutputIds(outputIds);
+ // }
+
+ public async outputNodesChanged() {
+ await mediaStore.outputNodesChanged();
}
}
diff --git a/src/frontend/lib/stores/GraphStore.ts b/src/frontend/lib/stores/GraphStore.ts
index 05db7e09..bf587d2f 100644
--- a/src/frontend/lib/stores/GraphStore.ts
+++ b/src/frontend/lib/stores/GraphStore.ts
@@ -17,18 +17,6 @@ import type { MediaOutputId } from "@shared/types/media";
import { tick } from "svelte";
import type { UUID } from "@shared/utils/UniqueEntity";
-// When the the CoreGraphApi type has to be imported into the backend
-// (WindowApi.ts) so that the API can be bound then it tries to import the type
-// below because the GraphStore gets used in the CoreGraphApi (its like one long
-// type dependency chain), this seems to cause some sort of duplicate export
-// issue originating from the svelvet node files when it tries to check the
-// types at compile time: node_modules/svelvet/dist/types/index.d.ts:4:1 - error
-// TS2308: Module './general' has already exported a member named
-// 'ActiveIntervals'. Consider explicitly re-exporting to resolve the ambiguity.
-
-// Not sure how to solve this at the moment, so had to add a temp fix below
-// unfortunately because of time constraints.
-
// import type { Connections } from "blix_svelvet";
// type Connections = (string | number | [string | number, string | number] | null)[];
@@ -116,25 +104,16 @@ export class GraphStore {
for (const input in inputs) {
if (!inputs.hasOwnProperty(input)) continue;
- let first = true;
- let oldState: unknown;
+
this.uiInputUnsubscribers[node].push(
inputs[input].subscribe((state) => {
- // Ensure the subscribe does not run when first created
- if (first) {
- first = false;
- oldState = state;
- } else if (state !== oldState) {
- // Prevent unnecessary updates when the value is the exact same as the prior
- // Only would then update if maybe the set function is used but old state === new state
- this.globalizeUIInputs(node, input).catch((err) => {
- return;
- });
- oldState = state;
- }
+ this.globalizeUIInputs(node, input).catch((err) => {
+ return;
+ });
})
);
}
+
// Update frontend
if (position)
this.uiPositionUnsubscribers[node].push(
diff --git a/src/frontend/lib/stores/MediaStore.ts b/src/frontend/lib/stores/MediaStore.ts
index 2adb719b..e1341cc5 100644
--- a/src/frontend/lib/stores/MediaStore.ts
+++ b/src/frontend/lib/stores/MediaStore.ts
@@ -1,6 +1,7 @@
import type { DisplayableMediaOutput, MediaOutputId } from "@shared/types/media";
import { derived, get, writable } from "svelte/store";
import { commandStore } from "./CommandStore";
+import { projectsStore } from "./ProjectStore";
type MediaOutputs = {
[key: MediaOutputId]: DisplayableMediaOutput;
@@ -8,27 +9,19 @@ type MediaOutputs = {
class MediaStore {
private store = writable({});
- private outputIds = writable>();
+ private outputIds = writable<{ nodeId: string; mediaId: string }[]>([]);
public refreshStore(media: DisplayableMediaOutput) {
- // Refresh media store
this.store.update((mediaOutputs) => {
mediaOutputs[media.outputId] = media;
return mediaOutputs;
});
}
- public updateOutputIds(ids: Set) {
- const oldIds = get(this.outputIds);
- if (!oldIds) this.outputIds.set(ids);
-
- // Wont set store and notfiy subscribers if the value allOutputIds have remained the same.
- // This is the case where the undo/redo system willchange something and then the stores see a change and do their
- // own update but their update wont matter.
-
- if (oldIds && ids && !equivSets(oldIds, ids)) {
- this.outputIds.set(ids);
- }
+ public async outputNodesChanged() {
+ const projectGraphIds = get(projectsStore.activeProjectGraphIds);
+ const mediaOutputs = await window.apis.graphApi.getMediaOutputs(projectGraphIds);
+ this.outputIds.set(mediaOutputs);
}
// Stop listening for graph changes
@@ -78,7 +71,8 @@ class MediaStore {
public getMediaOutputIdsReactive() {
return derived(this.outputIds, (store) => {
- return store;
+ const set = new Set(store.map((id) => id.mediaId));
+ return Array.from(set);
});
}
@@ -89,21 +83,15 @@ class MediaStore {
});
}
- public get subscribe() {
- return this.store.subscribe;
+ public getMediaOutputId(nodeId: string) {
+ return get(this.outputIds).find((id) => id.nodeId === nodeId)?.mediaId || null;
}
-}
-function equivSets(s1: Set, s2: Set): boolean {
- if (s1.size !== s2.size) return false;
- for (const element of s1) {
- if (!s2.has(element)) {
- return false;
- } else {
- s2.delete(element);
- }
+ public get subscribe() {
+ return this.store.subscribe;
}
- return true;
}
export const mediaStore = new MediaStore();
+
+export const nodeIdLastClicked = writable("");
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index fca51b40..9193f0b6 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -110,7 +110,7 @@
}
-
+
{#if Object.keys($cacheStore).length > 0}
{#each Object.keys($cacheStore) as uuid}
@@ -178,7 +178,7 @@
;
$: if ($mediaOutputIds) {
selectedItems = Array.from($mediaOutputIds)
@@ -55,31 +56,32 @@
.map((id) => ({ id, title: id }));
}
- let mediaId = writable("");
- let oldMediaId: string | null = null;
- let webview: WebView;
+ // Changes output on output node click
+ // $: if ($nodeIdLastClicked) {
+ // const mediaOutputId = mediaStore.getMediaOutputId($nodeIdLastClicked);
+ // if (mediaOutputId) {
+ // selectedMediaId.set(mediaOutputId);
+ // }
+ // }
- const unsubMedia = mediaId.subscribe((newMediaId) => {
- // console.log("SUBSCRIBE MEDIA ID", oldMediaId, newMediaId);
+ const unsubMedia = selectedMediaId.subscribe((newMediaId) => {
connectNewMedia(oldMediaId, newMediaId);
oldMediaId = newMediaId;
});
- // TODO: Add toggle that auto-switches media to the last selected output node
- let selectedNode: { graphUUID: GraphUUID; outNode: GraphNodeUUID } | null;
- let media: Readable;
-
async function connectNewMedia(oldMediaId: string | null, mediaId: string) {
if (oldMediaId !== null) {
- // console.log("STOPPING OLD", oldMediaId);
await mediaStore.stopMediaReactive(oldMediaId);
}
- media = await mediaStore.getMediaReactive(mediaId);
- // console.log("CONNECT NEW MEDIA", mediaId, media);
+ if (mediaId) {
+ media = await mediaStore.getMediaReactive(mediaId);
+ } else {
+ media = writable(null);
+ }
}
onDestroy(async () => {
- await mediaStore.stopMediaReactive($mediaId);
+ await mediaStore.stopMediaReactive($selectedMediaId);
unsubMedia();
});
@@ -135,7 +137,7 @@
@@ -175,7 +177,7 @@
/> -->
{:else}
-
+
No content!
Add an Output node to the graph to create a media output
diff --git a/src/frontend/ui/utils/graph/PluginNode.svelte b/src/frontend/ui/utils/graph/PluginNode.svelte
index e6f0f562..bff99fc4 100644
--- a/src/frontend/ui/utils/graph/PluginNode.svelte
+++ b/src/frontend/ui/utils/graph/PluginNode.svelte
@@ -7,6 +7,7 @@
import { graphMall } from "../../../lib/stores/GraphStore";
import { colord, extend } from "colord";
import a11yPlugin from "colord/plugins/a11y";
+ import { nodeIdLastClicked } from "../../../lib/stores/MediaStore";
extend([a11yPlugin]);
@@ -59,13 +60,12 @@
}
async function nodeClicked(e: CustomEvent) {
- console.log("NODE CLICKED");
if (e.detail.e.button === 2) {
- console.log("DELETE NODE EVENT");
-
const [_2, ...nodeUUIDParts] = e.detail.node.id.split("_");
const nodeUUID = nodeUUIDParts.join("_");
await window.apis.graphApi.removeNode(graphId, nodeUUID);
+ } else {
+ nodeIdLastClicked.set(node.uuid);
}
}
diff --git a/src/frontend/ui/utils/graph/nodeUIComponents/dials/DiffDial.svelte b/src/frontend/ui/utils/graph/nodeUIComponents/dials/DiffDial.svelte
index 37b03cdd..a50c548e 100644
--- a/src/frontend/ui/utils/graph/nodeUIComponents/dials/DiffDial.svelte
+++ b/src/frontend/ui/utils/graph/nodeUIComponents/dials/DiffDial.svelte
@@ -3,10 +3,13 @@
import type { UIComponentConfig, UIComponentProps } from "@shared/ui/NodeUITypes";
import Dial from "./Dial.svelte";
import { faCodeCompare } from "@fortawesome/free-solid-svg-icons";
+ import { blixStore } from "../../../../../lib/stores/BlixStore";
export let props: UIComponentProps;
export let inputStore: UIValueStore;
export let config: UIComponentConfig;
-
+
+
+
diff --git a/src/frontend/ui/utils/graph/nodeUIComponents/dials/TweakDial.svelte b/src/frontend/ui/utils/graph/nodeUIComponents/dials/TweakDial.svelte
index 7ea70e4c..ff4d7154 100644
--- a/src/frontend/ui/utils/graph/nodeUIComponents/dials/TweakDial.svelte
+++ b/src/frontend/ui/utils/graph/nodeUIComponents/dials/TweakDial.svelte
@@ -3,10 +3,13 @@
import type { UIComponentConfig, UIComponentProps } from "@shared/ui/NodeUITypes";
import { faCogs } from "@fortawesome/free-solid-svg-icons";
import Dial from "./Dial.svelte";
+ import { blixStore } from "../../../../../lib/stores/BlixStore";
export let props: UIComponentProps;
export let inputStore: UIValueStore;
export let config: UIComponentConfig;
-
+
+
+
From d9fdb29125962a70bed67232764715d5e00260b3 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 27 Sep 2023 10:10:56 +0200
Subject: [PATCH 182/209] Fix test
---
.../electron/lib/core-graph/CoreGraphManager.spec.ts | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts b/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
index 7acfd71e..f5521b4b 100644
--- a/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
+++ b/tests/unit-tests/electron/lib/core-graph/CoreGraphManager.spec.ts
@@ -24,6 +24,9 @@ const mainWindow: MainWindow = {
},
graphClientApi: {
graphRemoved: jest.fn(),
+ },
+ mediaClientApi: {
+ outputNodesChanged: jest.fn(),
}
}
From cc204200accd43f17d36ad9894168d8a3004239e Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 27 Sep 2023 11:21:14 +0200
Subject: [PATCH 183/209] General UI improvements
---
src/frontend/ui/base/layout/Layout.svelte | 38 ++++++++++++++++++-
src/frontend/ui/base/layout/Panel.svelte | 4 +-
src/frontend/ui/base/layout/PanelBlip.svelte | 4 +-
.../ui/base/layout/TileSelector.svelte | 34 ++++++++++-------
src/frontend/ui/tiles/Assets.svelte | 9 +++--
src/frontend/ui/tiles/Graph.svelte | 38 ++++++++++++++++++-
src/frontend/ui/tiles/Media.svelte | 11 +++---
.../ui/utils/graph/SelectionBoxItem.svelte | 2 +-
8 files changed, 109 insertions(+), 31 deletions(-)
diff --git a/src/frontend/ui/base/layout/Layout.svelte b/src/frontend/ui/base/layout/Layout.svelte
index 5f2c46d1..91e9392a 100644
--- a/src/frontend/ui/base/layout/Layout.svelte
+++ b/src/frontend/ui/base/layout/Layout.svelte
@@ -1,6 +1,8 @@
@@ -8,14 +10,48 @@
{#key $projectsStore.activeProject.id}
{/key}
+ {:else}
+
+
+
No projects!
+
Create a project to begin creating
+
{/if}
-
diff --git a/src/frontend/ui/base/layout/Panel.svelte b/src/frontend/ui/base/layout/Panel.svelte
index f6a2e4bc..2ef431f7 100644
--- a/src/frontend/ui/base/layout/Panel.svelte
+++ b/src/frontend/ui/base/layout/Panel.svelte
@@ -388,12 +388,12 @@
}
.splitpanes.main-theme .splitpanes__splitter:active {
- background-color: rgb(244 63 94 / 0.7);
+ background-color: rgb(244, 63, 94);
border: none;
}
.splitpanes.main-theme .splitpanes__splitter:hover {
- background-color: rgb(244 63 94 / 0.7);
+ background-color: rgb(244, 63, 94);
border: none;
}
diff --git a/src/frontend/ui/base/layout/PanelBlip.svelte b/src/frontend/ui/base/layout/PanelBlip.svelte
index a0e560d2..823d9157 100644
--- a/src/frontend/ui/base/layout/PanelBlip.svelte
+++ b/src/frontend/ui/base/layout/PanelBlip.svelte
@@ -124,7 +124,7 @@
height: var(--width);
background-color: transparent;
- color: #f38ba8;
+ color: rgb(244, 63, 94);
text-align: center;
font-size: 0.8em;
@@ -134,7 +134,7 @@
z-index: 10000;
}
.blip:hover {
- background-color: #f38ba8;
+ background-color: rgb(244, 63, 94);
cursor: crosshair;
}
diff --git a/src/frontend/ui/base/layout/TileSelector.svelte b/src/frontend/ui/base/layout/TileSelector.svelte
index da565f28..afd988f4 100644
--- a/src/frontend/ui/base/layout/TileSelector.svelte
+++ b/src/frontend/ui/base/layout/TileSelector.svelte
@@ -19,6 +19,7 @@
faPuzzlePiece,
faGlobe,
faCamera,
+ faBrush,
} from "@fortawesome/free-solid-svg-icons";
import { tileStore } from "../../../lib/stores/TileStore";
@@ -36,17 +37,17 @@
tileDict["graph"] = { displayName: "graph", description: null, icon: faDiagramProject };
tileDict["media"] = { displayName: "media", description: null, icon: faImage };
tileDict["webcamera"] = { displayName: "camera", description: null, icon: faCamera };
- tileDict["inspector"] = { displayName: "inspector", description: null, icon: faMagnifyingGlass };
+ // tileDict["inspector"] = { displayName: "inspector", description: null, icon: faMagnifyingGlass };
tileDict["webview"] = { displayName: "webview", description: null, icon: faPuzzlePiece };
tileDict["browser"] = { displayName: "browser", description: null, icon: faGlobe };
- tileDict["promptBox"] = { displayName: "promptBox", description: null, icon: faTerminal };
- tileDict["shortcutSettings"] = {
- displayName: "shortcutSettings",
- description: null,
- icon: faKeyboard,
- };
+ // tileDict["promptBox"] = { displayName: "promptBox", description: null, icon: faTerminal };
+ // tileDict["shortcutSettings"] = {
+ // displayName: "shortcutSettings",
+ // description: null,
+ // icon: faKeyboard,
+ // };
tileDict["debug"] = { displayName: "debug", description: null, icon: faCode };
- tileDict["assets"] = { displayName: "assets", description: null, icon: faImage };
+ tileDict["assets"] = { displayName: "assets", description: null, icon: faBrush };
for (const tile in tiles) {
tileDict[tiles[tile].signature] = {
@@ -64,8 +65,13 @@
}
-
+
{#if open}
@@ -94,17 +100,16 @@
position: absolute;
top: 0px;
left: 0.8rem;
- width: 1.6rem;
- height: 1rem;
+ /* height: 1.3rem; */
z-index: 40;
- padding: 0px;
- padding-top: 0.1rem;
+ /* padding: 0px;
+ padding-top: 0.1rem; */
border-radius: 0px 0px 0.4rem 0.4rem;
background-color: #11111b;
overflow: hidden;
- font-size: 0.6em;
+ font-size: 0.7em;
text-align: center;
cursor: pointer;
@@ -158,6 +163,7 @@
.tileOption {
cursor: pointer;
+ border-radius: 7px;
}
.innerTileOption {
diff --git a/src/frontend/ui/tiles/Assets.svelte b/src/frontend/ui/tiles/Assets.svelte
index 9193f0b6..e00a0552 100644
--- a/src/frontend/ui/tiles/Assets.svelte
+++ b/src/frontend/ui/tiles/Assets.svelte
@@ -260,12 +260,13 @@
}
.placeholder {
- padding-top: 140px;
- text-align: center;
- display: inline-block;
width: 100%;
height: 100%;
- margin-top: 1em;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
h1 {
font-size: 1.5em;
diff --git a/src/frontend/ui/tiles/Graph.svelte b/src/frontend/ui/tiles/Graph.svelte
index 26dec67d..f7e1a021 100644
--- a/src/frontend/ui/tiles/Graph.svelte
+++ b/src/frontend/ui/tiles/Graph.svelte
@@ -16,6 +16,8 @@
import { projectsStore } from "../../lib/stores/ProjectStore";
import { get } from "svelte/store";
import type { SelectionBoxItem } from "../../types/selection-box";
+ import { faDiagramProject } from "@fortawesome/free-solid-svg-icons";
+ import Fa from "svelte-fa";
// import { type Anchor } from "blix_svelvet/dist/types"; // TODO: Use to createEdge
// TODO: Abstract panelId to use a generic UUID
@@ -392,10 +394,14 @@
{/key} -->
{:else}
-
No graphs
+
+
+
No graphs!
+
Create a graph to start a workflow
+
{/if}
-
diff --git a/src/frontend/ui/tiles/Media.svelte b/src/frontend/ui/tiles/Media.svelte
index c6e6188c..6ddcf0bd 100644
--- a/src/frontend/ui/tiles/Media.svelte
+++ b/src/frontend/ui/tiles/Media.svelte
@@ -138,7 +138,7 @@
dispatch('removeItem', { id: item.id })}"
on:keydown="{null}"
>
From 0aaa4204c4276fe31f8ec775c9ad2bbf7c9061c9 Mon Sep 17 00:00:00 2001
From: Armand Krynauw
Date: Wed, 27 Sep 2023 11:50:32 +0200
Subject: [PATCH 184/209] Clean up some UI
---
.../ui/utils/graph/NodeUIFragment.svelte | 26 +++++++++++++++++--
.../graph/nodeUIComponents/Buffer.svelte | 5 +---
.../nodeUIComponents/dials/DiffDial.svelte | 4 +--
.../nodeUIComponents/dials/TweakDial.svelte | 4 +--
4 files changed, 27 insertions(+), 12 deletions(-)
diff --git a/src/frontend/ui/utils/graph/NodeUIFragment.svelte b/src/frontend/ui/utils/graph/NodeUIFragment.svelte
index 43cb9ca5..2fcd4dc8 100644
--- a/src/frontend/ui/utils/graph/NodeUIFragment.svelte
+++ b/src/frontend/ui/utils/graph/NodeUIFragment.svelte
@@ -1,7 +1,14 @@
{#if ui}
@@ -35,7 +57,7 @@
{/each}
{:else if ui.type === "leaf"}
-
+
-
+