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.
Files changed (143) hide show
  1. package/README.md +2 -0
  2. package/dist/canvas-public/canvas/actions.js +2 -1
  3. package/dist/canvas-public/canvas/chrome.js +2 -1
  4. package/dist/canvas-public/core/api.js +8 -3
  5. package/dist/canvas-public/core/layout.js +3 -3
  6. package/dist/canvas-public/elements/task-pipeline.js +4 -3
  7. package/dist/canvas-public/features/assets/official-assets.js +4 -3
  8. package/dist/canvas-public/features/editing/canvas-size.js +2 -2
  9. package/dist/canvas-public/features/editing/color-key.js +2 -0
  10. package/dist/canvas-public/features/editing/interactive-image-editing.js +9 -8
  11. package/dist/canvas-public/features/editing/layer-decomposition.js +2 -1
  12. package/dist/canvas-public/features/editing/manual-split.js +2 -1
  13. package/dist/canvas-public/features/editing/perfect-pixel.js +56 -93
  14. package/dist/canvas-public/features/editing/scroll-preview.js +3 -2
  15. package/dist/canvas-public/features/editing/smart-split.js +2 -1
  16. package/dist/canvas-public/features/editing/source-preview.js +2 -2
  17. package/dist/canvas-public/features/frame-editor/frame-editing.js +5 -5
  18. package/dist/canvas-public/features/frame-editor/preview.js +4 -2
  19. package/dist/canvas-public/features/generation/image.js +25 -25
  20. package/dist/canvas-public/features/map/tile-map-editor.js +7 -5
  21. package/dist/canvas-public/language/index.js +14 -2
  22. package/dist/canvas-public/map/export.js +2 -1
  23. package/dist/canvas-public/ui/feature-form.js +2 -1
  24. package/dist/canvas-public/ui/settings.js +2 -1
  25. package/dist/{canvas-public/workers/perfect-pixel-worker.js → image-workers/perfect-pixel/perfect-pixel-core.js} +3 -72
  26. package/dist/image-workers/perfect-pixel/perfect-pixel-worker.cjs +10 -0
  27. package/dist/index.js +13043 -7681
  28. package/dist/skills/en-US/evomeme-cloud-fs/SKILL.md +30 -3
  29. package/dist/skills/en-US/evomeme-cover/SKILL.md +7 -8
  30. package/dist/skills/en-US/evomeme-login/SKILL.md +5 -0
  31. package/dist/skills/en-US/evomeme-mcp/SKILL.md +71 -0
  32. package/dist/skills/en-US/evomeme-mcp/agents/openai.yaml +7 -0
  33. package/dist/skills/en-US/evomeme-mcp/references/troubleshooting.md +55 -0
  34. package/dist/skills/en-US/game-asset-align/SKILL.md +6 -4
  35. package/dist/skills/en-US/game-asset-align/references/blender-scene-pipeline.md +7 -17
  36. package/dist/skills/en-US/game-asset-align/references/canonical-package.md +1 -1
  37. package/dist/skills/en-US/game-asset-align/references/lightweight-review.md +7 -5
  38. package/dist/skills/en-US/game-asset-align/references/repair-policy.md +2 -2
  39. package/dist/skills/en-US/game-asset-align/references/tool-contract.md +47 -17
  40. package/dist/skills/en-US/game-asset-batch-generator/SKILL.md +117 -0
  41. package/dist/skills/en-US/game-asset-batch-generator/agents/openai.yaml +7 -0
  42. package/dist/skills/en-US/game-asset-image-generator/SKILL.md +53 -71
  43. package/dist/skills/en-US/game-asset-image-generator/references/background.md +3 -1
  44. package/dist/skills/en-US/game-asset-image-generator/references/static-game-assets.md +2 -2
  45. package/dist/skills/en-US/game-asset-mesh-generator/SKILL.md +35 -0
  46. package/dist/skills/en-US/game-asset-mesh-generator/agents/openai.yaml +7 -0
  47. package/dist/skills/en-US/game-asset-mesh-generator/references/animation-presets.md +313 -0
  48. package/dist/skills/en-US/game-asset-mesh-generator/references/generation-contracts.md +131 -0
  49. package/dist/skills/en-US/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
  50. package/dist/skills/en-US/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
  51. package/dist/skills/en-US/game-asset-mesh-generator/references/threejs-integration.md +113 -0
  52. package/dist/skills/en-US/game-asset-mesh-generator/references/tool-reference.md +67 -0
  53. package/dist/skills/en-US/game-audio/SKILL.md +55 -16
  54. package/dist/skills/en-US/game-audio/agents/openai.yaml +2 -2
  55. package/dist/skills/en-US/game-audio/references/bgm-cdn.md +7 -11
  56. package/dist/skills/en-US/game-audio/references/sfx-cdn.md +6 -4
  57. package/dist/skills/en-US/game-character-view-generator/SKILL.md +110 -0
  58. package/dist/skills/en-US/game-character-view-generator/agents/openai.yaml +7 -0
  59. package/dist/skills/en-US/game-image-layer-decomposer/SKILL.md +99 -0
  60. package/dist/skills/en-US/game-image-layer-decomposer/agents/openai.yaml +7 -0
  61. package/dist/skills/en-US/game-image-splitter/SKILL.md +114 -0
  62. package/dist/skills/en-US/game-image-splitter/agents/openai.yaml +7 -0
  63. package/dist/skills/en-US/game-perfect-pixel/SKILL.md +123 -0
  64. package/dist/skills/en-US/game-perfect-pixel/agents/openai.yaml +7 -0
  65. package/dist/skills/en-US/game-scroll-generator/SKILL.md +110 -0
  66. package/dist/skills/en-US/game-scroll-generator/agents/openai.yaml +7 -0
  67. package/dist/skills/en-US/game-tile-generator/SKILL.md +152 -0
  68. package/dist/skills/en-US/game-tile-generator/agents/openai.yaml +7 -0
  69. package/dist/skills/en-US/game-ui-generator/SKILL.md +82 -0
  70. package/dist/skills/en-US/game-ui-generator/agents/openai.yaml +7 -0
  71. package/dist/skills/en-US/game-ui-generator/references/component-patterns.md +65 -0
  72. package/dist/skills/en-US/game-ui-generator/references/runtime-integration.md +45 -0
  73. package/dist/skills/en-US/game-video-cycle/SKILL.md +98 -0
  74. package/dist/skills/en-US/game-video-cycle/agents/openai.yaml +7 -0
  75. package/dist/skills/en-US/game-video-generator/SKILL.md +78 -0
  76. package/dist/skills/en-US/game-video-generator/agents/openai.yaml +7 -0
  77. package/dist/skills/en-US/godot-3d-game/SKILL.md +4 -2
  78. package/dist/skills/en-US/godot-3d-game/references/alignment-gates.md +1 -1
  79. package/dist/skills/en-US/godot-3d-game/references/appearance-alignment.md +4 -2
  80. package/dist/skills/en-US/threejs-3d-game/SKILL.md +3 -3
  81. package/dist/skills/en-US/threejs-3d-game/references/engine-asset-pipeline.md +5 -3
  82. package/dist/skills/en-US/threejs-aaa-graphics-builder/SKILL.md +6 -6
  83. package/dist/skills/zh-CN/evomeme-cloud-fs/SKILL.md +31 -4
  84. package/dist/skills/zh-CN/evomeme-cover/SKILL.md +7 -8
  85. package/dist/skills/zh-CN/evomeme-login/SKILL.md +5 -0
  86. package/dist/skills/zh-CN/evomeme-mcp/SKILL.md +71 -0
  87. package/dist/skills/zh-CN/evomeme-mcp/agents/openai.yaml +7 -0
  88. package/dist/skills/zh-CN/evomeme-mcp/references/troubleshooting.md +55 -0
  89. package/dist/skills/zh-CN/game-asset-align/SKILL.md +6 -4
  90. package/dist/skills/zh-CN/game-asset-align/references/blender-scene-pipeline.md +7 -17
  91. package/dist/skills/zh-CN/game-asset-align/references/canonical-package.md +1 -1
  92. package/dist/skills/zh-CN/game-asset-align/references/lightweight-review.md +7 -5
  93. package/dist/skills/zh-CN/game-asset-align/references/repair-policy.md +2 -2
  94. package/dist/skills/zh-CN/game-asset-align/references/tool-contract.md +48 -18
  95. package/dist/skills/zh-CN/game-asset-batch-generator/SKILL.md +117 -0
  96. package/dist/skills/zh-CN/game-asset-batch-generator/agents/openai.yaml +7 -0
  97. package/dist/skills/zh-CN/game-asset-image-generator/SKILL.md +53 -71
  98. package/dist/skills/zh-CN/game-asset-image-generator/references/background.md +3 -1
  99. package/dist/skills/zh-CN/game-asset-image-generator/references/static-game-assets.md +2 -2
  100. package/dist/skills/zh-CN/game-asset-mesh-generator/SKILL.md +35 -0
  101. package/dist/skills/zh-CN/game-asset-mesh-generator/agents/openai.yaml +7 -0
  102. package/dist/skills/zh-CN/game-asset-mesh-generator/references/animation-presets.md +313 -0
  103. package/dist/skills/zh-CN/game-asset-mesh-generator/references/generation-contracts.md +131 -0
  104. package/dist/skills/zh-CN/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
  105. package/dist/skills/zh-CN/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
  106. package/dist/skills/zh-CN/game-asset-mesh-generator/references/threejs-integration.md +113 -0
  107. package/dist/skills/zh-CN/game-asset-mesh-generator/references/tool-reference.md +67 -0
  108. package/dist/skills/zh-CN/game-audio/SKILL.md +55 -16
  109. package/dist/skills/zh-CN/game-audio/agents/openai.yaml +2 -2
  110. package/dist/skills/zh-CN/game-audio/references/bgm-cdn.md +7 -11
  111. package/dist/skills/zh-CN/game-audio/references/sfx-cdn.md +6 -4
  112. package/dist/skills/zh-CN/game-character-view-generator/SKILL.md +110 -0
  113. package/dist/skills/zh-CN/game-character-view-generator/agents/openai.yaml +7 -0
  114. package/dist/skills/zh-CN/game-image-layer-decomposer/SKILL.md +99 -0
  115. package/dist/skills/zh-CN/game-image-layer-decomposer/agents/openai.yaml +7 -0
  116. package/dist/skills/zh-CN/game-image-splitter/SKILL.md +114 -0
  117. package/dist/skills/zh-CN/game-image-splitter/agents/openai.yaml +7 -0
  118. package/dist/skills/zh-CN/game-perfect-pixel/SKILL.md +123 -0
  119. package/dist/skills/zh-CN/game-perfect-pixel/agents/openai.yaml +7 -0
  120. package/dist/skills/zh-CN/game-scroll-generator/SKILL.md +110 -0
  121. package/dist/skills/zh-CN/game-scroll-generator/agents/openai.yaml +7 -0
  122. package/dist/skills/zh-CN/game-tile-generator/SKILL.md +151 -0
  123. package/dist/skills/zh-CN/game-tile-generator/agents/openai.yaml +7 -0
  124. package/dist/skills/zh-CN/game-ui-generator/SKILL.md +82 -0
  125. package/dist/skills/zh-CN/game-ui-generator/agents/openai.yaml +7 -0
  126. package/dist/skills/zh-CN/game-ui-generator/references/component-patterns.md +65 -0
  127. package/dist/skills/zh-CN/game-ui-generator/references/runtime-integration.md +45 -0
  128. package/dist/skills/zh-CN/game-video-cycle/SKILL.md +98 -0
  129. package/dist/skills/zh-CN/game-video-cycle/agents/openai.yaml +7 -0
  130. package/dist/skills/zh-CN/game-video-generator/SKILL.md +78 -0
  131. package/dist/skills/zh-CN/game-video-generator/agents/openai.yaml +7 -0
  132. package/dist/skills/zh-CN/godot-3d-game/SKILL.md +4 -2
  133. package/dist/skills/zh-CN/godot-3d-game/references/alignment-gates.md +1 -1
  134. package/dist/skills/zh-CN/godot-3d-game/references/appearance-alignment.md +4 -2
  135. package/dist/skills/zh-CN/threejs-3d-game/SKILL.md +3 -3
  136. package/dist/skills/zh-CN/threejs-3d-game/references/engine-asset-pipeline.md +5 -3
  137. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/SKILL.md +6 -6
  138. package/dist/stage-public/app.js +1 -1
  139. package/dist/stage-public/delivery.js +2 -2
  140. package/dist/stage-public/review-app.js +7 -7
  141. package/dist/stage-public/review-runtime.js +1 -1
  142. package/dist/stage-public/scene-runtime.js +11 -11
  143. 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(t('map.tileMapEditor.text002'))
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(t('map.tileMapEditor.text003')))
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(t('map.tileMapEditor.text019'))
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(t('map.tileMapEditor.text047'))
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(t('map.tileMapEditor.text051')))),
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 t(message.messageKey)
27
- return String(message?.message || t(fallbackKey))
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(t("map.export.text001"))
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)} ${String(error?.message || error)}`
24
+ settingsLanguageError.textContent = `${errorText(t(messageKey))} ${errorText(error)}`
24
25
  settingsLanguageError.hidden = false
25
26
  }
26
27
 
@@ -1,80 +1,11 @@
1
- let sourceBitmap = null
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
+ });