mirror of
https://github.com/koodo-reader/koodo-reader.git
synced 2025-12-23 23:17:55 -05:00
feat: update font files and add new import folder functionality with translations
This commit is contained in:
2
src/assets/lib/kookit-extra-browser.min.js
vendored
2
src/assets/lib/kookit-extra-browser.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -647,6 +647,11 @@
|
||||
"S3 Compatible": "S3 兼容",
|
||||
"OneDrive (Experimental)": "OneDrive (实验性)",
|
||||
"Google Drive (Experimental)": "Google Drive (实验性)",
|
||||
"Import folder": "导入文件夹",
|
||||
"Error scanning folder": "扫描文件夹出错",
|
||||
"No files found in this folder": "在此文件夹中未找到文件",
|
||||
"Scanning folder": "正在扫描文件夹",
|
||||
"Successfully scanned folder": "文件夹扫描成功",
|
||||
"In order to let you directly manage your data in Google Drive, we have deprecated the old Google Drive token. Please reauthorize Google Drive in the settings. Your new data will be stored in the root directory of your Google Drive, and you can manage it directly in the Google Drive web interface.": "为了让您直接管理 Google Drive 中的数据,我们已经废弃了旧的 Google Drive 令牌。请在设置中重新授权 Google Drive。您的新数据将存储在 Google Drive 的根目录中,您可以直接在 Google Drive 网页界面中管理它。",
|
||||
"This feature is only available in the developer version": "本功能仅支持开发版",
|
||||
"Paste the code of the plugin here, check out document to learn how to get more plugins": "将插件的代码粘贴在这里,查看帮助文档了解如何获取更多插件",
|
||||
|
||||
Binary file not shown.
@@ -100,4 +100,5 @@
|
||||
<glyph unicode="" glyph-name="phone" horiz-adv-x="751" d="M547.718 899c39.814 0 72.090-44.012 72.090-98.304v-704.515c0-54.286-32.276-98.304-72.090-98.304h-344.429c-39.814 0-72.089 44.018-72.089 98.304v704.515c0 54.292 32.275 98.304 72.089 98.304h344.429zM375.503 281.87c-24.331 0-44.055-26.894-44.055-60.079 0-33.178 19.724-60.075 44.055-60.075s44.055 26.897 44.055 60.075c0 33.184-19.724 60.079-44.055 60.079z" />
|
||||
<glyph unicode="" glyph-name="send" d="M1015.485 483.536c-7.599 15.198-20.198 27.797-35.396 35.396l-865.299 432.649c-18.998 9.499-40.495 10.999-60.593 4.299s-36.296-20.898-45.795-39.795c-9.899-19.898-11.099-43.095-3.1-63.893l155.382-404.253-155.382-404.153c-7.599-19.798-6.999-41.395 1.6-60.693 8.599-19.398 24.297-34.196 43.995-41.795 9.199-3.5 18.798-5.299 28.497-5.299 12.199 0 24.397 2.8 35.496 8.299l865.199 432.749c39.095 19.598 54.994 67.392 35.396 106.488zM79.095 15.491l151.182 392.954h310.364c21.797 0 39.495 17.698 39.495 39.495s-17.698 39.495-39.495 39.495h-310.364l-150.782 393.354 865.199-432.649-865.599-432.649z" />
|
||||
<glyph unicode="" glyph-name="loading" d="M512 864c-229.717 0-416-186.283-416-416s186.283-416 416-416v104.021c-172.283 0.024-311.936 139.693-311.936 311.979 0 172.301 139.678 311.979 311.979 311.979s311.979-139.678 311.979-311.979v0h103.979c0 229.717-186.283 416-416 416z" />
|
||||
<glyph unicode="" glyph-name="import" d="M512 576c23.564 0 42.667-19.103 42.667-42.667v-341.333h130.987c5.493-0.014 10.495-2.101 14.269-5.52l-0.018 0.016 2.688-2.901c2.721-3.544 4.36-8.042 4.36-12.923 0-6.884-3.26-13.006-8.321-16.906l-0.050-0.037-165.717-126.464c-5.324-4.101-12.087-6.573-19.428-6.573-7.365 0-14.15 2.488-19.558 6.67l0.074-0.055-164.48 126.464c-5.075 3.939-8.309 10.040-8.309 16.896 0 11.763 9.521 21.303 21.277 21.333h126.894v341.333c0 23.564 19.103 42.667 42.667 42.667v0zM741.675 277.632c-21.296 2.606-37.627 20.58-37.627 42.368 0 23.548 19.076 42.64 42.617 42.667h106.669v426.667h-682.667v-426.667h106.667c23.564 0 42.667-19.103 42.667-42.667s-19.103-42.667-42.667-42.667v0h-106.667c-47.128 0-85.333 38.205-85.333 85.333v426.667c0 47.128 38.205 85.333 85.333 85.333h682.667c47.128 0 85.333-38.205 85.333-85.333v0-426.667c0-47.128-38.205-85.333-85.333-85.333h-106.667z" />
|
||||
</font></defs></svg>
|
||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 94 KiB |
Binary file not shown.
Binary file not shown.
@@ -1,10 +1,10 @@
|
||||
@font-face {
|
||||
font-family: 'icomoon';
|
||||
src: url('fonts/icomoon.eot?rfb0hg');
|
||||
src: url('fonts/icomoon.eot?rfb0hg#iefix') format('embedded-opentype'),
|
||||
url('fonts/icomoon.ttf?rfb0hg') format('truetype'),
|
||||
url('fonts/icomoon.woff?rfb0hg') format('woff'),
|
||||
url('fonts/icomoon.svg?rfb0hg#icomoon') format('svg');
|
||||
src: url('fonts/icomoon.eot?7m940n');
|
||||
src: url('fonts/icomoon.eot?7m940n#iefix') format('embedded-opentype'),
|
||||
url('fonts/icomoon.ttf?7m940n') format('truetype'),
|
||||
url('fonts/icomoon.woff?7m940n') format('woff'),
|
||||
url('fonts/icomoon.svg?7m940n#icomoon') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
@@ -25,6 +25,9 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-import:before {
|
||||
content: "\e95d";
|
||||
}
|
||||
.icon-loading:before {
|
||||
content: "\e95c";
|
||||
}
|
||||
|
||||
@@ -139,6 +139,85 @@ class ImportDialog extends React.Component<
|
||||
toast.dismiss("importing");
|
||||
this.props.importBookFunc(file);
|
||||
};
|
||||
listAllFilesRecursively = async (folderName: string) => {
|
||||
toast.loading(this.props.t("Scanning folder"), {
|
||||
id: "scanning",
|
||||
});
|
||||
|
||||
try {
|
||||
const allFiles = await this.getAllFilesInFolder(
|
||||
this.state.currentPath + "/" + folderName
|
||||
);
|
||||
|
||||
// Filter only files (not folders) and get full paths
|
||||
const fileList = allFiles.filter((file) => file.indexOf(".") !== -1);
|
||||
|
||||
if (fileList.length === 0) {
|
||||
toast.dismiss("scanning");
|
||||
toast(this.props.t("No files found in this folder"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Add all files to selected list
|
||||
this.setState({
|
||||
selectedFileList: [
|
||||
...this.state.selectedFileList,
|
||||
...fileList.filter(
|
||||
(file) => !this.state.selectedFileList.includes(file)
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
toast.dismiss("scanning");
|
||||
toast.success(this.props.t("Successfully scanned folder"));
|
||||
} catch (error) {
|
||||
toast.dismiss("scanning");
|
||||
toast.error(this.props.t("Error scanning folder"));
|
||||
console.error("Error scanning folder:", error);
|
||||
}
|
||||
};
|
||||
|
||||
getAllFilesInFolder = async (folderPath: string): Promise<string[]> => {
|
||||
let allFiles: string[] = [];
|
||||
|
||||
try {
|
||||
let fileList: string[] = [];
|
||||
|
||||
if (isElectron) {
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
let tokenConfig = await getCloudConfig(this.state.currentDrive);
|
||||
fileList = await ipcRenderer.invoke("picker-list", {
|
||||
...tokenConfig,
|
||||
baseFolder: "",
|
||||
service: this.state.currentDrive,
|
||||
currentPath: folderPath,
|
||||
storagePath: getStorageLocation(),
|
||||
});
|
||||
} else {
|
||||
let pickerUtil = await SyncService.getPickerUtil(
|
||||
this.state.currentDrive
|
||||
);
|
||||
fileList = await pickerUtil.listFiles(folderPath);
|
||||
}
|
||||
|
||||
for (const item of fileList) {
|
||||
const fullPath = folderPath + "/" + item;
|
||||
|
||||
if (item.indexOf(".") === -1) {
|
||||
// It's a folder, recursively get files from it
|
||||
const subFiles = await this.getAllFilesInFolder(fullPath);
|
||||
allFiles = allFiles.concat(subFiles);
|
||||
} else {
|
||||
// It's a file, add to list
|
||||
allFiles.push(fullPath);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error listing files in folder:", error);
|
||||
}
|
||||
|
||||
return allFiles;
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
@@ -201,6 +280,22 @@ class ImportDialog extends React.Component<
|
||||
>
|
||||
{item}
|
||||
</span>
|
||||
{item.indexOf(".") === -1 && (
|
||||
<span
|
||||
className="import-dialog-folder-button"
|
||||
onClick={() => {
|
||||
//list all files in the folder and its subfolder
|
||||
this.listAllFilesRecursively(item);
|
||||
}}
|
||||
>
|
||||
<span
|
||||
data-tooltip-id="my-tooltip"
|
||||
data-tooltip-content={this.props.t("Import folder")}
|
||||
>
|
||||
<span className="icon-import import-dialog-folder-icon"></span>
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
{item.indexOf(".") === -1 ? (
|
||||
<span
|
||||
className="icon-dropdown import-dialog-more-file"
|
||||
|
||||
@@ -5,7 +5,18 @@
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.import-dialog-folder-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.import-dialog-folder-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.cloud-drive-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -168,7 +168,7 @@ class ActionDialog extends React.Component<MoreActionProps, MoreActionState> {
|
||||
);
|
||||
let cache = await rendition.preCache(result);
|
||||
if (cache !== "err" || cache) {
|
||||
BookUtil.addBook(
|
||||
await BookUtil.addBook(
|
||||
"cache-" + this.props.currentBook.key,
|
||||
"zip",
|
||||
cache
|
||||
|
||||
@@ -15,7 +15,11 @@ import {
|
||||
ConfigService,
|
||||
} from "../../assets/lib/kookit-extra-browser.min";
|
||||
import CoverUtil from "../../utils/file/coverUtil";
|
||||
import { calculateFileMD5, fetchFileFromPath } from "../../utils/common";
|
||||
import {
|
||||
calculateFileMD5,
|
||||
fetchFileFromPath,
|
||||
supportedFormats,
|
||||
} from "../../utils/common";
|
||||
import DatabaseService from "../../utils/storage/databaseService";
|
||||
import { BookHelper } from "../../assets/lib/kookit-extra-browser.min";
|
||||
import SyncService from "../../utils/storage/syncService";
|
||||
@@ -98,8 +102,8 @@ class ImportLocal extends React.Component<ImportLocalProps, ImportLocalState> {
|
||||
ConfigService.getReaderConfig("isImportPath") !== "yes" &&
|
||||
ConfigService.getReaderConfig("isPreventAdd") !== "yes"
|
||||
) {
|
||||
BookUtil.addBook(book.key, book.format.toLowerCase(), buffer);
|
||||
CoverUtil.addCover(book);
|
||||
await BookUtil.addBook(book.key, book.format.toLowerCase(), buffer);
|
||||
await CoverUtil.addCover(book);
|
||||
}
|
||||
if (ConfigService.getReaderConfig("isPreventAdd") === "yes") {
|
||||
this.handleJump(book);
|
||||
@@ -107,10 +111,11 @@ class ImportLocal extends React.Component<ImportLocalProps, ImportLocalState> {
|
||||
return resolve();
|
||||
}
|
||||
} else {
|
||||
ConfigService.getReaderConfig("isImportPath") !== "yes" &&
|
||||
BookUtil.addBook(book.key, book.format.toLowerCase(), buffer);
|
||||
if (ConfigService.getReaderConfig("isImportPath") !== "yes") {
|
||||
await BookUtil.addBook(book.key, book.format.toLowerCase(), buffer);
|
||||
}
|
||||
|
||||
CoverUtil.addCover(book);
|
||||
await CoverUtil.addCover(book);
|
||||
}
|
||||
if (
|
||||
this.props.isAuthed &&
|
||||
@@ -164,6 +169,12 @@ class ImportLocal extends React.Component<ImportLocalProps, ImportLocalState> {
|
||||
let bookBlob = new Blob([bookBuffer], {
|
||||
type: CommonTool.getMimeType(book.format.toLowerCase()),
|
||||
});
|
||||
console.log(
|
||||
bookBuffer,
|
||||
bookBlob,
|
||||
book.key + "." + book.format.toLowerCase(),
|
||||
"dfgdfgdf"
|
||||
);
|
||||
await syncUtil.uploadFile(
|
||||
book.key + "." + book.format.toLowerCase(),
|
||||
"book",
|
||||
@@ -271,7 +282,7 @@ class ImportLocal extends React.Component<ImportLocalProps, ImportLocalState> {
|
||||
) {
|
||||
let cache = await rendition.preCache(file_content);
|
||||
if (cache !== "err" || cache) {
|
||||
BookUtil.addBook("cache-" + result.key, "zip", cache);
|
||||
await BookUtil.addBook("cache-" + result.key, "zip", cache);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -299,8 +310,7 @@ class ImportLocal extends React.Component<ImportLocalProps, ImportLocalState> {
|
||||
}
|
||||
});
|
||||
};
|
||||
toggleMoreOptions = (e: React.MouseEvent) => {
|
||||
e.stopPropagation(); // Prevent triggering the Dropzone
|
||||
toggleMoreOptions = () => {
|
||||
this.setState((prevState) => ({
|
||||
isMoreOptionsVisible: !prevState.isMoreOptionsVisible,
|
||||
}));
|
||||
@@ -328,26 +338,7 @@ class ImportLocal extends React.Component<ImportLocalProps, ImportLocalState> {
|
||||
await this.getMd5WithBrowser(item);
|
||||
}
|
||||
}}
|
||||
accept={[
|
||||
".epub",
|
||||
".pdf",
|
||||
".txt",
|
||||
".mobi",
|
||||
".azw3",
|
||||
".azw",
|
||||
".htm",
|
||||
".html",
|
||||
".xml",
|
||||
".xhtml",
|
||||
".mhtml",
|
||||
".docx",
|
||||
".md",
|
||||
".fb2",
|
||||
".cbz",
|
||||
".cbt",
|
||||
".cbr",
|
||||
".cb7",
|
||||
]}
|
||||
accept={supportedFormats}
|
||||
multiple={true}
|
||||
>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
@@ -362,11 +353,135 @@ class ImportLocal extends React.Component<ImportLocalProps, ImportLocalState> {
|
||||
>
|
||||
<div
|
||||
className="more-import-option"
|
||||
onClick={this.toggleMoreOptions}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // Prevent triggering the Dropzone
|
||||
this.toggleMoreOptions();
|
||||
}}
|
||||
>
|
||||
<span className="dropdown-triangle"></span>
|
||||
{this.state.isMoreOptionsVisible && (
|
||||
<div className="more-options-dropdown">
|
||||
<div
|
||||
className="more-option-item"
|
||||
onClick={async (event) => {
|
||||
event.stopPropagation(); // Prevent triggering the Dropzone
|
||||
//select folder from local
|
||||
if (isElectron) {
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
const newPath = await ipcRenderer.invoke("select-path");
|
||||
if (!newPath) {
|
||||
return;
|
||||
}
|
||||
console.log("Selected folder path:", newPath);
|
||||
//get all files in the folder
|
||||
const fs = window.require("fs");
|
||||
const path = window.require("path");
|
||||
const getAllFiles = (dirPath: string): string[] => {
|
||||
let files: string[] = [];
|
||||
|
||||
try {
|
||||
const items = fs.readdirSync(dirPath);
|
||||
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dirPath, item);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// Recursively get files from subdirectories
|
||||
files = files.concat(getAllFiles(fullPath));
|
||||
} else if (stat.isFile()) {
|
||||
// Check if file has supported format
|
||||
const ext = path
|
||||
.extname(item)
|
||||
.toLowerCase()
|
||||
.substring(1);
|
||||
if (supportedFormats.includes(`.${ext}`)) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error reading directory ${dirPath}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
// Get all supported book files
|
||||
const allFiles = getAllFiles(newPath);
|
||||
console.log(
|
||||
`Found ${allFiles.length} supported book files`
|
||||
);
|
||||
|
||||
// Process each file
|
||||
for (const filePath of allFiles) {
|
||||
try {
|
||||
const buffer = await fs.promises.readFile(filePath);
|
||||
const arraybuffer = new Uint8Array(buffer).buffer;
|
||||
const blob = new Blob([arraybuffer]);
|
||||
const fileName = path.basename(filePath);
|
||||
|
||||
let file: any = new File([blob], fileName);
|
||||
file.path = filePath;
|
||||
|
||||
await this.getMd5WithBrowser(file);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error processing file ${filePath}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ isMoreOptionsVisible: false });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="more-option-text">
|
||||
<Trans>Import folder</Trans>
|
||||
</span>
|
||||
{!isElectron && (
|
||||
<input
|
||||
type="file"
|
||||
{...({
|
||||
webkitdirectory: "",
|
||||
mozdirectory: "",
|
||||
directory: "",
|
||||
} as React.InputHTMLAttributes<HTMLInputElement>)}
|
||||
multiple
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "45px",
|
||||
opacity: 0,
|
||||
marginLeft: "-20px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onChange={async (e) => {
|
||||
console.log("Selected folder:", e);
|
||||
const files = e.target.files;
|
||||
if (!files || files.length === 0) {
|
||||
return;
|
||||
}
|
||||
console.log(files);
|
||||
for (let item of files) {
|
||||
if (
|
||||
!supportedFormats.find((format) =>
|
||||
item.name.toLowerCase().endsWith(format)
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
await this.getMd5WithBrowser(item);
|
||||
}
|
||||
this.toggleMoreOptions();
|
||||
}}
|
||||
></input>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="more-option-item"
|
||||
onClick={this.handleCloudImport}
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
z-index: 999;
|
||||
margin-top: 5px;
|
||||
overflow: hidden;
|
||||
height: 45px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,26 @@ import toast from "react-hot-toast";
|
||||
import i18n from "../i18n";
|
||||
import { getThirdpartyRequest } from "./request/thirdparty";
|
||||
declare var window: any;
|
||||
export const supportedFormats = [
|
||||
".epub",
|
||||
".pdf",
|
||||
".txt",
|
||||
".mobi",
|
||||
".azw3",
|
||||
".azw",
|
||||
".htm",
|
||||
".html",
|
||||
".xml",
|
||||
".xhtml",
|
||||
".mhtml",
|
||||
".docx",
|
||||
".md",
|
||||
".fb2",
|
||||
".cbz",
|
||||
".cbt",
|
||||
".cbr",
|
||||
".cb7",
|
||||
];
|
||||
export const calculateFileMD5 = (file: File): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
@@ -337,7 +357,7 @@ export const preCacheAllBooks = async (bookList: Book[]) => {
|
||||
);
|
||||
let cache = await rendition.preCache(result);
|
||||
if (cache !== "err" || cache) {
|
||||
BookUtil.addBook("cache-" + selectedBook.key, "zip", cache);
|
||||
await BookUtil.addBook("cache-" + selectedBook.key, "zip", cache);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ import { LocalFileManager } from "./localFile";
|
||||
declare var window: any;
|
||||
|
||||
class BookUtil {
|
||||
static addBook(key: string, format: string, buffer: ArrayBuffer) {
|
||||
static async addBook(key: string, format: string, buffer: ArrayBuffer) {
|
||||
// for both original books and cached boks
|
||||
|
||||
if (isElectron) {
|
||||
@@ -40,9 +40,9 @@ class BookUtil {
|
||||
}
|
||||
} else {
|
||||
if (ConfigService.getReaderConfig("isUseLocal") === "yes") {
|
||||
LocalFileManager.saveFile(key + "." + format, buffer, "book");
|
||||
await LocalFileManager.saveFile(key + "." + format, buffer, "book");
|
||||
} else {
|
||||
localforage.setItem(key, buffer);
|
||||
await localforage.setItem(key, buffer);
|
||||
}
|
||||
|
||||
this.uploadBook(key, format);
|
||||
@@ -397,6 +397,7 @@ class BookUtil {
|
||||
} else {
|
||||
let syncUtil = await SyncService.getSyncUtil();
|
||||
let bookBuffer: any = await this.fetchBook(key, format, true, "");
|
||||
console.log("upload book", key, format, bookBuffer);
|
||||
let bookBlob = new Blob([bookBuffer], {
|
||||
type: CommonTool.getMimeType(format.toLowerCase()),
|
||||
});
|
||||
|
||||
@@ -149,9 +149,7 @@ class CoverUtil {
|
||||
"cover"
|
||||
);
|
||||
}
|
||||
await this.uploadCover(
|
||||
book.key + "." + this.base64ToFileType(coverBase64)
|
||||
);
|
||||
this.uploadCover(book.key + "." + this.base64ToFileType(coverBase64));
|
||||
// book.cover = "";
|
||||
}
|
||||
}
|
||||
@@ -296,6 +294,7 @@ class CoverUtil {
|
||||
let book = await DatabaseService.getRecord(cover.split(".")[0], "books");
|
||||
if (ConfigService.getReaderConfig("isUseLocal") === "yes") {
|
||||
let coverBuffer = await LocalFileManager.readFile(cover, "cover");
|
||||
console.log("coverBuffer", coverBuffer);
|
||||
if (!coverBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
5
types/index.d.ts
vendored
5
types/index.d.ts
vendored
@@ -3,3 +3,8 @@ declare module "react-tooltip" {
|
||||
export const Tooltip: any;
|
||||
export default Tooltip;
|
||||
}
|
||||
declare module "react" {
|
||||
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
|
||||
webkitdirectory?: string;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user