tongflow 0.2.0 → 1.0.0
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/LICENSE +661 -0
- package/README.md +3 -2
- package/abi/tongflow.abi.json +49 -4
- package/dist/canvas-messages.cjs +71 -63
- package/dist/canvas-messages.js +71 -63
- package/dist/canvas.cjs +860 -260
- package/dist/canvas.css +1 -1
- package/dist/canvas.d.cts +158 -12
- package/dist/canvas.d.ts +158 -12
- package/dist/canvas.js +856 -261
- package/dist/index.cjs +659 -12
- package/dist/index.d.cts +317 -22
- package/dist/index.d.ts +317 -22
- package/dist/index.js +651 -13
- package/package.json +235 -233
package/dist/canvas.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Background, BaseEdge, Controls, EdgeLabelRenderer, Handle, Panel, Position, ReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, getBezierPath, reconnectEdge, useNodeId, useNodesData, useReactFlow, useStore, useStoreApi } from "@xyflow/react";
|
|
3
|
-
import { AlertCircle, Atom, AudioLines, Box, Brain, Brush, Camera, CheckCircle, CheckIcon, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, Circle, CircleIcon, Clapperboard, Clock, Combine, Download, Droplets, Ear, Edit3, Eye, File as File$1, FileArchive, FileCode, FileSpreadsheet, FileText, Film, HelpCircle, Image as Image$1, ImageIcon, Layers, Library, Lightbulb, Link, Loader2, Lock, Maximize2, MenuIcon, MessageSquare, Mic, MicOff, Move3d, Music, PackagePlus, Palette, Pause, Pencil, PersonStanding, Play, Plus, Presentation, Puzzle, RectangleHorizontal, RefreshCw, RotateCcw, Scissors, Sparkles, Split, Square, StopCircle, Tag, Trash, Trash2, Type, Unlock, Upload, Video, VolumeX, Wand2, X, XIcon } from "lucide-react";
|
|
3
|
+
import { AlertCircle, Atom, AudioLines, AudioWaveform, Box, Brain, Brush, Camera, CheckCircle, CheckIcon, ChevronDownIcon, ChevronRight, ChevronRightIcon, ChevronUpIcon, Circle, CircleIcon, Clapperboard, Clock, Combine, Download, Droplets, Ear, Edit3, Eye, File as File$1, FileArchive, FileCode, FileSpreadsheet, FileText, Film, HelpCircle, Image as Image$1, ImageIcon, Layers, Library, Lightbulb, Link, Loader2, Lock, Maximize2, MenuIcon, MessageSquare, Mic, MicOff, Move3d, Music, PackagePlus, Palette, Pause, Pencil, PersonStanding, Play, Plus, Presentation, Puzzle, RectangleHorizontal, RefreshCw, RotateCcw, Scissors, Sparkles, Split, Square, StopCircle, Tag, Trash, Trash2, Type, Unlock, Upload, Video, VolumeX, Wand2, X, XIcon } from "lucide-react";
|
|
4
4
|
import * as React$1 from "react";
|
|
5
5
|
import React, { Component, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
6
6
|
import toast from "react-hot-toast";
|
|
@@ -23,9 +23,9 @@ import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
|
23
23
|
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
|
24
24
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
25
25
|
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
26
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
26
27
|
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
27
28
|
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
28
|
-
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
29
29
|
import { createPortal } from "react-dom";
|
|
30
30
|
import { Masonry } from "masonic";
|
|
31
31
|
import * as THREE from "three";
|
|
@@ -545,7 +545,11 @@ const ABI_NODES = {
|
|
|
545
545
|
properties: {
|
|
546
546
|
success: { type: "boolean" },
|
|
547
547
|
error: { type: "string" },
|
|
548
|
-
text: { type: "string" }
|
|
548
|
+
text: { type: "string" },
|
|
549
|
+
images: {
|
|
550
|
+
type: "array",
|
|
551
|
+
items: { $ref: "#/$defs/ImageRef" }
|
|
552
|
+
}
|
|
549
553
|
},
|
|
550
554
|
additionalProperties: false
|
|
551
555
|
}
|
|
@@ -661,6 +665,34 @@ const ABI_NODES = {
|
|
|
661
665
|
additionalProperties: false
|
|
662
666
|
}
|
|
663
667
|
},
|
|
668
|
+
"text-gen-audio": {
|
|
669
|
+
inputs: {
|
|
670
|
+
type: "object",
|
|
671
|
+
required: ["text"],
|
|
672
|
+
properties: {
|
|
673
|
+
text: {
|
|
674
|
+
type: "string",
|
|
675
|
+
minLength: 1
|
|
676
|
+
},
|
|
677
|
+
ref_audios: {
|
|
678
|
+
type: "array",
|
|
679
|
+
items: { $ref: "#/$defs/Asset" }
|
|
680
|
+
},
|
|
681
|
+
seed: { type: "integer" }
|
|
682
|
+
},
|
|
683
|
+
additionalProperties: false
|
|
684
|
+
},
|
|
685
|
+
outputs: {
|
|
686
|
+
type: "object",
|
|
687
|
+
required: ["success"],
|
|
688
|
+
properties: {
|
|
689
|
+
success: { type: "boolean" },
|
|
690
|
+
error: { type: "string" },
|
|
691
|
+
audio: { $ref: "#/$defs/AudioRef" }
|
|
692
|
+
},
|
|
693
|
+
additionalProperties: false
|
|
694
|
+
}
|
|
695
|
+
},
|
|
664
696
|
"text-gen-speech-preset": {
|
|
665
697
|
inputs: {
|
|
666
698
|
type: "object",
|
|
@@ -1530,10 +1562,10 @@ const ABI_NODES = {
|
|
|
1530
1562
|
convert_voice: {
|
|
1531
1563
|
inputs: {
|
|
1532
1564
|
type: "object",
|
|
1533
|
-
required: ["
|
|
1565
|
+
required: ["audio", "ref_audio"],
|
|
1534
1566
|
properties: {
|
|
1535
|
-
|
|
1536
|
-
|
|
1567
|
+
audio: { $ref: "#/$defs/Asset" },
|
|
1568
|
+
ref_audio: { $ref: "#/$defs/Asset" }
|
|
1537
1569
|
},
|
|
1538
1570
|
additionalProperties: false
|
|
1539
1571
|
},
|
|
@@ -2140,6 +2172,7 @@ const NODE_TYPE_TO_ABI_FEATURE = {
|
|
|
2140
2172
|
imageGenImageUpscaleNode: "image-upscale",
|
|
2141
2173
|
textGenImageNode: "image-gen",
|
|
2142
2174
|
textGenMusicNode: "gen-music",
|
|
2175
|
+
textGenAudioNode: "text-gen-audio",
|
|
2143
2176
|
textGenSpeechCloneNode: "text-gen-speech-clone",
|
|
2144
2177
|
textGenSpeechCloneComposeNode: "text-gen-speech-clone",
|
|
2145
2178
|
imageGenVideoComposeNode: "image-gen-video",
|
|
@@ -2263,6 +2296,18 @@ const NODE_TYPE_SOURCE_SPEC = {
|
|
|
2263
2296
|
manual: true
|
|
2264
2297
|
})
|
|
2265
2298
|
},
|
|
2299
|
+
musicCoverNode: {
|
|
2300
|
+
text: handle({
|
|
2301
|
+
nodeType: "textNode",
|
|
2302
|
+
path: "texts[0]",
|
|
2303
|
+
manual: true
|
|
2304
|
+
}),
|
|
2305
|
+
lyrics: handle({
|
|
2306
|
+
nodeType: "textNode",
|
|
2307
|
+
path: "texts[0]",
|
|
2308
|
+
manual: true
|
|
2309
|
+
})
|
|
2310
|
+
},
|
|
2266
2311
|
textGenSpeechCloneComposeNode: { text: batchOn({
|
|
2267
2312
|
nodeType: "textNode",
|
|
2268
2313
|
path: "texts"
|
|
@@ -2300,7 +2345,10 @@ const NODE_TYPE_SOURCE_SPEC = {
|
|
|
2300
2345
|
audioGenTextSpeechRecognizeNode: { audio: batchOn() },
|
|
2301
2346
|
concatVideoComposeNode: { videos: collectAll() },
|
|
2302
2347
|
concatVideoNode: { videos: collectAll() },
|
|
2303
|
-
convertVoiceNode: {
|
|
2348
|
+
convertVoiceNode: {
|
|
2349
|
+
audio: batchOn({ nodeType: "audioNode" }),
|
|
2350
|
+
ref_audio: handle({ nodeType: "audioNode" })
|
|
2351
|
+
},
|
|
2304
2352
|
denoiseAudioSubtitleNode: { fileKey: batchOn({ nodeType: "audioNode" }) },
|
|
2305
2353
|
dropVideoNode: { videos: collectAll() },
|
|
2306
2354
|
extractAudioNode: { video: batchOn() },
|
|
@@ -2348,6 +2396,14 @@ const NODE_TYPE_SOURCE_SPEC = {
|
|
|
2348
2396
|
path: "texts[0]"
|
|
2349
2397
|
})
|
|
2350
2398
|
},
|
|
2399
|
+
textGenAudioNode: {
|
|
2400
|
+
text: handle({
|
|
2401
|
+
nodeType: "textNode",
|
|
2402
|
+
path: "texts[0]",
|
|
2403
|
+
manual: true
|
|
2404
|
+
}),
|
|
2405
|
+
ref_audios: collectAll({ nodeType: "audioNode" })
|
|
2406
|
+
},
|
|
2351
2407
|
textGenSpeechCloneNode: {
|
|
2352
2408
|
text: batchOn({
|
|
2353
2409
|
nodeType: "textNode",
|
|
@@ -3831,7 +3887,11 @@ var tongflow_abi_default = {
|
|
|
3831
3887
|
"properties": {
|
|
3832
3888
|
"success": { "type": "boolean" },
|
|
3833
3889
|
"error": { "type": "string" },
|
|
3834
|
-
"text": { "type": "string" }
|
|
3890
|
+
"text": { "type": "string" },
|
|
3891
|
+
"images": {
|
|
3892
|
+
"type": "array",
|
|
3893
|
+
"items": { "$ref": "#/$defs/ImageRef" }
|
|
3894
|
+
}
|
|
3835
3895
|
},
|
|
3836
3896
|
"additionalProperties": false
|
|
3837
3897
|
}
|
|
@@ -3951,6 +4011,35 @@ var tongflow_abi_default = {
|
|
|
3951
4011
|
"additionalProperties": false
|
|
3952
4012
|
}
|
|
3953
4013
|
},
|
|
4014
|
+
{
|
|
4015
|
+
"nodeSlot": "text-gen-audio",
|
|
4016
|
+
"inputs": {
|
|
4017
|
+
"type": "object",
|
|
4018
|
+
"required": ["text"],
|
|
4019
|
+
"properties": {
|
|
4020
|
+
"text": {
|
|
4021
|
+
"type": "string",
|
|
4022
|
+
"minLength": 1
|
|
4023
|
+
},
|
|
4024
|
+
"ref_audios": {
|
|
4025
|
+
"type": "array",
|
|
4026
|
+
"items": { "$ref": "#/$defs/Asset" }
|
|
4027
|
+
},
|
|
4028
|
+
"seed": { "type": "integer" }
|
|
4029
|
+
},
|
|
4030
|
+
"additionalProperties": false
|
|
4031
|
+
},
|
|
4032
|
+
"outputs": {
|
|
4033
|
+
"type": "object",
|
|
4034
|
+
"required": ["success"],
|
|
4035
|
+
"properties": {
|
|
4036
|
+
"success": { "type": "boolean" },
|
|
4037
|
+
"error": { "type": "string" },
|
|
4038
|
+
"audio": { "$ref": "#/$defs/AudioRef" }
|
|
4039
|
+
},
|
|
4040
|
+
"additionalProperties": false
|
|
4041
|
+
}
|
|
4042
|
+
},
|
|
3954
4043
|
{
|
|
3955
4044
|
"nodeSlot": "text-gen-speech-preset",
|
|
3956
4045
|
"inputs": {
|
|
@@ -4853,10 +4942,10 @@ var tongflow_abi_default = {
|
|
|
4853
4942
|
"nodeSlot": "convert_voice",
|
|
4854
4943
|
"inputs": {
|
|
4855
4944
|
"type": "object",
|
|
4856
|
-
"required": ["
|
|
4945
|
+
"required": ["audio", "ref_audio"],
|
|
4857
4946
|
"properties": {
|
|
4858
|
-
"
|
|
4859
|
-
"
|
|
4947
|
+
"audio": { "$ref": "#/$defs/Asset" },
|
|
4948
|
+
"ref_audio": { "$ref": "#/$defs/Asset" }
|
|
4860
4949
|
},
|
|
4861
4950
|
"additionalProperties": false
|
|
4862
4951
|
},
|
|
@@ -5139,6 +5228,103 @@ parsed.nodes;
|
|
|
5139
5228
|
function getAbiNodeBySlot(nodeSlot) {
|
|
5140
5229
|
return bySlot.get(nodeSlot);
|
|
5141
5230
|
}
|
|
5231
|
+
//#endregion
|
|
5232
|
+
//#region src/core/registry/model-catalog.ts
|
|
5233
|
+
/** Read a dot-path (`a.b.c`) off an unknown JSON value; `undefined` when absent. */
|
|
5234
|
+
function getPath(value, path) {
|
|
5235
|
+
let cur = value;
|
|
5236
|
+
for (const key of path.split(".")) {
|
|
5237
|
+
if (cur === null || typeof cur !== "object") return void 0;
|
|
5238
|
+
cur = cur[key];
|
|
5239
|
+
}
|
|
5240
|
+
return cur;
|
|
5241
|
+
}
|
|
5242
|
+
/** Substring match against the field's JSON serialization (strings as-is). */
|
|
5243
|
+
function fieldContains(value, token) {
|
|
5244
|
+
if (value === void 0 || value === null) return false;
|
|
5245
|
+
return (typeof value === "string" ? value : JSON.stringify(value)).includes(token);
|
|
5246
|
+
}
|
|
5247
|
+
/**
|
|
5248
|
+
* Filter a fetched catalog payload into `slot -> model ids`, applying the
|
|
5249
|
+
* plugin's declared `exclude` and per-slot `slots` rules (substring tokens,
|
|
5250
|
+
* `!`-prefixed = must be absent). Ids keep catalog order; malformed records
|
|
5251
|
+
* are skipped. Pure — the fetch lives in the canvas.
|
|
5252
|
+
*/
|
|
5253
|
+
function filterModelCatalog(catalog, payload) {
|
|
5254
|
+
const out = {};
|
|
5255
|
+
for (const slot of Object.keys(catalog.slots)) out[slot] = [];
|
|
5256
|
+
const items = getPath(payload, catalog.items);
|
|
5257
|
+
if (!Array.isArray(items)) return out;
|
|
5258
|
+
const exclude = Object.entries(catalog.exclude ?? {});
|
|
5259
|
+
for (const record of items) {
|
|
5260
|
+
const id = getPath(record, catalog.id);
|
|
5261
|
+
if (typeof id !== "string" || !id.trim()) continue;
|
|
5262
|
+
if (exclude.some(([field, literal]) => getPath(record, field) === literal)) continue;
|
|
5263
|
+
for (const [slot, rules] of Object.entries(catalog.slots)) if (Object.entries(rules).every(([field, tokens]) => {
|
|
5264
|
+
const value = getPath(record, field);
|
|
5265
|
+
return (Array.isArray(tokens) ? tokens : [tokens]).every((token) => token.startsWith("!") ? !fieldContains(value, token.slice(1)) : fieldContains(value, token));
|
|
5266
|
+
}) && !out[slot].includes(id)) out[slot].push(id);
|
|
5267
|
+
}
|
|
5268
|
+
return out;
|
|
5269
|
+
}
|
|
5270
|
+
//#endregion
|
|
5271
|
+
//#region src/core/registry/plugin-params.ts
|
|
5272
|
+
/**
|
|
5273
|
+
* The params a node offers for the active plugin + model: every declared entry
|
|
5274
|
+
* whose `models` gate is absent or names the selected model.
|
|
5275
|
+
*/
|
|
5276
|
+
function visiblePluginParams(params, model) {
|
|
5277
|
+
if (!params) return [];
|
|
5278
|
+
const out = [];
|
|
5279
|
+
for (const [name, spec] of Object.entries(params)) {
|
|
5280
|
+
if (spec.models && !(model && spec.models.includes(model))) continue;
|
|
5281
|
+
out.push({
|
|
5282
|
+
name,
|
|
5283
|
+
spec
|
|
5284
|
+
});
|
|
5285
|
+
}
|
|
5286
|
+
return out;
|
|
5287
|
+
}
|
|
5288
|
+
/** True when `value` is a legal setting for `spec` (type + range/options). */
|
|
5289
|
+
function isValidPluginParamValue(spec, value) {
|
|
5290
|
+
switch (spec.type) {
|
|
5291
|
+
case "select": return (spec.options ?? []).some((o) => o === value);
|
|
5292
|
+
case "boolean": return typeof value === "boolean";
|
|
5293
|
+
case "text": return typeof value === "string";
|
|
5294
|
+
case "number":
|
|
5295
|
+
case "integer":
|
|
5296
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return false;
|
|
5297
|
+
if (spec.type === "integer" && !Number.isInteger(value)) return false;
|
|
5298
|
+
if (spec.min !== void 0 && value < spec.min) return false;
|
|
5299
|
+
if (spec.max !== void 0 && value > spec.max) return false;
|
|
5300
|
+
return true;
|
|
5301
|
+
default: return false;
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
/**
|
|
5305
|
+
* Keep only the entries of `values` that the visible params accept and that
|
|
5306
|
+
* differ from the declared default. Unset means "plugin default", so a value
|
|
5307
|
+
* equal to it is dropped; the result is what travels to the plugin.
|
|
5308
|
+
*/
|
|
5309
|
+
function normalizePluginParams(entries, values) {
|
|
5310
|
+
const out = {};
|
|
5311
|
+
if (!values) return out;
|
|
5312
|
+
for (const { name, spec } of entries) {
|
|
5313
|
+
if (!(name in values)) continue;
|
|
5314
|
+
const v = values[name];
|
|
5315
|
+
if (!isValidPluginParamValue(spec, v)) continue;
|
|
5316
|
+
if (spec.default !== void 0 && v === spec.default) continue;
|
|
5317
|
+
out[name] = v;
|
|
5318
|
+
}
|
|
5319
|
+
return out;
|
|
5320
|
+
}
|
|
5321
|
+
/** Shallow key/value equality for two param records. */
|
|
5322
|
+
function samePluginParams(a, b) {
|
|
5323
|
+
const ka = Object.keys(a ?? {});
|
|
5324
|
+
const kb = Object.keys(b ?? {});
|
|
5325
|
+
if (ka.length !== kb.length) return false;
|
|
5326
|
+
return ka.every((k) => Object.is(a?.[k], b?.[k]));
|
|
5327
|
+
}
|
|
5142
5328
|
/**
|
|
5143
5329
|
* Deep-copy the current canvas state for the history stack, stripping
|
|
5144
5330
|
* transient interaction flags so undo never restores a stale selection
|
|
@@ -5233,6 +5419,10 @@ const NODE_SIZE_TABLE = {
|
|
|
5233
5419
|
w: 520,
|
|
5234
5420
|
h: 480
|
|
5235
5421
|
},
|
|
5422
|
+
textGenAudioNode: {
|
|
5423
|
+
w: 480,
|
|
5424
|
+
h: 480
|
|
5425
|
+
},
|
|
5236
5426
|
musicExtractNode: {
|
|
5237
5427
|
w: 420,
|
|
5238
5428
|
h: 400
|
|
@@ -5737,7 +5927,7 @@ function createFlowSlice(set, get, options = {}) {
|
|
|
5737
5927
|
if (newlyCreatedIds.length > 0) get().nodeCreatedCallbacks.forEach((cb) => cb(newlyCreatedIds));
|
|
5738
5928
|
return ids;
|
|
5739
5929
|
},
|
|
5740
|
-
compose: ({ type, data }) => {
|
|
5930
|
+
compose: ({ type, data, sourceOrder }) => {
|
|
5741
5931
|
get().commitHistory();
|
|
5742
5932
|
const { comboSelectedIds, nodes, edges } = get();
|
|
5743
5933
|
const nodeId = createId();
|
|
@@ -5765,7 +5955,7 @@ function createFlowSlice(set, get, options = {}) {
|
|
|
5765
5955
|
data: data ?? {}
|
|
5766
5956
|
};
|
|
5767
5957
|
const usedTargetHandles = /* @__PURE__ */ new Set();
|
|
5768
|
-
const newEdges = Array.from(comboSelectedIds).map((id) => {
|
|
5958
|
+
const newEdges = (sourceOrder ? [...sourceOrder.filter((id) => comboSelectedIds.has(id)), ...Array.from(comboSelectedIds).filter((id) => !sourceOrder.includes(id))] : Array.from(comboSelectedIds)).map((id) => {
|
|
5769
5959
|
const node = nodes.find((n) => n.id === id);
|
|
5770
5960
|
if (!node) return null;
|
|
5771
5961
|
const { sourceHandle, targetHandle } = resolveEdgeHandles({
|
|
@@ -5971,6 +6161,7 @@ var en_default = {
|
|
|
5971
6161
|
"separateAudio": "Separate Vocals",
|
|
5972
6162
|
"separateSpeaker": "Speaker Separation",
|
|
5973
6163
|
"separateSound": "Extract Sound",
|
|
6164
|
+
"speakWithStyle": "Speak with Style",
|
|
5974
6165
|
"denoise": "Denoise",
|
|
5975
6166
|
"convertVoice": "Voice Replacement",
|
|
5976
6167
|
"filter": "Filter",
|
|
@@ -6108,6 +6299,9 @@ var en_default = {
|
|
|
6108
6299
|
"modalDeployProgressHint": "Deployed {done}/{total}",
|
|
6109
6300
|
"pluginImplementationTitle": "Implementation",
|
|
6110
6301
|
"pluginModelTitle": "Model",
|
|
6302
|
+
"pluginParamsTitle": "Advanced",
|
|
6303
|
+
"pluginParamsReset": "Reset",
|
|
6304
|
+
"pluginParamDefault": "default {value}",
|
|
6111
6305
|
"pluginImplementationLoading": "Loading plugin implementations…",
|
|
6112
6306
|
"pluginImplementationEmpty": "No plugin implementations were scanned for this capability. Add Modal or API plugins under the repo's plugins/ directory (see README), then reload this page.",
|
|
6113
6307
|
"pluginRegistryLoadError": "Could not load the plugin registry: {message}",
|
|
@@ -6233,11 +6427,7 @@ var en_default = {
|
|
|
6233
6427
|
"referenceTextPlaceholder": "Optional: transcript of the reference audio — improves clone quality",
|
|
6234
6428
|
"referenceTextCloneHint": "Leave empty for fast x-vector clone; paste the reference transcript for higher-quality ICL clone",
|
|
6235
6429
|
"language": "Language",
|
|
6236
|
-
"inputText": "Input Text"
|
|
6237
|
-
"voiceOptions": {
|
|
6238
|
-
"female": "Female Voice",
|
|
6239
|
-
"male": "Male Voice"
|
|
6240
|
-
}
|
|
6430
|
+
"inputText": "Input Text"
|
|
6241
6431
|
},
|
|
6242
6432
|
"titles": {
|
|
6243
6433
|
"imageGenVideo": "Image to Video",
|
|
@@ -6301,7 +6491,8 @@ var en_default = {
|
|
|
6301
6491
|
"musicExtract": "Extract Stem",
|
|
6302
6492
|
"musicLego": "Add Track",
|
|
6303
6493
|
"musicComplete": "Complete Arrangement",
|
|
6304
|
-
"musicBrief": "Music Brief"
|
|
6494
|
+
"musicBrief": "Music Brief",
|
|
6495
|
+
"textGenAudio": "Text to Audio"
|
|
6305
6496
|
},
|
|
6306
6497
|
"options": {
|
|
6307
6498
|
"portrait": "Portrait",
|
|
@@ -6608,15 +6799,9 @@ var en_default = {
|
|
|
6608
6799
|
"audioPromptPlaceholder": "Enter audio dubbing prompt"
|
|
6609
6800
|
},
|
|
6610
6801
|
"convertVoice": {
|
|
6611
|
-
"
|
|
6612
|
-
"
|
|
6613
|
-
"
|
|
6614
|
-
"uploadVoice": "Upload Voice",
|
|
6615
|
-
"recordVoice": "Record Voice",
|
|
6616
|
-
"recordAudio": "Record Audio",
|
|
6617
|
-
"recordHint": "You can record audio here.",
|
|
6618
|
-
"recordNeedImpl": "Audio recording feature needs implementation",
|
|
6619
|
-
"done": "Done"
|
|
6802
|
+
"sourceAudio": "Source audio",
|
|
6803
|
+
"referenceVoice": "Reference voice",
|
|
6804
|
+
"connectHint": "Connect the audio to convert and a reference voice clip"
|
|
6620
6805
|
},
|
|
6621
6806
|
"removeWatermark": { "hint": "Automatically detect and remove Sora video watermarks" },
|
|
6622
6807
|
"videoGenText": {
|
|
@@ -6810,6 +6995,12 @@ var en_default = {
|
|
|
6810
6995
|
"separateSound": {
|
|
6811
6996
|
"textLabel": "Sound to isolate",
|
|
6812
6997
|
"textPlaceholder": "e.g. dog barking, piano, sirens in the background"
|
|
6998
|
+
},
|
|
6999
|
+
"textGenAudio": {
|
|
7000
|
+
"voiceReference": "Reference voices",
|
|
7001
|
+
"promptLabel": "Audio prompt",
|
|
7002
|
+
"promptPlaceholder": "Describe the whole scene: who speaks and how, background ambience, sound effects. Quote the lines, e.g. @voice1 says: \"It's sunny today.\" Rain patters on the window.",
|
|
7003
|
+
"hint": "One pass renders speech, sound effects and ambience together. Connect up to 3 audio clips as reference voices and click one to insert @voice1…@voice3."
|
|
6813
7004
|
}
|
|
6814
7005
|
}
|
|
6815
7006
|
},
|
|
@@ -6872,6 +7063,7 @@ var en_default = {
|
|
|
6872
7063
|
"copy": "Copy",
|
|
6873
7064
|
"copied": "Copied",
|
|
6874
7065
|
"close": "Close",
|
|
7066
|
+
"openModalBilling": "Open Modal billing",
|
|
6875
7067
|
"needHelp": "Stuck? Come find us:",
|
|
6876
7068
|
"wechatGroup": "WeChat group",
|
|
6877
7069
|
"wechatQrAlt": "WeChat group QR code"
|
|
@@ -6958,7 +7150,7 @@ var ja_default = {
|
|
|
6958
7150
|
"saveSuccess": "保存しました!",
|
|
6959
7151
|
"saveFailed": "保存に失敗しました。もう一度お試しください",
|
|
6960
7152
|
"generateVideo": "動画生成",
|
|
6961
|
-
"generateAudio": "
|
|
7153
|
+
"generateAudio": "オーディオ生成",
|
|
6962
7154
|
"generateText": "テキスト生成",
|
|
6963
7155
|
"generateImage": "画像生成",
|
|
6964
7156
|
"generateMusic": "音楽生成",
|
|
@@ -7003,6 +7195,7 @@ var ja_default = {
|
|
|
7003
7195
|
"separateAudio": "ボーカル分離",
|
|
7004
7196
|
"separateSpeaker": "話者分離",
|
|
7005
7197
|
"separateSound": "音を抽出",
|
|
7198
|
+
"speakWithStyle": "感情付き音声",
|
|
7006
7199
|
"denoise": "ノイズ除去",
|
|
7007
7200
|
"convertVoice": "音声置換",
|
|
7008
7201
|
"filter": "フィルター",
|
|
@@ -7140,6 +7333,9 @@ var ja_default = {
|
|
|
7140
7333
|
"modalDeployProgressHint": "{done}/{total} をデプロイ済み",
|
|
7141
7334
|
"pluginImplementationTitle": "実装",
|
|
7142
7335
|
"pluginModelTitle": "モデル",
|
|
7336
|
+
"pluginParamsTitle": "詳細設定",
|
|
7337
|
+
"pluginParamsReset": "リセット",
|
|
7338
|
+
"pluginParamDefault": "既定 {value}",
|
|
7143
7339
|
"pluginImplementationLoading": "プラグイン実装一覧を読み込み中…",
|
|
7144
7340
|
"pluginImplementationEmpty": "この機能向けのプラグイン実装がスキャン結果にありません。リポジトリの plugins/ に Modal または API プラグインを追加してください(README 参照)。ページを再読み込みしてください。",
|
|
7145
7341
|
"pluginRegistryLoadError": "プラグイン登録情報を読み込めませんでした:{message}",
|
|
@@ -7265,11 +7461,7 @@ var ja_default = {
|
|
|
7265
7461
|
"referenceTextPlaceholder": "任意:参照音声の書き起こしテキスト。クローン品質の向上に役立ちます",
|
|
7266
7462
|
"referenceTextCloneHint": "空欄の場合は x-vector クローン。転写テキストを入力すると ICL 高品質クローンになります",
|
|
7267
7463
|
"language": "言語",
|
|
7268
|
-
"inputText": "テキスト入力"
|
|
7269
|
-
"voiceOptions": {
|
|
7270
|
-
"female": "女性の声",
|
|
7271
|
-
"male": "男性の声"
|
|
7272
|
-
}
|
|
7464
|
+
"inputText": "テキスト入力"
|
|
7273
7465
|
},
|
|
7274
7466
|
"titles": {
|
|
7275
7467
|
"imageGenVideo": "画像から動画",
|
|
@@ -7333,7 +7525,8 @@ var ja_default = {
|
|
|
7333
7525
|
"musicExtract": "ステム抽出",
|
|
7334
7526
|
"musicLego": "トラック追加",
|
|
7335
7527
|
"musicComplete": "編曲補完",
|
|
7336
|
-
"musicBrief": "音楽ブリーフ"
|
|
7528
|
+
"musicBrief": "音楽ブリーフ",
|
|
7529
|
+
"textGenAudio": "テキストからオーディオ"
|
|
7337
7530
|
},
|
|
7338
7531
|
"options": {
|
|
7339
7532
|
"portrait": "縦向き",
|
|
@@ -7356,7 +7549,7 @@ var ja_default = {
|
|
|
7356
7549
|
"describeVideo": "動画を説明",
|
|
7357
7550
|
"recognizeSpeech": "音声を認識",
|
|
7358
7551
|
"generateMusic": "音楽を生成",
|
|
7359
|
-
"generateAudio": "
|
|
7552
|
+
"generateAudio": "オーディオを生成",
|
|
7360
7553
|
"startReplace": "置換を開始",
|
|
7361
7554
|
"startDenoise": "ノイズ除去を開始",
|
|
7362
7555
|
"parseDocument": "ドキュメントを解析",
|
|
@@ -7554,15 +7747,9 @@ var ja_default = {
|
|
|
7554
7747
|
"audioPromptPlaceholder": "音声吹き替えのプロンプトを入力"
|
|
7555
7748
|
},
|
|
7556
7749
|
"convertVoice": {
|
|
7557
|
-
"
|
|
7558
|
-
"
|
|
7559
|
-
"
|
|
7560
|
-
"uploadVoice": "音声をアップロード",
|
|
7561
|
-
"recordVoice": "音声を録音",
|
|
7562
|
-
"recordAudio": "音声を録音",
|
|
7563
|
-
"recordHint": "ここで音声を録音できます。",
|
|
7564
|
-
"recordNeedImpl": "音声録音機能は実装が必要です",
|
|
7565
|
-
"done": "完了"
|
|
7750
|
+
"sourceAudio": "元の音声",
|
|
7751
|
+
"referenceVoice": "参考ボイス",
|
|
7752
|
+
"connectHint": "変換したい音声と、参考にする声の音声をつないでください"
|
|
7566
7753
|
},
|
|
7567
7754
|
"removeWatermark": { "hint": "Sora動画の透かしを自動検出して削除" },
|
|
7568
7755
|
"videoGenText": {
|
|
@@ -7756,6 +7943,12 @@ var ja_default = {
|
|
|
7756
7943
|
"separateSound": {
|
|
7757
7944
|
"textLabel": "抽出したい音",
|
|
7758
7945
|
"textPlaceholder": "例:犬の鳴き声、ピアノ、背景のサイレン"
|
|
7946
|
+
},
|
|
7947
|
+
"textGenAudio": {
|
|
7948
|
+
"voiceReference": "参照ボイス",
|
|
7949
|
+
"promptLabel": "オーディオの説明",
|
|
7950
|
+
"promptPlaceholder": "場面をまるごと書きます:誰がどう話すか、背景の環境音、効果音。セリフは引用符で囲みます。例:@voice1 が言う:「今日はいい天気だね。」窓の外で小雨が降っている。",
|
|
7951
|
+
"hint": "話し声・効果音・環境音を一度に生成します。参照ボイスとして音声を最大 3 本つなぎ、クリックすると @voice1…@voice3 が挿入されます。"
|
|
7759
7952
|
}
|
|
7760
7953
|
}
|
|
7761
7954
|
},
|
|
@@ -7818,6 +8011,7 @@ var ja_default = {
|
|
|
7818
8011
|
"copy": "コピー",
|
|
7819
8012
|
"copied": "コピーしました",
|
|
7820
8013
|
"close": "閉じる",
|
|
8014
|
+
"openModalBilling": "Modal の請求設定を開く",
|
|
7821
8015
|
"needHelp": "解決しない場合はこちらへ:",
|
|
7822
8016
|
"wechatGroup": "WeChat グループ",
|
|
7823
8017
|
"wechatQrAlt": "WeChat グループの QR コード"
|
|
@@ -7949,6 +8143,7 @@ var ko_default = {
|
|
|
7949
8143
|
"separateAudio": "보컬 분리",
|
|
7950
8144
|
"separateSpeaker": "화자 분리",
|
|
7951
8145
|
"separateSound": "소리 추출",
|
|
8146
|
+
"speakWithStyle": "감정 음성 생성",
|
|
7952
8147
|
"denoise": "노이즈 제거",
|
|
7953
8148
|
"convertVoice": "음성 교체",
|
|
7954
8149
|
"filter": "필터",
|
|
@@ -8086,6 +8281,9 @@ var ko_default = {
|
|
|
8086
8281
|
"modalDeployProgressHint": "{done}/{total} 배포됨",
|
|
8087
8282
|
"pluginImplementationTitle": "구현",
|
|
8088
8283
|
"pluginModelTitle": "모델",
|
|
8284
|
+
"pluginParamsTitle": "고급 설정",
|
|
8285
|
+
"pluginParamsReset": "초기화",
|
|
8286
|
+
"pluginParamDefault": "기본 {value}",
|
|
8089
8287
|
"pluginImplementationLoading": "플러그인 구현을 불러오는 중…",
|
|
8090
8288
|
"pluginImplementationEmpty": "이 기능에 대해 스캔된 플러그인 구현이 없습니다. 저장소의 plugins/ 디렉터리 아래에 Modal 또는 API 플러그인을 추가한 후(README 참조) 이 페이지를 새로 고치세요.",
|
|
8091
8289
|
"pluginRegistryLoadError": "플러그인 레지스트리를 불러올 수 없습니다: {message}",
|
|
@@ -8211,11 +8409,7 @@ var ko_default = {
|
|
|
8211
8409
|
"referenceTextPlaceholder": "선택 사항: 참조 오디오의 전사본 — 복제 품질을 향상시킵니다",
|
|
8212
8410
|
"referenceTextCloneHint": "빠른 x-vector 복제는 비워 두고, 더 높은 품질의 ICL 복제를 위해서는 참조 전사본을 붙여넣으세요",
|
|
8213
8411
|
"language": "언어",
|
|
8214
|
-
"inputText": "입력 텍스트"
|
|
8215
|
-
"voiceOptions": {
|
|
8216
|
-
"female": "여성 목소리",
|
|
8217
|
-
"male": "남성 목소리"
|
|
8218
|
-
}
|
|
8412
|
+
"inputText": "입력 텍스트"
|
|
8219
8413
|
},
|
|
8220
8414
|
"titles": {
|
|
8221
8415
|
"imageGenVideo": "이미지-비디오",
|
|
@@ -8279,7 +8473,8 @@ var ko_default = {
|
|
|
8279
8473
|
"musicExtract": "스템 추출",
|
|
8280
8474
|
"musicLego": "트랙 추가",
|
|
8281
8475
|
"musicComplete": "편곡 완성",
|
|
8282
|
-
"musicBrief": "음악 브리프"
|
|
8476
|
+
"musicBrief": "음악 브리프",
|
|
8477
|
+
"textGenAudio": "텍스트로 오디오 생성"
|
|
8283
8478
|
},
|
|
8284
8479
|
"options": {
|
|
8285
8480
|
"portrait": "세로",
|
|
@@ -8586,15 +8781,9 @@ var ko_default = {
|
|
|
8586
8781
|
"audioPromptPlaceholder": "오디오 더빙 프롬프트 입력"
|
|
8587
8782
|
},
|
|
8588
8783
|
"convertVoice": {
|
|
8589
|
-
"
|
|
8590
|
-
"
|
|
8591
|
-
"
|
|
8592
|
-
"uploadVoice": "음성 업로드",
|
|
8593
|
-
"recordVoice": "음성 녹음",
|
|
8594
|
-
"recordAudio": "오디오 녹음",
|
|
8595
|
-
"recordHint": "여기에서 오디오를 녹음할 수 있습니다.",
|
|
8596
|
-
"recordNeedImpl": "오디오 녹음 기능 구현이 필요합니다",
|
|
8597
|
-
"done": "완료"
|
|
8784
|
+
"sourceAudio": "원본 오디오",
|
|
8785
|
+
"referenceVoice": "참고 목소리",
|
|
8786
|
+
"connectHint": "바꿀 오디오와 참고할 목소리 오디오를 연결하세요"
|
|
8598
8787
|
},
|
|
8599
8788
|
"removeWatermark": { "hint": "Sora 비디오 워터마크를 자동으로 감지하고 제거합니다" },
|
|
8600
8789
|
"videoGenText": {
|
|
@@ -8788,6 +8977,12 @@ var ko_default = {
|
|
|
8788
8977
|
"separateSound": {
|
|
8789
8978
|
"textLabel": "추출할 소리",
|
|
8790
8979
|
"textPlaceholder": "예: 개 짖는 소리, 피아노, 배경 사이렌"
|
|
8980
|
+
},
|
|
8981
|
+
"textGenAudio": {
|
|
8982
|
+
"voiceReference": "참조 음색",
|
|
8983
|
+
"promptLabel": "오디오 설명",
|
|
8984
|
+
"promptPlaceholder": "장면 전체를 적어 주세요. 누가 어떻게 말하는지, 배경 소리, 효과음까지. 대사는 따옴표로 묶어요. 예: @voice1이 말한다: \"오늘 날씨 좋다.\" 창밖에 가랑비가 내린다.",
|
|
8985
|
+
"hint": "말소리, 효과음, 배경음을 한 번에 만들어요. 참조 음색으로 오디오를 최대 3개까지 연결하고, 누르면 @voice1…@voice3이 설명에 들어가요."
|
|
8791
8986
|
}
|
|
8792
8987
|
}
|
|
8793
8988
|
},
|
|
@@ -8850,6 +9045,7 @@ var ko_default = {
|
|
|
8850
9045
|
"copy": "복사",
|
|
8851
9046
|
"copied": "복사됨",
|
|
8852
9047
|
"close": "닫기",
|
|
9048
|
+
"openModalBilling": "Modal 결제 설정 열기",
|
|
8853
9049
|
"needHelp": "해결이 안 되면 커뮤니티로 오세요:",
|
|
8854
9050
|
"wechatGroup": "WeChat 그룹",
|
|
8855
9051
|
"wechatQrAlt": "WeChat 그룹 QR 코드"
|
|
@@ -8936,7 +9132,7 @@ var zh_default = {
|
|
|
8936
9132
|
"saveSuccess": "保存成功!",
|
|
8937
9133
|
"saveFailed": "保存失败,请重试",
|
|
8938
9134
|
"generateVideo": "生成视频",
|
|
8939
|
-
"generateAudio": "
|
|
9135
|
+
"generateAudio": "生成音频",
|
|
8940
9136
|
"generateText": "生成文本",
|
|
8941
9137
|
"generateImage": "生成图片",
|
|
8942
9138
|
"generateMusic": "生成音乐",
|
|
@@ -8981,6 +9177,7 @@ var zh_default = {
|
|
|
8981
9177
|
"separateAudio": "人声分离",
|
|
8982
9178
|
"separateSpeaker": "说话人分离",
|
|
8983
9179
|
"separateSound": "提取声音",
|
|
9180
|
+
"speakWithStyle": "按情绪配音",
|
|
8984
9181
|
"denoise": "降噪",
|
|
8985
9182
|
"convertVoice": "人声替换",
|
|
8986
9183
|
"filter": "筛选",
|
|
@@ -9118,6 +9315,9 @@ var zh_default = {
|
|
|
9118
9315
|
"modalDeployProgressHint": "已部署 {done}/{total}",
|
|
9119
9316
|
"pluginImplementationTitle": "实现",
|
|
9120
9317
|
"pluginModelTitle": "模型",
|
|
9318
|
+
"pluginParamsTitle": "高级参数",
|
|
9319
|
+
"pluginParamsReset": "重置",
|
|
9320
|
+
"pluginParamDefault": "默认 {value}",
|
|
9121
9321
|
"pluginImplementationLoading": "正在加载插件实现列表…",
|
|
9122
9322
|
"pluginImplementationEmpty": "未扫描到适用于该能力的插件实现。请在仓库的 plugins/ 目录下添加 Modal 或 API 插件(见 README),然后刷新页面。",
|
|
9123
9323
|
"pluginRegistryLoadError": "无法加载插件注册表:{message}",
|
|
@@ -9243,11 +9443,7 @@ var zh_default = {
|
|
|
9243
9443
|
"referenceTextPlaceholder": "可选:参考音频的转写文本,有助于提升克隆质量",
|
|
9244
9444
|
"referenceTextCloneHint": "不填则使用 x-vector 快速克隆;填写参考音频转写文本可启用更高质量的 ICL 克隆",
|
|
9245
9445
|
"language": "语言",
|
|
9246
|
-
"inputText": "输入文本"
|
|
9247
|
-
"voiceOptions": {
|
|
9248
|
-
"female": "普通女声",
|
|
9249
|
-
"male": "普通男声"
|
|
9250
|
-
}
|
|
9446
|
+
"inputText": "输入文本"
|
|
9251
9447
|
},
|
|
9252
9448
|
"titles": {
|
|
9253
9449
|
"imageGenVideo": "图片生成视频",
|
|
@@ -9311,7 +9507,8 @@ var zh_default = {
|
|
|
9311
9507
|
"musicExtract": "分离音轨",
|
|
9312
9508
|
"musicLego": "添加乐轨",
|
|
9313
9509
|
"musicComplete": "补全编曲",
|
|
9314
|
-
"musicBrief": "音乐企划"
|
|
9510
|
+
"musicBrief": "音乐企划",
|
|
9511
|
+
"textGenAudio": "文本生成音频"
|
|
9315
9512
|
},
|
|
9316
9513
|
"options": {
|
|
9317
9514
|
"portrait": "竖屏",
|
|
@@ -9618,15 +9815,9 @@ var zh_default = {
|
|
|
9618
9815
|
"audioPromptPlaceholder": "输入音频配音提示词"
|
|
9619
9816
|
},
|
|
9620
9817
|
"convertVoice": {
|
|
9621
|
-
"
|
|
9622
|
-
"
|
|
9623
|
-
"
|
|
9624
|
-
"uploadVoice": "上传音色",
|
|
9625
|
-
"recordVoice": "录制音色",
|
|
9626
|
-
"recordAudio": "录制音频",
|
|
9627
|
-
"recordHint": "您可以在这里录制音频。",
|
|
9628
|
-
"recordNeedImpl": "音频录制功能需要实现",
|
|
9629
|
-
"done": "完成"
|
|
9818
|
+
"sourceAudio": "源音频",
|
|
9819
|
+
"referenceVoice": "参考音色",
|
|
9820
|
+
"connectHint": "连上要换声的音频,再连一段参考音色"
|
|
9630
9821
|
},
|
|
9631
9822
|
"removeWatermark": { "hint": "自动识别并去除 Sora 视频水印" },
|
|
9632
9823
|
"videoGenText": {
|
|
@@ -9820,6 +10011,12 @@ var zh_default = {
|
|
|
9820
10011
|
"separateSound": {
|
|
9821
10012
|
"textLabel": "要提取的声音",
|
|
9822
10013
|
"textPlaceholder": "如:狗叫、钢琴、背景里的警笛"
|
|
10014
|
+
},
|
|
10015
|
+
"textGenAudio": {
|
|
10016
|
+
"voiceReference": "参考音色",
|
|
10017
|
+
"promptLabel": "音频描述",
|
|
10018
|
+
"promptPlaceholder": "把整段场景写出来:谁在说话、怎么说,背景环境,音效。台词用引号,例如:@voice1 说:“今天天气真好。”窗外下着小雨。",
|
|
10019
|
+
"hint": "一次生成人声、音效和环境声。最多连 3 段音频作参考音色,点一下就把 @voice1…@voice3 插进描述里。"
|
|
9823
10020
|
}
|
|
9824
10021
|
}
|
|
9825
10022
|
},
|
|
@@ -9882,6 +10079,7 @@ var zh_default = {
|
|
|
9882
10079
|
"copy": "复制",
|
|
9883
10080
|
"copied": "已复制",
|
|
9884
10081
|
"close": "关闭",
|
|
10082
|
+
"openModalBilling": "打开 Modal 账单设置",
|
|
9885
10083
|
"needHelp": "解决不了?来社群里找我们:",
|
|
9886
10084
|
"wechatGroup": "微信群",
|
|
9887
10085
|
"wechatQrAlt": "微信群二维码"
|
|
@@ -9972,6 +10170,7 @@ function hostFetch(input, init) {
|
|
|
9972
10170
|
}
|
|
9973
10171
|
/** URL for an asset `file_key` (private uploads endpoint by default). */
|
|
9974
10172
|
function assetUrl(fileKey) {
|
|
10173
|
+
if (/^https?:\/\//.test(fileKey)) return fileKey;
|
|
9975
10174
|
return host.resolveAssetUrl ? host.resolveAssetUrl(fileKey) : `${host.apiBaseUrl}/api/uploads/${fileKey}`;
|
|
9976
10175
|
}
|
|
9977
10176
|
/**
|
|
@@ -12677,12 +12876,84 @@ function dedupeIds(list) {
|
|
|
12677
12876
|
function useNodePluginIds(nodeSlot) {
|
|
12678
12877
|
return dedupeIds(usePluginsRegistryStore((s) => s.registry)?.nodePluginMap?.[nodeSlot] ?? []);
|
|
12679
12878
|
}
|
|
12879
|
+
const useLiveModelsStore = create(() => ({ byPlugin: {} }));
|
|
12880
|
+
const MODEL_CATALOG_TTL_MS = 6e5;
|
|
12881
|
+
const catalogInflight = /* @__PURE__ */ new Map();
|
|
12680
12882
|
/**
|
|
12681
|
-
*
|
|
12682
|
-
*
|
|
12883
|
+
* Fetch (or refresh, once the TTL elapsed) a plugin's live model catalog. Safe
|
|
12884
|
+
* to call eagerly — it is a no-op for plugins without a catalog and while a
|
|
12885
|
+
* fresh result is cached.
|
|
12886
|
+
*/
|
|
12887
|
+
async function loadPluginModelCatalog(pluginId) {
|
|
12888
|
+
const catalog = usePluginsRegistryStore.getState().registry?.plugins?.[pluginId]?.modelCatalog;
|
|
12889
|
+
if (!catalog) return;
|
|
12890
|
+
const cached = useLiveModelsStore.getState().byPlugin[pluginId];
|
|
12891
|
+
if (cached && Date.now() - cached.fetchedAt < MODEL_CATALOG_TTL_MS) return;
|
|
12892
|
+
const inflight = catalogInflight.get(pluginId);
|
|
12893
|
+
if (inflight) return inflight;
|
|
12894
|
+
const job = (async () => {
|
|
12895
|
+
try {
|
|
12896
|
+
const res = catalog.authEnv ? await hostFetch(apiUrl(`/api/plugins/model-catalog?pluginId=${encodeURIComponent(pluginId)}`), {
|
|
12897
|
+
cache: "no-store",
|
|
12898
|
+
credentials: "same-origin"
|
|
12899
|
+
}) : await fetch(catalog.url, {
|
|
12900
|
+
method: "GET",
|
|
12901
|
+
cache: "no-store"
|
|
12902
|
+
});
|
|
12903
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
12904
|
+
const bySlot = filterModelCatalog(catalog, await res.json());
|
|
12905
|
+
useLiveModelsStore.setState((s) => ({ byPlugin: {
|
|
12906
|
+
...s.byPlugin,
|
|
12907
|
+
[pluginId]: {
|
|
12908
|
+
fetchedAt: Date.now(),
|
|
12909
|
+
bySlot
|
|
12910
|
+
}
|
|
12911
|
+
} }));
|
|
12912
|
+
} catch (e) {
|
|
12913
|
+
console.warn(`[tongflow] model catalog for ${pluginId} unavailable:`, e);
|
|
12914
|
+
} finally {
|
|
12915
|
+
catalogInflight.delete(pluginId);
|
|
12916
|
+
}
|
|
12917
|
+
})();
|
|
12918
|
+
catalogInflight.set(pluginId, job);
|
|
12919
|
+
return job;
|
|
12920
|
+
}
|
|
12921
|
+
/**
|
|
12922
|
+
* Synchronous read of a plugin's model ids for one slot: the static shortlist
|
|
12923
|
+
* (declared order, first = default) followed by any live-catalog extras.
|
|
12924
|
+
*/
|
|
12925
|
+
function getNodePluginModels(nodeSlot, pluginId) {
|
|
12926
|
+
const declared = usePluginsRegistryStore.getState().registry?.plugins?.[pluginId]?.methodsByNodeSlot?.[nodeSlot]?.models ?? [];
|
|
12927
|
+
const live = useLiveModelsStore.getState().byPlugin[pluginId]?.bySlot[nodeSlot] ?? [];
|
|
12928
|
+
return dedupeIds([...declared, ...live]);
|
|
12929
|
+
}
|
|
12930
|
+
/**
|
|
12931
|
+
* Model ids a plugin offers for one ABI `nodeSlot` (empty for single-model
|
|
12932
|
+
* plugins — the model dropdown is hidden in that case): the static shortlist
|
|
12933
|
+
* plus live-catalog extras, which this hook fetches on first use.
|
|
12683
12934
|
*/
|
|
12684
12935
|
function useNodePluginModels(nodeSlot, pluginId) {
|
|
12685
|
-
|
|
12936
|
+
const registry = usePluginsRegistryStore((s) => s.registry);
|
|
12937
|
+
const declared = registry?.plugins?.[pluginId]?.methodsByNodeSlot?.[nodeSlot]?.models ?? [];
|
|
12938
|
+
const hasCatalog = Boolean(registry?.plugins?.[pluginId]?.modelCatalog);
|
|
12939
|
+
const live = useLiveModelsStore((s) => s.byPlugin[pluginId]?.bySlot[nodeSlot]);
|
|
12940
|
+
useEffect(() => {
|
|
12941
|
+
if (hasCatalog) loadPluginModelCatalog(pluginId);
|
|
12942
|
+
}, [hasCatalog, pluginId]);
|
|
12943
|
+
return dedupeIds([...declared, ...live ?? []]);
|
|
12944
|
+
}
|
|
12945
|
+
/**
|
|
12946
|
+
* Synchronous read of the params a plugin offers for one slot given the
|
|
12947
|
+
* selected model, in declaration order.
|
|
12948
|
+
*/
|
|
12949
|
+
function getNodePluginParams(nodeSlot, pluginId, model) {
|
|
12950
|
+
const registry = usePluginsRegistryStore.getState().registry;
|
|
12951
|
+
return visiblePluginParams(registry?.plugins?.[pluginId]?.methodsByNodeSlot?.[nodeSlot]?.params, model);
|
|
12952
|
+
}
|
|
12953
|
+
/** Reactive form of {@link getNodePluginParams}. */
|
|
12954
|
+
function useNodePluginParams(nodeSlot, pluginId, model) {
|
|
12955
|
+
const declared = usePluginsRegistryStore((s) => s.registry?.plugins?.[pluginId]?.methodsByNodeSlot?.[nodeSlot]?.params);
|
|
12956
|
+
return useMemo(() => visiblePluginParams(declared, model), [declared, model]);
|
|
12686
12957
|
}
|
|
12687
12958
|
/** Presentation metadata (name/description/icon) for a single plugin. */
|
|
12688
12959
|
function usePluginMeta(pluginId) {
|
|
@@ -12741,7 +13012,7 @@ function PluginIcon({ icon, label }) {
|
|
|
12741
13012
|
/**
|
|
12742
13013
|
* Shared plugin implementation selector (`plugins/<id>` directory name from registry).
|
|
12743
13014
|
*/
|
|
12744
|
-
function NodePluginSelect({ value, onValueChange, options, title }) {
|
|
13015
|
+
function NodePluginSelect({ value, onValueChange, options, title, onOpenChange }) {
|
|
12745
13016
|
const t = useTranslations("Workspace.nodes.base");
|
|
12746
13017
|
return /* @__PURE__ */ jsx(Card, {
|
|
12747
13018
|
className: "p-3",
|
|
@@ -12753,6 +13024,7 @@ function NodePluginSelect({ value, onValueChange, options, title }) {
|
|
|
12753
13024
|
}), /* @__PURE__ */ jsxs(Select, {
|
|
12754
13025
|
value,
|
|
12755
13026
|
onValueChange,
|
|
13027
|
+
onOpenChange,
|
|
12756
13028
|
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
12757
13029
|
className: "w-full",
|
|
12758
13030
|
size: "sm",
|
|
@@ -12870,10 +13142,12 @@ function NodePluginIdSelect({ nodeSlot, data, dataKey = "pluginId" }) {
|
|
|
12870
13142
|
//#region src/canvas/nodes/base/node-plugin-model-select.tsx
|
|
12871
13143
|
/**
|
|
12872
13144
|
* Model selector for router-style plugins that declare per-slot model lists
|
|
12873
|
-
* (`TONGFLOW_SLOT_MODELS
|
|
12874
|
-
*
|
|
12875
|
-
*
|
|
12876
|
-
*
|
|
13145
|
+
* (`TONGFLOW_SLOT_MODELS`, optionally extended live via
|
|
13146
|
+
* `TONGFLOW_MODEL_CATALOG` — re-checked each time the dropdown opens). Renders
|
|
13147
|
+
* nothing when the active plugin declares no models, so single-model plugins
|
|
13148
|
+
* are visually unchanged. The selection is stored as `data.pluginModel` and
|
|
13149
|
+
* travels top-level (like `pluginId`) through the create-task API and workflow
|
|
13150
|
+
* export.
|
|
12877
13151
|
*/
|
|
12878
13152
|
function NodePluginModelSelect({ nodeSlot, data }) {
|
|
12879
13153
|
const id = useNodeId();
|
|
@@ -12908,10 +13182,218 @@ function NodePluginModelSelect({ nodeSlot, data }) {
|
|
|
12908
13182
|
pluginModel: value
|
|
12909
13183
|
}),
|
|
12910
13184
|
options,
|
|
12911
|
-
title: t("pluginModelTitle")
|
|
13185
|
+
title: t("pluginModelTitle"),
|
|
13186
|
+
onOpenChange: (open) => {
|
|
13187
|
+
if (open) loadPluginModelCatalog(pluginId);
|
|
13188
|
+
}
|
|
13189
|
+
});
|
|
13190
|
+
}
|
|
13191
|
+
//#endregion
|
|
13192
|
+
//#region src/canvas/ui/input.tsx
|
|
13193
|
+
function Input({ className, type, ...props }) {
|
|
13194
|
+
return /* @__PURE__ */ jsx("input", {
|
|
13195
|
+
type,
|
|
13196
|
+
"data-slot": "input",
|
|
13197
|
+
className: cn("file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className),
|
|
13198
|
+
...props
|
|
13199
|
+
});
|
|
13200
|
+
}
|
|
13201
|
+
//#endregion
|
|
13202
|
+
//#region src/canvas/ui/switch.tsx
|
|
13203
|
+
function Switch({ className, ...props }) {
|
|
13204
|
+
return /* @__PURE__ */ jsx(SwitchPrimitive.Root, {
|
|
13205
|
+
"data-slot": "switch",
|
|
13206
|
+
className: cn("peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
13207
|
+
...props,
|
|
13208
|
+
children: /* @__PURE__ */ jsx(SwitchPrimitive.Thumb, {
|
|
13209
|
+
"data-slot": "switch-thumb",
|
|
13210
|
+
className: cn("bg-background pointer-events-none block size-4 rounded-full ring-0 shadow-sm transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0")
|
|
13211
|
+
})
|
|
12912
13212
|
});
|
|
12913
13213
|
}
|
|
12914
13214
|
//#endregion
|
|
13215
|
+
//#region src/canvas/nodes/base/node-plugin-params.tsx
|
|
13216
|
+
function readParams(data) {
|
|
13217
|
+
const raw = data.pluginParams;
|
|
13218
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
13219
|
+
}
|
|
13220
|
+
/**
|
|
13221
|
+
* Collapsed "Advanced" section listing the run-time knobs the active plugin
|
|
13222
|
+
* declares for this slot (`TONGFLOW_SLOT_PARAMS`, optionally gated per router
|
|
13223
|
+
* model). Renders nothing for plugins that declare none. Choices live in
|
|
13224
|
+
* `data.pluginParams` as a diff against the declared defaults — an unset key
|
|
13225
|
+
* means "plugin default" — and travel top-level as `params` through the
|
|
13226
|
+
* create-task API and the workflow export, never inside the ABI prompt.
|
|
13227
|
+
*/
|
|
13228
|
+
function NodePluginParams({ nodeSlot, data }) {
|
|
13229
|
+
const id = useNodeId();
|
|
13230
|
+
const updates = useFlow((s) => s.updates);
|
|
13231
|
+
const t = useTranslations("Workspace.nodes.base");
|
|
13232
|
+
const [open, setOpen] = useState(false);
|
|
13233
|
+
const { resolved: pluginId } = useResolvedPluginId(nodeSlot, data);
|
|
13234
|
+
const models = useNodePluginModels(nodeSlot, pluginId);
|
|
13235
|
+
const currentModel = String(data.pluginModel ?? "").trim();
|
|
13236
|
+
const entries = useNodePluginParams(nodeSlot, pluginId, models.includes(currentModel) ? currentModel : models[0] ?? void 0);
|
|
13237
|
+
const values = useMemo(() => readParams(data), [data]);
|
|
13238
|
+
useEffect(() => {
|
|
13239
|
+
const normalized = normalizePluginParams(entries, values);
|
|
13240
|
+
if (samePluginParams(normalized, data.pluginParams)) return;
|
|
13241
|
+
if (Object.keys(normalized).length === 0 && data.pluginParams === void 0) return;
|
|
13242
|
+
const next = { ...data };
|
|
13243
|
+
if (Object.keys(normalized).length === 0) delete next.pluginParams;
|
|
13244
|
+
else next.pluginParams = normalized;
|
|
13245
|
+
updates(id, next, { history: false });
|
|
13246
|
+
}, [
|
|
13247
|
+
id,
|
|
13248
|
+
data,
|
|
13249
|
+
entries,
|
|
13250
|
+
values,
|
|
13251
|
+
updates
|
|
13252
|
+
]);
|
|
13253
|
+
const setParam = useCallback((name, value) => {
|
|
13254
|
+
const next = { ...values };
|
|
13255
|
+
if (value === void 0) delete next[name];
|
|
13256
|
+
else next[name] = value;
|
|
13257
|
+
const normalized = normalizePluginParams(entries, next);
|
|
13258
|
+
const nextData = { ...data };
|
|
13259
|
+
if (Object.keys(normalized).length === 0) delete nextData.pluginParams;
|
|
13260
|
+
else nextData.pluginParams = normalized;
|
|
13261
|
+
updates(id, nextData);
|
|
13262
|
+
}, [
|
|
13263
|
+
id,
|
|
13264
|
+
data,
|
|
13265
|
+
entries,
|
|
13266
|
+
values,
|
|
13267
|
+
updates
|
|
13268
|
+
]);
|
|
13269
|
+
const reset = useCallback(() => {
|
|
13270
|
+
const nextData = { ...data };
|
|
13271
|
+
delete nextData.pluginParams;
|
|
13272
|
+
updates(id, nextData);
|
|
13273
|
+
}, [
|
|
13274
|
+
id,
|
|
13275
|
+
data,
|
|
13276
|
+
updates
|
|
13277
|
+
]);
|
|
13278
|
+
if (entries.length === 0) return null;
|
|
13279
|
+
const changed = Object.keys(values).length;
|
|
13280
|
+
return /* @__PURE__ */ jsx(Card, {
|
|
13281
|
+
className: "p-3",
|
|
13282
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
13283
|
+
className: "space-y-2",
|
|
13284
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
13285
|
+
className: "flex items-center gap-1",
|
|
13286
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
13287
|
+
type: "button",
|
|
13288
|
+
onClick: () => setOpen((o) => !o),
|
|
13289
|
+
className: "nodrag flex flex-1 items-center gap-1 text-left text-sm font-medium text-muted-foreground hover:text-foreground",
|
|
13290
|
+
"aria-expanded": open,
|
|
13291
|
+
children: [
|
|
13292
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: `size-3.5 transition-transform ${open ? "rotate-90" : ""}` }),
|
|
13293
|
+
/* @__PURE__ */ jsx("span", { children: t("pluginParamsTitle") }),
|
|
13294
|
+
changed > 0 && /* @__PURE__ */ jsx("span", {
|
|
13295
|
+
className: "ml-1 rounded-full bg-primary/10 px-1.5 text-[10px] font-semibold text-primary",
|
|
13296
|
+
children: changed
|
|
13297
|
+
})
|
|
13298
|
+
]
|
|
13299
|
+
}), changed > 0 && /* @__PURE__ */ jsx("button", {
|
|
13300
|
+
type: "button",
|
|
13301
|
+
onClick: reset,
|
|
13302
|
+
title: t("pluginParamsReset"),
|
|
13303
|
+
"aria-label": t("pluginParamsReset"),
|
|
13304
|
+
className: "nodrag rounded p-1 text-muted-foreground hover:text-foreground",
|
|
13305
|
+
children: /* @__PURE__ */ jsx(RotateCcw, { className: "size-3.5" })
|
|
13306
|
+
})]
|
|
13307
|
+
}), open && /* @__PURE__ */ jsx("div", {
|
|
13308
|
+
className: "space-y-2",
|
|
13309
|
+
children: entries.map((entry) => /* @__PURE__ */ jsx(ParamRow, {
|
|
13310
|
+
entry,
|
|
13311
|
+
value: values[entry.name],
|
|
13312
|
+
onChange: (v) => setParam(entry.name, v)
|
|
13313
|
+
}, entry.name))
|
|
13314
|
+
})]
|
|
13315
|
+
})
|
|
13316
|
+
});
|
|
13317
|
+
}
|
|
13318
|
+
function ParamRow({ entry, value, onChange }) {
|
|
13319
|
+
const t = useTranslations("Workspace.nodes.base");
|
|
13320
|
+
const { name, spec } = entry;
|
|
13321
|
+
const label = spec.label ?? name;
|
|
13322
|
+
const inline = spec.type === "boolean";
|
|
13323
|
+
const changed = value !== void 0 && spec.default !== void 0;
|
|
13324
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
13325
|
+
className: inline ? "flex items-center justify-between gap-2" : "space-y-1",
|
|
13326
|
+
children: [/* @__PURE__ */ jsxs(Label, {
|
|
13327
|
+
className: "flex items-baseline gap-1.5 text-xs text-muted-foreground",
|
|
13328
|
+
title: spec.description,
|
|
13329
|
+
children: [/* @__PURE__ */ jsx("span", { children: label }), changed && /* @__PURE__ */ jsx("span", {
|
|
13330
|
+
className: "text-[10px] text-muted-foreground/60",
|
|
13331
|
+
children: t("pluginParamDefault", { value: String(spec.default) })
|
|
13332
|
+
})]
|
|
13333
|
+
}), /* @__PURE__ */ jsx(ParamControl, {
|
|
13334
|
+
spec,
|
|
13335
|
+
value,
|
|
13336
|
+
onChange
|
|
13337
|
+
})]
|
|
13338
|
+
});
|
|
13339
|
+
}
|
|
13340
|
+
function ParamControl({ spec, value, onChange }) {
|
|
13341
|
+
const effective = value === void 0 ? spec.default : value;
|
|
13342
|
+
switch (spec.type) {
|
|
13343
|
+
case "select": {
|
|
13344
|
+
const options = spec.options ?? [];
|
|
13345
|
+
return /* @__PURE__ */ jsxs(Select, {
|
|
13346
|
+
value: effective === void 0 ? "" : String(effective),
|
|
13347
|
+
onValueChange: (v) => {
|
|
13348
|
+
onChange(options.find((o) => String(o) === v));
|
|
13349
|
+
},
|
|
13350
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
13351
|
+
className: "nodrag w-full",
|
|
13352
|
+
size: "sm",
|
|
13353
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
13354
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: options.map((o) => /* @__PURE__ */ jsx(SelectItem, {
|
|
13355
|
+
value: String(o),
|
|
13356
|
+
children: String(o)
|
|
13357
|
+
}, String(o))) })]
|
|
13358
|
+
});
|
|
13359
|
+
}
|
|
13360
|
+
case "boolean": return /* @__PURE__ */ jsx(Switch, {
|
|
13361
|
+
className: "nodrag",
|
|
13362
|
+
checked: effective === true,
|
|
13363
|
+
onCheckedChange: (checked) => onChange(checked)
|
|
13364
|
+
});
|
|
13365
|
+
case "number":
|
|
13366
|
+
case "integer": return /* @__PURE__ */ jsx(Input, {
|
|
13367
|
+
type: "number",
|
|
13368
|
+
className: "nodrag h-8",
|
|
13369
|
+
value: typeof effective === "number" ? String(effective) : "",
|
|
13370
|
+
min: spec.min,
|
|
13371
|
+
max: spec.max,
|
|
13372
|
+
step: spec.step ?? (spec.type === "integer" ? 1 : "any"),
|
|
13373
|
+
onChange: (e) => {
|
|
13374
|
+
const raw = e.target.value.trim();
|
|
13375
|
+
if (raw === "") {
|
|
13376
|
+
onChange(void 0);
|
|
13377
|
+
return;
|
|
13378
|
+
}
|
|
13379
|
+
const n = Number(raw);
|
|
13380
|
+
if (!Number.isFinite(n)) return;
|
|
13381
|
+
onChange(spec.type === "integer" ? Math.round(n) : n);
|
|
13382
|
+
}
|
|
13383
|
+
});
|
|
13384
|
+
case "text": return /* @__PURE__ */ jsx(Input, {
|
|
13385
|
+
type: "text",
|
|
13386
|
+
className: "nodrag h-8",
|
|
13387
|
+
value: typeof effective === "string" ? effective : "",
|
|
13388
|
+
onChange: (e) => {
|
|
13389
|
+
const v = e.target.value;
|
|
13390
|
+
onChange(v === "" ? void 0 : v);
|
|
13391
|
+
}
|
|
13392
|
+
});
|
|
13393
|
+
default: return null;
|
|
13394
|
+
}
|
|
13395
|
+
}
|
|
13396
|
+
//#endregion
|
|
12915
13397
|
//#region src/canvas/nodes/base/node-thinking-bubble.tsx
|
|
12916
13398
|
/**
|
|
12917
13399
|
* A transient, auto-scrolling bubble anchored to the right of a node that
|
|
@@ -13015,13 +13497,20 @@ const BaseNodeShell = forwardRef(({ className, selected, count, data, children,
|
|
|
13015
13497
|
] }),
|
|
13016
13498
|
renderPluginSelect && /* @__PURE__ */ jsxs("div", {
|
|
13017
13499
|
className: "p-4 pb-0 space-y-2",
|
|
13018
|
-
children: [
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13500
|
+
children: [
|
|
13501
|
+
/* @__PURE__ */ jsx(NodePluginIdSelect, {
|
|
13502
|
+
nodeSlot: feature,
|
|
13503
|
+
data: data ?? { feature }
|
|
13504
|
+
}),
|
|
13505
|
+
/* @__PURE__ */ jsx(NodePluginModelSelect, {
|
|
13506
|
+
nodeSlot: feature,
|
|
13507
|
+
data: data ?? { feature }
|
|
13508
|
+
}),
|
|
13509
|
+
/* @__PURE__ */ jsx(NodePluginParams, {
|
|
13510
|
+
nodeSlot: feature,
|
|
13511
|
+
data: data ?? { feature }
|
|
13512
|
+
})
|
|
13513
|
+
]
|
|
13025
13514
|
}),
|
|
13026
13515
|
/* @__PURE__ */ jsx("div", {
|
|
13027
13516
|
className: "relative z-0",
|
|
@@ -14020,16 +14509,6 @@ function useNodeState(initialState, nodeData) {
|
|
|
14020
14509
|
}, [id, flowUpdates])];
|
|
14021
14510
|
}
|
|
14022
14511
|
//#endregion
|
|
14023
|
-
//#region src/canvas/ui/input.tsx
|
|
14024
|
-
function Input({ className, type, ...props }) {
|
|
14025
|
-
return /* @__PURE__ */ jsx("input", {
|
|
14026
|
-
type,
|
|
14027
|
-
"data-slot": "input",
|
|
14028
|
-
className: cn("file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className),
|
|
14029
|
-
...props
|
|
14030
|
-
});
|
|
14031
|
-
}
|
|
14032
|
-
//#endregion
|
|
14033
14512
|
//#region src/canvas/nodes/add/add-link-node.tsx
|
|
14034
14513
|
const AddLinkNode = ({ selected, data }) => {
|
|
14035
14514
|
const t = useTranslations("Workspace.nodes.add");
|
|
@@ -16118,24 +16597,47 @@ function useNodePluginResolver(feature) {
|
|
|
16118
16597
|
defaultPluginIdFromRegistry,
|
|
16119
16598
|
pluginOptions
|
|
16120
16599
|
]);
|
|
16600
|
+
/**
|
|
16601
|
+
* Model for router-style plugins: the node's `pluginModel` when it is one
|
|
16602
|
+
* of the active plugin's models (declared shortlist or live catalog), else
|
|
16603
|
+
* that plugin's default (first declared model). `undefined` when the
|
|
16604
|
+
* plugin declares none — the create-task API then omits the field entirely.
|
|
16605
|
+
*/
|
|
16606
|
+
const resolveActiveModel = useCallback(() => {
|
|
16607
|
+
if (!feature) return void 0;
|
|
16608
|
+
const pluginId = resolveActivePluginId();
|
|
16609
|
+
if (!pluginId) return void 0;
|
|
16610
|
+
const models = getNodePluginModels(feature, pluginId);
|
|
16611
|
+
if (models.length === 0) return void 0;
|
|
16612
|
+
const n = nodeId ? getNode(nodeId) : void 0;
|
|
16613
|
+
const fromData = String((n?.data)?.pluginModel ?? "").trim();
|
|
16614
|
+
return models.includes(fromData) ? fromData : models[0];
|
|
16615
|
+
}, [
|
|
16616
|
+
feature,
|
|
16617
|
+
nodeId,
|
|
16618
|
+
getNode,
|
|
16619
|
+
resolveActivePluginId
|
|
16620
|
+
]);
|
|
16121
16621
|
return {
|
|
16122
16622
|
pluginOptions,
|
|
16123
16623
|
defaultPluginIdFromRegistry,
|
|
16124
16624
|
resolveActivePluginId,
|
|
16125
|
-
resolveActiveModel
|
|
16625
|
+
resolveActiveModel,
|
|
16626
|
+
resolveActiveParams: useCallback(() => {
|
|
16126
16627
|
if (!feature) return void 0;
|
|
16127
16628
|
const pluginId = resolveActivePluginId();
|
|
16128
16629
|
if (!pluginId) return void 0;
|
|
16129
|
-
const
|
|
16130
|
-
if (
|
|
16131
|
-
const
|
|
16132
|
-
const
|
|
16133
|
-
return
|
|
16630
|
+
const entries = getNodePluginParams(feature, pluginId, resolveActiveModel());
|
|
16631
|
+
if (entries.length === 0) return void 0;
|
|
16632
|
+
const raw = ((nodeId ? getNode(nodeId) : void 0)?.data)?.pluginParams;
|
|
16633
|
+
const params = normalizePluginParams(entries, raw && typeof raw === "object" && !Array.isArray(raw) ? raw : void 0);
|
|
16634
|
+
return Object.keys(params).length > 0 ? params : void 0;
|
|
16134
16635
|
}, [
|
|
16135
16636
|
feature,
|
|
16136
16637
|
nodeId,
|
|
16137
16638
|
getNode,
|
|
16138
|
-
resolveActivePluginId
|
|
16639
|
+
resolveActivePluginId,
|
|
16640
|
+
resolveActiveModel
|
|
16139
16641
|
])
|
|
16140
16642
|
};
|
|
16141
16643
|
}
|
|
@@ -16231,7 +16733,7 @@ function useAbiExecution(opts) {
|
|
|
16231
16733
|
const expands = useFlow((s) => s.expands);
|
|
16232
16734
|
const { isLoading: taskLoading, createBatchTasks, cancelTasks } = useBatchTaskManager();
|
|
16233
16735
|
const loading = taskLoading || nodeExecutionStatus === "running";
|
|
16234
|
-
const { pluginOptions, resolveActivePluginId, resolveActiveModel } = useNodePluginResolver(feature);
|
|
16736
|
+
const { pluginOptions, resolveActivePluginId, resolveActiveModel, resolveActiveParams } = useNodePluginResolver(feature);
|
|
16235
16737
|
const { isLoaded: pluginsRegistryLoaded } = usePluginsRegistry();
|
|
16236
16738
|
const handleTaskUpdate = useCallback(async (task) => {
|
|
16237
16739
|
if (!nodeId) return;
|
|
@@ -16337,11 +16839,13 @@ function useAbiExecution(opts) {
|
|
|
16337
16839
|
prompt: prompts.length === 1 ? prompts[0] : prompts
|
|
16338
16840
|
});
|
|
16339
16841
|
const model = resolveActiveModel();
|
|
16842
|
+
const params = resolveActiveParams();
|
|
16340
16843
|
try {
|
|
16341
16844
|
const taskConfigs = prompts.map((prompt) => ({
|
|
16342
16845
|
feature,
|
|
16343
16846
|
pluginId,
|
|
16344
16847
|
...model ? { model } : {},
|
|
16848
|
+
...params ? { params } : {},
|
|
16345
16849
|
prompt,
|
|
16346
16850
|
nodeId
|
|
16347
16851
|
}));
|
|
@@ -16358,6 +16862,7 @@ function useAbiExecution(opts) {
|
|
|
16358
16862
|
createBatchTasks,
|
|
16359
16863
|
resolveActivePluginId,
|
|
16360
16864
|
resolveActiveModel,
|
|
16865
|
+
resolveActiveParams,
|
|
16361
16866
|
pluginOptions,
|
|
16362
16867
|
pluginsRegistryLoaded,
|
|
16363
16868
|
transformPrompts
|
|
@@ -17483,30 +17988,63 @@ var merge_video_audio_default = memo(MergeVideoAudioNode);
|
|
|
17483
17988
|
//#endregion
|
|
17484
17989
|
//#region src/canvas/nodes/compose/music-cover.tsx
|
|
17485
17990
|
const DEFAULT_STRENGTH$1 = .8;
|
|
17991
|
+
function UpstreamTextPreview({ label, text }) {
|
|
17992
|
+
return /* @__PURE__ */ jsx(Card, {
|
|
17993
|
+
className: "p-3",
|
|
17994
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
17995
|
+
className: "space-y-2",
|
|
17996
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
17997
|
+
className: "text-sm font-medium text-muted-foreground",
|
|
17998
|
+
children: label
|
|
17999
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
18000
|
+
className: "text-sm text-foreground p-2 bg-background rounded border border-border/50 line-clamp-4 whitespace-pre-line",
|
|
18001
|
+
children: text
|
|
18002
|
+
})]
|
|
18003
|
+
})
|
|
18004
|
+
});
|
|
18005
|
+
}
|
|
17486
18006
|
const MusicCoverNode = ({ selected, data }) => {
|
|
17487
18007
|
const t = useTranslations("Workspace.nodes");
|
|
17488
18008
|
const form = useAbiForm("music-cover");
|
|
17489
18009
|
const nodeId = useNodeId();
|
|
17490
18010
|
const edges = useStore((state) => state.edges);
|
|
17491
|
-
const { audioSourceId, refAudioSourceId } = useMemo(() => {
|
|
17492
|
-
|
|
18011
|
+
const { audioSourceId, refAudioSourceId, textSourceId, lyricsSourceId } = useMemo(() => {
|
|
18012
|
+
const ids = {
|
|
17493
18013
|
audioSourceId: null,
|
|
17494
|
-
refAudioSourceId: null
|
|
18014
|
+
refAudioSourceId: null,
|
|
18015
|
+
textSourceId: null,
|
|
18016
|
+
lyricsSourceId: null
|
|
17495
18017
|
};
|
|
17496
|
-
|
|
17497
|
-
let refAudioSrc = null;
|
|
18018
|
+
if (!nodeId) return ids;
|
|
17498
18019
|
for (const e of edges) {
|
|
17499
18020
|
if (e.target !== nodeId) continue;
|
|
17500
|
-
if (e.targetHandle === "in:audio")
|
|
17501
|
-
else if (e.targetHandle === "in:ref_audio")
|
|
18021
|
+
if (e.targetHandle === "in:audio") ids.audioSourceId = e.source;
|
|
18022
|
+
else if (e.targetHandle === "in:ref_audio") ids.refAudioSourceId = e.source;
|
|
18023
|
+
else if (e.targetHandle === "in:text") ids.textSourceId = e.source;
|
|
18024
|
+
else if (e.targetHandle === "in:lyrics") ids.lyricsSourceId = e.source;
|
|
17502
18025
|
}
|
|
17503
|
-
return
|
|
17504
|
-
audioSourceId: audioSrc,
|
|
17505
|
-
refAudioSourceId: refAudioSrc
|
|
17506
|
-
};
|
|
18026
|
+
return ids;
|
|
17507
18027
|
}, [edges, nodeId]);
|
|
17508
|
-
const upstreamIds = useMemo(() => [
|
|
18028
|
+
const upstreamIds = useMemo(() => [
|
|
18029
|
+
audioSourceId,
|
|
18030
|
+
refAudioSourceId,
|
|
18031
|
+
textSourceId,
|
|
18032
|
+
lyricsSourceId
|
|
18033
|
+
].filter((v) => !!v), [
|
|
18034
|
+
audioSourceId,
|
|
18035
|
+
refAudioSourceId,
|
|
18036
|
+
textSourceId,
|
|
18037
|
+
lyricsSourceId
|
|
18038
|
+
]);
|
|
17509
18039
|
const upstreamNodes = useNodesData(upstreamIds);
|
|
18040
|
+
const upstreamText = (sourceId) => {
|
|
18041
|
+
if (!sourceId) return void 0;
|
|
18042
|
+
const n = upstreamNodes.find((u) => u.id === sourceId);
|
|
18043
|
+
if (!n || n.type !== "textNode") return void 0;
|
|
18044
|
+
return coerceBaseNodeData(n.data).texts?.[0] ?? "";
|
|
18045
|
+
};
|
|
18046
|
+
const promptFromUpstream = upstreamText(textSourceId);
|
|
18047
|
+
const lyricsFromUpstream = upstreamText(lyricsSourceId);
|
|
17510
18048
|
const audioKey = useMemo(() => {
|
|
17511
18049
|
if (!audioSourceId) return void 0;
|
|
17512
18050
|
const n = upstreamNodes.find((u) => u.id === audioSourceId);
|
|
@@ -17569,14 +18107,20 @@ const MusicCoverNode = ({ selected, data }) => {
|
|
|
17569
18107
|
})]
|
|
17570
18108
|
})
|
|
17571
18109
|
}),
|
|
17572
|
-
/* @__PURE__ */ jsx(
|
|
18110
|
+
promptFromUpstream !== void 0 ? /* @__PURE__ */ jsx(UpstreamTextPreview, {
|
|
18111
|
+
label: t("musicTasks.promptLabel"),
|
|
18112
|
+
text: promptFromUpstream
|
|
18113
|
+
}) : /* @__PURE__ */ jsx(NodeTextarea, {
|
|
17573
18114
|
label: t("musicTasks.promptLabel"),
|
|
17574
18115
|
icon: MessageSquare,
|
|
17575
18116
|
placeholder: t("musicTasks.promptPlaceholder"),
|
|
17576
18117
|
...form.bind("text"),
|
|
17577
18118
|
rows: 3
|
|
17578
18119
|
}),
|
|
17579
|
-
/* @__PURE__ */ jsx(
|
|
18120
|
+
lyricsFromUpstream !== void 0 ? /* @__PURE__ */ jsx(UpstreamTextPreview, {
|
|
18121
|
+
label: t("musicTasks.lyricsLabel"),
|
|
18122
|
+
text: lyricsFromUpstream
|
|
18123
|
+
}) : /* @__PURE__ */ jsx(NodeTextarea, {
|
|
17580
18124
|
label: t("musicTasks.lyricsLabel"),
|
|
17581
18125
|
placeholder: t("musicTasks.lyricsPlaceholder"),
|
|
17582
18126
|
...form.bind("lyrics"),
|
|
@@ -18880,19 +19424,6 @@ const VideoImageGenVideoMoveNode = ({ selected, data }) => {
|
|
|
18880
19424
|
VideoImageGenVideoMoveNode.displayName = "VideoImageGenVideoMoveNode";
|
|
18881
19425
|
var video_image_gen_video_move_default = memo(VideoImageGenVideoMoveNode);
|
|
18882
19426
|
//#endregion
|
|
18883
|
-
//#region src/canvas/ui/switch.tsx
|
|
18884
|
-
function Switch({ className, ...props }) {
|
|
18885
|
-
return /* @__PURE__ */ jsx(SwitchPrimitive.Root, {
|
|
18886
|
-
"data-slot": "switch",
|
|
18887
|
-
className: cn("peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
18888
|
-
...props,
|
|
18889
|
-
children: /* @__PURE__ */ jsx(SwitchPrimitive.Thumb, {
|
|
18890
|
-
"data-slot": "switch-thumb",
|
|
18891
|
-
className: cn("bg-background pointer-events-none block size-4 rounded-full ring-0 shadow-sm transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0")
|
|
18892
|
-
})
|
|
18893
|
-
});
|
|
18894
|
-
}
|
|
18895
|
-
//#endregion
|
|
18896
19427
|
//#region src/canvas/nodes/decompose/music-brief.tsx
|
|
18897
19428
|
function buildLanguageOptions$1(tLang) {
|
|
18898
19429
|
return [
|
|
@@ -21302,143 +21833,102 @@ AudioGenTextSpeechRecognizeNode.displayName = "AudioGenTextSpeechRecognizeNode";
|
|
|
21302
21833
|
var audio_gen_text_speech_recognize_default = memo(AudioGenTextSpeechRecognizeNode);
|
|
21303
21834
|
//#endregion
|
|
21304
21835
|
//#region src/canvas/nodes/transfer/convert-voice.tsx
|
|
21305
|
-
const
|
|
21306
|
-
|
|
21307
|
-
|
|
21308
|
-
}
|
|
21309
|
-
key: "male",
|
|
21310
|
-
value: "zh_male_1.wav"
|
|
21311
|
-
}];
|
|
21836
|
+
const firstKey = (value) => {
|
|
21837
|
+
const v = Array.isArray(value) ? value[0] : value;
|
|
21838
|
+
return typeof v === "string" && v ? v : void 0;
|
|
21839
|
+
};
|
|
21312
21840
|
const ConvertVoiceNode = ({ selected, data }) => {
|
|
21313
21841
|
const t = useTranslations("Workspace.nodes");
|
|
21314
21842
|
const form = useAbiForm("convert_voice");
|
|
21315
|
-
const
|
|
21316
|
-
const
|
|
21317
|
-
const
|
|
21318
|
-
const
|
|
21843
|
+
const nodeId = useNodeId();
|
|
21844
|
+
const nodeLookup = useStore((state) => state.nodeLookup);
|
|
21845
|
+
const edges = useStore((state) => state.edges);
|
|
21846
|
+
const resolvedSpec = useNodeAbiSpec("convert_voice");
|
|
21847
|
+
const { sourceKey, refKey } = useMemo(() => {
|
|
21848
|
+
if (!nodeId) return {
|
|
21849
|
+
sourceKey: void 0,
|
|
21850
|
+
refKey: void 0
|
|
21851
|
+
};
|
|
21852
|
+
const values = collectHandleValues(nodeId, resolvedSpec, Array.from(nodeLookup.values()), edges);
|
|
21853
|
+
return {
|
|
21854
|
+
sourceKey: firstKey(values.audio),
|
|
21855
|
+
refKey: firstKey(values.ref_audio)
|
|
21856
|
+
};
|
|
21857
|
+
}, [
|
|
21858
|
+
nodeId,
|
|
21859
|
+
resolvedSpec,
|
|
21860
|
+
nodeLookup,
|
|
21861
|
+
edges
|
|
21862
|
+
]);
|
|
21319
21863
|
return /* @__PURE__ */ jsx(AbiNodeShell, {
|
|
21320
21864
|
feature: "convert_voice",
|
|
21321
21865
|
form,
|
|
21322
21866
|
selected,
|
|
21867
|
+
className: "min-w-[360px]",
|
|
21323
21868
|
data,
|
|
21324
21869
|
title: t("titles.convertVoice"),
|
|
21325
21870
|
icon: /* @__PURE__ */ jsx(Atom, { className: "h-5 w-5" }),
|
|
21326
21871
|
executeLabel: t("actions.startReplace"),
|
|
21327
|
-
executeDisabled: !
|
|
21328
|
-
children: /* @__PURE__ */ jsx(
|
|
21329
|
-
className: "p-
|
|
21330
|
-
|
|
21331
|
-
|
|
21332
|
-
|
|
21333
|
-
|
|
21334
|
-
|
|
21335
|
-
|
|
21336
|
-
|
|
21337
|
-
|
|
21338
|
-
}),
|
|
21339
|
-
/* @__PURE__ */ jsxs(Select, {
|
|
21340
|
-
value: targetKey,
|
|
21341
|
-
onValueChange: (value) => form.set("targetKey", value),
|
|
21342
|
-
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
21343
|
-
id: "voice-select",
|
|
21344
|
-
className: "w-36 h-9",
|
|
21345
|
-
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("convertVoice.selectVoice") })
|
|
21346
|
-
}), /* @__PURE__ */ jsx(SelectContent, { children: speakers.map((opt) => /* @__PURE__ */ jsx(SelectItem, {
|
|
21347
|
-
value: opt.value,
|
|
21348
|
-
children: opt.key ? t(`common.voiceOptions.${opt.key}`) : opt.value
|
|
21349
|
-
}, opt.value)) })]
|
|
21350
|
-
}),
|
|
21351
|
-
/* @__PURE__ */ jsx(SpeakerVoiceUploader, {
|
|
21352
|
-
trigger: /* @__PURE__ */ jsx(Button, {
|
|
21353
|
-
variant: "outline",
|
|
21354
|
-
size: "icon",
|
|
21355
|
-
className: "ml-1",
|
|
21356
|
-
title: t("convertVoice.uploadVoice"),
|
|
21357
|
-
children: /* @__PURE__ */ jsx(Upload, { className: "w-4 h-4" })
|
|
21872
|
+
executeDisabled: !sourceKey || !refKey,
|
|
21873
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
21874
|
+
className: "p-4",
|
|
21875
|
+
children: /* @__PURE__ */ jsx(Card, {
|
|
21876
|
+
className: "p-3",
|
|
21877
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
21878
|
+
className: "space-y-2",
|
|
21879
|
+
children: [
|
|
21880
|
+
/* @__PURE__ */ jsx(Label, {
|
|
21881
|
+
className: "text-sm font-medium text-muted-foreground",
|
|
21882
|
+
children: t("compose.inputData")
|
|
21358
21883
|
}),
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21365
|
-
|
|
21366
|
-
|
|
21367
|
-
|
|
21368
|
-
|
|
21369
|
-
|
|
21370
|
-
|
|
21371
|
-
|
|
21372
|
-
|
|
21884
|
+
/* @__PURE__ */ jsxs("div", {
|
|
21885
|
+
className: "flex gap-4",
|
|
21886
|
+
children: [sourceKey ? /* @__PURE__ */ jsx(MediaThumbnail, {
|
|
21887
|
+
fileKey: sourceKey,
|
|
21888
|
+
label: t("convertVoice.sourceAudio"),
|
|
21889
|
+
type: "audio"
|
|
21890
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
21891
|
+
className: "flex flex-col items-center gap-1.5",
|
|
21892
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
21893
|
+
className: "relative w-16 h-16 rounded-md border-2 border-gray-300 overflow-hidden bg-gray-100",
|
|
21894
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
21895
|
+
className: "flex items-center justify-center h-full w-full bg-orange-50",
|
|
21896
|
+
children: /* @__PURE__ */ jsx(Music, { className: "w-6 h-6 text-orange-600" })
|
|
21897
|
+
})
|
|
21898
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
21899
|
+
className: "px-1.5 py-0.5 bg-orange-100 text-orange-700 text-xs font-medium rounded",
|
|
21900
|
+
children: t("convertVoice.sourceAudio")
|
|
21901
|
+
})]
|
|
21902
|
+
}), refKey ? /* @__PURE__ */ jsx(MediaThumbnail, {
|
|
21903
|
+
fileKey: refKey,
|
|
21904
|
+
label: t("convertVoice.referenceVoice"),
|
|
21905
|
+
type: "audio"
|
|
21906
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
21907
|
+
className: "flex flex-col items-center gap-1.5",
|
|
21908
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
21909
|
+
className: "relative w-16 h-16 rounded-md border-2 border-gray-300 overflow-hidden bg-gray-100",
|
|
21910
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
21911
|
+
className: "flex items-center justify-center h-full w-full bg-blue-50",
|
|
21912
|
+
children: /* @__PURE__ */ jsx(Mic, { className: "w-6 h-6 text-blue-600" })
|
|
21913
|
+
})
|
|
21914
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
21915
|
+
className: "px-1.5 py-0.5 bg-blue-100 text-blue-700 text-xs font-medium rounded",
|
|
21916
|
+
children: t("convertVoice.referenceVoice")
|
|
21917
|
+
})]
|
|
21918
|
+
})]
|
|
21373
21919
|
}),
|
|
21374
|
-
|
|
21375
|
-
|
|
21376
|
-
|
|
21377
|
-
|
|
21378
|
-
|
|
21379
|
-
|
|
21380
|
-
})
|
|
21381
|
-
]
|
|
21920
|
+
(!sourceKey || !refKey) && /* @__PURE__ */ jsx("p", {
|
|
21921
|
+
className: "text-xs text-red-500",
|
|
21922
|
+
children: t("convertVoice.connectHint")
|
|
21923
|
+
})
|
|
21924
|
+
]
|
|
21925
|
+
})
|
|
21382
21926
|
})
|
|
21383
21927
|
})
|
|
21384
21928
|
});
|
|
21385
21929
|
};
|
|
21930
|
+
ConvertVoiceNode.displayName = "ConvertVoiceNode";
|
|
21386
21931
|
var convert_voice_default = memo(ConvertVoiceNode);
|
|
21387
|
-
const SpeakerVoiceUploader = ({ trigger, onChange: _onChange }) => {
|
|
21388
|
-
const [_uploaded, setUploaded] = useState(false);
|
|
21389
|
-
const doUpload = async (files) => {
|
|
21390
|
-
logger.debug("Uploading files:", files);
|
|
21391
|
-
setUploaded(true);
|
|
21392
|
-
};
|
|
21393
|
-
return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("label", {
|
|
21394
|
-
className: "cursor-pointer",
|
|
21395
|
-
children: [/* @__PURE__ */ jsx("input", {
|
|
21396
|
-
type: "file",
|
|
21397
|
-
multiple: true,
|
|
21398
|
-
hidden: true,
|
|
21399
|
-
onChange: (e) => {
|
|
21400
|
-
const files = Array.from(e.target.files || []);
|
|
21401
|
-
if (files.length > 0) doUpload(files);
|
|
21402
|
-
}
|
|
21403
|
-
}), trigger]
|
|
21404
|
-
}) });
|
|
21405
|
-
};
|
|
21406
|
-
const SpeakerVoiceRecorder$1 = ({ trigger, onChange: _onChange }) => {
|
|
21407
|
-
const t = useTranslations("Workspace.nodes.convertVoice");
|
|
21408
|
-
const [file, _setFile] = useState();
|
|
21409
|
-
const onFinish = async () => {
|
|
21410
|
-
if (!file) return;
|
|
21411
|
-
logger.debug("Recording file:", file);
|
|
21412
|
-
};
|
|
21413
|
-
return /* @__PURE__ */ jsxs(Dialog, { children: [/* @__PURE__ */ jsx(DialogTrigger, {
|
|
21414
|
-
asChild: true,
|
|
21415
|
-
children: trigger
|
|
21416
|
-
}), /* @__PURE__ */ jsxs(DialogContent, {
|
|
21417
|
-
className: "sm:max-w-md",
|
|
21418
|
-
children: [
|
|
21419
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: t("recordAudio") }), /* @__PURE__ */ jsx(DialogDescription, { children: t("recordHint") })] }),
|
|
21420
|
-
/* @__PURE__ */ jsx("div", {
|
|
21421
|
-
className: "overflow-auto scroll-smooth w-80 max-h-80",
|
|
21422
|
-
children: /* @__PURE__ */ jsx("p", {
|
|
21423
|
-
className: "text-sm text-gray-500",
|
|
21424
|
-
children: t("recordNeedImpl")
|
|
21425
|
-
})
|
|
21426
|
-
}),
|
|
21427
|
-
/* @__PURE__ */ jsx(DialogFooter, {
|
|
21428
|
-
className: "sm:justify-start",
|
|
21429
|
-
children: /* @__PURE__ */ jsx(DialogClose, {
|
|
21430
|
-
asChild: true,
|
|
21431
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
21432
|
-
type: "button",
|
|
21433
|
-
variant: "secondary",
|
|
21434
|
-
onClick: () => onFinish(),
|
|
21435
|
-
children: t("done")
|
|
21436
|
-
})
|
|
21437
|
-
})
|
|
21438
|
-
})
|
|
21439
|
-
]
|
|
21440
|
-
})] });
|
|
21441
|
-
};
|
|
21442
21932
|
//#endregion
|
|
21443
21933
|
//#region src/canvas/nodes/transfer/denoise-audio.tsx
|
|
21444
21934
|
const DenoiseAudioNode = ({ selected, data }) => {
|
|
@@ -22223,7 +22713,7 @@ const RemoveWatermarkNode = ({ selected, data }) => {
|
|
|
22223
22713
|
executeLabel: t("actions.removeWatermark"),
|
|
22224
22714
|
executeDisabled: !fileKeys?.length,
|
|
22225
22715
|
children: /* @__PURE__ */ jsx(Card, {
|
|
22226
|
-
className: "p-
|
|
22716
|
+
className: "mx-4 mb-4 p-4 space-y-4",
|
|
22227
22717
|
children: /* @__PURE__ */ jsx("div", {
|
|
22228
22718
|
className: "text-sm text-muted-foreground",
|
|
22229
22719
|
children: t("removeWatermark.hint")
|
|
@@ -22368,6 +22858,109 @@ const SpeechGenVideoNode = ({ selected, data }) => {
|
|
|
22368
22858
|
SpeechGenVideoNode.displayName = "SpeechGenVideoNode";
|
|
22369
22859
|
var speech_gen_video_default = memo(SpeechGenVideoNode);
|
|
22370
22860
|
//#endregion
|
|
22861
|
+
//#region src/canvas/nodes/transfer/text-gen-audio.tsx
|
|
22862
|
+
const MAX_REF_AUDIOS = 3;
|
|
22863
|
+
const VOICE_TAG = "@voice";
|
|
22864
|
+
const TextGenAudioNode = ({ selected, data }) => {
|
|
22865
|
+
const t = useTranslations("Workspace.nodes");
|
|
22866
|
+
const form = useAbiForm("text-gen-audio");
|
|
22867
|
+
const ids = useUpstreamNodeIds(data.ids);
|
|
22868
|
+
const fromNodes = useNodesData(ids);
|
|
22869
|
+
const refAudios = fromNodes.filter((node) => node.type === "audioNode").map((node) => coerceBaseNodeData(node.data).fileKeys).filter((keys) => !!keys && keys.length > 0);
|
|
22870
|
+
const textNode = fromNodes.find((node) => node.type === "textNode");
|
|
22871
|
+
const upstreamTexts = useMemo(() => textNode ? coerceBaseNodeData(textNode.data).texts || [] : [], [textNode]);
|
|
22872
|
+
const hasUpstreamTexts = upstreamTexts.length > 0;
|
|
22873
|
+
const prompt = form.state.text ?? "";
|
|
22874
|
+
const textareaRef = useRef(null);
|
|
22875
|
+
const insertVoiceTag = useCallback((tag) => {
|
|
22876
|
+
const textarea = textareaRef.current;
|
|
22877
|
+
if (!textarea) return;
|
|
22878
|
+
const start = textarea.selectionStart;
|
|
22879
|
+
const end = textarea.selectionEnd;
|
|
22880
|
+
form.set("text", prompt.substring(0, start) + tag + prompt.substring(end));
|
|
22881
|
+
setTimeout(() => {
|
|
22882
|
+
textarea.focus();
|
|
22883
|
+
const cursor = start + tag.length;
|
|
22884
|
+
textarea.setSelectionRange(cursor, cursor);
|
|
22885
|
+
}, 0);
|
|
22886
|
+
}, [prompt, form]);
|
|
22887
|
+
return /* @__PURE__ */ jsx(AbiNodeShell, {
|
|
22888
|
+
feature: "text-gen-audio",
|
|
22889
|
+
form,
|
|
22890
|
+
selected,
|
|
22891
|
+
className: "min-w-[480px]",
|
|
22892
|
+
data,
|
|
22893
|
+
title: t("titles.textGenAudio"),
|
|
22894
|
+
icon: /* @__PURE__ */ jsx(AudioWaveform, { className: "h-5 w-5" }),
|
|
22895
|
+
executeLabel: t("actions.generateAudio"),
|
|
22896
|
+
executeDisabled: !hasUpstreamTexts && !prompt.trim(),
|
|
22897
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
22898
|
+
className: "p-4 space-y-4",
|
|
22899
|
+
children: [
|
|
22900
|
+
refAudios.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
22901
|
+
className: "space-y-2",
|
|
22902
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
22903
|
+
className: "text-sm font-medium text-muted-foreground",
|
|
22904
|
+
children: [t("textGenAudio.voiceReference"), /* @__PURE__ */ jsxs("span", {
|
|
22905
|
+
className: "ml-2 text-xs font-normal",
|
|
22906
|
+
children: [
|
|
22907
|
+
"(",
|
|
22908
|
+
refAudios.length,
|
|
22909
|
+
"/",
|
|
22910
|
+
MAX_REF_AUDIOS,
|
|
22911
|
+
")"
|
|
22912
|
+
]
|
|
22913
|
+
})]
|
|
22914
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
22915
|
+
className: "flex gap-3 flex-wrap",
|
|
22916
|
+
children: refAudios.slice(0, MAX_REF_AUDIOS).map((keys, index) => /* @__PURE__ */ jsx(MediaThumbnail, {
|
|
22917
|
+
fileKey: keys[0],
|
|
22918
|
+
label: `${VOICE_TAG}${index + 1}`,
|
|
22919
|
+
type: "audio",
|
|
22920
|
+
onClick: () => insertVoiceTag(`${VOICE_TAG}${index + 1}`)
|
|
22921
|
+
}, keys[0]))
|
|
22922
|
+
})]
|
|
22923
|
+
}),
|
|
22924
|
+
/* @__PURE__ */ jsx("p", {
|
|
22925
|
+
className: "text-xs text-muted-foreground",
|
|
22926
|
+
children: t("textGenAudio.hint")
|
|
22927
|
+
}),
|
|
22928
|
+
hasUpstreamTexts ? /* @__PURE__ */ jsxs("div", {
|
|
22929
|
+
className: "space-y-2",
|
|
22930
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
22931
|
+
className: "flex items-center gap-2 text-sm font-medium text-muted-foreground",
|
|
22932
|
+
children: [
|
|
22933
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" }),
|
|
22934
|
+
t("textGenAudio.promptLabel"),
|
|
22935
|
+
t("imageEdit.fromUpstream")
|
|
22936
|
+
]
|
|
22937
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
22938
|
+
className: "space-y-1 max-h-32 overflow-y-auto",
|
|
22939
|
+
children: upstreamTexts.map((text, index) => /* @__PURE__ */ jsx("div", {
|
|
22940
|
+
className: "text-sm text-foreground p-2 bg-background rounded border border-border/50 line-clamp-3",
|
|
22941
|
+
children: text
|
|
22942
|
+
}, `${index}-${text.slice(0, 48)}`))
|
|
22943
|
+
})]
|
|
22944
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
22945
|
+
className: "space-y-2",
|
|
22946
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
22947
|
+
className: "flex items-center gap-2 text-sm font-medium text-muted-foreground",
|
|
22948
|
+
children: [/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" }), t("textGenAudio.promptLabel")]
|
|
22949
|
+
}), /* @__PURE__ */ jsx(NodeTextarea, {
|
|
22950
|
+
ref: textareaRef,
|
|
22951
|
+
showCard: false,
|
|
22952
|
+
placeholder: t("textGenAudio.promptPlaceholder"),
|
|
22953
|
+
...form.bind("text"),
|
|
22954
|
+
rows: 5
|
|
22955
|
+
})]
|
|
22956
|
+
})
|
|
22957
|
+
]
|
|
22958
|
+
})
|
|
22959
|
+
});
|
|
22960
|
+
};
|
|
22961
|
+
TextGenAudioNode.displayName = "TextGenAudioNode";
|
|
22962
|
+
var text_gen_audio_default = memo(TextGenAudioNode);
|
|
22963
|
+
//#endregion
|
|
22371
22964
|
//#region src/canvas/nodes/base/resolution-picker.tsx
|
|
22372
22965
|
function ResolutionPicker({ tiers, value, onChange }) {
|
|
22373
22966
|
const t = useTranslations("Workspace.nodes");
|
|
@@ -22932,7 +23525,7 @@ const TextGenSpeechCloneNode = ({ selected, data }) => {
|
|
|
22932
23525
|
executeLabel: t("actions.generateSpeech"),
|
|
22933
23526
|
executeDisabled: !texts?.length || !refAudio,
|
|
22934
23527
|
children: /* @__PURE__ */ jsxs(Card, {
|
|
22935
|
-
className: "p-
|
|
23528
|
+
className: "mx-4 mb-4 p-4 nodrag",
|
|
22936
23529
|
onPointerDown: (e) => e.stopPropagation(),
|
|
22937
23530
|
children: [
|
|
22938
23531
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -23046,7 +23639,7 @@ const TextGenSpeechInstructNode = ({ selected, data }) => {
|
|
|
23046
23639
|
executeLabel: t("actions.generateSpeech"),
|
|
23047
23640
|
executeDisabled: !texts?.length,
|
|
23048
23641
|
children: /* @__PURE__ */ jsxs(Card, {
|
|
23049
|
-
className: "p-
|
|
23642
|
+
className: "mx-4 mb-4 p-4 nodrag",
|
|
23050
23643
|
onPointerDown: (e) => e.stopPropagation(),
|
|
23051
23644
|
children: [
|
|
23052
23645
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -23136,7 +23729,7 @@ const TextGenSpeechPresetNode = ({ selected, data }) => {
|
|
|
23136
23729
|
executeLabel: t("actions.generateSpeech"),
|
|
23137
23730
|
executeDisabled: !texts?.length,
|
|
23138
23731
|
children: /* @__PURE__ */ jsxs(Card, {
|
|
23139
|
-
className: "p-
|
|
23732
|
+
className: "mx-4 mb-4 p-4 nodrag",
|
|
23140
23733
|
onPointerDown: (e) => e.stopPropagation(),
|
|
23141
23734
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
23142
23735
|
className: "mb-4 flex flex-wrap items-center gap-3",
|
|
@@ -23635,6 +24228,7 @@ const NODE_TYPES = {
|
|
|
23635
24228
|
genTextNode: text_gen_text_default,
|
|
23636
24229
|
textGenImageNode: text_gen_image_default,
|
|
23637
24230
|
textGenMusicNode: text_gen_music_default,
|
|
24231
|
+
textGenAudioNode: text_gen_audio_default,
|
|
23638
24232
|
textGenSpeechCloneNode: text_gen_speech_clone_default,
|
|
23639
24233
|
textGenSpeechCloneComposeNode: text_gen_speech_clone_compose_default,
|
|
23640
24234
|
textGenSpeechPresetNode: text_gen_speech_preset_default,
|
|
@@ -23726,6 +24320,7 @@ const NODE_CATEGORIES = {
|
|
|
23726
24320
|
"genTextNode",
|
|
23727
24321
|
"textGenImageNode",
|
|
23728
24322
|
"textGenMusicNode",
|
|
24323
|
+
"textGenAudioNode",
|
|
23729
24324
|
"textGenSpeechCloneNode",
|
|
23730
24325
|
"textGenSpeechPresetNode",
|
|
23731
24326
|
"textGenSpeechInstructNode",
|
|
@@ -24492,4 +25087,4 @@ function SheetDescription({ className, ...props }) {
|
|
|
24492
25087
|
});
|
|
24493
25088
|
}
|
|
24494
25089
|
//#endregion
|
|
24495
|
-
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AudioRecorderWithVisualizer, Badge, Button, CANVAS_LOCALES, CanvasProvider, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, DEFAULT_QWEN_SPEAKER, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EDGE_TYPES, ErrorBoundary, FORMAT_CATEGORIES, FlowCanvas, Input, Label, LibInput, NODE_CATEGORIES, NODE_TYPES, NodePluginIdSelect, Progress, QWEN_SPEAKERS, RECOMMENDATIONS, SSE_TASK_MESSAGE_EVENT, SUPPORTED_FORMATS, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Skeleton, Slider, SpeakerVoiceRecorder, Switch, TASK_CANCEL_REQUEST_EVENT, TROUBLESHOOTING, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadValidationError, VideoRecorder, WORKSPACE_MODE_KEY, Waterfall, WhiteBoard, apiClient, apiDelete, apiGet, apiPatch, apiPost, apiPut, apiUrl, applyResolvedOutputRoutes, assetUrl, badgeVariants, buttonVariants, canvasMessages, cn, computeOutputView, configureCanvasHost, createMaterial, createTask, deleteMaterial, emitSSEConnected, emitSSETaskMessage, emitTaskCancelRequest, getCanvasHost, getCanvasLocale, getClientTranslator, getFileUrl, getGlobalFileCache, getGlobalFileQueue, getImageDimensions, getMaterialFileUrls, getPresignedUploadUrl, getPrivateFileUrl, getSharedFileUrl, getTaskStopUrl, getTaskWaitUrl, getVideoMetadata, hostFetch, isCanvasLocale, isImageFile, isVideoFile, listMaterials, listTasks, normalizeTaskPayloadData, pluginDisplayName, refreshPluginsRegistry, saveFromTask, setCanvasLocale, showErrorToast, toggleFavorite, traceMaterialWorkflow, updateTaskStatus, useAbiExecution, useAbiForm, useBatchTaskManager, useCreateTask, useDropzone, useFileAsyncLoader, useFileAsyncLoaderBatch, useFileLoaderStats, useFileUrl, useFlow, useMultipleUpload, useNodeAbiSpec, useNodePluginIds, useNodePluginIdsUnion, useNodePluginModels, useNodePluginResolver, useNodeState, useNodeTaskUpdate, useNodeType, usePluginMeta, usePluginsRegistry, usePluginsRegistryStore, useResolvedPluginId, useTask, useTaskStore, useTaskSubscription, useUpload, useUpstreamNodeIds, validateFile };
|
|
25090
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AudioRecorderWithVisualizer, Badge, Button, CANVAS_LOCALES, CanvasProvider, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, DEFAULT_QWEN_SPEAKER, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EDGE_TYPES, ErrorBoundary, FORMAT_CATEGORIES, FlowCanvas, Input, Label, LibInput, NODE_CATEGORIES, NODE_TYPES, NodePluginIdSelect, Progress, QWEN_SPEAKERS, RECOMMENDATIONS, SSE_TASK_MESSAGE_EVENT, SUPPORTED_FORMATS, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Skeleton, Slider, SpeakerVoiceRecorder, Switch, TASK_CANCEL_REQUEST_EVENT, TROUBLESHOOTING, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadValidationError, VideoRecorder, WORKSPACE_MODE_KEY, Waterfall, WhiteBoard, apiClient, apiDelete, apiGet, apiPatch, apiPost, apiPut, apiUrl, applyResolvedOutputRoutes, assetUrl, badgeVariants, buttonVariants, canvasMessages, cn, computeOutputView, configureCanvasHost, createMaterial, createTask, deleteMaterial, emitSSEConnected, emitSSETaskMessage, emitTaskCancelRequest, getCanvasHost, getCanvasLocale, getClientTranslator, getFileUrl, getGlobalFileCache, getGlobalFileQueue, getImageDimensions, getMaterialFileUrls, getNodePluginModels, getNodePluginParams, getPresignedUploadUrl, getPrivateFileUrl, getSharedFileUrl, getTaskStopUrl, getTaskWaitUrl, getVideoMetadata, hostFetch, isCanvasLocale, isImageFile, isVideoFile, listMaterials, listTasks, loadPluginModelCatalog, normalizeTaskPayloadData, pluginDisplayName, refreshPluginsRegistry, saveFromTask, setCanvasLocale, showErrorToast, toggleFavorite, traceMaterialWorkflow, updateTaskStatus, useAbiExecution, useAbiForm, useBatchTaskManager, useCreateTask, useDropzone, useFileAsyncLoader, useFileAsyncLoaderBatch, useFileLoaderStats, useFileUrl, useFlow, useLiveModelsStore, useMultipleUpload, useNodeAbiSpec, useNodePluginIds, useNodePluginIdsUnion, useNodePluginModels, useNodePluginParams, useNodePluginResolver, useNodeState, useNodeTaskUpdate, useNodeType, usePluginMeta, usePluginsRegistry, usePluginsRegistryStore, useResolvedPluginId, useTask, useTaskStore, useTaskSubscription, useUpload, useUpstreamNodeIds, validateFile };
|