23 | : never
24 | : P extends keyof T
25 | ? T[P]
26 | : never;
27 |
28 | const store = {
29 | get: (path: P): GetValue => {
30 | return ipcRenderer.sendSync("store", { type: "get", path });
31 | },
32 | set: (
33 | path: P,
34 | value: GetValue | (unknown & {}),
35 | ): void => {
36 | ipcRenderer.send("store", { type: "set", path, value });
37 | },
38 | getAll: () => {
39 | return ipcRenderer.sendSync("store", { type: "getAll" }) as setting;
40 | },
41 | setAll: (value: setting) => {
42 | ipcRenderer.send("store", { type: "setAll", value });
43 | },
44 | };
45 |
46 | export default store;
47 |
48 | export type { SettingPath, GetValue };
49 |
--------------------------------------------------------------------------------
/lib/store/store.ts:
--------------------------------------------------------------------------------
1 | const { app } = require("electron");
2 | const fs = require("node:fs") as typeof import("fs");
3 | const path = require("node:path") as typeof import("path");
4 | import type { GetValue, SettingPath } from "./renderStore";
5 | import type { setting } from "../../src/ShareTypes";
6 |
7 | type data = {
8 | [key: string]: unknown;
9 | };
10 |
11 | class Store {
12 | private configPath: string;
13 | private data: data | undefined;
14 |
15 | constructor() {
16 | this.configPath = path.join(app.getPath("userData"), "config.json");
17 | if (!fs.existsSync(this.configPath)) {
18 | this.init();
19 | }
20 | this.data = this.getStore();
21 | }
22 |
23 | private init() {
24 | fs.writeFileSync(this.configPath, "{}");
25 | this.data = {};
26 | }
27 |
28 | private getStore() {
29 | if (this.data) return this.data;
30 | let str = "{}";
31 | try {
32 | str = fs.readFileSync(this.configPath).toString() || "{}";
33 | } catch (error) {
34 | this.init();
35 | }
36 | return JSON.parse(str) as data;
37 | }
38 |
39 | private setStore(data: data) {
40 | this.data = data;
41 | fs.writeFileSync(this.configPath, JSON.stringify(data, null, 2));
42 | }
43 |
44 | set(
45 | keyPath: P,
46 | value: GetValue | (unknown & {}),
47 | ): void {
48 | const store = this.getStore();
49 | const pathx = keyPath.split(".");
50 | let obj = store;
51 | for (let i = 0; i < pathx.length; i++) {
52 | const p = pathx[i];
53 | if (i === pathx.length - 1) obj[p] = value;
54 | else {
55 | if (obj[p]?.constructor !== Object) {
56 | if (!Number.isNaN(Number(pathx[i + 1]))) {
57 | obj[p] = [];
58 | } else {
59 | obj[p] = {};
60 | }
61 | }
62 | // @ts-ignore
63 | obj = obj[p];
64 | }
65 | }
66 | this.setStore(store);
67 | }
68 | get(keyPath: P): GetValue {
69 | const store = this.getStore();
70 | const pathx = keyPath.split(".");
71 | const lastp = pathx.pop() ?? "";
72 | // @ts-ignore
73 | const lastobj = pathx.reduce((p, c) => {
74 | return p[c] || {};
75 | }, store);
76 | return lastobj[lastp];
77 | }
78 |
79 | clear() {
80 | this.init();
81 | }
82 |
83 | getAll() {
84 | return this.getStore();
85 | }
86 | setAll(data: data) {
87 | this.setStore(data);
88 | }
89 | }
90 |
91 | export default Store;
92 |
--------------------------------------------------------------------------------
/lib/time_format.ts:
--------------------------------------------------------------------------------
1 | function format(_fmt: string, date: Date) {
2 | let fmt = _fmt;
3 | const opt = {
4 | YYYY: date.getFullYear(),
5 | YY: date.getFullYear() % 1000,
6 | MM: String(date.getMonth() + 1).padStart(2, "0"),
7 | M: date.getMonth() + 1,
8 | DD: String(date.getDate()).padStart(2, "0"),
9 | D: date.getDate(),
10 | d: date.getDay(),
11 | HH: String(date.getHours()).padStart(2, "0"),
12 | H: date.getHours(),
13 | hh: String(date.getHours() % 12).padStart(2, "0"),
14 | h: date.getHours() % 12,
15 | mm: String(date.getMinutes()).padStart(2, "0"),
16 | m: date.getMinutes(),
17 | ss: String(date.getSeconds()).padStart(2, "0"),
18 | s: date.getSeconds(),
19 | S: date.getMilliseconds(),
20 | };
21 | for (const k in opt) {
22 | const ret = new RegExp(`${k}`, "g");
23 | fmt = fmt.replace(ret, `${String(opt[k])}`);
24 | }
25 | return fmt;
26 | }
27 | export default format;
28 |
--------------------------------------------------------------------------------
/lib/translate/ignore.json:
--------------------------------------------------------------------------------
1 | ["×5", "×10"]
2 |
--------------------------------------------------------------------------------
/lib/translate/readme.md:
--------------------------------------------------------------------------------
1 | # 翻译 Translate
2 |
3 | 创建 csv,指定存在的语言进行编辑或指定新语言
4 | Create a csv, specify an existing language for editing or specify a new language
5 |
6 | > [!TIP]
7 | > 选项`-a`用于输出全部文字。如果你发现原来的翻译存在不足,需要修改,请使用这一选项。如果你需要跟进翻译,即 eSearch 的某些文字已经修改,但翻译未修改,请不使用这一选项
8 | >
9 | > The option `- a` is used to output all text. If you find that the original translation is inadequate and need to be modified, please use this option. If you need to follow up on the translation, that is, some text in eSearch has been modified, but the translation has not been modified, please do not use this option
10 |
11 | ```shell
12 | node lib/translate/tool.js -l en
13 | # en.csv
14 | # 或 or
15 | node lib/translate/tool.js -l en -a
16 | ```
17 |
18 | ```csv
19 | "abc","你好世界",""
20 | ...
21 | ```
22 |
23 | with arg `-e`,output Chinese with English
24 |
25 | ```csv
26 | "abc","你好世界","Hello World",""
27 | ...
28 | ```
29 |
30 | 编辑 edit
31 |
32 | > [!TIP]
33 | > AI prompt:“以下是 csv 文件,把第二列翻译成 en 并复制到第三列”
34 |
35 | ```csv
36 | "abc","你好世界","Hello World"
37 | ...
38 | ```
39 |
40 | 保存 save:
41 |
42 | ```shell
43 | node lib/translate/tool.js -i en.csv
44 | ```
45 |
46 | 只有在 source.json 里定义的文字才能被翻译。如果找不到需要翻译的文字,那可能是我没针对某些页面进行国际化,请在 issue 上提交 bug,指明需要国际化位置
47 |
48 | Only words defined in source.json can be translated. If you can't find the text that needs to be translated, it may be that I have not internationalized some pages. Please submit bug on issue to indicate the location where you need to be internationalized.
49 |
50 | ## 标记翻译进度
51 |
52 | `tool.js`定义了 srcCommit,借助 git 的 diff 功能,来方便地了解需要翻译什么内容。翻译完某个 id 后,将其添加到对应语言的`finishId`,再次输出 csv 时,将忽略他,这样可以专注与未翻译/修改的翻译。如果全部翻译完,请把 srcCommit 的`id`改为 latestSrcId,并清空`finishId`
53 |
54 | `tool.js` defines srcCommit. With the diff feature of git, you can easily understand what needs to be translated. After translating an id, add it to the `finishiId` of the corresponding language, and ignore it when you output csv again, so that you can focus on the untranslated / modified translation. If you have finished the translation, please change the `id` of srcCommit to latestSrcId and clear `finishiId`
55 |
56 | ## 开发者
57 |
58 | 一般地,若 source.json 没有定义语言,在 console 控制台以红色 🟥 字体和背景输出,若未翻译,以蓝色 🟦 字体和背景
59 |
60 | In general, if source.json does not have a defined language, it will be output in red 🟥 font and background on the console, and in blue 🟦 font and background if not translated.
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eSearch",
3 | "version": "14.8.0",
4 | "description": "识屏 · 搜索",
5 | "main": "./out/main/main.js",
6 | "scripts": {
7 | "pack": "npm run build && electron-builder --dir -c electron-builder.config.js",
8 | "dist": "npm run build && electron-builder -p never -c electron-builder.config.js",
9 | "start": "electron-vite --ignoreConfigWarning preview",
10 | "dev": "electron-vite --ignoreConfigWarning dev",
11 | "build": "electron-vite --ignoreConfigWarning build",
12 | "uitest": "electron-vite -c electron.vite.config_test.ts"
13 | },
14 | "author": {
15 | "name": "xsf",
16 | "email": "xushengfeng_zg@163.com"
17 | },
18 | "homepage": "https://github.com/xushengfeng/eSearch/",
19 | "license": "GPL-3.0",
20 | "dependencies": {
21 | "@erase2d/fabric": "^1.1.6",
22 | "@techstark/opencv-js": "4.10.0-release.1",
23 | "chroma-js": "^3.1.1",
24 | "diff-match-patch": "^1.0.5",
25 | "diff-match-patch-line-and-word": "^0.1.3",
26 | "dkh-ui": "^0.14.0",
27 | "download": "^8.0.0",
28 | "esearch-ocr": "^8.4.4",
29 | "esearch-seg": "^1.1.4",
30 | "fabric": "^6.4.0",
31 | "franc": "^6.2.0",
32 | "fuse.js": "^7.1.0",
33 | "gifenc": "^1.0.3",
34 | "hex-to-css-filter": "^5.4.0",
35 | "hotkeys-js": "^3.13.7",
36 | "iso-639-3-to-1": "^1.0.0",
37 | "minimist": "^1.2.8",
38 | "mp4-muxer": "^5.1.3",
39 | "node-screenshots": "^0.2.1",
40 | "onnxruntime-node": "^1.21.0",
41 | "qr-scanner-wechat": "^0.1.3",
42 | "remarkable": "^2.0.1",
43 | "sortablejs": "^1.15.2",
44 | "uiohook-napi": "^1.5.4",
45 | "webm-muxer": "^5.0.2",
46 | "xtranslator": "^1.3.2"
47 | },
48 | "devDependencies": {
49 | "@biomejs/biome": "^1.9.4",
50 | "@types/chroma-js": "^3.1.1",
51 | "@types/diff-match-patch": "^1.0.34",
52 | "@types/minimist": "^1.2.5",
53 | "@types/node": "^22.9.3",
54 | "@types/sortablejs": "^1.15.8",
55 | "archiver": "^7.0.1",
56 | "canvas": "^3.1.0",
57 | "csv-parse": "^5.5.5",
58 | "electron": "^35.0.1",
59 | "electron-builder": "^25.1.8",
60 | "electron-vite": "^2.1.0",
61 | "svgo": "^3.3.2",
62 | "typescript": "^5.7.2",
63 | "vite": "^5.4.12",
64 | "vite-plugin-image-optimizer": "^1.1.8",
65 | "xtimelog": "^1.0.5"
66 | },
67 | "optionalDependencies": {
68 | "node-screenshots-darwin-arm64": "0.1.9",
69 | "node-screenshots-darwin-x64": "0.1.9",
70 | "node-screenshots-linux-x64-gnu": "0.1.9",
71 | "node-screenshots-linux-x64-musl": "0.1.9",
72 | "node-screenshots-win32-arm64-msvc": "0.1.9",
73 | "node-screenshots-win32-ia32-msvc": "0.1.9",
74 | "node-screenshots-win32-x64-msvc": "0.1.9"
75 | },
76 | "pnpm": {
77 | "neverBuiltDependencies": [
78 | "canvas"
79 | ]
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/script/gen_icon_types.ts:
--------------------------------------------------------------------------------
1 | const path = require("node:path") as typeof import("path");
2 | const fs = require("node:fs") as typeof import("fs");
3 |
4 | const assetsPath = path.join(
5 | __dirname,
6 | "..",
7 | "src",
8 | "renderer",
9 | "assets",
10 | "icons",
11 | );
12 | const typesPath = path.join(__dirname, "..", "src", "iconTypes.d.ts");
13 |
14 | console.log(assetsPath);
15 |
16 | const iconTypes = fs
17 | .readdirSync(assetsPath, { withFileTypes: true })
18 | .filter((dirent) => !dirent.isDirectory())
19 | .map((dirent) => dirent.name);
20 |
21 | const content = `export type RawIconType = ${iconTypes.map((name) => `"${name}"`).join(" | ")};\n`;
22 |
23 | const content2 = `export type IconType = ${iconTypes.map((name) => `"${name.replace(/\.svg$/, "")}"`).join(" | ")};\n`;
24 |
25 | fs.writeFileSync(typesPath, `${content}\n${content2}`);
26 |
--------------------------------------------------------------------------------
/script/release_fast_download.ts:
--------------------------------------------------------------------------------
1 | const release: Record> = {
2 | win32: { gh: ["exe", "zip"], arch: ["x64", "arm64"] },
3 | linux: { gh: ["AppImage", "deb", "rpm", "tar.gz"], arch: ["x64", "arm64"] },
4 | darwin: { gh: ["dmg", "zip"], arch: ["x64", "arm64"] },
5 | };
6 |
7 | function getUrl(url: string) {
8 | const version = require("../package.json").version;
9 | let t = "| | Windows | macOS | Linux|\n| --- | --- | --- | --- |\n";
10 | for (const arch of ["x64", "arm64"]) {
11 | t += `|${arch}| `;
12 | for (const p of ["win32", "darwin", "linux"]) {
13 | if (!release[p].arch.includes(arch)) continue;
14 | t += `${(release[p].gh || []).map((i) => `[${i}](${url.replaceAll("$v", version).replace("$arch", arch).replace("$p", p).replace("$h", i)})`).join(" ")}|`;
15 | }
16 | t += "\n";
17 | }
18 | return t;
19 | }
20 |
21 | console.log(
22 | "⚡镜像下载:\n",
23 | getUrl(
24 | "https://github.moeyy.xyz/https://github.com/xushengfeng/eSearch/releases/download/$v/eSearch-$v-$p-$arch.$h",
25 | ),
26 | );
27 |
--------------------------------------------------------------------------------
/src/iconTypes.d.ts:
--------------------------------------------------------------------------------
1 | export type RawIconType = "add.svg" | "add_history.svg" | "ai_check.svg" | "arrow.svg" | "back.svg" | "blur.svg" | "brightness.svg" | "browser.svg" | "camera.svg" | "circle.svg" | "clear.svg" | "close.svg" | "concise.svg" | "contrast.svg" | "copy.svg" | "cut.svg" | "delete.svg" | "delete_enter.svg" | "ding.svg" | "down.svg" | "draw.svg" | "draw_select.svg" | "eraser.svg" | "excel.svg" | "file.svg" | "fill_storke.svg" | "filters.svg" | "free_draw.svg" | "free_select.svg" | "handle.svg" | "help.svg" | "hide.svg" | "history.svg" | "hue.svg" | "ignore.svg" | "img.svg" | "last.svg" | "last_last.svg" | "left.svg" | "line.svg" | "link.svg" | "long_clip.svg" | "main.svg" | "mask.svg" | "md.svg" | "mic.svg" | "minimize.svg" | "next.svg" | "next_next.svg" | "number.svg" | "ocr.svg" | "opacity.svg" | "open.svg" | "paste.svg" | "pause.svg" | "pixelate.svg" | "play_pause.svg" | "polygon.svg" | "polyline.svg" | "position.svg" | "position_back.svg" | "position_backwards.svg" | "position_forwards.svg" | "position_front.svg" | "record.svg" | "rect.svg" | "rect_select.svg" | "recume.svg" | "regex.svg" | "reload.svg" | "replace.svg" | "replace_all.svg" | "right.svg" | "saturation.svg" | "save.svg" | "scan.svg" | "screen.svg" | "search.svg" | "select_all.svg" | "setting.svg" | "shapes.svg" | "size.svg" | "space.svg" | "spray.svg" | "star.svg" | "start_record.svg" | "stop_record.svg" | "super_edit.svg" | "text.svg" | "toptop.svg" | "translate.svg" | "up.svg" | "updown.svg";
2 |
3 | export type IconType = "add" | "add_history" | "ai_check" | "arrow" | "back" | "blur" | "brightness" | "browser" | "camera" | "circle" | "clear" | "close" | "concise" | "contrast" | "copy" | "cut" | "delete" | "delete_enter" | "ding" | "down" | "draw" | "draw_select" | "eraser" | "excel" | "file" | "fill_storke" | "filters" | "free_draw" | "free_select" | "handle" | "help" | "hide" | "history" | "hue" | "ignore" | "img" | "last" | "last_last" | "left" | "line" | "link" | "long_clip" | "main" | "mask" | "md" | "mic" | "minimize" | "next" | "next_next" | "number" | "ocr" | "opacity" | "open" | "paste" | "pause" | "pixelate" | "play_pause" | "polygon" | "polyline" | "position" | "position_back" | "position_backwards" | "position_forwards" | "position_front" | "record" | "rect" | "rect_select" | "recume" | "regex" | "reload" | "replace" | "replace_all" | "right" | "saturation" | "save" | "scan" | "screen" | "search" | "select_all" | "setting" | "shapes" | "size" | "space" | "spray" | "star" | "start_record" | "stop_record" | "super_edit" | "text" | "toptop" | "translate" | "up" | "updown";
4 |
--------------------------------------------------------------------------------
/src/renderer/aiVision.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/renderer/assets/1temple.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
75 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/add.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
85 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/back.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
95 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/blur.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
96 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/camera.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
88 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
90 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/concise.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/contrast.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/ding.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
90 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/down.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/draw.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
86 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/draw_select.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
86 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/eraser.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
96 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/filters.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
91 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/free_select.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
86 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/help.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/hue.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/last.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
84 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/last_last.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
90 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/line.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
80 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/long_clip.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/main.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
81 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/minimize.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
80 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/next.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
85 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/next_next.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
90 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/paste.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/pause.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
85 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/play_pause.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
88 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/polygon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
92 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/polyline.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
81 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/record.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/rect.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
83 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/rect_select.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/recume.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
81 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
90 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
86 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/setting.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
81 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/shapes.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/size.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/space.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
86 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/start_record.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
96 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/stop_record.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
96 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/text.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
84 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/toptop.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/icons/up.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/src/renderer/assets/sample_picture.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/renderer/browser_bg.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/renderer/browser_bg/browser_bg.ts:
--------------------------------------------------------------------------------
1 | import { ele, initDKH, p, view } from "dkh-ui";
2 | import { t } from "../../../lib/translate/translate";
3 |
4 | initDKH({ pureStyle: true });
5 |
6 | const pEl = view("y")
7 | .style({
8 | width: "100vw",
9 | height: "100vh",
10 | alignItems: "center",
11 | justifyContent: "center",
12 | })
13 | .addInto();
14 | const h1 = ele("h1")
15 | .addInto(pEl)
16 | .bindSet((v: string, el) => {
17 | el.innerText = t(v);
18 | });
19 | const details = view().addInto(pEl);
20 |
21 | const search = new URLSearchParams(location.search);
22 |
23 | if (navigator.onLine) {
24 | switch (search.get("type")) {
25 | case "did-fail-load":
26 | h1.sv("加载失败");
27 | details
28 | .clear()
29 | .add([
30 | p(`${t("错误代码:")}${search.get("err_code")}`),
31 | p(`${t("错误描述:")}${search.get("err_des")}`),
32 | ]);
33 | break;
34 | case "render-process-gone":
35 | h1.sv("进程崩溃");
36 | break;
37 | case "unresponsive":
38 | h1.sv("页面未响应");
39 | break;
40 | case "certificate-error":
41 | h1.sv("证书错误");
42 | break;
43 | default:
44 | break;
45 | }
46 | } else {
47 | h1.sv("无网络连接");
48 | }
49 |
--------------------------------------------------------------------------------
/src/renderer/capture.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/renderer/css/ding.css:
--------------------------------------------------------------------------------
1 | body {
2 | --hover-color: var(--bar-hover-color) !important;
3 | }
4 | ::-webkit-scrollbar {
5 | display: none;
6 | }
7 | html,
8 | body {
9 | margin: 0;
10 | width: 100%;
11 | height: 100%;
12 | }
13 | #photo {
14 | width: 100%;
15 | height: 100%;
16 | }
17 | .ding_photo {
18 | position: fixed;
19 | overflow: hidden;
20 | &:focus {
21 | outline: none;
22 | }
23 | }
24 | .ding_photo:hover,
25 | .ding_photo_h {
26 | box-shadow: var(--shadow);
27 | }
28 | #tool_bar {
29 | overflow: hidden;
30 | position: absolute;
31 | width: 100%;
32 | z-index: 2;
33 | }
34 | #tool_bar_c {
35 | display: none;
36 | align-items: center;
37 | backdrop-filter: var(--blur);
38 | background: var(--bar-bg);
39 | width: 100%;
40 | transform: translateY(-105%);
41 | transition: var(--transition);
42 | user-select: none;
43 | }
44 | #tool_bar_c > div {
45 | display: flex;
46 | align-items: center;
47 | width: auto;
48 | }
49 | .img {
50 | position: absolute;
51 | width: 100%;
52 | user-select: none;
53 | }
54 |
55 | #b {
56 | border-radius: var(--border-radius);
57 | overflow: hidden;
58 | transition: var(--transition);
59 | }
60 | #b:hover {
61 | box-shadow: var(--shadow);
62 | }
63 | #b > button {
64 | transition: var(--transition);
65 | background-color: transparent;
66 | padding: 0;
67 | width: 32px;
68 | height: 32px;
69 | }
70 | #b > button > .icon {
71 | width: 32px;
72 | }
73 |
74 | .minimize {
75 | opacity: 0 !important;
76 | pointer-events: none !important;
77 | }
78 |
79 | #dock {
80 | position: fixed;
81 | height: 50px;
82 | width: 10px;
83 | left: 0;
84 | top: 0;
85 | transition: var(--transition);
86 | z-index: 2;
87 | }
88 |
89 | .dock {
90 | width: 200px !important;
91 | height: 100% !important;
92 | top: 0 !important;
93 | border-radius: 0 !important;
94 | }
95 |
96 | .dock_left {
97 | left: 0 !important;
98 | }
99 |
100 | .dock_right {
101 | left: calc(100vw - 200px) !important;
102 | }
103 |
104 | #dock > div {
105 | overflow: hidden;
106 | width: 100%;
107 | display: none;
108 | }
109 | #dock img {
110 | width: 100%;
111 | }
112 | .i_bar {
113 | position: absolute;
114 | right: 8px;
115 | border-radius: var(--o-padding);
116 | }
117 |
--------------------------------------------------------------------------------
/src/renderer/css/recorder.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | #m {
6 | overflow: hidden;
7 | display: flex;
8 | flex-direction: column;
9 | align-items: center;
10 | border-radius: var(--border-radius);
11 | justify-content: space-between;
12 | height: 100vh;
13 | transition: var(--transition);
14 | }
15 |
16 | #video {
17 | display: flex;
18 | justify-content: center;
19 | align-items: center;
20 | width: 100%;
21 | height: 100%;
22 | border-radius: var(--border-radius);
23 | overflow: hidden;
24 | }
25 | #v_p {
26 | border-radius: var(--border-radius);
27 | overflow: hidden;
28 | }
29 | video {
30 | border-radius: var(--border-radius);
31 | position: relative;
32 | }
33 |
34 | #s {
35 | height: 0;
36 | }
37 | .s_show {
38 | padding: 8px;
39 | height: auto !important;
40 | }
41 |
42 | #jdt {
43 | -webkit-appearance: none;
44 | width: 100%;
45 | border-radius: 2px;
46 | background: var(--hover-color);
47 | margin: 0;
48 | }
49 |
50 | #jdt::-webkit-slider-thumb {
51 | -webkit-appearance: none;
52 | background-color: var(--font-color);
53 | border-radius: 2px;
54 | height: 16px;
55 | width: 4px;
56 | }
57 | input {
58 | vertical-align: middle;
59 | font-family: var(--main-font);
60 | }
61 | input[type="number"] {
62 | width: 16px;
63 | padding: 0;
64 | }
65 |
66 | #t_nt,
67 | #t_t {
68 | font-family: var(--monospace);
69 | }
70 |
71 | #t_start,
72 | #t_end {
73 | width: 100px;
74 | }
75 |
76 | select {
77 | border: none;
78 | background-color: transparent;
79 | font-size: 1em;
80 | }
81 |
82 | .pro_p {
83 | width: 100%;
84 | height: 2rem;
85 | line-height: 2rem;
86 | border-radius: var(--border-radius);
87 | overflow: hidden;
88 | background: var(--hover-color);
89 | margin-top: 4px;
90 | }
91 | .pro {
92 | width: 0%;
93 | height: 100%;
94 | transition: var(--transition);
95 | border-radius: var(--border-radius);
96 | white-space: nowrap;
97 | }
98 | .pro_running {
99 | background-color: #9999ff;
100 | }
101 | .pro_ok {
102 | background-color: #99ff99;
103 | }
104 | .pro_error {
105 | background-color: #ff9999;
106 | }
107 |
108 | .hide_log {
109 | display: none;
110 | }
111 |
112 | textarea {
113 | width: 100%;
114 | min-height: 2lh;
115 | }
116 |
--------------------------------------------------------------------------------
/src/renderer/css/recorderTip.css:
--------------------------------------------------------------------------------
1 | body {
2 | user-select: none;
3 | }
4 |
5 | #recorder_rect {
6 | border: 1px dashed #000;
7 | box-sizing: border-box;
8 | width: 100vw;
9 | height: 100vh;
10 | }
11 |
12 | #mouse_c {
13 | display: none;
14 | position: fixed;
15 | width: 24px;
16 | height: 24px;
17 | transform: translate(-50%, -50%);
18 | border-radius: 50%;
19 | background-color: #ff08;
20 | overflow: hidden;
21 | }
22 |
23 | #recorder_bar {
24 | position: absolute;
25 | }
26 |
27 | #recorder_key {
28 | display: flex;
29 | gap: 4px;
30 | }
31 |
32 | #recorder_key > div {
33 | display: flex;
34 | transition: var(--transition);
35 | }
36 | #recorder_key kbd {
37 | transition: var(--transition);
38 | display: flex;
39 | flex-direction: column-reverse;
40 | min-width: 2em;
41 | height: 2em;
42 | border-radius: 0.25em;
43 | justify-content: space-between;
44 | }
45 |
46 | .only_key {
47 | font-size: 1em;
48 | font-weight: bold;
49 | align-items: center;
50 | justify-content: center !important;
51 | }
52 | .main_key {
53 | font-size: 1em;
54 | font-weight: bold;
55 | height: 60%;
56 | }
57 | .top_key {
58 | font-size: 0.75em;
59 | height: 30%;
60 | line-height: 100%;
61 | }
62 | .right_key {
63 | align-items: end;
64 | }
65 |
66 | #recorder_key .key_hidden {
67 | opacity: 0.5;
68 | }
69 |
70 | #mouse_c > div:nth-child(1) {
71 | width: 10px;
72 | height: 100%;
73 | }
74 | #mouse_c > div:nth-child(2) {
75 | width: 4px;
76 | height: 100%;
77 | }
78 | #mouse_c > div:nth-child(3) {
79 | width: 10px;
80 | height: 100%;
81 | }
82 |
83 | .wheel_u {
84 | clip-path: polygon(50% 0, 0% 100%, 100% 100%);
85 | }
86 | .wheel_d {
87 | clip-path: polygon(50% 100%, 0% 0%, 100% 0%);
88 | }
89 | .wheel_l {
90 | clip-path: polygon(0 50%, 100% 0%, 100% 100%);
91 | }
92 | .wheel_r {
93 | clip-path: polygon(100% 50%, 0% 0%, 0% 100%);
94 | }
95 |
--------------------------------------------------------------------------------
/src/renderer/ding.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/renderer/editor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/renderer/lib/removeObj.ts:
--------------------------------------------------------------------------------
1 | type ort = typeof import("onnxruntime-node");
2 |
3 | async function removeObj(op: {
4 | ort: ort;
5 | session: Awaited>;
6 | img: HTMLImageElement | HTMLCanvasElement | HTMLVideoElement;
7 | mask: ImageData;
8 | }) {
9 | const w =
10 | op.img instanceof HTMLImageElement ? op.img.naturalWidth : op.img.width;
11 | const h =
12 | op.img instanceof HTMLImageElement
13 | ? op.img.naturalHeight
14 | : op.img.height;
15 | const imputImg = new OffscreenCanvas(w, h).getContext("2d");
16 | if (!imputImg) throw new Error("imputImg is null");
17 | imputImg.drawImage(op.img, 0, 0);
18 | const imgData = imputImg.getImageData(0, 0, w, h);
19 |
20 | const ort = op.ort;
21 |
22 | const maskOrt = op.session;
23 | const r = new Uint8Array(w * h);
24 | const g = new Uint8Array(w * h);
25 | const b = new Uint8Array(w * h);
26 | for (let i = 0; i < w * h; i++) {
27 | r[i] = imgData.data[4 * i];
28 | g[i] = imgData.data[4 * i + 1];
29 | b[i] = imgData.data[4 * i + 2];
30 | }
31 | const input = new ort.Tensor("uint8", [...r, ...g, ...b], [1, 3, h, w]);
32 | const m = new Uint8Array(w * h);
33 | for (let i = 0; i < w * h; i++) {
34 | m[i] = op.mask.data[4 * i];
35 | }
36 | const maskInput = new ort.Tensor("uint8", m, [1, 1, h, w]);
37 | const output = await maskOrt.run({
38 | [maskOrt.inputNames[0]]: input,
39 | [maskOrt.inputNames[1]]: maskInput,
40 | });
41 | console.log(output);
42 | const outputData0 = output[maskOrt.outputNames[0]].data as Uint8Array;
43 | const outputData = new ImageData(w, h);
44 | const wh = w * h;
45 | for (let i = 0; i < w * h; i++) {
46 | outputData.data[4 * i] = outputData0[i];
47 | outputData.data[4 * i + 1] = outputData0[i + wh];
48 | outputData.data[4 * i + 2] = outputData0[i + wh * 2];
49 | outputData.data[4 * i + 3] = 255;
50 | }
51 | return outputData;
52 | }
53 |
54 | export default removeObj;
55 |
--------------------------------------------------------------------------------
/src/renderer/photoEditor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/renderer/recorder.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/renderer/recorderTip.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/renderer/setting.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/renderer/translate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/renderer/translator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/renderer/ui/ui.ts:
--------------------------------------------------------------------------------
1 | import { button, ele, image, initDKH, input, pack, select, view } from "dkh-ui";
2 | import { Class, getImgUrl } from "../root/root";
3 | import type { IconType } from "../../iconTypes";
4 |
5 | console.log("hi");
6 |
7 | function iconBEl(src: IconType) {
8 | return button().add(image(getImgUrl(`${src}.svg`), "icon").class("icon"));
9 | }
10 |
11 | initDKH({ pureStyle: true });
12 |
13 | pack(document.body).style({ background: "var(--bg)" });
14 |
15 | const p = view("x").style({ gap: "8px", padding: "8px" }).addInto();
16 | p.add([
17 | button("文字按钮"),
18 | iconBEl("translate"),
19 | select([{ value: "1" }]),
20 | input().sv("hi"),
21 | input("number"),
22 | input("checkbox"),
23 | view()
24 | .class(Class.group)
25 | .add([button("hi"), select([])]),
26 | ]);
27 |
28 | ele("textarea").addInto();
29 |
30 | const p2 = view("x")
31 | .style({ background: "linear-gradient(blue, pink)" })
32 | .addInto();
33 | const bar = view().class(Class.glassBar).addInto(p2);
34 | bar.add([iconBEl("translate")]);
35 |
--------------------------------------------------------------------------------
/src/renderer/ui_test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | eSearch - 高级编辑
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/renderer/videoEditor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/ui/clip.ts:
--------------------------------------------------------------------------------
1 | import { initConfig, restoreConfig, runApp, sleep } from "./init.ts";
2 |
3 | initConfig();
4 |
5 | const app = runApp();
6 |
7 | await sleep(10000);
8 |
9 | await sleep(10000);
10 |
11 | app.kill();
12 | app.kill("SIGKILL");
13 |
14 | console.log("stop app");
15 |
16 | restoreConfig();
17 |
18 | process.exit(0);
19 |
--------------------------------------------------------------------------------
/test/ui/init.ts:
--------------------------------------------------------------------------------
1 | import { exec, execSync } from "node:child_process";
2 | import { renameSync, rmSync } from "node:fs";
3 |
4 | const appPath =
5 | process.platform === "linux"
6 | ? `${process.env.HOME}/.config/eSearch`
7 | : process.platform === "win32"
8 | ? `${process.env.APPDATA}/eSearch`
9 | : process.platform === "darwin"
10 | ? `${process.env.HOME}/Library/Application Support/eSearch`
11 | : "";
12 | const configPath = `${appPath}/config.json`;
13 | const backupPath = `${appPath}/config.json.bak`;
14 |
15 | function runApp() {
16 | return exec("pnpm run start");
17 | }
18 |
19 | function initConfig() {
20 | renameSync(configPath, backupPath);
21 | }
22 |
23 | function restoreConfig() {
24 | try {
25 | rmSync(configPath);
26 | } catch (error) {}
27 | renameSync(backupPath, configPath);
28 | }
29 |
30 | function sleep(ms: number) {
31 | return new Promise((resolve) => setTimeout(resolve, ms));
32 | }
33 |
34 | export { runApp, initConfig, restoreConfig, sleep };
35 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.node.json"
6 | },
7 | {
8 | "path": "./tsconfig.web.json"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "moduleResolution": "node",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noImplicitAny": false,
15 | "noImplicitReturns": true,
16 | "composite": true,
17 | "types": [
18 | "node"
19 | ]
20 | },
21 | "include": [
22 | "electron.vite.config.*",
23 | "src/main/*",
24 | "src/preload/*",
25 | "lib/**/*.ts",
26 | "src/renderer/screenShot/*"
27 | ],
28 | }
--------------------------------------------------------------------------------
/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "moduleResolution": "node",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noImplicitAny": false,
15 | "noImplicitReturns": true,
16 | "composite": true,
17 | "lib": [
18 | "ESNext",
19 | "DOM",
20 | "DOM.Iterable"
21 | ],
22 | },
23 | "include": [
24 | "src/renderer/**/*.ts",
25 | "src/preload/*.d.ts",
26 | "lib/**/*.ts",
27 | ]
28 | }
--------------------------------------------------------------------------------