ref: format

This commit is contained in:
alikia2x (寒寒) 2024-12-29 22:35:25 +08:00
parent bc0483cdc8
commit d2899d4b50
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
48 changed files with 600 additions and 568 deletions

View File

@ -1,8 +1,8 @@
{ {
"useTabs": true, "useTabs": true,
"tabWidth": 4, "tabWidth": 4,
"trailingComma": "none", "trailingComma": "none",
"singleQuote": false, "singleQuote": false,
"printWidth": 100, "printWidth": 100,
"endOfLine": "lf" "endOfLine": "lf"
} }

View File

@ -9,11 +9,11 @@ to rewind.ai, and that app is **OpenRewind**.
### Update the OCR Engine ### Update the OCR Engine
OpenRecall currently uses docTR as its OCR engine, but it performs inadequately. OpenRecall currently uses docTR as its OCR engine, but it performs inadequately.
On my MacBook Air M2 (2022), processing a screenshot takes around 20 seconds, with CPU usage peaking at over 400%. On my MacBook Air M2 (2022), processing a screenshot takes around 20 seconds, with CPU usage peaking at over 400%.
During this time, screenshots cannot be captured, and the engine appears to recognize only Latin characters. During this time, screenshots cannot be captured, and the engine appears to recognize only Latin characters.
To address this, we plan to replace the OCR with a more efficient alternative that supports multiple writing systems. To address this, we plan to replace the OCR with a more efficient alternative that supports multiple writing systems.
We are working on [RapidOCR ONNX](https://github.com/alikia2x/RapidOCR-ONNX), a fork of a project which has same name, We are working on [RapidOCR ONNX](https://github.com/alikia2x/RapidOCR-ONNX), a fork of a project which has same name,
developed by RapidAI. developed by RapidAI.
RapidOCR ONNX uses [PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR) as its model architecture, and RapidOCR ONNX uses [PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR) as its model architecture, and
@ -21,17 +21,17 @@ runs on the [ONNX Runtime](https://github.com/microsoft/onnxruntime/).
### Implement a Task Queue/Scheduler ### Implement a Task Queue/Scheduler
Currently, OpenRecall's OCR recognition and database operations are synchronous (blocking). Currently, OpenRecall's OCR recognition and database operations are synchronous (blocking).
This results in increased screenshot frequency, as described in the previous section. This results in increased screenshot frequency, as described in the previous section.
Our next goal is to introduce a task queue to handle high-load tasks (such as OCR, indexing, and archiving) asynchronously. This will ensure that time-sensitive tasks (like capturing screenshots) are prioritized. Our next goal is to introduce a task queue to handle high-load tasks (such as OCR, indexing, and archiving) asynchronously. This will ensure that time-sensitive tasks (like capturing screenshots) are prioritized.
### Improve the Frontend ### Improve the Frontend
The current frontend of OpenRecall is quite basic. Given my expertise in web development, The current frontend of OpenRecall is quite basic. Given my expertise in web development,
I will build a more elegant frontend from scratch. I will build a more elegant frontend from scratch.
We are now switched to Electron in order to deliver a native experience, We are now switched to Electron in order to deliver a native experience,
aiming to match the functionality of [rewind.ai](https://rewind.ai). aiming to match the functionality of [rewind.ai](https://rewind.ai).
### Add More Features ### Add More Features

View File

@ -7,14 +7,22 @@ export default function IconWithText() {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className="flex"> <div className="flex">
<img src={imgUrl} className="h-20 w-20 mr-2" alt="OpenRewind icon"/> <img src={imgUrl} className="h-20 w-20 mr-2" alt="OpenRewind icon" />
<div className="flex flex-col justify-start w-auto h-[4.2rem] overflow-hidden mt-1"> <div className="flex flex-col justify-start w-auto h-[4.2rem] overflow-hidden mt-1">
<span className="text-2xl font-semibold">OpenRewind</span> <span className="text-2xl font-semibold">OpenRewind</span>
<span className="text-sm text-gray-700 dark:text-gray-200 <span
font-medium ml-0.5">{t("settings.version", { version: pjson.version })}</span> className="text-sm text-gray-700 dark:text-gray-200
<span className="text-xs text-gray-700 dark:text-gray-200 font-medium ml-0.5"
font-medium ml-0.5">{t("settings.copyright")}</span> >
{t("settings.version", { version: pjson.version })}
</span>
<span
className="text-xs text-gray-700 dark:text-gray-200
font-medium ml-0.5"
>
{t("settings.copyright")}
</span>
</div> </div>
</div> </div>
); );
} }

View File

@ -1,10 +1,14 @@
import { MouseEventHandler } from "react"; import { MouseEventHandler } from "react";
import { Icon } from "@iconify-icon/react"; import { Icon } from "@iconify-icon/react";
const MenuItem = ({ icon, text, onClick }: { const MenuItem = ({
icon: string, icon,
text: string, text,
onClick: MouseEventHandler<HTMLDivElement> onClick
}: {
icon: string;
text: string;
onClick: MouseEventHandler<HTMLDivElement>;
}) => { }) => {
return ( return (
<div <div
@ -20,4 +24,4 @@ const MenuItem = ({ icon, text, onClick }: {
); );
}; };
export default MenuItem; export default MenuItem;

View File

@ -7,10 +7,8 @@ export default function OpenSourceNote() {
OpenRewind is open source software licensed under OpenRewind is open source software licensed under
<a href="https://www.gnu.org/licenses/gpl-3.0.html">GPL 3.0</a>.<br /> <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPL 3.0</a>.<br />
Source code is avaliable at Source code is avaliable at
<a href="https://github.com/alikia2x/openrewind"> <a href="https://github.com/alikia2x/openrewind">GitHub</a>.
GitHub
</a>.
</Trans> </Trans>
</p> </p>
) );
} }

View File

@ -1,9 +1,15 @@
import * as React from "react"; import * as React from "react";
import { useRef } from "react"; import { useRef } from "react";
const SettingsGroup = ( const SettingsGroup = ({
{ children, groupName, addGroupRef }: children,
{ children: React.ReactNode, groupName: string, addGroupRef: Function }) => { groupName,
addGroupRef
}: {
children: React.ReactNode;
groupName: string;
addGroupRef: Function;
}) => {
const groupRef = useRef(null); const groupRef = useRef(null);
React.useEffect(() => { React.useEffect(() => {
@ -17,4 +23,4 @@ const SettingsGroup = (
); );
}; };
export default SettingsGroup; export default SettingsGroup;

View File

@ -2,9 +2,7 @@ import { useTranslation } from "react-i18next";
const Title = ({ i18nKey }: { i18nKey: string }) => { const Title = ({ i18nKey }: { i18nKey: string }) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return <h1 className="text-3xl font-bold leading-[3rem]">{t(i18nKey)}</h1>;
<h1 className="text-3xl font-bold leading-[3rem]">{t(i18nKey)}</h1> };
);
}
export default Title; export default Title;

View File

@ -1,6 +1,6 @@
# Database Schema Changelog # Database Schema Changelog
This document outlines the changes made across different versions of This document outlines the changes made across different versions of
database structure used in the OpenRewind, including tables and fields. database structure used in the OpenRewind, including tables and fields.
## Version 3 Schema Changes ## Version 3 Schema Changes
@ -22,11 +22,14 @@ ALTER TABLE encoding_task RENAME COLUMN createAt TO createdAt;
The `createdAt` column was updated to store Unix timestamps instead of formatted timestamps. The `createdAt` column was updated to store Unix timestamps instead of formatted timestamps.
```typescript ```typescript
const rows = db.prepare(`SELECT id, createdAt FROM encoding_task`).all() as { [x: string]: unknown; id: unknown; }[]; const rows = db.prepare(`SELECT id, createdAt FROM encoding_task`).all() as {
[x: string]: unknown;
id: unknown;
}[];
const updateStmt = db.prepare(`UPDATE encoding_task SET createdAt_new = ? WHERE id = ?`); const updateStmt = db.prepare(`UPDATE encoding_task SET createdAt_new = ? WHERE id = ?`);
rows.forEach((row) => { rows.forEach((row) => {
const unixTimestamp = convertTimestampToUnix(row.createdAt as string); const unixTimestamp = convertTimestampToUnix(row.createdAt as string);
updateStmt.run(unixTimestamp, row.id); updateStmt.run(unixTimestamp, row.id);
}); });
``` ```
@ -45,11 +48,14 @@ ALTER TABLE frame RENAME COLUMN createAt TO createdAt;
The `createdAt` column was updated to store Unix timestamps instead of formatted timestamps. The `createdAt` column was updated to store Unix timestamps instead of formatted timestamps.
```typescript ```typescript
const rows = db.prepare(`SELECT id, createdAt FROM frame`).all() as { [x: string]: unknown; id: unknown; }[]; const rows = db.prepare(`SELECT id, createdAt FROM frame`).all() as {
[x: string]: unknown;
id: unknown;
}[];
const updateStmt = db.prepare(`UPDATE frame SET createdAt_new = ? WHERE id = ?`); const updateStmt = db.prepare(`UPDATE frame SET createdAt_new = ? WHERE id = ?`);
rows.forEach((row) => { rows.forEach((row) => {
const unixTimestamp = convertTimestampToUnix(row.createdAt as string); const unixTimestamp = convertTimestampToUnix(row.createdAt as string);
updateStmt.run(unixTimestamp, row.id); updateStmt.run(unixTimestamp, row.id);
}); });
``` ```
@ -69,12 +75,15 @@ ALTER TABLE segments RENAME COLUMN endAt TO endedAt;
The `startedAt` and `endedAt` columns were updated to store Unix timestamps instead of formatted timestamps. The `startedAt` and `endedAt` columns were updated to store Unix timestamps instead of formatted timestamps.
```typescript ```typescript
const rows = db.prepare(`SELECT id, startedAt, endedAt FROM segments`).all() as { [x: string]: unknown; id: unknown; }[]; const rows = db.prepare(`SELECT id, startedAt, endedAt FROM segments`).all() as {
[x: string]: unknown;
id: unknown;
}[];
const updateStart = db.prepare(`UPDATE segments SET startedAt_new = ? WHERE id = ?`); const updateStart = db.prepare(`UPDATE segments SET startedAt_new = ? WHERE id = ?`);
const updateEnd = db.prepare(`UPDATE segments SET endedAt_new = ? WHERE id = ?`); const updateEnd = db.prepare(`UPDATE segments SET endedAt_new = ? WHERE id = ?`);
rows.forEach((row) => { rows.forEach((row) => {
updateStart.run(convertTimestampToUnix(row.startedAt as string), row.id); updateStart.run(convertTimestampToUnix(row.startedAt as string), row.id);
updateEnd.run(convertTimestampToUnix(row.endedAt as string), row.id); updateEnd.run(convertTimestampToUnix(row.endedAt as string), row.id);
}); });
``` ```
@ -89,15 +98,15 @@ ALTER TABLE frame DROP COLUMN encoded;
### Summary of Changes ### Summary of Changes
- **Update `encoding_task` Table:** - **Update `encoding_task` Table:**
- Renamed `createAt` to `createdAt`. - Renamed `createAt` to `createdAt`.
- Converted `createdAt` to store Unix timestamps. - Converted `createdAt` to store Unix timestamps.
- **Update `frame` Table:** - **Update `frame` Table:**
- Renamed `createAt` to `createdAt`. - Renamed `createAt` to `createdAt`.
- Converted `createdAt` to store Unix timestamps. - Converted `createdAt` to store Unix timestamps.
- Dropped the deprecated `encoded` column. - Dropped the deprecated `encoded` column.
- **Update `segments` Table:** - **Update `segments` Table:**
- Renamed `startAt` and `endAt` to `startedAt` and `endedAt` respectively. - Renamed `startAt` and `endAt` to `startedAt` and `endedAt` respectively.
- Converted `startedAt` and `endedAt` to store Unix timestamps. - Converted `startedAt` and `endedAt` to store Unix timestamps.
## Version 2 Schema ## Version 2 Schema
@ -107,10 +116,10 @@ Corresponding version: 0.4.0
Stores configuration data, including the database version. Stores configuration data, including the database version.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------|-----------|---------------------|-----------------------------------------------------------------------------| | ----------- | --------- | ------------------- | -------------------------------------- |
| `key` | TEXT | PRIMARY KEY | Unique key for configuration settings. | | `key` | TEXT | PRIMARY KEY | Unique key for configuration settings. |
| `value` | TEXT | | Value associated with the key. | | `value` | TEXT | | Value associated with the key. |
#### Insert Default Version #### Insert Default Version
@ -123,7 +132,7 @@ INSERT INTO config (key, value) VALUES ('version', '2');
Stores encoding tasks that are queued for processing. Stores encoding tasks that are queued for processing.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------|-----------|----------------------------|--------------------------------------| | ----------- | --------- | -------------------------- | ------------------------------------ |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique ID for the task. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique ID for the task. |
| `createAt` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Timestamp when the task was created. | | `createAt` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Timestamp when the task was created. |
| `status` | INTEGER | DEFAULT 0 | Indicates the status of the task. | | `status` | INTEGER | DEFAULT 0 | Indicates the status of the task. |
@ -132,8 +141,8 @@ Stores encoding tasks that are queued for processing.
- `0`: Pending - `0`: Pending
- `1`: In Progress - `1`: In Progress
- `2`: Completed - `2`: Completed
- Once the task was set to this status, it will be imminently deleted by a trigger mentioned below. - Once the task was set to this status, it will be imminently deleted by a trigger mentioned below.
### New Trigger: `delete_encoding_task` ### New Trigger: `delete_encoding_task`
@ -158,11 +167,10 @@ END;
Stores the frames that need to be encoded for the encoding task Stores the frames that need to be encoded for the encoding task
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|------------------|-----------|-------------------------------------|------------------------------------------------------| | ---------------- | --------- | ----------------------------------- | ---------------------------------------------------- |
| `frame` | INTEGER | PRIMARY KEY, FOREIGN KEY (frame.id) | ID for the frame associated with the encoding task. | | `frame` | INTEGER | PRIMARY KEY, FOREIGN KEY (frame.id) | ID for the frame associated with the encoding task. |
| `encodingTaskID` | TIMESTAMP | FOREIGN KEY (encoding_task.id) | ID for the encoding task associated with this frame. | | `encodingTaskID` | TIMESTAMP | FOREIGN KEY (encoding_task.id) | ID for the encoding task associated with this frame. |
### Update `frame` Table ### Update `frame` Table
#### Simplify `imgFilename` #### Simplify `imgFilename`
@ -170,12 +178,12 @@ Stores the frames that need to be encoded for the encoding task
The `imgFilename` column was updated to store only the filename without the full path. The `imgFilename` column was updated to store only the filename without the full path.
```typescript ```typescript
const rows = db.prepare('SELECT id, imgFilename FROM frame').all() as OldFrame[]; const rows = db.prepare("SELECT id, imgFilename FROM frame").all() as OldFrame[];
rows.forEach(row => { rows.forEach((row) => {
const filename = row.imgFilename.match(/[^\\/]+$/)?.[0]; const filename = row.imgFilename.match(/[^\\/]+$/)?.[0];
if (filename) { if (filename) {
db.prepare('UPDATE frame SET imgFilename = ? WHERE id = ?').run(filename, row.id); db.prepare("UPDATE frame SET imgFilename = ? WHERE id = ?").run(filename, row.id);
} }
}); });
``` ```
@ -192,12 +200,11 @@ UPDATE frame SET encodeStatus = CASE WHEN encoded THEN 2 ELSE 0 END;
- **New Table:** `config` to store configuration data. - **New Table:** `config` to store configuration data.
- **Update `frame` Table:** - **Update `frame` Table:**
- Simplified `imgFilename` to store only the filename. - Simplified `imgFilename` to store only the filename.
- Added `encodeStatus` column to replace the deprecated `encoded` column. - Added `encodeStatus` column to replace the deprecated `encoded` column.
- **Deprecated `encoded` column.** - **Deprecated `encoded` column.**
- The `encoded` column is no longer used and is retained due to SQLite's inability to drop columns. - The `encoded` column is no longer used and is retained due to SQLite's inability to drop columns.
Creating a new table without this column and copying data to the new table could be time-consuming. Creating a new table without this column and copying data to the new table could be time-consuming.
## Version 1 Schema ## Version 1 Schema
@ -208,7 +215,7 @@ Corresponding version: 0.3.x
Stores information about individual frames. Stores information about individual frames.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------------|-----------|---------------------------------|-------------------------------------------------------------------------| | ----------------- | --------- | ------------------------------- | ----------------------------------------------------------------------- |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each frame. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each frame. |
| `createAt` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Timestamp when the frame was created. | | `createAt` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Timestamp when the frame was created. |
| `imgFilename` | TEXT | | Filename of the image associated with the frame. | | `imgFilename` | TEXT | | Filename of the image associated with the frame. |
@ -223,7 +230,7 @@ Stores information about individual frames.
Stores recognition data associated with frames. Stores recognition data associated with frames.
| Column Name | Data Type | Constraints/Default | Desc[database-structure.md](database-structure.md)ription | | Column Name | Data Type | Constraints/Default | Desc[database-structure.md](database-structure.md)ription |
|-------------|-----------|----------------------------|-----------------------------------------------------------| | ----------- | --------- | -------------------------- | --------------------------------------------------------- |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each recognition data entry. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each recognition data entry. |
| `frameID` | INTEGER | FOREIGN KEY (frame.id) | ID of the frame to which the recognition data belongs. | | `frameID` | INTEGER | FOREIGN KEY (frame.id) | ID of the frame to which the recognition data belongs. |
| `data` | TEXT | | Raw recognition data. | | `data` | TEXT | | Raw recognition data. |
@ -236,7 +243,7 @@ While capturing the screen, OpenRewind retrieves the currently active window.
When it finds that the currently active window has changed to another application, a new segment will start. When it finds that the currently active window has changed to another application, a new segment will start.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|---------------|-----------|----------------------------|------------------------------------------------------| | ------------- | --------- | -------------------------- | ---------------------------------------------------- |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each segment. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each segment. |
| `startAt` | TIMESTAMP | | Timestamp when the segment starts. | | `startAt` | TIMESTAMP | | Timestamp when the segment starts. |
| `endAt` | TIMESTAMP | | Timestamp when the segment ends. | | `endAt` | TIMESTAMP | | Timestamp when the segment ends. |
@ -253,7 +260,7 @@ When it finds that the currently active window has changed to another applicatio
Used for full-text search on recognition data. Used for full-text search on recognition data.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------|-----------|---------------------|--------------------------------------------------------| | ----------- | --------- | ------------------- | ------------------------------------------------------ |
| `id` | INTEGER | UNINDEXED | ID of the recognition data entry. | | `id` | INTEGER | UNINDEXED | ID of the recognition data entry. |
| `frameID` | INTEGER | UNINDEXED | ID of the frame to which the recognition data belongs. | | `frameID` | INTEGER | UNINDEXED | ID of the frame to which the recognition data belongs. |
| `data` | TEXT | | Raw recognition data. | | `data` | TEXT | | Raw recognition data. |

View File

@ -6,10 +6,10 @@ This document outlines the current structure of the database schema used in the
Stores configuration data. Stores configuration data.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------|-----------|---------------------|-----------------------------------------------------------------------------| | ----------- | --------- | ------------------- | -------------------------------------- |
| `key` | TEXT | PRIMARY KEY | Unique key for configuration settings. | | `key` | TEXT | PRIMARY KEY | Unique key for configuration settings. |
| `value` | TEXT | | Value associated with the key. | | `value` | TEXT | | Value associated with the key. |
### Key: version ### Key: version
@ -20,7 +20,7 @@ The current database schema version, represented as an integer. Since the `confi
Stores information about individual frames. Stores information about individual frames.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------------|-----------|---------------------------------|-----------------------------------------------------------| | ----------------- | --------- | ------------------------------- | --------------------------------------------------------- |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each frame. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each frame. |
| `createdAt` | REAL | | Timestamp when the frame was created. | | `createdAt` | REAL | | Timestamp when the frame was created. |
| `imgFilename` | TEXT | | Filename of the image associated with the frame. | | `imgFilename` | TEXT | | Filename of the image associated with the frame. |
@ -40,19 +40,19 @@ Stores information about individual frames.
Stores recognition data associated with frames. Stores recognition data associated with frames.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------|-----------|------------------------------|-----------------------------------------------------------------------------| | ----------- | --------- | -------------------------- | ------------------------------------------------------ |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each recognition data entry. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each recognition data entry. |
| `frameID` | INTEGER | FOREIGN KEY (frame.id) | ID of the frame to which the recognition data belongs. | | `frameID` | INTEGER | FOREIGN KEY (frame.id) | ID of the frame to which the recognition data belongs. |
| `data` | TEXT | | Raw recognition data. | | `data` | TEXT | | Raw recognition data. |
| `text` | TEXT | | Recognized text. | | `text` | TEXT | | Recognized text. |
## Table: `segments` ## Table: `segments`
A segment is a period of time when a user uses a particular application. While capturing the screen, OpenRewind detects the currently active window. When it finds that the currently active window has changed to another application, a new segment will start. A segment is a period of time when a user uses a particular application. While capturing the screen, OpenRewind detects the currently active window. When it finds that the currently active window has changed to another application, a new segment will start.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|---------------|-----------|----------------------------|------------------------------------------------------| | ------------- | --------- | -------------------------- | ---------------------------------------------------- |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each segment. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier for each segment. |
| `startedAt` | REAL | | Timestamp when the segment starts. | | `startedAt` | REAL | | Timestamp when the segment starts. |
| `endedAt` | REAL | | Timestamp when the segment ends. | | `endedAt` | REAL | | Timestamp when the segment ends. |
@ -69,7 +69,7 @@ A segment is a period of time when a user uses a particular application. While c
Stores encoding tasks that are queued for processing. Stores encoding tasks that are queued for processing.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------|-----------|----------------------------|--------------------------------------| | ----------- | --------- | -------------------------- | ------------------------------------ |
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique ID for the task. | | `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique ID for the task. |
| `createdAt` | REAL | | Timestamp when the task was created. | | `createdAt` | REAL | | Timestamp when the task was created. |
| `status` | INTEGER | DEFAULT 0 | Indicates the status of the task. | | `status` | INTEGER | DEFAULT 0 | Indicates the status of the task. |
@ -79,14 +79,14 @@ Stores encoding tasks that are queued for processing.
- `0`: Pending - `0`: Pending
- `1`: In Progress - `1`: In Progress
- `2`: Completed - `2`: Completed
- Once the task was set to this status, it will be imminently deleted by a trigger mentioned below. - Once the task was set to this status, it will be imminently deleted by a trigger mentioned below.
## Table: `encoding_task_data` ## Table: `encoding_task_data`
Stores the frames that need to be encoded for the encoding task. Stores the frames that need to be encoded for the encoding task.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|------------------|-----------|-------------------------------------|------------------------------------------------------| | ---------------- | --------- | ----------------------------------- | ---------------------------------------------------- |
| `encodingTaskID` | INTEGER | FOREIGN KEY (encoding_task.id) | ID for the encoding task associated with this frame. | | `encodingTaskID` | INTEGER | FOREIGN KEY (encoding_task.id) | ID for the encoding task associated with this frame. |
| `frame` | INTEGER | PRIMARY KEY, FOREIGN KEY (frame.id) | ID for the frame associated with the encoding task. | | `frame` | INTEGER | PRIMARY KEY, FOREIGN KEY (frame.id) | ID for the frame associated with the encoding task. |
@ -95,7 +95,7 @@ Stores the frames that need to be encoded for the encoding task.
Used for full-text search on recognition data. Used for full-text search on recognition data.
| Column Name | Data Type | Constraints/Default | Description | | Column Name | Data Type | Constraints/Default | Description |
|-------------|-----------|---------------------|--------------------------------------------------------| | ----------- | --------- | ------------------- | ------------------------------------------------------ |
| `id` | INTEGER | UNINDEXED | ID of the recognition data entry. | | `id` | INTEGER | UNINDEXED | ID of the recognition data entry. |
| `frameID` | INTEGER | UNINDEXED | ID of the frame to which the recognition data belongs. | | `frameID` | INTEGER | UNINDEXED | ID of the frame to which the recognition data belongs. |
| `data` | TEXT | | Raw recognition data. | | `data` | TEXT | | Raw recognition data. |
@ -154,4 +154,4 @@ BEGIN
DELETE FROM encoding_task DELETE FROM encoding_task
WHERE id = OLD.id AND NEW.status = 2; WHERE id = OLD.id AND NEW.status = 2;
END; END;
``` ```

View File

@ -1,32 +1,28 @@
{ {
"appId": "com.alikia2x.openrewind", "appId": "com.alikia2x.openrewind",
"mac": { "mac": {
"category": "public.app-category.productivity", "category": "public.app-category.productivity",
"target": "dmg", "target": "dmg",
"files": [ "files": ["bin/macos"]
"bin/macos" },
] "productName": "OpenRewind",
}, "directories": {
"productName": "OpenRewind", "output": "dist/release"
"directories": { },
"output": "dist/release" "files": [
}, "dist/electron/**/*",
"files": [ "dist/electron/assets/*",
"dist/electron/**/*", "dist/electron/i18n/*",
"dist/electron/assets/*", "dist/renderer/**/*",
"dist/electron/i18n/*", "dist/renderer/assets/*"
"dist/renderer/**/*", ],
"dist/renderer/assets/*" "win": {
], "target": "nsis",
"win": { "files": ["bin/win32"]
"target": "nsis", },
"files": [ "linux": {
"bin/win32" "target": "AppImage"
] },
}, "copyright": "Copyright © 2024 alikia2x",
"linux": { "asar": false
"target": "AppImage" }
},
"copyright": "Copyright © 2024 alikia2x",
"asar": false
}

View File

@ -4,42 +4,37 @@ import ts from "gulp-typescript";
import clean from "gulp-clean"; import clean from "gulp-clean";
import fs from "fs"; import fs from "fs";
const tsProject = ts.createProject('tsconfig.json'); const tsProject = ts.createProject("tsconfig.json");
gulp.task('clean', function () { gulp.task("clean", function () {
return gulp.src('dist/electron', {read: false, allowEmpty: true}) return gulp.src("dist/electron", { read: false, allowEmpty: true }).pipe(clean());
.pipe(clean());
}); });
gulp.task('scripts', () => { gulp.task("scripts", () => {
if (!fs.existsSync("dist/electron")) { if (!fs.existsSync("dist/electron")) {
fs.mkdirSync("dist/electron", { recursive: true }); fs.mkdirSync("dist/electron", { recursive: true });
} }
const tsResult = tsProject.src() const tsResult = tsProject.src().pipe(tsProject());
.pipe(tsProject());
const jsFiles = gulp.src(['src/electron/**/*.js', 'src/electron/**/*.cjs']); const jsFiles = gulp.src(["src/electron/**/*.js", "src/electron/**/*.cjs"]);
return tsResult.js return tsResult.js.pipe(gulp.dest("dist/electron")).on("end", () => {
.pipe(gulp.dest('dist/electron')) jsFiles.pipe(gulp.dest("dist/electron"));
.on('end', () => { });
jsFiles.pipe(gulp.dest('dist/electron'));
});
}); });
gulp.task('assets', () => { gulp.task("assets", () => {
return gulp.src('src/electron/assets/**/*', { encoding: false }) return gulp
.pipe(gulp.dest('dist/electron/assets')); .src("src/electron/assets/**/*", { encoding: false })
.pipe(gulp.dest("dist/electron/assets"));
}); });
gulp.task('binary', () => { gulp.task("binary", () => {
return gulp.src('bin/**/*', { encoding: false }) return gulp.src("bin/**/*", { encoding: false }).pipe(gulp.dest("dist/electron/bin"));
.pipe(gulp.dest('dist/electron/bin'));
}); });
gulp.task("locales", () => { gulp.task("locales", () => {
return gulp.src('i18n/**/*') return gulp.src("i18n/**/*").pipe(gulp.dest("dist/electron/i18n"));
.pipe(gulp.dest('dist/electron/i18n')); });
})
gulp.task('build', gulp.series('clean', 'scripts', 'assets', 'binary', 'locales')); gulp.task("build", gulp.series("clean", "scripts", "assets", "binary", "locales"));

View File

@ -1,8 +1,8 @@
{ {
"settings": "إعدادات", "settings": "إعدادات",
"tray": { "tray": {
"quit": "يترك", "quit": "يترك",
"showMainWindow": "يبحث", "showMainWindow": "يبحث",
"showSettingsWindow": "إعدادات" "showSettingsWindow": "إعدادات"
} }
} }

View File

@ -1,8 +1,8 @@
{ {
"settings": "Einstellungen", "settings": "Einstellungen",
"tray": { "tray": {
"quit": "Aufhören", "quit": "Aufhören",
"showMainWindow": "Suchen", "showMainWindow": "Suchen",
"showSettingsWindow": "Einstellungen" "showSettingsWindow": "Einstellungen"
} }
} }

View File

@ -1,22 +1,22 @@
{ {
"settings": { "settings": {
"title": "Settings", "title": "Settings",
"about": "About", "about": "About",
"screen": "Screen", "screen": "Screen",
"screen-recording": "Screen Recording", "screen-recording": "Screen Recording",
"version": "Version {version}", "version": "Version {version}",
"copyright": "Copyright © 2024 alikia2x", "copyright": "Copyright © 2024 alikia2x",
"note": "OpenRewind is an open source software licensed under <1>GPL 3.0</1>, <3></3>and its source code is avaliable at <5>GitHub</5>.", "note": "OpenRewind is an open source software licensed under <1>GPL 3.0</1>, <3></3>and its source code is avaliable at <5>GitHub</5>.",
"environment-details": "Environment Info", "environment-details": "Environment Info",
"node-version": "Node.js Version", "node-version": "Node.js Version",
"electron-version": "Electron Version", "electron-version": "Electron Version",
"chrome-version": "Chrome Version", "chrome-version": "Chrome Version",
"os-version": "OS Version", "os-version": "OS Version",
"os": "Operating System" "os": "Operating System"
}, },
"tray": { "tray": {
"showMainWindow": "Search", "showMainWindow": "Search",
"showSettingsWindow": "Settings", "showSettingsWindow": "Settings",
"quit": "Quit" "quit": "Quit"
} }
} }

View File

@ -1,8 +1,8 @@
{ {
"settings": "Ajustes", "settings": "Ajustes",
"tray": { "tray": {
"quit": "Abandonar", "quit": "Abandonar",
"showMainWindow": "Buscar", "showMainWindow": "Buscar",
"showSettingsWindow": "Ajustes" "showSettingsWindow": "Ajustes"
} }
} }

View File

@ -1,8 +1,8 @@
{ {
"settings": "Paramètres", "settings": "Paramètres",
"tray": { "tray": {
"quit": "Quitter", "quit": "Quitter",
"showMainWindow": "Recherche", "showMainWindow": "Recherche",
"showSettingsWindow": "Paramètres" "showSettingsWindow": "Paramètres"
} }
} }

View File

@ -1,8 +1,8 @@
{ {
"settings": "Impostazioni", "settings": "Impostazioni",
"tray": { "tray": {
"quit": "Esentato", "quit": "Esentato",
"showMainWindow": "Ricerca", "showMainWindow": "Ricerca",
"showSettingsWindow": "Impostazioni" "showSettingsWindow": "Impostazioni"
} }
} }

View File

@ -1,8 +1,8 @@
{ {
"settings": "設定", "settings": "設定",
"tray": { "tray": {
"quit": "やめる", "quit": "やめる",
"showMainWindow": "検索", "showMainWindow": "検索",
"showSettingsWindow": "設定" "showSettingsWindow": "設定"
} }
} }

View File

@ -1,8 +1,8 @@
{ {
"settings": "설정", "settings": "설정",
"tray": { "tray": {
"quit": "그만두다", "quit": "그만두다",
"showMainWindow": "찾다", "showMainWindow": "찾다",
"showSettingsWindow": "설정" "showSettingsWindow": "설정"
} }
} }

View File

@ -1,22 +1,22 @@
{ {
"settings": { "settings": {
"title": "设置", "title": "设置",
"about": "关于", "about": "关于",
"screen": "屏幕", "screen": "屏幕",
"screen-recording": "屏幕录制", "screen-recording": "屏幕录制",
"version": "版本 {version}", "version": "版本 {version}",
"copyright": "版权所有 © 2024 alikia2x", "copyright": "版权所有 © 2024 alikia2x",
"note": "OpenRewind 是基于<1>GPL 3.0</1>授权的开源软件, <3></3>其源代码托管在<5>GitHub</5>上。", "note": "OpenRewind 是基于<1>GPL 3.0</1>授权的开源软件, <3></3>其源代码托管在<5>GitHub</5>上。",
"environment-details": "运行环境", "environment-details": "运行环境",
"node-version": "Node.js 版本", "node-version": "Node.js 版本",
"electron-version": "Electron 版本", "electron-version": "Electron 版本",
"chrome-version": "Chrome 版本", "chrome-version": "Chrome 版本",
"os-version": "系统版本", "os-version": "系统版本",
"os": "操作系统" "os": "操作系统"
}, },
"tray": { "tray": {
"showMainWindow": "搜索…", "showMainWindow": "搜索…",
"showSettingsWindow": "应用设置", "showSettingsWindow": "应用设置",
"quit": "退出 OpenRewind" "quit": "退出 OpenRewind"
} }
} }

View File

@ -1,84 +1,85 @@
{ {
"name": "openrewind", "name": "openrewind",
"version": "0.6.0", "version": "0.6.0",
"type": "module", "type": "module",
"description": "Your second brain, superpowered.", "description": "Your second brain, superpowered.",
"main": "dist/electron/index.js", "main": "dist/electron/index.js",
"scripts": { "scripts": {
"dev": "cross-env NODE_ENV=dev bun run dev:all", "dev": "cross-env NODE_ENV=dev bun run dev:all",
"dev:all": "concurrently -n=react,electron -c='#ff3e00',blue \"bun run dev:react\" \"bun run dev:electron\"", "dev:all": "concurrently -n=react,electron -c='#ff3e00',blue \"bun run dev:react\" \"bun run dev:electron\"",
"dev:react": "vite dev", "dev:react": "vite dev",
"dev:electron": "bunx gulp build && electron dist/electron/index.js", "dev:electron": "bunx gulp build && electron dist/electron/index.js",
"build:react": "vite build", "build:react": "vite build",
"build:app": "bunx gulp build", "build:app": "bunx gulp build",
"build:electron": "electron-builder" "build:electron": "electron-builder",
}, "format": "bunx prettier --write ."
"keywords": [], },
"author": "", "keywords": [],
"license": "MIT", "author": "",
"dependencies": { "license": "MIT",
"@alikia/random-key": "npm:@jsr/alikia__random-key", "dependencies": {
"@electron/remote": "^2.1.2", "@alikia/random-key": "npm:@jsr/alikia__random-key",
"@hono/node-server": "^1.13.7", "@electron/remote": "^2.1.2",
"@unly/universal-language-detector": "^2.0.3", "@hono/node-server": "^1.13.7",
"better-sqlite3": "^11.6.0", "@unly/universal-language-detector": "^2.0.3",
"detect-port": "^2.1.0", "better-sqlite3": "^11.6.0",
"electron-context-menu": "^4.0.4", "detect-port": "^2.1.0",
"electron-reloader": "^1.2.3", "electron-context-menu": "^4.0.4",
"electron-screencapture": "^1.1.0", "electron-reloader": "^1.2.3",
"electron-serve": "^2.1.1", "electron-screencapture": "^1.1.0",
"electron-store": "^10.0.0", "electron-serve": "^2.1.1",
"electron-window-state": "^5.0.3", "electron-store": "^10.0.0",
"execa": "^9.5.1", "electron-window-state": "^5.0.3",
"hono": "^4.6.15", "execa": "^9.5.1",
"i18next": "^24.0.2", "hono": "^4.6.15",
"i18next-browser-languagedetector": "^8.0.0", "i18next": "^24.0.2",
"i18next-electron-fs-backend": "^3.0.2", "i18next-browser-languagedetector": "^8.0.0",
"i18next-fs-backend": "^2.6.0", "i18next-electron-fs-backend": "^3.0.2",
"i18next-icu": "^2.3.0", "i18next-fs-backend": "^2.6.0",
"image-size": "^1.1.1", "i18next-icu": "^2.3.0",
"memory-cache": "^0.2.0", "image-size": "^1.1.1",
"react": "^18.3.1", "memory-cache": "^0.2.0",
"react-dom": "^18.3.1", "react": "^18.3.1",
"react-i18next": "^15.1.2", "react-dom": "^18.3.1",
"react-router": "^7.0.1", "react-i18next": "^15.1.2",
"react-router-dom": "^7.0.1", "react-router": "^7.0.1",
"screenshot-desktop": "^1.15.0", "react-router-dom": "^7.0.1",
"sqlite3": "^5.1.7", "screenshot-desktop": "^1.15.0",
"sqlstring": "^2.3.3", "sqlite3": "^5.1.7",
"vite-tsconfig-paths": "^5.1.3" "sqlstring": "^2.3.3",
}, "vite-tsconfig-paths": "^5.1.3"
"devDependencies": { },
"@electron/rebuild": "^3.7.1", "devDependencies": {
"@eslint/js": "^9.13.0", "@electron/rebuild": "^3.7.1",
"@iconify-icon/react": "^2.1.0", "@eslint/js": "^9.13.0",
"@types/better-sqlite3": "^7.6.12", "@iconify-icon/react": "^2.1.0",
"@types/gulp": "^4.0.17", "@types/better-sqlite3": "^7.6.12",
"@types/memory-cache": "^0.2.6", "@types/gulp": "^4.0.17",
"@types/react": "^18.3.12", "@types/memory-cache": "^0.2.6",
"@types/react-dom": "^18.3.1", "@types/react": "^18.3.12",
"@types/screenshot-desktop": "^1.12.3", "@types/react-dom": "^18.3.1",
"@types/sqlstring": "^2.3.2", "@types/screenshot-desktop": "^1.12.3",
"@vitejs/plugin-react": "^4.3.3", "@types/sqlstring": "^2.3.2",
"autoprefixer": "^10.4.19", "@vitejs/plugin-react": "^4.3.3",
"concurrently": "^9.0.1", "autoprefixer": "^10.4.19",
"cross-env": "^7.0.3", "concurrently": "^9.0.1",
"del": "^8.0.0", "cross-env": "^7.0.3",
"electron": "^33.2.0", "del": "^8.0.0",
"electron-build": "^0.0.3", "electron": "^33.2.0",
"electron-builder": "^25.1.8", "electron-build": "^0.0.3",
"eslint": "^9.13.0", "electron-builder": "^25.1.8",
"eslint-plugin-react-hooks": "^5.0.0", "eslint": "^9.13.0",
"eslint-plugin-react-refresh": "^0.4.14", "eslint-plugin-react-hooks": "^5.0.0",
"globals": "^15.11.0", "eslint-plugin-react-refresh": "^0.4.14",
"gulp": "^5.0.0", "globals": "^15.11.0",
"gulp-clean": "^0.4.0", "gulp": "^5.0.0",
"gulp-typescript": "6.0.0-alpha.1", "gulp-clean": "^0.4.0",
"postcss": "^8.4.38", "gulp-typescript": "6.0.0-alpha.1",
"tailwindcss": "^3.4.15", "postcss": "^8.4.38",
"typescript": "~5.6.2", "tailwindcss": "^3.4.15",
"typescript-eslint": "^8.11.0", "typescript": "~5.6.2",
"vite": "^5.4.10", "typescript-eslint": "^8.11.0",
"vite-plugin-chunk-split": "^0.5.0" "vite": "^5.4.10",
} "vite-plugin-chunk-split": "^0.5.0"
}
} }

View File

@ -1,3 +1,3 @@
body { body {
margin: 0; margin: 0;
} }

View File

@ -1,12 +1,9 @@
import "./index.css"; import "./index.css";
export default function RewindPage() { export default function RewindPage() {
return ( return (
<> <>
<div className="w-screen h-screen relative dark:text-white"> <div className="w-screen h-screen relative dark:text-white"></div>
</div>
</> </>
); );
} }

View File

@ -1,19 +1,19 @@
#root { #root {
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
} }
#settings-note a { #settings-note a {
@apply text-blue-700 dark:text-[#66ccff] @apply text-blue-700 dark:text-[#66ccff];
} }
.text-weaken { .text-weaken {
@apply text-gray-700 dark:text-gray-300; @apply text-gray-700 dark:text-gray-300;
} }
#settings-scroll-container::-webkit-scrollbar { #settings-scroll-container::-webkit-scrollbar {
display: none; display: none;
} }
#settings-scroll-container { #settings-scroll-container {
scrollbar-width: none; scrollbar-width: none;
} }

View File

@ -36,7 +36,7 @@ function TitleBar() {
<div <div
className="z-50 absolute right-2.5 top-2.5 bg-red-500 hover:bg-rose-400 h-3 w-3 rounded-full" className="z-50 absolute right-2.5 top-2.5 bg-red-500 hover:bg-rose-400 h-3 w-3 rounded-full"
onClick={() => { onClick={() => {
console.log(window.settingsWindow) console.log(window.settingsWindow);
window.settingsWindow.close(); window.settingsWindow.close();
}} }}
> >

View File

@ -6,21 +6,23 @@ import { getDatabaseDir } from "../utils/backend.js";
import { migrate } from "./migrate/index.js"; import { migrate } from "./migrate/index.js";
function getLibSimpleExtensionPath() { function getLibSimpleExtensionPath() {
switch (process.platform) { switch (process.platform) {
case "win32": case "win32":
return path.join(__dirname, "bin", process.platform, "libsimple", "simple.dll"); return path.join(__dirname, "bin", process.platform, "libsimple", "simple.dll");
case "darwin": case "darwin":
return path.join(__dirname, "bin", process.platform, "libsimple", "libsimple.dylib"); return path.join(__dirname, "bin", process.platform, "libsimple", "libsimple.dylib");
case "linux": case "linux":
return path.join(__dirname, "bin", process.platform, "libsimple", "libsimple.so"); return path.join(__dirname, "bin", process.platform, "libsimple", "libsimple.so");
default: default:
throw new Error("Unsupported platform"); throw new Error("Unsupported platform");
} }
} }
function databaseInitialized(db: Database) { function databaseInitialized(db: Database) {
return db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='frame';`).get() return (
!== undefined; db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='frame';`).get() !==
undefined
);
} }
function init(db: Database) { function init(db: Database) {
@ -147,8 +149,7 @@ export async function initDatabase() {
if (!databaseInitialized(db)) { if (!databaseInitialized(db)) {
init(db); init(db);
} } else {
else {
migrate(db); migrate(db);
} }

View File

@ -55,9 +55,9 @@ function initSchemaInV2(db: Database) {
} }
/* /*
* This function assumes that the database does not contain the "config" table, * This function assumes that the database does not contain the "config" table,
* and thus needs to be migrated to Version 2. * and thus needs to be migrated to Version 2.
* */ * */
export function migrateToV2(db: Database) { export function migrateToV2(db: Database) {
initSchemaInV2(db); initSchemaInV2(db);
@ -65,12 +65,11 @@ export function migrateToV2(db: Database) {
// Before: /Users/username/Library/Application Support/OpenRewind/Record Data/temp/screenshots/1733568609960.jpg // Before: /Users/username/Library/Application Support/OpenRewind/Record Data/temp/screenshots/1733568609960.jpg
// After: 1733568609960.jpg // After: 1733568609960.jpg
const rows = db.prepare("SELECT id, imgFilename FROM frame").all() as OldFrame[]; const rows = db.prepare("SELECT id, imgFilename FROM frame").all() as OldFrame[];
rows.forEach(row => { rows.forEach((row) => {
const filename = row.imgFilename.match(/[^\\/]+$/)?.[0]; const filename = row.imgFilename.match(/[^\\/]+$/)?.[0];
if (filename) { if (filename) {
db.prepare("UPDATE frame SET imgFilename = ? WHERE id = ?") db.prepare("UPDATE frame SET imgFilename = ? WHERE id = ?").run(filename, row.id);
.run(filename, row.id);
} }
}); });
@ -78,4 +77,4 @@ export function migrateToV2(db: Database) {
ALTER TABLE frame ADD encodeStatus INT DEFAULT 0; ALTER TABLE frame ADD encodeStatus INT DEFAULT 0;
UPDATE frame SET encodeStatus = CASE WHEN encoded THEN 2 ELSE 0 END; UPDATE frame SET encodeStatus = CASE WHEN encoded THEN 2 ELSE 0 END;
`); `);
} }

View File

@ -24,7 +24,10 @@ function transformEncodingTask(db: Database) {
`; `;
db.exec(createTableSql); db.exec(createTableSql);
const rows = db.prepare(`SELECT id, createdAt FROM encoding_task`).all() as { [x: string]: unknown; id: unknown; }[]; const rows = db.prepare(`SELECT id, createdAt FROM encoding_task`).all() as {
[x: string]: unknown;
id: unknown;
}[];
const updateStmt = db.prepare(`UPDATE encoding_task SET createdAt_new = ? WHERE id = ?`); const updateStmt = db.prepare(`UPDATE encoding_task SET createdAt_new = ? WHERE id = ?`);
rows.forEach((row) => { rows.forEach((row) => {
const unixTimestamp = convertTimestampToUnix(row.createdAt as string); const unixTimestamp = convertTimestampToUnix(row.createdAt as string);
@ -55,10 +58,13 @@ function transformFrame(db: Database) {
DROP TABLE frame; DROP TABLE frame;
ALTER TABLE frame_new RENAME TO frame; ALTER TABLE frame_new RENAME TO frame;
ALTER TABLE frame ADD COLUMN createdAt_new REAL; ALTER TABLE frame ADD COLUMN createdAt_new REAL;
` `;
db.exec(createTableSql); db.exec(createTableSql);
const rows = db.prepare(`SELECT id, createdAt FROM frame`).all() as { [x: string]: unknown; id: unknown; }[]; const rows = db.prepare(`SELECT id, createdAt FROM frame`).all() as {
[x: string]: unknown;
id: unknown;
}[];
const updateStmt = db.prepare(`UPDATE frame SET createdAt_new = ? WHERE id = ?`); const updateStmt = db.prepare(`UPDATE frame SET createdAt_new = ? WHERE id = ?`);
rows.forEach((row) => { rows.forEach((row) => {
const unixTimestamp = convertTimestampToUnix(row.createdAt as string); const unixTimestamp = convertTimestampToUnix(row.createdAt as string);
@ -92,7 +98,10 @@ function transformSegments(db: Database) {
ALTER TABLE segments ADD COLUMN startedAt_new REAL; ALTER TABLE segments ADD COLUMN startedAt_new REAL;
ALTER TABLE segments ADD COLUMN endedAt_new REAL; ALTER TABLE segments ADD COLUMN endedAt_new REAL;
`); `);
const rows = db.prepare(`SELECT id, startedAt, endedAt FROM segments`).all() as { [x: string]: unknown; id: unknown; }[]; const rows = db.prepare(`SELECT id, startedAt, endedAt FROM segments`).all() as {
[x: string]: unknown;
id: unknown;
}[];
const updateStart = db.prepare(`UPDATE segments SET startedAt_new = ? WHERE id = ?`); const updateStart = db.prepare(`UPDATE segments SET startedAt_new = ? WHERE id = ?`);
const updateEnd = db.prepare(`UPDATE segments SET endedAt_new = ? WHERE id = ?`); const updateEnd = db.prepare(`UPDATE segments SET endedAt_new = ? WHERE id = ?`);
rows.forEach((row) => { rows.forEach((row) => {
@ -108,8 +117,17 @@ function transformSegments(db: Database) {
`); `);
} }
function renameColumn(tableName: string, oldColumnName: string, newColumnName: string, db: Database) { function renameColumn(
if (db.prepare(`SELECT 1 FROM pragma_table_info(?) WHERE name=?`).get([tableName, oldColumnName])) { tableName: string,
oldColumnName: string,
newColumnName: string,
db: Database
) {
if (
db
.prepare(`SELECT 1 FROM pragma_table_info(?) WHERE name=?`)
.get([tableName, oldColumnName])
) {
db.exec(`ALTER TABLE ${tableName} RENAME COLUMN ${oldColumnName} TO ${newColumnName};`); db.exec(`ALTER TABLE ${tableName} RENAME COLUMN ${oldColumnName} TO ${newColumnName};`);
} }
} }
@ -173,10 +191,10 @@ export function migrateToV3(db: Database) {
PRAGMA foreign_keys = ON; PRAGMA foreign_keys = ON;
`); `);
renameColumn('encoding_task', 'createAt', 'createdAt', db); renameColumn("encoding_task", "createAt", "createdAt", db);
renameColumn('frame', 'createAt', 'createdAt', db); renameColumn("frame", "createAt", "createdAt", db);
renameColumn('segments', 'startAt', 'startedAt', db); renameColumn("segments", "startAt", "startedAt", db);
renameColumn('segments', 'endAt', 'endedAt', db); renameColumn("segments", "endAt", "endedAt", db);
if (db.prepare(`SELECT 1 FROM pragma_table_info('frame') WHERE name='encoded'`).get()) { if (db.prepare(`SELECT 1 FROM pragma_table_info('frame') WHERE name='encoded'`).get()) {
db.prepare(`ALTER TABLE frame DROP COLUMN encoded`).run(); db.prepare(`ALTER TABLE frame DROP COLUMN encoded`).run();
} }
@ -188,4 +206,4 @@ export function migrateToV3(db: Database) {
db.exec(` db.exec(`
UPDATE config SET value = '3' WHERE key = 'version'; UPDATE config SET value = '3' WHERE key = 'version';
`); `);
} }

View File

@ -9,7 +9,6 @@ export interface Frame {
encodeStatus: number; encodeStatus: number;
} }
export interface EncodingTask { export interface EncodingTask {
id: number; id: number;
createdAt: number; createdAt: number;
@ -19,4 +18,4 @@ export interface EncodingTask {
export interface EncodingTaskData { export interface EncodingTaskData {
encodingTaskID: number; encodingTaskID: number;
frame: number; frame: number;
} }

View File

@ -1,7 +1,7 @@
import screenshot from "screenshot-desktop"; import screenshot from "screenshot-desktop";
import { getScreenshotsDir } from "../utils/backend.js"; import { getScreenshotsDir } from "../utils/backend.js";
import { join } from "path"; import { join } from "path";
import { Database }from "better-sqlite3"; import { Database } from "better-sqlite3";
import SqlString from "sqlstring"; import SqlString from "sqlstring";
export function startScreenshotLoop(db: Database) { export function startScreenshotLoop(db: Database) {
@ -10,15 +10,16 @@ export function startScreenshotLoop(db: Database) {
const screenshotDir = getScreenshotsDir(); const screenshotDir = getScreenshotsDir();
const filename = `${timestamp}.png`; const filename = `${timestamp}.png`;
const screenshotPath = join(screenshotDir, filename); const screenshotPath = join(screenshotDir, filename);
screenshot({filename: screenshotPath, format: "png"}).then((absolutePath) => { screenshot({ filename: screenshotPath, format: "png" })
const SQL = SqlString.format( .then((absolutePath) => {
"INSERT INTO frame (imgFilename, createdAt) VALUES (?, ?)", const SQL = SqlString.format(
[filename, new Date().getTime() / 1000] "INSERT INTO frame (imgFilename, createdAt) VALUES (?, ?)",
); [filename, new Date().getTime() / 1000]
db.exec(SQL); );
}).catch((err) => { db.exec(SQL);
console.error(err); })
}); .catch((err) => {
console.error(err);
});
}, 2000); }, 2000);
} }

View File

@ -10,13 +10,14 @@ function loadURL(window: BrowserWindow, path = "", vitePort: string) {
window.loadURL(`http://localhost:${vitePort}/#${path}`).catch((e) => { window.loadURL(`http://localhost:${vitePort}/#${path}`).catch((e) => {
console.log("Error loading URL:", e); console.log("Error loading URL:", e);
}); });
} } else {
else { window
window.loadFile(join(__dirname, "../renderer/index.html"), { .loadFile(join(__dirname, "../renderer/index.html"), {
hash: path, hash: path
}).catch((e) => { })
console.log("Error loading URL:", e); .catch((e) => {
}); console.log("Error loading URL:", e);
});
} }
} }
@ -26,9 +27,9 @@ export function createSettingsWindow(vitePort: string, closeCallBack: Function)
defaultHeight: 550 defaultHeight: 550
}); });
const enableFrame = process.platform === "darwin"; const enableFrame = process.platform === "darwin";
let icon let icon;
switch (process.platform) { switch (process.platform) {
case "darwin": case "darwin":
icon = undefined; icon = undefined;
break; break;
case "win32": case "win32":
@ -46,13 +47,13 @@ export function createSettingsWindow(vitePort: string, closeCallBack: Function)
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: true, contextIsolation: true,
preload: join(__dirname, 'preload/settings.cjs') preload: join(__dirname, "preload/settings.cjs")
}, },
titleBarStyle: "hiddenInset", titleBarStyle: "hiddenInset",
resizable: false, resizable: false,
show: false, show: false,
frame: enableFrame, frame: enableFrame,
icon: icon, icon: icon
}); });
windowState.manage(window); windowState.manage(window);
window.on("show", () => { window.on("show", () => {
@ -92,7 +93,7 @@ export function createMainWindow(vitePort: string, closeCallBack: Function) {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: true, contextIsolation: true,
preload: join(__dirname, 'preload/rewind.cjs') preload: join(__dirname, "preload/rewind.cjs")
}, },
roundedCorners: false, roundedCorners: false,
transparent: true, transparent: true,

View File

@ -4,7 +4,6 @@ import fs from "fs";
import { app } from "electron"; import { app } from "electron";
import { __dirname } from "./dirname.js"; import { __dirname } from "./dirname.js";
/** /**
* Selects the appropriate language based on system preferences and available languages * Selects the appropriate language based on system preferences and available languages
* *
@ -21,7 +20,7 @@ export function detectLanguage(langs: string[], fallback: string): string {
const normalizedLang = systemLanguage.toLowerCase().split("-")[0]; const normalizedLang = systemLanguage.toLowerCase().split("-")[0];
// Find a matching language // Find a matching language
const matchedLanguage = langs.find(lang => { const matchedLanguage = langs.find((lang) => {
if (lang.indexOf(normalizedLang) !== -1) { if (lang.indexOf(normalizedLang) !== -1) {
return lang; return lang;
} }
@ -53,4 +52,3 @@ export default function initI18n() {
}); });
return i18n; return i18n;
} }

View File

@ -1,4 +1,13 @@
import { app, BrowserWindow, globalShortcut, ipcMain, Menu, nativeImage, screen, Tray } from "electron"; import {
app,
BrowserWindow,
globalShortcut,
ipcMain,
Menu,
nativeImage,
screen,
Tray
} from "electron";
import contextMenu from "electron-context-menu"; import contextMenu from "electron-context-menu";
import { join } from "path"; import { join } from "path";
import initI18n from "./i18n.js"; import initI18n from "./i18n.js";
@ -8,12 +17,16 @@ import { Database } from "better-sqlite3";
import { startScreenshotLoop } from "./backend/screenshot.js"; import { startScreenshotLoop } from "./backend/screenshot.js";
import { __dirname } from "./dirname.js"; import { __dirname } from "./dirname.js";
import { hideDock } from "./utils/electron.js"; import { hideDock } from "./utils/electron.js";
import { checkFramesForEncoding, deleteUnnecessaryScreenshots, processEncodingTasks } from "./backend/encoding.js"; import {
checkFramesForEncoding,
deleteUnnecessaryScreenshots,
processEncodingTasks
} from "./backend/encoding.js";
import honoApp from "./server/index.js"; import honoApp from "./server/index.js";
import { serve } from "@hono/node-server"; import { serve } from "@hono/node-server";
import { findAvailablePort } from "./utils/server.js"; import { findAvailablePort } from "./utils/server.js";
import cache from "memory-cache"; import cache from "memory-cache";
import { generate } from '@alikia/random-key'; import { generate } from "@alikia/random-key";
const i18n = initI18n(); const i18n = initI18n();
@ -75,21 +88,19 @@ contextMenu({
app.once("ready", () => { app.once("ready", () => {
hideDock(); hideDock();
}); });
app.on("activate", () => { app.on("activate", () => {});
});
app.on("ready", () => { app.on("ready", () => {
createTray(); createTray();
findAvailablePort(12412).then((port) => { findAvailablePort(12412).then((port) => {
generate().then((key) => { generate().then((key) => {
cache.put("server:APIKey",key); cache.put("server:APIKey", key);
cache.put("server:port", port); cache.put("server:port", port);
if (dev) if (dev) console.log(`API Key: ${key}`);
console.log(`API Key: ${key}`);
serve({ fetch: honoApp.fetch, port: port }); serve({ fetch: honoApp.fetch, port: port });
console.log(`App server running on port ${port}`); console.log(`App server running on port ${port}`);
}); });
}) });
initDatabase().then((db) => { initDatabase().then((db) => {
screenshotInterval = startScreenshotLoop(db); screenshotInterval = startScreenshotLoop(db);
setInterval(checkFramesForEncoding, 5000, db); setInterval(checkFramesForEncoding, 5000, db);
@ -108,8 +119,7 @@ app.on("ready", () => {
app.on("will-quit", () => { app.on("will-quit", () => {
dbConnection?.close(); dbConnection?.close();
if (screenshotInterval) if (screenshotInterval) clearInterval(screenshotInterval);
clearInterval(screenshotInterval);
}); });
// app.on("window-all-closed", () => { // app.on("window-all-closed", () => {
@ -118,4 +128,4 @@ app.on("will-quit", () => {
ipcMain.on("close-settings", () => { ipcMain.on("close-settings", () => {
settingsWindow?.hide(); settingsWindow?.hide();
}); });

View File

@ -1,52 +1,52 @@
const os = require('node:os'); const os = require("node:os");
const nameMap = new Map([ const nameMap = new Map([
[24, ['Sequoia', '15']], [24, ["Sequoia", "15"]],
[23, ['Sonoma', '14']], [23, ["Sonoma", "14"]],
[22, ['Ventura', '13']], [22, ["Ventura", "13"]],
[21, ['Monterey', '12']], [21, ["Monterey", "12"]],
[20, ['Big Sur', '11']], [20, ["Big Sur", "11"]],
[19, ['Catalina', '10.15']], [19, ["Catalina", "10.15"]],
[18, ['Mojave', '10.14']], [18, ["Mojave", "10.14"]],
[17, ['High Sierra', '10.13']], [17, ["High Sierra", "10.13"]],
[16, ['Sierra', '10.12']], [16, ["Sierra", "10.12"]],
[15, ['El Capitan', '10.11']], [15, ["El Capitan", "10.11"]],
[14, ['Yosemite', '10.10']], [14, ["Yosemite", "10.10"]],
[13, ['Mavericks', '10.9']], [13, ["Mavericks", "10.9"]],
[12, ['Mountain Lion', '10.8']], [12, ["Mountain Lion", "10.8"]],
[11, ['Lion', '10.7']], [11, ["Lion", "10.7"]],
[10, ['Snow Leopard', '10.6']], [10, ["Snow Leopard", "10.6"]],
[9, ['Leopard', '10.5']], [9, ["Leopard", "10.5"]],
[8, ['Tiger', '10.4']], [8, ["Tiger", "10.4"]],
[7, ['Panther', '10.3']], [7, ["Panther", "10.3"]],
[6, ['Jaguar', '10.2']], [6, ["Jaguar", "10.2"]],
[5, ['Puma', '10.1']], [5, ["Puma", "10.1"]]
]); ]);
const names = new Map([ const names = new Map([
['10.0.2', '11'], // It's unclear whether future Windows 11 versions will use this version scheme: https://github.com/sindresorhus/windows-release/pull/26/files#r744945281 ["10.0.2", "11"], // It's unclear whether future Windows 11 versions will use this version scheme: https://github.com/sindresorhus/windows-release/pull/26/files#r744945281
['10.0', '10'], ["10.0", "10"],
['6.3', '8.1'], ["6.3", "8.1"],
['6.2', '8'], ["6.2", "8"],
['6.1', '7'], ["6.1", "7"],
['6.0', 'Vista'], ["6.0", "Vista"],
['5.2', 'Server 2003'], ["5.2", "Server 2003"],
['5.1', 'XP'], ["5.1", "XP"],
['5.0', '2000'], ["5.0", "2000"],
['4.90', 'ME'], ["4.90", "ME"],
['4.10', '98'], ["4.10", "98"],
['4.03', '95'], ["4.03", "95"],
['4.00', '95'], ["4.00", "95"]
]); ]);
function macosRelease(release) { function macosRelease(release) {
release = Number((release || os.release()).split('.')[0]); release = Number((release || os.release()).split(".")[0]);
const [name, version] = nameMap.get(release) || ['Unknown', '']; const [name, version] = nameMap.get(release) || ["Unknown", ""];
return { return {
name, name,
version, version
}; };
} }
@ -54,14 +54,14 @@ function windowsRelease(release) {
const version = /(\d+\.\d+)(?:\.(\d+))?/.exec(release || os.release()); const version = /(\d+\.\d+)(?:\.(\d+))?/.exec(release || os.release());
if (release && !version) { if (release && !version) {
throw new Error('`release` argument doesn\'t match `n.n`'); throw new Error("`release` argument doesn't match `n.n`");
} }
let ver = version[1] || ''; let ver = version[1] || "";
const build = version[2] || ''; const build = version[2] || "";
if (ver === '10.0' && build.startsWith('2')) { if (ver === "10.0" && build.startsWith("2")) {
ver = '10.0.2'; ver = "10.0.2";
} }
return names.get(ver); return names.get(ver);
@ -69,47 +69,47 @@ function windowsRelease(release) {
function osName(platform, release) { function osName(platform, release) {
if (!platform && release) { if (!platform && release) {
throw new Error('You can\'t specify a `release` without specifying `platform`'); throw new Error("You can't specify a `release` without specifying `platform`");
} }
platform = platform ?? os.platform(); platform = platform ?? os.platform();
let id; let id;
if (platform === 'darwin') { if (platform === "darwin") {
if (!release && os.platform() === 'darwin') { if (!release && os.platform() === "darwin") {
release = os.release(); release = os.release();
} }
const prefix = release ? (Number(release.split('.')[0]) > 15 ? 'macOS' : 'OS X') : 'macOS'; const prefix = release ? (Number(release.split(".")[0]) > 15 ? "macOS" : "OS X") : "macOS";
try { try {
id = release ? macosRelease(release).name : ''; id = release ? macosRelease(release).name : "";
if (id === 'Unknown') { if (id === "Unknown") {
return prefix; return prefix;
} }
} catch {} } catch {}
return prefix + (id ? ' ' + id : ''); return prefix + (id ? " " + id : "");
} }
if (platform === 'linux') { if (platform === "linux") {
if (!release && os.platform() === 'linux') { if (!release && os.platform() === "linux") {
release = os.release(); release = os.release();
} }
id = release ? release.replace(/^(\d+\.\d+).*/, '$1') : ''; id = release ? release.replace(/^(\d+\.\d+).*/, "$1") : "";
return 'Linux' + (id ? ' ' + id : ''); return "Linux" + (id ? " " + id : "");
} }
if (platform === 'win32') { if (platform === "win32") {
if (!release && os.platform() === 'win32') { if (!release && os.platform() === "win32") {
release = os.release(); release = os.release();
} }
id = release ? windowsRelease(release) : ''; id = release ? windowsRelease(release) : "";
return 'Windows' + (id ? ' ' + id : ''); return "Windows" + (id ? " " + id : "");
} }
return platform; return platform;

View File

@ -1,15 +1,15 @@
import { app } from "electron"; import { app } from "electron";
export function hideDock(){ export function hideDock() {
if (process.platform === 'darwin') { if (process.platform === "darwin") {
// Hide the dock icon on macOS // Hide the dock icon on macOS
app.dock.hide(); app.dock.hide();
} }
} }
export function showDock(){ export function showDock() {
if (process.platform === 'darwin') { if (process.platform === "darwin") {
// Show the dock icon on macOS // Show the dock icon on macOS
app.dock.show(); app.dock.show();
} }
} }

View File

@ -1,4 +1,4 @@
import { detect } from 'detect-port'; import { detect } from "detect-port";
/** /**
* Finds an available port starting from a given port. * Finds an available port starting from a given port.
@ -7,11 +7,11 @@ import { detect } from 'detect-port';
*/ */
export async function findAvailablePort(startingFrom: number): Promise<number> { export async function findAvailablePort(startingFrom: number): Promise<number> {
return detect(startingFrom) return detect(startingFrom)
.then(realPort => { .then((realPort) => {
return realPort; // Return the available port return realPort; // Return the available port
}) })
.catch(err => { .catch((err) => {
console.error(`Error detecting port: ${err.message}`); console.error(`Error detecting port: ${err.message}`);
throw err; // Rethrow the error for further handling if needed throw err; // Rethrow the error for further handling if needed
}); });
} }

16
src/global.d.ts vendored
View File

@ -7,13 +7,13 @@ interface Window {
osDisplay: () => string; osDisplay: () => string;
}; };
electron: { electron: {
getScreenshot: () => Promise<string>; getScreenshot: () => Promise<string>;
} };
api: { api: {
send: (channel: any, data: any) => void, send: (channel: any, data: any) => void;
receive: (channel: any, func: any) => void receive: (channel: any, func: any) => void;
}, };
settingsWindow: { settingsWindow: {
close: () => void, close: () => void;
} };
} }

View File

@ -1,3 +1,3 @@
#title-bar { #title-bar {
-webkit-app-region: drag; -webkit-app-region: drag;
} }

View File

@ -2,7 +2,7 @@ import { HashRouter, Routes, Route } from "react-router-dom";
import SettingsPage from "pages/settings"; import SettingsPage from "pages/settings";
import "./i18n.ts"; import "./i18n.ts";
import RewindPage from "pages/rewind"; import RewindPage from "pages/rewind";
import './app.css'; import "./app.css";
export function App() { export function App() {
return ( return (

View File

@ -38,7 +38,7 @@ i18n.use(initReactI18next) // passes i18n down to react-i18next
}, },
ko: { ko: {
translation: ko translation: ko
}, }
}, },
fallbackLng: "en", fallbackLng: "en",

View File

@ -3,5 +3,5 @@
@tailwind utilities; @tailwind utilities;
body { body {
@apply bg-transparent; @apply bg-transparent;
} }

View File

@ -1,11 +1,11 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="./main.tsx"></script> <script type="module" src="./main.tsx"></script>
</body> </body>
</html> </html>

View File

@ -6,7 +6,7 @@ import "./index.css";
const app = createRoot(document.getElementById("root")!); const app = createRoot(document.getElementById("root")!);
app.render( app.render(
<StrictMode> <StrictMode>
<App /> <App />
</StrictMode> </StrictMode>
); );

View File

@ -4,7 +4,7 @@ const config: Config = {
content: [ content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}", "./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/**/*.{js,ts,jsx,tsx,mdx}", "./src/**/*.{js,ts,jsx,tsx,mdx}"
], ],
theme: { theme: {
extend: { extend: {

View File

@ -1,35 +1,35 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"composite": true, "composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020", "target": "ES2020",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"], "lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"moduleDetection": "force", "moduleDetection": "force",
"jsx": "react-jsx", "jsx": "react-jsx",
/* Linting */ /* Linting */
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },
"include": [ "include": [
"src/renderer", "src/renderer",
"src/renderer/**/*.ts", "src/renderer/**/*.ts",
"src/renderer/**/*.tsx", "src/renderer/**/*.tsx",
"src/global.d.ts", "src/global.d.ts",
"pages/**/*.tsx", "pages/**/*.tsx",
"components/**/*.tsx" "components/**/*.tsx"
], ]
} }

View File

@ -1,18 +1,15 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"outDir": "./dist/electron", "outDir": "./dist/electron",
"rootDir": "./src/electron", "rootDir": "./src/electron",
"strict": true, "strict": true,
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, "skipLibCheck": true,
"forceConsistentCasingInFileNames": true "forceConsistentCasingInFileNames": true
}, },
"include": ["src/electron"], "include": ["src/electron"],
"references": [ "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
{ "path": "./tsconfig.app.json"},
{ "path": "./tsconfig.node.json"}
]
} }

View File

@ -1,25 +1,23 @@
{ {
"compilerOptions": { "compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022", "target": "ES2022",
"lib": ["ES2023"], "lib": ["ES2023"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
"composite": true, "composite": true,
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"isolatedModules": true, "isolatedModules": true,
"moduleDetection": "force", "moduleDetection": "force",
/* Linting */ /* Linting */
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },
"include": [ "include": ["vite.config.ts"]
"vite.config.ts"
]
} }

View File

@ -19,10 +19,10 @@ export default defineConfig({
customChunk: (args) => { customChunk: (args) => {
// files into pages directory is export in single files // files into pages directory is export in single files
const { id } = args; const { id } = args;
if (id.includes('node_modules')) { if (id.includes("node_modules")) {
return 'vendor'; return "vendor";
} else { } else {
return 'main'; return "main";
} }
} }
}) })