@cnc_cbz/usefultools-plugin-official 1.0.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,179 @@
1
+ import { defineComponent, ref, openBlock, createElementBlock, createElementVNode, Fragment, renderList, normalizeClass, toDisplayString, withDirectives, vModelText, createTextVNode, createCommentVNode } from "vue";
2
+ const _hoisted_1 = { class: "flex flex-col h-full gap-5" };
3
+ const _hoisted_2 = { class: "bg-deep-charcoal border-4 border-black rounded-xl p-4 shadow-hard" };
4
+ const _hoisted_3 = { class: "flex items-center justify-between mb-3" };
5
+ const _hoisted_4 = { class: "flex gap-2" };
6
+ const _hoisted_5 = ["onClick"];
7
+ const _hoisted_6 = ["placeholder"];
8
+ const _hoisted_7 = {
9
+ key: 0,
10
+ class: "px-4 py-2 bg-coral-red/20 border-2 border-coral-red rounded flex items-center gap-2 text-coral-red font-bold text-sm"
11
+ };
12
+ const _hoisted_8 = { class: "flex-1 bg-deep-charcoal border-4 border-black rounded-xl p-4 shadow-hard" };
13
+ const _hoisted_9 = { class: "flex items-center justify-between mb-3" };
14
+ const _hoisted_10 = { class: "flex items-center gap-2" };
15
+ const _hoisted_11 = { class: "text-sm font-bold text-gray-400 uppercase tracking-wider" };
16
+ const _hoisted_12 = { class: "material-icons text-sm" };
17
+ const _hoisted_13 = { class: "font-mono text-sm text-gray-100 break-all select-all min-h-[80px] bg-bg-dark rounded-lg p-3 border-2 border-black whitespace-pre-wrap" };
18
+ const _sfc_main = /* @__PURE__ */ defineComponent({
19
+ __name: "index",
20
+ setup(__props) {
21
+ const input = ref("");
22
+ const output = ref("");
23
+ const mode = ref("encode");
24
+ const copyField = ref("");
25
+ const errorMsg = ref("");
26
+ function convert() {
27
+ errorMsg.value = "";
28
+ if (!input.value) {
29
+ output.value = "";
30
+ return;
31
+ }
32
+ try {
33
+ output.value = mode.value === "encode" ? encodeURIComponent(input.value) : decodeURIComponent(input.value);
34
+ } catch {
35
+ errorMsg.value = "解码失败,输入不是有效的 URL 编码";
36
+ output.value = "";
37
+ }
38
+ }
39
+ function swap() {
40
+ input.value = output.value || "";
41
+ output.value = "";
42
+ mode.value = mode.value === "encode" ? "decode" : "encode";
43
+ convert();
44
+ }
45
+ async function copy(text, field) {
46
+ await navigator.clipboard.writeText(text);
47
+ copyField.value = field;
48
+ setTimeout(() => {
49
+ copyField.value = "";
50
+ }, 1200);
51
+ }
52
+ return (_ctx, _cache) => {
53
+ return openBlock(), createElementBlock("div", _hoisted_1, [
54
+ createElementVNode("div", _hoisted_2, [
55
+ createElementVNode("div", _hoisted_3, [
56
+ _cache[2] || (_cache[2] = createElementVNode(
57
+ "div",
58
+ { class: "flex items-center gap-2" },
59
+ [
60
+ createElementVNode("span", { class: "material-icons text-primary text-lg" }, "link"),
61
+ createElementVNode("span", { class: "text-sm font-bold text-gray-400 uppercase tracking-wider" }, "URL 编解码")
62
+ ],
63
+ -1
64
+ /* CACHED */
65
+ )),
66
+ createElementVNode("div", _hoisted_4, [
67
+ (openBlock(), createElementBlock(
68
+ Fragment,
69
+ null,
70
+ renderList(["encode", "decode"], (m) => {
71
+ return createElementVNode("button", {
72
+ key: m,
73
+ onClick: ($event) => {
74
+ mode.value = m;
75
+ convert();
76
+ },
77
+ class: normalizeClass(["h-8 px-3 font-bold border-2 border-black rounded text-xs transition-all", mode.value === m ? "bg-primary text-black shadow-none translate-x-0.5 translate-y-0.5" : "bg-bg-dark text-gray-400 shadow-hard-sm hover:shadow-none hover:translate-x-0.5 hover:translate-y-0.5"])
78
+ }, toDisplayString(m === "encode" ? "编码" : "解码"), 11, _hoisted_5);
79
+ }),
80
+ 64
81
+ /* STABLE_FRAGMENT */
82
+ ))
83
+ ])
84
+ ]),
85
+ withDirectives(createElementVNode("textarea", {
86
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => input.value = $event),
87
+ onInput: convert,
88
+ rows: "5",
89
+ placeholder: mode.value === "encode" ? "输入要编码的文本或 URL..." : "输入要解码的 URL 编码...",
90
+ class: "w-full bg-bg-dark text-gray-100 border-4 border-black rounded-lg px-4 py-3 font-mono text-sm shadow-hard focus:border-primary focus:shadow-none focus:translate-x-[4px] focus:translate-y-[4px] transition-all outline-none placeholder-gray-600 resize-none"
91
+ }, null, 40, _hoisted_6), [
92
+ [vModelText, input.value]
93
+ ])
94
+ ]),
95
+ errorMsg.value ? (openBlock(), createElementBlock("div", _hoisted_7, [
96
+ _cache[3] || (_cache[3] = createElementVNode(
97
+ "span",
98
+ { class: "material-icons text-lg" },
99
+ "error_outline",
100
+ -1
101
+ /* CACHED */
102
+ )),
103
+ createTextVNode(
104
+ " " + toDisplayString(errorMsg.value),
105
+ 1
106
+ /* TEXT */
107
+ )
108
+ ])) : createCommentVNode("v-if", true),
109
+ createElementVNode("div", { class: "flex justify-center" }, [
110
+ createElementVNode("button", {
111
+ onClick: swap,
112
+ class: "h-10 px-4 bg-bg-dark text-gray-400 font-bold border-2 border-black rounded shadow-hard-sm hover:shadow-none hover:translate-x-0.5 hover:translate-y-0.5 transition-all text-sm flex items-center gap-2"
113
+ }, [..._cache[4] || (_cache[4] = [
114
+ createElementVNode(
115
+ "span",
116
+ { class: "material-icons text-lg" },
117
+ "swap_vert",
118
+ -1
119
+ /* CACHED */
120
+ ),
121
+ createTextVNode(
122
+ " 交换 ",
123
+ -1
124
+ /* CACHED */
125
+ )
126
+ ])])
127
+ ]),
128
+ createElementVNode("div", _hoisted_8, [
129
+ createElementVNode("div", _hoisted_9, [
130
+ createElementVNode("div", _hoisted_10, [
131
+ _cache[5] || (_cache[5] = createElementVNode(
132
+ "span",
133
+ { class: "material-icons text-neon-green text-lg" },
134
+ "output",
135
+ -1
136
+ /* CACHED */
137
+ )),
138
+ createElementVNode(
139
+ "span",
140
+ _hoisted_11,
141
+ toDisplayString(mode.value === "encode" ? "编码结果" : "解码结果"),
142
+ 1
143
+ /* TEXT */
144
+ )
145
+ ]),
146
+ output.value ? (openBlock(), createElementBlock("button", {
147
+ key: 0,
148
+ onClick: _cache[1] || (_cache[1] = ($event) => copy(output.value, "output")),
149
+ class: "flex items-center gap-1 text-xs text-gray-500 hover:text-primary transition-colors"
150
+ }, [
151
+ createElementVNode(
152
+ "span",
153
+ _hoisted_12,
154
+ toDisplayString(copyField.value === "output" ? "check" : "content_copy"),
155
+ 1
156
+ /* TEXT */
157
+ ),
158
+ createTextVNode(
159
+ " " + toDisplayString(copyField.value === "output" ? "已复制" : "复制"),
160
+ 1
161
+ /* TEXT */
162
+ )
163
+ ])) : createCommentVNode("v-if", true)
164
+ ]),
165
+ createElementVNode(
166
+ "div",
167
+ _hoisted_13,
168
+ toDisplayString(output.value || ""),
169
+ 1
170
+ /* TEXT */
171
+ )
172
+ ])
173
+ ]);
174
+ };
175
+ }
176
+ });
177
+ export {
178
+ _sfc_main as default
179
+ };
@@ -0,0 +1,165 @@
1
+ import { defineComponent, ref, openBlock, createElementBlock, createCommentVNode, createElementVNode, Fragment, renderList, normalizeClass, toDisplayString, withDirectives, vModelText } from "vue";
2
+ const _hoisted_1 = { class: "flex flex-col h-full gap-5" };
3
+ const _hoisted_2 = { class: "bg-deep-charcoal border-4 border-black rounded-xl p-4 shadow-hard" };
4
+ const _hoisted_3 = { class: "flex flex-wrap items-center gap-3" };
5
+ const _hoisted_4 = { class: "flex gap-2" };
6
+ const _hoisted_5 = ["onClick"];
7
+ const _hoisted_6 = { class: "flex items-center gap-2" };
8
+ const _hoisted_7 = { class: "flex-1 overflow-auto bg-deep-charcoal border-4 border-black rounded-xl p-4 shadow-hard" };
9
+ const _hoisted_8 = ["onClick"];
10
+ const _hoisted_9 = { class: "font-mono text-sm text-gray-100 select-all" };
11
+ const _sfc_main = /* @__PURE__ */ defineComponent({
12
+ __name: "index",
13
+ setup(__props) {
14
+ const count = ref(5);
15
+ const version = ref("v4");
16
+ const uuids = ref([]);
17
+ const copyField = ref("");
18
+ function generateV4() {
19
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
20
+ const r = Math.random() * 16 | 0;
21
+ return (c === "x" ? r : r & 3 | 8).toString(16);
22
+ });
23
+ }
24
+ function generateV7() {
25
+ const now = Date.now();
26
+ const hex = now.toString(16).padStart(12, "0");
27
+ const rand = () => Math.random().toString(16).slice(2);
28
+ const r = rand() + rand();
29
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-7${r.slice(0, 3)}-${(parseInt(r[3], 16) & 3 | 8).toString(16)}${r.slice(4, 7)}-${r.slice(7, 19)}`;
30
+ }
31
+ function generate() {
32
+ const fn = version.value === "v4" ? generateV4 : generateV7;
33
+ uuids.value = Array.from({ length: count.value }, () => fn());
34
+ }
35
+ async function copy(text, field) {
36
+ await navigator.clipboard.writeText(text);
37
+ copyField.value = field;
38
+ setTimeout(() => {
39
+ copyField.value = "";
40
+ }, 1200);
41
+ }
42
+ async function copyAll() {
43
+ await navigator.clipboard.writeText(uuids.value.join("\n"));
44
+ copyField.value = "all";
45
+ setTimeout(() => {
46
+ copyField.value = "";
47
+ }, 1200);
48
+ }
49
+ generate();
50
+ return (_ctx, _cache) => {
51
+ return openBlock(), createElementBlock("div", _hoisted_1, [
52
+ createCommentVNode(" 控制区 "),
53
+ createElementVNode("div", _hoisted_2, [
54
+ _cache[2] || (_cache[2] = createElementVNode(
55
+ "div",
56
+ { class: "flex items-center gap-2 mb-3" },
57
+ [
58
+ createElementVNode("span", { class: "material-icons text-primary text-lg" }, "fingerprint"),
59
+ createElementVNode("span", { class: "text-sm font-bold text-gray-400 uppercase tracking-wider" }, "UUID 生成器")
60
+ ],
61
+ -1
62
+ /* CACHED */
63
+ )),
64
+ createElementVNode("div", _hoisted_3, [
65
+ createElementVNode("div", _hoisted_4, [
66
+ (openBlock(), createElementBlock(
67
+ Fragment,
68
+ null,
69
+ renderList(["v4", "v7"], (v) => {
70
+ return createElementVNode("button", {
71
+ key: v,
72
+ onClick: ($event) => version.value = v,
73
+ class: normalizeClass(["h-10 px-4 font-bold border-2 border-black rounded text-sm transition-all", version.value === v ? "bg-primary text-black shadow-none translate-x-0.5 translate-y-0.5" : "bg-bg-dark text-gray-400 shadow-hard-sm hover:shadow-none hover:translate-x-0.5 hover:translate-y-0.5"])
74
+ }, toDisplayString(v.toUpperCase()), 11, _hoisted_5);
75
+ }),
76
+ 64
77
+ /* STABLE_FRAGMENT */
78
+ ))
79
+ ]),
80
+ createElementVNode("div", _hoisted_6, [
81
+ _cache[1] || (_cache[1] = createElementVNode(
82
+ "span",
83
+ { class: "text-xs text-gray-500" },
84
+ "数量:",
85
+ -1
86
+ /* CACHED */
87
+ )),
88
+ withDirectives(createElementVNode(
89
+ "input",
90
+ {
91
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => count.value = $event),
92
+ type: "number",
93
+ min: "1",
94
+ max: "100",
95
+ class: "w-16 h-10 bg-bg-dark text-gray-100 border-2 border-black rounded px-2 font-mono text-sm text-center outline-none focus:border-primary"
96
+ },
97
+ null,
98
+ 512
99
+ /* NEED_PATCH */
100
+ ), [
101
+ [
102
+ vModelText,
103
+ count.value,
104
+ void 0,
105
+ { number: true }
106
+ ]
107
+ ])
108
+ ]),
109
+ createElementVNode("button", {
110
+ onClick: generate,
111
+ class: "h-10 px-5 bg-primary text-black font-bold border-2 border-black rounded shadow-hard-sm hover:shadow-none hover:translate-x-0.5 hover:translate-y-0.5 transition-all text-sm"
112
+ }, " 生成 "),
113
+ uuids.value.length ? (openBlock(), createElementBlock(
114
+ "button",
115
+ {
116
+ key: 0,
117
+ onClick: copyAll,
118
+ class: "h-10 px-4 bg-bg-dark text-gray-400 font-bold border-2 border-black rounded shadow-hard-sm hover:shadow-none hover:translate-x-0.5 hover:translate-y-0.5 transition-all text-sm"
119
+ },
120
+ toDisplayString(copyField.value === "all" ? "已复制" : "全部复制"),
121
+ 1
122
+ /* TEXT */
123
+ )) : createCommentVNode("v-if", true)
124
+ ])
125
+ ]),
126
+ createCommentVNode(" 结果列表 "),
127
+ createElementVNode("div", _hoisted_7, [
128
+ (openBlock(true), createElementBlock(
129
+ Fragment,
130
+ null,
131
+ renderList(uuids.value, (uuid, i) => {
132
+ return openBlock(), createElementBlock("div", {
133
+ key: i,
134
+ class: "flex items-center justify-between py-2 px-3 rounded hover:bg-white/5 cursor-pointer group",
135
+ onClick: ($event) => copy(uuid, String(i))
136
+ }, [
137
+ createElementVNode(
138
+ "span",
139
+ _hoisted_9,
140
+ toDisplayString(uuid),
141
+ 1
142
+ /* TEXT */
143
+ ),
144
+ createElementVNode(
145
+ "span",
146
+ {
147
+ class: normalizeClass(["material-icons text-sm opacity-0 group-hover:opacity-100 transition-opacity", copyField.value === String(i) ? "text-neon-green" : "text-gray-500"])
148
+ },
149
+ toDisplayString(copyField.value === String(i) ? "check" : "content_copy"),
150
+ 3
151
+ /* TEXT, CLASS */
152
+ )
153
+ ], 8, _hoisted_8);
154
+ }),
155
+ 128
156
+ /* KEYED_FRAGMENT */
157
+ ))
158
+ ])
159
+ ]);
160
+ };
161
+ }
162
+ });
163
+ export {
164
+ _sfc_main as default
165
+ };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@cnc_cbz/usefultools-plugin-official",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "description": "UsefulTools 官方工具插件包",
6
+ "keywords": [
7
+ "usefultools-plugin"
8
+ ],
9
+ "author": "UsefulTools",
10
+ "license": "MIT",
11
+ "files": [
12
+ "plugin.json",
13
+ "dist/*.mjs"
14
+ ],
15
+ "scripts": {
16
+ "dev": "vite",
17
+ "tauri": "tauri",
18
+ "tauri:dev": "tauri dev",
19
+ "build": "tsx build.ts",
20
+ "build:frontend": "vite build --config vite.preview.config.ts"
21
+ },
22
+ "dependencies": {
23
+ "@fontsource/space-grotesk": "^5.2.10",
24
+ "@tauri-apps/api": "^2",
25
+ "@tauri-apps/plugin-dialog": "^2.6.0",
26
+ "@tauri-apps/plugin-fs": "^2.4.5",
27
+ "@tauri-apps/plugin-http": "^2.5.7",
28
+ "@vue-flow/background": "^1.3.2",
29
+ "@vue-flow/controls": "^1.1.3",
30
+ "@vue-flow/core": "^1.48.2",
31
+ "codemirror": "^6.0.2",
32
+ "@codemirror/autocomplete": "^6.20.0",
33
+ "@codemirror/lang-javascript": "^6.2.4",
34
+ "@codemirror/state": "^6.5.4",
35
+ "@codemirror/theme-one-dark": "^6.1.3",
36
+ "crypto-js": "^4.2.0",
37
+ "diff": "^8.0.3",
38
+ "material-icons": "^1.13.14",
39
+ "pako": "^2.1.0",
40
+ "prettier": "^3.8.1",
41
+ "vue": "^3.5.13",
42
+ "vue-json-pretty": "^2.6.0"
43
+ },
44
+ "devDependencies": {
45
+ "@tailwindcss/vite": "^4.1.18",
46
+ "@tauri-apps/cli": "^2",
47
+ "@types/crypto-js": "^4.2.2",
48
+ "@types/diff": "^8.0.0",
49
+ "@types/pako": "^2.0.4",
50
+ "@vitejs/plugin-vue": "^5.2.1",
51
+ "tailwindcss": "^4.1.18",
52
+ "tsx": "^4.19.0",
53
+ "typescript": "~5.6.2",
54
+ "vite": "^6.0.3"
55
+ }
56
+ }
package/plugin.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "plugins": [
3
+ { "id": "json-formatter", "version": "1.0.0", "author": "UsefulTools", "icon": "data_object", "title": "JSON<br/>格式化", "subtitle": "JSON格式化", "description": "格式化、压缩、校验", "bgColor": "bg-primary", "categories": ["编码开发"], "requires": [], "bundle": "dist/json-formatter.mjs" },
4
+ { "id": "js-runner", "version": "1.0.0", "author": "UsefulTools", "icon": "javascript", "title": "JS代码<br/>运行器", "subtitle": "JS代码运行器", "description": "编写、运行、调试JS代码", "bgColor": "bg-electric-blue", "categories": ["编码开发"], "requires": [], "bundle": "dist/js-runner.mjs" },
5
+ { "id": "translator", "version": "1.0.0", "author": "UsefulTools", "icon": "translate", "title": "Google<br/>翻译", "subtitle": "Google翻译", "description": "多语言翻译,支持自动检测", "bgColor": "bg-neon-green", "textColor": "text-black", "categories": ["文本处理"], "requires": ["http"], "bundle": "dist/translator.mjs" },
6
+ { "id": "timestamp-converter", "version": "1.0.0", "author": "UsefulTools", "icon": "schedule", "title": "时间戳<br/>转换", "subtitle": "时间戳转换", "description": "时间戳与日期互转,实时时钟", "bgColor": "bg-vibrant-purple", "categories": ["转换解析"], "requires": [], "bundle": "dist/timestamp-converter.mjs" },
7
+ { "id": "text-diff", "version": "1.0.0", "author": "UsefulTools", "icon": "difference", "title": "文本<br/>对比", "subtitle": "文本对比", "description": "逐行对比两段文本,高亮差异", "bgColor": "bg-coral-red", "categories": ["文本处理"], "requires": [], "bundle": "dist/text-diff.mjs" },
8
+ { "id": "json-diff", "version": "1.0.0", "author": "UsefulTools", "icon": "data_object", "title": "JSON<br/>对比", "subtitle": "JSON对比", "description": "深度比较两个JSON的key与value差异", "bgColor": "bg-hot-pink", "categories": ["编码开发"], "requires": [], "bundle": "dist/json-diff.mjs" },
9
+ { "id": "cron-expression", "version": "1.0.0", "author": "UsefulTools", "icon": "timer", "title": "Cron<br/>表达式", "subtitle": "Cron表达式", "description": "解析Cron表达式,预览执行时间", "bgColor": "bg-electric-blue", "categories": ["转换解析"], "requires": [], "bundle": "dist/cron-expression.mjs" },
10
+ { "id": "regex-tester", "version": "1.0.0", "author": "UsefulTools", "icon": "manage_search", "title": "正则<br/>测试", "subtitle": "正则表达式测试", "description": "测试正则表达式,可视化匹配结果", "bgColor": "bg-vibrant-purple", "categories": ["编码开发"], "requires": [], "bundle": "dist/regex-tester.mjs" },
11
+ { "id": "jwt-parser", "version": "1.0.0", "author": "UsefulTools", "icon": "vpn_key", "title": "JWT<br/>解析器", "subtitle": "JWT解析器", "description": "解析JWT Token,查看Header/Payload/过期状态", "bgColor": "bg-primary", "categories": ["转换解析"], "requires": [], "bundle": "dist/jwt-parser.mjs" },
12
+ { "id": "base-converter", "version": "1.0.0", "author": "UsefulTools", "icon": "swap_horiz", "title": "进制<br/>转换", "subtitle": "进制转换", "description": "二/八/十/十六进制互转,支持自定义进制", "bgColor": "bg-neon-green", "textColor": "text-black", "categories": ["转换解析"], "requires": [], "bundle": "dist/base-converter.mjs" },
13
+ { "id": "cyber-chef", "version": "1.0.0", "author": "UsefulTools", "icon": "science", "title": "加解密<br/>工坊", "subtitle": "加解密工坊", "description": "流程化加解密、编码转换,类似CyberChef", "bgColor": "bg-coral-red", "categories": ["编码开发"], "requires": [], "bundle": "dist/cyber-chef.mjs" },
14
+ { "id": "uuid-generator", "version": "1.0.0", "author": "UsefulTools", "icon": "fingerprint", "title": "UUID<br/>生成器", "subtitle": "UUID生成器", "description": "批量生成 UUID v4/v7,一键复制", "bgColor": "bg-vibrant-purple", "categories": ["生成工具"], "requires": [], "bundle": "dist/uuid-generator.mjs" },
15
+ { "id": "hash-generator", "version": "1.0.0", "author": "UsefulTools", "icon": "tag", "title": "Hash<br/>生成器", "subtitle": "Hash生成器", "description": "MD5、SHA-1、SHA-256、SHA-512 哈希计算", "bgColor": "bg-electric-blue", "categories": ["编码开发"], "requires": [], "bundle": "dist/hash-generator.mjs" },
16
+ { "id": "url-codec", "version": "1.0.0", "author": "UsefulTools", "icon": "link", "title": "URL<br/>编解码", "subtitle": "URL编解码", "description": "URL 编码与解码,处理查询参数", "bgColor": "bg-neon-green", "textColor": "text-black", "categories": ["编码开发"], "requires": [], "bundle": "dist/url-codec.mjs" },
17
+ { "id": "color-converter", "version": "1.0.0", "author": "UsefulTools", "icon": "palette", "title": "颜色<br/>转换器", "subtitle": "颜色转换器", "description": "HEX、RGB、HSL 颜色格式互转", "bgColor": "bg-hot-pink", "categories": ["设计工具"], "requires": [], "bundle": "dist/color-converter.mjs" },
18
+ { "id": "markdown-preview", "version": "1.0.0", "author": "UsefulTools", "icon": "article", "title": "Markdown<br/>预览", "subtitle": "Markdown预览", "description": "实时渲染 Markdown,左右分栏", "bgColor": "bg-primary", "categories": ["文本处理"], "requires": [], "bundle": "dist/markdown-preview.mjs" },
19
+ { "id": "password-generator", "version": "1.0.0", "author": "UsefulTools", "icon": "lock", "title": "密码<br/>生成器", "subtitle": "密码生成器", "description": "自定义长度与字符集,生成随机强密码", "bgColor": "bg-coral-red", "categories": ["生成工具"], "requires": [], "bundle": "dist/password-generator.mjs" },
20
+ { "id": "json-yaml", "version": "1.0.0", "author": "UsefulTools", "icon": "sync_alt", "title": "JSON<br/>YAML", "subtitle": "JSON与YAML互转", "description": "JSON 与 YAML 格式互相转换", "bgColor": "bg-electric-blue", "categories": ["转换解析"], "requires": [], "bundle": "dist/json-yaml.mjs" },
21
+ { "id": "lorem-ipsum", "version": "1.0.0", "author": "UsefulTools", "icon": "notes", "title": "Lorem<br/>Ipsum", "subtitle": "Lorem Ipsum生成器", "description": "生成占位文本,支持段落/句子/单词", "bgColor": "bg-vibrant-purple", "categories": ["生成工具"], "requires": [], "bundle": "dist/lorem-ipsum.mjs" },
22
+ { "id": "qr-generator", "version": "1.0.0", "author": "UsefulTools", "icon": "qr_code_2", "title": "QR码<br/>生成器", "subtitle": "QR码生成器", "description": "输入 URL 或文本生成二维码", "bgColor": "bg-neon-green", "textColor": "text-black", "categories": ["生成工具"], "requires": [], "bundle": "dist/qr-generator.mjs" },
23
+ { "id": "html-entity", "version": "1.0.0", "author": "UsefulTools", "icon": "code", "title": "HTML<br/>实体", "subtitle": "HTML实体编解码", "description": "HTML 实体编码与解码", "bgColor": "bg-primary", "categories": ["编码开发"], "requires": [], "bundle": "dist/html-entity.mjs" },
24
+ { "id": "sql-formatter", "version": "1.0.0", "author": "UsefulTools", "icon": "storage", "title": "SQL<br/>格式化", "subtitle": "SQL格式化", "description": "格式化 SQL 语句,提升可读性", "bgColor": "bg-hot-pink", "categories": ["编码开发"], "requires": [], "bundle": "dist/sql-formatter.mjs" },
25
+ { "id": "image-compressor", "version": "1.0.0", "author": "UsefulTools", "icon": "photo_size_select_large", "title": "图片<br/>压缩", "subtitle": "图片压缩转换", "description": "压缩图片、转换格式(JPEG/PNG/WebP)", "bgColor": "bg-coral-red", "categories": ["设计工具"], "requires": [], "bundle": "dist/image-compressor.mjs" },
26
+ { "id": "case-converter", "version": "1.0.0", "author": "UsefulTools", "icon": "text_fields", "title": "大小写<br/>转换", "subtitle": "大小写转换", "description": "camelCase、snake_case、kebab-case 等互转", "bgColor": "bg-electric-blue", "categories": ["文本处理"], "requires": [], "bundle": "dist/case-converter.mjs" },
27
+ { "id": "ip-subnet", "version": "1.0.0", "author": "UsefulTools", "icon": "lan", "title": "IP/子网<br/>计算器", "subtitle": "IP子网计算器", "description": "子网掩码计算、IP 范围查询", "bgColor": "bg-vibrant-purple", "categories": ["转换解析"], "requires": [], "bundle": "dist/ip-subnet.mjs" },
28
+ { "id": "text-stats", "version": "1.0.0", "author": "UsefulTools", "icon": "analytics", "title": "文本<br/>统计", "subtitle": "文本统计", "description": "字数、字符数、行数、词频统计", "bgColor": "bg-neon-green", "textColor": "text-black", "categories": ["文本处理"], "requires": [], "bundle": "dist/text-stats.mjs" },
29
+ { "id": "chmod-calculator", "version": "1.0.0", "author": "UsefulTools", "icon": "security", "title": "Chmod<br/>计算器", "subtitle": "Chmod权限计算器", "description": "Unix文件权限计算,八进制/符号互转", "bgColor": "bg-coral-red", "categories": ["转换解析"], "requires": [], "bundle": "dist/chmod-calculator.mjs" }
30
+ ]
31
+ }