editmamei 0.22.4 → 0.24.1
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 +8 -6
- package/dist/api/extendscript/_helpers.js +97 -13
- package/dist/api/photoshop-api.js +4 -3
- package/dist/api/snippet-client.js +38 -2
- 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/core/server.js +56 -180
- package/dist/delivery/client.js +50 -1
- package/dist/delivery/provision.js +18 -1
- package/dist/delivery/signing.js +38 -1
- package/dist/delivery/store.js +149 -16
- package/dist/detection/detect-active-doc.js +41 -10
- package/dist/detection/detection-client.js +3 -3
- package/dist/detection/face-detector.js +2 -2
- package/dist/detection/geometry.js +14 -0
- package/dist/detection/landmark-detection-client.js +3 -3
- package/dist/detection/mesh-face.js +2 -3
- package/dist/detection/object-detector.js +2 -2
- package/dist/detection/runtime.js +14 -3
- package/dist/detection/sam-segmenter.js +69 -26
- package/dist/kernel/kernel.js +5 -9
- package/dist/kernel/module-lifecycle.js +181 -0
- package/dist/license/entitlement.js +13 -3
- package/dist/license/store.js +8 -1
- package/dist/modules/ce/index.js +1 -1
- package/dist/perception/facets.js +19 -1
- package/dist/perception/grounding-anchors.js +2 -12
- package/dist/perception/grounding-locate.js +1 -9
- package/dist/perception/grounding-review-crop.js +4 -29
- package/dist/perception/overlay.js +62 -0
- package/dist/perception/pixel-identity.js +41 -0
- package/dist/perception/region-precompute.js +85 -17
- package/dist/perception/scene-model.js +93 -50
- package/dist/perception/select-recipes.js +107 -66
- package/dist/perception/sky-ground-flood.js +0 -5
- package/dist/perception/sky-mask-transfer.js +72 -13
- package/dist/platform/connection.js +42 -11
- package/dist/platform/executor-queue.js +68 -0
- package/dist/platform/macos-detector.js +2 -1
- package/dist/platform/macos-executor.js +18 -67
- package/dist/platform/run-child.js +6 -3
- package/dist/platform/windows-detector.js +14 -7
- package/dist/platform/windows-executor.js +9 -64
- package/dist/skills/editmamei-skill.zip +0 -0
- package/dist/tools/adjustment-tools.js +78 -130
- package/dist/tools/brush-tools.js +3 -14
- package/dist/tools/channel-compose-tools.js +4 -23
- package/dist/tools/detection-tools.js +59 -47
- package/dist/tools/document-tools.js +121 -131
- package/dist/tools/filter-tools.js +489 -730
- package/dist/tools/group-tools.js +90 -156
- package/dist/tools/guide-tools.js +52 -83
- package/dist/tools/history-tools.js +29 -63
- package/dist/tools/image-placement-tools.js +18 -37
- package/dist/tools/image-tools.js +23 -51
- package/dist/tools/layer-ordering-tools.js +2 -9
- package/dist/tools/layer-properties-tools.js +245 -384
- package/dist/tools/layer-tools.js +115 -181
- package/dist/tools/layer-transform-tools.js +41 -106
- package/dist/tools/metadata-tools.js +2 -9
- package/dist/tools/path-tools.js +3 -14
- package/dist/tools/portrait-tools.js +2 -5
- package/dist/tools/preview-tools.js +53 -65
- package/dist/tools/retouch-tools.js +32 -70
- package/dist/tools/scene-tools.js +94 -73
- package/dist/tools/selection-tools.js +306 -563
- package/dist/tools/shape-tools.js +18 -10
- package/dist/tools/text-tools.js +24 -57
- package/dist/tools/transform-canvas-tools.js +21 -54
- package/dist/tools/vector-mask-tools.js +2 -9
- package/dist/utils/jpeg-quality.js +1 -0
- package/dist/utils/logger.js +3 -0
- package/dist/utils/operation-timeouts.js +7 -0
- package/dist/utils/run-script.js +7 -2
- package/dist/utils/session-log.js +134 -12
- package/dist/utils/template-storage.js +5 -6
- package/dist/utils/tool-helpers.js +37 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { runScript } from '../utils/run-script.js';
|
|
2
2
|
import { GROUP_BLEND_MODES } from '../utils/blend-modes.js';
|
|
3
3
|
import { validateArgs } from '../utils/validate.js';
|
|
4
|
+
import { toolErrorResult, runSnippetTool } from '../utils/tool-helpers.js';
|
|
5
|
+
const emptySchema = {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {},
|
|
8
|
+
};
|
|
4
9
|
const deleteGroupSchema = {
|
|
5
10
|
type: 'object',
|
|
6
11
|
properties: {
|
|
@@ -27,6 +32,11 @@ const createGroupSchema = {
|
|
|
27
32
|
items: { type: 'string' },
|
|
28
33
|
description: 'Optional list of existing layer names to move into the new group. The first listed name ends up on top of the group stack. Layers not found are returned in `not_found`.',
|
|
29
34
|
},
|
|
35
|
+
into_active_group: {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
description: "Photoshop's Mk-layerSection descriptor carries no placement target, so with a GROUP active it would natively nest the new group INSIDE that group. Default false hoists the new group back out so it lands above the active layer/group as a sibling, matching this tool's documented placement. Pass true to keep it nested inside the active group instead.",
|
|
38
|
+
default: false,
|
|
39
|
+
},
|
|
30
40
|
},
|
|
31
41
|
required: ['name'],
|
|
32
42
|
};
|
|
@@ -78,7 +88,7 @@ export function createGroupTools(connection, snippetClient) {
|
|
|
78
88
|
{
|
|
79
89
|
tool: {
|
|
80
90
|
name: 'ps_create_group',
|
|
81
|
-
description:
|
|
91
|
+
description: `Create a new layer group (LayerSet) above the active layer with the given name — hoisted out of the active layer's group by default even though Photoshop's own Mk-layerSection placement rule would otherwise nest it INSIDE that group (pass into_active_group:true to keep that native nesting; this is why groups created one after another land as siblings rather than nested, so bottom-to-top group creation is safe). Optionally moves existing layers into it in one step. Non-destructive. Foundational for structured non-destructive editing — e.g. group all adjustment layers into an "edits" group so you can A/B toggle the whole stack via group visibility.`,
|
|
82
92
|
inputSchema: createGroupSchema,
|
|
83
93
|
outputSchema: {
|
|
84
94
|
type: 'object',
|
|
@@ -87,6 +97,15 @@ export function createGroupTools(connection, snippetClient) {
|
|
|
87
97
|
groupName: { type: 'string' },
|
|
88
98
|
moved_count: { type: 'number' },
|
|
89
99
|
not_found: { type: 'array', items: { type: 'string' } },
|
|
100
|
+
hoisted: {
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
description: 'True when the new group had to be moved back out of the previously-active group to honor into_active_group:false (the default). False when it landed correctly on its own, or when the move-back itself failed — check the layer tree if this matters and hoisted is false.',
|
|
103
|
+
},
|
|
104
|
+
parent_path: {
|
|
105
|
+
type: ['array', 'null'],
|
|
106
|
+
items: { type: 'string' },
|
|
107
|
+
description: 'The containing-group name chain (outermost first), empty array at the document root.',
|
|
108
|
+
},
|
|
90
109
|
context: { type: 'object' },
|
|
91
110
|
},
|
|
92
111
|
},
|
|
@@ -226,150 +245,81 @@ export function createGroupTools(connection, snippetClient) {
|
|
|
226
245
|
},
|
|
227
246
|
];
|
|
228
247
|
}
|
|
229
|
-
async function createClippingMask(connection, snippetClient,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
],
|
|
240
|
-
structuredContent: result,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
return {
|
|
245
|
-
content: [
|
|
246
|
-
{
|
|
247
|
-
type: 'text',
|
|
248
|
-
text: `Error creating clipping mask: ${error instanceof Error ? error.message : String(error)}`,
|
|
249
|
-
},
|
|
250
|
-
],
|
|
251
|
-
isError: true,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
248
|
+
async function createClippingMask(connection, snippetClient, rawArgs) {
|
|
249
|
+
return runSnippetTool({
|
|
250
|
+
connection,
|
|
251
|
+
snippetClient,
|
|
252
|
+
rawArgs,
|
|
253
|
+
schema: emptySchema,
|
|
254
|
+
snippet: 'createClippingMask',
|
|
255
|
+
errorPrefix: 'Error creating clipping mask',
|
|
256
|
+
successText: (result) => `Clipped layer "${result.layerName ?? ''}" to the layer below.`,
|
|
257
|
+
});
|
|
254
258
|
}
|
|
255
|
-
async function releaseClippingMask(connection, snippetClient,
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
],
|
|
266
|
-
structuredContent: result,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
catch (error) {
|
|
270
|
-
return {
|
|
271
|
-
content: [
|
|
272
|
-
{
|
|
273
|
-
type: 'text',
|
|
274
|
-
text: `Error releasing clipping mask: ${error instanceof Error ? error.message : String(error)}`,
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
isError: true,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
259
|
+
async function releaseClippingMask(connection, snippetClient, rawArgs) {
|
|
260
|
+
return runSnippetTool({
|
|
261
|
+
connection,
|
|
262
|
+
snippetClient,
|
|
263
|
+
rawArgs,
|
|
264
|
+
schema: emptySchema,
|
|
265
|
+
snippet: 'releaseClippingMask',
|
|
266
|
+
errorPrefix: 'Error releasing clipping mask',
|
|
267
|
+
successText: (result) => `Released clipping mask on layer "${result.layerName ?? ''}".`,
|
|
268
|
+
});
|
|
280
269
|
}
|
|
281
270
|
async function createGroup(connection, snippetClient, rawArgs) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
{
|
|
305
|
-
type: 'text',
|
|
306
|
-
text: `Error creating group: ${error instanceof Error ? error.message : String(error)}`,
|
|
307
|
-
},
|
|
308
|
-
],
|
|
309
|
-
isError: true,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
271
|
+
return runSnippetTool({
|
|
272
|
+
connection,
|
|
273
|
+
snippetClient,
|
|
274
|
+
rawArgs,
|
|
275
|
+
schema: createGroupSchema,
|
|
276
|
+
snippet: 'createGroup',
|
|
277
|
+
errorPrefix: 'Error creating group',
|
|
278
|
+
params: (args) => {
|
|
279
|
+
const name = args.name;
|
|
280
|
+
const layers = args.layers;
|
|
281
|
+
const intoActiveGroup = args.into_active_group ?? false;
|
|
282
|
+
const params = { name, into_active_group: intoActiveGroup };
|
|
283
|
+
if (layers !== undefined)
|
|
284
|
+
params.layerNames = layers;
|
|
285
|
+
return params;
|
|
286
|
+
},
|
|
287
|
+
successText: (result, args) => {
|
|
288
|
+
const name = args.name;
|
|
289
|
+
const layers = args.layers;
|
|
290
|
+
return `Group "${name}" created${layers && layers.length ? ` (moved ${result.moved_count ?? 0}/${layers.length} layers)` : ''}`;
|
|
291
|
+
},
|
|
292
|
+
});
|
|
312
293
|
}
|
|
313
294
|
async function moveLayerToGroup(connection, snippetClient, rawArgs) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
structuredContent: result,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
catch (error) {
|
|
331
|
-
return {
|
|
332
|
-
content: [
|
|
333
|
-
{
|
|
334
|
-
type: 'text',
|
|
335
|
-
text: `Error moving layer to group: ${error instanceof Error ? error.message : String(error)}`,
|
|
336
|
-
},
|
|
337
|
-
],
|
|
338
|
-
isError: true,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
295
|
+
return runSnippetTool({
|
|
296
|
+
connection,
|
|
297
|
+
snippetClient,
|
|
298
|
+
rawArgs,
|
|
299
|
+
schema: moveLayerToGroupSchema,
|
|
300
|
+
snippet: 'moveLayerToGroup',
|
|
301
|
+
errorPrefix: 'Error moving layer to group',
|
|
302
|
+
params: (args) => ({
|
|
303
|
+
layerName: args.layer_name,
|
|
304
|
+
groupName: args.group_name,
|
|
305
|
+
}),
|
|
306
|
+
successText: (_result, args) => `Layer "${args.layer_name}" moved into group "${args.group_name}"`,
|
|
307
|
+
});
|
|
341
308
|
}
|
|
342
309
|
async function setGroupBlendMode(connection, snippetClient, rawArgs) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
text: `Group "${name}" blend mode set to ${blendMode}`,
|
|
357
|
-
},
|
|
358
|
-
],
|
|
359
|
-
structuredContent: result,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
catch (error) {
|
|
363
|
-
return {
|
|
364
|
-
content: [
|
|
365
|
-
{
|
|
366
|
-
type: 'text',
|
|
367
|
-
text: `Error setting group blend mode: ${error instanceof Error ? error.message : String(error)}`,
|
|
368
|
-
},
|
|
369
|
-
],
|
|
370
|
-
isError: true,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
310
|
+
return runSnippetTool({
|
|
311
|
+
connection,
|
|
312
|
+
snippetClient,
|
|
313
|
+
rawArgs,
|
|
314
|
+
schema: setGroupBlendModeSchema,
|
|
315
|
+
snippet: 'setGroupBlendMode',
|
|
316
|
+
errorPrefix: 'Error setting group blend mode',
|
|
317
|
+
params: (args) => ({
|
|
318
|
+
groupName: args.name,
|
|
319
|
+
blendMode: args.blend_mode,
|
|
320
|
+
}),
|
|
321
|
+
successText: (_result, args) => `Group "${args.name}" blend mode set to ${args.blend_mode}`,
|
|
322
|
+
});
|
|
373
323
|
}
|
|
374
324
|
async function ungroup(connection, snippetClient, rawArgs) {
|
|
375
325
|
try {
|
|
@@ -400,15 +350,7 @@ async function ungroup(connection, snippetClient, rawArgs) {
|
|
|
400
350
|
};
|
|
401
351
|
}
|
|
402
352
|
catch (error) {
|
|
403
|
-
return
|
|
404
|
-
content: [
|
|
405
|
-
{
|
|
406
|
-
type: 'text',
|
|
407
|
-
text: `Error ungrouping: ${error instanceof Error ? error.message : String(error)}`,
|
|
408
|
-
},
|
|
409
|
-
],
|
|
410
|
-
isError: true,
|
|
411
|
-
};
|
|
353
|
+
return toolErrorResult('Error ungrouping', error);
|
|
412
354
|
}
|
|
413
355
|
}
|
|
414
356
|
async function deleteGroup(connection, snippetClient, rawArgs) {
|
|
@@ -440,14 +382,6 @@ async function deleteGroup(connection, snippetClient, rawArgs) {
|
|
|
440
382
|
};
|
|
441
383
|
}
|
|
442
384
|
catch (error) {
|
|
443
|
-
return
|
|
444
|
-
content: [
|
|
445
|
-
{
|
|
446
|
-
type: 'text',
|
|
447
|
-
text: `Error deleting group: ${error instanceof Error ? error.message : String(error)}`,
|
|
448
|
-
},
|
|
449
|
-
],
|
|
450
|
-
isError: true,
|
|
451
|
-
};
|
|
385
|
+
return toolErrorResult('Error deleting group', error);
|
|
452
386
|
}
|
|
453
387
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { validateArgs } from '../utils/validate.js';
|
|
1
|
+
import { runSnippetTool } from '../utils/tool-helpers.js';
|
|
3
2
|
const addGuideSchema = {
|
|
4
3
|
type: 'object',
|
|
5
4
|
properties: {
|
|
@@ -33,6 +32,10 @@ const guideLayoutSchema = {
|
|
|
33
32
|
},
|
|
34
33
|
},
|
|
35
34
|
};
|
|
35
|
+
const clearGuidesSchema = {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {},
|
|
38
|
+
};
|
|
36
39
|
const GUIDE_OPS = ['add', 'layout', 'clear'];
|
|
37
40
|
const GUIDE_INPUT_SCHEMA = {
|
|
38
41
|
type: 'object',
|
|
@@ -98,89 +101,55 @@ async function guides(connection, snippetClient, rawArgs) {
|
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
async function addGuide(connection, snippetClient, rawArgs) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
content: [
|
|
120
|
-
{
|
|
121
|
-
type: 'text',
|
|
122
|
-
text: `Error adding guide: ${error instanceof Error ? error.message : String(error)}`,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
isError: true,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
104
|
+
return runSnippetTool({
|
|
105
|
+
connection,
|
|
106
|
+
snippetClient,
|
|
107
|
+
rawArgs,
|
|
108
|
+
schema: addGuideSchema,
|
|
109
|
+
snippet: 'addGuide',
|
|
110
|
+
errorPrefix: 'Error adding guide',
|
|
111
|
+
params: (args) => ({
|
|
112
|
+
orientation: args.orientation,
|
|
113
|
+
position: args.position,
|
|
114
|
+
}),
|
|
115
|
+
successText: (result, args) => {
|
|
116
|
+
const orientation = args.orientation;
|
|
117
|
+
const position = args.position;
|
|
118
|
+
return `Added ${orientation} guide at ${position}px\nResult: ${JSON.stringify(result)}`;
|
|
119
|
+
},
|
|
120
|
+
});
|
|
128
121
|
}
|
|
129
122
|
async function addGuideLayout(connection, snippetClient, rawArgs) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
content: [
|
|
152
|
-
{
|
|
153
|
-
type: 'text',
|
|
154
|
-
text: `Error creating guide layout: ${error instanceof Error ? error.message : String(error)}`,
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
isError: true,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
123
|
+
return runSnippetTool({
|
|
124
|
+
connection,
|
|
125
|
+
snippetClient,
|
|
126
|
+
rawArgs,
|
|
127
|
+
schema: guideLayoutSchema,
|
|
128
|
+
snippet: 'addGuideLayout',
|
|
129
|
+
errorPrefix: 'Error creating guide layout',
|
|
130
|
+
params: (args) => {
|
|
131
|
+
const columns = args.columns ?? 0;
|
|
132
|
+
const rows = args.rows ?? 0;
|
|
133
|
+
if (columns <= 0 && rows <= 0) {
|
|
134
|
+
throw new Error('op=layout requires at least one of columns / rows to be ≥ 1.');
|
|
135
|
+
}
|
|
136
|
+
return { columns, rows };
|
|
137
|
+
},
|
|
138
|
+
successText: (result, args) => {
|
|
139
|
+
const columns = args.columns ?? 0;
|
|
140
|
+
const rows = args.rows ?? 0;
|
|
141
|
+
return `Created guide layout (${columns} columns × ${rows} rows)\nResult: ${JSON.stringify(result)}`;
|
|
142
|
+
},
|
|
143
|
+
});
|
|
160
144
|
}
|
|
161
145
|
async function clearGuides(connection, snippetClient) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
],
|
|
172
|
-
structuredContent: result,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
return {
|
|
177
|
-
content: [
|
|
178
|
-
{
|
|
179
|
-
type: 'text',
|
|
180
|
-
text: `Error clearing guides: ${error instanceof Error ? error.message : String(error)}`,
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
isError: true,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
146
|
+
return runSnippetTool({
|
|
147
|
+
connection,
|
|
148
|
+
snippetClient,
|
|
149
|
+
rawArgs: {},
|
|
150
|
+
schema: clearGuidesSchema,
|
|
151
|
+
snippet: 'clearGuides',
|
|
152
|
+
errorPrefix: 'Error clearing guides',
|
|
153
|
+
successText: (result) => `Cleared all guides\nResult: ${JSON.stringify(result)}`,
|
|
154
|
+
});
|
|
186
155
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runScript } from '../utils/run-script.js';
|
|
2
|
-
import {
|
|
2
|
+
import { toolErrorResult, runSnippetTool } from '../utils/tool-helpers.js';
|
|
3
3
|
const undoSchema = {
|
|
4
4
|
type: 'object',
|
|
5
5
|
properties: {
|
|
@@ -39,7 +39,7 @@ export function createHistoryTools(connection, snippetClient) {
|
|
|
39
39
|
{
|
|
40
40
|
tool: {
|
|
41
41
|
name: 'ps_undo',
|
|
42
|
-
description: 'Step backward in the document history (equivalent to Ctrl/Cmd+Z). WHEN TO REACH FOR THIS: after an experimental destructive bake (
|
|
42
|
+
description: 'Step backward in the document history (equivalent to Ctrl/Cmd+Z). WHEN TO REACH FOR THIS: after an experimental destructive bake (ps_merge mode=visible/flatten, ps_apply_filter type=*_blur/sharpen/noise) produced a wrong result; after a play_action whose scope you mispredicted; or to revert an entire branch of exploration. Non-destructive workflows (adjustment layers + masks) rarely need this — just tweak/delete the offending layer instead. Reversible via ps_redo as long as no new edit has been made since. Returns current history state, remaining steps, and document context.',
|
|
43
43
|
inputSchema: undoSchema,
|
|
44
44
|
outputSchema: HISTORY_STEP_RESULT_SCHEMA,
|
|
45
45
|
annotations: {
|
|
@@ -65,60 +65,34 @@ export function createHistoryTools(connection, snippetClient) {
|
|
|
65
65
|
];
|
|
66
66
|
}
|
|
67
67
|
async function undo(connection, snippetClient, rawArgs) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
return {
|
|
85
|
-
content: [
|
|
86
|
-
{
|
|
87
|
-
type: 'text',
|
|
88
|
-
text: `Error undoing: ${error instanceof Error ? error.message : String(error)}`,
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
isError: true,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
68
|
+
return runSnippetTool({
|
|
69
|
+
connection,
|
|
70
|
+
snippetClient,
|
|
71
|
+
rawArgs,
|
|
72
|
+
schema: undoSchema,
|
|
73
|
+
snippet: 'undo',
|
|
74
|
+
errorPrefix: 'Error undoing',
|
|
75
|
+
params: (args) => ({ steps: args.steps }),
|
|
76
|
+
successText: (result, args) => {
|
|
77
|
+
const steps = args.steps;
|
|
78
|
+
return `Undo successful (${steps} step${steps > 1 ? 's' : ''})\nResult: ${JSON.stringify(result)}`;
|
|
79
|
+
},
|
|
80
|
+
});
|
|
94
81
|
}
|
|
95
82
|
async function redo(connection, snippetClient, rawArgs) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
catch (error) {
|
|
112
|
-
return {
|
|
113
|
-
content: [
|
|
114
|
-
{
|
|
115
|
-
type: 'text',
|
|
116
|
-
text: `Error redoing: ${error instanceof Error ? error.message : String(error)}`,
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
isError: true,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
83
|
+
return runSnippetTool({
|
|
84
|
+
connection,
|
|
85
|
+
snippetClient,
|
|
86
|
+
rawArgs,
|
|
87
|
+
schema: redoSchema,
|
|
88
|
+
snippet: 'redo',
|
|
89
|
+
errorPrefix: 'Error redoing',
|
|
90
|
+
params: (args) => ({ steps: args.steps }),
|
|
91
|
+
successText: (result, args) => {
|
|
92
|
+
const steps = args.steps;
|
|
93
|
+
return `Redo successful (${steps} step${steps > 1 ? 's' : ''})\nResult: ${JSON.stringify(result)}`;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
122
96
|
}
|
|
123
97
|
export async function getHistory(connection, snippetClient) {
|
|
124
98
|
try {
|
|
@@ -135,14 +109,6 @@ export async function getHistory(connection, snippetClient) {
|
|
|
135
109
|
};
|
|
136
110
|
}
|
|
137
111
|
catch (error) {
|
|
138
|
-
return
|
|
139
|
-
content: [
|
|
140
|
-
{
|
|
141
|
-
type: 'text',
|
|
142
|
-
text: `Error getting history: ${error instanceof Error ? error.message : String(error)}`,
|
|
143
|
-
},
|
|
144
|
-
],
|
|
145
|
-
isError: true,
|
|
146
|
-
};
|
|
112
|
+
return toolErrorResult('Error getting history', error);
|
|
147
113
|
}
|
|
148
114
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { validateArgs } from '../utils/validate.js';
|
|
1
|
+
import { runSnippetTool } from '../utils/tool-helpers.js';
|
|
3
2
|
const PS_COORD_MAX_PX = 300_000;
|
|
4
3
|
const placeImageSchema = {
|
|
5
4
|
type: 'object',
|
|
@@ -65,39 +64,21 @@ export function createImagePlacementTools(connection, snippetClient) {
|
|
|
65
64
|
];
|
|
66
65
|
}
|
|
67
66
|
async function placeImage(connection, snippetClient, rawArgs) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
type: 'text',
|
|
86
|
-
text: `Image placed successfully: ${filePath}\nPosition: (${x}, ${y})\nResult: ${JSON.stringify(result)}`,
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
structuredContent: result,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
return {
|
|
94
|
-
content: [
|
|
95
|
-
{
|
|
96
|
-
type: 'text',
|
|
97
|
-
text: `Error placing image: ${error instanceof Error ? error.message : String(error)}`,
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
isError: true,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
67
|
+
return runSnippetTool({
|
|
68
|
+
connection,
|
|
69
|
+
snippetClient,
|
|
70
|
+
rawArgs,
|
|
71
|
+
schema: placeImageSchema,
|
|
72
|
+
snippet: 'placeImage',
|
|
73
|
+
errorPrefix: 'Error placing image',
|
|
74
|
+
params: (args) => {
|
|
75
|
+
const params = { filePath: args.file_path, x: args.x, y: args.y };
|
|
76
|
+
if (args.width_percent !== undefined)
|
|
77
|
+
params.widthPercent = args.width_percent;
|
|
78
|
+
if (args.height_percent !== undefined)
|
|
79
|
+
params.heightPercent = args.height_percent;
|
|
80
|
+
return params;
|
|
81
|
+
},
|
|
82
|
+
successText: (result, args) => `Image placed successfully: ${args.file_path}\nPosition: (${args.x}, ${args.y})\nResult: ${JSON.stringify(result)}`,
|
|
83
|
+
});
|
|
103
84
|
}
|