editmamei 1.0.3 → 1.2.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/README.md +9 -9
- package/dist/api/extendscript/_helpers.js +70 -2
- package/dist/api/photoshop-api.js +17 -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/cli/activate.js +1 -1
- package/dist/cli/config.js +1 -1
- package/dist/cli/deactivate.js +1 -1
- package/dist/core/raw-develop-state.js +13 -0
- package/dist/core/server.js +147 -18
- package/dist/core/tool-groups.js +18 -21
- package/dist/core/tool-tiers.js +7 -10
- package/dist/detection/runtime.js +2 -2
- package/dist/modules/ce/index.js +2 -0
- package/dist/perception/region-scorer.js +1 -1
- package/dist/platform/connection.js +15 -0
- package/dist/skills/editmamei-skill.zip +0 -0
- package/dist/telemetry/client.js +15 -2
- package/dist/telemetry/sanitize.js +1 -0
- package/dist/tools/adjustment-tools.js +21 -3
- package/dist/tools/detection-tools.js +1 -1
- package/dist/tools/document-tools.js +5 -2
- package/dist/tools/filter-tools.js +118 -15
- package/dist/tools/group-tools.js +146 -146
- package/dist/tools/history-tools.js +1 -1
- package/dist/tools/inspect-tools.js +22 -3
- package/dist/tools/layer-tools.js +4 -27
- package/dist/tools/overview-tools.js +37 -8
- package/dist/tools/preview-tools.js +3 -2
- package/dist/tools/scene-tools.js +1 -1
- package/dist/tools/selection-tools.js +91 -18
- package/dist/tools/sky-tools.js +126 -0
- package/dist/tools/smart-object-tools.js +163 -0
- package/dist/tools/text-tools.js +62 -41
- package/dist/update/check.js +65 -6
- package/dist/update/session-fixes.js +33 -0
- package/dist/utils/jsx.js +2 -1
- package/dist/utils/operation-timeouts.js +2 -0
- package/dist/utils/session-log.js +46 -6
- package/dist/utils/temp.js +1 -1
- package/dist/utils/tool-helpers.js +23 -0
- package/dist/version.js +1 -1
- package/package.json +13 -2
package/dist/core/tool-tiers.js
CHANGED
|
@@ -5,6 +5,7 @@ export const TOOL_TIERS = {
|
|
|
5
5
|
ps_list_actions: 'pro',
|
|
6
6
|
ps_play_action: 'pro',
|
|
7
7
|
ps_execute_script: 'pro',
|
|
8
|
+
ps_batch: 'pro',
|
|
8
9
|
ps_add_adjustment_layer: 'community',
|
|
9
10
|
ps_apply_adjustment: 'community',
|
|
10
11
|
ps_create_document: 'community',
|
|
@@ -12,14 +13,9 @@ export const TOOL_TIERS = {
|
|
|
12
13
|
ps_open_document: 'community',
|
|
13
14
|
ps_save_psd: 'community',
|
|
14
15
|
ps_export: 'community',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
ps_set_group_blend_mode: 'community',
|
|
19
|
-
ps_ungroup: 'community',
|
|
20
|
-
ps_delete_group: 'community',
|
|
21
|
-
ps_create_clipping_mask: 'community',
|
|
22
|
-
ps_release_clipping_mask: 'dev',
|
|
16
|
+
ps_filter: 'community',
|
|
17
|
+
ps_group: 'community',
|
|
18
|
+
ps_clipping_mask: 'community',
|
|
23
19
|
ps_undo: 'community',
|
|
24
20
|
ps_redo: 'community',
|
|
25
21
|
ps_place_image: 'community',
|
|
@@ -37,7 +33,6 @@ export const TOOL_TIERS = {
|
|
|
37
33
|
ps_add_layer_style: 'community',
|
|
38
34
|
ps_create_layer: 'community',
|
|
39
35
|
ps_delete_layer: 'community',
|
|
40
|
-
ps_create_text_layer: 'community',
|
|
41
36
|
ps_fill_layer: 'community',
|
|
42
37
|
ps_add_fill_layer: 'community',
|
|
43
38
|
ps_select_layer: 'community',
|
|
@@ -72,6 +67,8 @@ export const TOOL_TIERS = {
|
|
|
72
67
|
ps_select_sky: 'community',
|
|
73
68
|
ps_select_subject_instance: 'pro',
|
|
74
69
|
ps_select_object: 'pro',
|
|
70
|
+
ps_select_focus_area: 'dev',
|
|
71
|
+
ps_replace_sky: 'dev',
|
|
75
72
|
ps_modify_selection: 'community',
|
|
76
73
|
ps_get_selection_preview: 'community',
|
|
77
74
|
ps_selection_channel: 'community',
|
|
@@ -89,7 +86,7 @@ export const TOOL_TIERS = {
|
|
|
89
86
|
ps_template_verify: 'pro',
|
|
90
87
|
ps_template_recall: 'pro',
|
|
91
88
|
ps_template_delete: 'pro',
|
|
92
|
-
|
|
89
|
+
ps_text: 'community',
|
|
93
90
|
};
|
|
94
91
|
export function tierOf(toolName) {
|
|
95
92
|
const tier = TOOL_TIERS[toolName];
|
|
@@ -67,7 +67,7 @@ export function decodeJpegBuffer(bytes) {
|
|
|
67
67
|
}
|
|
68
68
|
catch (err) {
|
|
69
69
|
const msg = err instanceof Error ? err.message : String(err);
|
|
70
|
-
throw new Error(`failed to decode JPEG buffer: ${msg}
|
|
70
|
+
throw new Error(`failed to decode JPEG buffer: ${msg}`, { cause: err });
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
export function decodeJpeg(path) {
|
|
@@ -76,6 +76,6 @@ export function decodeJpeg(path) {
|
|
|
76
76
|
}
|
|
77
77
|
catch (err) {
|
|
78
78
|
const msg = err instanceof Error ? err.message : String(err);
|
|
79
|
-
throw new Error(`failed to decode JPEG at ${path}: ${msg}
|
|
79
|
+
throw new Error(`failed to decode JPEG at ${path}: ${msg}`, { cause: err });
|
|
80
80
|
}
|
|
81
81
|
}
|
package/dist/modules/ce/index.js
CHANGED
|
@@ -29,6 +29,7 @@ import { createPathTools } from '../../tools/path-tools.js';
|
|
|
29
29
|
import { createVectorMaskTools } from '../../tools/vector-mask-tools.js';
|
|
30
30
|
import { createChannelComposeTools } from '../../tools/channel-compose-tools.js';
|
|
31
31
|
import { createShapeTools } from '../../tools/shape-tools.js';
|
|
32
|
+
import { createSkyTools } from '../../tools/sky-tools.js';
|
|
32
33
|
const ceFactories = [
|
|
33
34
|
createDocumentTools,
|
|
34
35
|
createLayerTools,
|
|
@@ -57,6 +58,7 @@ const ceFactories = [
|
|
|
57
58
|
createVectorMaskTools,
|
|
58
59
|
createChannelComposeTools,
|
|
59
60
|
createShapeTools,
|
|
61
|
+
createSkyTools,
|
|
60
62
|
];
|
|
61
63
|
export const ceModule = {
|
|
62
64
|
manifest: { id: 'ce', name: 'Editmamei Community Tools', abi: KERNEL_ABI },
|
|
@@ -13,6 +13,7 @@ export class PhotoshopConnection {
|
|
|
13
13
|
photoshopInfo = null;
|
|
14
14
|
detectInFlight = null;
|
|
15
15
|
runningLatchUntil = 0;
|
|
16
|
+
everReachedPhotoshop = false;
|
|
16
17
|
constructor(opts = {}) {
|
|
17
18
|
this.logger = new Logger('PhotoshopConnection');
|
|
18
19
|
this.now = opts.now ?? Date.now;
|
|
@@ -71,6 +72,7 @@ export class PhotoshopConnection {
|
|
|
71
72
|
}
|
|
72
73
|
const result = await this.host.adapter.run(script, timeout);
|
|
73
74
|
this.runningLatchUntil = this.now() + RUNNING_LATCH_TTL_MS;
|
|
75
|
+
this.everReachedPhotoshop = true;
|
|
74
76
|
return result;
|
|
75
77
|
}
|
|
76
78
|
catch (error) {
|
|
@@ -99,4 +101,17 @@ export class PhotoshopConnection {
|
|
|
99
101
|
getPhotoshopInfo() {
|
|
100
102
|
return this.photoshopInfo;
|
|
101
103
|
}
|
|
104
|
+
hasReachedPhotoshop() {
|
|
105
|
+
return this.everReachedPhotoshop;
|
|
106
|
+
}
|
|
107
|
+
async isCurrentlyRunning() {
|
|
108
|
+
if (!this.photoshopInfo)
|
|
109
|
+
return false;
|
|
110
|
+
try {
|
|
111
|
+
return await this.host.adapter.isRunning();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
102
117
|
}
|
|
Binary file
|
package/dist/telemetry/client.js
CHANGED
|
@@ -118,6 +118,19 @@ export class TelemetryClient {
|
|
|
118
118
|
...(diag.stderr_tail ? { stderr_tail: sanitizeStderrTail(diag.stderr_tail) } : {}),
|
|
119
119
|
}, this.now()));
|
|
120
120
|
}
|
|
121
|
+
restampPsVersion(events) {
|
|
122
|
+
const resolved = this.dims.getPsVersion();
|
|
123
|
+
if (!resolved)
|
|
124
|
+
return events;
|
|
125
|
+
for (const event of events) {
|
|
126
|
+
if (event.type === 'session_summary')
|
|
127
|
+
continue;
|
|
128
|
+
if ('ps_version' in event && event.ps_version === PS_VERSION_UNKNOWN) {
|
|
129
|
+
event.ps_version = resolved;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return events;
|
|
133
|
+
}
|
|
121
134
|
enqueue(event) {
|
|
122
135
|
this.queue.push(event);
|
|
123
136
|
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
@@ -129,7 +142,7 @@ export class TelemetryClient {
|
|
|
129
142
|
async flush() {
|
|
130
143
|
if (!this.active || this.queue.length === 0)
|
|
131
144
|
return;
|
|
132
|
-
const batch = this.queue.splice(0, this.maxBatchSize).filter(isContentSafe);
|
|
145
|
+
const batch = this.restampPsVersion(this.queue.splice(0, this.maxBatchSize)).filter(isContentSafe);
|
|
133
146
|
if (batch.length === 0)
|
|
134
147
|
return;
|
|
135
148
|
try {
|
|
@@ -160,7 +173,7 @@ export class TelemetryClient {
|
|
|
160
173
|
}, this.now()));
|
|
161
174
|
}
|
|
162
175
|
if (this.queue.length > 0) {
|
|
163
|
-
appendOutboxSync(this.queue.splice(0).filter(isContentSafe), this.outboxOpts);
|
|
176
|
+
appendOutboxSync(this.restampPsVersion(this.queue.splice(0)).filter(isContentSafe), this.outboxOpts);
|
|
164
177
|
}
|
|
165
178
|
clearSessionState(this.outboxOpts);
|
|
166
179
|
}
|
|
@@ -45,6 +45,7 @@ export function sanitizeMessage(input, maxLen = MAX_MESSAGE_LEN) {
|
|
|
45
45
|
}
|
|
46
46
|
out = redactGenericUserDirs(out);
|
|
47
47
|
out = basenamePaths(out);
|
|
48
|
+
out = out.replace(/\bnot found: [^\n]*/gi, 'not found: [redacted]');
|
|
48
49
|
out = out.replace(/\\/g, '/');
|
|
49
50
|
out = out.replace(/^[/]+/, '');
|
|
50
51
|
if (out.length > maxLen)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { runScript } from '../utils/run-script.js';
|
|
2
2
|
import { validateArgs } from '../utils/validate.js';
|
|
3
3
|
import { toolErrorResult, runSnippetTool, applyToActiveLayerProp } from '../utils/tool-helpers.js';
|
|
4
|
+
import { getPendingRawDevelop } from '../core/raw-develop-state.js';
|
|
4
5
|
const addAdjustmentLayerSchema = {
|
|
5
6
|
type: 'object',
|
|
6
7
|
properties: {
|
|
@@ -669,7 +670,7 @@ export function createAdjustmentTools(connection, snippetClient) {
|
|
|
669
670
|
{
|
|
670
671
|
tool: {
|
|
671
672
|
name: 'ps_add_adjustment_layer',
|
|
672
|
-
description: "Create a non-destructive adjustment layer above the active layer — hoisted out of the active layer's group by default even though Photoshop's own Mk-AdjL placement rule would otherwise nest it INSIDE that group (pass into_active_group:true to keep that native nesting). Supports the full real-Photoshop tonal/color surface: Curves (with S-curve presets), Levels, Hue/Saturation, Brightness/Contrast, Black & White (with optional tint), Color Balance, Photo Filter (preset or custom color), Vibrance, Channel Mixer, Selective Color, Gradient Map (preset), Exposure (stops + offset + gamma), Color Lookup (3DLUT presets or custom file path), and Invert. Values are editable, maskable, and removable. This is the canonical entry point for
|
|
673
|
+
description: "Create a non-destructive adjustment layer above the active layer — hoisted out of the active layer's group by default even though Photoshop's own Mk-AdjL placement rule would otherwise nest it INSIDE that group (pass into_active_group:true to keep that native nesting). Supports the full real-Photoshop tonal/color surface: Curves (with S-curve presets), Levels, Hue/Saturation, Brightness/Contrast, Black & White (with optional tint), Color Balance, Photo Filter (preset or custom color), Vibrance, Channel Mixer, Selective Color, Gradient Map (preset), Exposure (stops + offset + gamma), Color Lookup (3DLUT presets or custom file path), and Invert. Values are editable, maskable, and removable. This is the canonical entry point for tonal/color adjustments — EXCEPT global tone/color on raw-sourced documents, which starts with a Camera Raw develop pass when a camera-raw develop tool is registered (see ps_open_document's is_raw_source); the old destructive bake tools (auto_levels / auto_contrast / desaturate / invert) were removed on 2026-05-31 — if you genuinely need a pixel bake, follow this call with `ps_merge` (mode=visible). Optionally clips the adjustment to only affect the layer directly below it. If a selection is active at call time, the new layer is automatically masked by it (toggle with mask_from_selection / mask_inverted). For destructive ops that don't have an adjustment-layer equivalent in Photoshop (Shadows/Highlights — single-pass shadow/highlight recovery), use `ps_apply_adjustment` (type=shadows_highlights) which auto-duplicates the active layer to keep the original intact. Returns context (the new adjustment layer becomes active) plus parent_path — the actual containing-group chain, so placement is never silent.",
|
|
673
674
|
inputSchema: addAdjustmentLayerSchema,
|
|
674
675
|
outputSchema: {
|
|
675
676
|
type: 'object',
|
|
@@ -695,6 +696,10 @@ export function createAdjustmentTools(connection, snippetClient) {
|
|
|
695
696
|
items: { type: 'string' },
|
|
696
697
|
description: 'The containing-group name chain (outermost first), empty array at the document root.',
|
|
697
698
|
},
|
|
699
|
+
raw_develop_pending: {
|
|
700
|
+
type: 'string',
|
|
701
|
+
description: 'Advisory, present only when the active document was opened from a raw source this session and no Camera Raw develop pass has run yet. Nothing failed — the layer was created. Global tone/color on raw sources normally starts with the camera-raw develop pass; ignore if the user prescribed this exact layer or the file was already developed elsewhere.',
|
|
702
|
+
},
|
|
698
703
|
context: { type: 'object' },
|
|
699
704
|
},
|
|
700
705
|
},
|
|
@@ -833,14 +838,27 @@ async function addAdjustmentLayer(connection, snippetClient, rawArgs) {
|
|
|
833
838
|
buildParams.name = name;
|
|
834
839
|
const script = await snippetClient.build('addAdjustmentLayer', buildParams);
|
|
835
840
|
const result = await runScript(connection, script);
|
|
841
|
+
const structured = result;
|
|
842
|
+
let advisory = '';
|
|
843
|
+
const pendingRaw = getPendingRawDevelop();
|
|
844
|
+
if (pendingRaw) {
|
|
845
|
+
const source = pendingRaw.documentName || pendingRaw.filePath || 'a raw source';
|
|
846
|
+
const note = `This document was opened from raw source ${source} with no Camera Raw develop pass yet — ` +
|
|
847
|
+
'consider ps_apply_camera_raw first for global tone/color. Nothing failed; ignore if the ' +
|
|
848
|
+
'user prescribed this exact layer or the file was already developed elsewhere.';
|
|
849
|
+
if (typeof result === 'object' && result !== null) {
|
|
850
|
+
structured.raw_develop_pending = note;
|
|
851
|
+
}
|
|
852
|
+
advisory = `\nNote: ${note}`;
|
|
853
|
+
}
|
|
836
854
|
return {
|
|
837
855
|
content: [
|
|
838
856
|
{
|
|
839
857
|
type: 'text',
|
|
840
|
-
text: `Adjustment layer added:\n${JSON.stringify(result, null, 2)}`,
|
|
858
|
+
text: `Adjustment layer added:\n${JSON.stringify(result, null, 2)}${advisory}`,
|
|
841
859
|
},
|
|
842
860
|
],
|
|
843
|
-
structuredContent:
|
|
861
|
+
structuredContent: structured,
|
|
844
862
|
};
|
|
845
863
|
}
|
|
846
864
|
catch (error) {
|
|
@@ -174,7 +174,7 @@ export function createDetectionTools(connection, _snippetClient, client = new On
|
|
|
174
174
|
{
|
|
175
175
|
tool: {
|
|
176
176
|
name: 'ps_detect',
|
|
177
|
-
description: '
|
|
177
|
+
description: 'The cheap, narrow read: labeled bounding boxes only — faces and/or COCO-80 objects (person, dog, car, chair, sofa, …) in DOCUMENT-pixel space, plus an annotated preview for visual confirmation. LOCAL on-device computer vision; the image is never sent anywhere. Use this for real coordinates before a spatially-targeted edit when boxes are all you need — far more reliable than estimating positions from a preview. For the full scene model (regions, horizon, tonal zones, composition, and a menu of selectable named regions), use ps_read_scene instead. `target` selects faces / objects / both. Read-only: renders a throwaway duplicate, never modifies the working document. Boxes are [x1, y1, x2, y2]. Validate surprising results against the annotated image before acting on them.',
|
|
178
178
|
inputSchema: detectSchema,
|
|
179
179
|
outputSchema: {
|
|
180
180
|
type: 'object',
|
|
@@ -205,7 +205,7 @@ export function createDocumentTools(connection, snippetClient) {
|
|
|
205
205
|
{
|
|
206
206
|
tool: {
|
|
207
207
|
name: 'ps_open_document',
|
|
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).',
|
|
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. `is_raw_source` is workflow-critical, not a passive status field: when true, the first edit should be a Camera Raw develop pass on the base smart object (via a camera-raw develop tool, if one is registered in tools/list) — NOT stacked tonal adjustment layers. 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).',
|
|
209
209
|
inputSchema: openDocumentSchema,
|
|
210
210
|
outputSchema: {
|
|
211
211
|
type: 'object',
|
|
@@ -217,7 +217,10 @@ export function createDocumentTools(connection, snippetClient) {
|
|
|
217
217
|
resolution: { type: 'number' },
|
|
218
218
|
color_mode: { type: 'string' },
|
|
219
219
|
bits_per_channel: { type: 'number' },
|
|
220
|
-
is_raw_source: {
|
|
220
|
+
is_raw_source: {
|
|
221
|
+
type: 'boolean',
|
|
222
|
+
description: 'True when the source file was a raw capture (DNG/NEF/CR3/ARW/…). Workflow-critical: the open used last-used/default Camera Raw settings, so no deliberate develop has happened yet. When true, run the Camera Raw develop pass FIRST (via a camera-raw develop tool, if registered) for global tone/color — before any tonal adjustment layers — unless the user explicitly directs otherwise.',
|
|
223
|
+
},
|
|
221
224
|
already_open: { type: 'boolean' },
|
|
222
225
|
file_path: { type: 'string' },
|
|
223
226
|
context: { type: 'object' },
|