@nodetool-ai/base-nodes 0.7.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +270 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audio-wav.d.ts +66 -0
- package/dist/lib/audio-wav.d.ts.map +1 -0
- package/dist/lib/audio-wav.js +215 -0
- package/dist/lib/audio-wav.js.map +1 -0
- package/dist/nodes/agents.d.ts +253 -0
- package/dist/nodes/agents.d.ts.map +1 -0
- package/dist/nodes/agents.js +2784 -0
- package/dist/nodes/agents.js.map +1 -0
- package/dist/nodes/anthropic.d.ts +26 -0
- package/dist/nodes/anthropic.d.ts.map +1 -0
- package/dist/nodes/anthropic.js +133 -0
- package/dist/nodes/anthropic.js.map +1 -0
- package/dist/nodes/apify.d.ts +120 -0
- package/dist/nodes/apify.d.ts.map +1 -0
- package/dist/nodes/apify.js +618 -0
- package/dist/nodes/apify.js.map +1 -0
- package/dist/nodes/audio.d.ts +301 -0
- package/dist/nodes/audio.d.ts.map +1 -0
- package/dist/nodes/audio.js +1274 -0
- package/dist/nodes/audio.js.map +1 -0
- package/dist/nodes/code-node.d.ts +34 -0
- package/dist/nodes/code-node.d.ts.map +1 -0
- package/dist/nodes/code-node.js +343 -0
- package/dist/nodes/code-node.js.map +1 -0
- package/dist/nodes/code.d.ts +301 -0
- package/dist/nodes/code.d.ts.map +1 -0
- package/dist/nodes/code.js +850 -0
- package/dist/nodes/code.js.map +1 -0
- package/dist/nodes/compare.d.ts +18 -0
- package/dist/nodes/compare.d.ts.map +1 -0
- package/dist/nodes/compare.js +122 -0
- package/dist/nodes/compare.js.map +1 -0
- package/dist/nodes/constant.d.ts +315 -0
- package/dist/nodes/constant.d.ts.map +1 -0
- package/dist/nodes/constant.js +715 -0
- package/dist/nodes/constant.js.map +1 -0
- package/dist/nodes/control.d.ts +83 -0
- package/dist/nodes/control.d.ts.map +1 -0
- package/dist/nodes/control.js +212 -0
- package/dist/nodes/control.js.map +1 -0
- package/dist/nodes/data.d.ts +369 -0
- package/dist/nodes/data.d.ts.map +1 -0
- package/dist/nodes/data.js +1590 -0
- package/dist/nodes/data.js.map +1 -0
- package/dist/nodes/document.d.ts +134 -0
- package/dist/nodes/document.d.ts.map +1 -0
- package/dist/nodes/document.js +984 -0
- package/dist/nodes/document.js.map +1 -0
- package/dist/nodes/extended-placeholders.d.ts +6 -0
- package/dist/nodes/extended-placeholders.d.ts.map +1 -0
- package/dist/nodes/extended-placeholders.js +19 -0
- package/dist/nodes/extended-placeholders.js.map +1 -0
- package/dist/nodes/gemini.d.ts +109 -0
- package/dist/nodes/gemini.d.ts.map +1 -0
- package/dist/nodes/gemini.js +861 -0
- package/dist/nodes/gemini.js.map +1 -0
- package/dist/nodes/generators.d.ts +84 -0
- package/dist/nodes/generators.d.ts.map +1 -0
- package/dist/nodes/generators.js +937 -0
- package/dist/nodes/generators.js.map +1 -0
- package/dist/nodes/image.d.ts +232 -0
- package/dist/nodes/image.d.ts.map +1 -0
- package/dist/nodes/image.js +1395 -0
- package/dist/nodes/image.js.map +1 -0
- package/dist/nodes/input.d.ts +445 -0
- package/dist/nodes/input.d.ts.map +1 -0
- package/dist/nodes/input.js +1459 -0
- package/dist/nodes/input.js.map +1 -0
- package/dist/nodes/kie-dynamic.d.ts +31 -0
- package/dist/nodes/kie-dynamic.d.ts.map +1 -0
- package/dist/nodes/kie-dynamic.js +270 -0
- package/dist/nodes/kie-dynamic.js.map +1 -0
- package/dist/nodes/lib-audio-dsp.d.ts +85 -0
- package/dist/nodes/lib-audio-dsp.d.ts.map +1 -0
- package/dist/nodes/lib-audio-dsp.js +450 -0
- package/dist/nodes/lib-audio-dsp.js.map +1 -0
- package/dist/nodes/lib-audio-effects.d.ts +116 -0
- package/dist/nodes/lib-audio-effects.d.ts.map +1 -0
- package/dist/nodes/lib-audio-effects.js +905 -0
- package/dist/nodes/lib-audio-effects.js.map +1 -0
- package/dist/nodes/lib-browser.d.ts +99 -0
- package/dist/nodes/lib-browser.d.ts.map +1 -0
- package/dist/nodes/lib-browser.js +692 -0
- package/dist/nodes/lib-browser.js.map +1 -0
- package/dist/nodes/lib-charts.d.ts +20 -0
- package/dist/nodes/lib-charts.d.ts.map +1 -0
- package/dist/nodes/lib-charts.js +245 -0
- package/dist/nodes/lib-charts.js.map +1 -0
- package/dist/nodes/lib-datetime.d.ts +108 -0
- package/dist/nodes/lib-datetime.d.ts.map +1 -0
- package/dist/nodes/lib-datetime.js +421 -0
- package/dist/nodes/lib-datetime.js.map +1 -0
- package/dist/nodes/lib-doc-convert.d.ts +19 -0
- package/dist/nodes/lib-doc-convert.d.ts.map +1 -0
- package/dist/nodes/lib-doc-convert.js +257 -0
- package/dist/nodes/lib-doc-convert.js.map +1 -0
- package/dist/nodes/lib-doc-transform.d.ts +39 -0
- package/dist/nodes/lib-doc-transform.d.ts.map +1 -0
- package/dist/nodes/lib-doc-transform.js +335 -0
- package/dist/nodes/lib-doc-transform.js.map +1 -0
- package/dist/nodes/lib-docx.d.ts +106 -0
- package/dist/nodes/lib-docx.d.ts.map +1 -0
- package/dist/nodes/lib-docx.js +595 -0
- package/dist/nodes/lib-docx.js.map +1 -0
- package/dist/nodes/lib-epub.d.ts +55 -0
- package/dist/nodes/lib-epub.d.ts.map +1 -0
- package/dist/nodes/lib-epub.js +220 -0
- package/dist/nodes/lib-epub.js.map +1 -0
- package/dist/nodes/lib-excel.d.ts +74 -0
- package/dist/nodes/lib-excel.d.ts.map +1 -0
- package/dist/nodes/lib-excel.js +448 -0
- package/dist/nodes/lib-excel.js.map +1 -0
- package/dist/nodes/lib-graphql.d.ts +66 -0
- package/dist/nodes/lib-graphql.d.ts.map +1 -0
- package/dist/nodes/lib-graphql.js +333 -0
- package/dist/nodes/lib-graphql.js.map +1 -0
- package/dist/nodes/lib-grid.d.ts +27 -0
- package/dist/nodes/lib-grid.d.ts.map +1 -0
- package/dist/nodes/lib-grid.js +172 -0
- package/dist/nodes/lib-grid.js.map +1 -0
- package/dist/nodes/lib-html-parse.d.ts +108 -0
- package/dist/nodes/lib-html-parse.d.ts.map +1 -0
- package/dist/nodes/lib-html-parse.js +365 -0
- package/dist/nodes/lib-html-parse.js.map +1 -0
- package/dist/nodes/lib-http.d.ts +98 -0
- package/dist/nodes/lib-http.d.ts.map +1 -0
- package/dist/nodes/lib-http.js +267 -0
- package/dist/nodes/lib-http.js.map +1 -0
- package/dist/nodes/lib-image-color-grading.d.ts +3 -0
- package/dist/nodes/lib-image-color-grading.d.ts.map +1 -0
- package/dist/nodes/lib-image-color-grading.js +1386 -0
- package/dist/nodes/lib-image-color-grading.js.map +1 -0
- package/dist/nodes/lib-image-draw.d.ts +3 -0
- package/dist/nodes/lib-image-draw.d.ts.map +1 -0
- package/dist/nodes/lib-image-draw.js +470 -0
- package/dist/nodes/lib-image-draw.js.map +1 -0
- package/dist/nodes/lib-image-enhance.d.ts +3 -0
- package/dist/nodes/lib-image-enhance.d.ts.map +1 -0
- package/dist/nodes/lib-image-enhance.js +731 -0
- package/dist/nodes/lib-image-enhance.js.map +1 -0
- package/dist/nodes/lib-image-filter.d.ts +3 -0
- package/dist/nodes/lib-image-filter.d.ts.map +1 -0
- package/dist/nodes/lib-image-filter.js +684 -0
- package/dist/nodes/lib-image-filter.js.map +1 -0
- package/dist/nodes/lib-image-utils.d.ts +24 -0
- package/dist/nodes/lib-image-utils.d.ts.map +1 -0
- package/dist/nodes/lib-image-utils.js +150 -0
- package/dist/nodes/lib-image-utils.js.map +1 -0
- package/dist/nodes/lib-mail.d.ts +74 -0
- package/dist/nodes/lib-mail.d.ts.map +1 -0
- package/dist/nodes/lib-mail.js +452 -0
- package/dist/nodes/lib-mail.js.map +1 -0
- package/dist/nodes/lib-markdown.d.ts +70 -0
- package/dist/nodes/lib-markdown.d.ts.map +1 -0
- package/dist/nodes/lib-markdown.js +236 -0
- package/dist/nodes/lib-markdown.js.map +1 -0
- package/dist/nodes/lib-nlp.d.ts +98 -0
- package/dist/nodes/lib-nlp.d.ts.map +1 -0
- package/dist/nodes/lib-nlp.js +374 -0
- package/dist/nodes/lib-nlp.js.map +1 -0
- package/dist/nodes/lib-notion.d.ts +99 -0
- package/dist/nodes/lib-notion.d.ts.map +1 -0
- package/dist/nodes/lib-notion.js +601 -0
- package/dist/nodes/lib-notion.js.map +1 -0
- package/dist/nodes/lib-ocr.d.ts +31 -0
- package/dist/nodes/lib-ocr.d.ts.map +1 -0
- package/dist/nodes/lib-ocr.js +143 -0
- package/dist/nodes/lib-ocr.js.map +1 -0
- package/dist/nodes/lib-os.d.ts +312 -0
- package/dist/nodes/lib-os.d.ts.map +1 -0
- package/dist/nodes/lib-os.js +909 -0
- package/dist/nodes/lib-os.js.map +1 -0
- package/dist/nodes/lib-pdf.d.ts +142 -0
- package/dist/nodes/lib-pdf.d.ts.map +1 -0
- package/dist/nodes/lib-pdf.js +871 -0
- package/dist/nodes/lib-pdf.js.map +1 -0
- package/dist/nodes/lib-pptx.d.ts +29 -0
- package/dist/nodes/lib-pptx.d.ts.map +1 -0
- package/dist/nodes/lib-pptx.js +122 -0
- package/dist/nodes/lib-pptx.js.map +1 -0
- package/dist/nodes/lib-rss.d.ts +31 -0
- package/dist/nodes/lib-rss.d.ts.map +1 -0
- package/dist/nodes/lib-rss.js +163 -0
- package/dist/nodes/lib-rss.js.map +1 -0
- package/dist/nodes/lib-s3.d.ts +112 -0
- package/dist/nodes/lib-s3.d.ts.map +1 -0
- package/dist/nodes/lib-s3.js +521 -0
- package/dist/nodes/lib-s3.js.map +1 -0
- package/dist/nodes/lib-secret.d.ts +15 -0
- package/dist/nodes/lib-secret.d.ts.map +1 -0
- package/dist/nodes/lib-secret.js +42 -0
- package/dist/nodes/lib-secret.js.map +1 -0
- package/dist/nodes/lib-sqlite.d.ts +102 -0
- package/dist/nodes/lib-sqlite.d.ts.map +1 -0
- package/dist/nodes/lib-sqlite.js +532 -0
- package/dist/nodes/lib-sqlite.js.map +1 -0
- package/dist/nodes/lib-supabase.d.ts +86 -0
- package/dist/nodes/lib-supabase.d.ts.map +1 -0
- package/dist/nodes/lib-supabase.js +425 -0
- package/dist/nodes/lib-supabase.js.map +1 -0
- package/dist/nodes/lib-svg.d.ts +199 -0
- package/dist/nodes/lib-svg.d.ts.map +1 -0
- package/dist/nodes/lib-svg.js +1027 -0
- package/dist/nodes/lib-svg.js.map +1 -0
- package/dist/nodes/lib-tensorflow.d.ts +71 -0
- package/dist/nodes/lib-tensorflow.d.ts.map +1 -0
- package/dist/nodes/lib-tensorflow.js +351 -0
- package/dist/nodes/lib-tensorflow.js.map +1 -0
- package/dist/nodes/lib-twilio.d.ts +71 -0
- package/dist/nodes/lib-twilio.d.ts.map +1 -0
- package/dist/nodes/lib-twilio.js +469 -0
- package/dist/nodes/lib-twilio.js.map +1 -0
- package/dist/nodes/lib-validate.d.ts +84 -0
- package/dist/nodes/lib-validate.d.ts.map +1 -0
- package/dist/nodes/lib-validate.js +215 -0
- package/dist/nodes/lib-validate.js.map +1 -0
- package/dist/nodes/lib-video-download.d.ts +26 -0
- package/dist/nodes/lib-video-download.d.ts.map +1 -0
- package/dist/nodes/lib-video-download.js +299 -0
- package/dist/nodes/lib-video-download.js.map +1 -0
- package/dist/nodes/messaging.d.ts +88 -0
- package/dist/nodes/messaging.d.ts.map +1 -0
- package/dist/nodes/messaging.js +418 -0
- package/dist/nodes/messaging.js.map +1 -0
- package/dist/nodes/mistral.d.ts +83 -0
- package/dist/nodes/mistral.d.ts.map +1 -0
- package/dist/nodes/mistral.js +450 -0
- package/dist/nodes/mistral.js.map +1 -0
- package/dist/nodes/model3d/analysis.d.ts +13 -0
- package/dist/nodes/model3d/analysis.d.ts.map +1 -0
- package/dist/nodes/model3d/analysis.js +36 -0
- package/dist/nodes/model3d/analysis.js.map +1 -0
- package/dist/nodes/model3d/base.d.ts +11 -0
- package/dist/nodes/model3d/base.d.ts.map +1 -0
- package/dist/nodes/model3d/base.js +20 -0
- package/dist/nodes/model3d/base.js.map +1 -0
- package/dist/nodes/model3d/boolean-ops.d.ts +2 -0
- package/dist/nodes/model3d/boolean-ops.d.ts.map +1 -0
- package/dist/nodes/model3d/boolean-ops.js +172 -0
- package/dist/nodes/model3d/boolean-ops.js.map +1 -0
- package/dist/nodes/model3d/cleanup.d.ts +47 -0
- package/dist/nodes/model3d/cleanup.d.ts.map +1 -0
- package/dist/nodes/model3d/cleanup.js +136 -0
- package/dist/nodes/model3d/cleanup.js.map +1 -0
- package/dist/nodes/model3d/convert.d.ts +14 -0
- package/dist/nodes/model3d/convert.d.ts.map +1 -0
- package/dist/nodes/model3d/convert.js +64 -0
- package/dist/nodes/model3d/convert.js.map +1 -0
- package/dist/nodes/model3d/defaults.d.ts +34 -0
- package/dist/nodes/model3d/defaults.d.ts.map +1 -0
- package/dist/nodes/model3d/defaults.js +28 -0
- package/dist/nodes/model3d/defaults.js.map +1 -0
- package/dist/nodes/model3d/document-ops.d.ts +4 -0
- package/dist/nodes/model3d/document-ops.d.ts.map +1 -0
- package/dist/nodes/model3d/document-ops.js +40 -0
- package/dist/nodes/model3d/document-ops.js.map +1 -0
- package/dist/nodes/model3d/generation.d.ts +39 -0
- package/dist/nodes/model3d/generation.d.ts.map +1 -0
- package/dist/nodes/model3d/generation.js +179 -0
- package/dist/nodes/model3d/generation.js.map +1 -0
- package/dist/nodes/model3d/glb.d.ts +29 -0
- package/dist/nodes/model3d/glb.d.ts.map +1 -0
- package/dist/nodes/model3d/glb.js +255 -0
- package/dist/nodes/model3d/glb.js.map +1 -0
- package/dist/nodes/model3d/index.d.ts +20 -0
- package/dist/nodes/model3d/index.d.ts.map +1 -0
- package/dist/nodes/model3d/index.js +38 -0
- package/dist/nodes/model3d/index.js.map +1 -0
- package/dist/nodes/model3d/io.d.ts +38 -0
- package/dist/nodes/model3d/io.d.ts.map +1 -0
- package/dist/nodes/model3d/io.js +162 -0
- package/dist/nodes/model3d/io.js.map +1 -0
- package/dist/nodes/model3d/mesh-ops.d.ts +40 -0
- package/dist/nodes/model3d/mesh-ops.d.ts.map +1 -0
- package/dist/nodes/model3d/mesh-ops.js +775 -0
- package/dist/nodes/model3d/mesh-ops.js.map +1 -0
- package/dist/nodes/model3d/nodes.d.ts +2 -0
- package/dist/nodes/model3d/nodes.d.ts.map +1 -0
- package/dist/nodes/model3d/nodes.js +2 -0
- package/dist/nodes/model3d/nodes.js.map +1 -0
- package/dist/nodes/model3d/ops.d.ts +37 -0
- package/dist/nodes/model3d/ops.d.ts.map +1 -0
- package/dist/nodes/model3d/ops.js +137 -0
- package/dist/nodes/model3d/ops.js.map +1 -0
- package/dist/nodes/model3d/transforms.d.ts +49 -0
- package/dist/nodes/model3d/transforms.d.ts.map +1 -0
- package/dist/nodes/model3d/transforms.js +142 -0
- package/dist/nodes/model3d/transforms.js.map +1 -0
- package/dist/nodes/model3d/types.d.ts +81 -0
- package/dist/nodes/model3d/types.d.ts.map +1 -0
- package/dist/nodes/model3d/types.js +2 -0
- package/dist/nodes/model3d/types.js.map +1 -0
- package/dist/nodes/model3d/utils.d.ts +34 -0
- package/dist/nodes/model3d/utils.d.ts.map +1 -0
- package/dist/nodes/model3d/utils.js +185 -0
- package/dist/nodes/model3d/utils.js.map +1 -0
- package/dist/nodes/model3d.d.ts +2 -0
- package/dist/nodes/model3d.d.ts.map +1 -0
- package/dist/nodes/model3d.js +2 -0
- package/dist/nodes/model3d.js.map +1 -0
- package/dist/nodes/openai.d.ts +172 -0
- package/dist/nodes/openai.d.ts.map +1 -0
- package/dist/nodes/openai.js +1294 -0
- package/dist/nodes/openai.js.map +1 -0
- package/dist/nodes/output.d.ts +29 -0
- package/dist/nodes/output.d.ts.map +1 -0
- package/dist/nodes/output.js +87 -0
- package/dist/nodes/output.js.map +1 -0
- package/dist/nodes/sandbox.d.ts +67 -0
- package/dist/nodes/sandbox.d.ts.map +1 -0
- package/dist/nodes/sandbox.js +681 -0
- package/dist/nodes/sandbox.js.map +1 -0
- package/dist/nodes/search.d.ts +117 -0
- package/dist/nodes/search.d.ts.map +1 -0
- package/dist/nodes/search.js +607 -0
- package/dist/nodes/search.js.map +1 -0
- package/dist/nodes/team.d.ts +74 -0
- package/dist/nodes/team.d.ts.map +1 -0
- package/dist/nodes/team.js +304 -0
- package/dist/nodes/team.js.map +1 -0
- package/dist/nodes/text-extra.d.ts +633 -0
- package/dist/nodes/text-extra.d.ts.map +1 -0
- package/dist/nodes/text-extra.js +2226 -0
- package/dist/nodes/text-extra.js.map +1 -0
- package/dist/nodes/tool-agents.d.ts +334 -0
- package/dist/nodes/tool-agents.d.ts.map +1 -0
- package/dist/nodes/tool-agents.js +1861 -0
- package/dist/nodes/tool-agents.js.map +1 -0
- package/dist/nodes/triggers.d.ts +107 -0
- package/dist/nodes/triggers.d.ts.map +1 -0
- package/dist/nodes/triggers.js +706 -0
- package/dist/nodes/triggers.js.map +1 -0
- package/dist/nodes/vector.d.ts +168 -0
- package/dist/nodes/vector.d.ts.map +1 -0
- package/dist/nodes/vector.js +1121 -0
- package/dist/nodes/vector.js.map +1 -0
- package/dist/nodes/video.d.ts +387 -0
- package/dist/nodes/video.d.ts.map +1 -0
- package/dist/nodes/video.js +2361 -0
- package/dist/nodes/video.js.map +1 -0
- package/dist/nodes/workflow.d.ts +24 -0
- package/dist/nodes/workflow.d.ts.map +1 -0
- package/dist/nodes/workflow.js +146 -0
- package/dist/nodes/workflow.js.map +1 -0
- package/dist/nodes/workspace.d.ts +203 -0
- package/dist/nodes/workspace.d.ts.map +1 -0
- package/dist/nodes/workspace.js +795 -0
- package/dist/nodes/workspace.js.map +1 -0
- package/package.json +103 -0
|
@@ -0,0 +1,1395 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BaseNode, prop } from "@nodetool-ai/node-sdk";
|
|
8
|
+
import { promises as fs } from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import sharp from "sharp";
|
|
12
|
+
function toBytes(data) {
|
|
13
|
+
if (!data)
|
|
14
|
+
return new Uint8Array();
|
|
15
|
+
if (data instanceof Uint8Array)
|
|
16
|
+
return data;
|
|
17
|
+
return Uint8Array.from(Buffer.from(data, "base64"));
|
|
18
|
+
}
|
|
19
|
+
function imageBytes(image) {
|
|
20
|
+
if (!image || typeof image !== "object")
|
|
21
|
+
return new Uint8Array();
|
|
22
|
+
return toBytes(image.data);
|
|
23
|
+
}
|
|
24
|
+
async function imageBytesAsync(image, context) {
|
|
25
|
+
if (!image || typeof image !== "object")
|
|
26
|
+
return new Uint8Array();
|
|
27
|
+
const ref = image;
|
|
28
|
+
if (ref.data)
|
|
29
|
+
return toBytes(ref.data);
|
|
30
|
+
if (typeof ref.uri === "string" && ref.uri) {
|
|
31
|
+
if (context?.storage) {
|
|
32
|
+
const stored = await context.storage.retrieve(ref.uri);
|
|
33
|
+
if (stored !== null)
|
|
34
|
+
return new Uint8Array(stored);
|
|
35
|
+
}
|
|
36
|
+
if (ref.uri.startsWith("file://")) {
|
|
37
|
+
return new Uint8Array(await fs.readFile(filePath(ref.uri)));
|
|
38
|
+
}
|
|
39
|
+
if (ref.uri.startsWith("http://") || ref.uri.startsWith("https://")) {
|
|
40
|
+
const response = await fetch(ref.uri);
|
|
41
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return new Uint8Array();
|
|
45
|
+
}
|
|
46
|
+
function filePath(uriOrPath) {
|
|
47
|
+
if (uriOrPath.startsWith("file://")) {
|
|
48
|
+
try {
|
|
49
|
+
return fileURLToPath(new URL(uriOrPath));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Fallback for non-standard URIs like file://C:\path
|
|
53
|
+
return uriOrPath.slice("file://".length);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return uriOrPath;
|
|
57
|
+
}
|
|
58
|
+
function dateName(name) {
|
|
59
|
+
const now = new Date();
|
|
60
|
+
const pad = (v) => String(v).padStart(2, "0");
|
|
61
|
+
return name
|
|
62
|
+
.replaceAll("%Y", String(now.getFullYear()))
|
|
63
|
+
.replaceAll("%m", pad(now.getMonth() + 1))
|
|
64
|
+
.replaceAll("%d", pad(now.getDate()))
|
|
65
|
+
.replaceAll("%H", pad(now.getHours()))
|
|
66
|
+
.replaceAll("%M", pad(now.getMinutes()))
|
|
67
|
+
.replaceAll("%S", pad(now.getSeconds()));
|
|
68
|
+
}
|
|
69
|
+
function imageRef(data, extras = {}) {
|
|
70
|
+
return {
|
|
71
|
+
type: "image",
|
|
72
|
+
data: Buffer.from(data).toString("base64"),
|
|
73
|
+
...extras
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function inferImageMime(uri, bytes) {
|
|
77
|
+
const lower = (uri ?? "").toLowerCase();
|
|
78
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))
|
|
79
|
+
return "image/jpeg";
|
|
80
|
+
if (lower.endsWith(".webp"))
|
|
81
|
+
return "image/webp";
|
|
82
|
+
if (lower.endsWith(".gif"))
|
|
83
|
+
return "image/gif";
|
|
84
|
+
if (lower.endsWith(".bmp"))
|
|
85
|
+
return "image/bmp";
|
|
86
|
+
if (bytes.length < 4)
|
|
87
|
+
return "image/unknown";
|
|
88
|
+
if (bytes[0] === 0xff && bytes[1] === 0xd8)
|
|
89
|
+
return "image/jpeg";
|
|
90
|
+
if (bytes[0] === 0x47 && bytes[1] === 0x49)
|
|
91
|
+
return "image/gif";
|
|
92
|
+
if (bytes[0] === 0x42 && bytes[1] === 0x4d)
|
|
93
|
+
return "image/bmp";
|
|
94
|
+
if (bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[8] === 0x57)
|
|
95
|
+
return "image/webp";
|
|
96
|
+
if (bytes[0] === 0x89 &&
|
|
97
|
+
bytes[1] === 0x50 &&
|
|
98
|
+
bytes[2] === 0x4e &&
|
|
99
|
+
bytes[3] === 0x47) {
|
|
100
|
+
return "image/png";
|
|
101
|
+
}
|
|
102
|
+
return "image/unknown";
|
|
103
|
+
}
|
|
104
|
+
function getModelConfig(props) {
|
|
105
|
+
const model = (props.model ?? {});
|
|
106
|
+
return {
|
|
107
|
+
providerId: typeof model.provider === "string" ? model.provider : "",
|
|
108
|
+
modelId: typeof model.id === "string" ? model.id : ""
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const IMAGE_ASPECT_RATIOS = {
|
|
112
|
+
"21:9": [21, 9],
|
|
113
|
+
"16:9": [16, 9],
|
|
114
|
+
"3:2": [3, 2],
|
|
115
|
+
"7:5": [7, 5],
|
|
116
|
+
"4:3": [4, 3],
|
|
117
|
+
"5:4": [5, 4],
|
|
118
|
+
"1:1": [1, 1],
|
|
119
|
+
"9:16": [9, 16],
|
|
120
|
+
"2:3": [2, 3],
|
|
121
|
+
"5:7": [5, 7],
|
|
122
|
+
"3:4": [3, 4],
|
|
123
|
+
"4:5": [4, 5]
|
|
124
|
+
};
|
|
125
|
+
const IMAGE_RESOLUTION_PX = {
|
|
126
|
+
"1K": 1024,
|
|
127
|
+
"2K": 2048,
|
|
128
|
+
"4K": 4096
|
|
129
|
+
};
|
|
130
|
+
function resolveImageSize(resolution, aspectRatio) {
|
|
131
|
+
const base = IMAGE_RESOLUTION_PX[resolution] ?? 1024;
|
|
132
|
+
const [aw, ah] = IMAGE_ASPECT_RATIOS[aspectRatio] ?? [1, 1];
|
|
133
|
+
if (aw >= ah) {
|
|
134
|
+
const height = base;
|
|
135
|
+
const width = Math.round((height * aw) / ah);
|
|
136
|
+
return { width, height };
|
|
137
|
+
}
|
|
138
|
+
const width = base;
|
|
139
|
+
const height = Math.round((width * ah) / aw);
|
|
140
|
+
return { width, height };
|
|
141
|
+
}
|
|
142
|
+
const IMAGE_ASPECT_RATIO_VALUES = Object.keys(IMAGE_ASPECT_RATIOS);
|
|
143
|
+
const IMAGE_RESOLUTION_VALUES = Object.keys(IMAGE_RESOLUTION_PX);
|
|
144
|
+
const IMAGE_EDIT_STRENGTH_VALUES = [0.25, 0.5, 0.65, 0.75, 0.85, 1.0];
|
|
145
|
+
function hasProviderSupport(context, providerId, modelId) {
|
|
146
|
+
return (!!context &&
|
|
147
|
+
typeof context.runProviderPrediction === "function" &&
|
|
148
|
+
!!providerId &&
|
|
149
|
+
!!modelId);
|
|
150
|
+
}
|
|
151
|
+
async function metadataFor(bytes) {
|
|
152
|
+
try {
|
|
153
|
+
const md = await sharp(bytes).metadata();
|
|
154
|
+
return {
|
|
155
|
+
width: md.width ?? undefined,
|
|
156
|
+
height: md.height ?? undefined
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return { width: undefined, height: undefined };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function transformImage(image, operation, context) {
|
|
164
|
+
const bytes = await imageBytesAsync(image, context);
|
|
165
|
+
if (bytes.length === 0) {
|
|
166
|
+
return imageRef(bytes, {
|
|
167
|
+
uri: image.uri ?? "",
|
|
168
|
+
width: image.width ?? undefined,
|
|
169
|
+
height: image.height ?? undefined
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const outputBytes = await operation(sharp(bytes, { failOn: "none" }), bytes).toBuffer();
|
|
174
|
+
const meta = await metadataFor(outputBytes);
|
|
175
|
+
return imageRef(outputBytes, {
|
|
176
|
+
uri: image.uri ?? "",
|
|
177
|
+
mimeType: inferImageMime(image.uri, outputBytes),
|
|
178
|
+
width: meta.width,
|
|
179
|
+
height: meta.height
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return imageRef(bytes, {
|
|
184
|
+
uri: image.uri ?? "",
|
|
185
|
+
width: image.width ?? undefined,
|
|
186
|
+
height: image.height ?? undefined
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
export class LoadImageFileNode extends BaseNode {
|
|
191
|
+
static nodeType = "nodetool.image.LoadImageFile";
|
|
192
|
+
static title = "Load Image File";
|
|
193
|
+
static description = "Read an image file from disk.\n image, input, load, file";
|
|
194
|
+
static metadataOutputTypes = {
|
|
195
|
+
output: "image"
|
|
196
|
+
};
|
|
197
|
+
async process() {
|
|
198
|
+
const p = filePath(String(this.path ?? ""));
|
|
199
|
+
const data = new Uint8Array(await fs.readFile(p));
|
|
200
|
+
const meta = await metadataFor(data);
|
|
201
|
+
return {
|
|
202
|
+
output: imageRef(data, {
|
|
203
|
+
uri: `file://${p}`,
|
|
204
|
+
mimeType: inferImageMime(p, data),
|
|
205
|
+
width: meta.width,
|
|
206
|
+
height: meta.height
|
|
207
|
+
})
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
__decorate([
|
|
212
|
+
prop({
|
|
213
|
+
type: "str",
|
|
214
|
+
default: "",
|
|
215
|
+
title: "Path",
|
|
216
|
+
description: "Path to the image file to read"
|
|
217
|
+
})
|
|
218
|
+
], LoadImageFileNode.prototype, "path", void 0);
|
|
219
|
+
export class LoadImageFolderNode extends BaseNode {
|
|
220
|
+
static nodeType = "nodetool.image.LoadImageFolder";
|
|
221
|
+
static title = "Load Image Folder";
|
|
222
|
+
static description = "Load all images from a folder, optionally including subfolders.\n image, load, folder, files";
|
|
223
|
+
static metadataOutputTypes = {
|
|
224
|
+
image: "image",
|
|
225
|
+
path: "str",
|
|
226
|
+
images: "list"
|
|
227
|
+
};
|
|
228
|
+
static isStreamingOutput = true;
|
|
229
|
+
async process() {
|
|
230
|
+
const collected = [];
|
|
231
|
+
for await (const item of this._loadImages()) {
|
|
232
|
+
collected.push(item.image);
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
image: collected[0] ?? {},
|
|
236
|
+
name: "",
|
|
237
|
+
images: collected
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
async *_loadImages() {
|
|
241
|
+
const raw = this.folder;
|
|
242
|
+
const folder = typeof raw === "string" && raw.length > 0
|
|
243
|
+
? raw.startsWith("file:")
|
|
244
|
+
? filePath(raw)
|
|
245
|
+
: raw
|
|
246
|
+
: typeof raw === "object" && raw !== null && typeof raw.uri === "string" && raw.uri.length > 0
|
|
247
|
+
? filePath(raw.uri)
|
|
248
|
+
: "";
|
|
249
|
+
if (!folder)
|
|
250
|
+
return;
|
|
251
|
+
const extensions = Array.isArray(this.extensions)
|
|
252
|
+
? this.extensions.map((e) => String(e).toLowerCase())
|
|
253
|
+
: [".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp", ".tiff"];
|
|
254
|
+
const patternStr = String(this.pattern ?? "");
|
|
255
|
+
const patternRegex = patternStr
|
|
256
|
+
? new RegExp("^" +
|
|
257
|
+
patternStr
|
|
258
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
259
|
+
.replace(/\*/g, ".*")
|
|
260
|
+
.replace(/\?/g, ".") +
|
|
261
|
+
"$", "i")
|
|
262
|
+
: null;
|
|
263
|
+
const files = [];
|
|
264
|
+
const collect = async (dir) => {
|
|
265
|
+
let entries;
|
|
266
|
+
try {
|
|
267
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// directory does not exist or is not accessible
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
for (const entry of entries) {
|
|
274
|
+
if (entry.isDirectory() && this.include_subdirectories) {
|
|
275
|
+
await collect(path.join(dir, entry.name));
|
|
276
|
+
}
|
|
277
|
+
else if (entry.isFile()) {
|
|
278
|
+
files.push({ fullPath: path.join(dir, entry.name), name: entry.name });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
await collect(folder);
|
|
283
|
+
for (const file of files) {
|
|
284
|
+
const ext = path.extname(file.name).toLowerCase();
|
|
285
|
+
if (!extensions.includes(ext))
|
|
286
|
+
continue;
|
|
287
|
+
if (patternRegex && !patternRegex.test(file.name))
|
|
288
|
+
continue;
|
|
289
|
+
const data = new Uint8Array(await fs.readFile(file.fullPath));
|
|
290
|
+
const meta = await metadataFor(data);
|
|
291
|
+
yield {
|
|
292
|
+
image: imageRef(data, {
|
|
293
|
+
uri: `file://${file.fullPath}`,
|
|
294
|
+
mimeType: inferImageMime(file.fullPath, data),
|
|
295
|
+
width: meta.width,
|
|
296
|
+
height: meta.height
|
|
297
|
+
}),
|
|
298
|
+
name: file.name
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async *genProcess() {
|
|
303
|
+
const collected = [];
|
|
304
|
+
for await (const item of this._loadImages()) {
|
|
305
|
+
collected.push(item.image);
|
|
306
|
+
yield item;
|
|
307
|
+
}
|
|
308
|
+
yield { images: collected };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
__decorate([
|
|
312
|
+
prop({
|
|
313
|
+
type: "str",
|
|
314
|
+
default: "",
|
|
315
|
+
title: "Folder",
|
|
316
|
+
description: "Folder to scan for images"
|
|
317
|
+
})
|
|
318
|
+
], LoadImageFolderNode.prototype, "folder", void 0);
|
|
319
|
+
__decorate([
|
|
320
|
+
prop({
|
|
321
|
+
type: "bool",
|
|
322
|
+
default: false,
|
|
323
|
+
title: "Include Subdirectories",
|
|
324
|
+
description: "Include images in subfolders"
|
|
325
|
+
})
|
|
326
|
+
], LoadImageFolderNode.prototype, "include_subdirectories", void 0);
|
|
327
|
+
__decorate([
|
|
328
|
+
prop({
|
|
329
|
+
type: "list[str]",
|
|
330
|
+
default: [".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp", ".tiff"],
|
|
331
|
+
title: "Extensions",
|
|
332
|
+
description: "Image file extensions to include"
|
|
333
|
+
})
|
|
334
|
+
], LoadImageFolderNode.prototype, "extensions", void 0);
|
|
335
|
+
__decorate([
|
|
336
|
+
prop({
|
|
337
|
+
type: "str",
|
|
338
|
+
default: "",
|
|
339
|
+
title: "Pattern",
|
|
340
|
+
description: "Pattern to match image files"
|
|
341
|
+
})
|
|
342
|
+
], LoadImageFolderNode.prototype, "pattern", void 0);
|
|
343
|
+
export class SaveImageFileImageNode extends BaseNode {
|
|
344
|
+
static nodeType = "nodetool.image.SaveImageFile";
|
|
345
|
+
static title = "Save Image File";
|
|
346
|
+
static description = "Write an image to disk.\n image, output, save, file";
|
|
347
|
+
static metadataOutputTypes = {
|
|
348
|
+
output: "image"
|
|
349
|
+
};
|
|
350
|
+
async process() {
|
|
351
|
+
const folder = String(this.folder ?? ".");
|
|
352
|
+
const filename = dateName(String(this.filename ?? "image.png"));
|
|
353
|
+
let p = filePath(path.resolve(folder, filename));
|
|
354
|
+
await fs.mkdir(path.dirname(p), { recursive: true });
|
|
355
|
+
if (!this.overwrite) {
|
|
356
|
+
const ext = path.extname(p);
|
|
357
|
+
const base = p.slice(0, p.length - ext.length);
|
|
358
|
+
let counter = 1;
|
|
359
|
+
while (true) {
|
|
360
|
+
try {
|
|
361
|
+
await fs.access(p);
|
|
362
|
+
p = `${base}_${counter}${ext}`;
|
|
363
|
+
counter++;
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
await fs.writeFile(p, imageBytes(this.image));
|
|
371
|
+
return { output: p };
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
__decorate([
|
|
375
|
+
prop({
|
|
376
|
+
type: "image",
|
|
377
|
+
default: {
|
|
378
|
+
type: "image",
|
|
379
|
+
uri: "",
|
|
380
|
+
asset_id: null,
|
|
381
|
+
data: null,
|
|
382
|
+
metadata: null
|
|
383
|
+
},
|
|
384
|
+
title: "Image",
|
|
385
|
+
description: "The image to save"
|
|
386
|
+
})
|
|
387
|
+
], SaveImageFileImageNode.prototype, "image", void 0);
|
|
388
|
+
__decorate([
|
|
389
|
+
prop({
|
|
390
|
+
type: "str",
|
|
391
|
+
default: "",
|
|
392
|
+
title: "Folder",
|
|
393
|
+
description: "Folder where the file will be saved"
|
|
394
|
+
})
|
|
395
|
+
], SaveImageFileImageNode.prototype, "folder", void 0);
|
|
396
|
+
__decorate([
|
|
397
|
+
prop({
|
|
398
|
+
type: "str",
|
|
399
|
+
default: "",
|
|
400
|
+
title: "Filename",
|
|
401
|
+
description: "\n The name of the image file.\n You can use time and date variables to create unique names:\n %Y - Year\n %m - Month\n %d - Day\n %H - Hour\n %M - Minute\n %S - Second\n "
|
|
402
|
+
})
|
|
403
|
+
], SaveImageFileImageNode.prototype, "filename", void 0);
|
|
404
|
+
__decorate([
|
|
405
|
+
prop({
|
|
406
|
+
type: "bool",
|
|
407
|
+
default: false,
|
|
408
|
+
title: "Overwrite",
|
|
409
|
+
description: "Overwrite the file if it already exists, otherwise file will be renamed"
|
|
410
|
+
})
|
|
411
|
+
], SaveImageFileImageNode.prototype, "overwrite", void 0);
|
|
412
|
+
export class LoadImageAssetsNode extends BaseNode {
|
|
413
|
+
static nodeType = "nodetool.image.LoadImageAssets";
|
|
414
|
+
static title = "Load Image Assets";
|
|
415
|
+
static description = "Load images from an asset folder.\n load, image, file, import";
|
|
416
|
+
static metadataOutputTypes = {
|
|
417
|
+
image: "image",
|
|
418
|
+
name: "str",
|
|
419
|
+
images: "list"
|
|
420
|
+
};
|
|
421
|
+
static isStreamingOutput = true;
|
|
422
|
+
async process() {
|
|
423
|
+
const loader = new LoadImageFolderNode();
|
|
424
|
+
loader.assign({ folder: this.folder ?? "." });
|
|
425
|
+
const result = await loader.process();
|
|
426
|
+
return result;
|
|
427
|
+
}
|
|
428
|
+
async *genProcess() {
|
|
429
|
+
const loader = new LoadImageFolderNode();
|
|
430
|
+
loader.assign({ folder: this.folder ?? "." });
|
|
431
|
+
for await (const item of loader.genProcess()) {
|
|
432
|
+
yield item;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
__decorate([
|
|
437
|
+
prop({
|
|
438
|
+
type: "folder",
|
|
439
|
+
default: {
|
|
440
|
+
type: "folder",
|
|
441
|
+
uri: "",
|
|
442
|
+
asset_id: null,
|
|
443
|
+
data: null,
|
|
444
|
+
metadata: null
|
|
445
|
+
},
|
|
446
|
+
title: "Folder",
|
|
447
|
+
description: "The asset folder to load the images from."
|
|
448
|
+
})
|
|
449
|
+
], LoadImageAssetsNode.prototype, "folder", void 0);
|
|
450
|
+
export class SaveImageNode extends BaseNode {
|
|
451
|
+
static nodeType = "nodetool.image.SaveImage";
|
|
452
|
+
static title = "Save Image Asset";
|
|
453
|
+
static description = "Save an image to specified asset folder with customizable name format.\n save, image, folder, naming";
|
|
454
|
+
static metadataOutputTypes = {
|
|
455
|
+
output: "image"
|
|
456
|
+
};
|
|
457
|
+
async process(context) {
|
|
458
|
+
const bytes = await imageBytesAsync(this.image, context);
|
|
459
|
+
if (bytes.length === 0)
|
|
460
|
+
throw new Error("The input image is not connected.");
|
|
461
|
+
const name = dateName(String(this.name ?? "image.png"));
|
|
462
|
+
const mime = inferImageMime(undefined, bytes);
|
|
463
|
+
const meta = await metadataFor(bytes);
|
|
464
|
+
// Create asset via context (persists to DB + storage)
|
|
465
|
+
if (context && typeof context.createAsset === "function") {
|
|
466
|
+
const folderRef = this.folder;
|
|
467
|
+
const parentId = folderRef?.asset_id ?? null;
|
|
468
|
+
const asset = (await context.createAsset({
|
|
469
|
+
name,
|
|
470
|
+
contentType: mime === "image/unknown" ? "image/png" : mime,
|
|
471
|
+
content: bytes,
|
|
472
|
+
parentId,
|
|
473
|
+
nodeId: this.__node_id || null
|
|
474
|
+
}));
|
|
475
|
+
const assetId = asset.id;
|
|
476
|
+
const ext = name.includes(".") ? name.slice(name.lastIndexOf(".")) : ".png";
|
|
477
|
+
return {
|
|
478
|
+
output: imageRef(bytes, {
|
|
479
|
+
uri: `asset://${assetId}${ext}`,
|
|
480
|
+
mimeType: mime,
|
|
481
|
+
width: meta.width,
|
|
482
|
+
height: meta.height
|
|
483
|
+
})
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
// Fallback: write to filesystem
|
|
487
|
+
const folder = typeof this.folder === "string" ? this.folder : ".";
|
|
488
|
+
const full = path.resolve(folder, name);
|
|
489
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
490
|
+
await fs.writeFile(full, bytes);
|
|
491
|
+
return {
|
|
492
|
+
output: imageRef(bytes, {
|
|
493
|
+
uri: `file://${full}`,
|
|
494
|
+
mimeType: mime,
|
|
495
|
+
width: meta.width,
|
|
496
|
+
height: meta.height
|
|
497
|
+
})
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
__decorate([
|
|
502
|
+
prop({
|
|
503
|
+
type: "image",
|
|
504
|
+
default: {
|
|
505
|
+
type: "image",
|
|
506
|
+
uri: "",
|
|
507
|
+
asset_id: null,
|
|
508
|
+
data: null,
|
|
509
|
+
metadata: null
|
|
510
|
+
},
|
|
511
|
+
title: "Image",
|
|
512
|
+
description: "The image to save."
|
|
513
|
+
})
|
|
514
|
+
], SaveImageNode.prototype, "image", void 0);
|
|
515
|
+
__decorate([
|
|
516
|
+
prop({
|
|
517
|
+
type: "folder",
|
|
518
|
+
default: {
|
|
519
|
+
type: "folder",
|
|
520
|
+
uri: "",
|
|
521
|
+
asset_id: null,
|
|
522
|
+
data: null,
|
|
523
|
+
metadata: null
|
|
524
|
+
},
|
|
525
|
+
title: "Folder",
|
|
526
|
+
description: "The asset folder to save the image in."
|
|
527
|
+
})
|
|
528
|
+
], SaveImageNode.prototype, "folder", void 0);
|
|
529
|
+
__decorate([
|
|
530
|
+
prop({
|
|
531
|
+
type: "str",
|
|
532
|
+
default: "%Y-%m-%d_%H-%M-%S.png",
|
|
533
|
+
title: "Name",
|
|
534
|
+
description: "\n Name of the output file.\n You can use time and date variables to create unique names:\n %Y - Year\n %m - Month\n %d - Day\n %H - Hour\n %M - Minute\n %S - Second\n "
|
|
535
|
+
})
|
|
536
|
+
], SaveImageNode.prototype, "name", void 0);
|
|
537
|
+
export class GetMetadataNode extends BaseNode {
|
|
538
|
+
static nodeType = "nodetool.image.GetMetadata";
|
|
539
|
+
static title = "Get Metadata";
|
|
540
|
+
static description = "Get metadata about the input image.\n metadata, properties, analysis, information";
|
|
541
|
+
static metadataOutputTypes = {
|
|
542
|
+
format: "str",
|
|
543
|
+
mode: "str",
|
|
544
|
+
width: "int",
|
|
545
|
+
height: "int",
|
|
546
|
+
channels: "int"
|
|
547
|
+
};
|
|
548
|
+
async process(context) {
|
|
549
|
+
const image = (this.image ?? {});
|
|
550
|
+
const bytes = await imageBytesAsync(image, context);
|
|
551
|
+
try {
|
|
552
|
+
const md = await sharp(bytes).metadata();
|
|
553
|
+
const formatMap = {
|
|
554
|
+
jpeg: "JPEG",
|
|
555
|
+
png: "PNG",
|
|
556
|
+
webp: "WEBP",
|
|
557
|
+
gif: "GIF",
|
|
558
|
+
tiff: "TIFF",
|
|
559
|
+
svg: "SVG",
|
|
560
|
+
heif: "HEIF",
|
|
561
|
+
avif: "AVIF",
|
|
562
|
+
raw: "RAW"
|
|
563
|
+
};
|
|
564
|
+
const channelsToMode = {
|
|
565
|
+
1: "L",
|
|
566
|
+
2: "LA",
|
|
567
|
+
3: "RGB",
|
|
568
|
+
4: "RGBA"
|
|
569
|
+
};
|
|
570
|
+
return {
|
|
571
|
+
format: formatMap[md.format ?? ""] ?? (md.format ?? "unknown").toUpperCase(),
|
|
572
|
+
mode: channelsToMode[md.channels ?? 3] ?? "RGB",
|
|
573
|
+
width: md.width ?? 0,
|
|
574
|
+
height: md.height ?? 0,
|
|
575
|
+
channels: md.channels ?? 0
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
catch {
|
|
579
|
+
return {
|
|
580
|
+
format: "unknown",
|
|
581
|
+
mode: "RGB",
|
|
582
|
+
width: image.width ?? 0,
|
|
583
|
+
height: image.height ?? 0,
|
|
584
|
+
channels: 0
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
__decorate([
|
|
590
|
+
prop({
|
|
591
|
+
type: "image",
|
|
592
|
+
default: {
|
|
593
|
+
type: "image",
|
|
594
|
+
uri: "",
|
|
595
|
+
asset_id: null,
|
|
596
|
+
data: null,
|
|
597
|
+
metadata: null
|
|
598
|
+
},
|
|
599
|
+
title: "Image",
|
|
600
|
+
description: "The input image."
|
|
601
|
+
})
|
|
602
|
+
], GetMetadataNode.prototype, "image", void 0);
|
|
603
|
+
export class BatchToListNode extends BaseNode {
|
|
604
|
+
static nodeType = "nodetool.image.BatchToList";
|
|
605
|
+
static title = "Batch To List";
|
|
606
|
+
static description = "Convert an image batch to a list of image references.\n batch, list, images, processing";
|
|
607
|
+
static metadataOutputTypes = {
|
|
608
|
+
output: "list[image]"
|
|
609
|
+
};
|
|
610
|
+
async process() {
|
|
611
|
+
const batch = this.batch;
|
|
612
|
+
if (!batch) {
|
|
613
|
+
throw new Error("Batch input is empty.");
|
|
614
|
+
}
|
|
615
|
+
const batchObj = batch;
|
|
616
|
+
if (batchObj.data == null) {
|
|
617
|
+
throw new Error("Batch data is null.");
|
|
618
|
+
}
|
|
619
|
+
if (!Array.isArray(batchObj.data)) {
|
|
620
|
+
// Single image ref — wrap in list
|
|
621
|
+
return { output: [batch] };
|
|
622
|
+
}
|
|
623
|
+
// Unwrap batch data into individual ImageRefs
|
|
624
|
+
const output = batchObj.data.map((item) => {
|
|
625
|
+
if (item instanceof Uint8Array || typeof item === "string") {
|
|
626
|
+
return imageRef(toBytes(item));
|
|
627
|
+
}
|
|
628
|
+
return item;
|
|
629
|
+
});
|
|
630
|
+
return { output };
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
__decorate([
|
|
634
|
+
prop({
|
|
635
|
+
type: "image",
|
|
636
|
+
default: {
|
|
637
|
+
type: "image",
|
|
638
|
+
uri: "",
|
|
639
|
+
asset_id: null,
|
|
640
|
+
data: null,
|
|
641
|
+
metadata: null
|
|
642
|
+
},
|
|
643
|
+
title: "Batch",
|
|
644
|
+
description: "The batch of images to convert."
|
|
645
|
+
})
|
|
646
|
+
], BatchToListNode.prototype, "batch", void 0);
|
|
647
|
+
export class ImagesToListNode extends BaseNode {
|
|
648
|
+
static nodeType = "nodetool.image.ImagesToList";
|
|
649
|
+
static title = "Images To List";
|
|
650
|
+
static description = "Convert all dynamic properties to a list of image references.\n list, images, processing";
|
|
651
|
+
static metadataOutputTypes = {
|
|
652
|
+
output: "list[image]"
|
|
653
|
+
};
|
|
654
|
+
static isDynamic = true;
|
|
655
|
+
async process() {
|
|
656
|
+
const out = [];
|
|
657
|
+
for (const [, value] of this.dynamicProps) {
|
|
658
|
+
if (value == null)
|
|
659
|
+
continue;
|
|
660
|
+
if (Array.isArray(value)) {
|
|
661
|
+
for (const item of value) {
|
|
662
|
+
if (item != null)
|
|
663
|
+
out.push(item);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
out.push(value);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return { output: out };
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
class TransformImageNode extends BaseNode {
|
|
674
|
+
transformMeta() {
|
|
675
|
+
const image = (this.image ?? {});
|
|
676
|
+
return {
|
|
677
|
+
width: Number(this.width ?? image.width ?? 0) || null,
|
|
678
|
+
height: Number(this.height ?? image.height ?? 0) || null
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
export class PasteNode extends TransformImageNode {
|
|
683
|
+
static nodeType = "nodetool.image.Paste";
|
|
684
|
+
static title = "Paste";
|
|
685
|
+
static description = "Paste one image onto another at specified coordinates.\n paste, composite, positioning, overlay";
|
|
686
|
+
static metadataOutputTypes = {
|
|
687
|
+
output: "image"
|
|
688
|
+
};
|
|
689
|
+
async process(context) {
|
|
690
|
+
const image = (this.image ?? {});
|
|
691
|
+
const paste = (this.paste ?? {});
|
|
692
|
+
const left = Math.max(0, Number(this.left ?? 0));
|
|
693
|
+
const top = Math.max(0, Number(this.top ?? 0));
|
|
694
|
+
const baseBytes = await imageBytesAsync(image, context);
|
|
695
|
+
const overlayBytes = await imageBytesAsync(paste, context);
|
|
696
|
+
if (baseBytes.length === 0 || overlayBytes.length === 0) {
|
|
697
|
+
return {
|
|
698
|
+
output: imageRef(baseBytes, {
|
|
699
|
+
uri: image.uri ?? "",
|
|
700
|
+
...this.transformMeta()
|
|
701
|
+
})
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
try {
|
|
705
|
+
const outputBytes = await sharp(baseBytes, { failOn: "none" })
|
|
706
|
+
.composite([{ input: Buffer.from(overlayBytes), left, top }])
|
|
707
|
+
.toBuffer();
|
|
708
|
+
const meta = await metadataFor(outputBytes);
|
|
709
|
+
return {
|
|
710
|
+
output: imageRef(outputBytes, {
|
|
711
|
+
uri: image.uri ?? "",
|
|
712
|
+
mimeType: inferImageMime(image.uri, outputBytes),
|
|
713
|
+
width: meta.width,
|
|
714
|
+
height: meta.height
|
|
715
|
+
})
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
catch {
|
|
719
|
+
return {
|
|
720
|
+
output: imageRef(baseBytes, {
|
|
721
|
+
uri: image.uri ?? "",
|
|
722
|
+
...this.transformMeta()
|
|
723
|
+
})
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
__decorate([
|
|
729
|
+
prop({
|
|
730
|
+
type: "image",
|
|
731
|
+
default: {
|
|
732
|
+
type: "image",
|
|
733
|
+
uri: "",
|
|
734
|
+
asset_id: null,
|
|
735
|
+
data: null,
|
|
736
|
+
metadata: null
|
|
737
|
+
},
|
|
738
|
+
title: "Image",
|
|
739
|
+
description: "The image to paste into."
|
|
740
|
+
})
|
|
741
|
+
], PasteNode.prototype, "image", void 0);
|
|
742
|
+
__decorate([
|
|
743
|
+
prop({
|
|
744
|
+
type: "image",
|
|
745
|
+
default: {
|
|
746
|
+
type: "image",
|
|
747
|
+
uri: "",
|
|
748
|
+
asset_id: null,
|
|
749
|
+
data: null,
|
|
750
|
+
metadata: null
|
|
751
|
+
},
|
|
752
|
+
title: "Paste",
|
|
753
|
+
description: "The image to paste."
|
|
754
|
+
})
|
|
755
|
+
], PasteNode.prototype, "paste", void 0);
|
|
756
|
+
__decorate([
|
|
757
|
+
prop({
|
|
758
|
+
type: "int",
|
|
759
|
+
default: 0,
|
|
760
|
+
title: "Left",
|
|
761
|
+
description: "The left coordinate.",
|
|
762
|
+
min: 0,
|
|
763
|
+
max: 4096
|
|
764
|
+
})
|
|
765
|
+
], PasteNode.prototype, "left", void 0);
|
|
766
|
+
__decorate([
|
|
767
|
+
prop({
|
|
768
|
+
type: "int",
|
|
769
|
+
default: 0,
|
|
770
|
+
title: "Top",
|
|
771
|
+
description: "The top coordinate.",
|
|
772
|
+
min: 0,
|
|
773
|
+
max: 4096
|
|
774
|
+
})
|
|
775
|
+
], PasteNode.prototype, "top", void 0);
|
|
776
|
+
export class ScaleNode extends TransformImageNode {
|
|
777
|
+
static nodeType = "nodetool.image.Scale";
|
|
778
|
+
static title = "Scale";
|
|
779
|
+
static description = "Enlarge or shrink an image by a scale factor.\n image, resize, scale";
|
|
780
|
+
static metadataOutputTypes = {
|
|
781
|
+
output: "image"
|
|
782
|
+
};
|
|
783
|
+
async process(context) {
|
|
784
|
+
const image = (this.image ?? {});
|
|
785
|
+
const requestedScale = Number(this.scale ?? 0);
|
|
786
|
+
const targetWidth = 0;
|
|
787
|
+
const targetHeight = 0;
|
|
788
|
+
const scale = requestedScale > 0
|
|
789
|
+
? requestedScale
|
|
790
|
+
: targetWidth > 0 && (image.width ?? 0) > 0
|
|
791
|
+
? targetWidth / Number(image.width)
|
|
792
|
+
: targetHeight > 0 && (image.height ?? 0) > 0
|
|
793
|
+
? targetHeight / Number(image.height)
|
|
794
|
+
: 1;
|
|
795
|
+
const output = (await transformImage(image, (instance) => {
|
|
796
|
+
const fallbackWidth = image.width ?? 1;
|
|
797
|
+
const fallbackHeight = image.height ?? 1;
|
|
798
|
+
return instance.resize({
|
|
799
|
+
width: Math.max(1, Math.round(fallbackWidth * scale)),
|
|
800
|
+
height: Math.max(1, Math.round(fallbackHeight * scale))
|
|
801
|
+
});
|
|
802
|
+
}, context));
|
|
803
|
+
const fallbackWidth = targetWidth > 0
|
|
804
|
+
? targetWidth
|
|
805
|
+
: image.width != null
|
|
806
|
+
? Math.max(1, Math.round(Number(image.width) * scale))
|
|
807
|
+
: null;
|
|
808
|
+
const fallbackHeight = targetHeight > 0
|
|
809
|
+
? targetHeight
|
|
810
|
+
: image.height != null
|
|
811
|
+
? Math.max(1, Math.round(Number(image.height) * scale))
|
|
812
|
+
: null;
|
|
813
|
+
return {
|
|
814
|
+
output: {
|
|
815
|
+
...output,
|
|
816
|
+
width: fallbackWidth ?? output.width,
|
|
817
|
+
height: fallbackHeight ?? output.height
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
__decorate([
|
|
823
|
+
prop({
|
|
824
|
+
type: "image",
|
|
825
|
+
default: {
|
|
826
|
+
type: "image",
|
|
827
|
+
uri: "",
|
|
828
|
+
asset_id: null,
|
|
829
|
+
data: null,
|
|
830
|
+
metadata: null
|
|
831
|
+
},
|
|
832
|
+
title: "Image",
|
|
833
|
+
description: "The image to scale."
|
|
834
|
+
})
|
|
835
|
+
], ScaleNode.prototype, "image", void 0);
|
|
836
|
+
__decorate([
|
|
837
|
+
prop({
|
|
838
|
+
type: "float",
|
|
839
|
+
default: 1,
|
|
840
|
+
title: "Scale",
|
|
841
|
+
description: "The scale factor.",
|
|
842
|
+
min: 0,
|
|
843
|
+
max: 10
|
|
844
|
+
})
|
|
845
|
+
], ScaleNode.prototype, "scale", void 0);
|
|
846
|
+
export class ResizeNode extends TransformImageNode {
|
|
847
|
+
static nodeType = "nodetool.image.Resize";
|
|
848
|
+
static title = "Resize";
|
|
849
|
+
static description = "Change image dimensions to specified width and height.\n image, resize";
|
|
850
|
+
static metadataOutputTypes = {
|
|
851
|
+
output: "image"
|
|
852
|
+
};
|
|
853
|
+
async process(context) {
|
|
854
|
+
const image = (this.image ?? {});
|
|
855
|
+
const width = Number(this.width ?? image.width ?? 0) || null;
|
|
856
|
+
const height = Number(this.height ?? image.height ?? 0) || null;
|
|
857
|
+
const output = (await transformImage(image, (instance) => instance.resize(width ?? undefined, height ?? undefined), context));
|
|
858
|
+
return {
|
|
859
|
+
output: {
|
|
860
|
+
...output,
|
|
861
|
+
width: output.width ?? width,
|
|
862
|
+
height: output.height ?? height
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
__decorate([
|
|
868
|
+
prop({
|
|
869
|
+
type: "image",
|
|
870
|
+
default: {
|
|
871
|
+
type: "image",
|
|
872
|
+
uri: "",
|
|
873
|
+
asset_id: null,
|
|
874
|
+
data: null,
|
|
875
|
+
metadata: null
|
|
876
|
+
},
|
|
877
|
+
title: "Image",
|
|
878
|
+
description: "The image to resize."
|
|
879
|
+
})
|
|
880
|
+
], ResizeNode.prototype, "image", void 0);
|
|
881
|
+
__decorate([
|
|
882
|
+
prop({
|
|
883
|
+
type: "int",
|
|
884
|
+
default: 512,
|
|
885
|
+
title: "Width",
|
|
886
|
+
description: "The target width.",
|
|
887
|
+
min: 0,
|
|
888
|
+
max: 4096
|
|
889
|
+
})
|
|
890
|
+
], ResizeNode.prototype, "width", void 0);
|
|
891
|
+
__decorate([
|
|
892
|
+
prop({
|
|
893
|
+
type: "int",
|
|
894
|
+
default: 512,
|
|
895
|
+
title: "Height",
|
|
896
|
+
description: "The target height.",
|
|
897
|
+
min: 0,
|
|
898
|
+
max: 4096
|
|
899
|
+
})
|
|
900
|
+
], ResizeNode.prototype, "height", void 0);
|
|
901
|
+
export class CropNode extends TransformImageNode {
|
|
902
|
+
static nodeType = "nodetool.image.Crop";
|
|
903
|
+
static title = "Crop";
|
|
904
|
+
static description = "Crop an image to specified coordinates.\n image, crop";
|
|
905
|
+
static metadataOutputTypes = {
|
|
906
|
+
output: "image"
|
|
907
|
+
};
|
|
908
|
+
async process(context) {
|
|
909
|
+
const image = (this.image ?? {});
|
|
910
|
+
const left = Math.max(0, Number(this.left ?? 0));
|
|
911
|
+
const top = Math.max(0, Number(this.top ?? 0));
|
|
912
|
+
const right = Number(this.right ?? image.width ?? 0);
|
|
913
|
+
const bottom = Number(this.bottom ?? image.height ?? 0);
|
|
914
|
+
const width = Math.max(1, right - left);
|
|
915
|
+
const height = Math.max(1, bottom - top);
|
|
916
|
+
const output = (await transformImage(image, (instance) => instance.extract({ left, top, width, height }), context));
|
|
917
|
+
return {
|
|
918
|
+
output: {
|
|
919
|
+
...output,
|
|
920
|
+
width: output.width ?? width,
|
|
921
|
+
height: output.height ?? height
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
__decorate([
|
|
927
|
+
prop({
|
|
928
|
+
type: "image",
|
|
929
|
+
default: {
|
|
930
|
+
type: "image",
|
|
931
|
+
uri: "",
|
|
932
|
+
asset_id: null,
|
|
933
|
+
data: null,
|
|
934
|
+
metadata: null
|
|
935
|
+
},
|
|
936
|
+
title: "Image",
|
|
937
|
+
description: "The image to crop."
|
|
938
|
+
})
|
|
939
|
+
], CropNode.prototype, "image", void 0);
|
|
940
|
+
__decorate([
|
|
941
|
+
prop({
|
|
942
|
+
type: "int",
|
|
943
|
+
default: 0,
|
|
944
|
+
title: "Left",
|
|
945
|
+
description: "The left coordinate.",
|
|
946
|
+
min: 0,
|
|
947
|
+
max: 4096
|
|
948
|
+
})
|
|
949
|
+
], CropNode.prototype, "left", void 0);
|
|
950
|
+
__decorate([
|
|
951
|
+
prop({
|
|
952
|
+
type: "int",
|
|
953
|
+
default: 0,
|
|
954
|
+
title: "Top",
|
|
955
|
+
description: "The top coordinate.",
|
|
956
|
+
min: 0,
|
|
957
|
+
max: 4096
|
|
958
|
+
})
|
|
959
|
+
], CropNode.prototype, "top", void 0);
|
|
960
|
+
__decorate([
|
|
961
|
+
prop({
|
|
962
|
+
type: "int",
|
|
963
|
+
default: 512,
|
|
964
|
+
title: "Right",
|
|
965
|
+
description: "The right coordinate.",
|
|
966
|
+
min: 0,
|
|
967
|
+
max: 4096
|
|
968
|
+
})
|
|
969
|
+
], CropNode.prototype, "right", void 0);
|
|
970
|
+
__decorate([
|
|
971
|
+
prop({
|
|
972
|
+
type: "int",
|
|
973
|
+
default: 512,
|
|
974
|
+
title: "Bottom",
|
|
975
|
+
description: "The bottom coordinate.",
|
|
976
|
+
min: 0,
|
|
977
|
+
max: 4096
|
|
978
|
+
})
|
|
979
|
+
], CropNode.prototype, "bottom", void 0);
|
|
980
|
+
export class FitNode extends TransformImageNode {
|
|
981
|
+
static nodeType = "nodetool.image.Fit";
|
|
982
|
+
static title = "Fit";
|
|
983
|
+
static description = "Resize an image to fit within specified dimensions while preserving aspect ratio.\n image, resize, fit";
|
|
984
|
+
static metadataOutputTypes = {
|
|
985
|
+
output: "image"
|
|
986
|
+
};
|
|
987
|
+
async process(context) {
|
|
988
|
+
const image = (this.image ?? {});
|
|
989
|
+
const width = Math.max(1, Number(this.width ?? image.width ?? 512));
|
|
990
|
+
const height = Math.max(1, Number(this.height ?? image.height ?? 512));
|
|
991
|
+
const output = (await transformImage(image, (instance) => instance.resize(width, height, { fit: "cover", position: "centre" }), context));
|
|
992
|
+
return {
|
|
993
|
+
output: {
|
|
994
|
+
...output,
|
|
995
|
+
width: output.width ?? width,
|
|
996
|
+
height: output.height ?? height
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
__decorate([
|
|
1002
|
+
prop({
|
|
1003
|
+
type: "image",
|
|
1004
|
+
default: {
|
|
1005
|
+
type: "image",
|
|
1006
|
+
uri: "",
|
|
1007
|
+
asset_id: null,
|
|
1008
|
+
data: null,
|
|
1009
|
+
metadata: null
|
|
1010
|
+
},
|
|
1011
|
+
title: "Image",
|
|
1012
|
+
description: "The image to fit."
|
|
1013
|
+
})
|
|
1014
|
+
], FitNode.prototype, "image", void 0);
|
|
1015
|
+
__decorate([
|
|
1016
|
+
prop({
|
|
1017
|
+
type: "int",
|
|
1018
|
+
default: 512,
|
|
1019
|
+
title: "Width",
|
|
1020
|
+
description: "Width to fit to.",
|
|
1021
|
+
min: 1,
|
|
1022
|
+
max: 4096
|
|
1023
|
+
})
|
|
1024
|
+
], FitNode.prototype, "width", void 0);
|
|
1025
|
+
__decorate([
|
|
1026
|
+
prop({
|
|
1027
|
+
type: "int",
|
|
1028
|
+
default: 512,
|
|
1029
|
+
title: "Height",
|
|
1030
|
+
description: "Height to fit to.",
|
|
1031
|
+
min: 1,
|
|
1032
|
+
max: 4096
|
|
1033
|
+
})
|
|
1034
|
+
], FitNode.prototype, "height", void 0);
|
|
1035
|
+
export class TextToImageNode extends BaseNode {
|
|
1036
|
+
static nodeType = "nodetool.image.TextToImage";
|
|
1037
|
+
static title = "Text To Image";
|
|
1038
|
+
static description = "Generate images from text prompts using any supported image provider. Automatically routes to the appropriate backend (HuggingFace, FAL, MLX).\n image, generation, AI, text-to-image, t2i";
|
|
1039
|
+
static metadataOutputTypes = {
|
|
1040
|
+
output: "image"
|
|
1041
|
+
};
|
|
1042
|
+
static basicFields = [
|
|
1043
|
+
"model",
|
|
1044
|
+
"prompt",
|
|
1045
|
+
"aspect_ratio",
|
|
1046
|
+
"resolution"
|
|
1047
|
+
];
|
|
1048
|
+
static exposeAsTool = true;
|
|
1049
|
+
static autoSaveAsset = true;
|
|
1050
|
+
async process(context) {
|
|
1051
|
+
const prompt = String(this.prompt ?? "");
|
|
1052
|
+
const aspectRatio = String(this.aspect_ratio ?? "1:1");
|
|
1053
|
+
const resolution = String(this.resolution ?? "1K");
|
|
1054
|
+
const { width, height } = resolveImageSize(resolution, aspectRatio);
|
|
1055
|
+
const { providerId, modelId } = getModelConfig(this.serialize());
|
|
1056
|
+
if (!hasProviderSupport(context, providerId, modelId)) {
|
|
1057
|
+
throw new Error("No provider available for text-to-image generation.");
|
|
1058
|
+
}
|
|
1059
|
+
const output = (await context.runProviderPrediction({
|
|
1060
|
+
provider: providerId,
|
|
1061
|
+
capability: "text_to_image",
|
|
1062
|
+
model: modelId,
|
|
1063
|
+
params: {
|
|
1064
|
+
prompt,
|
|
1065
|
+
width,
|
|
1066
|
+
height,
|
|
1067
|
+
aspect_ratio: aspectRatio,
|
|
1068
|
+
resolution,
|
|
1069
|
+
negative_prompt: this.negative_prompt
|
|
1070
|
+
}
|
|
1071
|
+
}));
|
|
1072
|
+
const meta = await metadataFor(output);
|
|
1073
|
+
return {
|
|
1074
|
+
output: imageRef(output, {
|
|
1075
|
+
mimeType: inferImageMime(undefined, output),
|
|
1076
|
+
width: meta.width ?? width,
|
|
1077
|
+
height: meta.height ?? height
|
|
1078
|
+
})
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
__decorate([
|
|
1083
|
+
prop({
|
|
1084
|
+
type: "image_model",
|
|
1085
|
+
default: {
|
|
1086
|
+
type: "image_model",
|
|
1087
|
+
provider: "huggingface_fal_ai",
|
|
1088
|
+
id: "fal-ai/flux/schnell",
|
|
1089
|
+
name: "FLUX.1 Schnell",
|
|
1090
|
+
path: null,
|
|
1091
|
+
supported_tasks: []
|
|
1092
|
+
},
|
|
1093
|
+
title: "Model",
|
|
1094
|
+
description: "The image generation model to use"
|
|
1095
|
+
})
|
|
1096
|
+
], TextToImageNode.prototype, "model", void 0);
|
|
1097
|
+
__decorate([
|
|
1098
|
+
prop({
|
|
1099
|
+
type: "str",
|
|
1100
|
+
default: "A cat holding a sign that says hello world",
|
|
1101
|
+
title: "Prompt",
|
|
1102
|
+
description: "Text prompt describing the desired image"
|
|
1103
|
+
})
|
|
1104
|
+
], TextToImageNode.prototype, "prompt", void 0);
|
|
1105
|
+
__decorate([
|
|
1106
|
+
prop({
|
|
1107
|
+
type: "str",
|
|
1108
|
+
default: "",
|
|
1109
|
+
title: "Negative Prompt",
|
|
1110
|
+
description: "Text prompt describing what to avoid in the image"
|
|
1111
|
+
})
|
|
1112
|
+
], TextToImageNode.prototype, "negative_prompt", void 0);
|
|
1113
|
+
__decorate([
|
|
1114
|
+
prop({
|
|
1115
|
+
type: "str",
|
|
1116
|
+
default: "1:1",
|
|
1117
|
+
title: "Aspect Ratio",
|
|
1118
|
+
description: "Aspect ratio of the generated image",
|
|
1119
|
+
values: IMAGE_ASPECT_RATIO_VALUES,
|
|
1120
|
+
json_schema_extra: { type: "media_aspect_ratio_image" }
|
|
1121
|
+
})
|
|
1122
|
+
], TextToImageNode.prototype, "aspect_ratio", void 0);
|
|
1123
|
+
__decorate([
|
|
1124
|
+
prop({
|
|
1125
|
+
type: "str",
|
|
1126
|
+
default: "1K",
|
|
1127
|
+
title: "Resolution",
|
|
1128
|
+
description: "Output resolution (short edge in pixels)",
|
|
1129
|
+
values: IMAGE_RESOLUTION_VALUES,
|
|
1130
|
+
json_schema_extra: { type: "media_resolution_image" }
|
|
1131
|
+
})
|
|
1132
|
+
], TextToImageNode.prototype, "resolution", void 0);
|
|
1133
|
+
__decorate([
|
|
1134
|
+
prop({
|
|
1135
|
+
type: "int",
|
|
1136
|
+
default: 0,
|
|
1137
|
+
title: "Timeout Seconds",
|
|
1138
|
+
description: "Timeout in seconds for API calls (0 = use provider default)",
|
|
1139
|
+
min: 0,
|
|
1140
|
+
max: 3600
|
|
1141
|
+
})
|
|
1142
|
+
], TextToImageNode.prototype, "timeout_seconds", void 0);
|
|
1143
|
+
export class ImageToImageNode extends BaseNode {
|
|
1144
|
+
static nodeType = "nodetool.image.ImageToImage";
|
|
1145
|
+
static title = "Image To Image";
|
|
1146
|
+
static description = "Transform images using text prompts with any supported image provider. Automatically routes to the appropriate backend (HuggingFace, FAL, MLX).\n image, transformation, AI, image-to-image, i2i";
|
|
1147
|
+
static metadataOutputTypes = {
|
|
1148
|
+
output: "image"
|
|
1149
|
+
};
|
|
1150
|
+
static basicFields = [
|
|
1151
|
+
"model",
|
|
1152
|
+
"image",
|
|
1153
|
+
"prompt",
|
|
1154
|
+
"strength",
|
|
1155
|
+
"aspect_ratio",
|
|
1156
|
+
"resolution"
|
|
1157
|
+
];
|
|
1158
|
+
static autoSaveAsset = true;
|
|
1159
|
+
static exposeAsTool = true;
|
|
1160
|
+
async process(context) {
|
|
1161
|
+
const image = (this.image ?? {});
|
|
1162
|
+
const bytes = await imageBytesAsync(image, context);
|
|
1163
|
+
if (bytes.length === 0) {
|
|
1164
|
+
throw new Error("The input image is empty.");
|
|
1165
|
+
}
|
|
1166
|
+
const aspectRatio = String(this.aspect_ratio ?? "1:1");
|
|
1167
|
+
const resolution = String(this.resolution ?? "1K");
|
|
1168
|
+
const { width, height } = resolveImageSize(resolution, aspectRatio);
|
|
1169
|
+
const { providerId, modelId } = getModelConfig(this.serialize());
|
|
1170
|
+
if (!hasProviderSupport(context, providerId, modelId)) {
|
|
1171
|
+
throw new Error("No provider available for image-to-image generation.");
|
|
1172
|
+
}
|
|
1173
|
+
const output = (await context.runProviderPrediction({
|
|
1174
|
+
provider: providerId,
|
|
1175
|
+
capability: "image_to_image",
|
|
1176
|
+
model: modelId,
|
|
1177
|
+
params: {
|
|
1178
|
+
image: bytes,
|
|
1179
|
+
prompt: String(this.prompt ?? ""),
|
|
1180
|
+
negative_prompt: this.negative_prompt,
|
|
1181
|
+
target_width: width,
|
|
1182
|
+
target_height: height,
|
|
1183
|
+
aspect_ratio: aspectRatio,
|
|
1184
|
+
resolution,
|
|
1185
|
+
strength: this.strength,
|
|
1186
|
+
scheduler: this.scheduler
|
|
1187
|
+
}
|
|
1188
|
+
}));
|
|
1189
|
+
const meta = await metadataFor(output);
|
|
1190
|
+
return {
|
|
1191
|
+
output: imageRef(output, {
|
|
1192
|
+
uri: image.uri ?? "",
|
|
1193
|
+
mimeType: inferImageMime(image.uri, output),
|
|
1194
|
+
width: meta.width,
|
|
1195
|
+
height: meta.height
|
|
1196
|
+
})
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
__decorate([
|
|
1201
|
+
prop({
|
|
1202
|
+
type: "image_model",
|
|
1203
|
+
default: {
|
|
1204
|
+
type: "image_model",
|
|
1205
|
+
provider: "huggingface_fal_ai",
|
|
1206
|
+
id: "fal-ai/flux/dev",
|
|
1207
|
+
name: "FLUX.1 Dev",
|
|
1208
|
+
path: null,
|
|
1209
|
+
supported_tasks: []
|
|
1210
|
+
},
|
|
1211
|
+
title: "Model",
|
|
1212
|
+
description: "The image generation model to use"
|
|
1213
|
+
})
|
|
1214
|
+
], ImageToImageNode.prototype, "model", void 0);
|
|
1215
|
+
__decorate([
|
|
1216
|
+
prop({
|
|
1217
|
+
type: "image",
|
|
1218
|
+
default: {
|
|
1219
|
+
type: "image",
|
|
1220
|
+
uri: "",
|
|
1221
|
+
asset_id: null,
|
|
1222
|
+
data: null,
|
|
1223
|
+
metadata: null
|
|
1224
|
+
},
|
|
1225
|
+
title: "Image",
|
|
1226
|
+
description: "Input image to transform"
|
|
1227
|
+
})
|
|
1228
|
+
], ImageToImageNode.prototype, "image", void 0);
|
|
1229
|
+
__decorate([
|
|
1230
|
+
prop({
|
|
1231
|
+
type: "str",
|
|
1232
|
+
default: "A photorealistic version of the input image",
|
|
1233
|
+
title: "Prompt",
|
|
1234
|
+
description: "Text prompt describing the desired transformation"
|
|
1235
|
+
})
|
|
1236
|
+
], ImageToImageNode.prototype, "prompt", void 0);
|
|
1237
|
+
__decorate([
|
|
1238
|
+
prop({
|
|
1239
|
+
type: "str",
|
|
1240
|
+
default: "",
|
|
1241
|
+
title: "Negative Prompt",
|
|
1242
|
+
description: "Text prompt describing what to avoid"
|
|
1243
|
+
})
|
|
1244
|
+
], ImageToImageNode.prototype, "negative_prompt", void 0);
|
|
1245
|
+
__decorate([
|
|
1246
|
+
prop({
|
|
1247
|
+
type: "float",
|
|
1248
|
+
default: 0.65,
|
|
1249
|
+
title: "Strength",
|
|
1250
|
+
description: "How much to transform the input image (subtle = minor edit, strong = major edit)",
|
|
1251
|
+
values: IMAGE_EDIT_STRENGTH_VALUES,
|
|
1252
|
+
json_schema_extra: { type: "media_strength" }
|
|
1253
|
+
})
|
|
1254
|
+
], ImageToImageNode.prototype, "strength", void 0);
|
|
1255
|
+
__decorate([
|
|
1256
|
+
prop({
|
|
1257
|
+
type: "str",
|
|
1258
|
+
default: "1:1",
|
|
1259
|
+
title: "Aspect Ratio",
|
|
1260
|
+
description: "Aspect ratio of the output image",
|
|
1261
|
+
values: IMAGE_ASPECT_RATIO_VALUES,
|
|
1262
|
+
json_schema_extra: { type: "media_aspect_ratio_image" }
|
|
1263
|
+
})
|
|
1264
|
+
], ImageToImageNode.prototype, "aspect_ratio", void 0);
|
|
1265
|
+
__decorate([
|
|
1266
|
+
prop({
|
|
1267
|
+
type: "str",
|
|
1268
|
+
default: "1K",
|
|
1269
|
+
title: "Resolution",
|
|
1270
|
+
description: "Output resolution (short edge in pixels)",
|
|
1271
|
+
values: IMAGE_RESOLUTION_VALUES,
|
|
1272
|
+
json_schema_extra: { type: "media_resolution_image" }
|
|
1273
|
+
})
|
|
1274
|
+
], ImageToImageNode.prototype, "resolution", void 0);
|
|
1275
|
+
__decorate([
|
|
1276
|
+
prop({
|
|
1277
|
+
type: "str",
|
|
1278
|
+
default: "",
|
|
1279
|
+
title: "Scheduler",
|
|
1280
|
+
description: "Scheduler to use (provider-specific)"
|
|
1281
|
+
})
|
|
1282
|
+
], ImageToImageNode.prototype, "scheduler", void 0);
|
|
1283
|
+
__decorate([
|
|
1284
|
+
prop({
|
|
1285
|
+
type: "int",
|
|
1286
|
+
default: 0,
|
|
1287
|
+
title: "Timeout Seconds",
|
|
1288
|
+
description: "Timeout in seconds for API calls (0 = use provider default)",
|
|
1289
|
+
min: 0,
|
|
1290
|
+
max: 3600
|
|
1291
|
+
})
|
|
1292
|
+
], ImageToImageNode.prototype, "timeout_seconds", void 0);
|
|
1293
|
+
export class RotateNode extends TransformImageNode {
|
|
1294
|
+
static nodeType = "nodetool.image.Rotate";
|
|
1295
|
+
static title = "Rotate";
|
|
1296
|
+
static description = "Rotate an image by a specified angle in degrees.\n image, rotate, angle, transform, orientation";
|
|
1297
|
+
static metadataOutputTypes = {
|
|
1298
|
+
output: "image"
|
|
1299
|
+
};
|
|
1300
|
+
async process(context) {
|
|
1301
|
+
const image = (this.image ?? {});
|
|
1302
|
+
const angle = Number(this.angle ?? 0);
|
|
1303
|
+
if (angle === 0) {
|
|
1304
|
+
const bytes = await imageBytesAsync(image, context);
|
|
1305
|
+
return {
|
|
1306
|
+
output: imageRef(bytes, {
|
|
1307
|
+
uri: image.uri ?? "",
|
|
1308
|
+
width: image.width ?? undefined,
|
|
1309
|
+
height: image.height ?? undefined
|
|
1310
|
+
})
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
return transformImage(image, (instance) => instance.rotate(angle, { background: { r: 0, g: 0, b: 0, alpha: 0 } }), context);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
__decorate([
|
|
1317
|
+
prop({
|
|
1318
|
+
type: "image",
|
|
1319
|
+
default: {
|
|
1320
|
+
type: "image",
|
|
1321
|
+
uri: "",
|
|
1322
|
+
asset_id: null,
|
|
1323
|
+
data: null,
|
|
1324
|
+
metadata: null
|
|
1325
|
+
},
|
|
1326
|
+
title: "Image",
|
|
1327
|
+
description: "The image to rotate."
|
|
1328
|
+
})
|
|
1329
|
+
], RotateNode.prototype, "image", void 0);
|
|
1330
|
+
__decorate([
|
|
1331
|
+
prop({
|
|
1332
|
+
type: "float",
|
|
1333
|
+
default: 0,
|
|
1334
|
+
title: "Angle",
|
|
1335
|
+
description: "Rotation angle in degrees (clockwise).",
|
|
1336
|
+
min: -360,
|
|
1337
|
+
max: 360
|
|
1338
|
+
})
|
|
1339
|
+
], RotateNode.prototype, "angle", void 0);
|
|
1340
|
+
export class FlipNode extends TransformImageNode {
|
|
1341
|
+
static nodeType = "nodetool.image.Flip";
|
|
1342
|
+
static title = "Flip";
|
|
1343
|
+
static description = "Flip an image horizontally or vertically.\n image, flip, mirror, horizontal, vertical, transform";
|
|
1344
|
+
static metadataOutputTypes = {
|
|
1345
|
+
output: "image"
|
|
1346
|
+
};
|
|
1347
|
+
async process(context) {
|
|
1348
|
+
const image = (this.image ?? {});
|
|
1349
|
+
const direction = String(this.direction ?? "horizontal");
|
|
1350
|
+
return transformImage(image, (instance) => direction === "vertical" ? instance.flip() : instance.flop(), context);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
__decorate([
|
|
1354
|
+
prop({
|
|
1355
|
+
type: "image",
|
|
1356
|
+
default: {
|
|
1357
|
+
type: "image",
|
|
1358
|
+
uri: "",
|
|
1359
|
+
asset_id: null,
|
|
1360
|
+
data: null,
|
|
1361
|
+
metadata: null
|
|
1362
|
+
},
|
|
1363
|
+
title: "Image",
|
|
1364
|
+
description: "The image to flip."
|
|
1365
|
+
})
|
|
1366
|
+
], FlipNode.prototype, "image", void 0);
|
|
1367
|
+
__decorate([
|
|
1368
|
+
prop({
|
|
1369
|
+
type: "str",
|
|
1370
|
+
default: "horizontal",
|
|
1371
|
+
title: "Direction",
|
|
1372
|
+
description: "Flip direction.",
|
|
1373
|
+
values: ["horizontal", "vertical"]
|
|
1374
|
+
})
|
|
1375
|
+
], FlipNode.prototype, "direction", void 0);
|
|
1376
|
+
export const IMAGE_NODES = [
|
|
1377
|
+
LoadImageFileNode,
|
|
1378
|
+
LoadImageFolderNode,
|
|
1379
|
+
SaveImageFileImageNode,
|
|
1380
|
+
LoadImageAssetsNode,
|
|
1381
|
+
SaveImageNode,
|
|
1382
|
+
GetMetadataNode,
|
|
1383
|
+
BatchToListNode,
|
|
1384
|
+
ImagesToListNode,
|
|
1385
|
+
PasteNode,
|
|
1386
|
+
ScaleNode,
|
|
1387
|
+
ResizeNode,
|
|
1388
|
+
CropNode,
|
|
1389
|
+
FitNode,
|
|
1390
|
+
RotateNode,
|
|
1391
|
+
FlipNode,
|
|
1392
|
+
TextToImageNode,
|
|
1393
|
+
ImageToImageNode
|
|
1394
|
+
];
|
|
1395
|
+
//# sourceMappingURL=image.js.map
|