editmamei 1.8.0 → 1.9.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.
@@ -0,0 +1,114 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ const GUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5
+ const NAME_KEY = Buffer.from('skpNTEXT', 'latin1');
6
+ const ID_KEY = Buffer.from('SzidTEXT', 'latin1');
7
+ export function prettySkyName(raw) {
8
+ return raw
9
+ .replace(/([a-z])([A-Z0-9])/g, '$1 $2')
10
+ .replace(/(\d)([A-Za-z])/g, '$1 $2')
11
+ .replace(/\s+/g, ' ')
12
+ .trim();
13
+ }
14
+ export function parseSkyPresets(buf, dir) {
15
+ const readText = (at) => {
16
+ if (at + 4 > buf.length)
17
+ return null;
18
+ const units = buf.readUInt32BE(at);
19
+ const end = at + 4 + units * 2;
20
+ if (units === 0 || units > 512 || end > buf.length)
21
+ return null;
22
+ let text = '';
23
+ for (let i = at + 4; i < end; i += 2) {
24
+ const unit = buf.readUInt16BE(i);
25
+ if (unit === 0)
26
+ break;
27
+ text += String.fromCharCode(unit);
28
+ }
29
+ return { text, end };
30
+ };
31
+ const out = [];
32
+ for (let pos = buf.indexOf(NAME_KEY); pos !== -1; pos = buf.indexOf(NAME_KEY, pos + NAME_KEY.length)) {
33
+ const name = readText(pos + NAME_KEY.length);
34
+ if (!name)
35
+ continue;
36
+ const rel = buf.subarray(name.end, name.end + 16 + ID_KEY.length).indexOf(ID_KEY);
37
+ if (rel === -1)
38
+ continue;
39
+ const id = readText(name.end + rel + ID_KEY.length);
40
+ if (!id || !GUID_RE.test(id.text))
41
+ continue;
42
+ const file = join(dir, `${id.text}.jpg`);
43
+ if (existsSync(file))
44
+ out.push({ name: prettySkyName(name.text), file });
45
+ }
46
+ return out;
47
+ }
48
+ export function findSkyPresetsDir(env = process.env, platform = process.platform, home = homedir()) {
49
+ const list = (dir) => {
50
+ try {
51
+ return readdirSync(dir);
52
+ }
53
+ catch {
54
+ return [];
55
+ }
56
+ };
57
+ let candidates = [];
58
+ if (platform === 'win32') {
59
+ if (!env.APPDATA)
60
+ return null;
61
+ const base = join(env.APPDATA, 'Adobe');
62
+ candidates = list(base).flatMap((d) => {
63
+ const m = /^Adobe Photoshop (\d{4})$/.exec(d);
64
+ return m ? [{ year: +m[1], dir: join(base, d, `${d} Settings`, 'Sky_Presets') }] : [];
65
+ });
66
+ }
67
+ else if (platform === 'darwin') {
68
+ const base = join(home, 'Library', 'Preferences');
69
+ candidates = list(base).flatMap((d) => {
70
+ const m = /^Adobe Photoshop (\d{4}) Settings$/.exec(d);
71
+ return m ? [{ year: +m[1], dir: join(base, d, 'Sky_Presets') }] : [];
72
+ });
73
+ }
74
+ candidates.sort((a, b) => b.year - a.year);
75
+ return candidates.find((c) => existsSync(join(c.dir, 'Skies.psp')))?.dir ?? null;
76
+ }
77
+ export function loadSkyPresets(dir = findSkyPresetsDir()) {
78
+ if (!dir)
79
+ return [];
80
+ try {
81
+ return parseSkyPresets(readFileSync(join(dir, 'Skies.psp')), dir);
82
+ }
83
+ catch {
84
+ return [];
85
+ }
86
+ }
87
+ const norm = (s) => s.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
88
+ const familyAndNumber = (s) => {
89
+ const m = /^(\p{L}+?)(\d+)/u.exec(norm(s));
90
+ return m ? { family: m[1], num: Number(m[2]) } : null;
91
+ };
92
+ export function matchSkyPreset(presets, query) {
93
+ const q = norm(query);
94
+ if (!q)
95
+ return undefined;
96
+ const exact = presets.find((p) => norm(p.name) === q);
97
+ if (exact)
98
+ return exact;
99
+ const want = familyAndNumber(query);
100
+ if (want) {
101
+ const hit = presets.find((p) => {
102
+ const have = familyAndNumber(p.name);
103
+ return have !== null && have.family.startsWith(want.family) && have.num === want.num;
104
+ });
105
+ if (hit)
106
+ return hit;
107
+ }
108
+ return presets.find((p) => norm(p.name).startsWith(q));
109
+ }
110
+ export function defaultSkyPreset(presets) {
111
+ return (presets.find((p) => /^blue sky\b/i.test(p.name) && /\bby photoshop$/i.test(p.name)) ??
112
+ presets.find((p) => /^blue sky\b/i.test(p.name)) ??
113
+ presets[0]);
114
+ }
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '1.8.0';
1
+ export const VERSION = '1.9.1';
package/package.json CHANGED
@@ -1,23 +1,22 @@
1
1
  {
2
2
  "name": "editmamei",
3
- "version": "1.8.0",
3
+ "version": "1.9.1",
4
4
  "description": "Photoshop MCP server: natural-language AI photo editing in desktop Photoshop (Community Edition)",
5
5
  "mcpName": "io.github.editmamei/editmamei",
6
6
  "editmamei": {
7
7
  "fixesByVersion": {
8
+ "1.9.1": [
9
+ "ps_replace_sky"
10
+ ],
11
+ "1.9.0": [
12
+ "ps_save_psd"
13
+ ],
8
14
  "1.4.0": [
9
15
  "ps_read_scene",
10
16
  "ps_selection_channel",
11
17
  "ps_select",
12
18
  "ps_modify_selection"
13
19
  ],
14
- "1.1.0": [
15
- "ps_create_clipping_mask"
16
- ],
17
- "1.2.0": [
18
- "ps_delete_layer",
19
- "ps_select_layer"
20
- ],
21
20
  "1.2.1": [
22
21
  "ps_read_scene"
23
22
  ]