evomeme 0.1.10-beta.1 → 0.1.10-beta.13
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/README.md +2 -0
- package/dist/canvas-public/canvas/actions.js +2 -1
- package/dist/canvas-public/canvas/chrome.js +2 -1
- package/dist/canvas-public/core/api.js +8 -3
- package/dist/canvas-public/core/layout.js +3 -3
- package/dist/canvas-public/elements/task-pipeline.js +4 -3
- package/dist/canvas-public/features/assets/official-assets.js +4 -3
- package/dist/canvas-public/features/editing/canvas-size.js +2 -2
- package/dist/canvas-public/features/editing/color-key.js +2 -0
- package/dist/canvas-public/features/editing/interactive-image-editing.js +9 -8
- package/dist/canvas-public/features/editing/layer-decomposition.js +2 -1
- package/dist/canvas-public/features/editing/manual-split.js +2 -1
- package/dist/canvas-public/features/editing/perfect-pixel.js +56 -93
- package/dist/canvas-public/features/editing/scroll-preview.js +3 -2
- package/dist/canvas-public/features/editing/smart-split.js +2 -1
- package/dist/canvas-public/features/editing/source-preview.js +2 -2
- package/dist/canvas-public/features/frame-editor/frame-editing.js +5 -5
- package/dist/canvas-public/features/frame-editor/preview.js +4 -2
- package/dist/canvas-public/features/generation/image.js +25 -25
- package/dist/canvas-public/features/map/tile-map-editor.js +7 -5
- package/dist/canvas-public/language/index.js +14 -2
- package/dist/canvas-public/map/export.js +2 -1
- package/dist/canvas-public/ui/feature-form.js +2 -1
- package/dist/canvas-public/ui/settings.js +2 -1
- package/dist/{canvas-public/workers/perfect-pixel-worker.js → image-workers/perfect-pixel/perfect-pixel-core.js} +3 -72
- package/dist/image-workers/perfect-pixel/perfect-pixel-worker.cjs +10 -0
- package/dist/index.js +13043 -7681
- package/dist/skills/en-US/evomeme-cloud-fs/SKILL.md +30 -3
- package/dist/skills/en-US/evomeme-cover/SKILL.md +7 -8
- package/dist/skills/en-US/evomeme-login/SKILL.md +5 -0
- package/dist/skills/en-US/evomeme-mcp/SKILL.md +71 -0
- package/dist/skills/en-US/evomeme-mcp/agents/openai.yaml +7 -0
- package/dist/skills/en-US/evomeme-mcp/references/troubleshooting.md +55 -0
- package/dist/skills/en-US/game-asset-align/SKILL.md +6 -4
- package/dist/skills/en-US/game-asset-align/references/blender-scene-pipeline.md +7 -17
- package/dist/skills/en-US/game-asset-align/references/canonical-package.md +1 -1
- package/dist/skills/en-US/game-asset-align/references/lightweight-review.md +7 -5
- package/dist/skills/en-US/game-asset-align/references/repair-policy.md +2 -2
- package/dist/skills/en-US/game-asset-align/references/tool-contract.md +47 -17
- package/dist/skills/en-US/game-asset-batch-generator/SKILL.md +117 -0
- package/dist/skills/en-US/game-asset-batch-generator/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-asset-image-generator/SKILL.md +53 -71
- package/dist/skills/en-US/game-asset-image-generator/references/background.md +3 -1
- package/dist/skills/en-US/game-asset-image-generator/references/static-game-assets.md +2 -2
- package/dist/skills/en-US/game-asset-mesh-generator/SKILL.md +35 -0
- package/dist/skills/en-US/game-asset-mesh-generator/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-asset-mesh-generator/references/animation-presets.md +313 -0
- package/dist/skills/en-US/game-asset-mesh-generator/references/generation-contracts.md +131 -0
- package/dist/skills/en-US/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
- package/dist/skills/en-US/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
- package/dist/skills/en-US/game-asset-mesh-generator/references/threejs-integration.md +113 -0
- package/dist/skills/en-US/game-asset-mesh-generator/references/tool-reference.md +67 -0
- package/dist/skills/en-US/game-audio/SKILL.md +55 -16
- package/dist/skills/en-US/game-audio/agents/openai.yaml +2 -2
- package/dist/skills/en-US/game-audio/references/bgm-cdn.md +7 -11
- package/dist/skills/en-US/game-audio/references/sfx-cdn.md +6 -4
- package/dist/skills/en-US/game-character-view-generator/SKILL.md +110 -0
- package/dist/skills/en-US/game-character-view-generator/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-image-layer-decomposer/SKILL.md +99 -0
- package/dist/skills/en-US/game-image-layer-decomposer/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-image-splitter/SKILL.md +114 -0
- package/dist/skills/en-US/game-image-splitter/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-perfect-pixel/SKILL.md +123 -0
- package/dist/skills/en-US/game-perfect-pixel/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-scroll-generator/SKILL.md +110 -0
- package/dist/skills/en-US/game-scroll-generator/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-tile-generator/SKILL.md +152 -0
- package/dist/skills/en-US/game-tile-generator/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-ui-generator/SKILL.md +82 -0
- package/dist/skills/en-US/game-ui-generator/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-ui-generator/references/component-patterns.md +65 -0
- package/dist/skills/en-US/game-ui-generator/references/runtime-integration.md +45 -0
- package/dist/skills/en-US/game-video-cycle/SKILL.md +98 -0
- package/dist/skills/en-US/game-video-cycle/agents/openai.yaml +7 -0
- package/dist/skills/en-US/game-video-generator/SKILL.md +78 -0
- package/dist/skills/en-US/game-video-generator/agents/openai.yaml +7 -0
- package/dist/skills/en-US/godot-3d-game/SKILL.md +4 -2
- package/dist/skills/en-US/godot-3d-game/references/alignment-gates.md +1 -1
- package/dist/skills/en-US/godot-3d-game/references/appearance-alignment.md +4 -2
- package/dist/skills/en-US/threejs-3d-game/SKILL.md +3 -3
- package/dist/skills/en-US/threejs-3d-game/references/engine-asset-pipeline.md +5 -3
- package/dist/skills/en-US/threejs-aaa-graphics-builder/SKILL.md +6 -6
- package/dist/skills/zh-CN/evomeme-cloud-fs/SKILL.md +31 -4
- package/dist/skills/zh-CN/evomeme-cover/SKILL.md +7 -8
- package/dist/skills/zh-CN/evomeme-login/SKILL.md +5 -0
- package/dist/skills/zh-CN/evomeme-mcp/SKILL.md +71 -0
- package/dist/skills/zh-CN/evomeme-mcp/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/evomeme-mcp/references/troubleshooting.md +55 -0
- package/dist/skills/zh-CN/game-asset-align/SKILL.md +6 -4
- package/dist/skills/zh-CN/game-asset-align/references/blender-scene-pipeline.md +7 -17
- package/dist/skills/zh-CN/game-asset-align/references/canonical-package.md +1 -1
- package/dist/skills/zh-CN/game-asset-align/references/lightweight-review.md +7 -5
- package/dist/skills/zh-CN/game-asset-align/references/repair-policy.md +2 -2
- package/dist/skills/zh-CN/game-asset-align/references/tool-contract.md +48 -18
- package/dist/skills/zh-CN/game-asset-batch-generator/SKILL.md +117 -0
- package/dist/skills/zh-CN/game-asset-batch-generator/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-asset-image-generator/SKILL.md +53 -71
- package/dist/skills/zh-CN/game-asset-image-generator/references/background.md +3 -1
- package/dist/skills/zh-CN/game-asset-image-generator/references/static-game-assets.md +2 -2
- package/dist/skills/zh-CN/game-asset-mesh-generator/SKILL.md +35 -0
- package/dist/skills/zh-CN/game-asset-mesh-generator/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-asset-mesh-generator/references/animation-presets.md +313 -0
- package/dist/skills/zh-CN/game-asset-mesh-generator/references/generation-contracts.md +131 -0
- package/dist/skills/zh-CN/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
- package/dist/skills/zh-CN/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
- package/dist/skills/zh-CN/game-asset-mesh-generator/references/threejs-integration.md +113 -0
- package/dist/skills/zh-CN/game-asset-mesh-generator/references/tool-reference.md +67 -0
- package/dist/skills/zh-CN/game-audio/SKILL.md +55 -16
- package/dist/skills/zh-CN/game-audio/agents/openai.yaml +2 -2
- package/dist/skills/zh-CN/game-audio/references/bgm-cdn.md +7 -11
- package/dist/skills/zh-CN/game-audio/references/sfx-cdn.md +6 -4
- package/dist/skills/zh-CN/game-character-view-generator/SKILL.md +110 -0
- package/dist/skills/zh-CN/game-character-view-generator/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-image-layer-decomposer/SKILL.md +99 -0
- package/dist/skills/zh-CN/game-image-layer-decomposer/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-image-splitter/SKILL.md +114 -0
- package/dist/skills/zh-CN/game-image-splitter/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-perfect-pixel/SKILL.md +123 -0
- package/dist/skills/zh-CN/game-perfect-pixel/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-scroll-generator/SKILL.md +110 -0
- package/dist/skills/zh-CN/game-scroll-generator/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-tile-generator/SKILL.md +151 -0
- package/dist/skills/zh-CN/game-tile-generator/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-ui-generator/SKILL.md +82 -0
- package/dist/skills/zh-CN/game-ui-generator/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-ui-generator/references/component-patterns.md +65 -0
- package/dist/skills/zh-CN/game-ui-generator/references/runtime-integration.md +45 -0
- package/dist/skills/zh-CN/game-video-cycle/SKILL.md +98 -0
- package/dist/skills/zh-CN/game-video-cycle/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/game-video-generator/SKILL.md +78 -0
- package/dist/skills/zh-CN/game-video-generator/agents/openai.yaml +7 -0
- package/dist/skills/zh-CN/godot-3d-game/SKILL.md +4 -2
- package/dist/skills/zh-CN/godot-3d-game/references/alignment-gates.md +1 -1
- package/dist/skills/zh-CN/godot-3d-game/references/appearance-alignment.md +4 -2
- package/dist/skills/zh-CN/threejs-3d-game/SKILL.md +3 -3
- package/dist/skills/zh-CN/threejs-3d-game/references/engine-asset-pipeline.md +5 -3
- package/dist/skills/zh-CN/threejs-aaa-graphics-builder/SKILL.md +6 -6
- package/dist/stage-public/app.js +1 -1
- package/dist/stage-public/delivery.js +2 -2
- package/dist/stage-public/review-app.js +7 -7
- package/dist/stage-public/review-runtime.js +1 -1
- package/dist/stage-public/scene-runtime.js +11 -11
- package/package.json +4 -2
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { state } from '../../core/state.js'
|
|
2
2
|
import { t } from '../../language/index.js'
|
|
3
|
-
import { generationNode, promptField, modelField, refsField, imageWait } from './fields.js'
|
|
4
|
-
|
|
5
|
-
const sizePresets = (model) => state.meta?.models?.find((entry) => entry.id === model)?.size_presets ||
|
|
6
|
-
(state.meta?.size_presets || ['2048x2048']).filter((size) => model !== 'asset-lite' || size !== '1024x1024')
|
|
7
|
-
const normalizeModelFields = (p) => {
|
|
8
|
-
if (p.model !== 'asset-max' && p.background === 'auto') p.background = 'opaque'
|
|
9
|
-
const supported = state.meta?.models?.find((entry) => entry.id === p.model)?.size_presets
|
|
10
|
-
if (supported && !supported.includes(p.sizePreset) || p.model === 'asset-lite' && p.sizePreset === '1024x1024') {
|
|
11
|
-
p.sizePreset = '2048x2048'
|
|
12
|
-
}
|
|
13
|
-
return p
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default {
|
|
3
|
+
import { generationNode, promptField, modelField, refsField, imageWait } from './fields.js'
|
|
4
|
+
|
|
5
|
+
const sizePresets = (model) => state.meta?.models?.find((entry) => entry.id === model)?.size_presets ||
|
|
6
|
+
(state.meta?.size_presets || ['2048x2048']).filter((size) => model !== 'asset-lite' || size !== '1024x1024')
|
|
7
|
+
const normalizeModelFields = (p) => {
|
|
8
|
+
if (p.model !== 'asset-max' && p.background === 'auto') p.background = 'opaque'
|
|
9
|
+
const supported = state.meta?.models?.find((entry) => entry.id === p.model)?.size_presets
|
|
10
|
+
if (supported && !supported.includes(p.sizePreset) || p.model === 'asset-lite' && p.sizePreset === '1024x1024') {
|
|
11
|
+
p.sizePreset = '2048x2048'
|
|
12
|
+
}
|
|
13
|
+
return p
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
17
|
render: (ui) =>
|
|
18
18
|
ui.stack(
|
|
19
19
|
ui.prompt('prompt', 'refs'),
|
|
@@ -24,9 +24,9 @@ export default {
|
|
|
24
24
|
id: 'image',
|
|
25
25
|
title: 'features.generation.generationDialog.text009',
|
|
26
26
|
node: generationNode('gen_dialog', 420),
|
|
27
|
-
storage: 'imageGeneration',
|
|
28
|
-
migrate: (saved) => normalizeModelFields(saved),
|
|
29
|
-
changed: (p, field) => { if (field === 'model') normalizeModelFields(p) },
|
|
27
|
+
storage: 'imageGeneration',
|
|
28
|
+
migrate: (saved) => normalizeModelFields(saved),
|
|
29
|
+
changed: (p, field) => { if (field === 'model') normalizeModelFields(p) },
|
|
30
30
|
fields: [
|
|
31
31
|
promptField({ required: true, placeholder: 'features.generation.imageGeneration.text022' }),
|
|
32
32
|
refsField(),
|
|
@@ -34,14 +34,14 @@ export default {
|
|
|
34
34
|
{
|
|
35
35
|
path: 'background',
|
|
36
36
|
type: 'enum',
|
|
37
|
-
default: 'opaque',
|
|
37
|
+
default: 'opaque',
|
|
38
38
|
label: 'inspector.uiInspector.text014',
|
|
39
39
|
visible: (p) => p.model !== 'asset-lite',
|
|
40
|
-
options: (p) =>
|
|
41
|
-
['auto', 'transparent', 'opaque'].flatMap((value, index) => p.model !== 'asset-max' && value === 'auto' ? [] : [{
|
|
42
|
-
value,
|
|
43
|
-
labelKey: `features.generation.generationControls.text00${index + 1}`,
|
|
44
|
-
}]),
|
|
40
|
+
options: (p) =>
|
|
41
|
+
['auto', 'transparent', 'opaque'].flatMap((value, index) => p.model !== 'asset-max' && value === 'auto' ? [] : [{
|
|
42
|
+
value,
|
|
43
|
+
labelKey: `features.generation.generationControls.text00${index + 1}`,
|
|
44
|
+
}]),
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
path: 'sizeMode',
|
|
@@ -56,9 +56,9 @@ export default {
|
|
|
56
56
|
{
|
|
57
57
|
path: 'sizePreset',
|
|
58
58
|
type: 'enum',
|
|
59
|
-
default: () => state.meta?.default_size || '2048x2048',
|
|
59
|
+
default: () => state.meta?.default_size || '2048x2048',
|
|
60
60
|
label: 'inspector.uiInspector.text018',
|
|
61
|
-
options: (p) => sizePresets(p.model),
|
|
61
|
+
options: (p) => sizePresets(p.model),
|
|
62
62
|
visible: (p) => p.sizeMode !== 'custom',
|
|
63
63
|
},
|
|
64
64
|
{
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorT } from '../../language/index.js'
|
|
1
2
|
import { t } from '../../language/index.js'
|
|
2
3
|
import { installFeatureStyles } from '../../ui/controls.js'
|
|
3
4
|
/**
|
|
@@ -97,7 +98,7 @@ async function ensurePixiRuntime() {
|
|
|
97
98
|
if (!globalThis.PIXI?.tilemap?.CompositeTilemap)
|
|
98
99
|
await loadScriptOnce('/vendor/pixi-tilemap.js')
|
|
99
100
|
if (!globalThis.PIXI?.Application || !globalThis.PIXI?.tilemap?.CompositeTilemap) {
|
|
100
|
-
throw new Error(
|
|
101
|
+
throw new Error(errorT('map.tileMapEditor.text002'))
|
|
101
102
|
}
|
|
102
103
|
})().catch((error) => {
|
|
103
104
|
pixiRuntimePromise = null
|
|
@@ -174,9 +175,10 @@ function drawTransparencyGrid(context, width, height) {
|
|
|
174
175
|
function loadHtmlImage(url) {
|
|
175
176
|
return new Promise((resolve, reject) => {
|
|
176
177
|
const image = new Image()
|
|
178
|
+
image.crossOrigin = 'anonymous'
|
|
177
179
|
image.decoding = 'async'
|
|
178
180
|
image.onload = () => resolve(image)
|
|
179
|
-
image.onerror = () => reject(new Error(
|
|
181
|
+
image.onerror = () => reject(new Error(errorT('map.tileMapEditor.text003')))
|
|
180
182
|
image.src = url
|
|
181
183
|
})
|
|
182
184
|
}
|
|
@@ -408,7 +410,7 @@ class TileMapView {
|
|
|
408
410
|
}
|
|
409
411
|
try {
|
|
410
412
|
const reference = JSON.parse(raw)
|
|
411
|
-
if (!reference?.id) throw new Error(
|
|
413
|
+
if (!reference?.id) throw new Error(errorT('map.tileMapEditor.text019'))
|
|
412
414
|
await this.openSliceDialog(reference)
|
|
413
415
|
} catch (error) {
|
|
414
416
|
this.reportError(error)
|
|
@@ -1814,7 +1816,7 @@ class TileMapView {
|
|
|
1814
1816
|
tile_size: this.map.cellSize,
|
|
1815
1817
|
})
|
|
1816
1818
|
if (!result?.tileset?.id || !Array.isArray(result.tiles) || !result.tiles.length) {
|
|
1817
|
-
throw new Error(
|
|
1819
|
+
throw new Error(errorT('map.tileMapEditor.text047'))
|
|
1818
1820
|
}
|
|
1819
1821
|
const previousSourceIds = new Set(existing?.tileSourceIds || [])
|
|
1820
1822
|
const nextSources = result.tiles
|
|
@@ -1951,7 +1953,7 @@ class TileMapView {
|
|
|
1951
1953
|
}
|
|
1952
1954
|
const blob = await new Promise((resolve, reject) => {
|
|
1953
1955
|
canvas.toBlob(
|
|
1954
|
-
(value) => (value ? resolve(value) : reject(new Error(
|
|
1956
|
+
(value) => (value ? resolve(value) : reject(new Error(errorT('map.tileMapEditor.text051')))),
|
|
1955
1957
|
'image/png',
|
|
1956
1958
|
)
|
|
1957
1959
|
})
|
|
@@ -23,8 +23,20 @@ export function t(key) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function translateWorkerError(message, fallbackKey = 'common.browserProcessingFailed') {
|
|
26
|
-
if (message?.messageKey) return
|
|
27
|
-
return
|
|
26
|
+
if (message?.messageKey) return errorT(message.messageKey)
|
|
27
|
+
return errorText(message?.message || errorT(fallbackKey))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Errors remain English while the rest of the interface follows the user's language. */
|
|
31
|
+
export function errorT(key) { return enUS[key] ?? key }
|
|
32
|
+
|
|
33
|
+
export function errorText(value) {
|
|
34
|
+
let text = String(value?.errorInfo?.message || value?.message || value || '')
|
|
35
|
+
if (!/[\u3400-\u9fff]/.test(text)) return text
|
|
36
|
+
for (const [key, translated] of Object.entries(zhCN).sort((a, b) => b[1].length - a[1].length)) {
|
|
37
|
+
if (translated.length > 1 && enUS[key]) text = text.split(translated).join(enUS[key])
|
|
38
|
+
}
|
|
39
|
+
return /[\u3400-\u9fff]/.test(text) ? 'The operation failed. Check the input and the existing task.' : text
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
export function getLanguage() {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { errorT } from '../language/index.js'
|
|
1
2
|
import { t } from '../language/index.js'
|
|
2
3
|
import { getMountedTileMapView } from './tile-map-editor.js'
|
|
3
4
|
|
|
4
5
|
export async function exportTileMapPng(id) {
|
|
5
6
|
const view = getMountedTileMapView(id)
|
|
6
|
-
if (!view) throw new Error(
|
|
7
|
+
if (!view) throw new Error(errorT("map.export.text001"))
|
|
7
8
|
await view.exportPng()
|
|
8
9
|
if (view.selectedStatus) view.selectedStatus.textContent = t("map.export.text002")
|
|
9
10
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorText } from '../language/index.js'
|
|
1
2
|
import { t } from '../language/index.js'
|
|
2
3
|
import {
|
|
3
4
|
getField,
|
|
@@ -358,7 +359,7 @@ export function mountFeatureForm(host, item, definition, services) {
|
|
|
358
359
|
const params = get()
|
|
359
360
|
const validation = validateFeature(definition, params)
|
|
360
361
|
const message = params.messages?.findLast?.((entry) => entry.error)?.error || validation.message
|
|
361
|
-
error.textContent = message || ''
|
|
362
|
+
error.textContent = errorText(message) || ''
|
|
362
363
|
error.hidden = !message
|
|
363
364
|
submit.hidden = !definition.task && !definition.execute && !definition.setup
|
|
364
365
|
submit.disabled =
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorText } from '../language/index.js'
|
|
1
2
|
import { api } from '../core/api.js'
|
|
2
3
|
import { getLanguage, onLanguageChange, setLanguage, t } from '../language/index.js'
|
|
3
4
|
|
|
@@ -20,7 +21,7 @@ export function initSettingsDialog(elements) {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function showLanguageError(messageKey, error) {
|
|
23
|
-
settingsLanguageError.textContent = `${t(messageKey)} ${
|
|
24
|
+
settingsLanguageError.textContent = `${errorText(t(messageKey))} ${errorText(error)}`
|
|
24
25
|
settingsLanguageError.hidden = false
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -1,80 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
self.addEventListener('message', async (event) => {
|
|
4
|
-
const message = event.data || {}
|
|
5
|
-
try {
|
|
6
|
-
if (message.type === 'init') {
|
|
7
|
-
const response = await fetch(String(message.url || ''))
|
|
8
|
-
if (!response.ok) throw workerError('workers.imageLoadFailed', { status: response.status })
|
|
9
|
-
sourceBitmap?.close?.()
|
|
10
|
-
sourceBitmap = await createImageBitmap(await response.blob())
|
|
11
|
-
self.postMessage({
|
|
12
|
-
type: 'ready',
|
|
13
|
-
requestId: message.requestId,
|
|
14
|
-
width: sourceBitmap.width,
|
|
15
|
-
height: sourceBitmap.height,
|
|
16
|
-
})
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (message.type !== 'process') return
|
|
21
|
-
if (!sourceBitmap) throw workerError('workers.perfectPixel.notInitialized')
|
|
22
|
-
|
|
23
|
-
const settings = normalizeSettings(message.settings)
|
|
24
|
-
const sourceCanvas = new OffscreenCanvas(sourceBitmap.width, sourceBitmap.height)
|
|
25
|
-
const sourceContext = sourceCanvas.getContext('2d', { willReadFrequently: true })
|
|
26
|
-
sourceContext.drawImage(sourceBitmap, 0, 0)
|
|
27
|
-
const source = sourceContext.getImageData(0, 0, sourceBitmap.width, sourceBitmap.height)
|
|
28
|
-
const result = restorePixelGrid(source, settings)
|
|
29
|
-
|
|
30
|
-
const lowResolution = new OffscreenCanvas(result.width, result.height)
|
|
31
|
-
lowResolution.getContext('2d').putImageData(
|
|
32
|
-
new ImageData(result.pixels, result.width, result.height),
|
|
33
|
-
0,
|
|
34
|
-
0,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const outputWidth = result.width * settings.exportScale
|
|
38
|
-
const outputHeight = result.height * settings.exportScale
|
|
39
|
-
const output = new OffscreenCanvas(outputWidth, outputHeight)
|
|
40
|
-
const outputContext = output.getContext('2d')
|
|
41
|
-
outputContext.imageSmoothingEnabled = false
|
|
42
|
-
outputContext.clearRect(0, 0, outputWidth, outputHeight)
|
|
43
|
-
outputContext.drawImage(lowResolution, 0, 0, outputWidth, outputHeight)
|
|
44
|
-
const blob = await output.convertToBlob({ type: 'image/png' })
|
|
45
|
-
|
|
46
|
-
self.postMessage({
|
|
47
|
-
type: 'result',
|
|
48
|
-
requestId: message.requestId,
|
|
49
|
-
blob,
|
|
50
|
-
width: outputWidth,
|
|
51
|
-
height: outputHeight,
|
|
52
|
-
gridWidth: result.width,
|
|
53
|
-
gridHeight: result.height,
|
|
54
|
-
pixelSizeX: result.pixelSizeX,
|
|
55
|
-
pixelSizeY: result.pixelSizeY,
|
|
56
|
-
confidence: result.confidence,
|
|
57
|
-
automatic: settings.autoGrid,
|
|
58
|
-
})
|
|
59
|
-
} catch (error) {
|
|
60
|
-
self.postMessage({
|
|
61
|
-
type: 'error',
|
|
62
|
-
requestId: message.requestId,
|
|
63
|
-
messageKey: error?.messageKey || '',
|
|
64
|
-
messageVariables: error?.messageVariables || null,
|
|
65
|
-
message: error?.messageKey ? '' : String(error?.message || error),
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
function workerError(messageKey, messageVariables = null) {
|
|
1
|
+
export function workerError(messageKey, messageVariables = null) {
|
|
71
2
|
const error = new Error(messageKey)
|
|
72
3
|
error.messageKey = messageKey
|
|
73
4
|
error.messageVariables = messageVariables
|
|
74
5
|
return error
|
|
75
6
|
}
|
|
76
7
|
|
|
77
|
-
function normalizeSettings(value) {
|
|
8
|
+
export function normalizeSettings(value) {
|
|
78
9
|
const settings = value && typeof value === 'object' ? value : {}
|
|
79
10
|
const sampleMethod = ['majority', 'median', 'center'].includes(settings.sampleMethod)
|
|
80
11
|
? settings.sampleMethod
|
|
@@ -95,7 +26,7 @@ function clampInteger(value, min, max, fallback) {
|
|
|
95
26
|
return Number.isFinite(number) ? Math.max(min, Math.min(max, number)) : fallback
|
|
96
27
|
}
|
|
97
28
|
|
|
98
|
-
function restorePixelGrid(image, settings) {
|
|
29
|
+
export function restorePixelGrid(image, settings) {
|
|
99
30
|
const width = image.width
|
|
100
31
|
const height = image.height
|
|
101
32
|
if (!width || !height) throw workerError('workers.perfectPixel.invalidSourceSize')
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Node background thread. The browser never loads this entry or the pixel algorithm.
|
|
2
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
3
|
+
|
|
4
|
+
import('./perfect-pixel-core.js').then(({ normalizeSettings, restorePixelGrid }) => {
|
|
5
|
+
const settings = normalizeSettings(workerData.settings);
|
|
6
|
+
const result = restorePixelGrid(workerData.image, settings);
|
|
7
|
+
parentPort.postMessage({ result, settings }, [result.pixels.buffer]);
|
|
8
|
+
}).catch(error => {
|
|
9
|
+
parentPort.postMessage({ error: { message: error.message, messageKey: error.messageKey } });
|
|
10
|
+
});
|