@ai-game-assets/phaser 0.6.0 → 0.7.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/dist/animations.d.ts +2 -0
- package/dist/animations.d.ts.map +1 -1
- package/dist/animations.js +4 -3
- package/dist/animations.js.map +1 -1
- package/dist/debug-client.d.ts +52 -2
- package/dist/debug-client.d.ts.map +1 -1
- package/dist/debug-client.js +66 -0
- package/dist/debug-client.js.map +1 -1
- package/dist/designer-support.d.ts +28 -3
- package/dist/designer-support.d.ts.map +1 -1
- package/dist/designer-support.js +552 -29
- package/dist/designer-support.js.map +1 -1
- package/dist/designer.d.ts +19 -2
- package/dist/designer.d.ts.map +1 -1
- package/dist/designer.js +1546 -134
- package/dist/designer.js.map +1 -1
- package/dist/first-drafts.js +21 -9
- package/dist/first-drafts.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +2 -0
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +6 -0
- package/dist/keys.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +50 -3
- package/dist/loader.js.map +1 -1
- package/dist/phaser-types.d.ts +1 -0
- package/dist/phaser-types.d.ts.map +1 -1
- package/dist/runtime.d.ts +30 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +235 -8
- package/dist/runtime.js.map +1 -1
- package/dist/tileset-dialog.d.ts +121 -0
- package/dist/tileset-dialog.d.ts.map +1 -0
- package/dist/tileset-dialog.js +1594 -0
- package/dist/tileset-dialog.js.map +1 -0
- package/dist/tilesets.d.ts +13 -0
- package/dist/tilesets.d.ts.map +1 -0
- package/dist/tilesets.js +60 -0
- package/dist/tilesets.js.map +1 -0
- package/package.json +3 -2
package/dist/designer-support.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { aiTextureKey } from "./keys.js";
|
|
1
2
|
const assetFormatOptions = [
|
|
2
3
|
{ value: "png", label: "PNG" },
|
|
3
4
|
{ value: "jpg", label: "JPEG" },
|
|
@@ -69,6 +70,19 @@ export function createDesignerElements(options, manifest, selectedAssetId) {
|
|
|
69
70
|
const promptInput = document.createElement("textarea");
|
|
70
71
|
promptInput.className = "ai-game-assets-designer__prompt";
|
|
71
72
|
promptInput.rows = 6;
|
|
73
|
+
const promptField = labelWrap("Prompt", promptInput);
|
|
74
|
+
const tilesetPromptsField = document.createElement("section");
|
|
75
|
+
tilesetPromptsField.className = "ai-game-assets-designer__tile-prompts";
|
|
76
|
+
tilesetPromptsField.hidden = true;
|
|
77
|
+
const tilesetPromptsTitle = document.createElement("div");
|
|
78
|
+
tilesetPromptsTitle.className = "ai-game-assets-designer__tile-prompts-title";
|
|
79
|
+
tilesetPromptsTitle.textContent = "Tile prompts";
|
|
80
|
+
const tilesetPromptsHint = document.createElement("div");
|
|
81
|
+
tilesetPromptsHint.className = "ai-game-assets-designer__tile-prompts-hint";
|
|
82
|
+
tilesetPromptsHint.textContent = "Describe each tile in exact sheet order.";
|
|
83
|
+
const tilesetPromptsList = document.createElement("div");
|
|
84
|
+
tilesetPromptsList.className = "ai-game-assets-designer__tile-prompts-list";
|
|
85
|
+
tilesetPromptsField.append(tilesetPromptsTitle, tilesetPromptsHint, tilesetPromptsList);
|
|
72
86
|
const widthInput = numericInput();
|
|
73
87
|
const heightInput = numericInput();
|
|
74
88
|
const frameCountInput = numericInput();
|
|
@@ -98,7 +112,9 @@ export function createDesignerElements(options, manifest, selectedAssetId) {
|
|
|
98
112
|
}
|
|
99
113
|
const dimensionGrid = document.createElement("div");
|
|
100
114
|
dimensionGrid.className = "ai-game-assets-designer__dimensions";
|
|
101
|
-
|
|
115
|
+
const widthField = labelWrap("Width", widthInput);
|
|
116
|
+
const heightField = labelWrap("Height", heightInput);
|
|
117
|
+
dimensionGrid.append(widthField, heightField);
|
|
102
118
|
const frameCountField = labelWrap("Frames", frameCountInput);
|
|
103
119
|
const formatField = labelWrap("Format", formatSelect);
|
|
104
120
|
const audioFormatField = labelWrap("Audio format", audioFormatSelect);
|
|
@@ -135,6 +151,10 @@ export function createDesignerElements(options, manifest, selectedAssetId) {
|
|
|
135
151
|
const regenerateButton = document.createElement("button");
|
|
136
152
|
regenerateButton.type = "button";
|
|
137
153
|
regenerateButton.textContent = "Regenerate";
|
|
154
|
+
const mixTilesetButton = document.createElement("button");
|
|
155
|
+
mixTilesetButton.type = "button";
|
|
156
|
+
mixTilesetButton.textContent = "Mix tileset";
|
|
157
|
+
mixTilesetButton.hidden = true;
|
|
138
158
|
const uploadButton = document.createElement("button");
|
|
139
159
|
uploadButton.type = "button";
|
|
140
160
|
uploadButton.textContent = "Upload...";
|
|
@@ -148,19 +168,23 @@ export function createDesignerElements(options, manifest, selectedAssetId) {
|
|
|
148
168
|
promoteButton.type = "button";
|
|
149
169
|
promoteButton.textContent = "Promote";
|
|
150
170
|
promoteButton.disabled = true;
|
|
171
|
+
const promoteAllButton = document.createElement("button");
|
|
172
|
+
promoteAllButton.type = "button";
|
|
173
|
+
promoteAllButton.textContent = "Promote all";
|
|
174
|
+
promoteAllButton.disabled = true;
|
|
151
175
|
const restartButton = document.createElement("button");
|
|
152
176
|
restartButton.type = "button";
|
|
153
177
|
restartButton.textContent = "Restart";
|
|
154
178
|
const actions = document.createElement("div");
|
|
155
179
|
actions.className = "ai-game-assets-designer__actions";
|
|
156
|
-
actions.append(regenerateButton, uploadButton, deriveButton, versionsButton, promoteButton, restartButton);
|
|
180
|
+
actions.append(regenerateButton, mixTilesetButton, uploadButton, deriveButton, versionsButton, promoteButton, promoteAllButton, restartButton);
|
|
157
181
|
const versionLabel = document.createElement("div");
|
|
158
182
|
versionLabel.className = "ai-game-assets-designer__meta";
|
|
159
183
|
const optionsGrid = document.createElement("div");
|
|
160
184
|
optionsGrid.className = "ai-game-assets-designer__options";
|
|
161
185
|
const status = document.createElement("div");
|
|
162
186
|
status.className = "ai-game-assets-designer__status";
|
|
163
|
-
panel.append(header, targetField, labelWrap("Asset", assetBrowser), targetVariantLabel, animationField, dimensionGrid, frameCountField, formatField, audioFormatField, audioDurationField, audioLoopField, voiceTextField, labelWrap("Current", currentPreview),
|
|
187
|
+
panel.append(header, targetField, labelWrap("Asset", assetBrowser), targetVariantLabel, animationField, dimensionGrid, frameCountField, formatField, audioFormatField, audioDurationField, audioLoopField, voiceTextField, labelWrap("Current", currentPreview), promptField, tilesetPromptsField, actions, versionLabel, optionsGrid, status);
|
|
164
188
|
root.append(toggle, panel);
|
|
165
189
|
return {
|
|
166
190
|
root,
|
|
@@ -176,7 +200,9 @@ export function createDesignerElements(options, manifest, selectedAssetId) {
|
|
|
176
200
|
animationSelect,
|
|
177
201
|
animationField,
|
|
178
202
|
widthInput,
|
|
203
|
+
widthField,
|
|
179
204
|
heightInput,
|
|
205
|
+
heightField,
|
|
180
206
|
dimensionGrid,
|
|
181
207
|
frameCountInput,
|
|
182
208
|
frameCountField,
|
|
@@ -191,6 +217,9 @@ export function createDesignerElements(options, manifest, selectedAssetId) {
|
|
|
191
217
|
voiceTextInput,
|
|
192
218
|
voiceTextField,
|
|
193
219
|
promptInput,
|
|
220
|
+
promptField,
|
|
221
|
+
tilesetPromptsField,
|
|
222
|
+
tilesetPromptsList,
|
|
194
223
|
currentImage,
|
|
195
224
|
currentAudio,
|
|
196
225
|
currentAnimation,
|
|
@@ -201,8 +230,10 @@ export function createDesignerElements(options, manifest, selectedAssetId) {
|
|
|
201
230
|
uploadButton,
|
|
202
231
|
deriveButton,
|
|
203
232
|
regenerateButton,
|
|
233
|
+
mixTilesetButton,
|
|
204
234
|
versionsButton,
|
|
205
235
|
promoteButton,
|
|
236
|
+
promoteAllButton,
|
|
206
237
|
restartButton,
|
|
207
238
|
versionLabel,
|
|
208
239
|
options: optionsGrid,
|
|
@@ -406,6 +437,8 @@ export function renderOptions(options) {
|
|
|
406
437
|
options.elements.options.append(card);
|
|
407
438
|
}
|
|
408
439
|
}
|
|
440
|
+
const pendingImagePreviews = new WeakMap();
|
|
441
|
+
let promotedTextureInstallId = 0;
|
|
409
442
|
export function previewOption(options) {
|
|
410
443
|
const textureKey = `ai-preview:${options.assetId}:${options.option.index}:${Date.now()}`;
|
|
411
444
|
previewImageSource({
|
|
@@ -425,25 +458,80 @@ export function previewCurrentAsset(options) {
|
|
|
425
458
|
textureKey
|
|
426
459
|
});
|
|
427
460
|
}
|
|
461
|
+
export function installPromotedImageTexture(options) {
|
|
462
|
+
const textureKey = aiTextureKey({ assetId: options.assetId });
|
|
463
|
+
promotedTextureInstallId += 1;
|
|
464
|
+
const requestKey = `promote:${options.assetId}:${promotedTextureInstallId}`;
|
|
465
|
+
return new Promise((resolve, reject) => {
|
|
466
|
+
previewImageSource({
|
|
467
|
+
...options,
|
|
468
|
+
textureKey,
|
|
469
|
+
requestKey,
|
|
470
|
+
onPreview(assetId, installedTextureKey, asset) {
|
|
471
|
+
resolve({ assetId, textureKey: installedTextureKey, asset });
|
|
472
|
+
},
|
|
473
|
+
onError: reject
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
}
|
|
428
477
|
export function previewImageSource(options) {
|
|
429
478
|
const image = new Image();
|
|
479
|
+
const pendingForScene = pendingImagePreviews.get(options.scene) ?? new Map();
|
|
480
|
+
const requestKey = options.requestKey ?? options.assetId;
|
|
481
|
+
const previousImage = pendingForScene.get(requestKey);
|
|
482
|
+
if (previousImage) {
|
|
483
|
+
previousImage.onload = null;
|
|
484
|
+
previousImage.onerror = null;
|
|
485
|
+
}
|
|
486
|
+
pendingForScene.set(requestKey, image);
|
|
487
|
+
pendingImagePreviews.set(options.scene, pendingForScene);
|
|
488
|
+
if (shouldUseAnonymousCrossOrigin(options.src)) {
|
|
489
|
+
image.crossOrigin = "anonymous";
|
|
490
|
+
}
|
|
430
491
|
image.onload = () => {
|
|
431
|
-
if (
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
492
|
+
if (pendingForScene.get(requestKey) !== image)
|
|
493
|
+
return;
|
|
494
|
+
pendingForScene.delete(requestKey);
|
|
495
|
+
try {
|
|
496
|
+
if (options.scene.textures.exists(options.textureKey)) {
|
|
497
|
+
options.scene.textures.remove(options.textureKey);
|
|
498
|
+
}
|
|
499
|
+
const asset = options.assetOverride ?? options.manifest.assets[options.assetId];
|
|
500
|
+
const tileset = designerTilesetMetadata(asset);
|
|
501
|
+
if (tileset && options.scene.textures.addSpriteSheet) {
|
|
502
|
+
options.scene.textures.addSpriteSheet(options.textureKey, image, {
|
|
503
|
+
frameWidth: tileset.tileWidth,
|
|
504
|
+
frameHeight: tileset.tileHeight,
|
|
505
|
+
margin: tileset.margin,
|
|
506
|
+
spacing: tileset.spacing
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
else if (asset.frameGrid && options.scene.textures.addSpriteSheet) {
|
|
510
|
+
options.scene.textures.addSpriteSheet(options.textureKey, image, {
|
|
511
|
+
frameWidth: asset.frameGrid.frameWidth,
|
|
512
|
+
frameHeight: asset.frameGrid.frameHeight,
|
|
513
|
+
margin: asset.frameGrid.margin,
|
|
514
|
+
spacing: asset.frameGrid.spacing
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
options.scene.textures.addImage(options.textureKey, image);
|
|
519
|
+
}
|
|
520
|
+
options.onPreview(options.assetId, options.textureKey, asset);
|
|
442
521
|
}
|
|
443
|
-
|
|
444
|
-
|
|
522
|
+
catch (error) {
|
|
523
|
+
const previewError = error instanceof Error ? error : new Error(String(error));
|
|
524
|
+
if (options.onError)
|
|
525
|
+
options.onError(previewError);
|
|
526
|
+
else
|
|
527
|
+
console.error("Could not preview AI asset image.", previewError);
|
|
445
528
|
}
|
|
446
|
-
|
|
529
|
+
};
|
|
530
|
+
image.onerror = () => {
|
|
531
|
+
if (pendingForScene.get(requestKey) === image) {
|
|
532
|
+
pendingForScene.delete(requestKey);
|
|
533
|
+
}
|
|
534
|
+
options.onError?.(new Error(`Could not load preview image for "${options.assetId}".`));
|
|
447
535
|
};
|
|
448
536
|
image.src = options.src;
|
|
449
537
|
}
|
|
@@ -471,7 +559,9 @@ export async function uploadedOptionFromFile(options) {
|
|
|
471
559
|
};
|
|
472
560
|
}
|
|
473
561
|
const imageSize = await imageSizeFromSource(dataUrl);
|
|
474
|
-
const
|
|
562
|
+
const format = normalizeAssetFormatFromMimeType(mimeType, options.file.name);
|
|
563
|
+
const generationGeometry = generationOverridesFromInputs(options.elements, options.asset, format);
|
|
564
|
+
const geometry = uploadedImageGeometry(options.asset, imageSize, generationGeometry.tileset);
|
|
475
565
|
return {
|
|
476
566
|
index: 0,
|
|
477
567
|
dataUrl,
|
|
@@ -480,13 +570,38 @@ export async function uploadedOptionFromFile(options) {
|
|
|
480
570
|
model: "uploaded",
|
|
481
571
|
dimensions: geometry.dimensions,
|
|
482
572
|
frameGrid: geometry.frameGrid,
|
|
573
|
+
tileset: geometry.tileset,
|
|
483
574
|
settings: {
|
|
484
575
|
...options.asset.settings,
|
|
485
|
-
format
|
|
576
|
+
format
|
|
486
577
|
}
|
|
487
578
|
};
|
|
488
579
|
}
|
|
489
|
-
export function uploadedImageGeometry(asset, imageSize) {
|
|
580
|
+
export function uploadedImageGeometry(asset, imageSize, tilesetOverride) {
|
|
581
|
+
const baseTileset = designerTilesetMetadata(asset);
|
|
582
|
+
if (baseTileset) {
|
|
583
|
+
const tileset = {
|
|
584
|
+
...baseTileset,
|
|
585
|
+
...tilesetOverride
|
|
586
|
+
};
|
|
587
|
+
const capacity = tileset.columns * tileset.rows;
|
|
588
|
+
const tileCount = tileset.tileCount ?? capacity;
|
|
589
|
+
if (tileCount > capacity) {
|
|
590
|
+
throw new Error(`Tile count must not exceed this tileset's ${capacity}-tile capacity.`);
|
|
591
|
+
}
|
|
592
|
+
const margin = tileset.margin ?? 0;
|
|
593
|
+
const spacing = tileset.spacing ?? 0;
|
|
594
|
+
const dimensions = {
|
|
595
|
+
width: margin * 2 + tileset.columns * tileset.tileWidth +
|
|
596
|
+
Math.max(0, tileset.columns - 1) * spacing,
|
|
597
|
+
height: margin * 2 + tileset.rows * tileset.tileHeight +
|
|
598
|
+
Math.max(0, tileset.rows - 1) * spacing
|
|
599
|
+
};
|
|
600
|
+
if (imageSize.width !== dimensions.width || imageSize.height !== dimensions.height) {
|
|
601
|
+
throw new Error(`Tileset upload must be exactly ${dimensions.width}x${dimensions.height}px for the selected tile geometry; received ${imageSize.width}x${imageSize.height}px.`);
|
|
602
|
+
}
|
|
603
|
+
return { dimensions, tileset };
|
|
604
|
+
}
|
|
490
605
|
if (!asset.frameGrid) {
|
|
491
606
|
return {
|
|
492
607
|
dimensions: imageSize
|
|
@@ -588,6 +703,7 @@ const frameTouchUpPreferences = {
|
|
|
588
703
|
color: "#f8fafc",
|
|
589
704
|
tool: "brush"
|
|
590
705
|
};
|
|
706
|
+
let frameTouchUpClipboard;
|
|
591
707
|
export async function openFrameTouchUpEditor(options) {
|
|
592
708
|
const sourceImage = await loadImageElement(options.frameSrc);
|
|
593
709
|
const dialog = document.createElement("div");
|
|
@@ -646,6 +762,7 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
646
762
|
const eraserButton = touchUpButton("Eraser");
|
|
647
763
|
const pickerButton = touchUpButton("Picker");
|
|
648
764
|
const selectButton = touchUpButton("Select");
|
|
765
|
+
selectButton.title = "Select pixels. Cmd/Ctrl+C copies; Cmd/Ctrl+V pastes.";
|
|
649
766
|
const fillButton = touchUpButton("Fill");
|
|
650
767
|
const undoButton = touchUpButton("Undo");
|
|
651
768
|
const redoButton = touchUpButton("Redo");
|
|
@@ -679,6 +796,13 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
679
796
|
const selectionBox = document.createElement("div");
|
|
680
797
|
selectionBox.className = "ai-game-assets-designer__touchup-selection";
|
|
681
798
|
selectionBox.hidden = true;
|
|
799
|
+
const rotateSelectionButton = document.createElement("button");
|
|
800
|
+
rotateSelectionButton.type = "button";
|
|
801
|
+
rotateSelectionButton.className = "ai-game-assets-designer__touchup-selection-rotate";
|
|
802
|
+
rotateSelectionButton.textContent = "↻";
|
|
803
|
+
rotateSelectionButton.setAttribute("aria-label", "Rotate selection 90 degrees clockwise");
|
|
804
|
+
rotateSelectionButton.title = "Rotate selection 90° clockwise";
|
|
805
|
+
selectionBox.append(rotateSelectionButton);
|
|
682
806
|
canvasWrap.append(canvas, selectionBox);
|
|
683
807
|
const side = document.createElement("div");
|
|
684
808
|
side.className = "ai-game-assets-designer__touchup-side";
|
|
@@ -718,6 +842,7 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
718
842
|
let drawingPointerId;
|
|
719
843
|
let selectionStart;
|
|
720
844
|
let selection;
|
|
845
|
+
let floatingSelection;
|
|
721
846
|
let animationTimeout;
|
|
722
847
|
let animationCursor = 0;
|
|
723
848
|
let disposed = false;
|
|
@@ -775,6 +900,63 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
775
900
|
redoButton.disabled = redoStack.length === 0;
|
|
776
901
|
};
|
|
777
902
|
const snapshot = () => context.getImageData(0, 0, canvas.width, canvas.height);
|
|
903
|
+
const clipboardImageData = () => {
|
|
904
|
+
if (!frameTouchUpClipboard)
|
|
905
|
+
return undefined;
|
|
906
|
+
const width = Math.min(frameTouchUpClipboard.width, canvas.width);
|
|
907
|
+
const height = Math.min(frameTouchUpClipboard.height, canvas.height);
|
|
908
|
+
const imageData = context.createImageData(width, height);
|
|
909
|
+
const sourceStride = frameTouchUpClipboard.width * 4;
|
|
910
|
+
const targetStride = width * 4;
|
|
911
|
+
for (let row = 0; row < height; row += 1) {
|
|
912
|
+
imageData.data.set(frameTouchUpClipboard.pixels.subarray(row * sourceStride, (row * sourceStride) + targetStride), row * targetStride);
|
|
913
|
+
}
|
|
914
|
+
return imageData;
|
|
915
|
+
};
|
|
916
|
+
const drawFloatingSelection = () => {
|
|
917
|
+
if (!selection || !floatingSelection)
|
|
918
|
+
return;
|
|
919
|
+
context.putImageData(floatingSelection.background, 0, 0);
|
|
920
|
+
const floatingCanvas = document.createElement("canvas");
|
|
921
|
+
floatingCanvas.width = floatingSelection.pixels.width;
|
|
922
|
+
floatingCanvas.height = floatingSelection.pixels.height;
|
|
923
|
+
const floatingContext = floatingCanvas.getContext("2d");
|
|
924
|
+
if (!floatingContext)
|
|
925
|
+
return;
|
|
926
|
+
floatingContext.putImageData(floatingSelection.pixels, 0, 0);
|
|
927
|
+
context.drawImage(floatingCanvas, selection.x, selection.y);
|
|
928
|
+
};
|
|
929
|
+
const rotateImageDataWithinBounds = (imageData) => {
|
|
930
|
+
const sourceCanvas = document.createElement("canvas");
|
|
931
|
+
sourceCanvas.width = imageData.width;
|
|
932
|
+
sourceCanvas.height = imageData.height;
|
|
933
|
+
const sourceContext = sourceCanvas.getContext("2d");
|
|
934
|
+
const targetCanvas = document.createElement("canvas");
|
|
935
|
+
targetCanvas.width = imageData.width;
|
|
936
|
+
targetCanvas.height = imageData.height;
|
|
937
|
+
const targetContext = targetCanvas.getContext("2d", { willReadFrequently: true });
|
|
938
|
+
if (!sourceContext || !targetContext)
|
|
939
|
+
return imageData;
|
|
940
|
+
sourceContext.putImageData(imageData, 0, 0);
|
|
941
|
+
targetContext.imageSmoothingEnabled = false;
|
|
942
|
+
targetContext.translate(imageData.width / 2, imageData.height / 2);
|
|
943
|
+
targetContext.scale(imageData.width / imageData.height, imageData.height / imageData.width);
|
|
944
|
+
targetContext.rotate(Math.PI / 2);
|
|
945
|
+
targetContext.drawImage(sourceCanvas, -imageData.width / 2, -imageData.height / 2);
|
|
946
|
+
targetContext.setTransform(1, 0, 0, 1, 0, 0);
|
|
947
|
+
return targetContext.getImageData(0, 0, imageData.width, imageData.height);
|
|
948
|
+
};
|
|
949
|
+
const copySelection = () => {
|
|
950
|
+
if (!selection)
|
|
951
|
+
return false;
|
|
952
|
+
const imageData = floatingSelection?.pixels ?? context.getImageData(selection.x, selection.y, selection.width, selection.height);
|
|
953
|
+
frameTouchUpClipboard = {
|
|
954
|
+
width: imageData.width,
|
|
955
|
+
height: imageData.height,
|
|
956
|
+
pixels: new Uint8ClampedArray(imageData.data)
|
|
957
|
+
};
|
|
958
|
+
return true;
|
|
959
|
+
};
|
|
778
960
|
const pushUndo = () => {
|
|
779
961
|
undoStack.push(snapshot());
|
|
780
962
|
if (undoStack.length > 60)
|
|
@@ -785,6 +967,7 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
785
967
|
const restore = (imageData) => {
|
|
786
968
|
context.putImageData(imageData, 0, 0);
|
|
787
969
|
selection = undefined;
|
|
970
|
+
floatingSelection = undefined;
|
|
788
971
|
redrawEditor();
|
|
789
972
|
setDirty(true);
|
|
790
973
|
};
|
|
@@ -814,6 +997,12 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
814
997
|
selectionBox.style.top = `${selection.y * zoom}px`;
|
|
815
998
|
selectionBox.style.width = `${selection.width * zoom}px`;
|
|
816
999
|
selectionBox.style.height = `${selection.height * zoom}px`;
|
|
1000
|
+
const renderedWidth = selection.width * zoom;
|
|
1001
|
+
const renderedHeight = selection.height * zoom;
|
|
1002
|
+
rotateSelectionButton.hidden = renderedWidth < 12 || renderedHeight < 12;
|
|
1003
|
+
const handleSize = Math.max(0, Math.min(18, renderedWidth - 4, renderedHeight - 4));
|
|
1004
|
+
rotateSelectionButton.style.width = `${handleSize}px`;
|
|
1005
|
+
rotateSelectionButton.style.height = `${handleSize}px`;
|
|
817
1006
|
};
|
|
818
1007
|
const refreshPreviews = () => {
|
|
819
1008
|
stillContext.clearRect(0, 0, stillPreview.width, stillPreview.height);
|
|
@@ -997,6 +1186,13 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
997
1186
|
pushUndo();
|
|
998
1187
|
const nextX = clamp(selection.x + deltaX, 0, canvas.width - selection.width);
|
|
999
1188
|
const nextY = clamp(selection.y + deltaY, 0, canvas.height - selection.height);
|
|
1189
|
+
if (floatingSelection) {
|
|
1190
|
+
selection = { ...selection, x: nextX, y: nextY };
|
|
1191
|
+
drawFloatingSelection();
|
|
1192
|
+
setDirty(true);
|
|
1193
|
+
redrawEditor();
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1000
1196
|
const imageData = context.getImageData(selection.x, selection.y, selection.width, selection.height);
|
|
1001
1197
|
context.clearRect(selection.x, selection.y, selection.width, selection.height);
|
|
1002
1198
|
context.putImageData(imageData, nextX, nextY);
|
|
@@ -1004,15 +1200,60 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
1004
1200
|
setDirty(true);
|
|
1005
1201
|
redrawEditor();
|
|
1006
1202
|
};
|
|
1203
|
+
const rotateSelection = () => {
|
|
1204
|
+
if (!selection)
|
|
1205
|
+
return;
|
|
1206
|
+
pushUndo();
|
|
1207
|
+
const pixels = floatingSelection?.pixels ?? context.getImageData(selection.x, selection.y, selection.width, selection.height);
|
|
1208
|
+
const rotated = rotateImageDataWithinBounds(pixels);
|
|
1209
|
+
if (floatingSelection) {
|
|
1210
|
+
floatingSelection.pixels = rotated;
|
|
1211
|
+
drawFloatingSelection();
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
context.putImageData(rotated, selection.x, selection.y);
|
|
1215
|
+
}
|
|
1216
|
+
setDirty(true);
|
|
1217
|
+
redrawEditor();
|
|
1218
|
+
};
|
|
1007
1219
|
const deleteSelection = () => {
|
|
1008
1220
|
if (!selection)
|
|
1009
1221
|
return;
|
|
1010
1222
|
pushUndo();
|
|
1011
|
-
|
|
1223
|
+
if (floatingSelection) {
|
|
1224
|
+
context.putImageData(floatingSelection.background, 0, 0);
|
|
1225
|
+
floatingSelection = undefined;
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
context.clearRect(selection.x, selection.y, selection.width, selection.height);
|
|
1229
|
+
}
|
|
1012
1230
|
selection = undefined;
|
|
1013
1231
|
setDirty(true);
|
|
1014
1232
|
redrawEditor();
|
|
1015
1233
|
};
|
|
1234
|
+
const pasteSelection = () => {
|
|
1235
|
+
const pixels = clipboardImageData();
|
|
1236
|
+
if (!pixels)
|
|
1237
|
+
return false;
|
|
1238
|
+
const x = clamp(selection?.x ?? Math.floor((canvas.width - pixels.width) / 2), 0, canvas.width - pixels.width);
|
|
1239
|
+
const y = clamp(selection?.y ?? Math.floor((canvas.height - pixels.height) / 2), 0, canvas.height - pixels.height);
|
|
1240
|
+
pushUndo();
|
|
1241
|
+
floatingSelection = {
|
|
1242
|
+
background: snapshot(),
|
|
1243
|
+
pixels
|
|
1244
|
+
};
|
|
1245
|
+
selection = {
|
|
1246
|
+
x,
|
|
1247
|
+
y,
|
|
1248
|
+
width: pixels.width,
|
|
1249
|
+
height: pixels.height
|
|
1250
|
+
};
|
|
1251
|
+
selectTool("select");
|
|
1252
|
+
drawFloatingSelection();
|
|
1253
|
+
setDirty(true);
|
|
1254
|
+
redrawEditor();
|
|
1255
|
+
return true;
|
|
1256
|
+
};
|
|
1016
1257
|
const drawAnimationFrame = () => {
|
|
1017
1258
|
if (disposed)
|
|
1018
1259
|
return;
|
|
@@ -1054,6 +1295,8 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
1054
1295
|
brushMenu.hidden = true;
|
|
1055
1296
|
};
|
|
1056
1297
|
const close = () => {
|
|
1298
|
+
if (disposed)
|
|
1299
|
+
return;
|
|
1057
1300
|
disposed = true;
|
|
1058
1301
|
if (animationTimeout !== undefined)
|
|
1059
1302
|
window.clearTimeout(animationTimeout);
|
|
@@ -1063,6 +1306,7 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
1063
1306
|
window.removeEventListener("blur", cancelDrawing);
|
|
1064
1307
|
document.removeEventListener("pointerdown", closeBrushMenuOnOutsidePointer);
|
|
1065
1308
|
dialog.remove();
|
|
1309
|
+
options.onClose?.();
|
|
1066
1310
|
};
|
|
1067
1311
|
const requestClose = () => {
|
|
1068
1312
|
if (dirty && !window.confirm("You have unsaved changes. Close without saving?"))
|
|
@@ -1074,6 +1318,23 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
1074
1318
|
return;
|
|
1075
1319
|
const key = event.key.toLowerCase();
|
|
1076
1320
|
const commandModifier = event.metaKey || event.ctrlKey;
|
|
1321
|
+
const target = event.target;
|
|
1322
|
+
const isEditingInput = target instanceof HTMLInputElement ||
|
|
1323
|
+
target instanceof HTMLTextAreaElement ||
|
|
1324
|
+
target instanceof HTMLSelectElement ||
|
|
1325
|
+
(target instanceof HTMLElement && target.isContentEditable);
|
|
1326
|
+
if (isEditingInput && event.key !== "Escape")
|
|
1327
|
+
return;
|
|
1328
|
+
if (commandModifier && !event.altKey && key === "c" && copySelection()) {
|
|
1329
|
+
event.preventDefault();
|
|
1330
|
+
event.stopPropagation();
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
if (commandModifier && !event.altKey && key === "v" && pasteSelection()) {
|
|
1334
|
+
event.preventDefault();
|
|
1335
|
+
event.stopPropagation();
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1077
1338
|
if (commandModifier && !event.altKey && (key === "z" || key === "y")) {
|
|
1078
1339
|
event.preventDefault();
|
|
1079
1340
|
event.stopPropagation();
|
|
@@ -1141,6 +1402,7 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
1141
1402
|
pickColorAt(point);
|
|
1142
1403
|
return;
|
|
1143
1404
|
}
|
|
1405
|
+
floatingSelection = undefined;
|
|
1144
1406
|
pushUndo();
|
|
1145
1407
|
drawing = true;
|
|
1146
1408
|
drawingPointerId = event.pointerId;
|
|
@@ -1246,6 +1508,15 @@ export async function openFrameTouchUpEditor(options) {
|
|
|
1246
1508
|
clientY: event.clientY
|
|
1247
1509
|
});
|
|
1248
1510
|
}, { passive: false });
|
|
1511
|
+
rotateSelectionButton.addEventListener("pointerdown", (event) => {
|
|
1512
|
+
event.preventDefault();
|
|
1513
|
+
event.stopPropagation();
|
|
1514
|
+
});
|
|
1515
|
+
rotateSelectionButton.addEventListener("click", (event) => {
|
|
1516
|
+
event.preventDefault();
|
|
1517
|
+
event.stopPropagation();
|
|
1518
|
+
rotateSelection();
|
|
1519
|
+
});
|
|
1249
1520
|
closeButton.addEventListener("click", requestClose);
|
|
1250
1521
|
zoomOutButton.addEventListener("click", () => {
|
|
1251
1522
|
setZoom(zoom - 1);
|
|
@@ -1747,9 +2018,12 @@ export async function openStyleGuideEditor(options) {
|
|
|
1747
2018
|
const promoteButton = document.createElement("button");
|
|
1748
2019
|
promoteButton.type = "button";
|
|
1749
2020
|
promoteButton.textContent = "Promote style";
|
|
2021
|
+
const regenerateAllButton = document.createElement("button");
|
|
2022
|
+
regenerateAllButton.type = "button";
|
|
2023
|
+
regenerateAllButton.textContent = "Regenerate all...";
|
|
1750
2024
|
const actions = document.createElement("div");
|
|
1751
2025
|
actions.className = "ai-game-assets-designer__modal-actions";
|
|
1752
|
-
actions.append(cancelButton, promoteButton, confirmButton);
|
|
2026
|
+
actions.append(cancelButton, promoteButton, regenerateAllButton, confirmButton);
|
|
1753
2027
|
const draft = () => ({
|
|
1754
2028
|
prompt: promptInput.value.trim(),
|
|
1755
2029
|
images: images.map((image) => ({ ...image }))
|
|
@@ -1775,6 +2049,12 @@ export async function openStyleGuideEditor(options) {
|
|
|
1775
2049
|
promoteButton.disabled = false;
|
|
1776
2050
|
}
|
|
1777
2051
|
});
|
|
2052
|
+
regenerateAllButton.addEventListener("click", () => {
|
|
2053
|
+
const current = draft();
|
|
2054
|
+
options.onConfirm(current);
|
|
2055
|
+
close();
|
|
2056
|
+
void options.onRegenerateAll(current);
|
|
2057
|
+
});
|
|
1778
2058
|
card.append(title, promptField, dropZone, uploadButton, fileInput, imageList, actions);
|
|
1779
2059
|
dialog.append(card);
|
|
1780
2060
|
options.root.append(dialog);
|
|
@@ -2492,10 +2772,21 @@ export function ensureFramePreviewElement(element) {
|
|
|
2492
2772
|
return frameElement;
|
|
2493
2773
|
}
|
|
2494
2774
|
export function generationOverridesFromInputs(elements, asset, format) {
|
|
2775
|
+
const tileset = designerTilesetMetadata(asset);
|
|
2495
2776
|
const dimensions = {
|
|
2496
|
-
width: positiveIntegerInput(elements.widthInput, asset.frameGrid?.frameWidth ?? asset.dimensions?.width ?? 1),
|
|
2497
|
-
height: positiveIntegerInput(elements.heightInput, asset.frameGrid?.frameHeight ?? asset.dimensions?.height ?? 1)
|
|
2777
|
+
width: positiveIntegerInput(elements.widthInput, asset.frameGrid?.frameWidth ?? tileset?.tileWidth ?? asset.dimensions?.width ?? 1),
|
|
2778
|
+
height: positiveIntegerInput(elements.heightInput, asset.frameGrid?.frameHeight ?? tileset?.tileHeight ?? asset.dimensions?.height ?? 1)
|
|
2498
2779
|
};
|
|
2780
|
+
if (tileset) {
|
|
2781
|
+
return {
|
|
2782
|
+
tileset: {
|
|
2783
|
+
tileWidth: dimensions.width,
|
|
2784
|
+
tileHeight: dimensions.height,
|
|
2785
|
+
tileCount: positiveIntegerInput(elements.frameCountInput, tileset.tileCount ?? tileset.columns * tileset.rows)
|
|
2786
|
+
},
|
|
2787
|
+
settings: { format }
|
|
2788
|
+
};
|
|
2789
|
+
}
|
|
2499
2790
|
if (!asset.frameGrid) {
|
|
2500
2791
|
return {
|
|
2501
2792
|
dimensions,
|
|
@@ -2564,6 +2855,9 @@ export function isAudioAsset(asset) {
|
|
|
2564
2855
|
asset?.kind === "voice" ||
|
|
2565
2856
|
asset?.kind === "voice-line";
|
|
2566
2857
|
}
|
|
2858
|
+
function designerTilesetMetadata(asset) {
|
|
2859
|
+
return asset?.tileset;
|
|
2860
|
+
}
|
|
2567
2861
|
export function isVoiceAsset(asset) {
|
|
2568
2862
|
return asset?.kind === "voice" || asset?.kind === "voice-line";
|
|
2569
2863
|
}
|
|
@@ -2572,6 +2866,7 @@ export function assetWithGeneratedGeometry(asset, option, options = {}) {
|
|
|
2572
2866
|
...asset,
|
|
2573
2867
|
dimensions: option.dimensions ?? asset.dimensions,
|
|
2574
2868
|
frameGrid: option.frameGrid ?? asset.frameGrid,
|
|
2869
|
+
tileset: option.tileset ?? asset.tileset,
|
|
2575
2870
|
animations: option.animations ??
|
|
2576
2871
|
(options.inheritAnimations
|
|
2577
2872
|
? asset.animations
|
|
@@ -2593,7 +2888,11 @@ export function positiveIntegerInput(input, fallback) {
|
|
|
2593
2888
|
const value = Number(input.value);
|
|
2594
2889
|
if (!Number.isFinite(value))
|
|
2595
2890
|
return fallback;
|
|
2596
|
-
|
|
2891
|
+
const normalized = Math.max(1, Math.floor(value));
|
|
2892
|
+
const maximum = Number(input.max);
|
|
2893
|
+
return input.max && Number.isFinite(maximum)
|
|
2894
|
+
? Math.min(normalized, Math.max(1, Math.floor(maximum)))
|
|
2895
|
+
: normalized;
|
|
2597
2896
|
}
|
|
2598
2897
|
export function positiveNumberInput(input, fallback) {
|
|
2599
2898
|
const value = Number(input.value);
|
|
@@ -2724,7 +3023,7 @@ export async function imageSourceToDataUrl(src) {
|
|
|
2724
3023
|
return src;
|
|
2725
3024
|
const response = await fetch(src);
|
|
2726
3025
|
if (!response.ok) {
|
|
2727
|
-
throw new Error(`Could not load current
|
|
3026
|
+
throw new Error(`Could not load current asset image (${response.status}).`);
|
|
2728
3027
|
}
|
|
2729
3028
|
const responseBlob = await response.blob();
|
|
2730
3029
|
const blob = responseBlob.type === "application/octet-stream"
|
|
@@ -2737,11 +3036,11 @@ export async function imageSourceToDataUrl(src) {
|
|
|
2737
3036
|
resolve(reader.result);
|
|
2738
3037
|
}
|
|
2739
3038
|
else {
|
|
2740
|
-
reject(new Error("Could not convert current
|
|
3039
|
+
reject(new Error("Could not convert current asset image to a data URL."));
|
|
2741
3040
|
}
|
|
2742
3041
|
});
|
|
2743
3042
|
reader.addEventListener("error", () => {
|
|
2744
|
-
reject(reader.error ?? new Error("Could not read current
|
|
3043
|
+
reject(reader.error ?? new Error("Could not read current asset image."));
|
|
2745
3044
|
});
|
|
2746
3045
|
reader.readAsDataURL(blob);
|
|
2747
3046
|
});
|
|
@@ -2947,6 +3246,36 @@ export function ensureDesignerStyles() {
|
|
|
2947
3246
|
font: inherit;
|
|
2948
3247
|
}
|
|
2949
3248
|
.ai-game-assets-designer__field textarea { resize: vertical; }
|
|
3249
|
+
.ai-game-assets-designer__tile-prompts {
|
|
3250
|
+
display: grid;
|
|
3251
|
+
gap: 7px;
|
|
3252
|
+
margin-bottom: 12px;
|
|
3253
|
+
padding: 10px;
|
|
3254
|
+
border: 1px solid #303949;
|
|
3255
|
+
border-radius: 8px;
|
|
3256
|
+
background: rgba(11, 15, 22, 0.58);
|
|
3257
|
+
}
|
|
3258
|
+
.ai-game-assets-designer__tile-prompts[hidden] { display: none; }
|
|
3259
|
+
.ai-game-assets-designer__tile-prompts-title {
|
|
3260
|
+
color: #e5edf9;
|
|
3261
|
+
font-size: 13px;
|
|
3262
|
+
font-weight: 700;
|
|
3263
|
+
}
|
|
3264
|
+
.ai-game-assets-designer__tile-prompts-hint {
|
|
3265
|
+
color: #8f9cb5;
|
|
3266
|
+
font-size: 11px;
|
|
3267
|
+
line-height: 1.35;
|
|
3268
|
+
}
|
|
3269
|
+
.ai-game-assets-designer__tile-prompts-list {
|
|
3270
|
+
display: grid;
|
|
3271
|
+
gap: 8px;
|
|
3272
|
+
}
|
|
3273
|
+
.ai-game-assets-designer__tile-prompts-list .ai-game-assets-designer__field {
|
|
3274
|
+
margin-bottom: 0;
|
|
3275
|
+
}
|
|
3276
|
+
.ai-game-assets-designer__tile-prompts-list textarea {
|
|
3277
|
+
min-height: 62px;
|
|
3278
|
+
}
|
|
2950
3279
|
.ai-game-assets-designer__target-variant {
|
|
2951
3280
|
margin: -4px 0 12px;
|
|
2952
3281
|
padding: 7px 9px;
|
|
@@ -3171,6 +3500,17 @@ export function ensureDesignerStyles() {
|
|
|
3171
3500
|
gap: 8px;
|
|
3172
3501
|
margin-top: 10px;
|
|
3173
3502
|
}
|
|
3503
|
+
.ai-game-assets-designer__tileset-tile-prompt {
|
|
3504
|
+
margin-top: 10px;
|
|
3505
|
+
padding: 9px 10px;
|
|
3506
|
+
border: 1px solid #354058;
|
|
3507
|
+
border-radius: 6px;
|
|
3508
|
+
background: rgba(37, 49, 73, 0.48);
|
|
3509
|
+
color: #d8e4f7;
|
|
3510
|
+
font-size: 12px;
|
|
3511
|
+
line-height: 1.45;
|
|
3512
|
+
white-space: pre-wrap;
|
|
3513
|
+
}
|
|
3174
3514
|
.ai-game-assets-designer__options.is-audio {
|
|
3175
3515
|
grid-template-columns: 1fr;
|
|
3176
3516
|
}
|
|
@@ -3245,6 +3585,159 @@ export function ensureDesignerStyles() {
|
|
|
3245
3585
|
box-shadow: 0 22px 70px rgba(0, 0, 0, 0.55);
|
|
3246
3586
|
padding: 14px;
|
|
3247
3587
|
}
|
|
3588
|
+
.ai-game-assets-designer__tileset-mixer-card {
|
|
3589
|
+
width: min(980px, calc(100vw - 36px));
|
|
3590
|
+
}
|
|
3591
|
+
.ai-game-assets-designer__tileset-editor-card {
|
|
3592
|
+
width: min(720px, calc(100vw - 36px));
|
|
3593
|
+
}
|
|
3594
|
+
.ai-game-assets-designer__tileset-editor-stage {
|
|
3595
|
+
border: 1px solid #303a49;
|
|
3596
|
+
border-radius: 8px;
|
|
3597
|
+
}
|
|
3598
|
+
.ai-game-assets-designer__tileset-editor-strip {
|
|
3599
|
+
margin-top: 8px;
|
|
3600
|
+
}
|
|
3601
|
+
.ai-game-assets-designer__tileset-transform-fields {
|
|
3602
|
+
margin-bottom: 6px;
|
|
3603
|
+
}
|
|
3604
|
+
.ai-game-assets-designer__tileset-transform-hint {
|
|
3605
|
+
margin: 0 0 10px;
|
|
3606
|
+
color: #aeb8c8;
|
|
3607
|
+
font-size: 12px;
|
|
3608
|
+
line-height: 1.4;
|
|
3609
|
+
}
|
|
3610
|
+
.ai-game-assets-designer__tileset-transform-thumb {
|
|
3611
|
+
width: 48px;
|
|
3612
|
+
height: 48px;
|
|
3613
|
+
display: grid;
|
|
3614
|
+
place-items: center;
|
|
3615
|
+
}
|
|
3616
|
+
.ai-game-assets-designer__tileset-transform-thumb-viewport {
|
|
3617
|
+
position: relative;
|
|
3618
|
+
display: block;
|
|
3619
|
+
overflow: hidden;
|
|
3620
|
+
border-radius: 3px;
|
|
3621
|
+
pointer-events: none;
|
|
3622
|
+
}
|
|
3623
|
+
@media (max-width: 520px) {
|
|
3624
|
+
.ai-game-assets-designer__tileset-transform-fields {
|
|
3625
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
.ai-game-assets-designer__tileset-regenerate-card {
|
|
3629
|
+
width: min(620px, calc(100vw - 36px));
|
|
3630
|
+
}
|
|
3631
|
+
.ai-game-assets-designer__tileset-regenerate-card .ai-game-assets-designer__tileset-tile-prompt {
|
|
3632
|
+
margin: 0 0 12px;
|
|
3633
|
+
}
|
|
3634
|
+
.ai-game-assets-designer__tileset-mixer-hint {
|
|
3635
|
+
margin: -5px 0 12px;
|
|
3636
|
+
color: #aeb8c8;
|
|
3637
|
+
font-size: 12px;
|
|
3638
|
+
line-height: 1.4;
|
|
3639
|
+
}
|
|
3640
|
+
.ai-game-assets-designer__tileset-mixer-body {
|
|
3641
|
+
display: grid;
|
|
3642
|
+
grid-template-columns: minmax(160px, 220px) minmax(0, 1fr);
|
|
3643
|
+
gap: 14px;
|
|
3644
|
+
min-height: 300px;
|
|
3645
|
+
}
|
|
3646
|
+
.ai-game-assets-designer__tileset-navigator-panel,
|
|
3647
|
+
.ai-game-assets-designer__tileset-preview-panel {
|
|
3648
|
+
min-width: 0;
|
|
3649
|
+
border: 1px solid #303a49;
|
|
3650
|
+
border-radius: 8px;
|
|
3651
|
+
background: #0f1218;
|
|
3652
|
+
padding: 10px;
|
|
3653
|
+
}
|
|
3654
|
+
.ai-game-assets-designer__tileset-section-title {
|
|
3655
|
+
min-height: 20px;
|
|
3656
|
+
margin-bottom: 8px;
|
|
3657
|
+
color: #dbeafe;
|
|
3658
|
+
font-size: 12px;
|
|
3659
|
+
font-weight: 700;
|
|
3660
|
+
}
|
|
3661
|
+
.ai-game-assets-designer__tileset-navigator {
|
|
3662
|
+
display: grid;
|
|
3663
|
+
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
|
|
3664
|
+
gap: 7px;
|
|
3665
|
+
max-height: min(58vh, 540px);
|
|
3666
|
+
overflow: auto;
|
|
3667
|
+
padding: 2px;
|
|
3668
|
+
}
|
|
3669
|
+
.ai-game-assets-designer__tileset-tile {
|
|
3670
|
+
position: relative;
|
|
3671
|
+
overflow: hidden;
|
|
3672
|
+
border: 2px solid #384251;
|
|
3673
|
+
border-radius: 6px;
|
|
3674
|
+
background: #111827;
|
|
3675
|
+
cursor: pointer;
|
|
3676
|
+
}
|
|
3677
|
+
.ai-game-assets-designer__tileset-tile::after {
|
|
3678
|
+
content: attr(data-source);
|
|
3679
|
+
position: absolute;
|
|
3680
|
+
right: 2px;
|
|
3681
|
+
bottom: 2px;
|
|
3682
|
+
z-index: 3;
|
|
3683
|
+
border-radius: 999px;
|
|
3684
|
+
background: rgba(8, 12, 20, 0.88);
|
|
3685
|
+
color: #9ca8ba;
|
|
3686
|
+
padding: 1px 4px;
|
|
3687
|
+
font-size: 9px;
|
|
3688
|
+
line-height: 1.4;
|
|
3689
|
+
}
|
|
3690
|
+
.ai-game-assets-designer__tileset-tile.is-selected {
|
|
3691
|
+
border-color: #6ed3ff;
|
|
3692
|
+
box-shadow: 0 0 0 1px rgba(110, 211, 255, 0.35);
|
|
3693
|
+
}
|
|
3694
|
+
.ai-game-assets-designer__tileset-tile.is-mixed::after {
|
|
3695
|
+
background: #155e75;
|
|
3696
|
+
color: #ecfeff;
|
|
3697
|
+
}
|
|
3698
|
+
.ai-game-assets-designer__tileset-choices {
|
|
3699
|
+
display: grid;
|
|
3700
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
3701
|
+
gap: 10px;
|
|
3702
|
+
align-items: start;
|
|
3703
|
+
width: 100%;
|
|
3704
|
+
min-width: 0;
|
|
3705
|
+
}
|
|
3706
|
+
.ai-game-assets-designer__tileset-choice {
|
|
3707
|
+
display: grid;
|
|
3708
|
+
justify-items: center;
|
|
3709
|
+
gap: 7px;
|
|
3710
|
+
min-width: 0;
|
|
3711
|
+
max-width: 100%;
|
|
3712
|
+
overflow: hidden;
|
|
3713
|
+
border: 2px solid #384251;
|
|
3714
|
+
border-radius: 8px;
|
|
3715
|
+
background: #151b25;
|
|
3716
|
+
color: #dbeafe;
|
|
3717
|
+
padding: 9px;
|
|
3718
|
+
font: inherit;
|
|
3719
|
+
font-size: 12px;
|
|
3720
|
+
cursor: pointer;
|
|
3721
|
+
}
|
|
3722
|
+
.ai-game-assets-designer__tileset-choice.is-selected {
|
|
3723
|
+
border-color: #6ed3ff;
|
|
3724
|
+
background: #16283a;
|
|
3725
|
+
}
|
|
3726
|
+
.ai-game-assets-designer__tileset-choice-preview {
|
|
3727
|
+
position: relative;
|
|
3728
|
+
overflow: hidden;
|
|
3729
|
+
max-width: 100%;
|
|
3730
|
+
border-radius: 6px;
|
|
3731
|
+
background: #0a0d12;
|
|
3732
|
+
}
|
|
3733
|
+
@media (max-width: 760px) {
|
|
3734
|
+
.ai-game-assets-designer__tileset-mixer-body {
|
|
3735
|
+
grid-template-columns: 1fr;
|
|
3736
|
+
}
|
|
3737
|
+
.ai-game-assets-designer__tileset-navigator {
|
|
3738
|
+
max-height: 180px;
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3248
3741
|
.ai-game-assets-designer__modal-title {
|
|
3249
3742
|
margin-bottom: 12px;
|
|
3250
3743
|
font-weight: 700;
|
|
@@ -3268,6 +3761,9 @@ export function ensureDesignerStyles() {
|
|
|
3268
3761
|
.ai-game-assets-designer__modal-feedback[data-kind="error"] {
|
|
3269
3762
|
color: #ffb4b4;
|
|
3270
3763
|
}
|
|
3764
|
+
.ai-game-assets-designer__modal-feedback[data-kind="success"] {
|
|
3765
|
+
color: #86efac;
|
|
3766
|
+
}
|
|
3271
3767
|
.ai-game-assets-designer__version-list {
|
|
3272
3768
|
display: grid;
|
|
3273
3769
|
gap: 10px;
|
|
@@ -3381,6 +3877,9 @@ export function ensureDesignerStyles() {
|
|
|
3381
3877
|
.ai-game-assets-designer__frame-thumb.is-selected::after {
|
|
3382
3878
|
border-color: #6ed3ff;
|
|
3383
3879
|
}
|
|
3880
|
+
.ai-game-assets-designer__frame-thumb.is-previewing::after {
|
|
3881
|
+
box-shadow: inset 0 -3px 0 #facc15;
|
|
3882
|
+
}
|
|
3384
3883
|
.ai-game-assets-designer__frame-fields {
|
|
3385
3884
|
display: grid;
|
|
3386
3885
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
@@ -3530,7 +4029,7 @@ export function ensureDesignerStyles() {
|
|
|
3530
4029
|
.ai-game-assets-designer__touchup {
|
|
3531
4030
|
position: fixed;
|
|
3532
4031
|
inset: 0;
|
|
3533
|
-
z-index:
|
|
4032
|
+
z-index: 2147483647;
|
|
3534
4033
|
display: grid;
|
|
3535
4034
|
grid-template-rows: auto auto minmax(0, 1fr);
|
|
3536
4035
|
gap: 10px;
|
|
@@ -3716,6 +4215,30 @@ export function ensureDesignerStyles() {
|
|
|
3716
4215
|
box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.9);
|
|
3717
4216
|
transform: translate(24px, 24px);
|
|
3718
4217
|
}
|
|
4218
|
+
.ai-game-assets-designer__touchup-selection-rotate {
|
|
4219
|
+
position: absolute;
|
|
4220
|
+
top: 2px;
|
|
4221
|
+
right: 2px;
|
|
4222
|
+
z-index: 2;
|
|
4223
|
+
display: grid;
|
|
4224
|
+
place-items: center;
|
|
4225
|
+
min-width: 0;
|
|
4226
|
+
min-height: 0;
|
|
4227
|
+
border: 1px solid #dbeafe;
|
|
4228
|
+
border-radius: 999px;
|
|
4229
|
+
background: rgba(15, 23, 42, 0.94);
|
|
4230
|
+
color: #f8fafc;
|
|
4231
|
+
padding: 0;
|
|
4232
|
+
font: 700 11px/1 ui-sans-serif, system-ui, sans-serif;
|
|
4233
|
+
cursor: pointer;
|
|
4234
|
+
pointer-events: auto;
|
|
4235
|
+
}
|
|
4236
|
+
.ai-game-assets-designer__touchup-selection-rotate:hover,
|
|
4237
|
+
.ai-game-assets-designer__touchup-selection-rotate:focus-visible {
|
|
4238
|
+
border-color: #6ed3ff;
|
|
4239
|
+
color: #6ed3ff;
|
|
4240
|
+
outline: none;
|
|
4241
|
+
}
|
|
3719
4242
|
.ai-game-assets-designer__touchup-side {
|
|
3720
4243
|
min-height: 0;
|
|
3721
4244
|
display: grid;
|