@ai-game-assets/phaser 0.7.0 → 0.8.1
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/debug-client.d.ts +3 -0
- package/dist/debug-client.d.ts.map +1 -1
- package/dist/debug-client.js +11 -0
- package/dist/debug-client.js.map +1 -1
- package/dist/designer-support.d.ts +7 -0
- package/dist/designer-support.d.ts.map +1 -1
- package/dist/designer-support.js +39 -1
- package/dist/designer-support.js.map +1 -1
- package/dist/designer.d.ts.map +1 -1
- package/dist/designer.js +354 -10
- package/dist/designer.js.map +1 -1
- package/dist/first-drafts.d.ts.map +1 -1
- package/dist/first-drafts.js +47 -31
- package/dist/first-drafts.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/voice-line-regeneration.d.ts +47 -0
- package/dist/voice-line-regeneration.d.ts.map +1 -0
- package/dist/voice-line-regeneration.js +185 -0
- package/dist/voice-line-regeneration.js.map +1 -0
- package/package.json +2 -2
package/dist/designer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"designer.d.ts","sourceRoot":"","sources":["../src/designer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,iBAAiB,EAOjB,eAAe,EACf,cAAc,EACd,sBAAsB,EAEvB,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EACL,kBAAkB,EAElB,KAAK,oBAAoB,EAE1B,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"designer.d.ts","sourceRoot":"","sources":["../src/designer.ts"],"names":[],"mappings":"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"}
|
package/dist/designer.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ensureMissingAiAssetFirstDrafts } from "./first-drafts.js";
|
|
|
4
4
|
import { createLocalDerivedOption, openDeriveDialog, referenceImageForCandidate } from "./derive-dialog.js";
|
|
5
5
|
import { createMixedTilesetOption, isDesignerTilesetAsset, openTilesetBaseMixerDialog, openTilesetAnimationEditor, openTilesetAnimationMixerDialog, openTilesetEditor, tilesetAnimationForKey, resolveTilesetBaseMixCurrent, tilesetMetadataForAsset, tilesetTileGenerationOverride } from "./tileset-dialog.js";
|
|
6
6
|
import { aiTilesetAnimationTextureKey } from "./keys.js";
|
|
7
|
+
import { promotedVoiceId, regenerateAndPromoteVoiceLines, voiceLineRegenerationPlan } from "./voice-line-regeneration.js";
|
|
7
8
|
/**
|
|
8
9
|
* Resolves the canonical, untransformed source used by Edit Tileset. A pending
|
|
9
10
|
* option is its own editing lineage: when it has no saved source metadata, its
|
|
@@ -33,7 +34,7 @@ export function resolveTilesetEditorInitialState(options) {
|
|
|
33
34
|
function cloneTilesetTransforms(transforms) {
|
|
34
35
|
return transforms?.map((transform) => ({ ...transform }));
|
|
35
36
|
}
|
|
36
|
-
import { assetWithGeneratedGeometry, audioGenerationOverridesFromInputs, bindKeyboardCapture, canEditGenerationFormat, createDesignerElements, effectiveGenerationFormat, ensureDesignerStyles, errorMessage, generationOverridesFromInputs, hasStyleGuide, imageSourceToDataUrl, installPromotedImageTexture, isAbortError, isAudioAsset, isSvgSource, isVoiceAsset, loadImageElement, mimeTypeFromDataUrl, normalizeAssetFormat, normalizeAudioFormat, openAnimationEditor, openAssetVersionsDialog, openAudioEditor, openFrameTouchUpEditor, openStyleGuideEditor, pickUploadFile, positiveIntegerInput, previewCurrentAsset, previewImageSource, previewOption, readableAssetName, renderAssetFolderBrowser, renderAudioPlayer, renderOptions, resolvePreviewDisplaySize, setStatus, stopStatusAnimation, styleGuideDraftFromManifest, styleGuideRequest, uploadedOptionFromFile, voiceGenerationOverridesFromInputs } from "./designer-support.js";
|
|
37
|
+
import { assetWithGeneratedGeometry, audioGenerationOverridesFromInputs, bindDesignerInputBoundary, bindKeyboardCapture, canEditGenerationFormat, createDesignerElements, effectiveGenerationFormat, ensureDesignerStyles, errorMessage, generationOverridesFromInputs, hasStyleGuide, imageSourceToDataUrl, installPromotedImageTexture, isAbortError, isAudioAsset, isSvgSource, isVoiceAsset, loadImageElement, mimeTypeFromDataUrl, normalizeAssetFormat, normalizeAudioFormat, openAnimationEditor, openAssetVersionsDialog, openAudioEditor, openFrameTouchUpEditor, openStyleGuideEditor, pickUploadFile, positiveIntegerInput, previewCurrentAsset, previewImageSource, previewOption, readableAssetName, renderAssetFolderBrowser, renderAudioPlayer, renderOptions, resolvePreviewDisplaySize, setStatus, stopStatusAnimation, styleGuideDraftFromManifest, styleGuideRequest, uploadedOptionFromFile, voiceGenerationOverridesFromInputs } from "./designer-support.js";
|
|
37
38
|
export function installAiAssetDesigner(options) {
|
|
38
39
|
ensureDesignerStyles();
|
|
39
40
|
const client = options.client ?? new AiAssetDebugClient();
|
|
@@ -63,6 +64,13 @@ export function installAiAssetDesigner(options) {
|
|
|
63
64
|
let panelRevision = 0;
|
|
64
65
|
let promotionId = 0;
|
|
65
66
|
let activePromotionId;
|
|
67
|
+
let activeVoiceLineRegeneration;
|
|
68
|
+
let activeFirstDrafts = options.autoFirstDrafts !== false;
|
|
69
|
+
let destroyed = false;
|
|
70
|
+
let deferredVoiceLineManifestSync = false;
|
|
71
|
+
const deferredVoiceLinePendingAssetIds = new Set();
|
|
72
|
+
let deferredVoiceLineManifestSyncPromise;
|
|
73
|
+
let voiceLineBatchControlStates;
|
|
66
74
|
let mixingTileset = false;
|
|
67
75
|
const regenerateTilesetTile = async (assetId, tile, tileset, currentTileSrc) => {
|
|
68
76
|
const guide = await styleGuideRequest(styleGuideDraft);
|
|
@@ -99,12 +107,89 @@ export function installAiAssetDesigner(options) {
|
|
|
99
107
|
}
|
|
100
108
|
const mount = options.mount ?? document.body;
|
|
101
109
|
mount.append(elements.root);
|
|
110
|
+
const unbindInputBoundary = bindDesignerInputBoundary(elements.root, elements.toggle);
|
|
102
111
|
bindKeyboardCapture(elements.root, options.scene);
|
|
112
|
+
const setVoiceLineBatchControlsLocked = (locked) => {
|
|
113
|
+
if (locked) {
|
|
114
|
+
if (voiceLineBatchControlStates)
|
|
115
|
+
return;
|
|
116
|
+
const controls = Array.from(elements.panel.querySelectorAll("button, input, select, textarea")).filter((control) => control !== elements.regenerateAllLinesButton);
|
|
117
|
+
voiceLineBatchControlStates = controls.map((control) => [control, control.disabled]);
|
|
118
|
+
for (const control of controls)
|
|
119
|
+
control.disabled = true;
|
|
120
|
+
elements.assetBrowser.setAttribute("inert", "");
|
|
121
|
+
elements.currentPreview.setAttribute("inert", "");
|
|
122
|
+
elements.options.setAttribute("inert", "");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (const [control, wasDisabled] of voiceLineBatchControlStates ?? []) {
|
|
126
|
+
control.disabled = wasDisabled;
|
|
127
|
+
}
|
|
128
|
+
voiceLineBatchControlStates = undefined;
|
|
129
|
+
elements.assetBrowser.removeAttribute("inert");
|
|
130
|
+
elements.currentPreview.removeAttribute("inert");
|
|
131
|
+
elements.options.removeAttribute("inert");
|
|
132
|
+
};
|
|
133
|
+
const syncRegenerateAllLinesButton = () => {
|
|
134
|
+
if (activeVoiceLineRegeneration) {
|
|
135
|
+
const stopping = activeVoiceLineRegeneration.controller.signal.aborted;
|
|
136
|
+
elements.regenerateAllLinesButton.hidden = false;
|
|
137
|
+
elements.regenerateAllLinesButton.disabled = stopping;
|
|
138
|
+
elements.regenerateAllLinesButton.textContent = stopping
|
|
139
|
+
? "Stopping after current line..."
|
|
140
|
+
: "Stop after current line";
|
|
141
|
+
elements.regenerateAllLinesButton.title = stopping
|
|
142
|
+
? "The batch will stop after the current line is promoted."
|
|
143
|
+
: "Finish and promote the current line, then stop before generating another.";
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const selectedAsset = manifest.assets[selectedAssetId];
|
|
147
|
+
const plan = voiceLineRegenerationPlan(manifest, selectedAssetId, selectedTargetId);
|
|
148
|
+
const { baseVoiceAssetId, lineAssetIds, missingTargetLineAssetIds } = plan;
|
|
149
|
+
const isBaseVoiceView = selectedAsset?.kind === "voice" &&
|
|
150
|
+
selectedTargetAssetId === baseVoiceAssetId &&
|
|
151
|
+
!selectedTilesetAnimationKey;
|
|
152
|
+
const hasPromotedVoice = Boolean(promotedVoiceId(manifest, baseVoiceAssetId));
|
|
153
|
+
const hasPendingBaseVoice = pendingOptions.has(baseVoiceAssetId);
|
|
154
|
+
const pendingLineCount = lineAssetIds.filter((assetId) => pendingOptions.has(assetId)).length;
|
|
155
|
+
const hasAnyPendingOption = pendingOptions.size > 0;
|
|
156
|
+
elements.regenerateAllLinesButton.textContent = "Regenerate all lines";
|
|
157
|
+
elements.regenerateAllLinesButton.hidden = !isBaseVoiceView;
|
|
158
|
+
elements.regenerateAllLinesButton.disabled =
|
|
159
|
+
!isBaseVoiceView ||
|
|
160
|
+
!hasPromotedVoice ||
|
|
161
|
+
hasPendingBaseVoice ||
|
|
162
|
+
pendingLineCount > 0 ||
|
|
163
|
+
hasAnyPendingOption ||
|
|
164
|
+
missingTargetLineAssetIds.length > 0 ||
|
|
165
|
+
lineAssetIds.length === 0 ||
|
|
166
|
+
activeFirstDrafts ||
|
|
167
|
+
Boolean(activeGeneration) ||
|
|
168
|
+
activePromotionId !== undefined;
|
|
169
|
+
elements.regenerateAllLinesButton.title = !isBaseVoiceView
|
|
170
|
+
? ""
|
|
171
|
+
: hasPendingBaseVoice
|
|
172
|
+
? "Promote the selected base voice before regenerating its lines."
|
|
173
|
+
: missingTargetLineAssetIds.length > 0
|
|
174
|
+
? `Create target variants for ${missingTargetLineAssetIds.length} linked line${missingTargetLineAssetIds.length === 1 ? "" : "s"} before regenerating this target voice.`
|
|
175
|
+
: pendingLineCount > 0
|
|
176
|
+
? `Promote or revert the ${pendingLineCount} pending linked line${pendingLineCount === 1 ? "" : "s"} before regenerating all lines.`
|
|
177
|
+
: hasAnyPendingOption
|
|
178
|
+
? "Promote or revert all pending asset options before regenerating voice lines."
|
|
179
|
+
: activeFirstDrafts
|
|
180
|
+
? "Wait for automatic first drafts to finish before regenerating voice lines."
|
|
181
|
+
: !hasPromotedVoice
|
|
182
|
+
? "Promote a base voice before regenerating its lines."
|
|
183
|
+
: lineAssetIds.length === 0
|
|
184
|
+
? "This voice has no linked lines to regenerate."
|
|
185
|
+
: `Generate and promote one new option for ${lineAssetIds.length} linked line${lineAssetIds.length === 1 ? "" : "s"}.`;
|
|
186
|
+
};
|
|
103
187
|
const syncPromoteAllButton = () => {
|
|
104
188
|
elements.promoteAllButton.disabled =
|
|
105
189
|
pendingOptions.size === 0 ||
|
|
106
190
|
Boolean(activeGeneration) ||
|
|
107
191
|
activePromotionId !== undefined;
|
|
192
|
+
syncRegenerateAllLinesButton();
|
|
108
193
|
};
|
|
109
194
|
const rememberPendingOption = (assetId, option, pending = {}) => {
|
|
110
195
|
pendingOptions.set(assetId, {
|
|
@@ -116,8 +201,53 @@ export function installAiAssetDesigner(options) {
|
|
|
116
201
|
});
|
|
117
202
|
syncPromoteAllButton();
|
|
118
203
|
};
|
|
204
|
+
const flushDeferredVoiceLineManifest = () => {
|
|
205
|
+
if (!deferredVoiceLineManifestSync)
|
|
206
|
+
return Promise.resolve(true);
|
|
207
|
+
if ([...deferredVoiceLinePendingAssetIds].some((assetId) => pendingOptions.has(assetId))) {
|
|
208
|
+
return Promise.resolve(false);
|
|
209
|
+
}
|
|
210
|
+
if (deferredVoiceLineManifestSyncPromise) {
|
|
211
|
+
return deferredVoiceLineManifestSyncPromise;
|
|
212
|
+
}
|
|
213
|
+
deferredVoiceLineManifestSyncPromise = (async () => {
|
|
214
|
+
try {
|
|
215
|
+
const syncedManifest = await client.syncManifestModule();
|
|
216
|
+
manifest = syncedManifest;
|
|
217
|
+
deferredVoiceLineManifestSync = false;
|
|
218
|
+
deferredVoiceLinePendingAssetIds.clear();
|
|
219
|
+
if (!destroyed) {
|
|
220
|
+
try {
|
|
221
|
+
options.onManifestUpdated?.(manifest);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
setStatus(elements, `Manifest refreshed, but the live game could not adopt it. ${errorMessage(error)}`, "error");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
if (!destroyed) {
|
|
231
|
+
setStatus(elements, `Could not refresh the generated manifest module. ${errorMessage(error)}`, "error");
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
deferredVoiceLineManifestSyncPromise = undefined;
|
|
237
|
+
}
|
|
238
|
+
})();
|
|
239
|
+
return deferredVoiceLineManifestSyncPromise;
|
|
240
|
+
};
|
|
241
|
+
const resolveDeferredVoiceLinePendingOption = (assetId) => {
|
|
242
|
+
if (!deferredVoiceLinePendingAssetIds.delete(assetId))
|
|
243
|
+
return;
|
|
244
|
+
if (deferredVoiceLinePendingAssetIds.size === 0) {
|
|
245
|
+
void flushDeferredVoiceLineManifest();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
119
248
|
const forgetPendingOption = (assetId) => {
|
|
120
249
|
pendingOptions.delete(assetId);
|
|
250
|
+
resolveDeferredVoiceLinePendingOption(assetId);
|
|
121
251
|
syncPromoteAllButton();
|
|
122
252
|
};
|
|
123
253
|
const hasEditableTilesetAnimationFrames = (assetId, animationKey) => {
|
|
@@ -1417,6 +1547,189 @@ export function installAiAssetDesigner(options) {
|
|
|
1417
1547
|
finishGeneration(currentGenerationId);
|
|
1418
1548
|
}
|
|
1419
1549
|
});
|
|
1550
|
+
elements.regenerateAllLinesButton.addEventListener("click", async () => {
|
|
1551
|
+
if (activeVoiceLineRegeneration) {
|
|
1552
|
+
if (!activeVoiceLineRegeneration.controller.signal.aborted) {
|
|
1553
|
+
activeVoiceLineRegeneration.controller.abort();
|
|
1554
|
+
syncRegenerateAllLinesButton();
|
|
1555
|
+
setStatus(elements, "Stopping after the current line is promoted...", "busy");
|
|
1556
|
+
}
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const voiceAssetId = selectedAssetId;
|
|
1560
|
+
const voice = manifest.assets[voiceAssetId];
|
|
1561
|
+
const plan = voiceLineRegenerationPlan(manifest, voiceAssetId, selectedTargetId);
|
|
1562
|
+
const { baseVoiceAssetId, lineAssetIds, missingTargetLineAssetIds } = plan;
|
|
1563
|
+
if (voice?.kind !== "voice" ||
|
|
1564
|
+
selectedTargetAssetId !== baseVoiceAssetId ||
|
|
1565
|
+
selectedTilesetAnimationKey ||
|
|
1566
|
+
activeFirstDrafts ||
|
|
1567
|
+
activeGeneration ||
|
|
1568
|
+
activePromotionId !== undefined) {
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
if (pendingOptions.has(baseVoiceAssetId)) {
|
|
1572
|
+
setStatus(elements, "Promote the selected base voice before regenerating its lines.", "error");
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
if (!promotedVoiceId(manifest, baseVoiceAssetId)) {
|
|
1576
|
+
setStatus(elements, "Promote a base voice before regenerating its lines.", "error");
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
if (missingTargetLineAssetIds.length > 0) {
|
|
1580
|
+
setStatus(elements, `Create target variants for ${missingTargetLineAssetIds.map(readableAssetName).join(", ")} before regenerating this target voice.`, "error");
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
if (lineAssetIds.length === 0) {
|
|
1584
|
+
setStatus(elements, "This voice has no linked lines to regenerate.", "error");
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
const pendingLineAssetIds = lineAssetIds.filter((assetId) => pendingOptions.has(assetId));
|
|
1588
|
+
if (pendingLineAssetIds.length > 0) {
|
|
1589
|
+
setStatus(elements, `Promote or revert pending options for ${pendingLineAssetIds.map(readableAssetName).join(", ")} before regenerating all lines.`, "error");
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
if (pendingOptions.size > 0) {
|
|
1593
|
+
setStatus(elements, "Promote or revert all pending asset options before regenerating voice lines.", "error");
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
const targetLabel = selectedTargetId
|
|
1597
|
+
? manifest.targets?.[selectedTargetId]?.label ?? readableAssetName(selectedTargetId)
|
|
1598
|
+
: "Default";
|
|
1599
|
+
const confirmed = window.confirm(`Regenerate and promote one new option for all ${lineAssetIds.length} linked line${lineAssetIds.length === 1 ? "" : "s"} of ${readableAssetName(baseVoiceAssetId)} (${targetLabel})?\n\nThis uses paid generation and replaces each line's active version as it completes.`);
|
|
1600
|
+
if (!confirmed)
|
|
1601
|
+
return;
|
|
1602
|
+
const currentPromotionId = promotionId + 1;
|
|
1603
|
+
const controller = new AbortController();
|
|
1604
|
+
promotionId = currentPromotionId;
|
|
1605
|
+
activePromotionId = currentPromotionId;
|
|
1606
|
+
activeVoiceLineRegeneration = { controller, id: currentPromotionId };
|
|
1607
|
+
elements.panel.setAttribute("aria-busy", "true");
|
|
1608
|
+
setVoiceLineBatchControlsLocked(true);
|
|
1609
|
+
syncPromoteAllButton();
|
|
1610
|
+
syncMixTilesetButton();
|
|
1611
|
+
let promotedCount = 0;
|
|
1612
|
+
let liveRefreshError;
|
|
1613
|
+
let batchCompletedCleanly = false;
|
|
1614
|
+
try {
|
|
1615
|
+
const result = await regenerateAndPromoteVoiceLines({
|
|
1616
|
+
manifest,
|
|
1617
|
+
voiceAssetId,
|
|
1618
|
+
targetId: selectedTargetId,
|
|
1619
|
+
client,
|
|
1620
|
+
signal: controller.signal,
|
|
1621
|
+
onProgress(progress) {
|
|
1622
|
+
if (activeVoiceLineRegeneration?.id !== currentPromotionId)
|
|
1623
|
+
return;
|
|
1624
|
+
const action = controller.signal.aborted
|
|
1625
|
+
? "Finishing"
|
|
1626
|
+
: progress.phase === "generation" ? "Generating" : "Promoting";
|
|
1627
|
+
setStatus(elements, `${action} ${readableAssetName(progress.assetId)} (${progress.index + 1}/${progress.total})...`, "busy");
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
manifest = result.manifest;
|
|
1631
|
+
promotedCount = result.promoted.length;
|
|
1632
|
+
if (destroyed || activeVoiceLineRegeneration?.id !== currentPromotionId) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
if (result.manifestModuleSyncDeferred || result.manifestModuleSyncError) {
|
|
1636
|
+
deferredVoiceLineManifestSync = true;
|
|
1637
|
+
}
|
|
1638
|
+
if (result.manifestModuleSyncDeferred) {
|
|
1639
|
+
for (const failure of result.failures) {
|
|
1640
|
+
if (failure.option)
|
|
1641
|
+
deferredVoiceLinePendingAssetIds.add(failure.assetId);
|
|
1642
|
+
}
|
|
1643
|
+
if (result.cancelled?.option) {
|
|
1644
|
+
deferredVoiceLinePendingAssetIds.add(result.cancelled.assetId);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
for (const saved of result.promoted) {
|
|
1648
|
+
pendingOptions.delete(saved.asset.id);
|
|
1649
|
+
resolveDeferredVoiceLinePendingOption(saved.asset.id);
|
|
1650
|
+
formatDrafts.delete(saved.asset.id);
|
|
1651
|
+
try {
|
|
1652
|
+
(options.onAssetReady ?? options.onPreview)(saved.asset.id, resolveAssetUrl(saved.file), saved.asset);
|
|
1653
|
+
}
|
|
1654
|
+
catch (error) {
|
|
1655
|
+
liveRefreshError ??= { assetId: saved.asset.id, error };
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
for (const failure of result.failures) {
|
|
1659
|
+
if (failure.option) {
|
|
1660
|
+
rememberPendingOption(failure.assetId, failure.option);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
if (result.cancelled?.option) {
|
|
1664
|
+
rememberPendingOption(result.cancelled.assetId, result.cancelled.option);
|
|
1665
|
+
}
|
|
1666
|
+
batchCompletedCleanly =
|
|
1667
|
+
promotedCount > 0 &&
|
|
1668
|
+
result.failures.length === 0 &&
|
|
1669
|
+
!result.cancelled &&
|
|
1670
|
+
!result.manifestModuleSyncError &&
|
|
1671
|
+
!result.manifestModuleSyncDeferred &&
|
|
1672
|
+
pendingOptions.size === 0;
|
|
1673
|
+
if (promotedCount > 0) {
|
|
1674
|
+
try {
|
|
1675
|
+
options.onManifestUpdated?.(manifest);
|
|
1676
|
+
}
|
|
1677
|
+
catch (error) {
|
|
1678
|
+
liveRefreshError ??= { assetId: baseVoiceAssetId, error };
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
syncTargetAsset(baseVoiceAssetId);
|
|
1682
|
+
renderAssetBrowser();
|
|
1683
|
+
const firstFailure = result.failures[0];
|
|
1684
|
+
const processedCount = result.promoted.length + result.failures.length;
|
|
1685
|
+
if (result.cancelled) {
|
|
1686
|
+
setStatus(elements, `Stopped after ${processedCount}/${lineAssetIds.length} lines; ${promotedCount} promoted` +
|
|
1687
|
+
`${result.failures.length > 0 ? ` and ${result.failures.length} failed` : ""}.`, result.failures.length > 0 ? "error" : "success");
|
|
1688
|
+
}
|
|
1689
|
+
else if (firstFailure) {
|
|
1690
|
+
const unattemptedCount = lineAssetIds.length - processedCount;
|
|
1691
|
+
setStatus(elements, `Regenerated and promoted ${promotedCount}/${lineAssetIds.length} lines. ` +
|
|
1692
|
+
`${result.failures.length} failed; first failure was ${readableAssetName(firstFailure.assetId)} ` +
|
|
1693
|
+
`during ${firstFailure.phase}: ${errorMessage(firstFailure.error)}` +
|
|
1694
|
+
`${firstFailure.phase === "promotion" && unattemptedCount > 0
|
|
1695
|
+
? ` ${unattemptedCount} remaining line${unattemptedCount === 1 ? " was" : "s were"} not generated to avoid further paid requests while promotion is unavailable.`
|
|
1696
|
+
: ""}` +
|
|
1697
|
+
`${result.manifestModuleSyncDeferred
|
|
1698
|
+
? " The generated option remains selected; promote or revert it before restarting."
|
|
1699
|
+
: ""}`, "error");
|
|
1700
|
+
}
|
|
1701
|
+
else if (result.manifestModuleSyncError) {
|
|
1702
|
+
setStatus(elements, `Regenerated and promoted all ${promotedCount} lines, but the generated manifest module could not be refreshed. ${errorMessage(result.manifestModuleSyncError)}`, "error");
|
|
1703
|
+
}
|
|
1704
|
+
else if (liveRefreshError) {
|
|
1705
|
+
setStatus(elements, `Regenerated all ${promotedCount} lines, but ${readableAssetName(liveRefreshError.assetId)} ` +
|
|
1706
|
+
`could not refresh live. Restart to load it. ${errorMessage(liveRefreshError.error)}`, "error");
|
|
1707
|
+
}
|
|
1708
|
+
else {
|
|
1709
|
+
setStatus(elements, `Regenerated and promoted all ${promotedCount} lines.`, "success");
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
catch (error) {
|
|
1713
|
+
setStatus(elements, `Could not regenerate voice lines. ${errorMessage(error)}`, "error");
|
|
1714
|
+
}
|
|
1715
|
+
finally {
|
|
1716
|
+
if (activeVoiceLineRegeneration?.id === currentPromotionId) {
|
|
1717
|
+
activeVoiceLineRegeneration = undefined;
|
|
1718
|
+
}
|
|
1719
|
+
if (activePromotionId === currentPromotionId) {
|
|
1720
|
+
activePromotionId = undefined;
|
|
1721
|
+
}
|
|
1722
|
+
if (!destroyed) {
|
|
1723
|
+
elements.panel.removeAttribute("aria-busy");
|
|
1724
|
+
setVoiceLineBatchControlsLocked(false);
|
|
1725
|
+
syncPromoteAllButton();
|
|
1726
|
+
syncMixTilesetButton();
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
if (!destroyed && options.restartOnPromote && batchCompletedCleanly) {
|
|
1730
|
+
window.location.reload();
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1420
1733
|
elements.uploadButton.addEventListener("click", async () => {
|
|
1421
1734
|
const asset = manifest.assets[selectedTargetAssetId];
|
|
1422
1735
|
try {
|
|
@@ -1865,6 +2178,7 @@ export function installAiAssetDesigner(options) {
|
|
|
1865
2178
|
liveRefreshErrors.push({ assetId, error });
|
|
1866
2179
|
}
|
|
1867
2180
|
pendingOptions.delete(assetId);
|
|
2181
|
+
resolveDeferredVoiceLinePendingOption(assetId);
|
|
1868
2182
|
formatDrafts.delete(assetId);
|
|
1869
2183
|
promotedCount += 1;
|
|
1870
2184
|
}
|
|
@@ -1904,8 +2218,19 @@ export function installAiAssetDesigner(options) {
|
|
|
1904
2218
|
window.location.reload();
|
|
1905
2219
|
}
|
|
1906
2220
|
});
|
|
1907
|
-
elements.restartButton.addEventListener("click", () => {
|
|
1908
|
-
|
|
2221
|
+
elements.restartButton.addEventListener("click", async () => {
|
|
2222
|
+
if (deferredVoiceLineManifestSync &&
|
|
2223
|
+
[...deferredVoiceLinePendingAssetIds].some((assetId) => pendingOptions.has(assetId))) {
|
|
2224
|
+
setStatus(elements, "Promote or revert the pending generated voice line before restarting.", "error");
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
if (deferredVoiceLineManifestSync) {
|
|
2228
|
+
setStatus(elements, "Refreshing the generated manifest module...", "busy");
|
|
2229
|
+
if (!await flushDeferredVoiceLineManifest())
|
|
2230
|
+
return;
|
|
2231
|
+
}
|
|
2232
|
+
if (!destroyed)
|
|
2233
|
+
window.location.reload();
|
|
1909
2234
|
});
|
|
1910
2235
|
elements.versionsButton.addEventListener("click", () => {
|
|
1911
2236
|
const asset = manifest.assets[selectedTargetAssetId];
|
|
@@ -2419,22 +2744,38 @@ export function installAiAssetDesigner(options) {
|
|
|
2419
2744
|
continueOnError: true,
|
|
2420
2745
|
onManifestUpdated: (updatedManifest) => {
|
|
2421
2746
|
manifest = updatedManifest;
|
|
2422
|
-
|
|
2423
|
-
|
|
2747
|
+
if (!destroyed) {
|
|
2748
|
+
options.onManifestUpdated?.(updatedManifest);
|
|
2749
|
+
syncAsset(selectedAssetId);
|
|
2750
|
+
}
|
|
2424
2751
|
},
|
|
2425
2752
|
onAssetReady: (assetId, textureKey, asset) => {
|
|
2426
|
-
(
|
|
2753
|
+
if (!destroyed) {
|
|
2754
|
+
(options.onAssetReady ?? options.onPreview)(assetId, textureKey, asset);
|
|
2755
|
+
}
|
|
2756
|
+
},
|
|
2757
|
+
onProgress: (progress) => {
|
|
2758
|
+
if (!destroyed)
|
|
2759
|
+
handleFirstDraftProgress(progress);
|
|
2427
2760
|
},
|
|
2428
|
-
onProgress: handleFirstDraftProgress,
|
|
2429
2761
|
onError: (error, assetId) => {
|
|
2430
|
-
|
|
2762
|
+
if (!destroyed) {
|
|
2763
|
+
setStatus(elements, `First draft failed for ${readableAssetName(assetId)}: ${errorMessage(error)}`, "error");
|
|
2764
|
+
}
|
|
2431
2765
|
}
|
|
2432
2766
|
}).then((result) => {
|
|
2433
|
-
if (result.generatedAssetIds.length > 0 && result.errors.length === 0) {
|
|
2767
|
+
if (!destroyed && result.generatedAssetIds.length > 0 && result.errors.length === 0) {
|
|
2434
2768
|
setStatus(elements, "First drafts generated.", "success");
|
|
2435
2769
|
}
|
|
2436
2770
|
}).catch((error) => {
|
|
2437
|
-
|
|
2771
|
+
if (!destroyed) {
|
|
2772
|
+
setStatus(elements, `First draft failed: ${errorMessage(error)}`, "error");
|
|
2773
|
+
}
|
|
2774
|
+
}).finally(() => {
|
|
2775
|
+
activeFirstDrafts = false;
|
|
2776
|
+
if (!destroyed) {
|
|
2777
|
+
syncPromoteAllButton();
|
|
2778
|
+
}
|
|
2438
2779
|
});
|
|
2439
2780
|
}
|
|
2440
2781
|
return {
|
|
@@ -2442,8 +2783,11 @@ export function installAiAssetDesigner(options) {
|
|
|
2442
2783
|
open: () => setOpen(true),
|
|
2443
2784
|
close: () => setOpen(false),
|
|
2444
2785
|
destroy: () => {
|
|
2786
|
+
destroyed = true;
|
|
2445
2787
|
activeGeneration?.controller.abort();
|
|
2788
|
+
activeVoiceLineRegeneration?.controller.abort();
|
|
2446
2789
|
stopStatusAnimation(elements.status);
|
|
2790
|
+
unbindInputBoundary();
|
|
2447
2791
|
dockPanel.destroy();
|
|
2448
2792
|
elements.root.remove();
|
|
2449
2793
|
}
|