pi-image-tools 1.3.0 → 1.3.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.
@@ -1,69 +1,69 @@
1
- import { buildNamespaceWrappedCommand, defaultCommandExists, type CommandExists } from "../shell-environment.js";
2
- import {
3
- defaultCommandRunner,
4
- MAX_BUFFER_BYTES,
5
- READ_TIMEOUT_MS,
6
- type CommandRunner,
7
- } from "./command-runner.js";
8
- import type { ClipboardImageProvider, ClipboardProviderContext, ClipboardReadResult } from "./types.js";
9
-
10
- export interface PngpasteProviderOptions {
11
- priority?: number;
12
- commandRunner?: CommandRunner;
13
- commandExists?: CommandExists;
14
- }
15
-
16
- export class PngpasteProvider implements ClipboardImageProvider {
17
- readonly capabilities;
18
- private readonly commandRunner: CommandRunner;
19
- private readonly commandExists: CommandExists;
20
-
21
- constructor(options: PngpasteProviderOptions = {}) {
22
- this.capabilities = {
23
- id: "mac-pngpaste",
24
- name: "pngpaste",
25
- platforms: ["darwin"],
26
- priority: options.priority ?? 10,
27
- };
28
- this.commandRunner = options.commandRunner ?? defaultCommandRunner;
29
- this.commandExists = options.commandExists ?? defaultCommandExists;
30
- }
31
-
32
- isAvailable(context: ClipboardProviderContext): boolean {
33
- try {
34
- return this.commandExists("pngpaste", context);
35
- } catch {
36
- return false;
37
- }
38
- }
39
-
40
- read(context: ClipboardProviderContext): ClipboardReadResult {
41
- const wrapped = buildNamespaceWrappedCommand(
42
- "pngpaste",
43
- ["-"],
44
- context,
45
- this.commandExists,
46
- );
47
- const result = this.commandRunner(wrapped.command, wrapped.args, {
48
- environment: context.environment,
49
- maxBuffer: MAX_BUFFER_BYTES,
50
- timeout: READ_TIMEOUT_MS,
51
- });
52
-
53
- if (result.missingCommand) {
54
- return { available: false, image: null };
55
- }
56
-
57
- if (!result.ok || result.stdout.length === 0) {
58
- return { available: true, image: null };
59
- }
60
-
61
- return {
62
- available: true,
63
- image: {
64
- bytes: new Uint8Array(result.stdout),
65
- mimeType: "image/png",
66
- },
67
- };
68
- }
69
- }
1
+ import { buildNamespaceWrappedCommand, defaultCommandExists, type CommandExists } from "../shell-environment.js";
2
+ import {
3
+ defaultCommandRunner,
4
+ MAX_BUFFER_BYTES,
5
+ READ_TIMEOUT_MS,
6
+ type CommandRunner,
7
+ } from "./command-runner.js";
8
+ import type { ClipboardImageProvider, ClipboardProviderContext, ClipboardReadResult } from "./types.js";
9
+
10
+ export interface PngpasteProviderOptions {
11
+ priority?: number;
12
+ commandRunner?: CommandRunner;
13
+ commandExists?: CommandExists;
14
+ }
15
+
16
+ export class PngpasteProvider implements ClipboardImageProvider {
17
+ readonly capabilities;
18
+ private readonly commandRunner: CommandRunner;
19
+ private readonly commandExists: CommandExists;
20
+
21
+ constructor(options: PngpasteProviderOptions = {}) {
22
+ this.capabilities = {
23
+ id: "mac-pngpaste",
24
+ name: "pngpaste",
25
+ platforms: ["darwin"],
26
+ priority: options.priority ?? 10,
27
+ };
28
+ this.commandRunner = options.commandRunner ?? defaultCommandRunner;
29
+ this.commandExists = options.commandExists ?? defaultCommandExists;
30
+ }
31
+
32
+ isAvailable(context: ClipboardProviderContext): boolean {
33
+ try {
34
+ return this.commandExists("pngpaste", context);
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ read(context: ClipboardProviderContext): ClipboardReadResult {
41
+ const wrapped = buildNamespaceWrappedCommand(
42
+ "pngpaste",
43
+ ["-"],
44
+ context,
45
+ this.commandExists,
46
+ );
47
+ const result = this.commandRunner(wrapped.command, wrapped.args, {
48
+ environment: context.environment,
49
+ maxBuffer: MAX_BUFFER_BYTES,
50
+ timeout: READ_TIMEOUT_MS,
51
+ });
52
+
53
+ if (result.missingCommand) {
54
+ return { available: false, image: null };
55
+ }
56
+
57
+ if (!result.ok || result.stdout.length === 0) {
58
+ return { available: true, image: null };
59
+ }
60
+
61
+ return {
62
+ available: true,
63
+ image: {
64
+ bytes: new Uint8Array(result.stdout),
65
+ mimeType: "image/png",
66
+ },
67
+ };
68
+ }
69
+ }
@@ -1,84 +1,84 @@
1
- import { createRequire } from "node:module";
2
-
3
- import { hasGraphicalSession } from "../shell-environment.js";
4
- import type { ClipboardImageProvider, ClipboardProviderContext, ClipboardReadResult } from "./types.js";
5
- import type { ClipboardModule } from "../types.js";
6
-
7
- const require = createRequire(import.meta.url);
8
- let cachedClipboardModule: ClipboardModule | null | undefined;
9
-
10
- export type ClipboardModuleLoader = (
11
- platform: NodeJS.Platform,
12
- environment: NodeJS.ProcessEnv,
13
- ) => ClipboardModule | null;
14
-
15
- export function loadClipboardModule(
16
- platform: NodeJS.Platform = process.platform,
17
- environment: NodeJS.ProcessEnv = process.env,
18
- ): ClipboardModule | null {
19
- if (cachedClipboardModule !== undefined) {
20
- return cachedClipboardModule;
21
- }
22
-
23
- if (environment.TERMUX_VERSION || !hasGraphicalSession(platform, environment)) {
24
- cachedClipboardModule = null;
25
- return cachedClipboardModule;
26
- }
27
-
28
- try {
29
- cachedClipboardModule = require("@mariozechner/clipboard") as ClipboardModule;
30
- } catch {
31
- cachedClipboardModule = null;
32
- }
33
-
34
- return cachedClipboardModule;
35
- }
36
-
37
- export interface NativeModuleProviderOptions {
38
- priority?: number;
39
- moduleLoader?: ClipboardModuleLoader;
40
- }
41
-
42
- export class NativeModuleProvider implements ClipboardImageProvider {
43
- readonly capabilities;
44
- private readonly moduleLoader: ClipboardModuleLoader;
45
-
46
- constructor(options: NativeModuleProviderOptions = {}) {
47
- this.capabilities = {
48
- id: "native-module",
49
- name: "@mariozechner/clipboard",
50
- platforms: "*" as const,
51
- priority: options.priority ?? 100,
52
- };
53
- this.moduleLoader = options.moduleLoader ?? loadClipboardModule;
54
- }
55
-
56
- isAvailable(context: ClipboardProviderContext): boolean {
57
- return this.moduleLoader(context.platform, context.environment) !== null;
58
- }
59
-
60
- async read(context: ClipboardProviderContext): Promise<ClipboardReadResult> {
61
- const clipboard = this.moduleLoader(context.platform, context.environment);
62
- if (!clipboard) {
63
- return { available: false, image: null };
64
- }
65
-
66
- if (!clipboard.hasImage()) {
67
- return { available: true, image: null };
68
- }
69
-
70
- const imageData = await clipboard.getImageBinary();
71
- if (!imageData || imageData.length === 0) {
72
- return { available: true, image: null };
73
- }
74
-
75
- const bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);
76
- return {
77
- available: true,
78
- image: {
79
- bytes,
80
- mimeType: "image/png",
81
- },
82
- };
83
- }
84
- }
1
+ import { createRequire } from "node:module";
2
+
3
+ import { hasGraphicalSession } from "../shell-environment.js";
4
+ import type { ClipboardImageProvider, ClipboardProviderContext, ClipboardReadResult } from "./types.js";
5
+ import type { ClipboardModule } from "../types.js";
6
+
7
+ const require = createRequire(import.meta.url);
8
+ let cachedClipboardModule: ClipboardModule | null | undefined;
9
+
10
+ export type ClipboardModuleLoader = (
11
+ platform: NodeJS.Platform,
12
+ environment: NodeJS.ProcessEnv,
13
+ ) => ClipboardModule | null;
14
+
15
+ export function loadClipboardModule(
16
+ platform: NodeJS.Platform = process.platform,
17
+ environment: NodeJS.ProcessEnv = process.env,
18
+ ): ClipboardModule | null {
19
+ if (cachedClipboardModule !== undefined) {
20
+ return cachedClipboardModule;
21
+ }
22
+
23
+ if (environment.TERMUX_VERSION || !hasGraphicalSession(platform, environment)) {
24
+ cachedClipboardModule = null;
25
+ return cachedClipboardModule;
26
+ }
27
+
28
+ try {
29
+ cachedClipboardModule = require("@mariozechner/clipboard") as ClipboardModule;
30
+ } catch {
31
+ cachedClipboardModule = null;
32
+ }
33
+
34
+ return cachedClipboardModule;
35
+ }
36
+
37
+ export interface NativeModuleProviderOptions {
38
+ priority?: number;
39
+ moduleLoader?: ClipboardModuleLoader;
40
+ }
41
+
42
+ export class NativeModuleProvider implements ClipboardImageProvider {
43
+ readonly capabilities;
44
+ private readonly moduleLoader: ClipboardModuleLoader;
45
+
46
+ constructor(options: NativeModuleProviderOptions = {}) {
47
+ this.capabilities = {
48
+ id: "native-module",
49
+ name: "@mariozechner/clipboard",
50
+ platforms: "*" as const,
51
+ priority: options.priority ?? 100,
52
+ };
53
+ this.moduleLoader = options.moduleLoader ?? loadClipboardModule;
54
+ }
55
+
56
+ isAvailable(context: ClipboardProviderContext): boolean {
57
+ return this.moduleLoader(context.platform, context.environment) !== null;
58
+ }
59
+
60
+ async read(context: ClipboardProviderContext): Promise<ClipboardReadResult> {
61
+ const clipboard = this.moduleLoader(context.platform, context.environment);
62
+ if (!clipboard) {
63
+ return { available: false, image: null };
64
+ }
65
+
66
+ if (!clipboard.hasImage()) {
67
+ return { available: true, image: null };
68
+ }
69
+
70
+ const imageData = await clipboard.getImageBinary();
71
+ if (!imageData || imageData.length === 0) {
72
+ return { available: true, image: null };
73
+ }
74
+
75
+ const bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);
76
+ return {
77
+ available: true,
78
+ image: {
79
+ bytes,
80
+ mimeType: "image/png",
81
+ },
82
+ };
83
+ }
84
+ }
@@ -1,95 +1,95 @@
1
- import { runPowerShellCommand, type PowerShellCommandResult, type RunPowerShellCommandOptions } from "../powershell.js";
2
- import { MAX_BUFFER_BYTES, READ_TIMEOUT_MS } from "./command-runner.js";
3
- import type { ClipboardImageProvider, ClipboardProviderContext, ClipboardReadResult } from "./types.js";
4
-
5
- export type PowerShellRunner = (
6
- script: string,
7
- options: RunPowerShellCommandOptions,
8
- ) => PowerShellCommandResult;
9
-
10
- export interface PowerShellFormsProviderOptions {
11
- priority?: number;
12
- powerShellRunner?: PowerShellRunner;
13
- }
14
-
15
- const READ_IMAGE_SCRIPT = `
16
- $ErrorActionPreference = 'Stop'
17
- Add-Type -AssemblyName System.Windows.Forms
18
- Add-Type -AssemblyName System.Drawing
19
-
20
- if (-not [System.Windows.Forms.Clipboard]::ContainsImage()) {
21
- return
22
- }
23
-
24
- $image = [System.Windows.Forms.Clipboard]::GetImage()
25
- if ($null -eq $image) {
26
- return
27
- }
28
-
29
- $stream = New-Object System.IO.MemoryStream
30
- try {
31
- $image.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png)
32
- [System.Convert]::ToBase64String($stream.ToArray())
33
- } finally {
34
- $stream.Dispose()
35
- $image.Dispose()
36
- }
37
- `;
38
-
39
- export class PowerShellFormsProvider implements ClipboardImageProvider {
40
- readonly capabilities;
41
- private readonly powerShellRunner: PowerShellRunner;
42
-
43
- constructor(options: PowerShellFormsProviderOptions = {}) {
44
- this.capabilities = {
45
- id: "powershell-forms",
46
- name: "Windows PowerShell Forms clipboard",
47
- platforms: ["win32"],
48
- priority: options.priority ?? 20,
49
- };
50
- this.powerShellRunner = options.powerShellRunner ?? runPowerShellCommand;
51
- }
52
-
53
- isAvailable(_context: ClipboardProviderContext): boolean {
54
- return true;
55
- }
56
-
57
- read(_context: ClipboardProviderContext): ClipboardReadResult {
58
- const result = this.powerShellRunner(READ_IMAGE_SCRIPT, {
59
- encoded: true,
60
- maxBuffer: MAX_BUFFER_BYTES,
61
- sta: true,
62
- timeout: READ_TIMEOUT_MS,
63
- });
64
-
65
- if (result.missingCommand) {
66
- return { available: false, image: null };
67
- }
68
-
69
- if (!result.ok) {
70
- return { available: true, image: null };
71
- }
72
-
73
- const base64 = result.stdout.trim();
74
- if (!base64) {
75
- return { available: true, image: null };
76
- }
77
-
78
- try {
79
- const bytes = Buffer.from(base64, "base64");
80
- if (bytes.length === 0) {
81
- return { available: true, image: null };
82
- }
83
-
84
- return {
85
- available: true,
86
- image: {
87
- bytes: new Uint8Array(bytes),
88
- mimeType: "image/png",
89
- },
90
- };
91
- } catch {
92
- return { available: true, image: null };
93
- }
94
- }
95
- }
1
+ import { runPowerShellCommand, type PowerShellCommandResult, type RunPowerShellCommandOptions } from "../powershell.js";
2
+ import { MAX_BUFFER_BYTES, READ_TIMEOUT_MS } from "./command-runner.js";
3
+ import type { ClipboardImageProvider, ClipboardProviderContext, ClipboardReadResult } from "./types.js";
4
+
5
+ export type PowerShellRunner = (
6
+ script: string,
7
+ options: RunPowerShellCommandOptions,
8
+ ) => PowerShellCommandResult;
9
+
10
+ export interface PowerShellFormsProviderOptions {
11
+ priority?: number;
12
+ powerShellRunner?: PowerShellRunner;
13
+ }
14
+
15
+ const READ_IMAGE_SCRIPT = `
16
+ $ErrorActionPreference = 'Stop'
17
+ Add-Type -AssemblyName System.Windows.Forms
18
+ Add-Type -AssemblyName System.Drawing
19
+
20
+ if (-not [System.Windows.Forms.Clipboard]::ContainsImage()) {
21
+ return
22
+ }
23
+
24
+ $image = [System.Windows.Forms.Clipboard]::GetImage()
25
+ if ($null -eq $image) {
26
+ return
27
+ }
28
+
29
+ $stream = New-Object System.IO.MemoryStream
30
+ try {
31
+ $image.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png)
32
+ [System.Convert]::ToBase64String($stream.ToArray())
33
+ } finally {
34
+ $stream.Dispose()
35
+ $image.Dispose()
36
+ }
37
+ `;
38
+
39
+ export class PowerShellFormsProvider implements ClipboardImageProvider {
40
+ readonly capabilities;
41
+ private readonly powerShellRunner: PowerShellRunner;
42
+
43
+ constructor(options: PowerShellFormsProviderOptions = {}) {
44
+ this.capabilities = {
45
+ id: "powershell-forms",
46
+ name: "Windows PowerShell Forms clipboard",
47
+ platforms: ["win32"],
48
+ priority: options.priority ?? 20,
49
+ };
50
+ this.powerShellRunner = options.powerShellRunner ?? runPowerShellCommand;
51
+ }
52
+
53
+ isAvailable(_context: ClipboardProviderContext): boolean {
54
+ return true;
55
+ }
56
+
57
+ read(_context: ClipboardProviderContext): ClipboardReadResult {
58
+ const result = this.powerShellRunner(READ_IMAGE_SCRIPT, {
59
+ encoded: true,
60
+ maxBuffer: MAX_BUFFER_BYTES,
61
+ sta: true,
62
+ timeout: READ_TIMEOUT_MS,
63
+ });
64
+
65
+ if (result.missingCommand) {
66
+ return { available: false, image: null };
67
+ }
68
+
69
+ if (!result.ok) {
70
+ return { available: true, image: null };
71
+ }
72
+
73
+ const base64 = result.stdout.trim();
74
+ if (!base64) {
75
+ return { available: true, image: null };
76
+ }
77
+
78
+ try {
79
+ const bytes = Buffer.from(base64, "base64");
80
+ if (bytes.length === 0) {
81
+ return { available: true, image: null };
82
+ }
83
+
84
+ return {
85
+ available: true,
86
+ image: {
87
+ bytes: new Uint8Array(bytes),
88
+ mimeType: "image/png",
89
+ },
90
+ };
91
+ } catch {
92
+ return { available: true, image: null };
93
+ }
94
+ }
95
+ }
@@ -1,88 +1,88 @@
1
- import type {
2
- ClipboardImageProvider,
3
- ClipboardProviderContext,
4
- ClipboardReadResult,
5
- } from "./types.js";
6
- import type { ClipboardImage } from "../types.js";
7
-
8
- export interface ClipboardProviderAttempt {
9
- providerId: string;
10
- available: boolean;
11
- imageFound: boolean;
12
- skipped: boolean;
13
- }
14
-
15
- export interface ClipboardProviderRegistryReadResult {
16
- image: ClipboardImage | null;
17
- attempts: ClipboardProviderAttempt[];
18
- }
19
-
20
- function supportsPlatform(provider: ClipboardImageProvider, platform: NodeJS.Platform): boolean {
21
- const { platforms } = provider.capabilities;
22
- return platforms === "*" || platforms.includes(platform);
23
- }
24
-
25
- function byPriority(left: ClipboardImageProvider, right: ClipboardImageProvider): number {
26
- return left.capabilities.priority - right.capabilities.priority;
27
- }
28
-
29
- export class ClipboardProviderRegistry {
30
- private readonly providers: ClipboardImageProvider[] = [];
31
-
32
- constructor(providers: readonly ClipboardImageProvider[] = []) {
33
- for (const provider of providers) {
34
- this.register(provider);
35
- }
36
- }
37
-
38
- register(provider: ClipboardImageProvider): this {
39
- this.providers.push(provider);
40
- return this;
41
- }
42
-
43
- getProviders(platform: NodeJS.Platform): ClipboardImageProvider[] {
44
- return this.providers.filter((provider) => supportsPlatform(provider, platform)).sort(byPriority);
45
- }
46
-
47
- async getEligible(context: ClipboardProviderContext): Promise<ClipboardImageProvider[]> {
48
- const eligible: ClipboardImageProvider[] = [];
49
- for (const provider of this.getProviders(context.platform)) {
50
- if (await provider.isAvailable(context)) {
51
- eligible.push(provider);
52
- }
53
- }
54
-
55
- return eligible;
56
- }
57
-
58
- async read(context: ClipboardProviderContext): Promise<ClipboardProviderRegistryReadResult> {
59
- const attempts: ClipboardProviderAttempt[] = [];
60
-
61
- for (const provider of this.getProviders(context.platform)) {
62
- const isAvailable = await provider.isAvailable(context);
63
- if (!isAvailable) {
64
- attempts.push({
65
- providerId: provider.capabilities.id,
66
- available: false,
67
- imageFound: false,
68
- skipped: true,
69
- });
70
- continue;
71
- }
72
-
73
- const result: ClipboardReadResult = await provider.read(context);
74
- attempts.push({
75
- providerId: provider.capabilities.id,
76
- available: result.available,
77
- imageFound: result.image !== null,
78
- skipped: false,
79
- });
80
-
81
- if (result.image) {
82
- return { image: result.image, attempts };
83
- }
84
- }
85
-
86
- return { image: null, attempts };
87
- }
88
- }
1
+ import type {
2
+ ClipboardImageProvider,
3
+ ClipboardProviderContext,
4
+ ClipboardReadResult,
5
+ } from "./types.js";
6
+ import type { ClipboardImage } from "../types.js";
7
+
8
+ export interface ClipboardProviderAttempt {
9
+ providerId: string;
10
+ available: boolean;
11
+ imageFound: boolean;
12
+ skipped: boolean;
13
+ }
14
+
15
+ export interface ClipboardProviderRegistryReadResult {
16
+ image: ClipboardImage | null;
17
+ attempts: ClipboardProviderAttempt[];
18
+ }
19
+
20
+ function supportsPlatform(provider: ClipboardImageProvider, platform: NodeJS.Platform): boolean {
21
+ const { platforms } = provider.capabilities;
22
+ return platforms === "*" || platforms.includes(platform);
23
+ }
24
+
25
+ function byPriority(left: ClipboardImageProvider, right: ClipboardImageProvider): number {
26
+ return left.capabilities.priority - right.capabilities.priority;
27
+ }
28
+
29
+ export class ClipboardProviderRegistry {
30
+ private readonly providers: ClipboardImageProvider[] = [];
31
+
32
+ constructor(providers: readonly ClipboardImageProvider[] = []) {
33
+ for (const provider of providers) {
34
+ this.register(provider);
35
+ }
36
+ }
37
+
38
+ register(provider: ClipboardImageProvider): this {
39
+ this.providers.push(provider);
40
+ return this;
41
+ }
42
+
43
+ getProviders(platform: NodeJS.Platform): ClipboardImageProvider[] {
44
+ return this.providers.filter((provider) => supportsPlatform(provider, platform)).sort(byPriority);
45
+ }
46
+
47
+ async getEligible(context: ClipboardProviderContext): Promise<ClipboardImageProvider[]> {
48
+ const eligible: ClipboardImageProvider[] = [];
49
+ for (const provider of this.getProviders(context.platform)) {
50
+ if (await provider.isAvailable(context)) {
51
+ eligible.push(provider);
52
+ }
53
+ }
54
+
55
+ return eligible;
56
+ }
57
+
58
+ async read(context: ClipboardProviderContext): Promise<ClipboardProviderRegistryReadResult> {
59
+ const attempts: ClipboardProviderAttempt[] = [];
60
+
61
+ for (const provider of this.getProviders(context.platform)) {
62
+ const isAvailable = await provider.isAvailable(context);
63
+ if (!isAvailable) {
64
+ attempts.push({
65
+ providerId: provider.capabilities.id,
66
+ available: false,
67
+ imageFound: false,
68
+ skipped: true,
69
+ });
70
+ continue;
71
+ }
72
+
73
+ const result: ClipboardReadResult = await provider.read(context);
74
+ attempts.push({
75
+ providerId: provider.capabilities.id,
76
+ available: result.available,
77
+ imageFound: result.image !== null,
78
+ skipped: false,
79
+ });
80
+
81
+ if (result.image) {
82
+ return { image: result.image, attempts };
83
+ }
84
+ }
85
+
86
+ return { image: null, attempts };
87
+ }
88
+ }