@bubblydoo/uxp-toolkit 0.0.8 → 0.0.10
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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-3CLJMC63.js → chunk-JBXCIUBZ.js} +49 -90
- package/dist/commands-library/index.d.ts +38 -39
- package/dist/commands-library/index.js +171 -171
- package/dist/index.d.ts +41 -83
- package/dist/index.js +203 -152
- package/dist/{psLayerRef-OY3h7srv.d.ts → psLayerRef-CGbTO4a5.d.ts} +36 -37
- package/package.json +9 -7
- package/src/commands-library/addLayerToSelection.ts +4 -4
- package/src/commands-library/applyLayerMask.ts +2 -2
- package/src/commands-library/convertMode.ts +2 -2
- package/src/commands-library/createColorLookupAdjustmentLayer.ts +2 -2
- package/src/commands-library/expandFolder.ts +4 -4
- package/src/commands-library/exportLUTs.ts +4 -4
- package/src/commands-library/getDocument.ts +7 -7
- package/src/commands-library/getLayer.ts +13 -13
- package/src/commands-library/hasVectorMask.ts +2 -2
- package/src/commands-library/index.ts +18 -18
- package/src/commands-library/loadLayerMaskAsSelection.ts +2 -2
- package/src/commands-library/multiGetDocument.ts +14 -14
- package/src/commands-library/rasterizeLayerStyle.ts +3 -3
- package/src/commands-library/rasterizeVectorMask.ts +2 -2
- package/src/commands-library/removeLayerMask.ts +2 -2
- package/src/commands-library/renameLayer.ts +6 -6
- package/src/commands-library/renameLayer.uxp-test.ts +13 -13
- package/src/commands-library/renderGrid.ts +2 -2
- package/src/commands-library/selectLayer.ts +3 -3
- package/src/commands-library/set3DLUTColorLookup.ts +2 -2
- package/src/core/batchPlay.ts +3 -3
- package/src/core/command.test-d.ts +11 -8
- package/src/core/command.ts +20 -23
- package/src/core/executeAsModal.ts +11 -67
- package/src/core/suspendHistory.ts +3 -3
- package/src/core/suspendHistory.uxp-test.ts +8 -8
- package/src/core-wrappers/executeAsModalAndSuspendHistory.ts +8 -6
- package/src/dom/getFlattenedDomLayersList.ts +6 -7
- package/src/dom/photoshopDomLayersToTree.ts +5 -5
- package/src/error-sourcemaps/sourcemaps.ts +27 -26
- package/src/error-sourcemaps/sourcemaps.uxp-test.ts +9 -8
- package/src/errors/ut-error.ts +2 -2
- package/src/filesystem/isFileOrFolder.ts +9 -0
- package/src/filesystem/openFileByPath.ts +10 -6
- package/src/general-tree/flattenTree.ts +1 -1
- package/src/general-tree/layerRef.ts +2 -2
- package/src/general-tree/mapTree.ts +1 -1
- package/src/general-tree/mapTreeRef.ts +1 -1
- package/src/general-tree/treeTypes.ts +0 -2
- package/src/index.ts +42 -43
- package/src/metadata-storage/metadataStorage.ts +31 -31
- package/src/metadata-storage/metadataStorage.uxp-test.ts +11 -11
- package/src/node-compat/path/resolvePath.ts +6 -4
- package/src/other/applicationInfo.ts +11 -11
- package/src/other/applicationInfo.uxp-test.ts +6 -6
- package/src/other/clipboard.ts +2 -2
- package/src/other/clipboard.uxp-test.ts +9 -8
- package/src/other/uxpEntrypoints.ts +2 -2
- package/src/ut-tree/getDocumentLayerDescriptors.ts +6 -6
- package/src/ut-tree/getLayerEffects.ts +5 -5
- package/src/ut-tree/hasBackgroundLayer.uxp-test.ts +25 -23
- package/src/ut-tree/photoshopLayerDescriptorsToUTLayers.test.ts +29 -29
- package/src/ut-tree/photoshopLayerDescriptorsToUTLayers.ts +62 -62
- package/src/ut-tree/photoshopLayerDescriptorsToUTLayers.uxp-test.ts +29 -29
- package/src/ut-tree/psLayerRef.ts +2 -2
- package/src/ut-tree/utLayersToText.test.ts +32 -32
- package/src/ut-tree/utLayersToText.ts +19 -19
- package/src/ut-tree/utLayersToTree.ts +4 -4
- package/src/util/utLayerPickKeysType.ts +3 -3
- package/src/util/utLayerToLayer.ts +14 -13
- package/test/index.ts +11 -11
- package/test/meta-tests/builtinModules.uxp-test.ts +28 -26
- package/test/meta-tests/executeAsModal.uxp-test.ts +14 -12
- package/test/meta-tests/suspendHistory.uxp-test.ts +10 -9
- package/tsconfig.json +5 -5
- package/tsup.config.ts +5 -5
- package/typedoc.json +6 -0
- package/uxp-tests.json +1 -1
- package/vitest-photoshop-alias-plugin.ts +23 -21
- package/vitest.config.ts +6 -6
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type { Test } from
|
|
2
|
-
import { expect } from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { Test } from '@bubblydoo/uxp-test-framework';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { app } from 'photoshop';
|
|
4
|
+
import { suspendHistory } from './suspendHistory';
|
|
5
5
|
|
|
6
6
|
export const suspendHistoryTest: Test = {
|
|
7
|
-
name:
|
|
7
|
+
name: 'core/suspendHistory should return correctly',
|
|
8
8
|
async run() {
|
|
9
9
|
const document = app.activeDocument;
|
|
10
10
|
if (!document) {
|
|
11
|
-
throw new Error(
|
|
11
|
+
throw new Error('No active document');
|
|
12
12
|
}
|
|
13
|
-
const result = await suspendHistory(document,
|
|
13
|
+
const result = await suspendHistory(document, 'Test', async (context) => {
|
|
14
14
|
return 'test';
|
|
15
15
|
});
|
|
16
16
|
expect(result).to.equal('test');
|
|
17
17
|
},
|
|
18
|
-
};
|
|
18
|
+
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type { Document } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { Document } from 'photoshop';
|
|
2
|
+
import type { ExtendedExecutionContext } from '../core/executeAsModal';
|
|
3
|
+
import type { SuspendHistoryContext } from '../core/suspendHistory';
|
|
4
|
+
import { executeAsModal } from '../core/executeAsModal';
|
|
5
|
+
import { suspendHistory } from '../core/suspendHistory';
|
|
4
6
|
|
|
5
7
|
type CombinedFn<T> = (executionContext: ExtendedExecutionContext, suspendHistoryContext: SuspendHistoryContext) => Promise<T>;
|
|
6
8
|
|
|
7
|
-
export
|
|
9
|
+
export async function executeAsModalAndSuspendHistory<T>(commandName: string, document: Document, fn: CombinedFn<T>): Promise<T> {
|
|
8
10
|
return await executeAsModal(commandName, async (ctx) => {
|
|
9
|
-
return await suspendHistory(document, commandName,
|
|
11
|
+
return await suspendHistory(document, commandName, suspendHistoryContext => fn(ctx, suspendHistoryContext));
|
|
10
12
|
});
|
|
11
|
-
}
|
|
13
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { Layer as DomLayer } from 'photoshop';
|
|
2
|
+
import { constants } from 'photoshop';
|
|
3
3
|
|
|
4
4
|
// get all layers (including nested in groups)
|
|
5
5
|
// TODO: I would rename this to getAllDOMLayers to avoid confusion with UXPToolkitLayer
|
|
6
|
-
export
|
|
7
|
-
layers: DomLayer[],
|
|
8
|
-
): DomLayer[] => {
|
|
6
|
+
export function getFlattenedDomLayersList(layers: DomLayer[]): DomLayer[] {
|
|
9
7
|
const allLayers: DomLayer[] = [];
|
|
10
8
|
|
|
11
9
|
// Use a stack to avoid maximal call stack size (recursion) errors efficiently
|
|
@@ -20,7 +18,8 @@ export const getFlattenedDomLayersList = (
|
|
|
20
18
|
|
|
21
19
|
while (stack.length > 0) {
|
|
22
20
|
const layer = stack.pop();
|
|
23
|
-
if (!layer)
|
|
21
|
+
if (!layer)
|
|
22
|
+
continue;
|
|
24
23
|
|
|
25
24
|
allLayers.push(layer);
|
|
26
25
|
|
|
@@ -40,4 +39,4 @@ export const getFlattenedDomLayersList = (
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
return allLayers;
|
|
43
|
-
}
|
|
42
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { Layer as DomLayer } from
|
|
2
|
-
import type { Tree } from
|
|
1
|
+
import type { Layer as DomLayer } from 'photoshop';
|
|
2
|
+
import type { Tree } from '../general-tree/treeTypes';
|
|
3
3
|
|
|
4
4
|
// get layers recursively
|
|
5
|
-
export
|
|
5
|
+
export function photoshopDomLayersToTree(layers: DomLayer[]): Tree<DomLayer> {
|
|
6
6
|
// Get top-level layers
|
|
7
|
-
const filteredLayers = layers.filter(
|
|
7
|
+
const filteredLayers = layers.filter(layer => layer.parent === null);
|
|
8
8
|
|
|
9
9
|
const generateTree = (layers: DomLayer[]): Tree<DomLayer> => {
|
|
10
10
|
return layers.map((layer: DomLayer) => ({
|
|
@@ -15,4 +15,4 @@ export const photoshopDomLayersToTree = (layers: DomLayer[]): Tree<DomLayer> =>
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
return generateTree(filteredLayers);
|
|
18
|
-
}
|
|
18
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { UTError } from
|
|
1
|
+
import type { File } from 'uxp';
|
|
2
|
+
import ErrorStackParser from 'error-stack-parser';
|
|
3
|
+
import { SourceMapConsumer } from 'source-map-js';
|
|
4
|
+
import { storage } from 'uxp';
|
|
5
|
+
import { UTError } from '../errors/ut-error';
|
|
6
|
+
import { pathResolve } from '../node-compat/path/resolvePath';
|
|
6
7
|
|
|
7
8
|
export type BasicStackFrame = Pick<
|
|
8
9
|
ErrorStackParser.StackFrame,
|
|
9
|
-
|
|
10
|
+
'functionName' | 'fileName' | 'lineNumber' | 'columnNumber'
|
|
10
11
|
>;
|
|
11
12
|
|
|
12
13
|
export async function parseUxpErrorSourcemaps(error: Error, opts: { unsourcemappedHeaderLines?: number } = {}) {
|
|
@@ -14,28 +15,28 @@ export async function parseUxpErrorSourcemaps(error: Error, opts: { unsourcemapp
|
|
|
14
15
|
|
|
15
16
|
const unsourcemappedHeaderLines = opts.unsourcemappedHeaderLines ?? 0;
|
|
16
17
|
|
|
17
|
-
const loadedFilesCache: Record<string,
|
|
18
|
+
const loadedFilesCache: Record<string, File> = {};
|
|
18
19
|
|
|
19
|
-
const fs =
|
|
20
|
+
const fs = storage.localFileSystem;
|
|
20
21
|
const parsedMappedError: BasicStackFrame[] = [];
|
|
21
22
|
for (const frame of parsedError) {
|
|
22
23
|
if (!frame.fileName || !frame.lineNumber || !frame.columnNumber) {
|
|
23
24
|
parsedMappedError.push(frame);
|
|
24
25
|
continue;
|
|
25
26
|
}
|
|
26
|
-
const entryPath =
|
|
27
|
-
const file
|
|
28
|
-
loadedFilesCache[entryPath]
|
|
29
|
-
|
|
27
|
+
const entryPath = `plugin:${frame.fileName}`;
|
|
28
|
+
const file
|
|
29
|
+
= loadedFilesCache[entryPath]
|
|
30
|
+
?? ((await fs.getEntryWithUrl(entryPath)) as File);
|
|
30
31
|
loadedFilesCache[entryPath] = file;
|
|
31
32
|
if (!file.isFile) {
|
|
32
33
|
parsedMappedError.push(frame);
|
|
33
34
|
continue;
|
|
34
35
|
}
|
|
35
|
-
const sourcemapFileEntryPath = entryPath
|
|
36
|
-
const sourcemapFile
|
|
37
|
-
loadedFilesCache[sourcemapFileEntryPath]
|
|
38
|
-
|
|
36
|
+
const sourcemapFileEntryPath = `${entryPath}.map`;
|
|
37
|
+
const sourcemapFile
|
|
38
|
+
= loadedFilesCache[sourcemapFileEntryPath]
|
|
39
|
+
?? ((await fs.getEntryWithUrl(sourcemapFileEntryPath)) as File);
|
|
39
40
|
loadedFilesCache[sourcemapFileEntryPath] = sourcemapFile;
|
|
40
41
|
if (!sourcemapFile.isFile) {
|
|
41
42
|
parsedMappedError.push(frame);
|
|
@@ -55,7 +56,8 @@ export async function parseUxpErrorSourcemaps(error: Error, opts: { unsourcemapp
|
|
|
55
56
|
lineNumber: mappedFrame.line,
|
|
56
57
|
columnNumber: mappedFrame.column,
|
|
57
58
|
} as ErrorStackParser.StackFrame);
|
|
58
|
-
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
59
61
|
parsedMappedError.push(frame);
|
|
60
62
|
}
|
|
61
63
|
}
|
|
@@ -74,27 +76,26 @@ function parseErrorIntoBasicStackFrames(error: Error): BasicStackFrame[] {
|
|
|
74
76
|
columnNumber: frame.columnNumber,
|
|
75
77
|
};
|
|
76
78
|
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
throw new UTStacktraceParsingError('Failed to parse error stack trace', { cause: e });
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
export async function getBasicStackFrameAbsoluteFilePath(frame: BasicStackFrame): Promise<string> {
|
|
83
|
-
const pluginFolder = await (
|
|
84
|
-
storage as any
|
|
85
|
-
).localFileSystem.getPluginFolder();
|
|
86
|
+
const pluginFolder = await storage.localFileSystem.getPluginFolder();
|
|
86
87
|
const absoluteFileName = pathResolve(
|
|
87
88
|
pluginFolder.nativePath,
|
|
88
|
-
|
|
89
|
+
'index.js',
|
|
89
90
|
frame.fileName!,
|
|
90
|
-
).replace(/^plugin:/,
|
|
91
|
+
).replace(/^plugin:/, '');
|
|
91
92
|
return `${absoluteFileName}:${frame.lineNumber}:${frame.columnNumber}`;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
export class UTStacktraceParsingError extends UTError {
|
|
95
|
-
public override readonly name =
|
|
96
|
+
public override readonly name = 'UTStacktraceParsingError';
|
|
96
97
|
|
|
97
98
|
constructor(message: string, opts: ErrorOptions = {}) {
|
|
98
99
|
super(message, opts);
|
|
99
100
|
}
|
|
100
|
-
}
|
|
101
|
+
}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import type { Test } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { Test } from '@bubblydoo/uxp-test-framework';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { parseUxpErrorSourcemaps } from './sourcemaps';
|
|
4
4
|
|
|
5
5
|
function throwError() {
|
|
6
|
-
throw new Error(
|
|
6
|
+
throw new Error('Uncaught error'); // this error should stay exactly here in the source code, see below
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export const sourcemapsTest: Test = {
|
|
10
|
-
name:
|
|
10
|
+
name: 'sourcemaps: should parse error sourcemaps',
|
|
11
11
|
async run() {
|
|
12
12
|
let error: Error;
|
|
13
13
|
try {
|
|
14
14
|
throwError();
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
16
17
|
error = e as Error;
|
|
17
18
|
}
|
|
18
19
|
const parsedError = await parseUxpErrorSourcemaps(error!);
|
|
19
20
|
console.log(parsedError);
|
|
20
|
-
expect(parsedError[0]!.fileName).to.include(
|
|
21
|
+
expect(parsedError[0]!.fileName).to.include('sourcemaps.uxp-test.ts');
|
|
21
22
|
expect(parsedError[0]!.lineNumber).to.eq(6);
|
|
22
23
|
expect(parsedError[0]!.columnNumber).to.eq(8);
|
|
23
24
|
},
|
|
24
|
-
};
|
|
25
|
+
};
|
package/src/errors/ut-error.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { app } from
|
|
2
|
-
import { storage } from
|
|
3
|
-
import { executeAsModal } from
|
|
1
|
+
import { app } from 'photoshop';
|
|
2
|
+
import { storage } from 'uxp';
|
|
3
|
+
import { executeAsModal } from '../core/executeAsModal';
|
|
4
|
+
import { isFile } from './isFileOrFolder';
|
|
4
5
|
|
|
5
6
|
export async function openFileByPath(path: string) {
|
|
6
|
-
const fs =
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const fs = storage.localFileSystem;
|
|
8
|
+
const entry = await fs.getEntryWithUrl(path);
|
|
9
|
+
if (!isFile(entry)) {
|
|
10
|
+
throw new Error('Entry is not a file');
|
|
11
|
+
}
|
|
12
|
+
const doc = await executeAsModal('Open file', () => app.open(entry));
|
|
9
13
|
return doc;
|
|
10
14
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,74 +1,73 @@
|
|
|
1
|
+
// UT tree – layer descriptors & Photoshop tree
|
|
2
|
+
export { createMultiGetDocumentCommand } from './commands-library/multiGetDocument';
|
|
3
|
+
// Core wrappers
|
|
4
|
+
export { executeAsModalAndSuspendHistory } from './core-wrappers/executeAsModalAndSuspendHistory';
|
|
5
|
+
|
|
1
6
|
// Core – batchPlay & command building
|
|
2
|
-
export { batchPlay, type CorrectBatchPlayOptions } from
|
|
7
|
+
export { batchPlay, type CorrectBatchPlayOptions } from './core/batchPlay';
|
|
3
8
|
export {
|
|
4
|
-
createCommand,
|
|
5
9
|
batchPlayCommand,
|
|
6
10
|
batchPlayCommands,
|
|
11
|
+
createCommand,
|
|
7
12
|
createModifyingBatchPlayContext,
|
|
8
13
|
type UTCommandBase,
|
|
9
14
|
type UTCommandModifying,
|
|
10
15
|
type UTCommandNonModifying,
|
|
11
16
|
type UTCommandResult,
|
|
12
|
-
} from
|
|
17
|
+
} from './core/command';
|
|
13
18
|
|
|
14
19
|
// Core – execution context
|
|
15
20
|
export {
|
|
16
21
|
executeAsModal,
|
|
17
|
-
type CorrectExecutionContext,
|
|
18
|
-
type CorrectExecuteAsModalOptions,
|
|
19
22
|
type ExtendedExecutionContext,
|
|
20
|
-
} from
|
|
23
|
+
} from './core/executeAsModal';
|
|
24
|
+
|
|
21
25
|
export {
|
|
22
26
|
suspendHistory,
|
|
23
27
|
type SuspendHistoryContext,
|
|
24
|
-
} from
|
|
25
|
-
|
|
26
|
-
// Core wrappers
|
|
27
|
-
export { executeAsModalAndSuspendHistory } from "./core-wrappers/executeAsModalAndSuspendHistory";
|
|
28
|
-
|
|
28
|
+
} from './core/suspendHistory';
|
|
29
29
|
// DOM – layers
|
|
30
|
-
export { getFlattenedDomLayersList } from
|
|
31
|
-
export { photoshopDomLayersToTree } from "./dom/photoshopDomLayersToTree";
|
|
30
|
+
export { getFlattenedDomLayersList } from './dom/getFlattenedDomLayersList';
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
export { openFileByPath } from "./filesystem/openFileByPath";
|
|
32
|
+
export { photoshopDomLayersToTree } from './dom/photoshopDomLayersToTree';
|
|
35
33
|
|
|
34
|
+
// Error sourcemaps
|
|
35
|
+
export {
|
|
36
|
+
type BasicStackFrame,
|
|
37
|
+
getBasicStackFrameAbsoluteFilePath,
|
|
38
|
+
parseUxpErrorSourcemaps,
|
|
39
|
+
} from './error-sourcemaps/sourcemaps';
|
|
40
|
+
// Filesystem
|
|
41
|
+
export { isFile, isFolder } from './filesystem/isFileOrFolder';
|
|
42
|
+
export { openFileByPath } from './filesystem/openFileByPath';
|
|
36
43
|
// General tree
|
|
37
|
-
export { flattenTree } from
|
|
38
|
-
export {
|
|
39
|
-
export {
|
|
40
|
-
export { type Tree } from "./general-tree/treeTypes";
|
|
41
|
-
export { type LayerRef } from "./general-tree/layerRef";
|
|
44
|
+
export { flattenTree } from './general-tree/flattenTree';
|
|
45
|
+
export { type LayerRef } from './general-tree/layerRef';
|
|
46
|
+
export { mapTree } from './general-tree/mapTree';
|
|
42
47
|
|
|
48
|
+
export { mapTreeRef } from './general-tree/mapTreeRef';
|
|
49
|
+
export { type Tree } from './general-tree/treeTypes';
|
|
43
50
|
// Other
|
|
44
|
-
export { photoshopGetApplicationInfo } from
|
|
45
|
-
export { copyToClipboard, readFromClipboard } from "./other/clipboard";
|
|
46
|
-
export { uxpEntrypointsSchema } from "./other/uxpEntrypoints";
|
|
51
|
+
export { photoshopGetApplicationInfo } from './other/applicationInfo';
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
export {
|
|
50
|
-
export { getDocumentLayerDescriptors, type LayerDescriptor } from
|
|
51
|
-
export { getLayerEffects } from
|
|
53
|
+
export { copyToClipboard, readFromClipboard } from './other/clipboard';
|
|
54
|
+
export { uxpEntrypointsSchema } from './other/uxpEntrypoints';
|
|
55
|
+
export { getDocumentLayerDescriptors, type LayerDescriptor } from './ut-tree/getDocumentLayerDescriptors';
|
|
56
|
+
export { getLayerEffects } from './ut-tree/getLayerEffects';
|
|
52
57
|
export {
|
|
53
|
-
type UTLayer,
|
|
54
58
|
photoshopLayerDescriptorsToUTLayers,
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
type UTLayer,
|
|
60
|
+
} from './ut-tree/photoshopLayerDescriptorsToUTLayers';
|
|
61
|
+
export { type PsLayerRef } from './ut-tree/psLayerRef';
|
|
62
|
+
|
|
63
|
+
// UT tree – text
|
|
64
|
+
export { utLayersToText as utTreeToText } from './ut-tree/utLayersToText';
|
|
65
|
+
|
|
57
66
|
export {
|
|
58
67
|
utLayersToTree,
|
|
59
68
|
type UTLayerWithoutChildren,
|
|
60
|
-
} from
|
|
61
|
-
|
|
62
|
-
// UT tree – text
|
|
63
|
-
export { utLayersToText as utTreeToText } from "./ut-tree/utLayersToText";
|
|
69
|
+
} from './ut-tree/utLayersToTree';
|
|
70
|
+
export { type UTLayerPickKeys } from './util/utLayerPickKeysType';
|
|
64
71
|
|
|
65
72
|
// Util
|
|
66
|
-
export {
|
|
67
|
-
export { type UTLayerPickKeys } from "./util/utLayerPickKeysType";
|
|
68
|
-
|
|
69
|
-
// Error sourcemaps
|
|
70
|
-
export {
|
|
71
|
-
parseUxpErrorSourcemaps,
|
|
72
|
-
getBasicStackFrameAbsoluteFilePath,
|
|
73
|
-
type BasicStackFrame,
|
|
74
|
-
} from "./error-sourcemaps/sourcemaps";
|
|
73
|
+
export { utLayersToDomLayers, utLayerToDomLayer } from './util/utLayerToLayer';
|
|
@@ -1,61 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { z } from
|
|
1
|
+
import type { Document } from 'photoshop';
|
|
2
|
+
import { batchPlayCommand, createCommand, executeAsModal } from '@bubblydoo/uxp-toolkit';
|
|
3
|
+
import { convert } from 'xmlbuilder2';
|
|
4
|
+
import { z } from 'zod';
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
const converted = await readAllDocumentMetadata(document);
|
|
8
|
-
const property =
|
|
9
|
-
converted["x:xmpmeta"]["rdf:RDF"]["rdf:Description"][`${prefix}:${key}`];
|
|
10
|
-
|
|
11
|
-
return property;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const readAllDocumentMetadata = async (document: Document) => {
|
|
6
|
+
export async function readAllDocumentMetadata(document: Document) {
|
|
15
7
|
const metadata = await batchPlayCommand(
|
|
16
8
|
createCommand({
|
|
17
9
|
modifying: false,
|
|
18
10
|
descriptor: {
|
|
19
|
-
_obj:
|
|
11
|
+
_obj: 'get',
|
|
20
12
|
_target: {
|
|
21
13
|
_ref: [
|
|
22
|
-
{ _property:
|
|
23
|
-
{ _ref:
|
|
14
|
+
{ _property: 'XMPMetadataAsUTF8' },
|
|
15
|
+
{ _ref: 'document', _id: document.id },
|
|
24
16
|
],
|
|
25
17
|
},
|
|
26
18
|
},
|
|
27
19
|
schema: z.object({
|
|
28
20
|
XMPMetadataAsUTF8: z.any(),
|
|
29
|
-
})
|
|
30
|
-
})
|
|
21
|
+
}),
|
|
22
|
+
}),
|
|
31
23
|
);
|
|
32
24
|
|
|
33
25
|
return convert(metadata.XMPMetadataAsUTF8, {
|
|
34
|
-
format:
|
|
26
|
+
format: 'object',
|
|
35
27
|
}) as any;
|
|
36
|
-
}
|
|
28
|
+
}
|
|
37
29
|
|
|
38
|
-
export
|
|
30
|
+
export async function writeDocumentMetadata(document: Document, newProperty: { key: string; value: string; prefix: string; prefixNamespace: string }) {
|
|
39
31
|
const initialMetadata = await readAllDocumentMetadata(document);
|
|
40
|
-
const obj = convert(initialMetadata, { format:
|
|
41
|
-
obj[
|
|
32
|
+
const obj = convert(initialMetadata, { format: 'object' }) as any;
|
|
33
|
+
obj['x:xmpmeta']['rdf:RDF']['rdf:Description'][
|
|
42
34
|
`@xmlns:${newProperty.prefix}`
|
|
43
35
|
] = newProperty.prefixNamespace;
|
|
44
|
-
obj[
|
|
36
|
+
obj['x:xmpmeta']['rdf:RDF']['rdf:Description'][
|
|
45
37
|
`${newProperty.prefix}:${newProperty.key}`
|
|
46
38
|
] = newProperty.value;
|
|
47
|
-
const newXmpString = convert(obj, { format:
|
|
48
|
-
await executeAsModal(
|
|
39
|
+
const newXmpString = convert(obj, { format: 'xml' });
|
|
40
|
+
await executeAsModal('Set Metadata', async (ctx) => {
|
|
49
41
|
const command = createCommand({
|
|
50
42
|
modifying: true,
|
|
51
43
|
descriptor: {
|
|
52
|
-
_obj:
|
|
44
|
+
_obj: 'set',
|
|
53
45
|
_target: [
|
|
54
|
-
{ _ref:
|
|
55
|
-
{ _ref:
|
|
46
|
+
{ _ref: 'property', _property: 'XMPMetadataAsUTF8' },
|
|
47
|
+
{ _ref: 'document', _id: document.id },
|
|
56
48
|
],
|
|
57
49
|
to: {
|
|
58
|
-
_obj:
|
|
50
|
+
_obj: 'document',
|
|
59
51
|
XMPMetadataAsUTF8: newXmpString,
|
|
60
52
|
},
|
|
61
53
|
},
|
|
@@ -63,4 +55,12 @@ export const writeDocumentMetadata = async (document: Document, newProperty: { k
|
|
|
63
55
|
});
|
|
64
56
|
await ctx.batchPlayCommand(command, { synchronousExecution: true });
|
|
65
57
|
});
|
|
66
|
-
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function readDocumentMetadata(document: Document, { key, prefix }: { key: string; prefix: string }) {
|
|
61
|
+
const converted = await readAllDocumentMetadata(document);
|
|
62
|
+
const property
|
|
63
|
+
= converted['x:xmpmeta']['rdf:RDF']['rdf:Description'][`${prefix}:${key}`];
|
|
64
|
+
|
|
65
|
+
return property;
|
|
66
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { Test } from '@bubblydoo/uxp-test-framework';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { app } from 'photoshop';
|
|
4
|
+
import { openFileByPath } from '../filesystem/openFileByPath';
|
|
5
5
|
import {
|
|
6
6
|
readDocumentMetadata,
|
|
7
7
|
writeDocumentMetadata,
|
|
8
|
-
} from
|
|
8
|
+
} from './metadataStorage';
|
|
9
9
|
|
|
10
|
-
const TEST_PREFIX =
|
|
11
|
-
const TEST_PREFIX_NAMESPACE =
|
|
12
|
-
const TEST_KEY =
|
|
13
|
-
const TEST_VALUE =
|
|
10
|
+
const TEST_PREFIX = 'bubblytest';
|
|
11
|
+
const TEST_PREFIX_NAMESPACE = 'https://example.com/bubbly-test';
|
|
12
|
+
const TEST_KEY = 'testKey';
|
|
13
|
+
const TEST_VALUE = 'test-value-written-by-uxp-test';
|
|
14
14
|
|
|
15
15
|
export const metadataStorageTest: Test = {
|
|
16
|
-
name:
|
|
16
|
+
name: 'Metadata Storage',
|
|
17
17
|
async run() {
|
|
18
|
-
await openFileByPath(
|
|
18
|
+
await openFileByPath('plugin:/fixtures/one-layer.psd');
|
|
19
19
|
const document = app.activeDocument!;
|
|
20
20
|
|
|
21
21
|
await writeDocumentMetadata(document, {
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
// eslint-disable-next-line unicorn/prefer-node-protocol
|
|
2
|
+
import { resolve as nativeResolve } from 'path';
|
|
2
3
|
|
|
3
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* for some reason native path.resolve in UXP returns a URL object
|
|
4
6
|
* this function converts it to a string
|
|
5
7
|
*/
|
|
6
8
|
export function pathResolve(...pathSegments: string[]): string {
|
|
7
9
|
const urlOrString = nativeResolve(...pathSegments) as URL | string;
|
|
8
|
-
if (typeof urlOrString ===
|
|
10
|
+
if (typeof urlOrString === 'string') {
|
|
9
11
|
return urlOrString;
|
|
10
12
|
}
|
|
11
13
|
if (isUrl(urlOrString)) {
|
|
12
14
|
return urlOrString.toString();
|
|
13
15
|
}
|
|
14
|
-
throw new Error(
|
|
16
|
+
throw new Error('Unexpected URL object');
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
function isUrl(urlOrString: any): urlOrString is URL {
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { batchPlayCommand, createCommand } from
|
|
3
|
-
|
|
4
|
-
export async function photoshopGetApplicationInfo() {
|
|
5
|
-
return await batchPlayCommand(photoshopApplicationInfoCommand);
|
|
6
|
-
}
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { batchPlayCommand, createCommand } from '../core/command';
|
|
7
3
|
|
|
8
4
|
const photoshopAppInfoSchema = z.object({
|
|
9
5
|
active: z.boolean(),
|
|
@@ -32,25 +28,29 @@ const photoshopAppInfoSchema = z.object({
|
|
|
32
28
|
name: z.string(),
|
|
33
29
|
obscured: z.boolean(),
|
|
34
30
|
visible: z.boolean(),
|
|
35
|
-
})
|
|
31
|
+
}),
|
|
36
32
|
),
|
|
37
33
|
});
|
|
38
34
|
|
|
39
35
|
const photoshopApplicationInfoCommand = createCommand({
|
|
40
36
|
modifying: false,
|
|
41
37
|
descriptor: {
|
|
42
|
-
_obj:
|
|
38
|
+
_obj: 'get',
|
|
43
39
|
_target: [
|
|
44
40
|
{
|
|
45
|
-
_ref:
|
|
46
|
-
_enum:
|
|
47
|
-
_value:
|
|
41
|
+
_ref: 'application',
|
|
42
|
+
_enum: 'ordinal',
|
|
43
|
+
_value: 'targetEnum',
|
|
48
44
|
},
|
|
49
45
|
],
|
|
50
46
|
},
|
|
51
47
|
schema: photoshopAppInfoSchema,
|
|
52
48
|
});
|
|
53
49
|
|
|
50
|
+
export async function photoshopGetApplicationInfo() {
|
|
51
|
+
return await batchPlayCommand(photoshopApplicationInfoCommand);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
54
|
// $PnCK: {_enum: "cursorKind", _value: "brushSize"}
|
|
55
55
|
// MRUColorList: (2) [{…}, {…}]
|
|
56
56
|
// active: false
|