editmamei 0.23.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/NOTICES.md +6 -30
  2. package/README.md +47 -13
  3. package/dist/api/extendscript/_helpers.js +1 -101
  4. package/dist/api/photoshop-api.js +23 -16
  5. package/dist/api/snippet-client.js +38 -2
  6. package/dist/bin/editmamei-core-darwin-arm64 +0 -0
  7. package/dist/bin/editmamei-core-darwin-x64 +0 -0
  8. package/dist/bin/editmamei-core-win-x64.exe +0 -0
  9. package/dist/core/server.js +60 -184
  10. package/dist/core/session.js +9 -10
  11. package/dist/core/tool-registry.js +4 -4
  12. package/dist/delivery/client.js +50 -1
  13. package/dist/delivery/provision.js +18 -1
  14. package/dist/delivery/signing.js +38 -1
  15. package/dist/delivery/store.js +157 -16
  16. package/dist/detection/detect-active-doc.js +42 -11
  17. package/dist/detection/detection-client.js +3 -3
  18. package/dist/detection/face-detector.js +2 -2
  19. package/dist/detection/geometry.js +14 -0
  20. package/dist/detection/landmark-detection-client.js +3 -3
  21. package/dist/detection/mesh-face.js +2 -3
  22. package/dist/detection/object-detector.js +2 -2
  23. package/dist/detection/runtime.js +14 -3
  24. package/dist/detection/sam-segmenter.js +69 -26
  25. package/dist/kernel/kernel.js +5 -9
  26. package/dist/kernel/module-lifecycle.js +187 -0
  27. package/dist/license/entitlement.js +13 -3
  28. package/dist/license/store.js +8 -1
  29. package/dist/modules/ce/index.js +1 -1
  30. package/dist/perception/facets.js +19 -1
  31. package/dist/perception/grounding-anchors.js +2 -12
  32. package/dist/perception/grounding-locate.js +1 -9
  33. package/dist/perception/grounding-review-crop.js +4 -29
  34. package/dist/perception/overlay.js +62 -0
  35. package/dist/perception/pixel-identity.js +41 -0
  36. package/dist/perception/region-precompute.js +96 -20
  37. package/dist/perception/scene-model.js +93 -50
  38. package/dist/perception/select-recipes.js +109 -68
  39. package/dist/perception/sky-ground-flood.js +0 -5
  40. package/dist/perception/sky-mask-transfer.js +73 -14
  41. package/dist/platform/connection.js +60 -43
  42. package/dist/platform/host-platform.js +16 -0
  43. package/dist/platform/macos-detector.js +41 -70
  44. package/dist/platform/macos-runner.js +94 -0
  45. package/dist/platform/script-queue.js +68 -0
  46. package/dist/platform/script-result.js +39 -0
  47. package/dist/platform/windows-detector.js +49 -66
  48. package/dist/platform/windows-runner.js +84 -0
  49. package/dist/skills/editmamei-skill.zip +0 -0
  50. package/dist/tools/adjustment-tools.js +80 -132
  51. package/dist/tools/brush-tools.js +4 -15
  52. package/dist/tools/channel-compose-tools.js +4 -23
  53. package/dist/tools/detection-tools.js +59 -47
  54. package/dist/tools/document-tools.js +92 -127
  55. package/dist/tools/filter-tools.js +490 -731
  56. package/dist/tools/group-tools.js +75 -156
  57. package/dist/tools/guide-tools.js +52 -83
  58. package/dist/tools/history-tools.js +29 -63
  59. package/dist/tools/image-placement-tools.js +18 -37
  60. package/dist/tools/image-tools.js +23 -51
  61. package/dist/tools/layer-ordering-tools.js +2 -9
  62. package/dist/tools/layer-properties-tools.js +207 -395
  63. package/dist/tools/layer-tools.js +224 -190
  64. package/dist/tools/layer-transform-tools.js +41 -106
  65. package/dist/tools/metadata-tools.js +2 -9
  66. package/dist/tools/path-tools.js +4 -15
  67. package/dist/tools/portrait-tools.js +2 -5
  68. package/dist/tools/preview-tools.js +59 -72
  69. package/dist/tools/retouch-tools.js +33 -71
  70. package/dist/tools/scene-tools.js +123 -75
  71. package/dist/tools/selection-tools.js +386 -566
  72. package/dist/tools/shape-tools.js +2 -9
  73. package/dist/tools/text-tools.js +24 -57
  74. package/dist/tools/transform-canvas-tools.js +21 -54
  75. package/dist/tools/vector-mask-tools.js +2 -9
  76. package/dist/update/check.js +1 -1
  77. package/dist/utils/jpeg-quality.js +1 -0
  78. package/dist/utils/logger.js +55 -45
  79. package/dist/utils/operation-timeouts.js +1 -0
  80. package/dist/utils/run-script.js +7 -2
  81. package/dist/utils/session-log.js +134 -12
  82. package/dist/utils/template-storage.js +5 -6
  83. package/dist/utils/tool-helpers.js +43 -0
  84. package/dist/version.js +1 -1
  85. package/package.json +10 -3
  86. package/LICENSE +0 -81
  87. package/dist/platform/detector.js +0 -32
  88. package/dist/platform/macos-executor.js +0 -151
  89. package/dist/platform/windows-executor.js +0 -160
  90. /package/dist/platform/{script-executor.js → ports.js} +0 -0
@@ -1,6 +1,10 @@
1
1
  import { runScript } from '../utils/run-script.js';
2
2
  import { validateArgs } from '../utils/validate.js';
3
3
  import { OPEN_DOCUMENT_TIMEOUT_MS, OPEN_DOCUMENT_REPROBE_TIMEOUT_MS, } from '../utils/operation-timeouts.js';
4
+ import { toolErrorResult, runSnippetTool } from '../utils/tool-helpers.js';
5
+ import { purgeSceneChannels } from '../perception/region-precompute.js';
6
+ import { Logger } from '../utils/logger.js';
7
+ const logger = new Logger('document-tools');
4
8
  const createDocumentSchema = {
5
9
  type: 'object',
6
10
  properties: {
@@ -69,6 +73,11 @@ const savePsdSchema = {
69
73
  description: 'Include flattened composite for compatibility with other apps. Default true.',
70
74
  default: true,
71
75
  },
76
+ keep_scene_channels: {
77
+ type: 'boolean',
78
+ description: "Keep the managed scene:* alpha channels ps_read_scene precomputes. Default false — they are DERIVED masks (rebuilt by the next scene read) and each is a full-resolution channel, so baking them into the .psd bloats it badly (~51MB per channel on a 51MP document). Set true only if you want the saved file to carry the masks. The result reports scene_channels_purged either way, so it's never silent. NOTE: the `scene:` channel-name prefix is RESERVED — the purge matches on the prefix alone, so a hand-made channel named e.g. `scene:mine` is deleted along with the derived ones. Name your own channels anything else.",
79
+ default: false,
80
+ },
72
81
  },
73
82
  required: ['output_path'],
74
83
  };
@@ -196,7 +205,7 @@ export function createDocumentTools(connection, snippetClient) {
196
205
  {
197
206
  tool: {
198
207
  name: 'ps_open_document',
199
- description: 'Open a file from disk into Photoshop with all dialogs suppressed (uses last-used Camera Raw settings for raw/HEIC). Returns document name, dimensions, color mode, and whether the source was a raw format. Use this in the pipeline to load Inbox files for editing.',
208
+ description: 'Open a file from disk into Photoshop with all dialogs suppressed (uses last-used Camera Raw settings for raw/HEIC). Returns document name, dimensions, color mode, and whether the source was a raw format. Use this in the pipeline to load Inbox files for editing. If the file is ALREADY open, its existing document is activated rather than opened a second time — `already_open: true` says so, and any edits made to it are still there (Photoshop would otherwise open a duplicate with a fresh Background, which silently strands prior work).',
200
209
  inputSchema: openDocumentSchema,
201
210
  outputSchema: {
202
211
  type: 'object',
@@ -209,6 +218,7 @@ export function createDocumentTools(connection, snippetClient) {
209
218
  color_mode: { type: 'string' },
210
219
  bits_per_channel: { type: 'number' },
211
220
  is_raw_source: { type: 'boolean' },
221
+ already_open: { type: 'boolean' },
212
222
  file_path: { type: 'string' },
213
223
  context: { type: 'object' },
214
224
  },
@@ -298,50 +308,34 @@ async function exportImage(connection, snippetClient, rawArgs) {
298
308
  }
299
309
  }
300
310
  async function createDocument(connection, snippetClient, rawArgs) {
301
- try {
302
- const args = validateArgs(createDocumentSchema, rawArgs);
303
- const width = args.width;
304
- const height = args.height;
305
- const resolution = args.resolution;
306
- const colorMode = args.color_mode;
307
- const colorModeMap = {
308
- RGB: 'NewDocumentMode.RGB',
309
- CMYK: 'NewDocumentMode.CMYK',
310
- Grayscale: 'NewDocumentMode.GRAYSCALE',
311
- };
312
- const mappedMode = colorModeMap[colorMode];
313
- if (!mappedMode) {
314
- throw new Error(`colorModeMap missing entry for "${colorMode}" — schema enum and ` +
315
- `internal mapping have drifted. Update src/tools/document-tools.ts.`);
316
- }
317
- const script = await snippetClient.build('newDocument', {
318
- width,
319
- height,
320
- resolution,
321
- colorMode: mappedMode,
322
- });
323
- const result = await runScript(connection, script);
324
- return {
325
- content: [
326
- {
327
- type: 'text',
328
- text: `Document created: ${width}x${height}px at ${resolution}dpi (${colorMode})`,
329
- },
330
- ],
331
- structuredContent: result,
332
- };
333
- }
334
- catch (error) {
335
- return {
336
- content: [
337
- {
338
- type: 'text',
339
- text: `Error creating document: ${error instanceof Error ? error.message : String(error)}`,
340
- },
341
- ],
342
- isError: true,
343
- };
344
- }
311
+ return runSnippetTool({
312
+ connection,
313
+ snippetClient,
314
+ rawArgs,
315
+ schema: createDocumentSchema,
316
+ snippet: 'newDocument',
317
+ errorPrefix: 'Error creating document',
318
+ params: (args) => {
319
+ const colorMode = args.color_mode;
320
+ const colorModeMap = {
321
+ RGB: 'NewDocumentMode.RGB',
322
+ CMYK: 'NewDocumentMode.CMYK',
323
+ Grayscale: 'NewDocumentMode.GRAYSCALE',
324
+ };
325
+ const mappedMode = colorModeMap[colorMode];
326
+ if (!mappedMode) {
327
+ throw new Error(`colorModeMap missing entry for "${colorMode}" — schema enum and ` +
328
+ `internal mapping have drifted. Update src/tools/document-tools.ts.`);
329
+ }
330
+ return {
331
+ width: args.width,
332
+ height: args.height,
333
+ resolution: args.resolution,
334
+ colorMode: mappedMode,
335
+ };
336
+ },
337
+ successText: (_result, args) => `Document created: ${args.width}x${args.height}px at ${args.resolution}dpi (${args.color_mode})`,
338
+ });
345
339
  }
346
340
  async function closeDocument(connection, snippetClient, rawArgs) {
347
341
  try {
@@ -365,15 +359,7 @@ async function closeDocument(connection, snippetClient, rawArgs) {
365
359
  };
366
360
  }
367
361
  catch (error) {
368
- return {
369
- content: [
370
- {
371
- type: 'text',
372
- text: `Error closing document: ${error instanceof Error ? error.message : String(error)}`,
373
- },
374
- ],
375
- isError: true,
376
- };
362
+ return toolErrorResult('Error closing document', error);
377
363
  }
378
364
  }
379
365
  function isScriptTimeoutError(message) {
@@ -425,45 +411,44 @@ async function openDocumentPipeline(connection, snippetClient, rawArgs) {
425
411
  };
426
412
  }
427
413
  }
428
- return {
429
- content: [
430
- {
431
- type: 'text',
432
- text: `Error opening document: ${message}`,
433
- },
434
- ],
435
- isError: true,
436
- };
414
+ return toolErrorResult('Error opening document', error);
437
415
  }
438
416
  }
439
417
  async function savePsdPipeline(connection, snippetClient, rawArgs) {
418
+ let args;
440
419
  try {
441
- const args = validateArgs(savePsdSchema, rawArgs);
442
- const outputPath = args.output_path;
443
- const maximizeCompat = args.maximize_compatibility;
444
- const script = await snippetClient.build('savePsdAsCopy', { outputPath, maximizeCompat });
445
- const result = await runScript(connection, script);
446
- return {
447
- content: [
448
- {
449
- type: 'text',
450
- text: `PSD saved:\n${JSON.stringify(result, null, 2)}`,
451
- },
452
- ],
453
- structuredContent: result,
454
- };
420
+ args = validateArgs(savePsdSchema, rawArgs);
455
421
  }
456
422
  catch (error) {
457
- return {
458
- content: [
459
- {
460
- type: 'text',
461
- text: `Error saving PSD: ${error instanceof Error ? error.message : String(error)}`,
462
- },
463
- ],
464
- isError: true,
465
- };
423
+ return toolErrorResult('Error saving PSD', error);
424
+ }
425
+ let purged = 0;
426
+ if (args.keep_scene_channels !== true) {
427
+ try {
428
+ purged = await purgeSceneChannels(connection);
429
+ }
430
+ catch (err) {
431
+ logger.debug(`save_psd: scene-channel purge failed, saving anyway — ` +
432
+ `${err instanceof Error ? err.message : String(err)}`);
433
+ }
434
+ }
435
+ const res = await runSnippetTool({
436
+ connection,
437
+ snippetClient,
438
+ rawArgs,
439
+ schema: savePsdSchema,
440
+ snippet: 'savePsdAsCopy',
441
+ errorPrefix: 'Error saving PSD',
442
+ params: (args) => ({
443
+ outputPath: args.output_path,
444
+ maximizeCompat: args.maximize_compatibility,
445
+ }),
446
+ successText: (result) => `PSD saved:\n${JSON.stringify(result, null, 2)}`,
447
+ });
448
+ if (!res.isError && typeof res.structuredContent === 'object' && res.structuredContent !== null) {
449
+ res.structuredContent.scene_channels_purged = purged;
466
450
  }
451
+ return res;
467
452
  }
468
453
  async function exportJpegPipeline(connection, snippetClient, rawArgs) {
469
454
  try {
@@ -491,48 +476,28 @@ async function exportJpegPipeline(connection, snippetClient, rawArgs) {
491
476
  };
492
477
  }
493
478
  catch (error) {
494
- return {
495
- content: [
496
- {
497
- type: 'text',
498
- text: `Error exporting JPEG: ${error instanceof Error ? error.message : String(error)}`,
499
- },
500
- ],
501
- isError: true,
502
- };
479
+ return toolErrorResult('Error exporting JPEG', error);
503
480
  }
504
481
  }
505
482
  async function exportPngPipeline(connection, snippetClient, rawArgs) {
506
- try {
507
- const args = validateArgs(exportPngSchema, rawArgs);
508
- const outputPath = args.output_path;
509
- const transparentBg = args.transparent_background;
510
- const longEdgePx = args.long_edge_px;
511
- const compression = args.compression;
512
- const params = { outputPath, transparentBg, compression };
513
- if (longEdgePx !== undefined)
514
- params.longEdgePx = longEdgePx;
515
- const script = await snippetClient.build('exportPngPipeline', params);
516
- const result = await runScript(connection, script);
517
- return {
518
- content: [
519
- {
520
- type: 'text',
521
- text: `PNG exported:\n${JSON.stringify(result, null, 2)}`,
522
- },
523
- ],
524
- structuredContent: result,
525
- };
526
- }
527
- catch (error) {
528
- return {
529
- content: [
530
- {
531
- type: 'text',
532
- text: `Error exporting PNG: ${error instanceof Error ? error.message : String(error)}`,
533
- },
534
- ],
535
- isError: true,
536
- };
537
- }
483
+ return runSnippetTool({
484
+ connection,
485
+ snippetClient,
486
+ rawArgs,
487
+ schema: exportPngSchema,
488
+ snippet: 'exportPngPipeline',
489
+ errorPrefix: 'Error exporting PNG',
490
+ params: (args) => {
491
+ const longEdgePx = args.long_edge_px;
492
+ const params = {
493
+ outputPath: args.output_path,
494
+ transparentBg: args.transparent_background,
495
+ compression: args.compression,
496
+ };
497
+ if (longEdgePx !== undefined)
498
+ params.longEdgePx = longEdgePx;
499
+ return params;
500
+ },
501
+ successText: (result) => `PNG exported:\n${JSON.stringify(result, null, 2)}`,
502
+ });
538
503
  }