Skip to content

Commit bc4e9b6

Browse files
committed
feat(toy): add cron tool
1 parent ff5f98f commit bc4e9b6

File tree

13 files changed

+402
-242
lines changed

13 files changed

+402
-242
lines changed

locales/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
"view.devtoys.graphic.qrcode.tooltip": "Generates the QR code of the specified string",
4949
"view.devtoys.graphic.tooltip": "All Graphic Tools",
5050
"view.devtoys.noTools": "No tools",
51+
"view.devtoys.others.cron.label": "CRON",
52+
"view.devtoys.others.cron.panel.title": "CRON Simulator",
53+
"view.devtoys.others.cron.tooltip": "Simulate CRON Expression",
5154
"view.devtoys.others.label": "Others",
5255
"view.devtoys.others.tooltip": "All Other Tools",
5356
"view.devtoys.text.label": "Text",

locales/zh-CN.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
"view.devtoys.coders.html.label": "HTML",
1717
"view.devtoys.coders.html.panel.title": "HTML 编码器/解码器",
1818
"view.devtoys.coders.html.tooltip": "编码 HTML 或解码 HTML",
19-
"view.devtoys.coders.jwt.label": "",
19+
"view.devtoys.coders.jwt.label": "JWT",
2020
"view.devtoys.coders.jwt.panel.title": "JWT 调试器",
21-
"view.devtoys.coders.jwt.tooltip": "",
21+
"view.devtoys.coders.jwt.tooltip": "调试你的 JWT",
2222
"view.devtoys.coders.label": "编码器/解码器",
2323
"view.devtoys.coders.tooltip": "所有编码器/解码器",
2424
"view.devtoys.coders.url.label": "URL",
@@ -48,6 +48,9 @@
4848
"view.devtoys.graphic.qrcode.tooltip": "生成指定字符串的二维码",
4949
"view.devtoys.graphic.tooltip": "全部图片类工具",
5050
"view.devtoys.noTools": "暂无工具",
51+
"view.devtoys.others.cron.label": "CRON",
52+
"view.devtoys.others.cron.panel.title": "CRON 模拟器",
53+
"view.devtoys.others.cron.tooltip": "模拟 CRON 表达式",
5154
"view.devtoys.others.label": "其他",
5255
"view.devtoys.others.tooltip": "全部其他工具",
5356
"view.devtoys.text.label": "文本",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
"@vscode/test-electron": "^2.0.3",
119119
"autosize": "^5.0.1",
120120
"concurrently": "^7.0.0",
121+
"cron-parser": "^4.2.1",
121122
"crypto-js": "^4.1.1",
122123
"esbuild": "^0.14.22",
123124
"eslint": "^8.6.0",

src/Panel/Cron.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { PanelType } from "../shared";
2+
import { ToolPanel } from "../common/ToolPanel";
3+
import * as vscode from "vscode";
4+
import i18n from "../i18n";
5+
6+
export class Cron extends ToolPanel<Cron> {
7+
constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
8+
super(panel, extensionUri, PanelType.cron, "react");
9+
}
10+
11+
public static createOrShow(extensionUri: vscode.Uri) {
12+
super.createOrShow(
13+
extensionUri,
14+
PanelType.cron,
15+
i18n.t("view.devtoys.others.cron.panel.title"),
16+
Cron
17+
);
18+
}
19+
20+
public static canBeTreatedByTool(data: string): boolean | PanelType {
21+
const regex =
22+
/^(\*|((\*\/)?[1-5]?[0-9])) (\*|((\*\/)?[1-5]?[0-9])) (\*|((\*\/)?(1?[0-9]|2[0-3]))) (\*|((\*\/)?([1-9]|[12][0-9]|3[0-1]))) (\*|((\*\/)?([1-9]|1[0-2]))) (\*|((\*\/)?[0-6]))$/;
23+
if (regex.test(data)) {
24+
return PanelType.cron;
25+
}
26+
return false;
27+
}
28+
29+
public dispose(): void {
30+
super.dispose();
31+
Cron.currentPanel = undefined;
32+
}
33+
}
34+
35+
ToolPanel.allPanel.add(Cron);

src/commands/showTool.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
22
import { DevToysNode } from "../explorer/DevToysNode";
33
import { Base64 } from "../Panel/Base64";
44
import { ColorBlindnessSimulator } from "../Panel/ColorBlindnessSimulator";
5+
import { Cron } from "../Panel/Cron";
56
import { Hash } from "../Panel/Hash";
67
import { Html } from "../Panel/Html";
78
import { JsonToYaml } from "../Panel/JsonToYaml";
@@ -55,5 +56,8 @@ export default (context: vscode.ExtensionContext) => (node?: DevToysNode) => {
5556
case PanelType.timestamp:
5657
Timestamp.createOrShow(context.extensionUri);
5758
break;
59+
case PanelType.cron:
60+
Cron.createOrShow(context.extensionUri);
61+
break;
5862
}
5963
};

src/explorer/explorerNodeManager.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { Category, IToolData, PanelType } from "../shared";
44
import { DevToysNode } from "./DevToysNode";
55

66
class ExplorerNodeManager implements Disposable {
7-
private explorerNodeMap: Map<Category, DevToysNode[]> = new Map<Category, DevToysNode[]>();
7+
private explorerNodeMap: Map<Category, DevToysNode[]> = new Map<
8+
Category,
9+
DevToysNode[]
10+
>();
811

912
initialize(): void {
1013
const coders: IToolData[] = [
@@ -81,7 +84,13 @@ class ExplorerNodeManager implements Disposable {
8184
},
8285
];
8386

84-
const others: IToolData[] = [];
87+
const others: IToolData[] = [
88+
{
89+
label: i18n.t("view.devtoys.others.cron.label"),
90+
tooltip: i18n.t("view.devtoys.others.cron.tooltip"),
91+
panel: PanelType.cron,
92+
},
93+
];
8594

8695
this.explorerNodeMap.set(
8796
Category.coders,
@@ -208,4 +217,5 @@ class ExplorerNodeManager implements Disposable {
208217
}
209218
}
210219

211-
export const explorerNodeManager: ExplorerNodeManager = new ExplorerNodeManager();
220+
export const explorerNodeManager: ExplorerNodeManager =
221+
new ExplorerNodeManager();

src/shared.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ThemeIcon, Uri } from "vscode";
22

3-
43
export enum PanelType {
54
null = 0,
65
jsonToYaml = "jsonToYaml",
@@ -14,7 +13,8 @@ export enum PanelType {
1413
colorBlindnessSimulator = "colorBlindnessSimulator",
1514
jwt = "jwt",
1615
qrcode = "qrcode",
17-
timestamp = "timestamp"
16+
timestamp = "timestamp",
17+
cron = "cron",
1818
}
1919

2020
export enum Category {
@@ -31,5 +31,9 @@ export interface IToolData {
3131
label: string;
3232
tooltip: string;
3333
panel: PanelType | Category;
34-
iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon;
34+
iconPath?:
35+
| string
36+
| Uri
37+
| { light: string | Uri; dark: string | Uri }
38+
| ThemeIcon;
3539
}

webview/components/react/Cron/i18n.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import i18n from "i18next";
2+
import { initReactI18next } from "react-i18next";
3+
import en from "./locales/en.json";
4+
import zh_CN from "./locales/zh-CN.json";
5+
6+
i18n.use(initReactI18next).init({
7+
resources: {
8+
en: {
9+
translation: en,
10+
},
11+
"zh-cn": {
12+
translation: zh_CN,
13+
},
14+
},
15+
lng: window.displayLanguage,
16+
lowerCaseLng: true,
17+
fallbackLng: "en",
18+
});
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { useTranslation } from "react-i18next";
2+
import {
3+
VSCodeTextArea,
4+
VSCodeTextField,
5+
} from "@vscode/webview-ui-toolkit/react/index";
6+
import { useEffect, useState } from "react";
7+
import parser from "cron-parser";
8+
export default () => {
9+
const { t } = useTranslation();
10+
const [cron, setCron] = useState("0 */12 * * * *");
11+
const [preResult, setPreResult] = useState<string>("");
12+
const [nextResult, setNextResult] = useState<string>("");
13+
const tips = `* * * * * *
14+
┬ ┬ ┬ ┬ ┬ ┬
15+
│ │ │ │ │ |
16+
│ │ │ │ │ └ day of week (0 - 7, 1L - 7L) (0 or 7 is Sun)
17+
│ │ │ │ └───── month (1 - 12)
18+
│ │ │ └────────── day of month (1 - 31, L)
19+
│ │ └─────────────── hour (0 - 23)
20+
│ └──────────────────── minute (0 - 59)
21+
└───────────────────────── second (0 - 59, optional)`;
22+
useEffect(() => {
23+
try {
24+
const prev = parser.parseExpression(cron);
25+
const next = parser.parseExpression(cron);
26+
const prevResults = [
27+
prev.prev(),
28+
prev.prev(),
29+
prev.prev(),
30+
prev.prev(),
31+
prev.prev(),
32+
prev.prev(),
33+
prev.prev(),
34+
];
35+
const nextResults = [
36+
next.next(),
37+
next.next(),
38+
next.next(),
39+
next.next(),
40+
next.next(),
41+
next.next(),
42+
next.next(),
43+
];
44+
setPreResult(
45+
prevResults.map((item) => item.toDate().toLocaleString()).join("\n")
46+
);
47+
setNextResult(
48+
nextResults.map((item) => item.toDate().toLocaleString()).join("\n")
49+
);
50+
} catch (error) {
51+
setPreResult(String(error));
52+
setNextResult(String(error));
53+
}
54+
}, [cron]);
55+
return (
56+
<div>
57+
<h1> {t("tool.cron.title")}</h1>
58+
<VSCodeTextField
59+
value={cron}
60+
style={{ width: "100%" }}
61+
onInput={({ target }) => {
62+
setCron((target as HTMLInputElement).value);
63+
}}
64+
placeholder="0 */12 * * * *"
65+
>
66+
{t("tool.cron.crontextField.label")}
67+
</VSCodeTextField>
68+
<VSCodeTextArea
69+
value={preResult}
70+
readOnly
71+
style={{ width: "100%" }}
72+
cols={10}
73+
rows={8}
74+
>
75+
{t("tool.cron.firstTextArea.label")}
76+
</VSCodeTextArea>
77+
<VSCodeTextArea
78+
value={nextResult}
79+
readOnly
80+
style={{ width: "100%" }}
81+
cols={10}
82+
rows={8}
83+
>
84+
{t("tool.cron.lastTextArea.label")}
85+
</VSCodeTextArea>
86+
<pre>
87+
<code
88+
style={{
89+
width: "100%",
90+
fontFamily:
91+
"Menlo,Monaco,Consolas,Andale Mono,lucida console,Courier New,monospace",
92+
}}
93+
>
94+
{tips}
95+
</code>
96+
</pre>
97+
</div>
98+
);
99+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"tool.cron.crontextField.label": "CRON Expression",
3+
"tool.cron.firstTextArea.label": "First Seven Execution Times",
4+
"tool.cron.lastTextArea.label": "Last Seven Execution Times",
5+
"tool.cron.title": "CRON Simulator"
6+
}

0 commit comments

Comments
 (0)