Skip to content

Commit 8451970

Browse files
committed
feat: add script excution + clipboard support
The plugin can now run builint script from Flow Launcher. Use clipboard support (clipboardy) to get input and set new value
1 parent 78b2be6 commit 8451970

9 files changed

Lines changed: 261 additions & 22 deletions

File tree

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@
1818
"devDependencies": {
1919
"@types/node": "^22.13.10",
2020
"typescript": "^5.8.2"
21+
},
22+
"dependencies": {
23+
"clipboardy": "^4.0.0"
2124
}
2225
}

src/const.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@ export const defaultScriptPath = path.join(__rootpath, 'src', 'scripts/');
1717

1818
export const metaStartTerm = '/**';
1919
export const metaEndTerm = '**/';
20+
21+
// ┌ ┐
22+
// │ Script │
23+
// └ ┘
24+
25+
export const MAIN_FUNCTION_EXEC = 'main(this)';

src/main.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import process from 'node:process';
22
import type { Arguments } from './types.js';
33
import { ScriptManager } from './scriptManager.js';
4-
import {
5-
FLSearchResultFormat,
6-
sendJsonRpcRequest
7-
} from './utils/flowLauncher.js';
4+
import { FlowLauncher, sendJsonRpcRequest } from './utils/flowLauncher.js';
5+
import { Clipboard } from './utils/clipboard.js';
6+
import type { Script } from './models/Script.js';
87

98
const args: Arguments = JSON.parse(process.argv[2] ?? '{}');
109
const { method, parameters } = args;
@@ -13,11 +12,18 @@ let scriptManager = new ScriptManager();
1312
scriptManager.init();
1413

1514
if (method === 'query') {
16-
const result = FLSearchResultFormat(
15+
let results = FlowLauncher.searchResultFormat(
1716
scriptManager.search(parameters[0] ?? '')
1817
);
19-
sendJsonRpcRequest({ result });
18+
sendJsonRpcRequest({ result: results });
2019
}
2120

2221
if (method === 'run') {
22+
let clip = Clipboard.get();
23+
let activeScript = scriptManager.scripts.filter(
24+
script => script.name === parameters[0]
25+
)[0];
26+
const result = scriptManager.runScript(activeScript as Script, clip);
27+
Clipboard.copy(result);
28+
FlowLauncher.showMessage('New Result Copied!', 'Yeah !!!');
2329
}

src/models/Script.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import vm from 'node:vm';
12
import type { Metas } from '../types.js';
3+
import { MAIN_FUNCTION_EXEC } from '../const.js';
4+
import type { ScriptExecution } from './ScriptExecution.js';
25

36
type ScriptArguments = {
47
script: string;
@@ -15,15 +18,30 @@ export class Script {
1518
tags: string[] | undefined;
1619
bias: number | undefined;
1720

21+
vmScript: vm.Script;
22+
1823
constructor({ script, parameters, builtIn }: ScriptArguments) {
1924
const TAGS_SEPARATOR = ',';
2025

2126
this.scriptCode = script;
2227
this.isBuiltInt = builtIn;
2328

29+
// Define metas
2430
this.name = parameters['name'] as string;
2531
this.desc = parameters['description'] as string;
2632
this.tags = (parameters['tags'] as string).split(TAGS_SEPARATOR);
2733
this.bias = parameters['bias'] as number;
34+
35+
// Init script
36+
this.vmScript = new vm.Script(`${this.scriptCode}; ${MAIN_FUNCTION_EXEC}`);
37+
}
38+
39+
run(context: ScriptExecution) {
40+
vm.createContext(context);
41+
try {
42+
this.vmScript.runInContext(context);
43+
} catch (e) {
44+
console.error(`Error in VM Script execution: ${e}`);
45+
}
2846
}
2947
}

src/models/ScriptExecution.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { FlowLauncher } from '../utils/flowLauncher.js';
2+
3+
type ScriptExecutionArguments = {
4+
text: string;
5+
};
6+
7+
export class ScriptExecution {
8+
fullText: string;
9+
constructor({ text }: ScriptExecutionArguments) {
10+
this.fullText = text;
11+
}
12+
13+
get text(): string {
14+
return this.fullText;
15+
}
16+
17+
set text(txt: string) {
18+
this.fullText = txt;
19+
}
20+
}

src/scriptManager.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Metas } from './types.js';
44
import { Script } from './models/Script.js';
55
import { defaultScriptPath, metaEndTerm, metaStartTerm } from './const.js';
66
import { matchesMetas, stringRange } from './utils/utils.js';
7+
import { ScriptExecution } from './models/ScriptExecution.js';
78

89
export class ScriptManager {
910
scripts: Script[];
@@ -69,4 +70,10 @@ export class ScriptManager {
6970

7071
return results.sort(metaNameSort);
7172
}
73+
74+
runScript(script: Script, text: string) {
75+
let scriptExecution = new ScriptExecution({ text: text });
76+
script.run(scriptExecution);
77+
return scriptExecution.text;
78+
}
7279
}

src/utils/clipboard.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import clipboard from 'clipboardy';
2+
3+
export const Clipboard = {
4+
/**
5+
* Return clipboard from clipboardy dependency
6+
*/
7+
get: function (): string {
8+
return clipboard.readSync();
9+
},
10+
/**
11+
* Update clipboard with new content using clipboardy
12+
* @param text Text to add in clipboard
13+
*/
14+
copy: function (text: string | undefined) {
15+
if (text !== undefined) {
16+
clipboard.writeSync(text);
17+
}
18+
}
19+
};

src/utils/flowLauncher.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
11
import type { Script } from '../models/Script.js';
22
import type { Result } from '../types.js';
33

4-
/**
5-
* Return reformatted list of scripts for Flow Launcher
6-
* @param scripts list of scripts to format.
7-
*/
8-
export function FLSearchResultFormat(scripts: Script[]): Result[] {
9-
return scripts.map(script => ({
10-
Title: script.name ?? 'No Name',
11-
Subtitle: script.desc ?? 'No description',
12-
JsonRPCAction: {
13-
method: 'run',
14-
parameters: []
15-
},
16-
IcoPath: 'icon\\app.png',
17-
score: 0
18-
}));
19-
}
4+
export const FlowLauncher = {
5+
/**
6+
* Return reformatted list of scripts for Flow Launcher
7+
* @param scripts list of scripts to format.
8+
*/
9+
searchResultFormat: function (scripts: Script[]): Result[] {
10+
return scripts.map(script => ({
11+
Title: script.name ?? 'No Name',
12+
Subtitle: script.desc ?? 'No description',
13+
JsonRPCAction: {
14+
method: 'run',
15+
parameters: [script.name ?? '']
16+
},
17+
IcoPath: 'icon\\app.png',
18+
score: 0
19+
}));
20+
},
21+
/**
22+
* Shows a desktop notification.
23+
* @param title The notification title.
24+
* @param subtitle The notification text content.
25+
*/
26+
showMessage: function (title: string = '', subtitle: string = '') {
27+
sendJsonRpcRequest({
28+
method: 'Flow.Launcher.ShowMsg',
29+
parameters: [title, subtitle, '']
30+
});
31+
}
32+
};
2033

2134
/**
2235
* Send a Flow Launcher JsonRPC request

yarn.lock

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,146 @@
99
dependencies:
1010
undici-types "~6.20.0"
1111

12+
clipboardy@^4.0.0:
13+
version "4.0.0"
14+
resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-4.0.0.tgz#e73ced93a76d19dd379ebf1f297565426dffdca1"
15+
integrity sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==
16+
dependencies:
17+
execa "^8.0.1"
18+
is-wsl "^3.1.0"
19+
is64bit "^2.0.0"
20+
21+
cross-spawn@^7.0.3:
22+
version "7.0.6"
23+
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
24+
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
25+
dependencies:
26+
path-key "^3.1.0"
27+
shebang-command "^2.0.0"
28+
which "^2.0.1"
29+
30+
execa@^8.0.1:
31+
version "8.0.1"
32+
resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c"
33+
integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==
34+
dependencies:
35+
cross-spawn "^7.0.3"
36+
get-stream "^8.0.1"
37+
human-signals "^5.0.0"
38+
is-stream "^3.0.0"
39+
merge-stream "^2.0.0"
40+
npm-run-path "^5.1.0"
41+
onetime "^6.0.0"
42+
signal-exit "^4.1.0"
43+
strip-final-newline "^3.0.0"
44+
45+
get-stream@^8.0.1:
46+
version "8.0.1"
47+
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2"
48+
integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==
49+
50+
human-signals@^5.0.0:
51+
version "5.0.0"
52+
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
53+
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
54+
55+
is-docker@^3.0.0:
56+
version "3.0.0"
57+
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200"
58+
integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==
59+
60+
is-inside-container@^1.0.0:
61+
version "1.0.0"
62+
resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4"
63+
integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==
64+
dependencies:
65+
is-docker "^3.0.0"
66+
67+
is-stream@^3.0.0:
68+
version "3.0.0"
69+
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
70+
integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
71+
72+
is-wsl@^3.1.0:
73+
version "3.1.0"
74+
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2"
75+
integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==
76+
dependencies:
77+
is-inside-container "^1.0.0"
78+
79+
is64bit@^2.0.0:
80+
version "2.0.0"
81+
resolved "https://registry.yarnpkg.com/is64bit/-/is64bit-2.0.0.tgz#198c627cbcb198bbec402251f88e5e1a51236c07"
82+
integrity sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==
83+
dependencies:
84+
system-architecture "^0.1.0"
85+
86+
isexe@^2.0.0:
87+
version "2.0.0"
88+
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
89+
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
90+
91+
merge-stream@^2.0.0:
92+
version "2.0.0"
93+
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
94+
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
95+
96+
mimic-fn@^4.0.0:
97+
version "4.0.0"
98+
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
99+
integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
100+
101+
npm-run-path@^5.1.0:
102+
version "5.3.0"
103+
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f"
104+
integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==
105+
dependencies:
106+
path-key "^4.0.0"
107+
108+
onetime@^6.0.0:
109+
version "6.0.0"
110+
resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
111+
integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
112+
dependencies:
113+
mimic-fn "^4.0.0"
114+
115+
path-key@^3.1.0:
116+
version "3.1.1"
117+
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
118+
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
119+
120+
path-key@^4.0.0:
121+
version "4.0.0"
122+
resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
123+
integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
124+
125+
shebang-command@^2.0.0:
126+
version "2.0.0"
127+
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
128+
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
129+
dependencies:
130+
shebang-regex "^3.0.0"
131+
132+
shebang-regex@^3.0.0:
133+
version "3.0.0"
134+
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
135+
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
136+
137+
signal-exit@^4.1.0:
138+
version "4.1.0"
139+
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
140+
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
141+
142+
strip-final-newline@^3.0.0:
143+
version "3.0.0"
144+
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
145+
integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
146+
147+
system-architecture@^0.1.0:
148+
version "0.1.0"
149+
resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d"
150+
integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==
151+
12152
typescript@^5.8.2:
13153
version "5.8.2"
14154
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4"
@@ -18,3 +158,10 @@ undici-types@~6.20.0:
18158
version "6.20.0"
19159
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
20160
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
161+
162+
which@^2.0.1:
163+
version "2.0.2"
164+
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
165+
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
166+
dependencies:
167+
isexe "^2.0.0"

0 commit comments

Comments
 (0)