editmamei 0.19.0 → 0.22.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/NOTICES.md +17 -1
- package/README.md +9 -8
- package/dist/api/extendscript/_helpers.js +1 -1
- package/dist/bin/editmamei-core-darwin-arm64 +0 -0
- package/dist/bin/editmamei-core-darwin-x64 +0 -0
- package/dist/bin/editmamei-core-win-x64.exe +0 -0
- package/dist/cli/config.js +3 -3
- package/dist/cli/help.js +4 -0
- package/dist/cli/install.js +2 -2
- package/dist/cli/report.js +11 -0
- package/dist/cli/router.js +20 -0
- package/dist/core/server.js +8 -8
- package/dist/core/settings.js +2 -2
- package/dist/core/tool-groups.js +92 -82
- package/dist/core/tool-tiers.js +91 -81
- package/dist/detection/landmark-detection-client.js +8 -0
- package/dist/detection/mesh-eye-correction.js +126 -0
- package/dist/detection/sam-segmenter.js +83 -0
- package/dist/diagnostics/collect.js +169 -0
- package/dist/index.js +2 -0
- package/dist/license/entitlement.js +52 -0
- package/dist/modules/ce/index.js +6 -4
- package/dist/perception/grounding-anchors.js +37 -0
- package/dist/perception/grounding-corners.js +93 -0
- package/dist/perception/grounding-edge-trace.js +90 -0
- package/dist/perception/grounding-extrema.js +41 -0
- package/dist/perception/grounding-gate.js +154 -0
- package/dist/perception/grounding-geometry.js +132 -0
- package/dist/perception/grounding-grid.js +34 -0
- package/dist/perception/grounding-landmarks.js +40 -0
- package/dist/perception/grounding-locate.js +341 -0
- package/dist/perception/grounding-resolver.js +202 -0
- package/dist/perception/grounding-review-crop.js +85 -0
- package/dist/perception/grounding-warp.js +182 -0
- package/dist/perception/sky-mask-transfer.js +50 -18
- package/dist/skills/editmamei-skill.zip +0 -0
- package/dist/tools/adjustment-tools.js +3 -3
- package/dist/tools/brush-tools.js +111 -23
- package/dist/tools/channel-compose-tools.js +241 -0
- package/dist/tools/detection-tools.js +1 -1
- package/dist/tools/diagnostics-tools.js +66 -0
- package/dist/tools/document-tools.js +6 -6
- package/dist/tools/filter-tools.js +476 -19
- package/dist/tools/group-tools.js +10 -10
- package/dist/tools/guide-tools.js +4 -4
- package/dist/tools/history-tools.js +4 -4
- package/dist/tools/image-placement-tools.js +1 -1
- package/dist/tools/image-tools.js +62 -19
- package/dist/tools/inspect-tools.js +2 -2
- package/dist/tools/layer-ordering-tools.js +1 -1
- package/dist/tools/layer-properties-tools.js +42 -23
- package/dist/tools/layer-tools.js +10 -10
- package/dist/tools/layer-transform-tools.js +80 -41
- package/dist/tools/overview-tools.js +42 -42
- package/dist/tools/path-tools.js +53 -10
- package/dist/tools/portrait-tools.js +3 -3
- package/dist/tools/preview-tools.js +7 -82
- package/dist/tools/retouch-tools.js +93 -25
- package/dist/tools/scene-tools.js +13 -13
- package/dist/tools/selection-tools.js +319 -40
- package/dist/tools/shape-tools.js +216 -0
- package/dist/tools/text-tools.js +1 -1
- package/dist/tools/transform-canvas-tools.js +2 -2
- package/dist/tools/vector-mask-tools.js +23 -8
- package/dist/utils/log-buffer.js +41 -0
- package/dist/utils/logger.js +12 -1
- package/dist/utils/session-log-reader.js +27 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/preview/coordinate-grid.js +0 -250
- package/dist/tools/object-tools.js +0 -193
- package/dist/tools/text-on-object-tools.js +0 -225
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { runScript } from '../utils/run-script.js';
|
|
2
2
|
import { validateArgs } from '../utils/validate.js';
|
|
3
|
+
import { OnnxLandmarkDetectionClient } from '../detection/landmark-detection-client.js';
|
|
4
|
+
import { resolveGatedPlacement, PLACEMENT_SCHEMA } from '../perception/grounding-locate.js';
|
|
3
5
|
const APPLY_TO_ACTIVE_LAYER_PROP = {
|
|
4
6
|
type: 'boolean',
|
|
5
7
|
description: 'If false (default), the filter is applied to a duplicate of the active layer named "<FilterName> (<Original Name>)" — the original is preserved and the LLM can undo simply by deleting the copy. If true, the filter bakes directly into the active layer (the historical destructive behavior).',
|
|
@@ -388,6 +390,10 @@ const radialBlurSchema = {
|
|
|
388
390
|
maximum: 1,
|
|
389
391
|
default: 0.5,
|
|
390
392
|
},
|
|
393
|
+
center_placement: {
|
|
394
|
+
...PLACEMENT_SCHEMA,
|
|
395
|
+
description: 'Grounded alternative to center_x/center_y: NAME the blur center (a `placement` resolving to a POINT — an object centroid, an extremum, a grid intersection). The resolved document-pixel point is normalized to the 0-1 center for you and WINS over center_x/center_y. The blur runs ONLY if the objective gate PASSES.',
|
|
396
|
+
},
|
|
391
397
|
apply_to_active_layer: APPLY_TO_ACTIVE_LAYER_PROP,
|
|
392
398
|
},
|
|
393
399
|
};
|
|
@@ -396,8 +402,8 @@ const pixelateSchema = {
|
|
|
396
402
|
properties: {
|
|
397
403
|
mode: {
|
|
398
404
|
type: 'string',
|
|
399
|
-
enum: ['color_halftone', 'mosaic'],
|
|
400
|
-
description: 'color_halftone = simulated CMYK halftone dot screen (retro comic / print-dot look); mosaic = blocky square cells (pixelation / censor look).',
|
|
405
|
+
enum: ['color_halftone', 'mosaic', 'crystallize', 'pointillize', 'facet', 'fragment'],
|
|
406
|
+
description: 'color_halftone = simulated CMYK halftone dot screen (retro comic / print-dot look); mosaic = blocky square cells (pixelation / censor look); crystallize = irregular polygonal cells of solid color; pointillize = random dots over the background color (painterly stipple); facet = clumps similar-colored pixels into flat blocks (parameterless); fragment = four offset copies averaged for a shaken/blurry look (parameterless).',
|
|
401
407
|
},
|
|
402
408
|
max_radius: {
|
|
403
409
|
type: 'integer',
|
|
@@ -436,7 +442,7 @@ const pixelateSchema = {
|
|
|
436
442
|
},
|
|
437
443
|
cell_size: {
|
|
438
444
|
type: 'integer',
|
|
439
|
-
description: '
|
|
445
|
+
description: 'Cell size in pixels — used by mosaic (2-300, PS default 10), crystallize (3-300), and pointillize (3-300, PS default 5). Ignored by color_halftone/facet/fragment.',
|
|
440
446
|
minimum: 2,
|
|
441
447
|
maximum: 300,
|
|
442
448
|
default: 10,
|
|
@@ -450,8 +456,8 @@ const distortSchema = {
|
|
|
450
456
|
properties: {
|
|
451
457
|
mode: {
|
|
452
458
|
type: 'string',
|
|
453
|
-
enum: ['twirl', 'ripple', 'polar', 'wave'],
|
|
454
|
-
description: 'twirl = rotational swirl; ripple = fixed-size wavy displacement; polar = rectangular<->polar coordinate warp (wrap a strip into a circle / "tiny planet", or unwrap a panorama); wave = multi-generator sine/triangle/square waves.',
|
|
459
|
+
enum: ['twirl', 'ripple', 'polar', 'wave', 'pinch', 'spherize', 'zigzag'],
|
|
460
|
+
description: 'twirl = rotational swirl; ripple = fixed-size wavy displacement; polar = rectangular<->polar coordinate warp (wrap a strip into a circle / "tiny planet", or unwrap a panorama); wave = multi-generator sine/triangle/square waves; pinch = squeeze/bulge toward or away from center; spherize = wrap onto a sphere (bulge); zigzag = concentric ripples (pond-ripple style).',
|
|
455
461
|
},
|
|
456
462
|
angle: {
|
|
457
463
|
type: 'integer',
|
|
@@ -462,11 +468,18 @@ const distortSchema = {
|
|
|
462
468
|
},
|
|
463
469
|
amount: {
|
|
464
470
|
type: 'integer',
|
|
465
|
-
description: 'ripple
|
|
471
|
+
description: 'Displacement amount. ripple: -999 to 999 (default 100). pinch: -100 to 100 (positive pinches inward, negative bulges; default 50). spherize: -100 to 100 (positive bulges out; default 100). zigzag: -100 to 100 (default 10).',
|
|
466
472
|
minimum: -999,
|
|
467
473
|
maximum: 999,
|
|
468
474
|
default: 100,
|
|
469
475
|
},
|
|
476
|
+
ridges: {
|
|
477
|
+
type: 'integer',
|
|
478
|
+
description: 'zigzag only: number of concentric ridges (0-20). PS default 5.',
|
|
479
|
+
minimum: 0,
|
|
480
|
+
maximum: 20,
|
|
481
|
+
default: 5,
|
|
482
|
+
},
|
|
470
483
|
size: {
|
|
471
484
|
type: 'string',
|
|
472
485
|
enum: ['small', 'medium', 'large'],
|
|
@@ -550,6 +563,197 @@ const distortSchema = {
|
|
|
550
563
|
},
|
|
551
564
|
required: ['mode'],
|
|
552
565
|
};
|
|
566
|
+
const stylizeSchema = {
|
|
567
|
+
type: 'object',
|
|
568
|
+
properties: {
|
|
569
|
+
mode: {
|
|
570
|
+
type: 'string',
|
|
571
|
+
enum: ['emboss', 'find_edges', 'solarize', 'wind', 'trace_contour', 'tiles'],
|
|
572
|
+
description: 'emboss = grey relief with raised edges (angle/height/amount); find_edges = outline edges on a white field (parameterless); solarize = partial tonal inversion (parameterless); wind = fine directional streaks (wind_method + wind_direction); trace_contour = thin contour lines at a brightness level (level + edge); tiles = break the image into offset square tiles (number + offset).',
|
|
573
|
+
},
|
|
574
|
+
angle: {
|
|
575
|
+
type: 'integer',
|
|
576
|
+
description: 'emboss only: relief angle in degrees (-360 to 360). PS default 135.',
|
|
577
|
+
minimum: -360,
|
|
578
|
+
maximum: 360,
|
|
579
|
+
default: 135,
|
|
580
|
+
},
|
|
581
|
+
amount: {
|
|
582
|
+
type: 'integer',
|
|
583
|
+
description: 'emboss only: amount percent (1-500). PS default 100.',
|
|
584
|
+
minimum: 1,
|
|
585
|
+
maximum: 500,
|
|
586
|
+
default: 100,
|
|
587
|
+
},
|
|
588
|
+
height: {
|
|
589
|
+
type: 'integer',
|
|
590
|
+
description: 'emboss only: relief height in pixels (1-100). PS default 3.',
|
|
591
|
+
minimum: 1,
|
|
592
|
+
maximum: 100,
|
|
593
|
+
default: 3,
|
|
594
|
+
},
|
|
595
|
+
wind_method: {
|
|
596
|
+
type: 'string',
|
|
597
|
+
enum: ['wind', 'blast', 'stagger'],
|
|
598
|
+
description: 'wind only: streak intensity/style. PS default wind.',
|
|
599
|
+
default: 'wind',
|
|
600
|
+
},
|
|
601
|
+
wind_direction: {
|
|
602
|
+
type: 'string',
|
|
603
|
+
enum: ['left', 'right'],
|
|
604
|
+
description: 'wind only: streak direction. PS default left.',
|
|
605
|
+
default: 'left',
|
|
606
|
+
},
|
|
607
|
+
level: {
|
|
608
|
+
type: 'integer',
|
|
609
|
+
description: 'trace_contour only: brightness level the contour traces (0-255). PS default 128.',
|
|
610
|
+
minimum: 0,
|
|
611
|
+
maximum: 255,
|
|
612
|
+
default: 128,
|
|
613
|
+
},
|
|
614
|
+
edge: {
|
|
615
|
+
type: 'string',
|
|
616
|
+
enum: ['lower', 'upper'],
|
|
617
|
+
description: 'trace_contour only: trace the lower or upper edge of the level. PS default lower.',
|
|
618
|
+
default: 'lower',
|
|
619
|
+
},
|
|
620
|
+
number: {
|
|
621
|
+
type: 'integer',
|
|
622
|
+
description: 'tiles only: number of tiles across (1-99). PS default 10.',
|
|
623
|
+
minimum: 1,
|
|
624
|
+
maximum: 99,
|
|
625
|
+
default: 10,
|
|
626
|
+
},
|
|
627
|
+
offset: {
|
|
628
|
+
type: 'integer',
|
|
629
|
+
description: 'tiles only: maximum tile offset percent (1-99). PS default 10.',
|
|
630
|
+
minimum: 1,
|
|
631
|
+
maximum: 99,
|
|
632
|
+
default: 10,
|
|
633
|
+
},
|
|
634
|
+
apply_to_active_layer: APPLY_TO_ACTIVE_LAYER_PROP,
|
|
635
|
+
},
|
|
636
|
+
required: ['mode'],
|
|
637
|
+
};
|
|
638
|
+
const renderSchema = {
|
|
639
|
+
type: 'object',
|
|
640
|
+
properties: {
|
|
641
|
+
mode: {
|
|
642
|
+
type: 'string',
|
|
643
|
+
enum: ['clouds', 'difference_clouds', 'fibers'],
|
|
644
|
+
description: 'clouds = soft random cloud texture from the current foreground/background colors (replaces the layer content); difference_clouds = clouds blended via Difference with the existing pixels (run repeatedly for marble/veining); fibers = woven fiber texture from the FG/BG colors. All three read the FG/BG colors — set them first for a specific palette.',
|
|
645
|
+
},
|
|
646
|
+
variance: {
|
|
647
|
+
type: 'integer',
|
|
648
|
+
description: 'fibers only: color variance / streak length (1-64). PS default 16.',
|
|
649
|
+
minimum: 1,
|
|
650
|
+
maximum: 64,
|
|
651
|
+
default: 16,
|
|
652
|
+
},
|
|
653
|
+
fiber_strength: {
|
|
654
|
+
type: 'integer',
|
|
655
|
+
description: 'fibers only: fiber strength / definition (1-64). PS default 4.',
|
|
656
|
+
minimum: 1,
|
|
657
|
+
maximum: 64,
|
|
658
|
+
default: 4,
|
|
659
|
+
},
|
|
660
|
+
seed: {
|
|
661
|
+
type: 'integer',
|
|
662
|
+
description: 'fibers only: randomize seed — change for a different fiber pattern. PS default 12345.',
|
|
663
|
+
minimum: 0,
|
|
664
|
+
default: 12345,
|
|
665
|
+
},
|
|
666
|
+
apply_to_active_layer: APPLY_TO_ACTIVE_LAYER_PROP,
|
|
667
|
+
},
|
|
668
|
+
required: ['mode'],
|
|
669
|
+
};
|
|
670
|
+
const otherSchema = {
|
|
671
|
+
type: 'object',
|
|
672
|
+
properties: {
|
|
673
|
+
mode: {
|
|
674
|
+
type: 'string',
|
|
675
|
+
enum: ['maximum', 'minimum', 'offset'],
|
|
676
|
+
description: 'maximum = spread/grow bright areas (choke masks, radius + preserve); minimum = spread dark areas (radius + preserve); offset = shift the layer by horizontal/vertical pixels and wrap the edges (seamless-tile workflow).',
|
|
677
|
+
},
|
|
678
|
+
radius: {
|
|
679
|
+
type: 'number',
|
|
680
|
+
description: 'maximum/minimum only: radius in pixels (1-500). PS default 3.',
|
|
681
|
+
minimum: 1,
|
|
682
|
+
maximum: 500,
|
|
683
|
+
default: 3,
|
|
684
|
+
},
|
|
685
|
+
preserve: {
|
|
686
|
+
type: 'string',
|
|
687
|
+
enum: ['roundness', 'squareness'],
|
|
688
|
+
description: 'maximum/minimum only: edge-preservation shape. PS default roundness.',
|
|
689
|
+
default: 'roundness',
|
|
690
|
+
},
|
|
691
|
+
horizontal: {
|
|
692
|
+
type: 'integer',
|
|
693
|
+
description: 'offset only: horizontal shift in pixels (positive = right).',
|
|
694
|
+
default: 0,
|
|
695
|
+
},
|
|
696
|
+
vertical: {
|
|
697
|
+
type: 'integer',
|
|
698
|
+
description: 'offset only: vertical shift in pixels (positive = down).',
|
|
699
|
+
default: 0,
|
|
700
|
+
},
|
|
701
|
+
apply_to_active_layer: APPLY_TO_ACTIVE_LAYER_PROP,
|
|
702
|
+
},
|
|
703
|
+
required: ['mode'],
|
|
704
|
+
};
|
|
705
|
+
const denoiseSchema = {
|
|
706
|
+
type: 'object',
|
|
707
|
+
properties: {
|
|
708
|
+
mode: {
|
|
709
|
+
type: 'string',
|
|
710
|
+
enum: ['median', 'dust_and_scratches', 'despeckle'],
|
|
711
|
+
description: 'median = replace each pixel with the median of its neighborhood (removes speckle, radius); dust_and_scratches = remove small defects above a threshold (radius + threshold); despeckle = light edge-preserving blur (parameterless).',
|
|
712
|
+
},
|
|
713
|
+
radius: {
|
|
714
|
+
type: 'number',
|
|
715
|
+
description: 'median/dust_and_scratches only: radius in pixels (1-100). PS default 4 (median) / 3 (dust).',
|
|
716
|
+
minimum: 1,
|
|
717
|
+
maximum: 100,
|
|
718
|
+
default: 4,
|
|
719
|
+
},
|
|
720
|
+
threshold: {
|
|
721
|
+
type: 'integer',
|
|
722
|
+
description: 'dust_and_scratches only: tonal threshold (0-255) below which differences are smoothed. PS default 10.',
|
|
723
|
+
minimum: 0,
|
|
724
|
+
maximum: 255,
|
|
725
|
+
default: 10,
|
|
726
|
+
},
|
|
727
|
+
apply_to_active_layer: APPLY_TO_ACTIVE_LAYER_PROP,
|
|
728
|
+
},
|
|
729
|
+
required: ['mode'],
|
|
730
|
+
};
|
|
731
|
+
const blurAdvSchema = {
|
|
732
|
+
type: 'object',
|
|
733
|
+
properties: {
|
|
734
|
+
mode: {
|
|
735
|
+
type: 'string',
|
|
736
|
+
enum: ['surface_blur', 'box_blur', 'average'],
|
|
737
|
+
description: 'surface_blur = blur while preserving edges above a threshold (skin-smoothing; radius + threshold); box_blur = fast square-kernel blur (radius); average = fill the layer with its single average color (parameterless).',
|
|
738
|
+
},
|
|
739
|
+
radius: {
|
|
740
|
+
type: 'number',
|
|
741
|
+
description: 'surface_blur/box_blur only: radius in pixels (1-500). PS default 15 (surface) / 12 (box).',
|
|
742
|
+
minimum: 1,
|
|
743
|
+
maximum: 500,
|
|
744
|
+
default: 15,
|
|
745
|
+
},
|
|
746
|
+
threshold: {
|
|
747
|
+
type: 'integer',
|
|
748
|
+
description: 'surface_blur only: tonal threshold (0-255) — edges differing by more than this are preserved. PS default 20.',
|
|
749
|
+
minimum: 0,
|
|
750
|
+
maximum: 255,
|
|
751
|
+
default: 20,
|
|
752
|
+
},
|
|
753
|
+
apply_to_active_layer: APPLY_TO_ACTIVE_LAYER_PROP,
|
|
754
|
+
},
|
|
755
|
+
required: ['mode'],
|
|
756
|
+
};
|
|
553
757
|
const oilPaintSchema = {
|
|
554
758
|
type: 'object',
|
|
555
759
|
properties: {
|
|
@@ -663,6 +867,11 @@ const FILTER_TYPES = [
|
|
|
663
867
|
'high_pass',
|
|
664
868
|
'pixelate',
|
|
665
869
|
'distort',
|
|
870
|
+
'stylize',
|
|
871
|
+
'render',
|
|
872
|
+
'other',
|
|
873
|
+
'denoise',
|
|
874
|
+
'blur',
|
|
666
875
|
'displace',
|
|
667
876
|
'oil_paint',
|
|
668
877
|
];
|
|
@@ -678,7 +887,10 @@ const FILTER_INPUT_SCHEMA = {
|
|
|
678
887
|
'sharpen=Unsharp Mask(amount, radius, threshold); smart_sharpen(amount, radius, remove_mode, …); ' +
|
|
679
888
|
'noise=Add Noise(amount, distribution, monochromatic); ' +
|
|
680
889
|
'reduce_noise(strength, preserve_details, color_noise, …); high_pass(radius); ' +
|
|
681
|
-
'pixelate(mode color_halftone|mosaic, …); distort(mode twirl|ripple|polar|wave, …); ' +
|
|
890
|
+
'pixelate(mode color_halftone|mosaic|crystallize|pointillize|facet|fragment, …); distort(mode twirl|ripple|polar|wave|pinch|spherize|zigzag, …); ' +
|
|
891
|
+
'stylize(mode emboss|find_edges|solarize|wind|trace_contour|tiles, …); ' +
|
|
892
|
+
'render(mode clouds|difference_clouds|fibers, …); ' +
|
|
893
|
+
'other(mode maximum|minimum|offset, …); denoise(mode median|dust_and_scratches|despeckle, …); blur(mode surface_blur|box_blur|average, …); ' +
|
|
682
894
|
'displace(map_path, horizontal_scale, vertical_scale, …); oil_paint(stylization, cleanliness, …).',
|
|
683
895
|
},
|
|
684
896
|
...gaussianBlurSchema.properties,
|
|
@@ -692,6 +904,11 @@ const FILTER_INPUT_SCHEMA = {
|
|
|
692
904
|
...highPassSchema.properties,
|
|
693
905
|
...pixelateSchema.properties,
|
|
694
906
|
...distortSchema.properties,
|
|
907
|
+
...stylizeSchema.properties,
|
|
908
|
+
...renderSchema.properties,
|
|
909
|
+
...otherSchema.properties,
|
|
910
|
+
...denoiseSchema.properties,
|
|
911
|
+
...blurAdvSchema.properties,
|
|
695
912
|
...displaceSchema.properties,
|
|
696
913
|
...oilPaintSchema.properties,
|
|
697
914
|
radius: {
|
|
@@ -710,11 +927,11 @@ const FILTER_INPUT_SCHEMA = {
|
|
|
710
927
|
},
|
|
711
928
|
required: ['type'],
|
|
712
929
|
};
|
|
713
|
-
export function createFilterTools(connection, snippetClient) {
|
|
930
|
+
export function createFilterTools(connection, snippetClient, client = new OnnxLandmarkDetectionClient()) {
|
|
714
931
|
return [
|
|
715
932
|
{
|
|
716
933
|
tool: {
|
|
717
|
-
name: '
|
|
934
|
+
name: 'ps_apply_filter',
|
|
718
935
|
description: 'Apply a Photoshop filter to a DUPLICATE of the active layer by default (auto-duplicate-first per Bundle O — the original is preserved, undo by deleting the copy). Pass `apply_to_active_layer: true` to bake into the original. Auto-rasterizes text/smart-object layers. Choose the filter with `type`; each type takes its own parameters (see the `type` field). Covers blur (gaussian_blur/motion_blur/lens_blur/radial_blur), sharpen (`sharpen`=Unsharp Mask, `smart_sharpen`), noise (`noise`=Add Noise, `reduce_noise`), high_pass, pixelate, distort, displace, and oil_paint.',
|
|
719
936
|
inputSchema: FILTER_INPUT_SCHEMA,
|
|
720
937
|
outputSchema: FILTER_OUTPUT_SCHEMA,
|
|
@@ -724,11 +941,11 @@ export function createFilterTools(connection, snippetClient) {
|
|
|
724
941
|
idempotentHint: false,
|
|
725
942
|
},
|
|
726
943
|
},
|
|
727
|
-
handler: async (args) => applyFilter(connection, snippetClient, args),
|
|
944
|
+
handler: async (args) => applyFilter(connection, snippetClient, client, args),
|
|
728
945
|
},
|
|
729
946
|
];
|
|
730
947
|
}
|
|
731
|
-
async function applyFilter(connection, snippetClient, rawArgs) {
|
|
948
|
+
async function applyFilter(connection, snippetClient, detClient, rawArgs) {
|
|
732
949
|
const type = rawArgs.type;
|
|
733
950
|
const { type: _omit, ...rest } = rawArgs;
|
|
734
951
|
switch (type) {
|
|
@@ -739,7 +956,7 @@ async function applyFilter(connection, snippetClient, rawArgs) {
|
|
|
739
956
|
case 'lens_blur':
|
|
740
957
|
return applyLensBlur(connection, snippetClient, rest);
|
|
741
958
|
case 'radial_blur':
|
|
742
|
-
return applyRadialBlur(connection, snippetClient, rest);
|
|
959
|
+
return applyRadialBlur(connection, snippetClient, detClient, rest);
|
|
743
960
|
case 'sharpen':
|
|
744
961
|
return applySharpen(connection, snippetClient, rest);
|
|
745
962
|
case 'smart_sharpen':
|
|
@@ -754,6 +971,16 @@ async function applyFilter(connection, snippetClient, rawArgs) {
|
|
|
754
971
|
return applyPixelate(connection, snippetClient, rest);
|
|
755
972
|
case 'distort':
|
|
756
973
|
return applyDistort(connection, snippetClient, rest);
|
|
974
|
+
case 'stylize':
|
|
975
|
+
return applyStylize(connection, snippetClient, rest);
|
|
976
|
+
case 'render':
|
|
977
|
+
return applyRender(connection, snippetClient, rest);
|
|
978
|
+
case 'other':
|
|
979
|
+
return applyOther(connection, snippetClient, rest);
|
|
980
|
+
case 'denoise':
|
|
981
|
+
return applyDenoise(connection, snippetClient, rest);
|
|
982
|
+
case 'blur':
|
|
983
|
+
return applyBlurAdv(connection, snippetClient, rest);
|
|
757
984
|
case 'displace':
|
|
758
985
|
return applyDisplace(connection, snippetClient, rest);
|
|
759
986
|
case 'oil_paint':
|
|
@@ -1126,14 +1353,24 @@ async function applyHighPass(connection, snippetClient, rawArgs) {
|
|
|
1126
1353
|
};
|
|
1127
1354
|
}
|
|
1128
1355
|
}
|
|
1129
|
-
async function applyRadialBlur(connection, snippetClient, rawArgs) {
|
|
1356
|
+
async function applyRadialBlur(connection, snippetClient, detClient, rawArgs) {
|
|
1130
1357
|
try {
|
|
1131
1358
|
const args = validateArgs(radialBlurSchema, rawArgs);
|
|
1132
1359
|
const amount = args.amount ?? 10;
|
|
1133
1360
|
const method = args.method ?? 'spin';
|
|
1134
1361
|
const quality = args.quality ?? 'good';
|
|
1135
|
-
|
|
1136
|
-
|
|
1362
|
+
let centerX = args.center_x ?? 0.5;
|
|
1363
|
+
let centerY = args.center_y ?? 0.5;
|
|
1364
|
+
let grounded;
|
|
1365
|
+
if (args.center_placement) {
|
|
1366
|
+
const loc = await resolveGatedPlacement(connection, detClient, args.center_placement, {
|
|
1367
|
+
expect: 'point',
|
|
1368
|
+
label: 'radial-blur center',
|
|
1369
|
+
});
|
|
1370
|
+
centerX = loc.docW > 0 ? Math.min(1, Math.max(0, loc.geom.point.x / loc.docW)) : 0.5;
|
|
1371
|
+
centerY = loc.docH > 0 ? Math.min(1, Math.max(0, loc.geom.point.y / loc.docH)) : 0.5;
|
|
1372
|
+
grounded = { x: Math.round(loc.geom.point.x), y: Math.round(loc.geom.point.y) };
|
|
1373
|
+
}
|
|
1137
1374
|
const applyToActiveLayer = args.apply_to_active_layer ?? false;
|
|
1138
1375
|
const script = await snippetClient.build('applyRadialBlur', {
|
|
1139
1376
|
amount,
|
|
@@ -1148,14 +1385,25 @@ async function applyRadialBlur(connection, snippetClient, rawArgs) {
|
|
|
1148
1385
|
const target = r.target_was_copy
|
|
1149
1386
|
? `new copy "${r.target_layer_name ?? '?'}"`
|
|
1150
1387
|
: 'active layer (in place)';
|
|
1388
|
+
const groundedNote = grounded
|
|
1389
|
+
? ` centered on (${grounded.x},${grounded.y}) via placement (gate PASS)`
|
|
1390
|
+
: '';
|
|
1391
|
+
const structured = result;
|
|
1392
|
+
if (grounded) {
|
|
1393
|
+
structured.center_placement = {
|
|
1394
|
+
gate: { pass: true },
|
|
1395
|
+
point: grounded,
|
|
1396
|
+
center: { x: Number(centerX.toFixed(4)), y: Number(centerY.toFixed(4)) },
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1151
1399
|
return {
|
|
1152
1400
|
content: [
|
|
1153
1401
|
{
|
|
1154
1402
|
type: 'text',
|
|
1155
|
-
text: `Radial Blur (${method}, amount ${amount}) applied to ${target}.`,
|
|
1403
|
+
text: `Radial Blur (${method}, amount ${amount}) applied to ${target}${groundedNote}.`,
|
|
1156
1404
|
},
|
|
1157
1405
|
],
|
|
1158
|
-
structuredContent:
|
|
1406
|
+
structuredContent: structured,
|
|
1159
1407
|
};
|
|
1160
1408
|
}
|
|
1161
1409
|
catch (error) {
|
|
@@ -1176,10 +1424,10 @@ async function applyPixelate(connection, snippetClient, rawArgs) {
|
|
|
1176
1424
|
const mode = args.mode;
|
|
1177
1425
|
const applyToActiveLayer = args.apply_to_active_layer ?? false;
|
|
1178
1426
|
const params = { mode, applyToActiveLayer };
|
|
1179
|
-
if (mode === 'mosaic') {
|
|
1427
|
+
if (mode === 'mosaic' || mode === 'crystallize' || mode === 'pointillize') {
|
|
1180
1428
|
params.cellSize = args.cell_size ?? 10;
|
|
1181
1429
|
}
|
|
1182
|
-
else {
|
|
1430
|
+
else if (mode === 'color_halftone') {
|
|
1183
1431
|
params.maxRadius = args.max_radius ?? 8;
|
|
1184
1432
|
params.angle1 = args.angle_1 ?? 108;
|
|
1185
1433
|
params.angle2 = args.angle_2 ?? 162;
|
|
@@ -1223,6 +1471,16 @@ async function applyDistort(connection, snippetClient, rawArgs) {
|
|
|
1223
1471
|
if (mode === 'twirl') {
|
|
1224
1472
|
params.angle = args.angle ?? 90;
|
|
1225
1473
|
}
|
|
1474
|
+
else if (mode === 'pinch') {
|
|
1475
|
+
params.amount = args.amount ?? 50;
|
|
1476
|
+
}
|
|
1477
|
+
else if (mode === 'spherize') {
|
|
1478
|
+
params.amount = args.amount ?? 100;
|
|
1479
|
+
}
|
|
1480
|
+
else if (mode === 'zigzag') {
|
|
1481
|
+
params.amount = args.amount ?? 10;
|
|
1482
|
+
params.ridges = args.ridges ?? 5;
|
|
1483
|
+
}
|
|
1226
1484
|
else if (mode === 'ripple') {
|
|
1227
1485
|
params.amount = args.amount ?? 100;
|
|
1228
1486
|
params.size = args.size ?? 'medium';
|
|
@@ -1270,6 +1528,205 @@ async function applyDistort(connection, snippetClient, rawArgs) {
|
|
|
1270
1528
|
};
|
|
1271
1529
|
}
|
|
1272
1530
|
}
|
|
1531
|
+
async function applyStylize(connection, snippetClient, rawArgs) {
|
|
1532
|
+
try {
|
|
1533
|
+
const args = validateArgs(stylizeSchema, rawArgs);
|
|
1534
|
+
const mode = args.mode;
|
|
1535
|
+
const applyToActiveLayer = args.apply_to_active_layer ?? false;
|
|
1536
|
+
const params = { mode, applyToActiveLayer };
|
|
1537
|
+
if (mode === 'emboss') {
|
|
1538
|
+
params.angle = args.angle ?? 135;
|
|
1539
|
+
params.height = args.height ?? 3;
|
|
1540
|
+
params.amount = args.amount ?? 100;
|
|
1541
|
+
}
|
|
1542
|
+
else if (mode === 'wind') {
|
|
1543
|
+
params.method = args.wind_method ?? 'wind';
|
|
1544
|
+
params.direction = args.wind_direction ?? 'left';
|
|
1545
|
+
}
|
|
1546
|
+
else if (mode === 'trace_contour') {
|
|
1547
|
+
params.level = args.level ?? 128;
|
|
1548
|
+
params.edge = args.edge ?? 'lower';
|
|
1549
|
+
}
|
|
1550
|
+
else if (mode === 'tiles') {
|
|
1551
|
+
params.number = args.number ?? 10;
|
|
1552
|
+
params.offset = args.offset ?? 10;
|
|
1553
|
+
}
|
|
1554
|
+
const script = await snippetClient.build('applyStylize', params);
|
|
1555
|
+
const result = await runScript(connection, script);
|
|
1556
|
+
const r = result;
|
|
1557
|
+
const target = r.target_was_copy
|
|
1558
|
+
? `new copy "${r.target_layer_name ?? '?'}"`
|
|
1559
|
+
: 'active layer (in place)';
|
|
1560
|
+
return {
|
|
1561
|
+
content: [
|
|
1562
|
+
{
|
|
1563
|
+
type: 'text',
|
|
1564
|
+
text: `Stylize (${mode}) applied to ${target}.`,
|
|
1565
|
+
},
|
|
1566
|
+
],
|
|
1567
|
+
structuredContent: result,
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
catch (error) {
|
|
1571
|
+
return {
|
|
1572
|
+
content: [
|
|
1573
|
+
{
|
|
1574
|
+
type: 'text',
|
|
1575
|
+
text: `Error applying Stylize: ${error instanceof Error ? error.message : String(error)}`,
|
|
1576
|
+
},
|
|
1577
|
+
],
|
|
1578
|
+
isError: true,
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
async function applyRender(connection, snippetClient, rawArgs) {
|
|
1583
|
+
try {
|
|
1584
|
+
const args = validateArgs(renderSchema, rawArgs);
|
|
1585
|
+
const mode = args.mode;
|
|
1586
|
+
const applyToActiveLayer = args.apply_to_active_layer ?? false;
|
|
1587
|
+
const params = { mode, applyToActiveLayer };
|
|
1588
|
+
if (mode === 'fibers') {
|
|
1589
|
+
params.variance = args.variance ?? 16;
|
|
1590
|
+
params.strength = args.fiber_strength ?? 4;
|
|
1591
|
+
params.seed = args.seed ?? 12345;
|
|
1592
|
+
}
|
|
1593
|
+
const script = await snippetClient.build('applyRender', params);
|
|
1594
|
+
const result = await runScript(connection, script);
|
|
1595
|
+
const r = result;
|
|
1596
|
+
const target = r.target_was_copy
|
|
1597
|
+
? `new copy "${r.target_layer_name ?? '?'}"`
|
|
1598
|
+
: 'active layer (in place)';
|
|
1599
|
+
return {
|
|
1600
|
+
content: [
|
|
1601
|
+
{
|
|
1602
|
+
type: 'text',
|
|
1603
|
+
text: `Render (${mode}) applied to ${target}.`,
|
|
1604
|
+
},
|
|
1605
|
+
],
|
|
1606
|
+
structuredContent: result,
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
catch (error) {
|
|
1610
|
+
return {
|
|
1611
|
+
content: [
|
|
1612
|
+
{
|
|
1613
|
+
type: 'text',
|
|
1614
|
+
text: `Error applying Render: ${error instanceof Error ? error.message : String(error)}`,
|
|
1615
|
+
},
|
|
1616
|
+
],
|
|
1617
|
+
isError: true,
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
async function applyOther(connection, snippetClient, rawArgs) {
|
|
1622
|
+
try {
|
|
1623
|
+
const args = validateArgs(otherSchema, rawArgs);
|
|
1624
|
+
const mode = args.mode;
|
|
1625
|
+
const applyToActiveLayer = args.apply_to_active_layer ?? false;
|
|
1626
|
+
const params = { mode, applyToActiveLayer };
|
|
1627
|
+
if (mode === 'maximum' || mode === 'minimum') {
|
|
1628
|
+
params.radius = args.radius ?? 3;
|
|
1629
|
+
params.preserve = args.preserve ?? 'roundness';
|
|
1630
|
+
}
|
|
1631
|
+
else if (mode === 'offset') {
|
|
1632
|
+
params.horizontal = args.horizontal ?? 0;
|
|
1633
|
+
params.vertical = args.vertical ?? 0;
|
|
1634
|
+
}
|
|
1635
|
+
const script = await snippetClient.build('applyOther', params);
|
|
1636
|
+
const result = await runScript(connection, script);
|
|
1637
|
+
const r = result;
|
|
1638
|
+
const target = r.target_was_copy
|
|
1639
|
+
? `new copy "${r.target_layer_name ?? '?'}"`
|
|
1640
|
+
: 'active layer (in place)';
|
|
1641
|
+
return {
|
|
1642
|
+
content: [{ type: 'text', text: `Other filter (${mode}) applied to ${target}.` }],
|
|
1643
|
+
structuredContent: result,
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
catch (error) {
|
|
1647
|
+
return {
|
|
1648
|
+
content: [
|
|
1649
|
+
{
|
|
1650
|
+
type: 'text',
|
|
1651
|
+
text: `Error applying filter: ${error instanceof Error ? error.message : String(error)}`,
|
|
1652
|
+
},
|
|
1653
|
+
],
|
|
1654
|
+
isError: true,
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
async function applyDenoise(connection, snippetClient, rawArgs) {
|
|
1659
|
+
try {
|
|
1660
|
+
const args = validateArgs(denoiseSchema, rawArgs);
|
|
1661
|
+
const mode = args.mode;
|
|
1662
|
+
const applyToActiveLayer = args.apply_to_active_layer ?? false;
|
|
1663
|
+
const params = { mode, applyToActiveLayer };
|
|
1664
|
+
if (mode === 'median') {
|
|
1665
|
+
params.radius = args.radius ?? 4;
|
|
1666
|
+
}
|
|
1667
|
+
else if (mode === 'dust_and_scratches') {
|
|
1668
|
+
params.radius = args.radius ?? 3;
|
|
1669
|
+
params.threshold = args.threshold ?? 10;
|
|
1670
|
+
}
|
|
1671
|
+
const script = await snippetClient.build('applyDenoise', params);
|
|
1672
|
+
const result = await runScript(connection, script);
|
|
1673
|
+
const r = result;
|
|
1674
|
+
const target = r.target_was_copy
|
|
1675
|
+
? `new copy "${r.target_layer_name ?? '?'}"`
|
|
1676
|
+
: 'active layer (in place)';
|
|
1677
|
+
return {
|
|
1678
|
+
content: [{ type: 'text', text: `Noise reduction (${mode}) applied to ${target}.` }],
|
|
1679
|
+
structuredContent: result,
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
catch (error) {
|
|
1683
|
+
return {
|
|
1684
|
+
content: [
|
|
1685
|
+
{
|
|
1686
|
+
type: 'text',
|
|
1687
|
+
text: `Error applying noise reduction: ${error instanceof Error ? error.message : String(error)}`,
|
|
1688
|
+
},
|
|
1689
|
+
],
|
|
1690
|
+
isError: true,
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
async function applyBlurAdv(connection, snippetClient, rawArgs) {
|
|
1695
|
+
try {
|
|
1696
|
+
const args = validateArgs(blurAdvSchema, rawArgs);
|
|
1697
|
+
const mode = args.mode;
|
|
1698
|
+
const applyToActiveLayer = args.apply_to_active_layer ?? false;
|
|
1699
|
+
const params = { mode, applyToActiveLayer };
|
|
1700
|
+
if (mode === 'surface_blur') {
|
|
1701
|
+
params.radius = args.radius ?? 15;
|
|
1702
|
+
params.threshold = args.threshold ?? 20;
|
|
1703
|
+
}
|
|
1704
|
+
else if (mode === 'box_blur') {
|
|
1705
|
+
params.radius = args.radius ?? 12;
|
|
1706
|
+
}
|
|
1707
|
+
const script = await snippetClient.build('applyBlurAdv', params);
|
|
1708
|
+
const result = await runScript(connection, script);
|
|
1709
|
+
const r = result;
|
|
1710
|
+
const target = r.target_was_copy
|
|
1711
|
+
? `new copy "${r.target_layer_name ?? '?'}"`
|
|
1712
|
+
: 'active layer (in place)';
|
|
1713
|
+
return {
|
|
1714
|
+
content: [{ type: 'text', text: `Blur (${mode}) applied to ${target}.` }],
|
|
1715
|
+
structuredContent: result,
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
catch (error) {
|
|
1719
|
+
return {
|
|
1720
|
+
content: [
|
|
1721
|
+
{
|
|
1722
|
+
type: 'text',
|
|
1723
|
+
text: `Error applying blur: ${error instanceof Error ? error.message : String(error)}`,
|
|
1724
|
+
},
|
|
1725
|
+
],
|
|
1726
|
+
isError: true,
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1273
1730
|
async function applyOilPaint(connection, snippetClient, rawArgs) {
|
|
1274
1731
|
try {
|
|
1275
1732
|
const args = validateArgs(oilPaintSchema, rawArgs);
|