editmamei 1.5.1 → 1.6.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 +16 -1
- package/dist/backends/detect-editors.js +79 -0
- package/dist/backends/gimp/backend.js +96 -0
- package/dist/backends/gimp/bridge/lib.py +1229 -0
- package/dist/backends/gimp/bridge/ops.py +1501 -0
- package/dist/backends/gimp/detect.js +86 -0
- package/dist/backends/gimp/errors.js +21 -0
- package/dist/backends/gimp/launcher.js +6 -0
- package/dist/backends/gimp/session.js +629 -0
- 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 +28 -0
- package/dist/core/server.js +94 -49
- package/dist/core/settings.js +25 -0
- package/dist/core/tool-activity.js +114 -0
- package/dist/core/tool-groups.js +16 -0
- package/dist/core/tool-tiers.js +16 -0
- package/dist/diagnostics/collect.js +4 -0
- package/dist/index.js +5 -1
- package/dist/kernel/kernel.js +3 -0
- package/dist/modules/gimp/index.js +35 -0
- package/dist/skills/editmamei-skill.zip +0 -0
- package/dist/telemetry/activity.js +1 -93
- package/dist/tools/adjustment-tools.js +2 -1
- package/dist/tools/gimp-adjustment-tools.js +382 -0
- package/dist/tools/gimp-core-tools.js +288 -0
- package/dist/tools/gimp-document-tools.js +321 -0
- package/dist/tools/gimp-filter-tools.js +93 -0
- package/dist/tools/gimp-geometry-tools.js +191 -0
- package/dist/tools/gimp-inspect-tools.js +70 -0
- package/dist/tools/gimp-mask-tools.js +102 -0
- package/dist/tools/gimp-shared.js +51 -0
- package/dist/tools/gimp-verify-tools.js +374 -0
- package/dist/utils/gimp-path.js +27 -0
- package/dist/utils/operation-timeouts.js +16 -0
- package/dist/utils/session-log.js +19 -1
- package/dist/utils/tool-helpers.js +14 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ These are the choices that shape the surface, and the reason an AI assistant can
|
|
|
75
75
|
|
|
76
76
|
## Tool surface
|
|
77
77
|
|
|
78
|
-
**
|
|
78
|
+
**103 tools total.** The Photoshop surface is **87 tools across 16 capability groups** (62 Community, 25 Pro), every one namespaced `ps_*` and discoverable at runtime via `tools/list`. Community tools ship in both editions; Pro tools unlock with a license. Alongside it, a second, independent editor surface drives headless GIMP: **16 `gimp_*` tools, Community, currently in beta**, see [GIMP (beta)](#gimp-beta) below. Pro's additions are Photoshop-only; GIMP has no Pro tier.
|
|
79
79
|
|
|
80
80
|
| Group | Edition | Tools |
|
|
81
81
|
| --- | --- | --- |
|
|
@@ -101,15 +101,29 @@ These are the choices that shape the surface, and the reason an AI assistant can
|
|
|
101
101
|
| **Templates** | Pro | `template_create_evidence` · `template_save` · `template_list` · `template_apply` · `template_verify` · `template_recall` · `template_delete` |
|
|
102
102
|
| **Automation** | Pro | `list_actions` · `play_action` · `execute_script` · `batch` |
|
|
103
103
|
|
|
104
|
+
**GIMP (beta), a separate namespace, all Community:**
|
|
105
|
+
|
|
106
|
+
| Group | Edition | Tools |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| **GIMP** | Community (beta) | `gimp_ping` · `gimp_overview` · `gimp_open_document` · `gimp_close_document` · `gimp_save_xcf` · `gimp_export` · `gimp_inspect` · `gimp_add_adjustment` · `gimp_filter` · `gimp_crop_document` · `gimp_resize_image` · `gimp_transform_canvas` · `gimp_create_mask` · `gimp_get_preview` · `gimp_get_histogram` · `gimp_compare` |
|
|
109
|
+
|
|
104
110
|
## Editions
|
|
105
111
|
|
|
106
112
|
- **Community** is free and covers the everyday editing surface: documents, layers, selections (including AI Select Subject and Select Sky), non-destructive adjustments, filters, masks, type, retouching, on-device perception, and the inspect/verify primitives.
|
|
107
113
|
- **Pro** adds Camera Raw develop, the grounded precision tools (warp, named-object masks, precision placement), subject-instance targeting, face-mesh perception, the reproducible-template system, and Photoshop Actions plus the scripting escape hatch.
|
|
108
114
|
|
|
115
|
+
Pro is a Photoshop-only upgrade: it adds nothing to the GIMP surface below.
|
|
116
|
+
|
|
109
117
|
The split is detailed in [pro-features.md](docs/pro-features.md). Pricing is at [editmamei.com](https://editmamei.com).
|
|
110
118
|
|
|
111
119
|
This repository holds the Community source. Pro is a separately licensed module and its source is not published here; Community never imports it, so this tree builds and runs on its own.
|
|
112
120
|
|
|
121
|
+
## GIMP (beta)
|
|
122
|
+
|
|
123
|
+
Editmamei can also drive GIMP, headless, as a second editor alongside (or instead of) Photoshop. When GIMP 3.2+ is detected on your machine, a `gimp_*` tool set registers next to the `ps_*` one; use it when you ask for GIMP, or when Photoshop isn't available. There's no visible window: edits happen in a background `gimp-console` process, and you follow along through rendered previews in the conversation instead of watching a window change.
|
|
124
|
+
|
|
125
|
+
The beta covers documents (open, save `.xcf`, export), 13 non-destructive adjustment types, geometric masks, crop/resize/rotate/flip, and the same preview/histogram/compare verification primitives the Photoshop side has. It doesn't yet cover heal/clone, AI subject or sky selection, text, or undo for geometry changes. Full details, requirements, and troubleshooting: [docs/gimp.md](docs/gimp.md).
|
|
126
|
+
|
|
113
127
|
## Build from source
|
|
114
128
|
|
|
115
129
|
```bash
|
|
@@ -151,6 +165,7 @@ The source in this repository is the same code published to npm, so none of the
|
|
|
151
165
|
- **Getting started:** [docs/getting-started.md](docs/getting-started.md)
|
|
152
166
|
- **FAQ:** [docs/faq.md](docs/faq.md)
|
|
153
167
|
- **Troubleshooting:** [docs/troubleshooting.md](docs/troubleshooting.md)
|
|
168
|
+
- **GIMP (beta):** [docs/gimp.md](docs/gimp.md)
|
|
154
169
|
- **Pro features:** [docs/pro-features.md](docs/pro-features.md)
|
|
155
170
|
- **Roadmap:** [docs/roadmap.md](docs/roadmap.md)
|
|
156
171
|
- **Bugs and feature requests:** [the issue tracker](https://github.com/editmamei/editmamei/issues). If something's broken, ask your assistant to "report a problem" (or run `editmamei report`) to drop an anonymized diagnostic bundle in your Downloads folder, then attach it to the issue.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { detectGimp } from './gimp/detect.js';
|
|
2
|
+
import { applyEditorEnvOverride } from '../core/settings.js';
|
|
3
|
+
import { TOOL_TIERS, isToolAllowedInEdition } from '../core/tool-tiers.js';
|
|
4
|
+
import { EDITION } from '../edition.js';
|
|
5
|
+
import { Logger } from '../utils/logger.js';
|
|
6
|
+
const logger = new Logger('DetectEditors');
|
|
7
|
+
function anyGimpToolAllowedInEdition(edition) {
|
|
8
|
+
return Object.keys(TOOL_TIERS).some((name) => name.startsWith('gimp_') && isToolAllowedInEdition(name, edition));
|
|
9
|
+
}
|
|
10
|
+
export async function detectEditors(opts = {}) {
|
|
11
|
+
const budgetMs = opts.budgetMs ?? 750;
|
|
12
|
+
const gimpDetector = opts.detectGimp ?? detectGimp;
|
|
13
|
+
let gimp = null;
|
|
14
|
+
const gimpProbe = gimpDetector().then((install) => {
|
|
15
|
+
gimp = install;
|
|
16
|
+
}, () => {
|
|
17
|
+
gimp = null;
|
|
18
|
+
});
|
|
19
|
+
let timer;
|
|
20
|
+
const timeout = new Promise((resolve) => {
|
|
21
|
+
timer = setTimeout(() => resolve('timeout'), budgetMs);
|
|
22
|
+
});
|
|
23
|
+
const winner = await Promise.race([gimpProbe.then(() => 'settled'), timeout]);
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
return { gimp, timedOut: winner === 'timeout' };
|
|
26
|
+
}
|
|
27
|
+
export function resolveEditorRegistration(detected, pin = 'auto', gimpToolsAllowed = true) {
|
|
28
|
+
if (pin === 'gimp') {
|
|
29
|
+
if (!gimpToolsAllowed) {
|
|
30
|
+
logger.warn("editor pinned to 'gimp', but no gimp_* tool ships in this build — falling back to " +
|
|
31
|
+
'registering ps_* instead of leaving the tool surface almost empty.');
|
|
32
|
+
return {
|
|
33
|
+
registerPhotoshop: true,
|
|
34
|
+
registerGimp: false,
|
|
35
|
+
gimpInstall: null,
|
|
36
|
+
gimpDetectionTimedOut: false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
registerPhotoshop: false,
|
|
41
|
+
registerGimp: true,
|
|
42
|
+
gimpInstall: detected.gimp,
|
|
43
|
+
gimpDetectionTimedOut: detected.timedOut,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (pin === 'photoshop') {
|
|
47
|
+
return {
|
|
48
|
+
registerPhotoshop: true,
|
|
49
|
+
registerGimp: false,
|
|
50
|
+
gimpInstall: null,
|
|
51
|
+
gimpDetectionTimedOut: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const gimpFound = detected.gimp !== null;
|
|
55
|
+
return {
|
|
56
|
+
registerPhotoshop: true,
|
|
57
|
+
registerGimp: gimpFound,
|
|
58
|
+
gimpInstall: gimpFound ? detected.gimp : null,
|
|
59
|
+
gimpDetectionTimedOut: false,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export async function resolveBootEditors(opts) {
|
|
63
|
+
const env = opts.env ?? process.env;
|
|
64
|
+
const effectiveSettings = applyEditorEnvOverride(opts.settings, env);
|
|
65
|
+
const envGimpPath = env.EDITMAMEI_GIMP_PATH;
|
|
66
|
+
const gimpPathOverride = envGimpPath && envGimpPath.length > 0
|
|
67
|
+
? envGimpPath
|
|
68
|
+
: (effectiveSettings.gimp_path ?? undefined);
|
|
69
|
+
const detectGimpOverride = gimpPathOverride
|
|
70
|
+
? (o) => detectGimp({ ...o, env: { ...(o?.env ?? env), EDITMAMEI_GIMP_PATH: gimpPathOverride } })
|
|
71
|
+
: undefined;
|
|
72
|
+
const detectEditorsFn = opts.detectEditorsFn ?? detectEditors;
|
|
73
|
+
const gimpToolsAllowed = anyGimpToolAllowedInEdition(EDITION);
|
|
74
|
+
const detected = gimpToolsAllowed
|
|
75
|
+
? await detectEditorsFn({ detectGimp: detectGimpOverride })
|
|
76
|
+
: { gimp: null, timedOut: false };
|
|
77
|
+
const decision = resolveEditorRegistration(detected, effectiveSettings.editor, gimpToolsAllowed);
|
|
78
|
+
return gimpPathOverride ? { ...decision, gimpPathOverride } : decision;
|
|
79
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { GimpSession } from './session.js';
|
|
2
|
+
import { detectGimp } from './detect.js';
|
|
3
|
+
import { GimpError } from './errors.js';
|
|
4
|
+
import { currentToolBudget } from '../../utils/tool-budget-context.js';
|
|
5
|
+
export class GimpBackend {
|
|
6
|
+
installInfo;
|
|
7
|
+
sessionFactory;
|
|
8
|
+
sessionOptions;
|
|
9
|
+
detectGimpFn;
|
|
10
|
+
redetectPending;
|
|
11
|
+
session;
|
|
12
|
+
closed = false;
|
|
13
|
+
constructor(install, opts = {}) {
|
|
14
|
+
this.installInfo = install;
|
|
15
|
+
this.sessionFactory = opts.sessionFactory ?? ((o) => new GimpSession(o));
|
|
16
|
+
this.sessionOptions = opts.sessionOptions ?? {};
|
|
17
|
+
const gimpPath = opts.gimpPath;
|
|
18
|
+
this.detectGimpFn =
|
|
19
|
+
opts.detectGimpFn ??
|
|
20
|
+
(gimpPath
|
|
21
|
+
? () => detectGimp({ env: { ...process.env, EDITMAMEI_GIMP_PATH: gimpPath } })
|
|
22
|
+
: () => detectGimp());
|
|
23
|
+
this.redetectPending = opts.gimpDetectionTimedOut === true && install === null;
|
|
24
|
+
}
|
|
25
|
+
get installed() {
|
|
26
|
+
return this.installInfo !== null;
|
|
27
|
+
}
|
|
28
|
+
get install() {
|
|
29
|
+
return this.installInfo;
|
|
30
|
+
}
|
|
31
|
+
get gimpVersion() {
|
|
32
|
+
return this.session?.gimpVersion;
|
|
33
|
+
}
|
|
34
|
+
get state() {
|
|
35
|
+
return this.session?.state ?? 'idle';
|
|
36
|
+
}
|
|
37
|
+
get startOrigin() {
|
|
38
|
+
return this.session?.lastStartOrigin;
|
|
39
|
+
}
|
|
40
|
+
ensureSession() {
|
|
41
|
+
if (this.closed) {
|
|
42
|
+
throw new GimpError('gimp_session_restarted', 'session closed');
|
|
43
|
+
}
|
|
44
|
+
if (!this.installInfo) {
|
|
45
|
+
throw new GimpError('gimp_not_installed', 'no GIMP install was found on this machine. Install GIMP 3.2 or newer, or point at a ' +
|
|
46
|
+
'gimp-console binary with the EDITMAMEI_GIMP_PATH environment variable, or by running ' +
|
|
47
|
+
'`editmamei config set gimp_path <path>` (the gimp_path key in settings.json).');
|
|
48
|
+
}
|
|
49
|
+
if (!this.session) {
|
|
50
|
+
this.session = this.sessionFactory({ install: this.installInfo, ...this.sessionOptions });
|
|
51
|
+
}
|
|
52
|
+
return this.session;
|
|
53
|
+
}
|
|
54
|
+
async maybeRedetect() {
|
|
55
|
+
if (!this.redetectPending)
|
|
56
|
+
return;
|
|
57
|
+
this.redetectPending = false;
|
|
58
|
+
if (this.installInfo !== null)
|
|
59
|
+
return;
|
|
60
|
+
const found = await this.detectGimpFn();
|
|
61
|
+
if (found)
|
|
62
|
+
this.installInfo = found;
|
|
63
|
+
}
|
|
64
|
+
async call(op, args = {}, timeoutMs) {
|
|
65
|
+
await this.maybeRedetect();
|
|
66
|
+
let effectiveTimeoutMs = timeoutMs;
|
|
67
|
+
if (effectiveTimeoutMs === undefined) {
|
|
68
|
+
const budget = currentToolBudget();
|
|
69
|
+
if (budget) {
|
|
70
|
+
const remaining = budget.deadline - Date.now();
|
|
71
|
+
if (remaining <= 0) {
|
|
72
|
+
throw new GimpError('gimp_timeout', `Tool '${budget.toolName}' exceeded its ${budget.budgetMs}ms budget before ${op} could run.`);
|
|
73
|
+
}
|
|
74
|
+
effectiveTimeoutMs = remaining;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return this.ensureSession().call(op, args, { timeoutMs: effectiveTimeoutMs });
|
|
78
|
+
}
|
|
79
|
+
async prepare() {
|
|
80
|
+
await this.maybeRedetect();
|
|
81
|
+
}
|
|
82
|
+
latestPreviewPath() {
|
|
83
|
+
return this.ensureSession().latestPreviewPath();
|
|
84
|
+
}
|
|
85
|
+
copyToLatestPreview(src) {
|
|
86
|
+
this.ensureSession().copyToLatestPreview(src);
|
|
87
|
+
}
|
|
88
|
+
tempPath(name) {
|
|
89
|
+
return this.ensureSession().tempPath(name);
|
|
90
|
+
}
|
|
91
|
+
async shutdown() {
|
|
92
|
+
this.closed = true;
|
|
93
|
+
if (this.session)
|
|
94
|
+
await this.session.shutdown();
|
|
95
|
+
}
|
|
96
|
+
}
|