editmamei 1.6.0 → 1.7.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.
@@ -122,6 +122,9 @@ export class GimpSession {
122
122
  get lastStartOrigin() {
123
123
  return this.startOrigin;
124
124
  }
125
+ get generation() {
126
+ return this.deadGeneration;
127
+ }
125
128
  latestPreviewPath() {
126
129
  return join(this.rootDir, 'latest-preview.jpg');
127
130
  }
Binary file
Binary file
@@ -0,0 +1,54 @@
1
+ function isImageBlock(value) {
2
+ if (typeof value !== 'object' || value === null)
3
+ return false;
4
+ const block = value;
5
+ if (block.type === 'image') {
6
+ return typeof block.data === 'string' && typeof block.mimeType === 'string';
7
+ }
8
+ return (block.type === 'resource' &&
9
+ typeof block.resource?.blob === 'string' &&
10
+ typeof block.resource.mimeType === 'string' &&
11
+ block.resource.mimeType.startsWith('image/'));
12
+ }
13
+ function strip(value, counter) {
14
+ if (Array.isArray(value)) {
15
+ const out = [];
16
+ for (const item of value) {
17
+ if (isImageBlock(item))
18
+ counter.n++;
19
+ else
20
+ out.push(strip(item, counter));
21
+ }
22
+ return out;
23
+ }
24
+ if (typeof value === 'object' && value !== null) {
25
+ const out = {};
26
+ for (const [k, v] of Object.entries(value))
27
+ out[k] = strip(v, counter);
28
+ return out;
29
+ }
30
+ return value;
31
+ }
32
+ export function hasImageContent(result) {
33
+ const counter = { n: 0 };
34
+ strip(result, counter);
35
+ return counter.n > 0;
36
+ }
37
+ export function withholdImages(result) {
38
+ const counter = { n: 0 };
39
+ const stripped = strip(result, counter);
40
+ if (counter.n === 0)
41
+ return result;
42
+ const what = counter.n === 1 ? 'An image was' : `${counter.n} images were`;
43
+ return {
44
+ ...stripped,
45
+ content: [
46
+ ...(stripped.content ?? []),
47
+ {
48
+ type: 'text',
49
+ text: `${what} withheld because the user's privacy.send_previews_to_llm setting is off. ` +
50
+ 'Work from the text and numbers; ask the user if you need to see the image.',
51
+ },
52
+ ],
53
+ };
54
+ }
@@ -6,6 +6,7 @@ import { ToolRegistry } from './tool-registry.js';
6
6
  import { tierOf } from './tool-tiers.js';
7
7
  import { groupOf, GROUPS } from './tool-groups.js';
8
8
  import { CAMERA_RAW_TOOL, RAW_DEVELOP_TOOL } from './tool-activity.js';
9
+ import { hasImageContent, withholdImages } from './preview-privacy.js';
9
10
  import { EDITION } from '../edition.js';
10
11
  import { VERSION } from '../version.js';
11
12
  import { Session } from './session.js';
@@ -90,6 +91,7 @@ export class EditmameiServer {
90
91
  updateInfo = null;
91
92
  updateCheck = null;
92
93
  updateNoticeShown = false;
94
+ previewsAllowed = () => loadSettings().settings.privacy.send_previews_to_llm;
93
95
  snippetClient = new GoSnippetClient();
94
96
  refreshLicenseOnPing = createPingLicenseRefresher();
95
97
  moduleUpdateFailed = false;
@@ -436,6 +438,8 @@ export class EditmameiServer {
436
438
  const result = await this.toolRegistry.execute(name, args);
437
439
  this.session.updateActivity();
438
440
  this.trackRawDevelopState(name, result);
441
+ if (hasImageContent(result) && !this.previewsAllowed())
442
+ return withholdImages(result);
439
443
  return result;
440
444
  }
441
445
  catch (error) {
@@ -104,11 +104,19 @@ export const MUTATING_TOOLS = new Set([
104
104
  'gimp_open_document',
105
105
  'gimp_close_document',
106
106
  'gimp_add_adjustment',
107
+ 'gimp_add_effect',
107
108
  'gimp_filter',
108
109
  'gimp_crop_document',
109
110
  'gimp_resize_image',
110
111
  'gimp_transform_canvas',
111
112
  'gimp_create_mask',
113
+ 'gimp_checkpoint',
114
+ 'gimp_layer',
115
+ 'gimp_bake',
116
+ 'gimp_create_document',
117
+ 'gimp_place_image',
118
+ 'gimp_canvas',
119
+ 'gimp_convert_image_mode',
112
120
  ]);
113
121
  export const RAW_DEVELOP_TOOL = 'ps_develop_raw';
114
122
  export const CAMERA_RAW_TOOL = 'ps_apply_camera_raw';
@@ -186,7 +186,15 @@ export const TOOL_GROUPS = {
186
186
  gimp_transform_canvas: 'document',
187
187
  gimp_add_adjustment: 'adjust',
188
188
  gimp_filter: 'filter',
189
+ gimp_add_effect: 'filter',
189
190
  gimp_create_mask: 'masks',
191
+ gimp_checkpoint: 'document',
192
+ gimp_layer: 'layers',
193
+ gimp_bake: 'layers',
194
+ gimp_create_document: 'document',
195
+ gimp_place_image: 'layers',
196
+ gimp_canvas: 'document',
197
+ gimp_convert_image_mode: 'document',
190
198
  };
191
199
  export function groupOf(toolName) {
192
200
  const group = TOOL_GROUPS[toolName];
@@ -98,6 +98,7 @@ export const TOOL_TIERS = {
98
98
  gimp_inspect: 'community',
99
99
  gimp_add_adjustment: 'community',
100
100
  gimp_filter: 'community',
101
+ gimp_add_effect: 'community',
101
102
  gimp_crop_document: 'community',
102
103
  gimp_resize_image: 'community',
103
104
  gimp_transform_canvas: 'community',
@@ -105,6 +106,13 @@ export const TOOL_TIERS = {
105
106
  gimp_get_preview: 'community',
106
107
  gimp_get_histogram: 'community',
107
108
  gimp_compare: 'community',
109
+ gimp_checkpoint: 'community',
110
+ gimp_layer: 'community',
111
+ gimp_bake: 'community',
112
+ gimp_create_document: 'community',
113
+ gimp_place_image: 'community',
114
+ gimp_canvas: 'community',
115
+ gimp_convert_image_mode: 'community',
108
116
  };
109
117
  export function tierOf(toolName) {
110
118
  const tier = TOOL_TIERS[toolName];
@@ -5,19 +5,27 @@ import { createGimpCoreTools } from '../../tools/gimp-core-tools.js';
5
5
  import { createGimpDocumentTools } from '../../tools/gimp-document-tools.js';
6
6
  import { createGimpInspectTools } from '../../tools/gimp-inspect-tools.js';
7
7
  import { createGimpAdjustmentTools } from '../../tools/gimp-adjustment-tools.js';
8
+ import { createGimpEffectTools } from '../../tools/gimp-effect-tools.js';
8
9
  import { createGimpFilterTools } from '../../tools/gimp-filter-tools.js';
9
10
  import { createGimpGeometryTools } from '../../tools/gimp-geometry-tools.js';
10
11
  import { createGimpMaskTools } from '../../tools/gimp-mask-tools.js';
11
12
  import { createGimpVerifyTools } from '../../tools/gimp-verify-tools.js';
13
+ import { createGimpCheckpointTools } from '../../tools/gimp-checkpoint-tools.js';
14
+ import { createGimpLayerTools } from '../../tools/gimp-layer-tools.js';
15
+ import { createGimpComposeTools } from '../../tools/gimp-compose-tools.js';
12
16
  export const gimpFactories = [
13
17
  createGimpCoreTools,
14
18
  createGimpDocumentTools,
15
19
  createGimpInspectTools,
16
20
  createGimpAdjustmentTools,
21
+ createGimpEffectTools,
17
22
  createGimpFilterTools,
18
23
  createGimpGeometryTools,
19
24
  createGimpMaskTools,
20
25
  createGimpVerifyTools,
26
+ createGimpCheckpointTools,
27
+ createGimpLayerTools,
28
+ createGimpComposeTools,
21
29
  ];
22
30
  export const gimpModule = {
23
31
  manifest: { id: 'gimp', name: 'Editmamei GIMP Tools', abi: KERNEL_ABI },
Binary file
@@ -1,6 +1,6 @@
1
1
  import { validateArgs } from '../utils/validate.js';
2
2
  import { toolGimpErrorResult } from '../utils/tool-helpers.js';
3
- import { GIMP_IMAGE_PROP, GIMP_LAYER_PROP, pickSchemaDeclaredKeys } from './gimp-shared.js';
3
+ import { GIMP_IMAGE_PROP, GIMP_LAYER_ID_PROP, GIMP_LAYER_PROP, pickSchemaDeclaredKeys, } from './gimp-shared.js';
4
4
  const ADJUST_TYPES = [
5
5
  'curves',
6
6
  'levels',
@@ -34,6 +34,7 @@ const adjustSchema = {
34
34
  ' The filter applies to that ONE layer, not to the flattened image, so on a multi-layer ' +
35
35
  'document name the layer you mean. A layer inside a layer group can be named directly.',
36
36
  },
37
+ layer_id: GIMP_LAYER_ID_PROP,
37
38
  type: {
38
39
  type: 'string',
39
40
  enum: [...ADJUST_TYPES],
@@ -49,7 +50,8 @@ const adjustSchema = {
49
50
  'creation and cannot change on a re-edit. Only filters Editmamei created can be re-edited ' +
50
51
  '(gimp_filter op=list shows source: "editmamei"); re-editing any other filter (for example ' +
51
52
  'one added in the GIMP GUI) is refused, because its current values cannot be read back ' +
52
- 'exactly — delete it and re-create it instead.',
53
+ 'exactly — delete it and re-create it instead. A re-edit keeps the filter on its own ' +
54
+ 'layer; `layer` and `layer_id` are ignored.',
53
55
  },
54
56
  mask: {
55
57
  type: 'string',
@@ -338,7 +340,8 @@ export function createGimpAdjustmentTools(gimp) {
338
340
  'gimp_export, and gimp_filter op=delete removes it again. `type` picks the adjustment: ' +
339
341
  'curves, levels, exposure, brightness_contrast, hue_saturation, color_balance, ' +
340
342
  'color_temperature, shadows_highlights, saturation, vibrance, sharpen, noise_reduction, ' +
341
- 'gaussian_blur. A filter applies to ONE layer (`layer`, else the selected or top layer), ' +
343
+ "gaussian_blur. A field another type uses is refused, naming this type's own fields. A " +
344
+ 'filter applies to ONE layer (`layer_id` or `layer`, else the selected or top layer), ' +
342
345
  'not to the flattened image — on a multi-layer document, say which layer. For type ' +
343
346
  'vibrance, the knob is the `vibrance` field — `saturation` on that type is a separate ' +
344
347
  'plain multiplier that normally stays untouched. curves: ONE filter per channel — add ' +